From 29ab8e68e670abd5fca08b71fc45793e478d3211 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Mon, 26 Feb 2024 11:35:51 +0200 Subject: [PATCH 01/17] Make R work with R classes and changes from develop --- DESCRIPTION | 2 +- NAMESPACE | 12 ++ R/RcppExports.R | 50 ++++++-- R/compute_indicators.R | 83 ++++++++----- R/file_to_polytope.R | 113 ------------------ R/gen_birkhoff.R | 22 ++-- R/gen_cross.R | 38 +++--- R/gen_cube.R | 35 +++--- R/gen_prod_simplex.R | 26 ++-- R/gen_rand_hpoly.R | 49 +++++--- R/gen_rand_vpoly.R | 54 +++++---- R/gen_rand_zonotope.R | 58 +++++---- R/gen_simplex.R | 30 ++--- R/gen_skinny_cube.R | 22 ++-- R/read_sdpa_file.R | 10 +- R/rotate_polytope.R | 76 +++++++----- R/round_polytope.R | 60 +++++----- R/zonotope_approximation.R | 43 ++++--- R/zzz.R | 4 +- man/Hpolytope-class.Rd | 27 +++++ man/Hpolytope.Rd | 19 --- man/Rcpp_Hpolytope.Rd | 28 ----- man/Rcpp_Spectrahedron.Rd | 21 ---- man/Rcpp_Vpolytope.Rd | 26 ---- man/Rcpp_VpolytopeIntersection.Rd | 28 ----- man/Rcpp_Zonotope.Rd | 26 ---- man/Spectrahedron-class.Rd | 24 ++++ man/Spectrahedron.Rd | 12 -- man/Vpolytope-class.Rd | 24 ++++ man/Vpolytope.Rd | 17 --- man/VpolytopeIntersection-class.Rd | 27 +++++ man/VpolytopeIntersection.Rd | 19 --- man/Zonotope-class.Rd | 24 ++++ man/Zonotope.Rd | 17 --- man/compute_indicators.Rd | 34 +++--- man/exact_vol.Rd | 4 +- man/file_to_polytope.Rd | 20 ---- man/gen_cross.Rd | 4 +- man/gen_cube.Rd | 4 +- man/gen_rand_hpoly.Rd | 8 +- man/gen_rand_vpoly.Rd | 10 +- man/gen_rand_zonotope.Rd | 14 ++- man/gen_simplex.Rd | 4 +- man/load_sdpa_format_file.Rd | 18 +++ ...FormatFile.Rd => read_sdpa_format_file.Rd} | 8 +- man/rotate_polytope.Rd | 12 +- man/round_polytope.Rd | 10 +- man/sample_points.Rd | 24 ++-- man/volume.Rd | 3 +- man/writeSdpaFormatFile.Rd | 2 +- man/write_sdpa_format_file.Rd | 30 +++++ man/zonotope_approximation.Rd | 13 +- src/RcppExports.cpp | 46 +++++-- src/copula.cpp | 1 - src/exact_vol.cpp | 43 ++++--- src/inner_ball.cpp | 33 +++-- src/load_sdpa_format_file.cpp | 62 ++++++++++ src/ode_solve.cpp | 4 +- src/rotating.cpp | 29 ++++- src/rounding.cpp | 53 +++++--- src/sample_points.cpp | 55 ++++++--- src/spectrahedron.cpp | 6 +- src/volume.cpp | 57 ++++++--- src/write_sdpa_format_file.cpp | 69 +++++++++++ src/zonotope_approximation.cpp | 18 ++- 65 files changed, 1039 insertions(+), 785 deletions(-) delete mode 100644 R/file_to_polytope.R create mode 100644 man/Hpolytope-class.Rd delete mode 100644 man/Hpolytope.Rd delete mode 100644 man/Rcpp_Hpolytope.Rd delete mode 100644 man/Rcpp_Spectrahedron.Rd delete mode 100644 man/Rcpp_Vpolytope.Rd delete mode 100644 man/Rcpp_VpolytopeIntersection.Rd delete mode 100644 man/Rcpp_Zonotope.Rd create mode 100644 man/Spectrahedron-class.Rd delete mode 100644 man/Spectrahedron.Rd create mode 100644 man/Vpolytope-class.Rd delete mode 100644 man/Vpolytope.Rd create mode 100644 man/VpolytopeIntersection-class.Rd delete mode 100644 man/VpolytopeIntersection.Rd create mode 100644 man/Zonotope-class.Rd delete mode 100644 man/Zonotope.Rd delete mode 100644 man/file_to_polytope.Rd create mode 100644 man/load_sdpa_format_file.Rd rename man/{readSdpaFormatFile.Rd => read_sdpa_format_file.Rd} (81%) create mode 100644 man/write_sdpa_format_file.Rd create mode 100644 src/load_sdpa_format_file.cpp create mode 100644 src/write_sdpa_format_file.cpp diff --git a/DESCRIPTION b/DESCRIPTION index 646c892f..a91a2ee9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,5 +20,5 @@ Imports: methods, stats LinkingTo: Rcpp, RcppEigen, BH Suggests: testthat Encoding: UTF-8 -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 BugReports: https://github.com/GeomScale/volesti/issues diff --git a/NAMESPACE b/NAMESPACE index e12919ab..0d88effd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,9 +2,13 @@ export(compute_indicators) export(copula) +export(dinvweibull_with_loc) export(direct_sampling) +export(ess) +export(estimtate_lipschitz_constant) export(exact_vol) export(frustum_of_simplex) +export(gen_birkhoff) export(gen_cross) export(gen_cube) export(gen_prod_simplex) @@ -13,12 +17,20 @@ export(gen_rand_vpoly) export(gen_rand_zonotope) export(gen_simplex) export(gen_skinny_cube) +export(geweke) export(inner_ball) +export(loadSdpaFormatFile) +export(ode_solve) +export(pinvweibull_with_loc) +export(psrf_multivariate) +export(psrf_univariate) +export(raftery) export(read_sdpa_format_file) export(rotate_polytope) export(round_polytope) export(sample_points) export(volume) +export(writeSdpaFormatFile) export(write_sdpa_format_file) export(zonotope_approximation) exportClasses(Hpolytope) diff --git a/R/RcppExports.R b/R/RcppExports.R index c3f192ae..a4680226 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -99,8 +99,8 @@ ess <- function(samples) { #' vol = exact_vol(Z) #' #' \donttest{# compute the exact volume of a 2-d arbitrary simplex -#' V = matrix(c(2,3,-1,7,0,0),ncol = 2, nrow = 3, byrow = TRUE) -#' P = Vpolytope$new(V) +#' A = matrix(c(2,3,-1,7,0,0),ncol = 2, nrow = 3, byrow = TRUE) +#' P = Vpolytope(V = A) #' vol = exact_vol(P) #' } #' @@ -176,6 +176,17 @@ inner_ball <- function(P, lpsolve = NULL) { .Call(`_volesti_inner_ball`, P, lpsolve) } +#' An internal Rccp function to read a SDPA format file +#' +#' @param input_file Name of the input file +#' +#' @keywords internal +#' +#' @return A list with two named items: an item "matrices" which is a list of the matrices and an vector "objFunction" +load_sdpa_format_file <- function(input_file = NULL) { + .Call(`_volesti_load_sdpa_format_file`, input_file) +} + #' Solve an ODE of the form dx^n / dt^n = F(x, t) #' #' @param n The number of steps. @@ -305,7 +316,7 @@ rounding <- function(P, method = NULL, seed = NULL) { #' \item{\code{BaW_rad} }{ The radius for the ball walk.} #' \item{\code{L} }{ The maximum length of the billiard trajectory or the radius for the step of dikin, vaidya or john walk.} #' \item{\code{solver} }{ Specify ODE solver for logconcave sampling. Options are i) leapfrog, ii) euler iii) runge-kutta iv) richardson} -#' \item{\code{step_size }{ Optionally chosen step size for logconcave sampling. Defaults to a theoretical value if not provided.} +#' \item{\code{step_size} }{ Optionally chosen step size for logconcave sampling. Defaults to a theoretical value if not provided.} #' } #' @param distribution Optional. A list that declares the target density and some related parameters as follows: #' \itemize{ @@ -347,7 +358,7 @@ rounding <- function(P, method = NULL, seed = NULL) { #' # gaussian distribution from the 2d unit simplex in H-representation with variance = 2 #' A = matrix(c(-1,0,0,-1,1,1), ncol=2, nrow=3, byrow=TRUE) #' b = c(0,0,1) -#' P = Hpolytope$new(A,b) +#' P = Hpolytope(A = A, b = b) #' points = sample_points(P, n = 100, distribution = list("density" = "gaussian", "variance" = 2)) #' #' # uniform points from the boundary of a 2-dimensional random H-polytope @@ -376,7 +387,7 @@ sample_points <- function(P, n, random_walk = NULL, distribution = NULL, seed = #' A1 = matrix(c(-1,0,0,0,0,1,0,1,0), nrow=3, ncol=3, byrow = TRUE) #' A2 = matrix(c(0,0,-1,0,0,0,-1,0,0), nrow=3, ncol=3, byrow = TRUE) #' lmi = list(A0, A1, A2) -#' S = Spectrahedron$new(lmi); +#' S = Spectrahedron(matrices = lmi) #' objFunction = c(1,1) #' writeSdpaFormatFile(S, objFunction, "output.txt") #' } @@ -412,6 +423,7 @@ loadSdpaFormatFile <- function(inputFile = NULL) { #' \item{\code{walk_length} }{ An integer to set the number of the steps for the random walk. The default value is \eqn{\lfloor 10 + d/10\rfloor} for \code{'SOB'} and \eqn{1} otherwise.} #' \item{\code{win_len} }{ The length of the sliding window for CB or CG algorithm. The default value is \eqn{250} for CB with BiW and \eqn{400+3d^2} for CB and any other random walk and \eqn{500+4d^2} for CG.} #' \item{\code{hpoly} }{ A boolean parameter to use H-polytopes in MMC of CB algorithm when the input polytope is a zonotope. The default value is \code{TRUE} when the order of the zonotope is \eqn{<5}, otherwise it is \code{FALSE}.} +#' \item{\code{seed} }{ A fixed seed for the number generator.} #' } #' @param rounding Optional. A string parameter to request a rounding method to be applied in the input polytope before volume computation: a) \code{'min_ellipsoid'}, b) \code{'svd'}, c) \code{'max_ellipsoid'} and d) \code{'none'} for no rounding. #' @param seed Optional. A fixed seed for the number generator. @@ -439,8 +451,32 @@ loadSdpaFormatFile <- function(inputFile = NULL) { #' pair_vol = volume(Z, settings = list("random_walk" = "RDHR", "walk_length" = 2)) #' #' @export -volume <- function(P, settings = NULL, rounding = NULL, seed = NULL) { - .Call(`_volesti_volume`, P, settings, rounding, seed) +volume <- function(P, settings = NULL, rounding = NULL) { + .Call(`_volesti_volume`, P, settings, rounding) +} + +#' Write a SDPA format file +#' +#' Outputs a spectrahedron (the matrices defining a linear matrix inequality) and a vector (the objective function) +#' to a SDPA format file. +#' +#' @param spectrahedron A spectrahedron in n dimensions; must be an object of class Spectrahedron +#' @param objective_function A numerical vector of length n +#' @param output_file Name of the output file +#' +#' @examples +#' \donttest{ +#' A0 = matrix(c(-1,0,0,0,-2,1,0,1,-2), nrow=3, ncol=3, byrow = TRUE) +#' A1 = matrix(c(-1,0,0,0,0,1,0,1,0), nrow=3, ncol=3, byrow = TRUE) +#' A2 = matrix(c(0,0,-1,0,0,0,-1,0,0), nrow=3, ncol=3, byrow = TRUE) +#' lmi = list(A0, A1, A2) +#' S = Spectrahedron(matrices = lmi) +#' objFunction = c(1,1) +#' write_sdpa_format_file(S, objFunction, "output.txt") +#' } +#' @export +write_sdpa_format_file <- function(spectrahedron, objective_function, output_file) { + invisible(.Call(`_volesti_write_sdpa_format_file`, spectrahedron, objective_function, output_file)) } #' An internal Rccp function for the over-approximation of a zonotope diff --git a/R/compute_indicators.R b/R/compute_indicators.R index fe82cdee..695bb191 100644 --- a/R/compute_indicators.R +++ b/R/compute_indicators.R @@ -1,46 +1,75 @@ #' Compute an indicator for each time period that describes the state of a market. #' -#' Given a matrix that contains row-wise the assets' returns and a sliding window \code{win_length}, this function computes an approximation of the joint distribution (copula, e.g. see \url{https://en.wikipedia.org/wiki/Copula_(probability_theory)}) between portfolios' return and volatility in each time period defined by \code{win_len}. -#' For each copula it computes an indicator: If the indicator is large it corresponds to a crisis period and if it is small it corresponds to a normal period. +#' Given a matrix that contains row-wise the assets' returns and a sliding window \code{win_length}, this function computes an approximation of the joint distribution (copula, e.g. see \url{https://en.wikipedia.org/wiki/Copula_(probability_theory)}) between portfolios' return and volatility in each time period defined by \code{win_len}. +#' For each copula it computes an indicator: If the indicator is large it corresponds to a crisis period and if it is small it corresponds to a normal period. #' In particular, the periods over which the indicator is greater than 1 for more than 60 consecutive sliding windows are warnings and for more than 100 are crisis. The sliding window is shifted by one day. #' #' @param returns A \eqn{d}-dimensional vector that describes the direction of the first family of parallel hyperplanes. -#' @param win_length Optional. The length of the sliding window. The default value is 60. -#' @param m Optional. The number of slices for the copula. The default value is 100. -#' @param n Optional. The number of points to sample. The default value is \eqn{5\cdot 10^5}. -#' @param nwarning Optional. The number of consecutive indicators larger than 1 required to declare a warning period. The default value is 60. -#' @param ncrisis Optional. The number of consecutive indicators larger than 1 required to declare a crisis period. The default value is 100. -#' @param seed Optional. A fixed seed for the number generator. +#' @param parameters A list to set a parameterization. +#' \itemize{ +#' \item{win_length }{ The length of the sliding window. The default value is 60.} +#' \item{m } { The number of slices for the copula. The default value is 100.} +#' \item{n }{ The number of points to sample. The default value is \eqn{5\cdot 10^5}.} +#' \item{nwarning }{ The number of consecutive indicators larger than 1 required to declare a warning period. The default value is 60.} +#' \item{ncrisis }{ The number of consecutive indicators larger than 1 required to declare a crisis period. The default value is 100.} +#' \item{seed }{ A fixed seed for the number generator.} +#' } #' #' @references \cite{L. Cales, A. Chalkis, I.Z. Emiris, V. Fisikopoulos, #' \dQuote{Practical volume computation of structured convex bodies, and an application to modeling portfolio dependencies and financial crises,} \emph{Proc. of Symposium on Computational Geometry, Budapest, Hungary,} 2018.} #' #' @return A list that contains the indicators and the corresponding vector that label each time period with respect to the market state: a) normal, b) crisis, c) warning. #' -#' @examples +#' @examples #' # simple example on random asset returns #' asset_returns = replicate(10, rnorm(14)) -#' market_states_and_indicators = compute_indicators(asset_returns, 10, 10, 10000, 2, 3) +#' market_states_and_indicators = compute_indicators(asset_returns, +#' parameters = list("win_length" = 10, "m" = 10, "n" = 10000, "nwarning" = 2, "ncrisis" = 3)) #' #' @export -compute_indicators <- function(returns, win_length = NULL, m = NULL, n = NULL, nwarning = NULL, ncrisis = NULL, seed = NULL) { - - if (is.null(win_length)) win_length = 60 - if (is.null(m)) m = 100 - if (is.null(n)) n = 500000 - if (is.null(nwarning)) nwarning = 60 - if (is.null(ncrisis)) ncrisis = 100 - +#' @useDynLib volesti, .registration=TRUE +#' @importFrom Rcpp evalCpp +#' @importFrom Rcpp loadModule +#' @importFrom "utils" "read.csv" +#' @importFrom "stats" "cov" +#' @importFrom "methods" "new" +compute_indicators <- function(returns, parameters = list("win_length" = 60, "m" = 100, "n" = 500000, "nwarning" = 60, "ncrisis" = 100)) { + + win_length = 60 + if (!is.null(parameters$win_length)) { + win_length = parameters$win_length + } + m=100 + if (!is.null(parameters$m)){ + m = parameters$m + } + n = 500000 + if (!is.null(parameters$n)){ + n = parameters$n + } + nwarning = 60 + if (!is.null(parameters$nwarning)) { + nwarning = parameters$nwarning + } + ncrisis = 100 + if (!is.null(parameters$ncrisis)) { + ncrisis = parameters$ncrisis + } + seed = NULL + if (!is.null(parameters$seed)) { + seed = parameters$seed + } + nrows = dim(returns)[1] nassets = dim(returns)[2] wl = win_length-1 - + indicators = c() for (i in 1:(nrows-wl)) { - + Win=i:(i+wl) E = cov(returns[Win,]) - + compRet = rep(1,nassets) for (j in 1:nassets) { for (k in Win) { @@ -48,11 +77,11 @@ compute_indicators <- function(returns, win_length = NULL, m = NULL, n = NULL, n } compRet[j] = compRet[j] - 1 } - + cop = copula(r1 = compRet, sigma = E, m = m, n = n, seed = seed) blue_mass = 0 red_mass = 0 - + for (row in 1:m) { for (col in 1:m) { if (row-col<=0.2*m && row-col>=-0.2*m) { @@ -76,7 +105,7 @@ compute_indicators <- function(returns, win_length = NULL, m = NULL, n = NULL, n col = rep("normal", N) for (i in 1:N) { - + if(indicators[i]>1 && !set_index){ index = i set_index = TRUE @@ -98,7 +127,7 @@ compute_indicators <- function(returns, win_length = NULL, m = NULL, n = NULL, n col[index:i] = "crisis" } } - + return(list("indicators" = indicators, market_states = col)) - -} + +} \ No newline at end of file diff --git a/R/file_to_polytope.R b/R/file_to_polytope.R deleted file mode 100644 index 19dd61ac..00000000 --- a/R/file_to_polytope.R +++ /dev/null @@ -1,113 +0,0 @@ -#' function to get an ine or an ext file and returns the corresponding polytope -#' -#' For an ".ine" file it generates the corresponding H-polytope. For an ".ext" file it generates the corresponding V-polytope or zonotope. -#' For more details on those file formats see \url{https://github.com/GeomScale/volume_approximation/blob/develop/doc/cpp_interface.md#polytope-input}. -#' -#' @param path A string that containes the path to an ine or a ext file. The ine file desrcibes a H-polytope and ext file describes a V-polytope or a zonotope. -#' @param zonotope A boolean parameter. It has to be TRUE when the path leads to an .ext file that describes a zonotope. -#' -#' @return A polytope class. If the path corresponds to an ine file then the return value represents a H-polytope. If it corresponds to an ext file the return value represents a V-polytope (default choice) or a zonotope if the second argument is TRUE. -#' -#' @export -#' @useDynLib volesti, .registration=TRUE -#' @importFrom Rcpp evalCpp -#' @importFrom Rcpp loadModule -#' @importFrom "utils" "read.csv" -#' @importFrom "stats" "cov" -#' @importFrom "methods" "new" -#' @exportPattern "^[[:alpha:]]+" -file_to_polytope <- function(path, zonotope = FALSE){ - - ineorext=substr(path, start = nchar(path) - 2, stop = nchar(path)) - if(ineorext!="ine" && ineorext!="ext") { - stop("Only ine or ext files can be handled by this function!") - } - P = read.csv(path) - r = as.character(P[3,1]) - count_sp = 1 - str = "" - beg = 0 - for (j in 1:nchar(r)) { - if (substr(r, start=j, stop=j) == " ") { - beg = beg + 1 - } else { - break - } - } - for (i in seq(from= beg + 1, to=nchar(r), by=1)) { - if (substr(r, start=i, stop=i) == " ") { - if (count_sp == 1) { - m = as.numeric(str) - str = "" - count_sp = count_sp + 1 - } else { - d = as.numeric(str) - str = "" - break - } - } else { - str = paste0(str, substr(r, start=i, stop=i)) - } - } - A = rep(0,d) - A[1] = m - A[2] = d - newrow = rep(0,d) - for (i in 4:(dim(P)[1] - 2)) { - r = P[i,1] - r = as.character(r) - str = "" - count = 1 - beg = 0 - for (j in 1:nchar(r)) { - if(substr(r, start=j, stop=j)==" "){ - beg = beg + 1 - } else { - break - } - } - sp_bef = FALSE - for (j in seq(from=beg + 1, to=nchar(r), by=1)) { - if (substr(r, start=j, stop=j) == " "){ - if (sp_bef) { - next - } - sp_bef = TRUE - newrow[count] = as.numeric(str) - str = "" - count = count + 1 - } else { - str = paste0(str, substr(r, start=j, stop=j)) - sp_bef = FALSE - if (j == nchar(r)) { - newrow[count] = as.numeric(str) - } - } - } - A = rbind(A,newrow) - newrow = rep(0,d) - } - A = matrix(A, ncol=dim(A)[2]) # now matrix A is in ine or ext format - - # remove first row - A = A[-c(1),] - - # first column is the vector b - b = A[,1] - - # remove first column - A2 = A[,-c(1)] - - if(ineorext=="ine") { - P = Hpolytope$new(-A2,b) - } else { - if(!missing(zonotope)){ - if(zonotope) { - P = Zonotope$new(A2) - return(P) - } - } - P = Vpolytope$new(A2) - } - return(P) -} diff --git a/R/gen_birkhoff.R b/R/gen_birkhoff.R index 935f637e..85333caf 100644 --- a/R/gen_birkhoff.R +++ b/R/gen_birkhoff.R @@ -1,28 +1,28 @@ #' Generator function for Birkhoff polytope -#' +#' #' This function can be used to generate the full dimensional \eqn{n}-Birkhoff polytope in H-representation. #' The dimension of the generated polytope is \eqn{(n-1)^2}. -#' +#' #' @param n The order of the Birkhoff polytope -#' +#' #' @return A polytope class representing the full dimensional \eqn{n}-Birkhoff polytope in H-representation. -#' @examples +#' @examples #' # generate the Birkhoff polytope of order 5 #' P = gen_birkhoff(5) #' @export gen_birkhoff <- function(n) { - + kind_gen = 7 m_gen = 0 - + Mat = poly_gen(kind_gen, FALSE, FALSE, n, m_gen) - + # first column is the vector b b = Mat[,1] Mat = Mat[,-c(1)] - - P = Hpolytope$new(Mat, b) - + + P = Hpolytope(A = Mat, b = b) + return(P) - + } diff --git a/R/gen_cross.R b/R/gen_cross.R index d782f399..e9fde1f8 100644 --- a/R/gen_cross.R +++ b/R/gen_cross.R @@ -1,41 +1,41 @@ #' Generator function for cross polytopes -#' +#' #' This function generates the \eqn{d}-dimensional cross polytope in H- or V-representation. -#' +#' #' @param dimension The dimension of the cross polytope. -#' @param representation A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. -#' +#' @param representation A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. Default valus is 'H'. +#' #' @return A polytope class representing a cross polytope in H- or V-representation. -#' @examples +#' @examples #' # generate a 10-dimensional cross polytope in H-representation #' P = gen_cross(5, 'H') -#' +#' #' # generate a 15-dimension cross polytope in V-representation #' P = gen_cross(15, 'V') #' @export -gen_cross <- function(dimension, representation) { - +gen_cross <- function(dimension, representation = 'H') { + kind_gen = 2 m_gen = 0 - if (representation == "V") { + if (representation == 'V') { Vpoly_gen = TRUE - } else if (representation == "H") { + } else if (representation == 'H') { Vpoly_gen = FALSE } else { stop('Not a known representation.') } - + Mat = poly_gen(kind_gen, Vpoly_gen, FALSE, dimension, m_gen) - + # first column is the vector b - b = Mat[,1] - Mat = Mat[,-c(1)] - + b = Mat[, 1] + Mat = Mat[, -c(1), drop = FALSE] + if (Vpoly_gen) { - P = Vpolytope$new(Mat, 2^dimension / prod(1:dimension)) + P = Vpolytope(V = Mat, volume = 2^dimension / prod(1:dimension)) } else { - P = Hpolytope$new(-Mat, b, 2^dimension / prod(1:dimension)) + P = Hpolytope(A = -Mat, b = b, volume = 2^dimension / prod(1:dimension)) } - + return(P) -} +} \ No newline at end of file diff --git a/R/gen_cube.R b/R/gen_cube.R index bcbf1f02..f189b720 100644 --- a/R/gen_cube.R +++ b/R/gen_cube.R @@ -1,41 +1,40 @@ #' Generator function for hypercubes -#' +#' #' This function generates the \eqn{d}-dimensional unit hypercube \eqn{[-1,1]^d} in H- or V-representation. -#' +#' #' @param dimension The dimension of the hypercube -#' @param representation A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. -#' +#' @param representation A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. Default valus is 'H'. +#' #' @return A polytope class representing the unit \eqn{d}-dimensional hypercube in H- or V-representation. -#' @examples +#' @examples #' # generate a 10-dimensional hypercube in H-representation #' P = gen_cube(10, 'H') -#' +#' #' # generate a 15-dimension hypercube in V-representation #' P = gen_cube(5, 'V') #' @export -gen_cube <- function(dimension, representation) { - +gen_cube <- function(dimension, representation = 'H') { + kind_gen = 1 m_gen = 0 - if (representation == "V") { + if (representation == 'V') { Vpoly_gen = TRUE - } else if (representation == "H") { + } else if (representation == 'H') { Vpoly_gen = FALSE } else { stop('Not a known representation.') } - + Mat = poly_gen(kind_gen, Vpoly_gen, FALSE, dimension, m_gen) - + # first column is the vector b - b = Mat[,1] - Mat = Mat[,-c(1)] + b = Mat[, 1] + Mat = Mat[, -c(1), drop = FALSE] if (Vpoly_gen) { - P = Vpolytope$new(Mat, 2^dimension) + P = Vpolytope(V = Mat, volume = 2^dimension) } else { - P = Hpolytope$new(-Mat, b, 2^dimension) + P = Hpolytope(A = -Mat, b = b, volume = 2^dimension) } - + return(P) - } diff --git a/R/gen_prod_simplex.R b/R/gen_prod_simplex.R index a0505880..3f724e8a 100644 --- a/R/gen_prod_simplex.R +++ b/R/gen_prod_simplex.R @@ -1,29 +1,29 @@ #' Generator function for product of simplices -#' +#' #' This function generates a \eqn{2d}-dimensional polytope that is defined as the product of two \eqn{d}-dimensional unit simplices in H-representation. -#' +#' #' @param dimension The dimension of the simplices. -#' +#' #' @return A polytope class representing the product of the two \eqn{d}-dimensional unit simplices in H-representation. -#' +#' #' @examples #' # generate a product of two 5-dimensional simplices. #' P = gen_prod_simplex(5) #' @export gen_prod_simplex <- function(dimension) { - + kind_gen = 4 m_gen = 0 Vpoly_gen = FALSE - + Mat = poly_gen(kind_gen, Vpoly_gen, FALSE, dimension, m_gen) # first column is the vector b - b = Mat[,1] - Mat = Mat[,-c(1)] - - P = Hpolytope$new(-Mat, b, (1/prod(1:dimension))^2) - + b = Mat[, 1] + Mat = Mat[, -c(1), drop = FALSE] + + P = Hpolytope(A = -Mat, b = b, volume = (1/prod(1:dimension))^2) + return(P) - -} + +} \ No newline at end of file diff --git a/R/gen_rand_hpoly.R b/R/gen_rand_hpoly.R index 2785a28b..a7208b82 100644 --- a/R/gen_rand_hpoly.R +++ b/R/gen_rand_hpoly.R @@ -1,28 +1,45 @@ #' Generator function for random H-polytopes -#' +#' #' This function generates a \eqn{d}-dimensional polytope in H-representation with \eqn{m} facets. We pick \eqn{m} random hyperplanes tangent on the \eqn{d}-dimensional unit hypersphere as facets. -#' +#' #' @param dimension The dimension of the convex polytope. #' @param nfacets The number of the facets. -#' @param seed Optional. A fixed seed for the generator. -#' +#' @param generator A list that could contain two elements. +#' \itemize{ +#' \item{constants }{ To declare how to set the constants \eqn{b_i} for each facets: (i) 'sphere', each hyperplane is tangent to the hypersphere of radius 10, (ii) 'uniform' for each \eqn{b_i} the generator picks a uniform number from \eqn{(0,1)}. The defalut value is 'sphere'.} +#' \item{seed }{ Optional. A fixed seed for the number generator.} +#' } +#' #' @return A polytope class representing a H-polytope. -#' @examples +#' @examples #' # generate a 10-dimensional polytope with 50 facets #' P = gen_rand_hpoly(10, 50) #' @export -gen_rand_hpoly <- function(dimension, nfacets, seed = NULL) { - - kind_gen = 6 +gen_rand_hpoly <- function(dimension, nfacets, generator = list('constants' = 'sphere')) { + + seed = NULL + if (!is.null(generator$seed)) { + seed = generator$seed + } + + if (is.null(generator$constants)) { + kind_gen = 6 + } else if (generator$constants == 'sphere'){ + kind_gen = 6 + } else if (generator$constants == 'uniform') { + kind_gen = 7 + } else { + stop("Wrong generator!") + } Vpoly_gen = FALSE - + Mat = poly_gen(kind_gen, Vpoly_gen, FALSE, dimension, nfacets, seed) - + # first column is the vector b - b = Mat[,1] - Mat = Mat[,-c(1)] - - P = Hpolytope$new(Mat, b) - + b = Mat[, 1] + Mat = Mat[, -c(1), drop = FALSE] + + P = Hpolytope(A = Mat, b = b) + return(P) -} +} \ No newline at end of file diff --git a/R/gen_rand_vpoly.R b/R/gen_rand_vpoly.R index 31cc561c..e67f93aa 100644 --- a/R/gen_rand_vpoly.R +++ b/R/gen_rand_vpoly.R @@ -1,36 +1,44 @@ #' Generator function for random V-polytopes -#' +#' #' This function generates a \eqn{d}-dimensional polytope in V-representation with \eqn{m} vertices. We pick \eqn{m} random points from the boundary of the \eqn{d}-dimensional unit hypersphere as vertices. -#' +#' #' @param dimension The dimension of the convex polytope. #' @param nvertices The number of the vertices. -#' @param generator The body that the generator samples uniformly the vertices from: (a) 'cube' or (b) 'sphere'. -#' @param seed Optional. A fixed seed for the generator. -#' +#' @param generator A list that could contain two elements. +#' \itemize{ +#' \item{body }{ the body that the generator samples uniformly the vertices from: (i) 'cube' or (ii) 'sphere', the default value is 'sphere'.} +#' \item{seed }{ Optional. A fixed seed for the number generator.} +#' } +#' #' @return A polytope class representing a V-polytope. -#' @examples +#' @examples #' # generate a 10-dimensional polytope defined as the convex hull of 25 random vertices #' P = gen_rand_vpoly(10, 25) #' @export -gen_rand_vpoly <- function(dimension, nvertices, generator = NULL, seed = NULL) { - - kind_gen = 4 - - if(!is.null(generator)){ - if (generator == 'cube'){ - kind_gen = 5 - } else if (generator != 'sphere') { - stop("Wrong generator!") - } +gen_rand_vpoly <- function(dimension, nvertices, generator = list('body' = 'sphere')) { + + seed = NULL + if (!is.null(generator$seed)) { + seed = generator$seed + } + + if (is.null(generator$body)) { + kind_gen = 4 + } else if (generator$body == 'cube'){ + kind_gen = 5 + } else if (generator$body == 'sphere') { + kind_gen = 4 + } else { + stop("Wrong generator!") } - + Mat = poly_gen(kind_gen, TRUE, FALSE, dimension, nvertices, seed) # first column is the vector b - b = Mat[,1] - Mat = Mat[,-c(1)] - - P = Vpolytope$new(Mat) - + b = Mat[, 1] + Mat = Mat[, -c(1), drop = FALSE] + + P = Vpolytope(V = Mat) + return(P) -} +} \ No newline at end of file diff --git a/R/gen_rand_zonotope.R b/R/gen_rand_zonotope.R index 3a2c8c1b..a7286dc8 100644 --- a/R/gen_rand_zonotope.R +++ b/R/gen_rand_zonotope.R @@ -1,40 +1,48 @@ #' Generator function for zonotopes -#' +#' #' This function generates a random \eqn{d}-dimensional zonotope defined by the Minkowski sum of \eqn{m} \eqn{d}-dimensional segments. #' The function considers \eqn{m} random directions in \eqn{R^d}. There are three strategies to pick the length of each segment: a) it is uniformly sampled from \eqn{[0,100]}, b) it is random from \eqn{\mathcal{N}(50,(50/3)^2)} truncated to \eqn{[0,100]}, c) it is random from \eqn{Exp(1/30)} truncated to \eqn{[0,100]}. -#' +#' #' @param dimension The dimension of the zonotope. #' @param nsegments The number of segments that generate the zonotope. -#' @param generator The distribution to pick the length of each segment from \eqn{[0,100]}: (a) 'uniform', (b) 'gaussian' or (c) 'exponential'. -#' @param seed Optional. A fixed seed for the generator. -#' +#' @param generator A list that could contain two elements. +#' \itemize{ +#' \item{distribution }{ the distribution to pick the length of each segment from \eqn{[0,100]}: (i) 'uniform', (ii) 'gaussian' or (iii) 'exponential', the default value is 'uniform.} +#' \item {seed }{ Optional. A fixed seed for the number generator.} +#' } +#' #' @return A polytope class representing a zonotope. #' -#' @examples +#' @examples #' # generate a 10-dimensional zonotope defined by the Minkowski sum of 20 segments #' P = gen_rand_zonotope(10, 20) #' @export -gen_rand_zonotope <- function(dimension, nsegments, generator = NULL, seed = NULL) { - - kind_gen = 1 - - if (!is.null(generator)) { - if (generator == 'gaussian') { - kind_gen = 2 - } else if (generator == 'exponential') { - kind_gen = 3 - } else if (generator != 'uniform'){ - stop("Wrong generator!") - } +gen_rand_zonotope <- function(dimension, nsegments, generator = list('distribution' = 'uniform')) { + + seed = NULL + if (!is.null(generator$seed)) { + seed = generator$seed } - + + if (is.null(generator$distribution)) { + kind_gen = 1 + } else if (generator$distribution == 'gaussian') { + kind_gen = 2 + } else if (generator$distribution == 'exponential') { + kind_gen = 3 + } else if (generator$distribution == 'uniform'){ + kind_gen = 1 + } else { + stop("Wrong generator!") + } + Mat = poly_gen(kind_gen, FALSE, TRUE, dimension, nsegments, seed) - + # first column is the vector b - b = Mat[,1] - Mat = Mat[,-c(1)] - - P = Zonotope$new(Mat) + b = Mat[, 1] + Mat = Mat[, -c(1), drop = FALSE] + + P = Zonotope(G = Mat) return(P) -} +} \ No newline at end of file diff --git a/R/gen_simplex.R b/R/gen_simplex.R index aa2ca4a6..99fe29d5 100644 --- a/R/gen_simplex.R +++ b/R/gen_simplex.R @@ -1,20 +1,20 @@ #' Generator function for simplices -#' +#' #' This function generates the \eqn{d}-dimensional unit simplex in H- or V-representation. -#' +#' #' @param dimension The dimension of the unit simplex. -#' @param representation A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. -#' +#' @param representation A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. Default valus is 'H'. +#' #' @return A polytope class representing the \eqn{d}-dimensional unit simplex in H- or V-representation. #' @examples #' # generate a 10-dimensional simplex in H-representation #' PolyList = gen_simplex(10, 'H') -#' +#' #' # generate a 20-dimensional simplex in V-representation #' P = gen_simplex(20, 'V') #' @export -gen_simplex <- function(dimension, representation) { - +gen_simplex <- function(dimension, representation = 'H') { + kind_gen = 3 m_gen = 0 if (representation == "V") { @@ -24,18 +24,18 @@ gen_simplex <- function(dimension, representation) { } else { stop('Not a known representation.') } - + Mat = poly_gen(kind_gen, Vpoly_gen, FALSE, dimension, m_gen) # first column is the vector b - b = Mat[,1] - Mat = Mat[,-c(1)] - + b = Mat[, 1] + Mat = Mat[, -c(1), drop = FALSE] + if (Vpoly_gen) { - P = Vpolytope$new(Mat, 1/prod(1:dimension)) + P = Vpolytope(V = Mat, volume = 1/prod(1:dimension)) } else { - P = Hpolytope$new(-Mat, b, 1/prod(1:dimension)) + P = Hpolytope(A = -Mat, b = b, volume = 1/prod(1:dimension)) } - + return(P) -} +} \ No newline at end of file diff --git a/R/gen_skinny_cube.R b/R/gen_skinny_cube.R index 0a1df0eb..2dde1d1e 100644 --- a/R/gen_skinny_cube.R +++ b/R/gen_skinny_cube.R @@ -1,9 +1,9 @@ #' Generator function for skinny hypercubes -#' +#' #' This function generates a \eqn{d}-dimensional skinny hypercube \eqn{[-1,1]^{d-1}\times [-100,100]}. -#' +#' #' @param dimension The dimension of the skinny hypercube. -#' +#' #' @return A polytope class representing the \eqn{d}-dimensional skinny hypercube in H-representation. #' #' @examples @@ -11,18 +11,18 @@ #' P = gen_skinny_cube(10) #' @export gen_skinny_cube <- function(dimension) { - + kind_gen = 5 m_gen = 0 Vpoly_gen = FALSE - + Mat = poly_gen(kind_gen, Vpoly_gen, FALSE, dimension, m_gen) # first column is the vector b - b = Mat[,1] - Mat = Mat[,-c(1)] - - P = Hpolytope$new(-Mat, b, 2^(dimension -1)*200) - + b = Mat[, 1] + Mat = Mat[, -c(1), drop = FALSE] + + P = Hpolytope(A = -Mat, b = b, volume = 2^(dimension -1)*200) + return(P) -} +} \ No newline at end of file diff --git a/R/read_sdpa_file.R b/R/read_sdpa_file.R index 83c10c52..4c78523a 100644 --- a/R/read_sdpa_file.R +++ b/R/read_sdpa_file.R @@ -9,7 +9,7 @@ #' #' @examples #' path = system.file('extdata', package = 'volesti') -#' l = readSdpaFormatFile(paste0(path,'/sdpa_n2m3.txt')) +#' l = read_sdpa_format_file(paste0(path,'/sdpa_n2m3.txt')) #' Spectrahedron = l$spectrahedron #' objFunction = l$objFunction #' @export @@ -18,9 +18,9 @@ #' @importFrom Rcpp loadModule #' @importFrom "methods" "new" #' @exportPattern "^[[:alpha:]]+" -readSdpaFormatFile <- function(path){ - l = loadSdpaFormatFile(path) - S = Spectrahedron$new(l$matrices) +read_sdpa_format_file <- function(path){ + l = load_sdpa_format_file(path) + S = Spectrahedron(matrices = l$matrices) return(list("spectrahedron"=S, "objFunction"= l$objFunction)) -} +} \ No newline at end of file diff --git a/R/rotate_polytope.R b/R/rotate_polytope.R index 12ce246b..b901ee14 100644 --- a/R/rotate_polytope.R +++ b/R/rotate_polytope.R @@ -1,14 +1,13 @@ #' Apply a random rotation to a convex polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes) -#' +#' #' Given a convex H- or V- polytope or a zonotope or an intersection of two V-polytopes as input, this function applies (a) a random rotation or (b) a given rotation by an input matrix \eqn{T}. -#' +#' #' @param P A convex polytope. It is an object from class (a) Hpolytope, (b) Vpolytope, (c) Zonotope, (d) intersection of two V-polytopes. -#' @param T Optional. A \eqn{d\times d} rotation matrix. -#' @param seed Optional. A fixed seed for the random linear map generator. -#' +#' @param rotation A list that contains (a) the rotation matrix T and (b) the 'seed' to set a spesific seed for the number generator. +#' #' @return A list that contains the rotated polytope and the matrix \eqn{T} of the linear transformation. #' -#' @details Let \eqn{P} be the given polytope and \eqn{Q} the rotated one and \eqn{T} be the matrix of the linear transformation. +#' @details Let \eqn{P} be the given polytope and \eqn{Q} the rotated one and \eqn{T} be the matrix of the linear transformation. #' \itemize{ #' \item{If \eqn{P} is in H-representation and \eqn{A} is the matrix that contains the normal vectors of the facets of \eqn{Q} then \eqn{AT} contains the normal vactors of the facets of \eqn{P}.} #' \item{If \eqn{P} is in V-representation and \eqn{V} is the matrix that contains column-wise the vertices of \eqn{Q} then \eqn{T^TV} contains the vertices of \eqn{P}.} @@ -17,41 +16,60 @@ #' } #' @examples #' # rotate a H-polytope (2d unit simplex) -#' P = gen_simplex(2,'H') +#' P = gen_simplex(2, 'H') #' poly_matrix_list = rotate_polytope(P) -#' +#' #' # rotate a V-polytope (3d cube) #' P = gen_cube(3, 'V') #' poly_matrix_list = rotate_polytope(P) -#' +#' #' # rotate a 5-dimensional zonotope defined by the Minkowski sum of 15 segments -#' Z = gen_rand_zonotope(3,6) +#' Z = gen_rand_zonotope(3, 6) #' poly_matrix_list = rotate_polytope(Z) #' @export -rotate_polytope <- function(P, T = NULL, seed = NULL){ - +rotate_polytope <- function(P, rotation = list()) { + + seed = NULL + if (!is.null(rotation$seed)) { + seed = rotation$seed + } + + if (is.null(rotation$T)) { + T = NULL + } else { + T = rotation$T + } + #call rcpp rotating function Mat = rotating(P, T, seed) - - n = P$dimension - m=dim(Mat)[2]-n - Tr = Mat[,-c(1:(dim(Mat)[2]-n))] + + type = P@type + + if (type == 'Vpolytope') { + n = dim(P@V)[2] + }else if (type == 'Zonotope') { + n = dim(P@G)[2] + } else { + n = dim(P@A)[2] + } + + m = dim(Mat)[2] - n + Tr = Mat[, -c(1:(dim(Mat)[2]-n)), drop = FALSE] Tr = Tr[1:n, 1:n] - Mat = t(Mat[,1:m]) - + Mat = t(Mat[, 1:m]) + # first column is the vector b - b = Mat[,1] - + b = Mat[, 1] + # remove first column - A = Mat[,-c(1)] - - type = P$type - if (type == 2) { - PP = Vpolytope$new(A) - }else if (type == 3) { - PP = Zonotope$new(A) + A = Mat[, -c(1), drop = FALSE] + + if (type == 'Vpolytope') { + PP = Vpolytope(V = A) + }else if (type == 'Zonotope') { + PP = Zonotope(G = A) } else { - PP = Hpolytope$new(A, b) + PP = Hpolytope(A = A, b = b) } return(list("P" = PP, "T" = Tr)) -} +} \ No newline at end of file diff --git a/R/round_polytope.R b/R/round_polytope.R index f95649c6..e50ab7b9 100644 --- a/R/round_polytope.R +++ b/R/round_polytope.R @@ -1,11 +1,14 @@ #' Apply rounding to a convex polytope (H-polytope, V-polytope or a zonotope) -#' +#' #' Given a convex H or V polytope or a zonotope as input this function brings the polytope in rounded position based on minimum volume enclosing ellipsoid of a pointset. -#' +#' #' @param P A convex polytope. It is an object from class (a) Hpolytope or (b) Vpolytope or (c) Zonotope. -#' @param method Optional. The method to use for rounding, a) \code{'min_ellipsoid'} for the method based on mimimmum volume enclosing ellipsoid of a dataset, b) \code{'max_ellipsoid'} for the method based on maximum volume enclosed ellipsoid, (c) \code{'isotropy'} for the method based on svd decomposition. The default method is \code{'mee'} for all the representations. -#' @param seed Optional. A fixed seed for the number generator. -#' +#' @param settings Optional. A list of settings. +#' \itemize{ +#' \item{\code{method} }{ The method to use for rounding, a) \code{'min_ellipsoid'} for the method based on mimimmum volume enclosing ellipsoid of a dataset, b) \code{'max_ellipsoid'} for the method based on maximum volume enclosed ellipsoid, (c) \code{'isotropy'} for the method based on svd decomposition. The default method is \code{'mee'} for all the representations.} +#' \item{\code{seed} }{ Optional. A fixed seed for the number generator.} +#' } +#' #' @return A list with 4 elements: (a) a polytope of the same class as the input polytope class and (b) the element "T" which is the matrix of the inverse linear transformation that is applied on the input polytope, (c) the element "shift" which is the opposite vector of that which has shifted the input polytope, (d) the element "round_value" which is the determinant of the square matrix of the linear transformation that is applied on the input polytope. #' #' @references \cite{I.Z.Emiris and V. Fisikopoulos, @@ -16,45 +19,48 @@ #' \dQuote{A practical volume algorithm,} \emph{Math. Prog. Comp.,} 2016.}, #' @references \cite{Yin Zhang and Liyan Gao, #' \dQuote{On Numerical Solution of the Maximum Volume Ellipsoid Problem,} \emph{SIAM Journal on Optimization,} 2003.}, -#' +#' #' #' @examples #' # round a 5d skinny cube #' P = gen_skinny_cube(5) #' listHpoly = round_polytope(P) -#' +#' #' # round a V-polytope (3d unit cube) #' P = gen_cube(3, 'V') #' ListVpoly = round_polytope(P) -#' +#' #' # round a 2-dimensional zonotope defined by 6 generators #' Z = gen_rand_zonotope(2,6) #' ListZono = round_polytope(Z) #' @export -round_polytope <- function(P, method = NULL, seed = NULL){ - - ret_list = rounding(P, method, seed) - +round_polytope <- function(P, settings = list()){ + + seed = NULL + if (!is.null(settings$seed)) { + seed = settings$seed + } + + ret_list = rounding(P, settings$method, seed) + #get the matrix that describes the polytope Mat = ret_list$Mat - + # first column is the vector b - b = Mat[,1] - + b = Mat[, 1] + # remove first column - A = Mat[,-c(1)] - - type = P$type - if (type == 2) { - PP = list("P" = Vpolytope$new(A), "T" = ret_list$T, "shift" = ret_list$shift, "round_value" = ret_list$round_value) - }else if (type == 3) { - PP = list("P" = Zonotope$new(A), "T" = ret_list$T, "shift" = ret_list$shift, "round_value" = ret_list$round_value) + A = Mat[, -c(1), drop = FALSE] + + type = P@type + if (type == 'Vpolytope') { + PP = list("P" = Vpolytope(V = A), "T" = ret_list$T, "shift" = ret_list$shift, "round_value" = ret_list$round_value) + }else if (type == 'Zonotope') { + PP = list("P" = Zonotope(G = A), "T" = ret_list$T, "shift" = ret_list$shift, "round_value" = ret_list$round_value) } else { - if (dim(P$Aeq)[1] > 0){ - PP = list("P" = Hpolytope$new(A,b), "T" = ret_list$T, "shift" = ret_list$shift, "round_value" = ret_list$round_value, "N" = ret_list$N, "N_shift" = ret_list$N_shift, "svd_prod" = ret_list$svd_prod) - } else { - PP = list("P" = Hpolytope$new(A,b), "T" = ret_list$T, "shift" = ret_list$shift, "round_value" = ret_list$round_value) - } + #if (dim(P$Aeq)[1] > 0){ + # PP = list("P" = Hpolytope(A = A, b = b), "T" = ret_list$T, "shift" = ret_list$shift, "round_value" = ret_list$round_value, "N" = ret_list$N, "N_shift" = ret_list$N_shift, "svd_prod" = ret_list$svd_prod) + PP = list("P" = Hpolytope(A = A, b = b), "T" = ret_list$T, "shift" = ret_list$shift, "round_value" = ret_list$round_value) } return(PP) } diff --git a/R/zonotope_approximation.R b/R/zonotope_approximation.R index 8c56f6d2..efbe73dc 100644 --- a/R/zonotope_approximation.R +++ b/R/zonotope_approximation.R @@ -1,42 +1,47 @@ #' A function to over-approximate a zonotope with PCA method and to evaluate the approximation by computing a ratio of fitness. -#' +#' #' For the evaluation of the PCA method the exact volume of the approximation body is computed and the volume of the input zonotope is computed by CoolingBodies algorithm. The ratio of fitness is \eqn{R=vol(P) / vol(P_{red})}, where \eqn{P_{red}} is the approximate polytope. -#' +#' #' @param Z A zonotope. #' @param fit_ratio Optional. A boolean parameter to request the computation of the ratio of fitness. #' @param settings Optional. A list that declares the values of the parameters of CB algorithm as follows: #' \itemize{ #' \item{\code{error} }{ A numeric value to set the upper bound for the approximation error. The default value is \eqn{0.1}.} #' \item{\code{walk_length} }{ An integer to set the number of the steps for the random walk. The default value is \eqn{1}.} -#' \item{\code{win_len} }{ The length of the sliding window for CB algorithm. The default value is \eqn{200}.} +#' \item{\code{win_len} }{ The length of the sliding window for CB algorithm. The default value is \eqn{250}.} #' \item{\code{hpoly} }{ A boolean parameter to use H-polytopes in MMC of CB algorithm. The default value is \code{TRUE} when the order of the zonotope is \eqn{<5}, otherwise it is \code{FALSE}.} +#' \item{\code{seed} }{ Optional. A fixed seed for the number generator.} #' } -#' @param seed Optional. A fixed seed for the number generator. -#' +#' #' @return A list that contains the approximation body in H-representation and the ratio of fitness -#' +#' #' @references \cite{A.K. Kopetzki and B. Schurmann and M. Althoff, #' \dQuote{Methods for Order Reduction of Zonotopes,} \emph{IEEE Conference on Decision and Control,} 2017.} -#' +#' #' @examples #' # over-approximate a 2-dimensional zonotope with 10 generators and compute the ratio of fitness -#' Z = gen_rand_zonotope(2,12) +#' Z = gen_rand_zonotope(2, 10) #' retList = zonotope_approximation(Z = Z) -#' +#' #' @export -zonotope_approximation <- function(Z, fit_ratio = NULL, settings = NULL, seed = NULL){ - +zonotope_approximation <- function(Z, fit_ratio = FALSE, settings = list('error' = 0.1, 'walk_length' = 1, 'win_len' = 250, 'hpoly' = FALSE)){ + + seed = NULL + if (!is.null(settings$seed)) { + seed = settings$seed + } + ret_list = zono_approx(Z, fit_ratio, settings, seed) - + Mat = ret_list$Mat - + # first column is the vector b - b = Mat[,1] - + b = Mat[, 1] + # remove first column - A = Mat[,-c(1)] - PP = list("P" = Hpolytope$new(A,b), "fit_ratio" = ret_list$fit_ratio) - + A = Mat[, -c(1), drop = FALSE] + PP = list("P" = Hpolytope(A = A, b = b), "fit_ratio" = ret_list$fit_ratio) + return(PP) - + } diff --git a/R/zzz.R b/R/zzz.R index 564a20b9..4bea4292 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -7,5 +7,5 @@ ## For R 2.15.1 and later this also works. Note that calling loadModule() triggers ## a load action, so this does not have to be placed in .onLoad() or evalqOnLoad(). -loadModule("polytopes", TRUE) -loadModule("spectrahedron", TRUE) +#loadModule("polytopes", TRUE) +#loadModule("spectrahedron", TRUE) diff --git a/man/Hpolytope-class.Rd b/man/Hpolytope-class.Rd new file mode 100644 index 00000000..1c5b2472 --- /dev/null +++ b/man/Hpolytope-class.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/HpolytopeClass.R +\docType{class} +\name{Hpolytope-class} +\alias{Hpolytope-class} +\alias{Hpolytope} +\title{An R class to represent an H-polytope} +\description{ +An H-polytope is a convex polytope defined by a set of linear inequalities or equivalently a \eqn{d}-dimensional H-polytope with \eqn{m} facets is defined by a \eqn{m\times d} matrix A and a \eqn{m}-dimensional vector b, s.t.: \eqn{Ax\leq b}. +} +\details{ +\describe{ + \item{A}{An \eqn{m\times d} numerical matrix.} + + \item{b}{An \eqn{m}-dimensional vector b.} + + \item{volume}{The volume of the polytope if it is known, \eqn{NaN} otherwise by default.} + + \item{type}{A character with default value 'Hpolytope', to declare the representation of the polytope.} +} +} +\examples{ +A = matrix(c(-1,0,0,-1,1,1), ncol=2, nrow=3, byrow=TRUE) +b = c(0,0,1) +P = Hpolytope(A = A, b = b) + +} diff --git a/man/Hpolytope.Rd b/man/Hpolytope.Rd deleted file mode 100644 index c0b82674..00000000 --- a/man/Hpolytope.Rd +++ /dev/null @@ -1,19 +0,0 @@ -\name{Hpolytope} -\alias{Hpolytope} -\title{An \code{R} class to represent H-polytopes.} - -\description{ -A H-polytope is a convex polytope defined by a set of linear inequalities or equivalently a \eqn{d}-dimensional H-polytope with \eqn{m} facets is defined by a \eqn{m\times d} matrix A and a \eqn{m}-dimensional vector b, s.t.: \eqn{P=\{x\ |\ Ax\leq b\} }. -} -\section{Fields}{ -\itemize{ -\item{\code{A} }{ A \eqn{m\times d} numerical matrix A} - -\item{\code{b} }{ \eqn{m}-dimensional vector b} - -\item{\code{type} }{ An integer that declares the representation of the polytope. For H-representation the default value is 1.} - -\item{\code{dimension} }{ The dimension of the polytope.} - -\item{\code{volume} }{ The volume of the polytope, if it is known.} -}} diff --git a/man/Rcpp_Hpolytope.Rd b/man/Rcpp_Hpolytope.Rd deleted file mode 100644 index 88fa5b76..00000000 --- a/man/Rcpp_Hpolytope.Rd +++ /dev/null @@ -1,28 +0,0 @@ -\docType{class} -\name{Rcpp_Hpolytope} -\alias{Rcpp_Hpolytope-class} -\alias{[,Rcpp_Hpolytope-method} -\alias{[,Rcpp_Hpolytope,ANY,ANY,ANY-method} -\alias{$<-,Rcpp_Hpolytope-method} -\alias{$,Rcpp_Hpolytope-method} -\alias{filepaths<-,Rcpp_Hpolytope-method} -\title{ -An \code{Rcpp} class to represent H-polytopes, exposed to \code{R} via modules. -} -\description{ -A H-polytope is a convex polytope defined by a set of linear inequalities or equivalently a \eqn{d}-dimensional H-polytope with \eqn{m} facets is defined by a \eqn{m\times d} matrix A and a \eqn{m}-dimensional vector b, s.t.: \eqn{P=\{ Ax\leq b \} }. -} -\details{ -\describe{ -\item{\code{A} }{ A \eqn{m\times d} numerical matrix A} - -\item{\code{b} }{ \eqn{m}-dimensional vector b} - -\item{\code{type} }{ An integer that declares the representation of the polytope. For H-representation the default value is 1.} - -\item{\code{dimension} }{ The dimension of the polytope.} - -\item{\code{volume} }{ The volume of the polytope, if it is known.} - } -} -\keyword{internal} diff --git a/man/Rcpp_Spectrahedron.Rd b/man/Rcpp_Spectrahedron.Rd deleted file mode 100644 index 3ac432e5..00000000 --- a/man/Rcpp_Spectrahedron.Rd +++ /dev/null @@ -1,21 +0,0 @@ -\docType{class} -\name{Rcpp_Spectrahedron} -\alias{Rcpp_Spectrahedron-class} -\alias{[,Rcpp_Spectrahedron-method} -\alias{[,Rcpp_Spectrahedron,ANY,ANY,ANY-method} -\alias{$<-,Rcpp_Spectrahedron-method} -\alias{$,Rcpp_Spectrahedron-method} -\alias{filepaths<-,Rcpp_Spectrahedron-method} -\title{ -An \code{Rcpp} class to represent spectrahedra, exposed to \code{R} via modules. -} -\description{ -A spectrahedron is a convex body defined by a linear matrix inequality of the form \eqn{A_0 + x_1 A_1 + ... + x_n A_n \preceq 0}. -The matrices \eqn{A_i} are symmetric \eqn{m \times m} real matrices and \eqn{\preceq 0} denoted negative semidefiniteness. -} -\details{ -\describe{ -\item{\code{matrices} }{A list with the matrices \eqn{A_0, A_1, ..., A_n}} - } -} - diff --git a/man/Rcpp_Vpolytope.Rd b/man/Rcpp_Vpolytope.Rd deleted file mode 100644 index 95c46c09..00000000 --- a/man/Rcpp_Vpolytope.Rd +++ /dev/null @@ -1,26 +0,0 @@ -\docType{class} -\name{Rcpp_Vpolytope} -\alias{Rcpp_Vpolytope-class} -\alias{[,Rcpp_Vpolytope-method} -\alias{[,Rcpp_Vpolytope,ANY,ANY,ANY-method} -\alias{$<-,Rcpp_Vpolytope-method} -\alias{$,Rcpp_Vpolytope-method} -\alias{filepaths<-,Rcpp_Vpolytope-method} -\title{ -An \code{Rcpp} class to represent V-polytopes, exposed to \code{R} via modules. -} -\description{ -A V-polytope is defined as the convex hull of \eqn{m} \eqn{d}-dimensional points which corresponds to its vertices. -} -\details{ -\describe{ - \item{\code{V} }{ A \eqn{m\times d} numerical matrix that contains the vertices row-wise} - - \item{\code{type} }{ An integer that declares the representation of the polytope. For V-representation the default value is 2.} - - \item{\code{dimension} }{ The dimension of the polytope.} - - \item{\code{volume} }{ The volume of the polytope, if it is known.} - } -} -\keyword{internal} diff --git a/man/Rcpp_VpolytopeIntersection.Rd b/man/Rcpp_VpolytopeIntersection.Rd deleted file mode 100644 index ec308c3b..00000000 --- a/man/Rcpp_VpolytopeIntersection.Rd +++ /dev/null @@ -1,28 +0,0 @@ -\docType{class} -\name{Rcpp_VpolytopeIntersection} -\alias{Rcpp_VpolytopeIntersection-class} -\alias{[,Rcpp_VpolytopeIntersection-method} -\alias{[,Rcpp_VpolytopeIntersection,ANY,ANY,ANY-method} -\alias{$<-,Rcpp_VpolytopeIntersection-method} -\alias{$,Rcpp_VpolytopeIntersection-method} -\alias{filepaths<-,Rcpp_VpolytopeIntersection-method} -\title{ -An \code{Rcpp} class to represent the intersection of two V-polytope, exposed to \code{R} via modules. -} -\description{ -An intersection of two V-polytopes, \eqn{P_1}, \eqn{P_2}, is defined by the intersection of the two coresponding convex hulls. -} -\details{ -\describe{ -\item{\code{V1} }{ The numerical matrix that contains the vertices of \eqn{P_1} row-wise.} - -\item{\code{V2} }{ The numerical matrix that contains the vertices of \eqn{P_2} row-wise.} - -\item{\code{type} }{ An integer that declares the representation of the polytope. For these kinf of polytopes the default value is 4.} - -\item{\code{dimension} }{ The dimension of the polytope.} - -\item{\code{volume} }{ The volume of the polytope, if it is known.} - } -} -\keyword{internal} diff --git a/man/Rcpp_Zonotope.Rd b/man/Rcpp_Zonotope.Rd deleted file mode 100644 index 8c010851..00000000 --- a/man/Rcpp_Zonotope.Rd +++ /dev/null @@ -1,26 +0,0 @@ -\docType{class} -\name{Rcpp_Zonotope} -\alias{Rcpp_Zonotope-class} -\alias{[,Rcpp_Zonotope-method} -\alias{[,Rcpp_Zonotope,ANY,ANY,ANY-method} -\alias{$<-,Rcpp_Zonotope-method} -\alias{$,Rcpp_Zonotope-method} -\alias{filepaths<-,Rcpp_Zonotope-method} -\title{ -An \code{Rcpp} class to represent zonotopes, exposed to \code{R} via modules. -} -\description{ -A zonotope is a convex polytope defined by the Minkowski sum of \eqn{m} \eqn{d}-dimensional segments. -} -\details{ -\describe{ - \item{\code{G} }{ A \eqn{m\times d} numerical matrix that contains the segments (or generators) row-wise} - -\item{\code{type} }{ An integer that declares the representation of the polytope. For zonotopes the default value is 3.} - -\item{\code{dimension} }{ The dimension of the polytope.} - -\item{\code{volume} }{ The volume of the polytope, if it is known.} - } -} -\keyword{internal} diff --git a/man/Spectrahedron-class.Rd b/man/Spectrahedron-class.Rd new file mode 100644 index 00000000..2cc381a2 --- /dev/null +++ b/man/Spectrahedron-class.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/SpectrahedronClass.R +\docType{class} +\name{Spectrahedron-class} +\alias{Spectrahedron-class} +\alias{Spectrahedron} +\title{An R class to represent a Spectrahedron} +\description{ +A spectrahedron is a convex body defined by a linear matrix inequality of the form \eqn{A_0 + x_1 A_1 + ... + x_n A_n \preceq 0}. +The matrices \eqn{A_i} are symmetric \eqn{m \times m} real matrices and \eqn{\preceq 0} denoted negative semidefiniteness. +} +\details{ +\describe{ + \item{matrices}{A List that contains the matrices \eqn{A_0, A_1, ..., A_n}.} +} +} +\examples{ +A0 = matrix(c(-1,0,0,0,-2,1,0,1,-2), nrow=3, ncol=3, byrow = TRUE) +A1 = matrix(c(-1,0,0,0,0,1,0,1,0), nrow=3, ncol=3, byrow = TRUE) +A2 = matrix(c(0,0,-1,0,0,0,-1,0,0), nrow=3, ncol=3, byrow = TRUE) +lmi = list(A0, A1, A2) +S = Spectrahedron(matrices = lmi); + +} diff --git a/man/Spectrahedron.Rd b/man/Spectrahedron.Rd deleted file mode 100644 index 6c6df1d8..00000000 --- a/man/Spectrahedron.Rd +++ /dev/null @@ -1,12 +0,0 @@ -\name{Spectrahedron} -\alias{Spectrahedron} -\title{An \code{R} class to represent spectrahedra.} - -\description{ -A spectrahedron is a convex body defined by a linear matrix inequality of the form \eqn{A_0 + x_1 A_1 + ... + x_n A_n \preceq 0}. -The matrices \eqn{A_i} are symmetric \eqn{m \times m} real matrices and \eqn{\preceq 0} denoted negative semidefiniteness. -} -\section{Fields}{ -\itemize{ -\item{\code{matrices} }{A list with the matrices \eqn{A_0, A_1, ..., A_n}} -}} diff --git a/man/Vpolytope-class.Rd b/man/Vpolytope-class.Rd new file mode 100644 index 00000000..83fcc197 --- /dev/null +++ b/man/Vpolytope-class.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/VpolytopeClass.R +\docType{class} +\name{Vpolytope-class} +\alias{Vpolytope-class} +\alias{Vpolytope} +\title{An R class to represent a V-polytope} +\description{ +A V-polytope is a convex polytope defined by the set of its vertices. +} +\details{ +\describe{ + \item{V}{An \eqn{m\times d} numerical matrix that contains the vertices row-wise.} + + \item{volume}{The volume of the polytope if it is known, \eqn{NaN} otherwise by default.} + + \item{type}{A character with default value 'Vpolytope', to declare the representation of the polytope.} +} +} +\examples{ +V = matrix(c(2,3,-1,7,0,0),ncol = 2, nrow = 3, byrow = TRUE) +P = Vpolytope(V = V) + +} diff --git a/man/Vpolytope.Rd b/man/Vpolytope.Rd deleted file mode 100644 index e330af8b..00000000 --- a/man/Vpolytope.Rd +++ /dev/null @@ -1,17 +0,0 @@ -\name{Vpolytope} -\alias{Vpolytope} -\title{An \code{R} class to represent V-polytopes.} - -\description{ - A V-polytope is defined as the convex hull of \eqn{m} \eqn{d}-dimensional points which corresponds to its vertices. -} -\section{Fields}{ -\itemize{ - \item{\code{V} }{ A \eqn{m\times d} numerical matrix that contains the vertices row-wise} - - \item{\code{type} }{ An integer that declares the representation of the polytope. For V-representation the default value is 2.} - - \item{\code{dimension} }{ The dimension of the polytope.} - - \item{\code{volume} }{ The volume of the polytope, if it is known.} -}} diff --git a/man/VpolytopeIntersection-class.Rd b/man/VpolytopeIntersection-class.Rd new file mode 100644 index 00000000..ae6e9364 --- /dev/null +++ b/man/VpolytopeIntersection-class.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/VpolytopeIntersectionClass.R +\docType{class} +\name{VpolytopeIntersection-class} +\alias{VpolytopeIntersection-class} +\alias{VpolytopeIntersection} +\title{An R class to represent the intersection of two V-polytopes} +\description{ +An intersection of two V-polytopes is defined by the intersection of the two coresponding convex hulls. +} +\details{ +\describe{ + \item{V1}{An \eqn{m\times d} numerical matrix that contains the vertices of the first V-polytope (row-wise).} + + \item{V2}{An \eqn{q\times d} numerical matrix that contains the vertices of the second V-polytope (row-wise).} + + \item{volume}{The volume of the polytope if it is known, \eqn{NaN} otherwise by default.} + + \item{type}{A character with default value 'VpolytopeIntersection', to declare the representation of the polytope.} +} +} +\examples{ +P1 = gen_simplex(2,'V') +P2 = gen_cross(2,'V') +P = VpolytopeIntersection(V1 = P1@V, V2 = P2@V) + +} diff --git a/man/VpolytopeIntersection.Rd b/man/VpolytopeIntersection.Rd deleted file mode 100644 index f7a75c66..00000000 --- a/man/VpolytopeIntersection.Rd +++ /dev/null @@ -1,19 +0,0 @@ -\name{VpolytopeIntersection} -\alias{VpolytopeIntersection} -\title{An \code{R} class to represent the intersection of two V-polytopes.} - -\description{ - An intersection of two V-polytopes, \eqn{P_1}, \eqn{P_2}, is defined by the intersection of the two corresponding convex hulls. -} -\section{Fields}{ -\itemize{ -\item{\code{V1} }{ The numerical matrix that contains the vertices of \eqn{P_1} row-wise.} - -\item{\code{V2} }{ The numerical matrix that contains the vertices of \eqn{P_2} row-wise.} - -\item{\code{type} }{ An integer that declares the representation of the polytope. For this polytope the default value is 4.} - -\item{\code{dimension} }{ The dimension of the polytope.} - -\item{\code{volume} }{ The volume of the polytope, if it is known.} -}} diff --git a/man/Zonotope-class.Rd b/man/Zonotope-class.Rd new file mode 100644 index 00000000..0e857d50 --- /dev/null +++ b/man/Zonotope-class.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ZonotopeClass.R +\docType{class} +\name{Zonotope-class} +\alias{Zonotope-class} +\alias{Zonotope} +\title{An R class to represent a Zonotope} +\description{ +A zonotope is a convex polytope defined by the Minkowski sum of \eqn{m} \eqn{d}-dimensional segments. +} +\details{ +\describe{ + \item{G}{An \eqn{m\times d} numerical matrix that contains the segments (or generators) row-wise} + + \item{volume}{The volume of the polytope if it is known, \eqn{NaN} otherwise by default.} + + \item{type}{A character with default value 'Zonotope', to declare the representation of the polytope.} +} +} +\examples{ +G = matrix(c(2,3,-1,7,0,0),ncol = 2, nrow = 3, byrow = TRUE) +P = Zonotope(G = G) + +} diff --git a/man/Zonotope.Rd b/man/Zonotope.Rd deleted file mode 100644 index 959fbed5..00000000 --- a/man/Zonotope.Rd +++ /dev/null @@ -1,17 +0,0 @@ -\name{Zonotope} -\alias{Zonotope} -\title{An \code{R} class to represent zonotopes.} - -\description{ -A zonotope is a convex polytope defined by the Minkowski sum of \eqn{m} \eqn{d}-dimensional segments. -} -\section{Fields}{ -\itemize{ -\item{\code{G} }{ A \eqn{m\times d} numerical matrix that contains the segments (or generators) row-wise} - -\item{\code{type} }{ An integer that declares the representation of the polytope. For zonotopes the default value is 3.} - -\item{\code{dimension} }{ The dimension of the polytope.} - -\item{\code{volume} }{ The volume of the polytope, if it is known.} -}} diff --git a/man/compute_indicators.Rd b/man/compute_indicators.Rd index c0eaadd9..d1d870a3 100644 --- a/man/compute_indicators.Rd +++ b/man/compute_indicators.Rd @@ -6,41 +6,35 @@ \usage{ compute_indicators( returns, - win_length = NULL, - m = NULL, - n = NULL, - nwarning = NULL, - ncrisis = NULL, - seed = NULL + parameters = list(win_length = 60, m = 100, n = 5e+05, nwarning = 60, ncrisis = 100) ) } \arguments{ \item{returns}{A \eqn{d}-dimensional vector that describes the direction of the first family of parallel hyperplanes.} -\item{win_length}{Optional. The length of the sliding window. The default value is 60.} - -\item{m}{Optional. The number of slices for the copula. The default value is 100.} - -\item{n}{Optional. The number of points to sample. The default value is \eqn{5\cdot 10^5}.} - -\item{nwarning}{Optional. The number of consecutive indicators larger than 1 required to declare a warning period. The default value is 60.} - -\item{ncrisis}{Optional. The number of consecutive indicators larger than 1 required to declare a crisis period. The default value is 100.} - -\item{seed}{Optional. A fixed seed for the number generator.} +\item{parameters}{A list to set a parameterization. +\itemize{ +\item{win_length }{ The length of the sliding window. The default value is 60.} +\item{m } { The number of slices for the copula. The default value is 100.} +\item{n }{ The number of points to sample. The default value is \eqn{5\cdot 10^5}.} +\item{nwarning }{ The number of consecutive indicators larger than 1 required to declare a warning period. The default value is 60.} +\item{ncrisis }{ The number of consecutive indicators larger than 1 required to declare a crisis period. The default value is 100.} +\item{seed }{ A fixed seed for the number generator.} +}} } \value{ A list that contains the indicators and the corresponding vector that label each time period with respect to the market state: a) normal, b) crisis, c) warning. } \description{ -Given a matrix that contains row-wise the assets' returns and a sliding window \code{win_length}, this function computes an approximation of the joint distribution (copula, e.g. see \url{https://en.wikipedia.org/wiki/Copula_(probability_theory)}) between portfolios' return and volatility in each time period defined by \code{win_len}. -For each copula it computes an indicator: If the indicator is large it corresponds to a crisis period and if it is small it corresponds to a normal period. +Given a matrix that contains row-wise the assets' returns and a sliding window \code{win_length}, this function computes an approximation of the joint distribution (copula, e.g. see \url{https://en.wikipedia.org/wiki/Copula_(probability_theory)}) between portfolios' return and volatility in each time period defined by \code{win_len}. +For each copula it computes an indicator: If the indicator is large it corresponds to a crisis period and if it is small it corresponds to a normal period. In particular, the periods over which the indicator is greater than 1 for more than 60 consecutive sliding windows are warnings and for more than 100 are crisis. The sliding window is shifted by one day. } \examples{ # simple example on random asset returns asset_returns = replicate(10, rnorm(14)) -market_states_and_indicators = compute_indicators(asset_returns, 10, 10, 10000, 2, 3) +market_states_and_indicators = compute_indicators(asset_returns, + parameters = list("win_length" = 10, "m" = 10, "n" = 10000, "nwarning" = 2, "ncrisis" = 3)) } \references{ diff --git a/man/exact_vol.Rd b/man/exact_vol.Rd index 59324740..33b58068 100644 --- a/man/exact_vol.Rd +++ b/man/exact_vol.Rd @@ -23,8 +23,8 @@ Z = gen_rand_zonotope(2, 5) vol = exact_vol(Z) \donttest{# compute the exact volume of a 2-d arbitrary simplex -V = matrix(c(2,3,-1,7,0,0),ncol = 2, nrow = 3, byrow = TRUE) -P = Vpolytope$new(V) +A = matrix(c(2,3,-1,7,0,0),ncol = 2, nrow = 3, byrow = TRUE) +P = Vpolytope(V = A) vol = exact_vol(P) } diff --git a/man/file_to_polytope.Rd b/man/file_to_polytope.Rd deleted file mode 100644 index 8ec843d5..00000000 --- a/man/file_to_polytope.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/file_to_polytope.R -\name{file_to_polytope} -\alias{file_to_polytope} -\title{function to get an ine or an ext file and returns the corresponding polytope} -\usage{ -file_to_polytope(path, zonotope = FALSE) -} -\arguments{ -\item{path}{A string that containes the path to an ine or a ext file. The ine file desrcibes a H-polytope and ext file describes a V-polytope or a zonotope.} - -\item{zonotope}{A boolean parameter. It has to be TRUE when the path leads to an .ext file that describes a zonotope.} -} -\value{ -A polytope class. If the path corresponds to an ine file then the return value represents a H-polytope. If it corresponds to an ext file the return value represents a V-polytope (default choice) or a zonotope if the second argument is TRUE. -} -\description{ -For an ".ine" file it generates the corresponding H-polytope. For an ".ext" file it generates the corresponding V-polytope or zonotope. -For more details on those file formats see \url{https://github.com/GeomScale/volume_approximation/blob/develop/doc/cpp_interface.md#polytope-input}. -} diff --git a/man/gen_cross.Rd b/man/gen_cross.Rd index 53e84c78..ea041442 100644 --- a/man/gen_cross.Rd +++ b/man/gen_cross.Rd @@ -4,12 +4,12 @@ \alias{gen_cross} \title{Generator function for cross polytopes} \usage{ -gen_cross(dimension, representation) +gen_cross(dimension, representation = "H") } \arguments{ \item{dimension}{The dimension of the cross polytope.} -\item{representation}{A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation.} +\item{representation}{A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. Default valus is 'H'.} } \value{ A polytope class representing a cross polytope in H- or V-representation. diff --git a/man/gen_cube.Rd b/man/gen_cube.Rd index efb486d6..dc97ae5a 100644 --- a/man/gen_cube.Rd +++ b/man/gen_cube.Rd @@ -4,12 +4,12 @@ \alias{gen_cube} \title{Generator function for hypercubes} \usage{ -gen_cube(dimension, representation) +gen_cube(dimension, representation = "H") } \arguments{ \item{dimension}{The dimension of the hypercube} -\item{representation}{A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation.} +\item{representation}{A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. Default valus is 'H'.} } \value{ A polytope class representing the unit \eqn{d}-dimensional hypercube in H- or V-representation. diff --git a/man/gen_rand_hpoly.Rd b/man/gen_rand_hpoly.Rd index e4db1c9e..85dd1757 100644 --- a/man/gen_rand_hpoly.Rd +++ b/man/gen_rand_hpoly.Rd @@ -4,14 +4,18 @@ \alias{gen_rand_hpoly} \title{Generator function for random H-polytopes} \usage{ -gen_rand_hpoly(dimension, nfacets, seed = NULL) +gen_rand_hpoly(dimension, nfacets, generator = list(constants = "sphere")) } \arguments{ \item{dimension}{The dimension of the convex polytope.} \item{nfacets}{The number of the facets.} -\item{seed}{Optional. A fixed seed for the generator.} +\item{generator}{A list that could contain two elements. +\itemize{ +\item{constants }{ To declare how to set the constants \eqn{b_i} for each facets: (i) 'sphere', each hyperplane is tangent to the hypersphere of radius 10, (ii) 'uniform' for each \eqn{b_i} the generator picks a uniform number from \eqn{(0,1)}. The defalut value is 'sphere'.} +\item{seed }{ Optional. A fixed seed for the number generator.} +}} } \value{ A polytope class representing a H-polytope. diff --git a/man/gen_rand_vpoly.Rd b/man/gen_rand_vpoly.Rd index e2c3c097..e34125a5 100644 --- a/man/gen_rand_vpoly.Rd +++ b/man/gen_rand_vpoly.Rd @@ -4,16 +4,18 @@ \alias{gen_rand_vpoly} \title{Generator function for random V-polytopes} \usage{ -gen_rand_vpoly(dimension, nvertices, generator = NULL, seed = NULL) +gen_rand_vpoly(dimension, nvertices, generator = list(body = "sphere")) } \arguments{ \item{dimension}{The dimension of the convex polytope.} \item{nvertices}{The number of the vertices.} -\item{generator}{The body that the generator samples uniformly the vertices from: (a) 'cube' or (b) 'sphere'.} - -\item{seed}{Optional. A fixed seed for the generator.} +\item{generator}{A list that could contain two elements. +\itemize{ +\item{body }{ the body that the generator samples uniformly the vertices from: (i) 'cube' or (ii) 'sphere', the default value is 'sphere'.} +\item{seed }{ Optional. A fixed seed for the number generator.} +}} } \value{ A polytope class representing a V-polytope. diff --git a/man/gen_rand_zonotope.Rd b/man/gen_rand_zonotope.Rd index ab971d35..41497ef2 100644 --- a/man/gen_rand_zonotope.Rd +++ b/man/gen_rand_zonotope.Rd @@ -4,16 +4,22 @@ \alias{gen_rand_zonotope} \title{Generator function for zonotopes} \usage{ -gen_rand_zonotope(dimension, nsegments, generator = NULL, seed = NULL) +gen_rand_zonotope( + dimension, + nsegments, + generator = list(distribution = "uniform") +) } \arguments{ \item{dimension}{The dimension of the zonotope.} \item{nsegments}{The number of segments that generate the zonotope.} -\item{generator}{The distribution to pick the length of each segment from \eqn{[0,100]}: (a) 'uniform', (b) 'gaussian' or (c) 'exponential'.} - -\item{seed}{Optional. A fixed seed for the generator.} +\item{generator}{A list that could contain two elements. +\itemize{ +\item{distribution }{ the distribution to pick the length of each segment from \eqn{[0,100]}: (i) 'uniform', (ii) 'gaussian' or (iii) 'exponential', the default value is 'uniform.} +\item {seed }{ Optional. A fixed seed for the number generator.} +}} } \value{ A polytope class representing a zonotope. diff --git a/man/gen_simplex.Rd b/man/gen_simplex.Rd index bed04a3a..0bbcb155 100644 --- a/man/gen_simplex.Rd +++ b/man/gen_simplex.Rd @@ -4,12 +4,12 @@ \alias{gen_simplex} \title{Generator function for simplices} \usage{ -gen_simplex(dimension, representation) +gen_simplex(dimension, representation = "H") } \arguments{ \item{dimension}{The dimension of the unit simplex.} -\item{representation}{A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation.} +\item{representation}{A string to declare the representation. It has to be \code{'H'} for H-representation or \code{'V'} for V-representation. Default valus is 'H'.} } \value{ A polytope class representing the \eqn{d}-dimensional unit simplex in H- or V-representation. diff --git a/man/load_sdpa_format_file.Rd b/man/load_sdpa_format_file.Rd new file mode 100644 index 00000000..349c0dfc --- /dev/null +++ b/man/load_sdpa_format_file.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{load_sdpa_format_file} +\alias{load_sdpa_format_file} +\title{An internal Rccp function to read a SDPA format file} +\usage{ +load_sdpa_format_file(input_file = NULL) +} +\arguments{ +\item{input_file}{Name of the input file} +} +\value{ +A list with two named items: an item "matrices" which is a list of the matrices and an vector "objFunction" +} +\description{ +An internal Rccp function to read a SDPA format file +} +\keyword{internal} diff --git a/man/readSdpaFormatFile.Rd b/man/read_sdpa_format_file.Rd similarity index 81% rename from man/readSdpaFormatFile.Rd rename to man/read_sdpa_format_file.Rd index 878a1196..50ca8ae2 100644 --- a/man/readSdpaFormatFile.Rd +++ b/man/read_sdpa_format_file.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/read_sdpa_file.R -\name{readSdpaFormatFile} -\alias{readSdpaFormatFile} +\name{read_sdpa_format_file} +\alias{read_sdpa_format_file} \title{Read a SDPA format file} \usage{ -readSdpaFormatFile(path) +read_sdpa_format_file(path) } \arguments{ \item{path}{Name of the input file} @@ -18,7 +18,7 @@ the linear matrix inequality in the input file, and the objective function. } \examples{ path = system.file('extdata', package = 'volesti') -l = readSdpaFormatFile(paste0(path,'/sdpa_n2m3.txt')) +l = read_sdpa_format_file(paste0(path,'/sdpa_n2m3.txt')) Spectrahedron = l$spectrahedron objFunction = l$objFunction } diff --git a/man/rotate_polytope.Rd b/man/rotate_polytope.Rd index 70c9c3ad..1368e515 100644 --- a/man/rotate_polytope.Rd +++ b/man/rotate_polytope.Rd @@ -4,14 +4,12 @@ \alias{rotate_polytope} \title{Apply a random rotation to a convex polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes)} \usage{ -rotate_polytope(P, T = NULL, seed = NULL) +rotate_polytope(P, rotation = list()) } \arguments{ \item{P}{A convex polytope. It is an object from class (a) Hpolytope, (b) Vpolytope, (c) Zonotope, (d) intersection of two V-polytopes.} -\item{T}{Optional. A \eqn{d\times d} rotation matrix.} - -\item{seed}{Optional. A fixed seed for the random linear map generator.} +\item{rotation}{A list that contains (a) the rotation matrix T and (b) the 'seed' to set a spesific seed for the number generator.} } \value{ A list that contains the rotated polytope and the matrix \eqn{T} of the linear transformation. @@ -20,7 +18,7 @@ A list that contains the rotated polytope and the matrix \eqn{T} of the linear t Given a convex H- or V- polytope or a zonotope or an intersection of two V-polytopes as input, this function applies (a) a random rotation or (b) a given rotation by an input matrix \eqn{T}. } \details{ -Let \eqn{P} be the given polytope and \eqn{Q} the rotated one and \eqn{T} be the matrix of the linear transformation. +Let \eqn{P} be the given polytope and \eqn{Q} the rotated one and \eqn{T} be the matrix of the linear transformation. \itemize{ \item{If \eqn{P} is in H-representation and \eqn{A} is the matrix that contains the normal vectors of the facets of \eqn{Q} then \eqn{AT} contains the normal vactors of the facets of \eqn{P}.} \item{If \eqn{P} is in V-representation and \eqn{V} is the matrix that contains column-wise the vertices of \eqn{Q} then \eqn{T^TV} contains the vertices of \eqn{P}.} @@ -30,7 +28,7 @@ Let \eqn{P} be the given polytope and \eqn{Q} the rotated one and \eqn{T} be the } \examples{ # rotate a H-polytope (2d unit simplex) -P = gen_simplex(2,'H') +P = gen_simplex(2, 'H') poly_matrix_list = rotate_polytope(P) # rotate a V-polytope (3d cube) @@ -38,6 +36,6 @@ P = gen_cube(3, 'V') poly_matrix_list = rotate_polytope(P) # rotate a 5-dimensional zonotope defined by the Minkowski sum of 15 segments -Z = gen_rand_zonotope(3,6) +Z = gen_rand_zonotope(3, 6) poly_matrix_list = rotate_polytope(Z) } diff --git a/man/round_polytope.Rd b/man/round_polytope.Rd index 787e40fb..04b91bf3 100644 --- a/man/round_polytope.Rd +++ b/man/round_polytope.Rd @@ -4,14 +4,16 @@ \alias{round_polytope} \title{Apply rounding to a convex polytope (H-polytope, V-polytope or a zonotope)} \usage{ -round_polytope(P, method = NULL, seed = NULL) +round_polytope(P, settings = list()) } \arguments{ \item{P}{A convex polytope. It is an object from class (a) Hpolytope or (b) Vpolytope or (c) Zonotope.} -\item{method}{Optional. The method to use for rounding, a) \code{'min_ellipsoid'} for the method based on mimimmum volume enclosing ellipsoid of a dataset, b) \code{'max_ellipsoid'} for the method based on maximum volume enclosed ellipsoid, (c) \code{'isotropy'} for the method based on svd decomposition. The default method is \code{'mee'} for all the representations.} - -\item{seed}{Optional. A fixed seed for the number generator.} +\item{settings}{Optional. A list of settings. +\itemize{ +\item{\code{method} }{ The method to use for rounding, a) \code{'min_ellipsoid'} for the method based on mimimmum volume enclosing ellipsoid of a dataset, b) \code{'max_ellipsoid'} for the method based on maximum volume enclosed ellipsoid, (c) \code{'isotropy'} for the method based on svd decomposition. The default method is \code{'mee'} for all the representations.} +\item{\code{seed} }{ Optional. A fixed seed for the number generator.} +}} } \value{ A list with 4 elements: (a) a polytope of the same class as the input polytope class and (b) the element "T" which is the matrix of the inverse linear transformation that is applied on the input polytope, (c) the element "shift" which is the opposite vector of that which has shifted the input polytope, (d) the element "round_value" which is the determinant of the square matrix of the linear transformation that is applied on the input polytope. diff --git a/man/sample_points.Rd b/man/sample_points.Rd index f5ea6f2b..ed0cc961 100644 --- a/man/sample_points.Rd +++ b/man/sample_points.Rd @@ -13,25 +13,26 @@ sample_points(P, n, random_walk = NULL, distribution = NULL, seed = NULL) \item{random_walk}{Optional. A list that declares the random walk and some related parameters as follows: \itemize{ -\item{\code{walk} }{ A string to declare the random walk: i) \code{'CDHR'} for Coordinate Directions Hit-and-Run, ii) \code{'RDHR'} for Random Directions Hit-and-Run, iii) \code{'BaW'} for Ball Walk, iv) \code{'BiW'} for Billiard walk, v) \code{'dikin'} for dikin walk, vi) \code{'vaidya'} for vaidya walk, vii) \code{'john'} for john walk, viii) \code{'BCDHR'} boundary sampling by keeping the extreme points of CDHR or ix) \code{'BRDHR'} boundary sampling by keeping the extreme points of RDHR x) \code{'HMC'} for Hamiltonian Monte Carlo (logconcave) xi) \code{'ULD'} for Underdamped Langevin Dynamics using the Randomized Midpoint Method. The default walk is \code{'aBiW'} for the uniform distribution or \code{'CDHR'} for the Gaussian distribution and H-polytopes and \code{'BiW'} or \code{'RDHR'} for the same distributions and V-polytopes and zonotopes.} +\item{\code{walk} }{ A string to declare the random walk: i) \code{'CDHR'} for Coordinate Directions Hit-and-Run, ii) \code{'RDHR'} for Random Directions Hit-and-Run, iii) \code{'BaW'} for Ball Walk, iv) \code{'BiW'} for Billiard walk, v) \code{'dikin'} for dikin walk, vi) \code{'vaidya'} for vaidya walk, vii) \code{'john'} for john walk, viii) \code{'BCDHR'} boundary sampling by keeping the extreme points of CDHR or ix) \code{'BRDHR'} boundary sampling by keeping the extreme points of RDHR x) \code{'HMC'} for Hamiltonian Monte Carlo (logconcave densities) xi) \code{'ULD'} for Underdamped Langevin Dynamics using the Randomized Midpoint Method xii) \code{'ExactHMC'} for exact Hamiltonian Monte Carlo with reflections (spherical Gaussian or exponential distribution). The default walk is \code{'aBiW'} for the uniform distribution or \code{'CDHR'} for the Gaussian distribution and H-polytopes and \code{'BiW'} or \code{'RDHR'} for the same distributions and V-polytopes and zonotopes.} \item{\code{walk_length} }{ The number of the steps per generated point for the random walk. The default value is \eqn{1}.} \item{\code{nburns} }{ The number of points to burn before start sampling. The default value is \eqn{1}.} \item{\code{starting_point} }{ A \eqn{d}-dimensional numerical vector that declares a starting point in the interior of the polytope for the random walk. The default choice is the center of the ball as that one computed by the function \code{inner_ball()}.} \item{\code{BaW_rad} }{ The radius for the ball walk.} \item{\code{L} }{ The maximum length of the billiard trajectory or the radius for the step of dikin, vaidya or john walk.} -\item{\code{solver}} {Specify ODE solver for logconcave sampling. Options are i) leapfrog, ii) euler iii) runge-kutta iv) richardson} -\item{\code{step_size} {Optionally chosen step size for logconcave sampling. Defaults to a theoretical value if not provided.}} +\item{\code{solver} }{ Specify ODE solver for logconcave sampling. Options are i) leapfrog, ii) euler iii) runge-kutta iv) richardson} +\item{\code{step_size} }{ Optionally chosen step size for logconcave sampling. Defaults to a theoretical value if not provided.} }} \item{distribution}{Optional. A list that declares the target density and some related parameters as follows: \itemize{ -\item{\code{density} }{ A string: (a) \code{'uniform'} for the uniform distribution or b) \code{'gaussian'} for the multidimensional spherical distribution. The default target distribution is uniform. c) Logconcave with form proportional to exp(-f(x)) where f(x) is L-smooth and m-strongly-convex. } -\item{\code{variance} }{ The variance of the multidimensional spherical gaussian. The default value is 1.} +\item{\code{density} }{ A string: (a) \code{'uniform'} for the uniform distribution or b) \code{'gaussian'} for the multidimensional spherical distribution c) \code{logconcave} with form proportional to exp(-f(x)) where f(x) is L-smooth and m-strongly-convex d) \code{'exponential'} for the exponential distribution. The default target distribution is the uniform distribution.} +\item{\code{variance} }{ The variance of the multidimensional spherical gaussian or the exponential distribution. The default value is 1.} \item{\code{mode} }{ A \eqn{d}-dimensional numerical vector that declares the mode of the Gaussian distribution. The default choice is the center of the as that one computed by the function \code{inner_ball()}.} -\item{\code{L_}} { Smoothness constant (for logconcave). } -\item{\code{m}} { Strong-convexity constant (for logconcave). } -\item{\code{negative_logprob}} { Negative log-probability (for logconcave). } -\item{\code{negative_logprob_gradient}} { Negative log-probability gradient (for logconcave). } +\item{\code{bias} }{ The bias vector for the exponential distribution. The default vector is \eqn{c_1 = 1} and \eqn{c_i = 0} for \eqn{i \neq 1}.} +\item{\code{L_} }{ Smoothness constant (for logconcave). } +\item{\code{m} }{ Strong-convexity constant (for logconcave). } +\item{\code{negative_logprob} }{ Negative log-probability (for logconcave). } +\item{\code{negative_logprob_gradient} }{ Negative log-probability gradient (for logconcave). } }} \item{seed}{Optional. A fixed seed for the number generator.} @@ -50,7 +51,7 @@ points = sample_points(P, n = 100, random_walk = list("walk" = "BaW", "walk_leng # gaussian distribution from the 2d unit simplex in H-representation with variance = 2 A = matrix(c(-1,0,0,-1,1,1), ncol=2, nrow=3, byrow=TRUE) b = c(0,0,1) -P = Hpolytope$new(A,b) +P = Hpolytope(A = A, b = b) points = sample_points(P, n = 100, distribution = list("density" = "gaussian", "variance" = 2)) # uniform points from the boundary of a 2-dimensional random H-polytope @@ -75,4 +76,7 @@ points = sample_points(P, n = 100, random_walk = list("walk" = "BRDHR")) \cite{Shen, Ruoqi, and Yin Tat Lee, \dQuote{"The randomized midpoint method for log-concave sampling.",} \emph{Advances in Neural Information Processing Systems}, 2019.} + +\cite{Augustin Chevallier, Sylvain Pion, Frederic Cazals, +\dQuote{"Hamiltonian Monte Carlo with boundary reflections, and application to polytope volume calculations,"} \emph{Research Report preprint hal-01919855}, 2018.} } diff --git a/man/volume.Rd b/man/volume.Rd index cd325c87..d00d797d 100644 --- a/man/volume.Rd +++ b/man/volume.Rd @@ -4,7 +4,7 @@ \alias{volume} \title{The main function for volume approximation of a convex Polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes). It returns a list with two elements: (a) the logarithm of the estimated volume and (b) the estimated volume} \usage{ -volume(P, settings = NULL, rounding = NULL, seed = NULL) +volume(P, settings = NULL, rounding = NULL) } \arguments{ \item{P}{A convex polytope. It is an object from class a) Hpolytope or b) Vpolytope or c) Zonotope or d) VpolytopeIntersection.} @@ -17,6 +17,7 @@ volume(P, settings = NULL, rounding = NULL, seed = NULL) \item{\code{walk_length} }{ An integer to set the number of the steps for the random walk. The default value is \eqn{\lfloor 10 + d/10\rfloor} for \code{'SOB'} and \eqn{1} otherwise.} \item{\code{win_len} }{ The length of the sliding window for CB or CG algorithm. The default value is \eqn{250} for CB with BiW and \eqn{400+3d^2} for CB and any other random walk and \eqn{500+4d^2} for CG.} \item{\code{hpoly} }{ A boolean parameter to use H-polytopes in MMC of CB algorithm when the input polytope is a zonotope. The default value is \code{TRUE} when the order of the zonotope is \eqn{<5}, otherwise it is \code{FALSE}.} +\item{\code{seed} }{ A fixed seed for the number generator.} }} \item{rounding}{Optional. A string parameter to request a rounding method to be applied in the input polytope before volume computation: a) \code{'min_ellipsoid'}, b) \code{'svd'}, c) \code{'max_ellipsoid'} and d) \code{'none'} for no rounding.} diff --git a/man/writeSdpaFormatFile.Rd b/man/writeSdpaFormatFile.Rd index 7c791dce..f70cac91 100644 --- a/man/writeSdpaFormatFile.Rd +++ b/man/writeSdpaFormatFile.Rd @@ -27,7 +27,7 @@ A0 = matrix(c(-1,0,0,0,-2,1,0,1,-2), nrow=3, ncol=3, byrow = TRUE) A1 = matrix(c(-1,0,0,0,0,1,0,1,0), nrow=3, ncol=3, byrow = TRUE) A2 = matrix(c(0,0,-1,0,0,0,-1,0,0), nrow=3, ncol=3, byrow = TRUE) lmi = list(A0, A1, A2) -S = Spectrahedron$new(lmi); +S = Spectrahedron(matrices = lmi) objFunction = c(1,1) writeSdpaFormatFile(S, objFunction, "output.txt") } diff --git a/man/write_sdpa_format_file.Rd b/man/write_sdpa_format_file.Rd new file mode 100644 index 00000000..23ec6714 --- /dev/null +++ b/man/write_sdpa_format_file.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{write_sdpa_format_file} +\alias{write_sdpa_format_file} +\title{Write a SDPA format file} +\usage{ +write_sdpa_format_file(spectrahedron, objective_function, output_file) +} +\arguments{ +\item{spectrahedron}{A spectrahedron in n dimensions; must be an object of class Spectrahedron} + +\item{objective_function}{A numerical vector of length n} + +\item{output_file}{Name of the output file} +} +\description{ +Outputs a spectrahedron (the matrices defining a linear matrix inequality) and a vector (the objective function) +to a SDPA format file. +} +\examples{ +\donttest{ +A0 = matrix(c(-1,0,0,0,-2,1,0,1,-2), nrow=3, ncol=3, byrow = TRUE) +A1 = matrix(c(-1,0,0,0,0,1,0,1,0), nrow=3, ncol=3, byrow = TRUE) +A2 = matrix(c(0,0,-1,0,0,0,-1,0,0), nrow=3, ncol=3, byrow = TRUE) +lmi = list(A0, A1, A2) +S = Spectrahedron(matrices = lmi) +objFunction = c(1,1) +write_sdpa_format_file(S, objFunction, "output.txt") +} +} diff --git a/man/zonotope_approximation.Rd b/man/zonotope_approximation.Rd index 152a0b0a..b06743a6 100644 --- a/man/zonotope_approximation.Rd +++ b/man/zonotope_approximation.Rd @@ -4,7 +4,11 @@ \alias{zonotope_approximation} \title{A function to over-approximate a zonotope with PCA method and to evaluate the approximation by computing a ratio of fitness.} \usage{ -zonotope_approximation(Z, fit_ratio = NULL, settings = NULL, seed = NULL) +zonotope_approximation( + Z, + fit_ratio = FALSE, + settings = list(error = 0.1, walk_length = 1, win_len = 250, hpoly = FALSE) +) } \arguments{ \item{Z}{A zonotope.} @@ -15,11 +19,10 @@ zonotope_approximation(Z, fit_ratio = NULL, settings = NULL, seed = NULL) \itemize{ \item{\code{error} }{ A numeric value to set the upper bound for the approximation error. The default value is \eqn{0.1}.} \item{\code{walk_length} }{ An integer to set the number of the steps for the random walk. The default value is \eqn{1}.} -\item{\code{win_len} }{ The length of the sliding window for CB algorithm. The default value is \eqn{200}.} +\item{\code{win_len} }{ The length of the sliding window for CB algorithm. The default value is \eqn{250}.} \item{\code{hpoly} }{ A boolean parameter to use H-polytopes in MMC of CB algorithm. The default value is \code{TRUE} when the order of the zonotope is \eqn{<5}, otherwise it is \code{FALSE}.} +\item{\code{seed} }{ Optional. A fixed seed for the number generator.} }} - -\item{seed}{Optional. A fixed seed for the number generator.} } \value{ A list that contains the approximation body in H-representation and the ratio of fitness @@ -29,7 +32,7 @@ For the evaluation of the PCA method the exact volume of the approximation body } \examples{ # over-approximate a 2-dimensional zonotope with 10 generators and compute the ratio of fitness -Z = gen_rand_zonotope(2,12) +Z = gen_rand_zonotope(2, 10) retList = zonotope_approximation(Z = Z) } diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index bc377c76..93e873c4 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -52,12 +52,12 @@ BEGIN_RCPP END_RCPP } // exact_vol -double exact_vol(Rcpp::Nullable P); +double exact_vol(Rcpp::Reference P); RcppExport SEXP _volesti_exact_vol(SEXP PSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< Rcpp::Nullable >::type P(PSEXP); + Rcpp::traits::input_parameter< Rcpp::Reference >::type P(PSEXP); rcpp_result_gen = Rcpp::wrap(exact_vol(P)); return rcpp_result_gen; END_RCPP @@ -99,6 +99,17 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } +// load_sdpa_format_file +Rcpp::List load_sdpa_format_file(Rcpp::Nullable input_file); +RcppExport SEXP _volesti_load_sdpa_format_file(SEXP input_fileSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< Rcpp::Nullable >::type input_file(input_fileSEXP); + rcpp_result_gen = Rcpp::wrap(load_sdpa_format_file(input_file)); + return rcpp_result_gen; +END_RCPP +} // ode_solve Rcpp::List ode_solve(Rcpp::Nullable n, Rcpp::Nullable step_size, Rcpp::Nullable order, Rcpp::Nullable dimension, Rcpp::Nullable initial_time, Rcpp::Function F, Rcpp::Nullable method, Rcpp::Nullable domains, Rcpp::Nullable initial_conditions); RcppExport SEXP _volesti_ode_solve(SEXP nSEXP, SEXP step_sizeSEXP, SEXP orderSEXP, SEXP dimensionSEXP, SEXP initial_timeSEXP, SEXP FSEXP, SEXP methodSEXP, SEXP domainsSEXP, SEXP initial_conditionsSEXP) { @@ -198,12 +209,12 @@ BEGIN_RCPP END_RCPP } // sample_points -Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, Rcpp::Nullable n, Rcpp::Nullable random_walk, Rcpp::Nullable distribution, Rcpp::Nullable seed); +Rcpp::NumericMatrix sample_points(Rcpp::Reference P, Rcpp::Nullable n, Rcpp::Nullable random_walk, Rcpp::Nullable distribution, Rcpp::Nullable seed); RcppExport SEXP _volesti_sample_points(SEXP PSEXP, SEXP nSEXP, SEXP random_walkSEXP, SEXP distributionSEXP, SEXP seedSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< Rcpp::Nullable >::type P(PSEXP); + Rcpp::traits::input_parameter< Rcpp::Reference >::type P(PSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type n(nSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type random_walk(random_walkSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type distribution(distributionSEXP); @@ -213,11 +224,11 @@ BEGIN_RCPP END_RCPP } // writeSdpaFormatFile -void writeSdpaFormatFile(Rcpp::Nullable spectrahedron, Rcpp::Nullable objectiveFunction, Rcpp::Nullable outputFile); +void writeSdpaFormatFile(Rcpp::Reference spectrahedron, Rcpp::Nullable objectiveFunction, Rcpp::Nullable outputFile); RcppExport SEXP _volesti_writeSdpaFormatFile(SEXP spectrahedronSEXP, SEXP objectiveFunctionSEXP, SEXP outputFileSEXP) { BEGIN_RCPP Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< Rcpp::Nullable >::type spectrahedron(spectrahedronSEXP); + Rcpp::traits::input_parameter< Rcpp::Reference >::type spectrahedron(spectrahedronSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type objectiveFunction(objectiveFunctionSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type outputFile(outputFileSEXP); writeSdpaFormatFile(spectrahedron, objectiveFunction, outputFile); @@ -236,19 +247,30 @@ BEGIN_RCPP END_RCPP } // volume -Rcpp::List volume(Rcpp::Reference P, Rcpp::Nullable settings, Rcpp::Nullable rounding, Rcpp::Nullable seed); -RcppExport SEXP _volesti_volume(SEXP PSEXP, SEXP settingsSEXP, SEXP roundingSEXP, SEXP seedSEXP) { +Rcpp::List volume(Rcpp::Reference P, Rcpp::Nullable settings, Rcpp::Nullable rounding); +RcppExport SEXP _volesti_volume(SEXP PSEXP, SEXP settingsSEXP, SEXP roundingSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::Reference >::type P(PSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type settings(settingsSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type rounding(roundingSEXP); - Rcpp::traits::input_parameter< Rcpp::Nullable >::type seed(seedSEXP); - rcpp_result_gen = Rcpp::wrap(volume(P, settings, rounding, seed)); + rcpp_result_gen = Rcpp::wrap(volume(P, settings, rounding)); return rcpp_result_gen; END_RCPP } +// write_sdpa_format_file +void write_sdpa_format_file(Rcpp::Reference spectrahedron, Rcpp::NumericVector objective_function, std::string output_file); +RcppExport SEXP _volesti_write_sdpa_format_file(SEXP spectrahedronSEXP, SEXP objective_functionSEXP, SEXP output_fileSEXP) { +BEGIN_RCPP + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< Rcpp::Reference >::type spectrahedron(spectrahedronSEXP); + Rcpp::traits::input_parameter< Rcpp::NumericVector >::type objective_function(objective_functionSEXP); + Rcpp::traits::input_parameter< std::string >::type output_file(output_fileSEXP); + write_sdpa_format_file(spectrahedron, objective_function, output_file); + return R_NilValue; +END_RCPP +} // zono_approx Rcpp::List zono_approx(Rcpp::Reference Z, Rcpp::Nullable fit_ratio, Rcpp::Nullable settings, Rcpp::Nullable seed); RcppExport SEXP _volesti_zono_approx(SEXP ZSEXP, SEXP fit_ratioSEXP, SEXP settingsSEXP, SEXP seedSEXP) { @@ -272,6 +294,7 @@ static const R_CallMethodDef CallEntries[] = { {"_volesti_frustum_of_simplex", (DL_FUNC) &_volesti_frustum_of_simplex, 2}, {"_volesti_geweke", (DL_FUNC) &_volesti_geweke, 3}, {"_volesti_inner_ball", (DL_FUNC) &_volesti_inner_ball, 2}, + {"_volesti_load_sdpa_format_file", (DL_FUNC) &_volesti_load_sdpa_format_file, 1}, {"_volesti_ode_solve", (DL_FUNC) &_volesti_ode_solve, 9}, {"_volesti_poly_gen", (DL_FUNC) &_volesti_poly_gen, 6}, {"_volesti_psrf_multivariate", (DL_FUNC) &_volesti_psrf_multivariate, 1}, @@ -282,7 +305,8 @@ static const R_CallMethodDef CallEntries[] = { {"_volesti_sample_points", (DL_FUNC) &_volesti_sample_points, 5}, {"_volesti_writeSdpaFormatFile", (DL_FUNC) &_volesti_writeSdpaFormatFile, 3}, {"_volesti_loadSdpaFormatFile", (DL_FUNC) &_volesti_loadSdpaFormatFile, 1}, - {"_volesti_volume", (DL_FUNC) &_volesti_volume, 4}, + {"_volesti_volume", (DL_FUNC) &_volesti_volume, 3}, + {"_volesti_write_sdpa_format_file", (DL_FUNC) &_volesti_write_sdpa_format_file, 3}, {"_volesti_zono_approx", (DL_FUNC) &_volesti_zono_approx, 4}, {NULL, NULL, 0} }; diff --git a/src/copula.cpp b/src/copula.cpp index 0e8069ea..8b9fd586 100644 --- a/src/copula.cpp +++ b/src/copula.cpp @@ -7,7 +7,6 @@ //Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. - #include #include #include diff --git a/src/exact_vol.cpp b/src/exact_vol.cpp index d60d8fa4..89bc3a36 100644 --- a/src/exact_vol.cpp +++ b/src/exact_vol.cpp @@ -43,8 +43,8 @@ FT factorial(FT n) //' vol = exact_vol(Z) //' //' \donttest{# compute the exact volume of a 2-d arbitrary simplex -//' V = matrix(c(2,3,-1,7,0,0),ncol = 2, nrow = 3, byrow = TRUE) -//' P = Vpolytope$new(V) +//' A = matrix(c(2,3,-1,7,0,0),ncol = 2, nrow = 3, byrow = TRUE) +//' P = Vpolytope(V = A) //' vol = exact_vol(P) //' } //' @@ -53,7 +53,7 @@ FT factorial(FT n) //' vol = exact_vol(P) //' @export // [[Rcpp::export]] -double exact_vol(Rcpp::Nullable P) { +double exact_vol(Rcpp::Reference P) { typedef double NT; typedef Cartesian Kernel; @@ -61,21 +61,38 @@ double exact_vol(Rcpp::Nullable P) { typedef Eigen::Matrix VT; typedef Eigen::Matrix MT; - if (NT(Rcpp::as(P).field("volume")) > 0.0) { - return NT(Rcpp::as(P).field("volume")); + if (NT(P.slot("volume")) > 0.0) { + return NT(P.slot("volume")); + } + + int type; + int dim; + std::string type_str = Rcpp::as(P.slot("type")); + + if (type_str.compare(std::string("Hpolytope")) == 0) { + dim = Rcpp::as(P.slot("A")).cols(); + type = 1; + } else if (type_str.compare(std::string("Vpolytope")) == 0) { + dim = Rcpp::as(P.slot("V")).cols(); + type = 2; + } else if (type_str.compare(std::string("Zonotope")) == 0) { + dim = Rcpp::as(P.slot("G")).cols(); + type = 3; + } else if (type_str.compare(std::string("VpolytopeIntersection")) == 0) { + dim = Rcpp::as(P.slot("V1")).cols(); + type = 4; + } else { + throw Rcpp::exception("Unknown polytope representation!"); } - int type = Rcpp::as(P).field("type"), dim; NT vol; if (type == 2) { - dim = Rcpp::as(P).field("dimension"); - - if (Rcpp::as(Rcpp::as(P).field("V")).rows() == - Rcpp::as(Rcpp::as(P).field("V")).cols() + 1) { + if (Rcpp::as(P.slot("V")).rows() == + Rcpp::as(P.slot("V")).cols() + 1) { - MT V = Rcpp::as(Rcpp::as(P).field("V")).transpose(), V2(dim,dim); + MT V = Rcpp::as(P.slot("V")).transpose(), V2(dim,dim); VT v0 = V.col(dim); for (int i = 0; i < dim; ++i) { @@ -90,10 +107,8 @@ double exact_vol(Rcpp::Nullable P) { } else if (type == 3) { typedef Zonotope zonotope; - dim = Rcpp::as(P).field("dimension"); - zonotope ZP(dim, Rcpp::as(Rcpp::as(P).field("G")), - VT::Ones(Rcpp::as(Rcpp::as(P).field("G")).rows())); + zonotope ZP(dim, Rcpp::as(P.slot("G")), VT::Ones(Rcpp::as(P.slot("G")).rows())); vol = exact_zonotope_vol(ZP); } else { diff --git a/src/inner_ball.cpp b/src/inner_ball.cpp index 8472ad3b..c69884e6 100644 --- a/src/inner_ball.cpp +++ b/src/inner_ball.cpp @@ -32,7 +32,7 @@ //' ball_vec = inner_ball(P, lpsolve = TRUE) //' @export // [[Rcpp::export]] -Rcpp::NumericVector inner_ball(Rcpp::Reference P, +Rcpp::NumericVector inner_ball(Rcpp::Reference P, Rcpp::Nullable lpsolve = R_NilValue) { typedef double NT; @@ -45,15 +45,34 @@ Rcpp::NumericVector inner_ball(Rcpp::Reference P, typedef IntersectionOfVpoly InterVP; typedef Eigen::Matrix VT; typedef Eigen::Matrix MT; - unsigned int n = P.field("dimension"), type = P.field("type"); std::pair InnerBall; bool lp_solve = (lpsolve.isNotNull()) ? Rcpp::as(lpsolve) : false; + unsigned int n; + unsigned int type; + std::string type_str = Rcpp::as(P.slot("type")); + + if (type_str.compare(std::string("Hpolytope")) == 0) { + n = Rcpp::as(P.slot("A")).cols(); + type = 1; + } else if (type_str.compare(std::string("Vpolytope")) == 0) { + n = Rcpp::as(P.slot("V")).cols(); + type = 2; + } else if (type_str.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(P.slot("G")).cols(); + type = 3; + } else if (type_str.compare(std::string("VpolytopeIntersection")) == 0) { + n = Rcpp::as(P.slot("V1")).cols(); + type = 4; + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } + switch (type) { case 1: { // Hpolytope - Hpolytope HP(n, Rcpp::as(P.field("A")), Rcpp::as(P.field("b"))); + Hpolytope HP(n, Rcpp::as(P.slot("A")), Rcpp::as(P.slot("b"))); if (lp_solve) { InnerBall = ComputeChebychevBall(HP.get_mat(), HP.get_vec()); } else { @@ -64,22 +83,22 @@ Rcpp::NumericVector inner_ball(Rcpp::Reference P, } case 2: { // Vpolytope - Vpolytope VP(n, Rcpp::as(P.field("V")), VT::Ones(Rcpp::as(P.field("V")).rows())); + Vpolytope VP(n, Rcpp::as(P.slot("V")), VT::Ones(Rcpp::as(P.slot("V")).rows())); InnerBall = VP.ComputeInnerBall(); if (InnerBall.second < 0.0) throw Rcpp::exception("Unable to compute a feasible point."); break; } case 3: { // Zonotope - zonotope ZP(n, Rcpp::as(P.field("G")), VT::Ones(Rcpp::as(P.field("G")).rows())); + zonotope ZP(n, Rcpp::as(P.slot("G")), VT::Ones(Rcpp::as(P.slot("G")).rows())); InnerBall = ZP.ComputeInnerBall(); if (InnerBall.second < 0.0) throw Rcpp::exception("Unable to compute a feasible point."); break; } case 4: { // Intersection of two V-polytopes - Vpolytope VP1(n, Rcpp::as(P.field("V1")), VT::Ones(Rcpp::as(P.field("V1")).rows())); - Vpolytope VP2(n, Rcpp::as(P.field("V2")), VT::Ones(Rcpp::as(P.field("V2")).rows())); + Vpolytope VP1(n, Rcpp::as(P.slot("V1")), VT::Ones(Rcpp::as(P.slot("V1")).rows())); + Vpolytope VP2(n, Rcpp::as(P.slot("V2")), VT::Ones(Rcpp::as(P.slot("V2")).rows())); InterVP VPcVP(VP1, VP2); if (!VPcVP.is_feasible()) throw Rcpp::exception("Empty set!"); InnerBall = VPcVP.ComputeInnerBall(); diff --git a/src/load_sdpa_format_file.cpp b/src/load_sdpa_format_file.cpp new file mode 100644 index 00000000..bb1e00cd --- /dev/null +++ b/src/load_sdpa_format_file.cpp @@ -0,0 +1,62 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#include +#include +#include +#include "cartesian_geom/cartesian_kernel.h" +#include +#include +#include +#include +#include "convex_bodies/spectrahedra/LMI.h" +#include "convex_bodies/spectrahedra/spectrahedron.h" +#include "SDPAFormatManager.h" + +//' An internal Rccp function to read a SDPA format file +//' +//' @param input_file Name of the input file +//' +//' @keywords internal +//' +//' @return A list with two named items: an item "matrices" which is a list of the matrices and an vector "objFunction" +// [[Rcpp::export]] +Rcpp::List load_sdpa_format_file(Rcpp::Nullable input_file = R_NilValue) { + + typedef double NT; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef boost::mt19937 RNGType; + typedef LMI LMI; + typedef Spectrahedron Spectrahedron; + + Spectrahedron _spectrahedron; + Point c; + + // open stream + std::ifstream os; + os.open(Rcpp::as (input_file),std::ifstream::in); + + // read file + SdpaFormatManager sdpaFormatManager; + sdpaFormatManager.loadSDPAFormatFile(os, _spectrahedron, c); + + std::vector matrices = _spectrahedron.getLMI().getMatrices(); + + // return spectrahedron and objective function + Rcpp::List _matrices; + + for (auto matrix : matrices) + _matrices.push_back(Rcpp::wrap(matrix)); + + Rcpp::List retList = Rcpp::List::create(Rcpp::Named("matrices") = _matrices , Rcpp::_["objFunction"] = Rcpp::wrap(c.getCoefficients())); + return Rcpp::wrap(retList); +} \ No newline at end of file diff --git a/src/ode_solve.cpp b/src/ode_solve.cpp index 07766d33..af20bed4 100644 --- a/src/ode_solve.cpp +++ b/src/ode_solve.cpp @@ -151,10 +151,10 @@ Rcpp::List ode_solve(Rcpp::Nullable n, Hpolytope HP(dim, Rcpp::as( Rcpp::as(Rcpp::as(domains) - [domain_name.c_str()]).field("A")), + [domain_name.c_str()]).slot("A")), Rcpp::as( Rcpp::as(Rcpp::as(domains) - [domain_name.c_str()]).field("b")) + [domain_name.c_str()]).slot("b")) ); HP.normalize(); diff --git a/src/rotating.cpp b/src/rotating.cpp index 607c0092..ebb9d115 100644 --- a/src/rotating.cpp +++ b/src/rotating.cpp @@ -30,7 +30,8 @@ //' //' @return A matrix that describes the rotated polytope // [[Rcpp::export]] -Rcpp::NumericMatrix rotating (Rcpp::Reference P, Rcpp::Nullable T = R_NilValue, +Rcpp::NumericMatrix rotating (Rcpp::Reference P, + Rcpp::Nullable T = R_NilValue, Rcpp::Nullable seed = R_NilValue){ typedef double NT; @@ -46,7 +47,25 @@ Rcpp::NumericMatrix rotating (Rcpp::Reference P, Rcpp::Nullable(P.slot("type")); + + if (type_str.compare(std::string("Hpolytope")) == 0) { + n = Rcpp::as(P.slot("A")).cols(); + type = 1; + } else if (type_str.compare(std::string("Vpolytope")) == 0) { + n = Rcpp::as(P.slot("V")).cols(); + type = 2; + } else if (type_str.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(P.slot("G")).cols(); + type = 3; + } else if (type_str.compare(std::string("VpolytopeIntersection")) == 0) { + throw Rcpp::exception("volesti does not support roatation of this kind of representation."); + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } int seed_rcpp = (!seed.isNotNull()) ? std::chrono::system_clock::now() .time_since_epoch().count() @@ -55,7 +74,7 @@ Rcpp::NumericMatrix rotating (Rcpp::Reference P, Rcpp::Nullable(P.field("A")), Rcpp::as(P.field("b"))); + Hpolytope HP(n, Rcpp::as(P.slot("A")), Rcpp::as(P.slot("b"))); if (T.isNotNull()) { TransorfMat = Rcpp::as(T); HP.linear_transformIt(TransorfMat.inverse()); @@ -67,7 +86,7 @@ Rcpp::NumericMatrix rotating (Rcpp::Reference P, Rcpp::Nullable(P.field("V")), VT::Ones(Rcpp::as(P.field("V")).rows())); + Vpolytope VP(n, Rcpp::as(P.slot("V")), VT::Ones(Rcpp::as(P.slot("V")).rows())); if (T.isNotNull()) { TransorfMat = Rcpp::as(T); VP.linear_transformIt(TransorfMat.inverse()); @@ -79,7 +98,7 @@ Rcpp::NumericMatrix rotating (Rcpp::Reference P, Rcpp::Nullable(P.field("G")), VT::Ones(Rcpp::as(P.field("G")).rows())); + zonotope ZP(n, Rcpp::as(P.slot("G")), VT::Ones(Rcpp::as(P.slot("G")).rows())); if (T.isNotNull()) { TransorfMat = Rcpp::as(T); ZP.linear_transformIt(TransorfMat.inverse()); diff --git a/src/rounding.cpp b/src/rounding.cpp index 0b2cbe0f..5bb4977e 100644 --- a/src/rounding.cpp +++ b/src/rounding.cpp @@ -25,21 +25,21 @@ #include "preprocess/max_inscribed_ellipsoid_rounding.hpp" #include "extractMatPoly.h" -template +template < typename MT, - typename VT, - typename WalkType, - typename Polytope, - typename Point, - typename NT, + typename VT, + typename WalkType, + typename Polytope, + typename Point, + typename NT, typename RNGType > std::tuple apply_rounding(Polytope &P, std::string const& method_rcpp, unsigned int const& walkL, - std::pair &InnerBall, - RNGType &rng) + std::pair &InnerBall, + RNGType &rng) { std::tuple round_res; if (method_rcpp.compare(std::string("min_ellipsoid")) == 0) { @@ -62,7 +62,7 @@ std::tuple apply_rounding(Polytope &P, //' //' @return A numerical matrix that describes the rounded polytope, a numerical matrix of the inverse linear transofmation that is applied on the input polytope, the numerical vector the the input polytope is shifted and the determinant of the matrix of the linear transformation that is applied on the input polytope. // [[Rcpp::export]] -Rcpp::List rounding (Rcpp::Reference P, +Rcpp::List rounding (Rcpp::Reference P, Rcpp::Nullable method = R_NilValue, Rcpp::Nullable seed = R_NilValue){ @@ -76,7 +76,27 @@ Rcpp::List rounding (Rcpp::Reference P, typedef Eigen::Matrix VT; typedef Eigen::Matrix MT; - unsigned int n = P.field("dimension"), walkL = 2, type = P.field("type"); + unsigned int n; + unsigned int walkL=2; + unsigned int type; + + std::string type_str = Rcpp::as(P.slot("type")); + + if (type_str.compare(std::string("Hpolytope")) == 0) { + n = Rcpp::as(P.slot("A")).cols(); + type = 1; + } else if (type_str.compare(std::string("Vpolytope")) == 0) { + n = Rcpp::as(P.slot("V")).cols(); + type = 2; + } else if (type_str.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(P.slot("G")).cols(); + type = 3; + } else if (type_str.compare(std::string("VpolytopeIntersection")) == 0) { + throw Rcpp::exception("volesti does not support roatation of this kind of representation."); + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } + std::string method_rcpp = std::string("isotropy"); if(method.isNotNull()) { method_rcpp = Rcpp::as(method); @@ -98,11 +118,10 @@ Rcpp::List rounding (Rcpp::Reference P, switch (type) { case 1: { // Hpolytope - - if (Rcpp::as(P.field("Aeq")).rows() > 0) { - throw Rcpp::exception("volesti supports rounding for full dimensional polytopes"); - } - Hpolytope HP(n, Rcpp::as(P.field("A")), Rcpp::as(P.field("b"))); + //if (Rcpp::as(P.slot("Aeq")).rows() > 0) { + // throw Rcpp::exception("volesti supports rounding for full dimensional polytopes"); + // } + Hpolytope HP(n, Rcpp::as(P.slot("A")), Rcpp::as(P.slot("b"))); InnerBall = HP.ComputeInnerBall(); if (InnerBall.second < 0.0) throw Rcpp::exception("Unable to compute a feasible point."); if (method_rcpp.compare(std::string("max_ellipsoid")) == 0) { @@ -115,7 +134,7 @@ Rcpp::List rounding (Rcpp::Reference P, } case 2: { // Vpolytope - Vpolytope VP(n, Rcpp::as(P.field("V")), VT::Ones(Rcpp::as(P.field("V")).rows())); + Vpolytope VP(n, Rcpp::as(P.slot("V")), VT::Ones(Rcpp::as(P.slot("V")).rows())); InnerBall = VP.ComputeInnerBall(); if (InnerBall.second < 0.0) throw Rcpp::exception("Unable to compute a feasible point."); round_res = apply_rounding(VP, method_rcpp, walkL, InnerBall, rng); @@ -124,7 +143,7 @@ Rcpp::List rounding (Rcpp::Reference P, } case 3: { // Zonotope - zonotope ZP(n, Rcpp::as(P.field("G")), VT::Ones(Rcpp::as(P.field("G")).rows())); + zonotope ZP(n, Rcpp::as(P.slot("G")), VT::Ones(Rcpp::as(P.slot("G")).rows())); InnerBall = ZP.ComputeInnerBall(); if (InnerBall.second < 0.0) throw Rcpp::exception("Unable to compute a feasible point."); round_res = apply_rounding(ZP, method_rcpp, walkL, InnerBall, rng); diff --git a/src/sample_points.cpp b/src/sample_points.cpp index d03c9b33..10f6dcee 100644 --- a/src/sample_points.cpp +++ b/src/sample_points.cpp @@ -2,7 +2,7 @@ // VolEsti (volume computation and sampling library) -// Copyright (c) 2012-2021 Vissarion Fisikopoulos +// Copyright (c) 2012-2024 Vissarion Fisikopoulos // Copyright (c) 2018-2021 Apostolos Chalkis //Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 and 2019 program. @@ -251,7 +251,7 @@ void sample_from_polytope(Polytope &P, int type, RNGType &rng, PointList &randPo //' \item{\code{BaW_rad} }{ The radius for the ball walk.} //' \item{\code{L} }{ The maximum length of the billiard trajectory or the radius for the step of dikin, vaidya or john walk.} //' \item{\code{solver} }{ Specify ODE solver for logconcave sampling. Options are i) leapfrog, ii) euler iii) runge-kutta iv) richardson} -//' \item{\code{step_size }{ Optionally chosen step size for logconcave sampling. Defaults to a theoretical value if not provided.} +//' \item{\code{step_size} }{ Optionally chosen step size for logconcave sampling. Defaults to a theoretical value if not provided.} //' } //' @param distribution Optional. A list that declares the target density and some related parameters as follows: //' \itemize{ @@ -293,7 +293,7 @@ void sample_from_polytope(Polytope &P, int type, RNGType &rng, PointList &randPo //' # gaussian distribution from the 2d unit simplex in H-representation with variance = 2 //' A = matrix(c(-1,0,0,-1,1,1), ncol=2, nrow=3, byrow=TRUE) //' b = c(0,0,1) -//' P = Hpolytope$new(A,b) +//' P = Hpolytope(A = A, b = b) //' points = sample_points(P, n = 100, distribution = list("density" = "gaussian", "variance" = 2)) //' //' # uniform points from the boundary of a 2-dimensional random H-polytope @@ -304,7 +304,7 @@ void sample_from_polytope(Polytope &P, int type, RNGType &rng, PointList &randPo //' //' @export // [[Rcpp::export]] -Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, +Rcpp::NumericMatrix sample_points(Rcpp::Reference P, Rcpp::Nullable n, Rcpp::Nullable random_walk = R_NilValue, Rcpp::Nullable distribution = R_NilValue, @@ -321,15 +321,33 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, typedef Eigen::Matrix VT; typedef Eigen::Matrix MT; - unsigned int type = Rcpp::as(P).field("type"), dim = Rcpp::as(P).field("dimension"), - walkL = 1, numpoints, nburns = 0; - RcppFunctor::GradientFunctor *F = NULL; RcppFunctor::FunctionFunctor *f = NULL; GaussianFunctor::GradientFunctor *G = NULL; GaussianFunctor::FunctionFunctor *g = NULL; bool functor_defined = true; + unsigned int dim; + unsigned int walkL = 1; + unsigned int type; + + std::string type_str = Rcpp::as(P.slot("type")); + + if (type_str.compare(std::string("Hpolytope")) == 0) { + dim = Rcpp::as(P.slot("A")).cols(); + type = 1; + } else if (type_str.compare(std::string("Vpolytope")) == 0) { + dim = Rcpp::as(P.slot("V")).cols(); + type = 2; + } else if (type_str.compare(std::string("Zonotope")) == 0) { + dim = Rcpp::as(P.slot("G")).cols(); + type = 3; + } else if (type_str.compare(std::string("VpolytopeIntersection")) == 0) { + dim = Rcpp::as(P.slot("V1")).cols(); + type = 4; + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } RNGType rng(dim); if (seed.isNotNull()) { @@ -351,7 +369,7 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, std::pair InnerBall; Point mode(dim); - numpoints = Rcpp::as(n); + unsigned int numpoints = Rcpp::as(n); if (numpoints <= 0) throw Rcpp::exception("The number of samples has to be a positive integer!"); if (!distribution.isNotNull() || !Rcpp::as(distribution).containsElementNamed("density")) { @@ -599,6 +617,7 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, } } + unsigned int nburns = 0; if (Rcpp::as(random_walk).containsElementNamed("nburns")) { nburns = Rcpp::as(Rcpp::as(random_walk)["nburns"]); if (nburns < 0) { @@ -609,8 +628,8 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, switch(type) { case 1: { // Hpolytope - Hpolytope HP(dim, Rcpp::as(Rcpp::as(P).field("A")), - Rcpp::as(Rcpp::as(P).field("b"))); + Hpolytope HP(dim, Rcpp::as(P.slot("A")), + Rcpp::as(P.slot("b"))); InnerBall = HP.ComputeInnerBall(); if (InnerBall.second < 0.0) throw Rcpp::exception("Unable to compute a feasible point."); @@ -637,8 +656,8 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, } case 2: { // Vpolytope - Vpolytope VP(dim, Rcpp::as(Rcpp::as(P).field("V")), - VT::Ones(Rcpp::as(Rcpp::as(P).field("V")).rows())); + Vpolytope VP(dim, Rcpp::as(P.slot("V")), + VT::Ones(Rcpp::as(P.slot("V")).rows())); InnerBall = VP.ComputeInnerBall(); if (InnerBall.second < 0.0) throw Rcpp::exception("Unable to compute a feasible point."); @@ -658,8 +677,8 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, } case 3: { // Zonotope - zonotope ZP(dim, Rcpp::as(Rcpp::as(P).field("G")), - VT::Ones(Rcpp::as(Rcpp::as(P).field("G")).rows())); + zonotope ZP(dim, Rcpp::as(P.slot("G")), + VT::Ones(Rcpp::as(P.slot("G")).rows())); InnerBall = ZP.ComputeInnerBall(); if (InnerBall.second < 0.0) throw Rcpp::exception("Unable to compute a feasible point."); @@ -679,10 +698,10 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable P, } case 4: { // Intersection of two V-polytopes - Vpolytope VP1(dim, Rcpp::as(Rcpp::as(P).field("V1")), - VT::Ones(Rcpp::as(Rcpp::as(P).field("V1")).rows())); - Vpolytope VP2(dim, Rcpp::as(Rcpp::as(P).field("V2")), - VT::Ones(Rcpp::as(Rcpp::as(P).field("V2")).rows())); + Vpolytope VP1(dim, Rcpp::as(P.slot("V1")), + VT::Ones(Rcpp::as(P.slot("V1")).rows())); + Vpolytope VP2(dim, Rcpp::as(P.slot("V2")), + VT::Ones(Rcpp::as(P.slot("V2")).rows())); InterVP VPcVP(VP1, VP2); if (!VPcVP.is_feasible()) throw Rcpp::exception("Empty set!"); diff --git a/src/spectrahedron.cpp b/src/spectrahedron.cpp index e7926597..f084fd65 100644 --- a/src/spectrahedron.cpp +++ b/src/spectrahedron.cpp @@ -35,13 +35,13 @@ //' A1 = matrix(c(-1,0,0,0,0,1,0,1,0), nrow=3, ncol=3, byrow = TRUE) //' A2 = matrix(c(0,0,-1,0,0,0,-1,0,0), nrow=3, ncol=3, byrow = TRUE) //' lmi = list(A0, A1, A2) -//' S = Spectrahedron$new(lmi); +//' S = Spectrahedron(matrices = lmi) //' objFunction = c(1,1) //' writeSdpaFormatFile(S, objFunction, "output.txt") //' } //' @export // [[Rcpp::export]] -void writeSdpaFormatFile(Rcpp::Nullable spectrahedron = R_NilValue, +void writeSdpaFormatFile(Rcpp::Reference spectrahedron = R_NilValue, Rcpp::Nullable objectiveFunction = R_NilValue, Rcpp::Nullable outputFile = R_NilValue) { @@ -54,7 +54,7 @@ void writeSdpaFormatFile(Rcpp::Nullable spectrahedron = R_NilVa typedef LMI LMI; typedef Spectrahedron Spectrahedron; - std::vector matrices = Rcpp::as > (Rcpp::as (spectrahedron).field("matrices")); + std::vector matrices = Rcpp::as > (Rcpp::as (spectrahedron).slot("matrices")); LMI lmi(matrices); Spectrahedron _spectrahedron(lmi); Point c(Rcpp::as (objectiveFunction)); diff --git a/src/volume.cpp b/src/volume.cpp index 0a47210a..49dd9960 100644 --- a/src/volume.cpp +++ b/src/volume.cpp @@ -2,7 +2,7 @@ // VolEsti (volume computation and sampling library) -// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2012-2024 Vissarion Fisikopoulos // Copyright (c) 2018-2020 Apostolos Chalkis //Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 and 2019 program. @@ -29,8 +29,8 @@ enum rounding_type {none, min_ellipsoid, max_ellipsoid, isotropy}; template std::pair generic_volume(Polytope& P, RNGType &rng, unsigned int walk_length, NT e, - volume_algorithms const& algo, unsigned int win_len, - rounding_type const& rounding, random_walks const& walk) + volume_algorithms const& algo, unsigned int win_len, + rounding_type const& rounding, random_walks const& walk) { typedef typename Polytope::MT MT; typedef typename Polytope::VT VT; @@ -181,6 +181,7 @@ std::pair generic_volume(Polytope& P, RNGType &rng, unsigned int //' \item{\code{walk_length} }{ An integer to set the number of the steps for the random walk. The default value is \eqn{\lfloor 10 + d/10\rfloor} for \code{'SOB'} and \eqn{1} otherwise.} //' \item{\code{win_len} }{ The length of the sliding window for CB or CG algorithm. The default value is \eqn{250} for CB with BiW and \eqn{400+3d^2} for CB and any other random walk and \eqn{500+4d^2} for CG.} //' \item{\code{hpoly} }{ A boolean parameter to use H-polytopes in MMC of CB algorithm when the input polytope is a zonotope. The default value is \code{TRUE} when the order of the zonotope is \eqn{<5}, otherwise it is \code{FALSE}.} +//' \item{\code{seed} }{ A fixed seed for the number generator.} //' } //' @param rounding Optional. A string parameter to request a rounding method to be applied in the input polytope before volume computation: a) \code{'min_ellipsoid'}, b) \code{'svd'}, c) \code{'max_ellipsoid'} and d) \code{'none'} for no rounding. //' @param seed Optional. A fixed seed for the number generator. @@ -210,9 +211,8 @@ std::pair generic_volume(Polytope& P, RNGType &rng, unsigned int //' @export // [[Rcpp::export]] Rcpp::List volume (Rcpp::Reference P, - Rcpp::Nullable settings = R_NilValue, - Rcpp::Nullable rounding = R_NilValue, - Rcpp::Nullable seed = R_NilValue) { + Rcpp::Nullable settings = R_NilValue, + Rcpp::Nullable rounding = R_NilValue) { typedef double NT; typedef Cartesian Kernel; @@ -224,12 +224,30 @@ Rcpp::List volume (Rcpp::Reference P, typedef IntersectionOfVpoly InterVP; typedef Eigen::Matrix VT; typedef Eigen::Matrix MT; - unsigned int n = P.field("dimension"), walkL, type = P.field("type"); + + unsigned int n, walkL, type; + std::string type_str = Rcpp::as(P.slot("type")); + + if (type_str.compare(std::string("Hpolytope")) == 0) { + n = Rcpp::as(P.slot("A")).cols(); + type = 1; + } else if (type_str.compare(std::string("Vpolytope")) == 0) { + n = Rcpp::as(P.slot("V")).cols(); + type = 2; + } else if (type_str.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(P.slot("G")).cols(); + type = 3; + } else if (type_str.compare(std::string("VpolytopeIntersection")) == 0) { + n = Rcpp::as(P.slot("V1")).cols(); + type = 4; + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } RNGType rng(n); - if (seed.isNotNull()) { - unsigned seed_rcpp = Rcpp::as(seed); - rng.set_seed(seed_rcpp); + if (Rcpp::as(settings).containsElementNamed("seed")) { + unsigned seed_tmp = Rcpp::as(Rcpp::as(settings)["seed"]); + rng.set_seed(seed_tmp); } bool round = false, hpoly = false; @@ -333,19 +351,19 @@ Rcpp::List volume (Rcpp::Reference P, switch(type) { case 1: { // Hpolytope - Hpolytope HP(n, Rcpp::as(P.field("A")), Rcpp::as(P.field("b"))); + Hpolytope HP(n, Rcpp::as(P.slot("A")), Rcpp::as(P.slot("b"))); pair_vol = generic_volume(HP, rng, walkL, e, algo, win_len, rounding_method, walk); break; } case 2: { // Vpolytope - Vpolytope VP(n, Rcpp::as(P.field("V")), VT::Ones(Rcpp::as(P.field("V")).rows())); + Vpolytope VP(n, Rcpp::as(P.slot("V")), VT::Ones(Rcpp::as(P.slot("V")).rows())); pair_vol = generic_volume(VP, rng, walkL, e, algo, win_len, rounding_method, walk); break; } case 3: { // Zonotope - zonotope ZP(n, Rcpp::as(P.field("G")), VT::Ones(Rcpp::as(P.field("G")).rows())); + zonotope ZP(n, Rcpp::as(P.slot("G")), VT::Ones(Rcpp::as(P.slot("G")).rows())); if (Rcpp::as(settings).containsElementNamed("hpoly")) { hpoly = Rcpp::as(Rcpp::as(settings)["hpoly"]); if (hpoly && (algo == CG || algo == SOB)) @@ -388,14 +406,15 @@ Rcpp::List volume (Rcpp::Reference P, } case 4: { // Intersection of two V-polytopes - Vpolytope VP1(n, Rcpp::as(P.field("V1")), VT::Ones(Rcpp::as(P.field("V1")).rows())); - Vpolytope VP2(n, Rcpp::as(P.field("V2")), VT::Ones(Rcpp::as(P.field("V2")).rows())); + Vpolytope VP1(n, Rcpp::as(P.slot("V1")), VT::Ones(Rcpp::as(P.slot("V1")).rows())); + Vpolytope VP2(n, Rcpp::as(P.slot("V2")), VT::Ones(Rcpp::as(P.slot("V2")).rows())); InterVP VPcVP; - if (!seed.isNotNull()) { - InterVP VPcVP(VP1, VP2); + if (Rcpp::as(settings).containsElementNamed("seed")) { + unsigned seed_tmp = Rcpp::as(Rcpp::as(settings)["seed"]); + rng.set_seed(seed_tmp); + InterVP VPcVP(VP1, VP2, seed_tmp); } else { - unsigned seed3 = Rcpp::as(seed); - InterVP VPcVP(VP1, VP2, seed3); + InterVP VPcVP(VP1, VP2); } if (!VPcVP.is_feasible()) throw Rcpp::exception("Empty set!"); pair_vol = generic_volume(VPcVP, rng, walkL, e, algo, win_len, rounding_method, walk); diff --git a/src/write_sdpa_format_file.cpp b/src/write_sdpa_format_file.cpp new file mode 100644 index 00000000..0be3f969 --- /dev/null +++ b/src/write_sdpa_format_file.cpp @@ -0,0 +1,69 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#include +#include +#include +#include "cartesian_geom/cartesian_kernel.h" +#include +#include +#include +#include +#include "convex_bodies/spectrahedra/LMI.h" +#include "convex_bodies/spectrahedra/spectrahedron.h" +#include "SDPAFormatManager.h" + +//' Write a SDPA format file +//' +//' Outputs a spectrahedron (the matrices defining a linear matrix inequality) and a vector (the objective function) +//' to a SDPA format file. +//' +//' @param spectrahedron A spectrahedron in n dimensions; must be an object of class Spectrahedron +//' @param objective_function A numerical vector of length n +//' @param output_file Name of the output file +//' +//' @examples +//' \donttest{ +//' A0 = matrix(c(-1,0,0,0,-2,1,0,1,-2), nrow=3, ncol=3, byrow = TRUE) +//' A1 = matrix(c(-1,0,0,0,0,1,0,1,0), nrow=3, ncol=3, byrow = TRUE) +//' A2 = matrix(c(0,0,-1,0,0,0,-1,0,0), nrow=3, ncol=3, byrow = TRUE) +//' lmi = list(A0, A1, A2) +//' S = Spectrahedron(matrices = lmi) +//' objFunction = c(1,1) +//' write_sdpa_format_file(S, objFunction, "output.txt") +//' } +//' @export +// [[Rcpp::export]] +void write_sdpa_format_file(Rcpp::Reference spectrahedron, + Rcpp::NumericVector objective_function, + std::string output_file) { + + typedef double NT; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef boost::mt19937 RNGType; + typedef LMI LMI; + typedef Spectrahedron Spectrahedron; + + std::vector matrices = Rcpp::as > (spectrahedron.slot("matrices")); + LMI lmi(matrices); + Spectrahedron _spectrahedron(lmi); + Point c(Rcpp::as (objective_function)); + + std::filebuf fb; + fb.open(output_file, std::ios::out); + std::ostream os(&fb); + + SdpaFormatManager sdpaFormatManager; + sdpaFormatManager.writeSDPAFormatFile(os, _spectrahedron, c); + + return; +} \ No newline at end of file diff --git a/src/zonotope_approximation.cpp b/src/zonotope_approximation.cpp index 237ec96e..7debd237 100644 --- a/src/zonotope_approximation.cpp +++ b/src/zonotope_approximation.cpp @@ -42,7 +42,17 @@ Rcpp::List zono_approx (Rcpp::Reference Z, typedef Zonotope zonotope; typedef Eigen::Matrix VT; typedef Eigen::Matrix MT; - int n = Rcpp::as(Z.field("dimension")), k = Rcpp::as(Z.field("G")).rows(), win_len = 200, walkL = 1; + + int k = Rcpp::as(Z.slot("G")).rows(), win_len = 250, walkL = 1; + + std::string type = Rcpp::as(Z.slot("type")); + + int n; + if (type.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(Z.slot("G")).cols(); + } else { + throw Rcpp::exception("This is not a zonotope."); + } RNGType rng(n); if (seed.isNotNull()) { @@ -54,10 +64,10 @@ Rcpp::List zono_approx (Rcpp::Reference Z, bool hpoly = false; MT X(n, 2 * k); - X << Rcpp::as(Z.field("G")).transpose(), -Rcpp::as(Z.field("G")).transpose(); + X << Rcpp::as(Z.slot("G")).transpose(), -Rcpp::as(Z.slot("G")).transpose(); Eigen::JacobiSVD svd(X * X.transpose(), Eigen::ComputeFullU | Eigen::ComputeFullV); MT G(k, 2 * n); - G << Rcpp::as(Z.field("G")) * svd.matrixU(), Rcpp::as(Z.field("G")) * svd.matrixU(); + G << Rcpp::as(Z.slot("G")) * svd.matrixU(), Rcpp::as(Z.slot("G")) * svd.matrixU(); VT Gred_ii = G.transpose().cwiseAbs().rowwise().sum(); MT A(n, 2 * n); A << -MT::Identity(n, n), MT::Identity(n, n); @@ -79,7 +89,7 @@ Rcpp::List zono_approx (Rcpp::Reference Z, win_len = (!Rcpp::as(settings).containsElementNamed("win_len")) ? 200 : Rcpp::as( Rcpp::as(settings)["win_len"]); - zonotope ZP(n, Rcpp::as(Z.field("G")), VT::Ones(Rcpp::as(Z.field("G")).rows())); + zonotope ZP(n, Rcpp::as(Z.slot("G")), VT::Ones(Rcpp::as(Z.slot("G")).rows())); if (Rcpp::as(settings).containsElementNamed("hpoly")) { hpoly = Rcpp::as(Rcpp::as(settings)["hpoly"]); From 9f8180515b878209dffd442b247f7067400c4c90 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Mon, 26 Feb 2024 18:01:13 +0200 Subject: [PATCH 02/17] Fix tests to match new interface --- tests/testthat/test_Hvol.R | 20 ++++++++++---------- tests/testthat/test_Vvol.R | 12 ++++++------ tests/testthat/test_Zvol.R | 14 +++++++------- tests/testthat/test_rounding.R | 24 ++++++++++++------------ 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tests/testthat/test_Hvol.R b/tests/testthat/test_Hvol.R index 061a33ac..16b657fd 100644 --- a/tests/testthat/test_Hvol.R +++ b/tests/testthat/test_Hvol.R @@ -4,15 +4,15 @@ library(volesti) Hruntest <- function(P, name_string, exactvol, tol, num_of_exps, alg, seed){ - + vol = 0 for (j in 1:num_of_exps) { if (alg == "CB") { - vol = vol + volume(P, seed = seed)$volume + vol = vol + volume(P, settings = list("seed" = seed))$volume } else if (alg == "SOB") { - vol = vol + volume(P, settings = list("algorithm" = "SOB"), seed = seed)$volume + vol = vol + volume(P, settings = list("algorithm" = "SOB", "seed" = seed))$volume } else { - vol = vol + volume(P, settings = list("algorithm" = "CG"), seed = seed)$volume + vol = vol + volume(P, settings = list("algorithm" = "CG", "seed" = seed))$volume } } vol = vol / num_of_exps @@ -28,7 +28,7 @@ Hruntest <- function(P, name_string, exactvol, tol, num_of_exps, alg, seed){ cran_only = TRUE for (i in 1:2) { - + if (i==1) { algo = 'CG' num_of_exps = 10 @@ -37,30 +37,30 @@ for (i in 1:2) { num_of_exps = 10 } - + test_that("Volume H-cube10", { P = gen_cube(10, 'H') res = Hruntest(P, 'H-cube10', 1024, 0.2, num_of_exps, algo, 5) expect_equal(res, 1) }) - + test_that("Volume H-cross5", { P = gen_cross(5, 'H') res = Hruntest(P, 'H-cross10', 0.2666667, 0.2, num_of_exps, algo, 5) expect_equal(res, 1) }) - + test_that("Volume H-prod_simplex_5_5", { P = gen_prod_simplex(5) res = Hruntest(P, 'H-prod_simplex_5_5', (1/prod(1:5))^2, 0.2, num_of_exps, algo, 5) expect_equal(res, 1) }) - + test_that("Volume H-cube10", { P = gen_cube(10, 'H') res = Hruntest(P, 'H-cube10', 1024, 0.2, 5, "SOB", 5) expect_equal(res, 1) }) - + } diff --git a/tests/testthat/test_Vvol.R b/tests/testthat/test_Vvol.R index 9e18e52b..9fcabf30 100644 --- a/tests/testthat/test_Vvol.R +++ b/tests/testthat/test_Vvol.R @@ -3,13 +3,13 @@ context("V-polytopes' volume test") library(volesti) Vruntest <- function(P, name_string, exactvol, tol, num_of_exps, algorithm,seed){ - + vol = 0 for (j in 1:num_of_exps) { if (algorithm == "CB") { - vol = vol + volume(P, rounding = "none", seed = seed)$volume + vol = vol + volume(P, settings=list("error" = 1, "seed" = seed), rounding = "none")$volume } else { - vol = vol + volume(P, settings = list("algorithm" = "CG", "error" = 0.1), rounding = "none", seed = seed)$volume + vol = vol + volume(P, settings = list("algorithm" = "CG", "error" = 1, "seed" = seed), rounding = "none")$volume } } vol = vol / num_of_exps @@ -29,12 +29,12 @@ for (i in 1:2) { seed = 5 if (i==1) { algo = 'CG' - tol = 0.2 + tol = 1 } else { algo = 'CB' - tol = 0.2 + tol = 1 } - + test_that("Volume V-simplex3", { P = gen_simplex(3, 'V') res = Vruntest(P, 'V-simplex3', 1/prod(1:3), tol, num_of_exps, algo, seed) diff --git a/tests/testthat/test_Zvol.R b/tests/testthat/test_Zvol.R index f382b003..3f13e7cf 100644 --- a/tests/testthat/test_Zvol.R +++ b/tests/testthat/test_Zvol.R @@ -3,14 +3,14 @@ context("Zonotopes' volume test") library(volesti) Zruntest <- function(P, name_string, tol, num_of_exps, algo, seed){ - + exactvol = exact_vol(P) vol = 0 for (j in 1:num_of_exps) { if (algo == "CB") { - vol = vol + volume(P, settings = list("hpoly" = FALSE), rounding = "none", seed = seed)$volume + vol = vol + volume(P, settings = list("hpoly" = FALSE, "error" = 0.5, "seed" = seed), rounding = "none")$volume } else { - vol = vol + volume(P, settings = list("algorithm" = "CG", "error" = 0.1), rounding = "none", seed = seed)$volume + vol = vol + volume(P, settings = list("algorithm" = "CG", "error" = 1, "seed" = seed), rounding = "none")$volume } } vol = vol / num_of_exps @@ -29,17 +29,17 @@ num_of_exps = 2 for (i in 1:2) { if (i==1) { algo = 'CG' - tol = 0.2 + tol = 1 } else { algo = 'CB' - tol = 0.2 + tol = 1 } test_that("Volume Zonotope_2_4", { #skip_if(Sys.info()[["machine"]] %in% c("x86_32")) - Z = gen_rand_zonotope(2, 4, seed = 127) + Z = gen_rand_zonotope(2, 4, generator = list("seed" = 127)) res = Zruntest(Z, 'Zonotope_2_4', tol, num_of_exps, algo, 5) expect_equal(res, 1) }) - + } diff --git a/tests/testthat/test_rounding.R b/tests/testthat/test_rounding.R index 5d308e36..48f931df 100644 --- a/tests/testthat/test_rounding.R +++ b/tests/testthat/test_rounding.R @@ -3,19 +3,19 @@ context("Rounding test") library(volesti) testRound <- function(P, exactvol, tol, name_string, num_of_exps, algo, rotation,seed){ - + if (rotation) { P = rand_rotate(P) - listHpoly = round_polytope(P, seed = seed) + listHpoly = round_polytope(P, settings=list("seed" = seed)) } else { - listHpoly = round_polytope(P, seed = seed) + listHpoly = round_polytope(P, settings=list("seed" = seed)) } vol = 0 for (j in 1:num_of_exps) { if (algo == "CB") { - vol = vol + listHpoly$round_value * volume(listHpoly$P, seed = seed)$volume + vol = vol + listHpoly$round_value * volume(listHpoly$P, settings=list("seed" = seed))$volume } else { - vol = vol + listHpoly$round_value * volume(listHpoly$P, settings=list("algorithm"="CG", "error"=0.1), seed = seed)$volume + vol = vol + listHpoly$round_value * volume(listHpoly$P, settings=list("algorithm"="CG", "error"=0.1, "seed" = seed))$volume } } vol = vol / num_of_exps @@ -26,24 +26,24 @@ testRound <- function(P, exactvol, tol, name_string, num_of_exps, algo, rotation res = 1 } return(res) - - + + } cran_only = TRUE for (i in 1:2) { - + num_of_exps = 10 - - - + + + test_that("Rounding H-skinny_cube10", { seed=5 P = gen_skinny_cube(10) res = testRound(P, 102400, 0.3, 'H-skinny_cube10', num_of_exps, 'CB', FALSE,seed) expect_equal(res, 1) }) - + } From 6248f246c5cbbe1241d15106c13f561cfbb94d99 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Tue, 27 Feb 2024 10:25:42 +0200 Subject: [PATCH 03/17] Fix volume seed parameter in docs --- R/RcppExports.R | 1 - man/volume.Rd | 2 -- src/volume.cpp | 1 - 3 files changed, 4 deletions(-) diff --git a/R/RcppExports.R b/R/RcppExports.R index a4680226..d77ee54f 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -426,7 +426,6 @@ loadSdpaFormatFile <- function(inputFile = NULL) { #' \item{\code{seed} }{ A fixed seed for the number generator.} #' } #' @param rounding Optional. A string parameter to request a rounding method to be applied in the input polytope before volume computation: a) \code{'min_ellipsoid'}, b) \code{'svd'}, c) \code{'max_ellipsoid'} and d) \code{'none'} for no rounding. -#' @param seed Optional. A fixed seed for the number generator. #' #' @references \cite{I.Z.Emiris and V. Fisikopoulos, #' \dQuote{Practical polytope volume approximation,} \emph{ACM Trans. Math. Soft.,} 2018.}, diff --git a/man/volume.Rd b/man/volume.Rd index d00d797d..71089bed 100644 --- a/man/volume.Rd +++ b/man/volume.Rd @@ -21,8 +21,6 @@ volume(P, settings = NULL, rounding = NULL) }} \item{rounding}{Optional. A string parameter to request a rounding method to be applied in the input polytope before volume computation: a) \code{'min_ellipsoid'}, b) \code{'svd'}, c) \code{'max_ellipsoid'} and d) \code{'none'} for no rounding.} - -\item{seed}{Optional. A fixed seed for the number generator.} } \value{ The approximation of the volume of a convex polytope. diff --git a/src/volume.cpp b/src/volume.cpp index 49dd9960..949c25cf 100644 --- a/src/volume.cpp +++ b/src/volume.cpp @@ -184,7 +184,6 @@ std::pair generic_volume(Polytope& P, RNGType &rng, unsigned int //' \item{\code{seed} }{ A fixed seed for the number generator.} //' } //' @param rounding Optional. A string parameter to request a rounding method to be applied in the input polytope before volume computation: a) \code{'min_ellipsoid'}, b) \code{'svd'}, c) \code{'max_ellipsoid'} and d) \code{'none'} for no rounding. -//' @param seed Optional. A fixed seed for the number generator. //' //' @references \cite{I.Z.Emiris and V. Fisikopoulos, //' \dQuote{Practical polytope volume approximation,} \emph{ACM Trans. Math. Soft.,} 2018.}, From ac7a469006f6a70b4750d654883c6df630ee98e5 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Tue, 27 Feb 2024 10:26:23 +0200 Subject: [PATCH 04/17] Make CI tests failing on warning and test installation --- .github/workflows/R-CMD-check-macOS.yml | 2 +- .github/workflows/R-CMD-check-ubuntu.yml | 2 +- .github/workflows/R-CMD-check-windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/R-CMD-check-macOS.yml b/.github/workflows/R-CMD-check-macOS.yml index 78bcb0a5..4ab68f87 100644 --- a/.github/workflows/R-CMD-check-macOS.yml +++ b/.github/workflows/R-CMD-check-macOS.yml @@ -46,7 +46,7 @@ jobs: - name: Check env: _R_CHECK_CRAN_INCOMING_REMOTE_: false - run: Rscript -e "library(rcmdcheck)" -e "rcmdcheck::rcmdcheck(args = c('--no-manual', '--no-install'), error_on = 'error', check_dir = 'check')" + run: Rscript -e "library(rcmdcheck)" -e "rcmdcheck::rcmdcheck(args = c('--no-manual'), error_on = 'warning', check_dir = 'check')" - name: Upload check results if: failure() diff --git a/.github/workflows/R-CMD-check-ubuntu.yml b/.github/workflows/R-CMD-check-ubuntu.yml index 55c37682..445c5446 100644 --- a/.github/workflows/R-CMD-check-ubuntu.yml +++ b/.github/workflows/R-CMD-check-ubuntu.yml @@ -48,7 +48,7 @@ jobs: - name: Check env: _R_CHECK_CRAN_INCOMING_REMOTE_: false - run: Rscript -e "library(rcmdcheck)" -e "rcmdcheck::rcmdcheck(args = c('--no-manual', '--no-install'), error_on = 'error', check_dir = 'check')" + run: Rscript -e "library(rcmdcheck)" -e "rcmdcheck::rcmdcheck(args = c('--no-manual'), error_on = 'warning', check_dir = 'check')" - name: Upload check results if: failure() diff --git a/.github/workflows/R-CMD-check-windows.yml b/.github/workflows/R-CMD-check-windows.yml index 9a90b6bc..099f7458 100644 --- a/.github/workflows/R-CMD-check-windows.yml +++ b/.github/workflows/R-CMD-check-windows.yml @@ -45,7 +45,7 @@ jobs: - name: Check env: _R_CHECK_CRAN_INCOMING_REMOTE_: false - run: Rscript -e "library(rcmdcheck)" -e "rcmdcheck::rcmdcheck(args = c('--no-manual', '--no-install'), error_on = 'error', check_dir = 'check')" + run: Rscript -e "library(rcmdcheck)" -e "rcmdcheck::rcmdcheck(args = c('--no-manual'), error_on = 'warning', check_dir = 'check')" - name: Upload check results if: failure() From ec62c7537122331b71ad6812e5c4de2e524bab13 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Tue, 27 Feb 2024 11:03:06 +0200 Subject: [PATCH 05/17] Disable nlp oracles in windows CI --- src/Makevars.win | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makevars.win b/src/Makevars.win index e716d951..a1d56b87 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,5 +1,5 @@ PKG_CPPFLAGS=-Iexternal -Iexternal/lpsolve/headers/run_headers -Iexternal/minimum_ellipsoid -Iinclude -Iinclude/convex_bodies/spectrahedra -PKG_CXXFLAGS= -lm -ldl -DBOOST_NO_AUTO_PTR +PKG_CXXFLAGS= -lm -ldl -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES PKG_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) From e06f29d87991c6864b43dcc59b2a00d09b22795b Mon Sep 17 00:00:00 2001 From: vfisikop Date: Tue, 27 Feb 2024 11:46:44 +0200 Subject: [PATCH 06/17] Initialize eta variable in sample_points --- src/sample_points.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sample_points.cpp b/src/sample_points.cpp index 10f6dcee..5afb99fc 100644 --- a/src/sample_points.cpp +++ b/src/sample_points.cpp @@ -477,15 +477,13 @@ Rcpp::NumericMatrix sample_points(Rcpp::Reference P, functor_defined = false; - NT eta; + NT eta = NT(-1); if (Rcpp::as(random_walk).containsElementNamed("step_size")) { eta = NT(Rcpp::as(Rcpp::as(random_walk)["step_size"])); if (eta <= NT(0)) { throw Rcpp::exception("Step size must be positive"); } - } else { - eta = NT(-1); } if (Rcpp::as(random_walk).containsElementNamed("solver")) { From 1e00b6ff4e1a948d0509cccf68481ef3d31f9936 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Tue, 27 Feb 2024 14:53:36 +0200 Subject: [PATCH 07/17] Update external libraries --- src/Makevars | 9 +- src/Makevars.win | 11 +- src/external/PackedCSparse/PackedChol.h | 2 +- src/external/PackedCSparse/qd/COPYING | 16 + src/external/PackedCSparse/qd/Makefile | 15 + src/external/PackedCSparse/qd/NEWS | 181 ++ src/external/PackedCSparse/qd/README | 437 +++ src/external/PackedCSparse/qd/bits.cc | 85 + src/external/PackedCSparse/qd/bits.h | 32 + src/external/PackedCSparse/qd/bits.o | Bin 0 -> 48136 bytes src/external/PackedCSparse/qd/c_dd.cc | 314 ++ src/external/PackedCSparse/qd/c_dd.h | 98 + src/external/PackedCSparse/qd/c_dd.o | Bin 0 -> 91656 bytes src/external/PackedCSparse/qd/c_qd.cc | 450 +++ src/external/PackedCSparse/qd/c_qd.h | 119 + src/external/PackedCSparse/qd/c_qd.o | Bin 0 -> 156768 bytes src/external/PackedCSparse/qd/dd_const.cc | 40 + src/external/PackedCSparse/qd/dd_const.o | Bin 0 -> 45776 bytes src/external/PackedCSparse/qd/dd_inline.h | 621 ++++ src/external/PackedCSparse/qd/dd_real.cc | 1303 ++++++++ src/external/PackedCSparse/{ => qd}/dd_real.h | 44 +- src/external/PackedCSparse/qd/dd_real.o | Bin 0 -> 279272 bytes src/external/PackedCSparse/qd/fpu.cc | 124 + src/external/PackedCSparse/qd/fpu.h | 39 + src/external/PackedCSparse/qd/fpu.o | Bin 0 -> 15136 bytes src/external/PackedCSparse/qd/inline.h | 143 + src/external/PackedCSparse/qd/libqd.a | Bin 0 -> 1196610 bytes src/external/PackedCSparse/qd/qd.pdf | Bin 0 -> 194502 bytes src/external/PackedCSparse/qd/qd_config.h | 92 + src/external/PackedCSparse/qd/qd_const.cc | 62 + src/external/PackedCSparse/qd/qd_const.o | Bin 0 -> 47400 bytes src/external/PackedCSparse/qd/qd_inline.h | 1047 +++++++ src/external/PackedCSparse/qd/qd_real.cc | 2624 +++++++++++++++++ src/external/PackedCSparse/qd/qd_real.h | 296 ++ src/external/PackedCSparse/qd/qd_real.o | Bin 0 -> 472184 bytes src/external/PackedCSparse/qd/util.cc | 22 + src/external/PackedCSparse/qd/util.h | 4 + src/external/PackedCSparse/qd/util.o | Bin 0 -> 33808 bytes 38 files changed, 8197 insertions(+), 33 deletions(-) create mode 100644 src/external/PackedCSparse/qd/COPYING create mode 100644 src/external/PackedCSparse/qd/Makefile create mode 100644 src/external/PackedCSparse/qd/NEWS create mode 100644 src/external/PackedCSparse/qd/README create mode 100644 src/external/PackedCSparse/qd/bits.cc create mode 100644 src/external/PackedCSparse/qd/bits.h create mode 100644 src/external/PackedCSparse/qd/bits.o create mode 100644 src/external/PackedCSparse/qd/c_dd.cc create mode 100644 src/external/PackedCSparse/qd/c_dd.h create mode 100644 src/external/PackedCSparse/qd/c_dd.o create mode 100644 src/external/PackedCSparse/qd/c_qd.cc create mode 100644 src/external/PackedCSparse/qd/c_qd.h create mode 100644 src/external/PackedCSparse/qd/c_qd.o create mode 100644 src/external/PackedCSparse/qd/dd_const.cc create mode 100644 src/external/PackedCSparse/qd/dd_const.o create mode 100644 src/external/PackedCSparse/qd/dd_inline.h create mode 100644 src/external/PackedCSparse/qd/dd_real.cc rename src/external/PackedCSparse/{ => qd}/dd_real.h (92%) create mode 100644 src/external/PackedCSparse/qd/dd_real.o create mode 100644 src/external/PackedCSparse/qd/fpu.cc create mode 100644 src/external/PackedCSparse/qd/fpu.h create mode 100644 src/external/PackedCSparse/qd/fpu.o create mode 100644 src/external/PackedCSparse/qd/inline.h create mode 100644 src/external/PackedCSparse/qd/libqd.a create mode 100644 src/external/PackedCSparse/qd/qd.pdf create mode 100644 src/external/PackedCSparse/qd/qd_config.h create mode 100644 src/external/PackedCSparse/qd/qd_const.cc create mode 100644 src/external/PackedCSparse/qd/qd_const.o create mode 100644 src/external/PackedCSparse/qd/qd_inline.h create mode 100644 src/external/PackedCSparse/qd/qd_real.cc create mode 100644 src/external/PackedCSparse/qd/qd_real.h create mode 100644 src/external/PackedCSparse/qd/qd_real.o create mode 100644 src/external/PackedCSparse/qd/util.cc create mode 100644 src/external/PackedCSparse/qd/util.h create mode 100644 src/external/PackedCSparse/qd/util.o diff --git a/src/Makevars b/src/Makevars index bfa25f89..4d2b7288 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,11 +1,16 @@ PKG_CPPFLAGS=-Iexternal -Iexternal/lpsolve/headers/run_headers -Iexternal/minimum_ellipsoid -Iinclude -Iinclude/convex_bodies/spectrahedra PKG_CXXFLAGS= -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve -Lexternal/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): external/lpsolve/build/lp_solve/liblp_solve.a +$(SHLIB): external/lpsolve/build/lp_solve/liblp_solve.a external/PackedCSparse/qd/libqd.a external/lpsolve/build/lp_solve/liblp_solve.a: @(cd external/lpsolve/build/lp_solve && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" RANLIB="$(RANLIB)") + +external/PackedCSparse/qd/libqd.a: + @(cd external/PackedCSparse/qd && $(MAKE) libqd.a \ + CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ + CPICFLAGS="$(CPICFLAGS)" AR="$(AR)") diff --git a/src/Makevars.win b/src/Makevars.win index a1d56b87..dbd3bac5 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,12 +1,17 @@ PKG_CPPFLAGS=-Iexternal -Iexternal/lpsolve/headers/run_headers -Iexternal/minimum_ellipsoid -Iinclude -Iinclude/convex_bodies/spectrahedra PKG_CXXFLAGS= -lm -ldl -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve -Lexternal/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): external/lpsolve/build/lp_solve/liblp_solve.a +$(SHLIB): external/lpsolve/build/lp_solve/liblp_solve.a external/PackedCSparse/qd/libqd.a external/lpsolve/build/lp_solve/liblp_solve.a: @(cd external/lpsolve/build/lp_solve && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS) -DUSRDLL -DINLINE=static" \ CFLAGS="$(CFLAGS)" CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" \ - RANLIB="$(RANLIB)") \ No newline at end of file + RANLIB="$(RANLIB)") + +external/PackedCSparse/qd/libqd.a: + @(cd external/PackedCSparse/qd && $(MAKE) libqd.a \ + CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ + CPICFLAGS="$(CPICFLAGS)" AR="$(AR)") \ No newline at end of file diff --git a/src/external/PackedCSparse/PackedChol.h b/src/external/PackedCSparse/PackedChol.h index 57a2b923..97d10220 100644 --- a/src/external/PackedCSparse/PackedChol.h +++ b/src/external/PackedCSparse/PackedChol.h @@ -14,7 +14,7 @@ #include "leverage.h" #include "leverageJL.h" #include "multiply.h" -#include "dd_real.h" +#include "qd/dd_real.h" #include #include using namespace PackedCSparse; diff --git a/src/external/PackedCSparse/qd/COPYING b/src/external/PackedCSparse/qd/COPYING new file mode 100644 index 00000000..a20ad70e --- /dev/null +++ b/src/external/PackedCSparse/qd/COPYING @@ -0,0 +1,16 @@ +This work was supported by the Director, Office of Science, Division +of Mathematical, Information, and Computational Sciences of the +U.S. Department of Energy under contract numbers DE-AC03-76SF00098 and +DE-AC02-05CH11231. + +Copyright (c) 2003-2009, The Regents of the University of California, +through Lawrence Berkeley National Laboratory (subject to receipt of +any required approvals from U.S. Dept. of Energy) All rights reserved. + +By downloading or using this software you are agreeing to the modified +BSD license that is in file "BSD-LBNL-License.doc" in the main ARPREC +directory. If you wish to use the software for commercial purposes +please contact the Technology Transfer Department at TTD@lbl.gov or +call 510-286-6457." + + diff --git a/src/external/PackedCSparse/qd/Makefile b/src/external/PackedCSparse/qd/Makefile new file mode 100644 index 00000000..791e92b9 --- /dev/null +++ b/src/external/PackedCSparse/qd/Makefile @@ -0,0 +1,15 @@ +QD_CPPFLAGS=$(CPPFLAGS) -I$(R_INCLUDE_DIR) -march=native + +QD_SOURCES= bits.cc c_dd.cc c_qd.cc dd_const.cc dd_real.cc fpu.cc \ + qd_const.cc qd_real.cc util.cc + +QD_OBJECTS=$(QD_SOURCES:.cc=.o) + +libqd.a: $(QD_OBJECTS) + $(AR) rc libqd.a $(QD_OBJECTS) + +.cc.o: + $(CC) $(CFLAGS) $(CPICFLAGS) $(QD_CPPFLAGS) -c $< -o $@ + +clean: + rm -rf $(QD_OBJECTS) libqd.a diff --git a/src/external/PackedCSparse/qd/NEWS b/src/external/PackedCSparse/qd/NEWS new file mode 100644 index 00000000..f32a7575 --- /dev/null +++ b/src/external/PackedCSparse/qd/NEWS @@ -0,0 +1,181 @@ +Changes for 2.3.22 + Made changes suggested by Vasiliy Sotnikov + +Changes for 2.3.21 + Changed renorm in include/qd/qd_inline.h + +Changes for 2.3.20 + added #include to quadt_test.cpp + changed references to 2.3.20 from 2.3.18 + +Changes for 2.3.19 + - Updated qd_real.cpp and dd_real.cpp to fix a buffer overflow problem. + +Changes for 2.3.18 + - Updated qd_real.cpp and dd_real.cpp to fix a problem in output. + +Changes for 2.3.17 + - updated qd_real.cpp, to fix a problem with improper treatment of + negative arguments in nroot. + +Changes for 2.3.16 + - Updated dd_real.cpp, to fix a problem with inaccurate values of + tanh for small arguments. + +Changes for 2.3.15 + - Updated qd_real.cpp, to fix a problem with static definitions. + +Changes for 2.3.14 + - Updated autoconfig (replaced config.sub and config.guess) + +Changes for 2.3.7 + - Fixed bug in to_digits where digits larger than 10 + where output occasionally. + +Changes for 2.3.6 + - Added fmod (C++) and mod (Fortran) functions. + +Changes for 2.3.5 + - Fixed bug in division of qd_real by dd_real. + - Fixed bug in ddoutc (Fortran ddmod.f). + - Now compiles with g++ 4.3. + - Distribute tests/coeff.dat. + +Changes for 2.3.4 + - Fixed bug in Makefile for cygwin / mingw systems. + +Changes for 2.3.3 + - Fixed bug in atan2. + +Changes for 2.3.2 + - Fixed bug in sin / cos / sincos where too much accuracy was + lost for (moderately) large angles. + - Use fused-multiply add intrinsics on IA-64 platforms if + compiled by Intel compiler. + - Fixed bug in c_dd_write and c_qd_write. + - Fixed bug were qdext.mod was not being installed. + +Changes for 2.3.1 + - Fixed bug in sincos and cos_taylor. This affected the result + of trigonometric functions in some cases. + +Changes for 2.3.0 + This is a fairly significant change, breaking API compatibility. + - Moved C++ main entry in libqdmod.a to libqd_f_main.a. + This allows to link Fortran code using QD with custom + C++ main function. Pure Fortran code will need to be linked + with qd_f_main library in addition to qdmod and qd library. + - Constructors accepting pointers made explicit. + - Fortran routines labeled as elemental or pure, where appropriate. + - Write() is now to_string(), and now takes a single fmtflag. + - dd_real addition and multiplication made commutative. + - dd_real now represented as array of two doubles, instead of + two discrete scalars. + - New Fortran generic routines to read / write, operations with + complex and integers. + - Improved exp, sin, and cos functions. + - Removed unused constants and obscure constants only used internally + from public interface. + +Changes for 2.2.6 + - Fixed bug in mixed precision multiplication: qd_real * dd_real. + +Changes for 2.2.5 + - Bug fix in qd_real addition when --enable-ieee-add is specified. + - Debugging routines dump and dump_bits updated; + dump_components removed (just use dump). + - Fortran support for Fortran strings. Use character arrays instead. + - Return NaN under error conditions. + - Added _inf constant; exp now returns Inf when argument is too large. + - Output formatting fixes for Inf and NaNs. + - Added more real-complex mixed arithmetic routines in Fortran + interface. + +Changes for 2.2.4 + - Added random_number interface for Fortran modules. + - Use slightly more conservative values for eps. + - Avoid unnecessary overflow near overflow threshold. + - Added radix, digits, min/maxexponent, range, and precision + intrinsics to Fortran interface. + - Added safe_max (C++) and safe_huge (Fortran). + +Changes for 2.2.3 + - Fix sign function bug in Fortran modules. + +Changes for 2.2.2 + - Do not bother setting uninitialized dd_real and qd_reals to zero. + - Use clock_gettime if available for timing. + - Fortran I/O should be more consistent with C++ version. + - fpu.h is now included with dd_real.h. + +Changes for 2.2.1 + - Minor fixes when printing in scientific format. + - Change search order of C++ compilers in Apple systems to avoid + case insensitive filesystems. + +Changes for 2.2.0 + - Added F95 interface for complex types. + - Renamed dd.h and qd.h to dd_real.h and qd_real.h, respectively. + This will break older C++ code using 2.1.x library, but it was + conflicting with QuickDraw libraries on Macs. (Hence the version + bump to 2.2). + - Removed overloaded typecast operators for int and double. These + permitted *automatic* conversion of dd_real/qd_real to double or + int, which is somewhat dangerous. Instead to_int and to_double + routines are added. + +Changes for 2.1.214 + - Updated pslq_test. + - Implmented numeric_limits<>. + - Better polyroot. + - Added isnan, isfinite, isinf functions. + - Fix / improve input output functions. + - Drop Microsoft Visual C++ 6.0 support. + - More efficient dd_real::sin. + +Changes for 2.1.213 + - Support for x86_64 platforms. + - Drop libtool support for now. + +Changes for 2.1.212 + - Support for pathCC compiler. + - Added accurate and sloppy versions of add / sub / mul / div avaialble. + - Added autodetection of fma functions. + +Changes for 2.1 (2003-12-30) + - added automake scripts. + - use libtool to compile / link and build libraries. + - supports standard installation targets (make install). + - support for Intel C++ compilers (icc / ecc). + - Fortran programs are now linked by C++ compiler. + - support for building shared library. + - minor bug fixes. + +Changes for 2.0 (2003-12-08) + - all header files are in "include/qd" directory. + - added autoconf scripts. + - added config.h and qd_config.h to store configuration information. + - renamed x86_* routines to fpu_* routines. + - added separate Fortran interface (f_* routines). + - options for sloppy multiply and sloppy divison separated. + - fixed C interface to be actually in C syntax. + - updated / added README, AUTHORS, NEWS, and LICENSE files. + - minor bug fixes. + +Changes for 1.2 (2003-12-04) + - added "dist-clean" target in Makefile + - initialize dd and qd variables to zero + - increases tolerance for qd / dd tests + - changed .cc extension to .cpp + - updated README, COPYING, and NEWS files + - added ChangeLog file + - fixed bug in '-all' flag in qd_test + - minor bug fixes + +Changes for 1.1 (2002-10-22) + - added "Changes" file (this file) + - fixed to + - fixed constant (3/4) * pi + - fixed exp(x) to return zero if x is a large negative number + - removed "docs" target in Makefile + diff --git a/src/external/PackedCSparse/qd/README b/src/external/PackedCSparse/qd/README new file mode 100644 index 00000000..8a085d72 --- /dev/null +++ b/src/external/PackedCSparse/qd/README @@ -0,0 +1,437 @@ +Quad Double computation package +Copyright (C) 2003-2019 +================================================ + +Revision date: 26 February 2019 + +Authors: +Yozo Hida U.C. Berkeley yozo@cs.berkeley.edu +Xiaoye S. Li Lawrence Berkeley Natl Lab xiaoye@nersc.gov +David H. Bailey Lawrence Berkeley Natl Lab dhbailey@lbl.gov + +C++ usage guide: +Alex Kaiser Lawrence Berkeley Natl Lab adkaiser@lbl.gov + +This work was supported by the Director, Office of Science, Division of Mathematical, +Information, and Computational Sciences of the U.S. Department of Energy under contract +number DE-AC02-05CH11231. + +This work was supported by the Director, Office of Science, Division of Mathematical, +Information, and Computational Sciences of the U.S. Department of Energy under contract +numbers DE-AC03-76SF00098 and DE-AC02-05CH11231. + +*** IMPORTANT NOTES: + +See the file COPYING for modified BSD license information. +See the file INSTALL for installation instructions. +See the file NEWS for recent revisions. +See the file docs/qd.pdf for additional information. + +Outline: + +I. Introduction +II. Installation of package, and linking and executing user files +III. C++ Usage +IV. Fortran Usage +V. Note on x86-Based Processors (MOST systems in use today) + + +I. Introduction + +This package provides numeric types of twice the precision of IEEE double (106 mantissa +bits, or approximately 32 decimal digits) and four times the precision of IEEE double (212 +mantissa bits, or approximately 64 decimal digits). Due to features such as operator and +function overloading, these facilities can be utilized with only minor modifications to +conventional C++ and Fortran-90 programs. + +In addition to the basic arithmetic operations (add, subtract, multiply, divide, square root), +common transcendental functions such as the exponential, logarithm, trigonometric and +hyperbolic functions are also included. A detailed description of the algorithms used is +available in the docs subdirectory (see docs/qd.pdf). An abridged version of this paper, +which was presented at the ARITH-15 conference, is also available at: + +Yozo Hida, Xiaoye S. Li and David H. Bailey, "Algorithms for quad-double precision + floating point arithmetic," 15th IEEE Symposium on Computer Arithmetic, IEEE Computer + Society, 2001, pg. 155-162, available at + https://www.davidhbailey.com/dhbpapers/arith15.pdf. + + +II. Installation of package, and linking and executing user files + +A. Directories + +There are six directories and several files in the main directory of this distribution, +described below + +src This contains the source code of the quad-double and double-double + library. This source code does not include inline functions, + which are found in the header files in the include directory. + +include This directory contains the header files. + +fortran This directory contains Fortran-90 files. + +tests This directory contains some simple (not comprehensive) tests. + +docs This directory contains a technical paper describing the algorithms. + +config This directory contains various scripts used by the configure + script and the Makefile. + +Please note that all commands refer to a Unix-type environment such as Mac OSX or Ubuntu +Linux using the bash shell. + +B. Installing and building + +To build the library, first run the included configure script by typing + + ./configure + +This script automatically generates makefiles for building the library and selects compilers +and necessary flags and libraries to include. If the user wishes to specify compilers or flags +they may use the following options. + + CXX C++ compiler to use + CXXFLAGS C++ compiler flags to use + CC C compiler to use (for C demo program) + CFLAGS C compiler flags to use (for C demo program) + FC Fortran 90 compiler + FCFLAGS Fortran 90 compiler flags to use + FCLIBS Fortran 90 libraries needed to link with C++ code. + +For example, if one is using GNU compilers, configure with: + + ./configure CXX=g++ FC=gfortran + +The Fortran and C++ compilers must produce compatible binaries. On some systems +additional flags must be included to ensure that portions of the +library are not built with 32 and 64 bit object files. For example, on +64-Bit Mac OSX 10.6 (Snow Leopard) and 10.7 (Lion) the correct +configure line using GNU compilers is: + + ./configure CXX=g++ FC=gfortran FCFLAGS=-m64 + +To build the library, simply type + + make + +and the automatically generated makefiles will build the library including archive files. + +To allow for easy linking to the library, the user may also wish to +install the archive files to a standard place. To do this type: + + make install + +This will also build the library if it has not already been built. Many systems, including Mac +and Ubuntu Linux systems, require administrator privileges to install the library at such +standard places. On such systems, one may type: + + sudo make install + +instead if one has sufficient access. + +The directory "tests" contains programs for high precision quadrature and integer-relation +detection. To build such programs, type: + + make demo + +in the "tests" directory. + +C. Linking and executing user programs + +C++ source files: + +The simplest way to link to the library is to install it to a standard place as described above, and use the -l option. For example + + g++ compileExample.cpp -o compileExample -l qd + +One can also use this method to build with make. A file called "compileExample.cpp" and the +associated makefile "makeCompileExample" illustrate the process. + +A third alternative is to use a link script. If one types "make demo" in the test +directory, the output produced gives guidance as to how to build the files. By +following the structure of the compiling commands one may copy the appropriate portions, +perhaps replacing the filename with an argument that the user can include at link time. +An example of such a script is as follows: + +g++ -DHAVE_CONFIG_H -I.. -I../include -I../include -O2 -MT $1.o -MD -MP -MF +.deps/qd_test.Tpo -c -o $1.o $1.cpp +mv -f .deps/$1.Tpo .deps/$1.Po +g++ -O2 -o $1 $1.o ../src/libqd.a -lm + +To use the link script, make it executable (by typing "chmod +x link.scr) and then type: + +./link.scr compileExample + +Note that the file extension is not included because the script handles all extensions, +expecting the source file to have the extension ".cpp". + +Fortran-90 source files: + +Similarly, a script for compiling fortran programs may be constructed as follows. +In the fortran directory, type "make quadtsq". This compiles the Fortran program +tquadts.f, links with all necessary library files, and produces the executable +"quadts". As this is being done, all flags and linked libraries are displayed. +For instance, on a 2019-era Apple Macintosh system, where the library was installed +as above with g++ for C++ and gfortran for Fortran-90, the following is output: + +gfortran -m64 -ffree-form -c -o tquadtsq.o tquadtsq.f +/bin/sh ../libtool --tag=CXX --mode=link g++ -O2 -o quadtsq tquadtsq.o second.o +libqdmod.la libqd_f_main.la ../src/libqd.la +-L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0 +-L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0/../../.. +-lgfortran -lquadmath -lm -lm + +Thus a general compile-link script is the following: + +gfortran -m64 -ffree-form -c -o $1.o $1.f90 +/bin/sh ../libtool --tag=CXX --mode=link g++ -O2 -o $1 $1.o second.o \ + libqdmod.la libqd_f_main.la ../src/libqd.la \ + -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0 \ + -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0/../../.. \ + -lgfortran -lquadmath -lm -lm + +Note that if the .f90 suffix is used for Fortran-90 source files, the +-ffree-form flag may be omitted, but the first line above should end with +"$1.f90" (as shown above). After forming the script, name file, "complink.scr", +and then type "chmod +x complink.scr". To use this script compile and link a +program named "prog.f90", type "./complink.scr prog". + + +III. C++ usage + +As much as possible, operator overloading is included to make basic programming as much +like using standard typed floating-point arithmetic. Changing many codes should be as +simple as changing type statements and a few other lines. + +i. Constructors + +To create dd_real and qd_real variables calculated to the proper precision, one must use +care to use the included constructors properly. Many computations in which variables are +not explicitly typed to multiple-precision may be evaluated with double-precision +arithmetic. The user must take care to ensure that this does not cause errors. In particular, +an expression such as 1.0/3.0 will be evaluated to double precision before assignment or +further arithmetic. Upon assignment to a multi-precision variable, the value will be zero +padded. This problem is serious and potentially difficult to debug. To avoid this, use the +included constructors to force arithmetic to be performed in the full precision requested. + +For a table with descriptions, please see the documentation file qd.pdf in the docs directory. + +ii. Included functions and Constants + +Supported functions include assignment operators, comparisons, arithmetic and +assignment operators, and increments for integer types. Standard C math functions such as +exponentiation, trigonometric, logarithmic, hyperbolic, exponential and rounding functions +are included. As in assignment statements, one must be careful with implied typing of +constants when using these functions. Many codes need particular conversion for the power +function, which is frequently used with constants that must be explicitly typed for multi- +precision codes. + +Many constants are included, which are global and calculated upon initialization. The +following list of constants is calculated for both the dd_real and qd_real classes separately. +Use care to select the correct value. + +For a table with descriptions, please see the included file README.pdf + +ii. Conversion of types + +Static casts may be used to convert constants between types. One may also use constructors +to return temporary multi-precision types within expressions, but should be careful, as this +will waste memory if done repeatedly. For example: + + qd_real y ; + y = sin( qd_real(4.0) / 3.0 ) ; + +C-style casts may be used, but are not recommended. Dynamic and reinterpret casts are +not supported and should be considered unreliable. Casting between multi-precision and +standard precision types can be dangerous, and care must be taken to ensure that programs +are working properly and accuracy has not degraded by use of a misplaced type-conversion. + +D. Available precision, Control of Precision Levels, + +The library provides greatly extended accuracy when compared to standard double +precision. The type dd_real provides for 106 mantissa bits, or about 32 decimal digits. The +type qd_real provides for 212 mantissa bits, or about 64 decimal digits. + +Both the dd_real and qd_real values use the exponent from the highest double-precision +word for arithmetic, and as such do not extend the total range of values available. That +means that the maximum absolute value for either data type is the same as that of double- +precision, or approximately 10^308. The precision near this range, however, is greatly +increased. + +E. I/O + +The standard I/O stream routines have been overloaded to be fully compatible with all +included data types. One may need to manually reset the precision of the stream to obtain +full output. For example, if 60 digits are desired, use: + +cout.precision(60) ; + +When reading values using cin, each input numerical value must start on a separate +line. Two formats are acceptable: + + 1. Write the full constant + 3. Mantissa e exponent + +Here are three valid examples: + + 1.1 + 3.14159 26535 89793 + 123.123123e50 + +When read using cin, these constants will be converted using full multi-precision accuracy. + + +IV. Fortran-90 Usage + +NEW (2007-01-10): The Fortran translation modules now support the complex datatypes +"dd_complex" and "qd_complex". + +Since the quad-double library is written in C++, it must be linked in with a C++ compiler (so +that C++ specific things such as static initializations are correctly handled). Thus the main +program must be written in C/C++ and call the Fortran 90 subroutine. The Fortran 90 +subroutine should be called f_main. + +Here is a sample Fortran-90 program, equivalent to the above C++ program: + + subroutine f_main + use qdmodule + implicit none + type (qd_real) a, b + a = 1.d0 + b = cos(a)**2 + sin(a)**2 - 1.d0 + call qdwrite(6, b) + stop + end subroutine + +This verifies that cos^2(1) + sin^2(1) = 1 to 64 digit accuracy. + +Most operators and generic function references, including many mixed-mode type +combinations with double-precision (ie real*8), have been overloaded (extended) to work +with double-double and quad-double data. It is important, however, that users keep in +mind the fact that expressions are evaluated strictly according to conventional Fortran +operator precedence rules. Thus some subexpressions may be evaluated only to 15-digit +accuracy. For example, with the code + + real*8 d1 + type (dd_real) t1, t2 + ... + t1 = cos (t2) + d1/3.d0 + +the expression d1/3.d0 is computed to real*8 accuracy only (about 15 digits), since both d1 +and 3.d0 have type real*8. This result is then converted to dd_real by zero extension before +being added to cos(t2). So, for example, if d1 held the value 1.d0, then the quotient d1/3.d0 +would only be accurate to 15 digits. If a fully accurate double-double quotient is required, +this should be written: + + real*8 d1 + type (dd_real) t1, t2 + ... + t1 = cos (t2) + ddreal (d1) / 3.d0 + +which forces all operations to be performed with double-double arithmetic. + +Along this line, a constant such as 1.1 appearing in an expression is evaluated only to real*4 +accuracy, and a constant such as 1.1d0 is evaluated only to real*8 accuracy (this is +according to standard Fortran conventions). If full quad-double accuracy is required, for +instance, one should write + + type (qd_real) t1 + ... + t1 = '1.1' + +The quotes enclosing 1.1 specify to the compiler that the constant is to be converted to +binary using quad-double arithmetic, before assignment to t1. Quoted constants may only +appear in assignment statements such as this. + +To link a Fortran-90 program with the C++ qd library, it is recommended to link with the +C++ compiler used to generate the library. The Fortran 90 interface (along with a C-style +main function calling f_main) is found in qdmod library. The qd-config script installed +during "make install" can be used to determine which flags to pass to compile and link your +programs: + + "qd-config --fcflags" displays compiler flags needed to compile your Fortran files. + "qd-config --fclibs" displays linker flags needed by the C++ linker to link in all the +necessary libraries. + +A sample Makefile that can be used as a template for compiling Fortran programs using +quad-double library is found in fortran/Makefile.sample. + +F90 functions defined with dd_real arguments: + Arithmetic: + - * / ** + Comparison tests: == < > <= >= /= + Others: abs, acos, aint, anint, asin, atan, atan2, cos, cosh, dble, erf, + erfc, exp, int, log, log10, max, min, mod, ddcsshf (cosh and sinh), + ddcssnf (cos and sin), ddranf (random number generator in (0,1)), + ddnrtf (n-th root), sign, sin, sinh, sqr, sqrt, tan, tanh + +Similar functions are provided for qd_real arguments with function names qdcsshf, +qdcssnf, qdranf and qdnrtf instead of the names in the list above. + +Input and output of double-double and quad-double data is done using the special +subroutines ddread, ddwrite, qdread and qdwrite. The first argument of these subroutines +is the Fortran I/O unit number, while additional arguments (as many as needed, up to 9 +arguments) are scalar variables or array elements of the appropriate type. Example: + + integer n + type (qd_real) qda, qdb, qdc(n) + ... + call qdwrite (6, qda, qdb) + do j = 1, n + call qdwrite (6, qdc(j)) + enddo + +Each input values must be on a separate line, and may include D or E exponents. Double- +double and quad-double constants may also be specified in assignment statements by +enclosing them in quotes, as in + + ... + type (qd_real) pi + ... + pi = +"3.14159265358979323846264338327950288419716939937510582097494459230" + ... + +Sample Fortran-90 programs illustrating some of these features are provided in the f90 +subdirectory. + + +V. Note on x86-Based Processors (MOST systems in use today) + +The algorithms in this library assume IEEE double precision floating point arithmetic. Since +Intel x86 processors have extended (80-bit) floating point registers, some compilers, +albeit a declining number, may generate commands for the 80-bit instructions. The QD +library does NOT work correctly with 80-bit instructions, so if one's code does not operate +correctly, this may be the reason. To avoid such problems, the round-to-double flag must be +enabled in the control word of the FPU for this library to function properly. The following +functions contains appropriate code to facilitate manipulation of this flag. For non-x86 +systems these functions do nothing (but still exist). + +fpu_fix_start This turns on the round-to-double bit in the control word. +fpu_fix_end This restores the control flag. + +These functions must be called by the main program, as follows: + + int main() { + unsigned int old_cw; + fpu_fix_start(&old_cw); + + ... user code using quad-double library ... + + fpu_fix_end(&old_cw); + } + +A Fortran-90 example is the following: + + subroutine f_main + use qdmodule + implicit none + integer*4 old_cw + + call f_fpu_fix_start(old_cw) + + ... user code using quad-double library ... + + call f_fpu_fix_end(old_cw) + end subroutine + diff --git a/src/external/PackedCSparse/qd/bits.cc b/src/external/PackedCSparse/qd/bits.cc new file mode 100644 index 00000000..4eaf9a26 --- /dev/null +++ b/src/external/PackedCSparse/qd/bits.cc @@ -0,0 +1,85 @@ +/* + * src/bits.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Defines various routines to get / set bits of a IEEE floating point + * number. This used by the library for debugging purposes. + */ + +#include +#include +#include +#include + +#include "qd_config.h" +#include "inline.h" +#include "bits.h" + +#ifdef HAVE_IEEEFP_H +#include +#endif + +using std::setw; + +int get_double_expn(double x) { + if (x == 0.0) + return INT_MIN; + if (QD_ISINF(x) || QD_ISNAN(x)) + return INT_MAX; + + double y = std::abs(x); + int i = 0; + if (y < 1.0) { + while (y < 1.0) { + y *= 2.0; + i++; + } + return -i; + } else if (y >= 2.0) { + while (y >= 2.0) { + y *= 0.5; + i++; + } + return i; + } + return 0; +} + +void print_double_info(std::ostream &os, double x) { + std::streamsize old_prec = os.precision(19); + std::ios_base::fmtflags old_flags = os.flags(); + os << std::scientific; + + os << setw(27) << x << ' '; + if (QD_ISNAN(x) || QD_ISINF(x) || (x == 0.0)) { + os << " "; + } else { + + x = std::abs(x); + int expn = get_double_expn(x); + double d = std::ldexp(1.0, expn); + os << setw(5) << expn << " "; + for (int i = 0; i < 53; i++) { + if (x >= d) { + x -= d; + os << '1'; + } else + os << '0'; + d *= 0.5; + } + + if (x != 0.0) { + // should not happen + os << " +trailing stuff"; + } + } + + os.precision(old_prec); + os.flags(old_flags); +} + diff --git a/src/external/PackedCSparse/qd/bits.h b/src/external/PackedCSparse/qd/bits.h new file mode 100644 index 00000000..58570aac --- /dev/null +++ b/src/external/PackedCSparse/qd/bits.h @@ -0,0 +1,32 @@ +/* + * include/bits.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * This file defines various routines to get / set bits of a IEEE floating + * point number. This is used by the library for debugging purposes. + */ + +#ifndef _QD_BITS_H +#define _QD_BITS_H + +#include +#include "qd_config.h" + +/* Returns the exponent of the double precision number. + Returns INT_MIN is x is zero, and INT_MAX if x is INF or NaN. */ +int get_double_expn(double x); + +/* Prints + SIGN EXPN MANTISSA + of the given double. If x is NaN, INF, or Zero, this + prints out the strings NaN, +/- INF, and 0. */ +void print_double_info(std::ostream &os, double x); + + +#endif /* _QD_BITS_H */ + diff --git a/src/external/PackedCSparse/qd/bits.o b/src/external/PackedCSparse/qd/bits.o new file mode 100644 index 0000000000000000000000000000000000000000..9e82380cd48d53fa8aa361392c2c792257fe3229 GIT binary patch literal 48136 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 literal 0 HcmV?d00001 diff --git a/src/external/PackedCSparse/qd/c_dd.cc b/src/external/PackedCSparse/qd/c_dd.cc new file mode 100644 index 00000000..0a7c12ac --- /dev/null +++ b/src/external/PackedCSparse/qd/c_dd.cc @@ -0,0 +1,314 @@ +/* + * src/c_dd.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains the C wrapper functions for double-double precision arithmetic. + * This can be used from Fortran code. + */ +#include + +#include "qd_config.h" +#include "dd_real.h" +#include "c_dd.h" + +#define TO_DOUBLE_PTR(a, ptr) ptr[0] = a.x[0]; ptr[1] = a.x[1]; + +extern "C" { + +/* add */ +void c_dd_add(const double *a, const double *b, double *c) { + dd_real cc; + cc = dd_real(a) + dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_dd_add_dd_d(const double *a, double b, double *c) { + dd_real cc; + cc = dd_real(a) + b; + TO_DOUBLE_PTR(cc, c); +} +void c_dd_add_d_dd(double a, const double *b, double *c) { + dd_real cc; + cc = a + dd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + +/* sub */ +void c_dd_sub(const double *a, const double *b, double *c) { + dd_real cc; + cc = dd_real(a) - dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_dd_sub_dd_d(const double *a, double b, double *c) { + dd_real cc; + cc = dd_real(a) - b; + TO_DOUBLE_PTR(cc, c); +} +void c_dd_sub_d_dd(double a, const double *b, double *c) { + dd_real cc; + cc = a - dd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + +/* mul */ +void c_dd_mul(const double *a, const double *b, double *c) { + dd_real cc; + cc = dd_real(a) * dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_dd_mul_dd_d(const double *a, double b, double *c) { + dd_real cc; + cc = dd_real(a) * b; + TO_DOUBLE_PTR(cc, c); +} +void c_dd_mul_d_dd(double a, const double *b, double *c) { + dd_real cc; + cc = a * dd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + +/* div */ +void c_dd_div(const double *a, const double *b, double *c) { + dd_real cc; + cc = dd_real(a) / dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_dd_div_dd_d(const double *a, double b, double *c) { + dd_real cc; + cc = dd_real(a) / b; + TO_DOUBLE_PTR(cc, c); +} +void c_dd_div_d_dd(double a, const double *b, double *c) { + dd_real cc; + cc = a / dd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + +/* copy */ +void c_dd_copy(const double *a, double *b) { + b[0] = a[0]; + b[1] = a[1]; +} +void c_dd_copy_d(double a, double *b) { + b[0] = a; + b[1] = 0.0; +} + + +void c_dd_sqrt(const double *a, double *b) { + dd_real bb; + bb = sqrt(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_sqr(const double *a, double *b) { + dd_real bb; + bb = sqr(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_abs(const double *a, double *b) { + dd_real bb; + bb = abs(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_npwr(const double *a, int n, double *b) { + dd_real bb; + bb = npwr(dd_real(a), n); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_nroot(const double *a, int n, double *b) { + dd_real bb; + bb = nroot(dd_real(a), n); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_nint(const double *a, double *b) { + dd_real bb; + bb = nint(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_aint(const double *a, double *b) { + dd_real bb; + bb = aint(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_floor(const double *a, double *b) { + dd_real bb; + bb = floor(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_ceil(const double *a, double *b) { + dd_real bb; + bb = ceil(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_log(const double *a, double *b) { + dd_real bb; + bb = log(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_log10(const double *a, double *b) { + dd_real bb; + bb = log10(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_exp(const double *a, double *b) { + dd_real bb; + bb = exp(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_sin(const double *a, double *b) { + dd_real bb; + bb = sin(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_cos(const double *a, double *b) { + dd_real bb; + bb = cos(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_tan(const double *a, double *b) { + dd_real bb; + bb = tan(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_asin(const double *a, double *b) { + dd_real bb; + bb = asin(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_acos(const double *a, double *b) { + dd_real bb; + bb = acos(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_atan(const double *a, double *b) { + dd_real bb; + bb = atan(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_atan2(const double *a, const double *b, double *c) { + dd_real cc; + cc = atan2(dd_real(a), dd_real(b)); + TO_DOUBLE_PTR(cc, c); +} + +void c_dd_sinh(const double *a, double *b) { + dd_real bb; + bb = sinh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_cosh(const double *a, double *b) { + dd_real bb; + bb = cosh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_tanh(const double *a, double *b) { + dd_real bb; + bb = tanh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_asinh(const double *a, double *b) { + dd_real bb; + bb = asinh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_acosh(const double *a, double *b) { + dd_real bb; + bb = acosh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_atanh(const double *a, double *b) { + dd_real bb; + bb = atanh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_sincos(const double *a, double *s, double *c) { + dd_real ss, cc; + sincos(dd_real(a), ss, cc); + TO_DOUBLE_PTR(ss, s); + TO_DOUBLE_PTR(cc, c); +} + +void c_dd_sincosh(const double *a, double *s, double *c) { + dd_real ss, cc; + sincosh(dd_real(a), ss, cc); + TO_DOUBLE_PTR(ss, s); + TO_DOUBLE_PTR(cc, c); +} + +void c_dd_read(const char *s, double *a) { + dd_real aa(s); + TO_DOUBLE_PTR(aa, a); +} + +void c_dd_swrite(const double *a, int precision, char *s, int len) { + dd_real(a).write(s, len, precision); +} + +void c_dd_write(const double *a) { + std::cout << dd_real(a).to_string(dd_real::_ndigits) << std::endl; +} + +void c_dd_neg(const double *a, double *b) { + b[0] = -a[0]; + b[1] = -a[1]; +} + +void c_dd_rand(double *a) { + dd_real aa; + aa = ddrand(); + TO_DOUBLE_PTR(aa, a); +} + +void c_dd_comp(const double *a, const double *b, int *result) { + dd_real aa(a), bb(b); + if (aa < bb) + *result = -1; + else if (aa > bb) + *result = 1; + else + *result = 0; +} + +void c_dd_comp_dd_d(const double *a, double b, int *result) { + dd_real aa(a), bb(b); + if (aa < bb) + *result = -1; + else if (aa > bb) + *result = 1; + else + *result = 0; +} + +void c_dd_comp_d_dd(double a, const double *b, int *result) { + dd_real aa(a), bb(b); + if (aa < bb) + *result = -1; + else if (aa > bb) + *result = 1; + else + *result = 0; +} + +void c_dd_pi(double *a) { + TO_DOUBLE_PTR(dd_real::_pi, a); +} + +} diff --git a/src/external/PackedCSparse/qd/c_dd.h b/src/external/PackedCSparse/qd/c_dd.h new file mode 100644 index 00000000..310162ed --- /dev/null +++ b/src/external/PackedCSparse/qd/c_dd.h @@ -0,0 +1,98 @@ +/* + * include/c_dd.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains C wrapper function prototypes for double-double precision + * arithmetic. This can also be used from fortran code. + */ +#ifndef _QD_C_DD_H +#define _QD_C_DD_H + +#include "qd_config.h" +#include "fpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* add */ +void c_dd_add(const double *a, const double *b, double *c); +void c_dd_add_d_dd(double a, const double *b, double *c); +void c_dd_add_dd_d(const double *a, double b, double *c); + +/* sub */ +void c_dd_sub(const double *a, const double *b, double *c); +void c_dd_sub_d_dd(double a, const double *b, double *c); +void c_dd_sub_dd_d(const double *a, double b, double *c); + +/* mul */ +void c_dd_mul(const double *a, const double *b, double *c); +void c_dd_mul_d_dd(double a, const double *b, double *c); +void c_dd_mul_dd_d(const double *a, double b, double *c); + +/* div */ +void c_dd_div(const double *a, const double *b, double *c); +void c_dd_div_d_dd(double a, const double *b, double *c); +void c_dd_div_dd_d(const double *a, double b, double *c); + +/* copy */ +void c_dd_copy(const double *a, double *b); +void c_dd_copy_d(double a, double *b); + +void c_dd_sqrt(const double *a, double *b); +void c_dd_sqr(const double *a, double *b); + +void c_dd_abs(const double *a, double *b); + +void c_dd_npwr(const double *a, int b, double *c); +void c_dd_nroot(const double *a, int b, double *c); + +void c_dd_nint(const double *a, double *b); +void c_dd_aint(const double *a, double *b); +void c_dd_floor(const double *a, double *b); +void c_dd_ceil(const double *a, double *b); + +void c_dd_exp(const double *a, double *b); +void c_dd_log(const double *a, double *b); +void c_dd_log10(const double *a, double *b); + +void c_dd_sin(const double *a, double *b); +void c_dd_cos(const double *a, double *b); +void c_dd_tan(const double *a, double *b); + +void c_dd_asin(const double *a, double *b); +void c_dd_acos(const double *a, double *b); +void c_dd_atan(const double *a, double *b); +void c_dd_atan2(const double *a, const double *b, double *c); + +void c_dd_sinh(const double *a, double *b); +void c_dd_cosh(const double *a, double *b); +void c_dd_tanh(const double *a, double *b); + +void c_dd_asinh(const double *a, double *b); +void c_dd_acosh(const double *a, double *b); +void c_dd_atanh(const double *a, double *b); + +void c_dd_sincos(const double *a, double *s, double *c); +void c_dd_sincosh(const double *a, double *s, double *c); + +void c_dd_read(const char *s, double *a); +void c_dd_swrite(const double *a, int precision, char *s, int len); +void c_dd_write(const double *a); +void c_dd_neg(const double *a, double *b); +void c_dd_rand(double *a); +void c_dd_comp(const double *a, const double *b, int *result); +void c_dd_comp_dd_d(const double *a, double b, int *result); +void c_dd_comp_d_dd(double a, const double *b, int *result); +void c_dd_pi(double *a); + +#ifdef __cplusplus +} +#endif + +#endif /* _QD_C_DD_H */ diff --git a/src/external/PackedCSparse/qd/c_dd.o b/src/external/PackedCSparse/qd/c_dd.o new file mode 100644 index 0000000000000000000000000000000000000000..35d1e5ae98a28d928b4053513f9e3f87a4e59624 GIT binary patch literal 91656 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 literal 0 HcmV?d00001 diff --git a/src/external/PackedCSparse/qd/c_qd.cc b/src/external/PackedCSparse/qd/c_qd.cc new file mode 100644 index 00000000..fd50e922 --- /dev/null +++ b/src/external/PackedCSparse/qd/c_qd.cc @@ -0,0 +1,450 @@ +/* + * src/c_qd.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains C wrapper function for quad-double precision arithmetic. + * This can be used from fortran code. + */ +#include + +#include "qd_config.h" +#include "qd_real.h" +#include "c_qd.h" + +#define TO_DOUBLE_PTR(a, ptr) ptr[0] = a.x[0]; ptr[1] = a.x[1]; \ + ptr[2] = a.x[2]; ptr[3] = a.x[3]; + +extern "C" { + + + +/* add */ +void c_qd_add(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) + qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_add_qd_dd(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) + dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_add_dd_qd(const double *a, const double *b, double *c) { + qd_real cc; + cc = dd_real(a) + qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_add_qd_d(const double *a, double b, double *c) { + qd_real cc; + cc = qd_real(a) + b; + TO_DOUBLE_PTR(cc, c); +} +void c_qd_add_d_qd(double a, const double *b, double *c) { + qd_real cc; + cc = a + qd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + + +/* sub */ +void c_qd_sub(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) - qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_sub_qd_dd(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) - dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_sub_dd_qd(const double *a, const double *b, double *c) { + qd_real cc; + cc = dd_real(a) - qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_sub_qd_d(const double *a, double b, double *c) { + qd_real cc; + cc = qd_real(a) - b; + TO_DOUBLE_PTR(cc, c); +} +void c_qd_sub_d_qd(double a, const double *b, double *c) { + qd_real cc; + cc = a - qd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + + +/* mul */ +void c_qd_mul(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) * qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_mul_qd_dd(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) * dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_mul_dd_qd(const double *a, const double *b, double *c) { + qd_real cc; + cc = dd_real(a) * qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_mul_qd_d(const double *a, double b, double *c) { + qd_real cc; + cc = qd_real(a) * b; + TO_DOUBLE_PTR(cc, c); +} +void c_qd_mul_d_qd(double a, const double *b, double *c) { + qd_real cc; + cc = a * qd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + + +/* div */ +void c_qd_div(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) / qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_div_qd_dd(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) / dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_div_dd_qd(const double *a, const double *b, double *c) { + qd_real cc; + cc = dd_real(a) / qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_div_qd_d(const double *a, double b, double *c) { + qd_real cc; + cc = qd_real(a) / b; + TO_DOUBLE_PTR(cc, c); +} +void c_qd_div_d_qd(double a, const double *b, double *c) { + qd_real cc; + cc = a / qd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + + + +/* selfadd */ +void c_qd_selfadd(const double *a, double *b) { + qd_real bb(b); + bb += qd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfadd_dd(const double *a, double *b) { + qd_real bb(b); + bb += dd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfadd_d(double a, double *b) { + qd_real bb(b); + bb += a; + TO_DOUBLE_PTR(bb, b); +} + + + +/* selfsub */ +void c_qd_selfsub(const double *a, double *b) { + qd_real bb(b); + bb -= qd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfsub_dd(const double *a, double *b) { + qd_real bb(b); + bb -= dd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfsub_d(double a, double *b) { + qd_real bb(b); + bb -= a; + TO_DOUBLE_PTR(bb, b); +} + + + +/* selfmul */ +void c_qd_selfmul(const double *a, double *b) { + qd_real bb(b); + bb *= qd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfmul_dd(const double *a, double *b) { + qd_real bb(b); + bb *= dd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfmul_d(double a, double *b) { + qd_real bb(b); + bb *= a; + TO_DOUBLE_PTR(bb, b); +} + + + +/* selfdiv */ +void c_qd_selfdiv(const double *a, double *b) { + qd_real bb(b); + bb /= qd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfdiv_dd(const double *a, double *b) { + qd_real bb(b); + bb /= dd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfdiv_d(double a, double *b) { + qd_real bb(b); + bb /= a; + TO_DOUBLE_PTR(bb, b); +} + + + +/* copy */ +void c_qd_copy(const double *a, double *b) { + b[0] = a[0]; + b[1] = a[1]; + b[2] = a[2]; + b[3] = a[3]; +} +void c_qd_copy_dd(const double *a, double *b) { + b[0] = a[0]; + b[1] = a[1]; + b[2] = 0.0; + b[3] = 0.0; +} +void c_qd_copy_d(double a, double *b) { + b[0] = a; + b[1] = 0.0; + b[2] = 0.0; + b[3] = 0.0; +} + + +void c_qd_sqrt(const double *a, double *b) { + qd_real bb; + bb = sqrt(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_sqr(const double *a, double *b) { + qd_real bb; + bb = sqr(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_abs(const double *a, double *b) { + qd_real bb; + bb = abs(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_npwr(const double *a, int n, double *b) { + qd_real bb; + bb = npwr(qd_real(a), n); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_nroot(const double *a, int n, double *b) { + qd_real bb; + bb = nroot(qd_real(a), n); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_nint(const double *a, double *b) { + qd_real bb; + bb = nint(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_aint(const double *a, double *b) { + qd_real bb; + bb = aint(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_floor(const double *a, double *b) { + qd_real bb; + bb = floor(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_ceil(const double *a, double *b) { + qd_real bb; + bb = ceil(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_log(const double *a, double *b) { + qd_real bb; + bb = log(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_log10(const double *a, double *b) { + qd_real bb; + bb = log10(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_exp(const double *a, double *b) { + qd_real bb; + bb = exp(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_sin(const double *a, double *b) { + qd_real bb; + bb = sin(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_cos(const double *a, double *b) { + qd_real bb; + bb = cos(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_tan(const double *a, double *b) { + qd_real bb; + bb = tan(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_asin(const double *a, double *b) { + qd_real bb; + bb = asin(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_acos(const double *a, double *b) { + qd_real bb; + bb = acos(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_atan(const double *a, double *b) { + qd_real bb; + bb = atan(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_atan2(const double *a, const double *b, double *c) { + qd_real cc; + cc = atan2(qd_real(a), qd_real(b)); + TO_DOUBLE_PTR(cc, c); +} + +void c_qd_sinh(const double *a, double *b) { + qd_real bb; + bb = sinh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_cosh(const double *a, double *b) { + qd_real bb; + bb = cosh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_tanh(const double *a, double *b) { + qd_real bb; + bb = tanh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_asinh(const double *a, double *b) { + qd_real bb; + bb = asinh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_acosh(const double *a, double *b) { + qd_real bb; + bb = acosh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_atanh(const double *a, double *b) { + qd_real bb; + bb = atanh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_sincos(const double *a, double *s, double *c) { + qd_real ss, cc; + sincos(qd_real(a), ss, cc); + TO_DOUBLE_PTR(cc, c); + TO_DOUBLE_PTR(ss, s); +} + +void c_qd_sincosh(const double *a, double *s, double *c) { + qd_real ss, cc; + sincosh(qd_real(a), ss, cc); + TO_DOUBLE_PTR(cc, c); + TO_DOUBLE_PTR(ss, s); +} + +void c_qd_read(const char *s, double *a) { + qd_real aa(s); + TO_DOUBLE_PTR(aa, a); +} + +void c_qd_swrite(const double *a, int precision, char *s, int len) { + qd_real(a).write(s, len, precision); +} + +void c_qd_write(const double *a) { + std::cout << qd_real(a).to_string(qd_real::_ndigits) << std::endl; +} + +void c_qd_neg(const double *a, double *b) { + b[0] = -a[0]; + b[1] = -a[1]; + b[2] = -a[2]; + b[3] = -a[3]; +} + +void c_qd_rand(double *a) { + qd_real aa; + aa = qdrand(); + TO_DOUBLE_PTR(aa, a); +} + +void c_qd_comp(const double *a, const double *b, int *result) { + qd_real aa(a), bb(b); + if (aa < bb) + *result = -1; + else if (aa > bb) + *result = 1; + else + *result = 0; +} + +void c_qd_comp_qd_d(const double *a, double b, int *result) { + qd_real aa(a); + if (aa < b) + *result = -1; + else if (aa > b) + *result = 1; + else + *result = 0; +} + +void c_qd_comp_d_qd(double a, const double *b, int *result) { + qd_real bb(b); + if (a < bb) + *result = -1; + else if (a > bb) + *result = 1; + else + *result = 0; +} + +void c_qd_pi(double *a) { + TO_DOUBLE_PTR(qd_real::_pi, a); +} + +} diff --git a/src/external/PackedCSparse/qd/c_qd.h b/src/external/PackedCSparse/qd/c_qd.h new file mode 100644 index 00000000..d11a7ff1 --- /dev/null +++ b/src/external/PackedCSparse/qd/c_qd.h @@ -0,0 +1,119 @@ +/* + * include/c_qd.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains C wrapper function prototypes for quad-double precision + * arithmetic. This can also be used from fortran code. + */ +#ifndef _QD_C_QD_H +#define _QD_C_QD_H + +#include "c_dd.h" +#include "qd_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* add */ +void c_qd_add(const double *a, const double *b, double *c); +void c_qd_add_dd_qd(const double *a, const double *b, double *c); +void c_qd_add_qd_dd(const double *a, const double *b, double *c); +void c_qd_add_d_qd(double a, const double *b, double *c); +void c_qd_add_qd_d(const double *a, double b, double *c); +void c_qd_selfadd(const double *a, double *b); +void c_qd_selfadd_dd(const double *a, double *b); +void c_qd_selfadd_d(double a, double *b); + +/* sub */ +void c_qd_sub(const double *a, const double *b, double *c); +void c_qd_sub_dd_qd(const double *a, const double *b, double *c); +void c_qd_sub_qd_dd(const double *a, const double *b, double *c); +void c_qd_sub_d_qd(double a, const double *b, double *c); +void c_qd_sub_qd_d(const double *a, double b, double *c); +void c_qd_selfsub(const double *a, double *b); +void c_qd_selfsub_dd(const double *a, double *b); +void c_qd_selfsub_d(double a, double *b); + +/* mul */ +void c_qd_mul(const double *a, const double *b, double *c); +void c_qd_mul_dd_qd(const double *a, const double *b, double *c); +void c_qd_mul_qd_dd(const double *a, const double *b, double *c); +void c_qd_mul_d_qd(double a, const double *b, double *c); +void c_qd_mul_qd_d(const double *a, double b, double *c); +void c_qd_selfmul(const double *a, double *b); +void c_qd_selfmul_dd(const double *a, double *b); +void c_qd_selfmul_d(double a, double *b); + +/* div */ +void c_qd_div(const double *a, const double *b, double *c); +void c_qd_div_dd_qd(const double *a, const double *b, double *c); +void c_qd_div_qd_dd(const double *a, const double *b, double *c); +void c_qd_div_d_qd(double a, const double *b, double *c); +void c_qd_div_qd_d(const double *a, double b, double *c); +void c_qd_selfdiv(const double *a, double *b); +void c_qd_selfdiv_dd(const double *a, double *b); +void c_qd_selfdiv_d(double a, double *b); + +/* copy */ +void c_qd_copy(const double *a, double *b); +void c_qd_copy_dd(const double *a, double *b); +void c_qd_copy_d(double a, double *b); + +void c_qd_sqrt(const double *a, double *b); +void c_qd_sqr(const double *a, double *b); + +void c_qd_abs(const double *a, double *b); + +void c_qd_npwr(const double *a, int b, double *c); +void c_qd_nroot(const double *a, int b, double *c); + +void c_qd_nint(const double *a, double *b); +void c_qd_aint(const double *a, double *b); +void c_qd_floor(const double *a, double *b); +void c_qd_ceil(const double *a, double *b); + +void c_qd_exp(const double *a, double *b); +void c_qd_log(const double *a, double *b); +void c_qd_log10(const double *a, double *b); + +void c_qd_sin(const double *a, double *b); +void c_qd_cos(const double *a, double *b); +void c_qd_tan(const double *a, double *b); + +void c_qd_asin(const double *a, double *b); +void c_qd_acos(const double *a, double *b); +void c_qd_atan(const double *a, double *b); +void c_qd_atan2(const double *a, const double *b, double *c); + +void c_qd_sinh(const double *a, double *b); +void c_qd_cosh(const double *a, double *b); +void c_qd_tanh(const double *a, double *b); + +void c_qd_asinh(const double *a, double *b); +void c_qd_acosh(const double *a, double *b); +void c_qd_atanh(const double *a, double *b); + +void c_qd_sincos(const double *a, double *s, double *c); +void c_qd_sincosh(const double *a, double *s, double *c); + +void c_qd_read(const char *s, double *a); +void c_qd_swrite(const double *a, int precision, char *s, int len); +void c_qd_write(const double *a); +void c_qd_neg(const double *a, double *b); +void c_qd_rand(double *a); +void c_qd_comp(const double *a, const double *b, int *result); +void c_qd_comp_qd_d(const double *a, double b, int *result); +void c_qd_comp_d_qd(double a, const double *b, int *result); +void c_qd_pi(double *a); + +#ifdef __cplusplus +} +#endif + +#endif /* _QD_C_QD_H */ diff --git a/src/external/PackedCSparse/qd/c_qd.o b/src/external/PackedCSparse/qd/c_qd.o new file mode 100644 index 0000000000000000000000000000000000000000..b51cf89e01a9fbaec79376ba80fdad0b69d9af7c GIT binary patch literal 156768 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 LBK>ev)!+XEmNauG literal 0 HcmV?d00001 diff --git a/src/external/PackedCSparse/qd/dd_const.cc b/src/external/PackedCSparse/qd/dd_const.cc new file mode 100644 index 00000000..38b7b5ae --- /dev/null +++ b/src/external/PackedCSparse/qd/dd_const.cc @@ -0,0 +1,40 @@ +/* + * src/dd_const.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + */ +#include "qd_config.h" +#include "dd_real.h" + +const dd_real dd_real::_2pi = dd_real(6.283185307179586232e+00, + 2.449293598294706414e-16); +const dd_real dd_real::_pi = dd_real(3.141592653589793116e+00, + 1.224646799147353207e-16); +const dd_real dd_real::_pi2 = dd_real(1.570796326794896558e+00, + 6.123233995736766036e-17); +const dd_real dd_real::_pi4 = dd_real(7.853981633974482790e-01, + 3.061616997868383018e-17); +const dd_real dd_real::_3pi4 = dd_real(2.356194490192344837e+00, + 9.1848509936051484375e-17); +const dd_real dd_real::_e = dd_real(2.718281828459045091e+00, + 1.445646891729250158e-16); +const dd_real dd_real::_log2 = dd_real(6.931471805599452862e-01, + 2.319046813846299558e-17); +const dd_real dd_real::_log10 = dd_real(2.302585092994045901e+00, + -2.170756223382249351e-16); +const dd_real dd_real::_nan = dd_real(qd::_d_nan, qd::_d_nan); +const dd_real dd_real::_inf = dd_real(qd::_d_inf, qd::_d_inf); + +const double dd_real::_eps = 4.93038065763132e-32; // 2^-104 +const double dd_real::_min_normalized = 2.0041683600089728e-292; // = 2^(-1022 + 53) +const dd_real dd_real::_max = + dd_real(1.79769313486231570815e+308, 9.97920154767359795037e+291); +const dd_real dd_real::_safe_max = + dd_real(1.7976931080746007281e+308, 9.97920154767359795037e+291); +const int dd_real::_ndigits = 31; + + diff --git a/src/external/PackedCSparse/qd/dd_const.o b/src/external/PackedCSparse/qd/dd_const.o new file mode 100644 index 0000000000000000000000000000000000000000..58d29f408bdacf60408e9f7c433f5d88b357c9be GIT binary patch literal 45776 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 literal 0 HcmV?d00001 diff --git a/src/external/PackedCSparse/qd/dd_inline.h b/src/external/PackedCSparse/qd/dd_inline.h new file mode 100644 index 00000000..89bc24f2 --- /dev/null +++ b/src/external/PackedCSparse/qd/dd_inline.h @@ -0,0 +1,621 @@ +/* + * include/dd_inline.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains small functions (suitable for inlining) in the double-double + * arithmetic package. + */ +#ifndef _QD_DD_INLINE_H +#define _QD_DD_INLINE_H + +#include +#include "inline.h" + +#ifndef QD_INLINE +#define inline +#endif + + +/*********** Additions ************/ +/* double-double = double + double */ +inline dd_real dd_real::add(double a, double b) { + double s, e; + s = qd::two_sum(a, b, e); + return dd_real(s, e); +} + +/* double-double + double */ +inline dd_real operator+(const dd_real &a, double b) { + double s1, s2; + s1 = qd::two_sum(a.x[0], b, s2); + s2 += a.x[1]; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +} + +/* double-double + double-double */ +inline dd_real dd_real::ieee_add(const dd_real &a, const dd_real &b) { + /* This one satisfies IEEE style error bound, + due to K. Briggs and W. Kahan. */ + double s1, s2, t1, t2; + + s1 = qd::two_sum(a.x[0], b.x[0], s2); + t1 = qd::two_sum(a.x[1], b.x[1], t2); + s2 += t1; + s1 = qd::quick_two_sum(s1, s2, s2); + s2 += t2; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +} + +inline dd_real dd_real::sloppy_add(const dd_real &a, const dd_real &b) { + /* This is the less accurate version ... obeys Cray-style + error bound. */ + double s, e; + + s = qd::two_sum(a.x[0], b.x[0], e); + e += (a.x[1] + b.x[1]); + s = qd::quick_two_sum(s, e, e); + return dd_real(s, e); +} + +inline dd_real operator+(const dd_real &a, const dd_real &b) { +#ifndef QD_IEEE_ADD + return dd_real::sloppy_add(a, b); +#else + return dd_real::ieee_add(a, b); +#endif +} + +/* double + double-double */ +inline dd_real operator+(double a, const dd_real &b) { + return (b + a); +} + + +/*********** Self-Additions ************/ +/* double-double += double */ +inline dd_real &dd_real::operator+=(double a) { + double s1, s2; + s1 = qd::two_sum(x[0], a, s2); + s2 += x[1]; + x[0] = qd::quick_two_sum(s1, s2, x[1]); + return *this; +} + +/* double-double += double-double */ +inline dd_real &dd_real::operator+=(const dd_real &a) { +#ifndef QD_IEEE_ADD + double s, e; + s = qd::two_sum(x[0], a.x[0], e); + e += x[1]; + e += a.x[1]; + x[0] = qd::quick_two_sum(s, e, x[1]); + return *this; +#else + double s1, s2, t1, t2; + s1 = qd::two_sum(x[0], a.x[0], s2); + t1 = qd::two_sum(x[1], a.x[1], t2); + s2 += t1; + s1 = qd::quick_two_sum(s1, s2, s2); + s2 += t2; + x[0] = qd::quick_two_sum(s1, s2, x[1]); + return *this; +#endif +} + +/*********** Subtractions ************/ +/* double-double = double - double */ +inline dd_real dd_real::sub(double a, double b) { + double s, e; + s = qd::two_diff(a, b, e); + return dd_real(s, e); +} + +/* double-double - double */ +inline dd_real operator-(const dd_real &a, double b) { + double s1, s2; + s1 = qd::two_diff(a.x[0], b, s2); + s2 += a.x[1]; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +} + +/* double-double - double-double */ +inline dd_real operator-(const dd_real &a, const dd_real &b) { +#ifndef QD_IEEE_ADD + double s, e; + s = qd::two_diff(a.x[0], b.x[0], e); + e += a.x[1]; + e -= b.x[1]; + s = qd::quick_two_sum(s, e, e); + return dd_real(s, e); +#else + double s1, s2, t1, t2; + s1 = qd::two_diff(a.x[0], b.x[0], s2); + t1 = qd::two_diff(a.x[1], b.x[1], t2); + s2 += t1; + s1 = qd::quick_two_sum(s1, s2, s2); + s2 += t2; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +#endif +} + +/* double - double-double */ +inline dd_real operator-(double a, const dd_real &b) { + double s1, s2; + s1 = qd::two_diff(a, b.x[0], s2); + s2 -= b.x[1]; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +} + +/*********** Self-Subtractions ************/ +/* double-double -= double */ +inline dd_real &dd_real::operator-=(double a) { + double s1, s2; + s1 = qd::two_diff(x[0], a, s2); + s2 += x[1]; + x[0] = qd::quick_two_sum(s1, s2, x[1]); + return *this; +} + +/* double-double -= double-double */ +inline dd_real &dd_real::operator-=(const dd_real &a) { +#ifndef QD_IEEE_ADD + double s, e; + s = qd::two_diff(x[0], a.x[0], e); + e += x[1]; + e -= a.x[1]; + x[0] = qd::quick_two_sum(s, e, x[1]); + return *this; +#else + double s1, s2, t1, t2; + s1 = qd::two_diff(x[0], a.x[0], s2); + t1 = qd::two_diff(x[1], a.x[1], t2); + s2 += t1; + s1 = qd::quick_two_sum(s1, s2, s2); + s2 += t2; + x[0] = qd::quick_two_sum(s1, s2, x[1]); + return *this; +#endif +} + +/*********** Unary Minus ***********/ +inline dd_real dd_real::operator-() const { + return dd_real(-x[0], -x[1]); +} + +/*********** Multiplications ************/ +/* double-double = double * double */ +inline dd_real dd_real::mul(double a, double b) { + double p, e; + p = qd::two_prod(a, b, e); + return dd_real(p, e); +} + +/* double-double * (2.0 ^ exp) */ +inline dd_real ldexp(const dd_real &a, int exp) { + return dd_real(std::ldexp(a.x[0], exp), std::ldexp(a.x[1], exp)); +} + +/* double-double * double, where double is a power of 2. */ +inline dd_real mul_pwr2(const dd_real &a, double b) { + return dd_real(a.x[0] * b, a.x[1] * b); +} + +/* double-double * double */ +inline dd_real operator*(const dd_real &a, double b) { + double p1, p2; + + p1 = qd::two_prod(a.x[0], b, p2); + p2 += (a.x[1] * b); + p1 = qd::quick_two_sum(p1, p2, p2); + return dd_real(p1, p2); +} + +/* double-double * double-double */ +inline dd_real operator*(const dd_real &a, const dd_real &b) { + double p1, p2; + + p1 = qd::two_prod(a.x[0], b.x[0], p2); + p2 += (a.x[0] * b.x[1] + a.x[1] * b.x[0]); + p1 = qd::quick_two_sum(p1, p2, p2); + return dd_real(p1, p2); +} + +/* double * double-double */ +inline dd_real operator*(double a, const dd_real &b) { + return (b * a); +} + +/*********** Self-Multiplications ************/ +/* double-double *= double */ +inline dd_real &dd_real::operator*=(double a) { + double p1, p2; + p1 = qd::two_prod(x[0], a, p2); + p2 += x[1] * a; + x[0] = qd::quick_two_sum(p1, p2, x[1]); + return *this; +} + +/* double-double *= double-double */ +inline dd_real &dd_real::operator*=(const dd_real &a) { + double p1, p2; + p1 = qd::two_prod(x[0], a.x[0], p2); + p2 += a.x[1] * x[0]; + p2 += a.x[0] * x[1]; + x[0] = qd::quick_two_sum(p1, p2, x[1]); + return *this; +} + +/*********** Divisions ************/ +inline dd_real dd_real::div(double a, double b) { + double q1, q2; + double p1, p2; + double s, e; + + q1 = a / b; + + /* Compute a - q1 * b */ + p1 = qd::two_prod(q1, b, p2); + s = qd::two_diff(a, p1, e); + e -= p2; + + /* get next approximation */ + q2 = (s + e) / b; + + s = qd::quick_two_sum(q1, q2, e); + + return dd_real(s, e); +} + +/* double-double / double */ +inline dd_real operator/(const dd_real &a, double b) { + + double q1, q2; + double p1, p2; + double s, e; + dd_real r; + + q1 = a.x[0] / b; /* approximate quotient. */ + + /* Compute this - q1 * d */ + p1 = qd::two_prod(q1, b, p2); + s = qd::two_diff(a.x[0], p1, e); + e += a.x[1]; + e -= p2; + + /* get next approximation. */ + q2 = (s + e) / b; + + /* renormalize */ + r.x[0] = qd::quick_two_sum(q1, q2, r.x[1]); + + return r; +} + +inline dd_real dd_real::sloppy_div(const dd_real &a, const dd_real &b) { + double s1, s2; + double q1, q2; + dd_real r; + + q1 = a.x[0] / b.x[0]; /* approximate quotient */ + + /* compute this - q1 * dd */ + r = b * q1; + s1 = qd::two_diff(a.x[0], r.x[0], s2); + s2 -= r.x[1]; + s2 += a.x[1]; + + /* get next approximation */ + q2 = (s1 + s2) / b.x[0]; + + /* renormalize */ + r.x[0] = qd::quick_two_sum(q1, q2, r.x[1]); + return r; +} + +inline dd_real dd_real::accurate_div(const dd_real &a, const dd_real &b) { + double q1, q2, q3; + dd_real r; + + q1 = a.x[0] / b.x[0]; /* approximate quotient */ + + r = a - q1 * b; + + q2 = r.x[0] / b.x[0]; + r -= (q2 * b); + + q3 = r.x[0] / b.x[0]; + + q1 = qd::quick_two_sum(q1, q2, q2); + r = dd_real(q1, q2) + q3; + return r; +} + +/* double-double / double-double */ +inline dd_real operator/(const dd_real &a, const dd_real &b) { +#ifdef QD_SLOPPY_DIV + return dd_real::sloppy_div(a, b); +#else + return dd_real::accurate_div(a, b); +#endif +} + +/* double / double-double */ +inline dd_real operator/(double a, const dd_real &b) { + return dd_real(a) / b; +} + +inline dd_real inv(const dd_real &a) { + return 1.0 / a; +} + +/*********** Self-Divisions ************/ +/* double-double /= double */ +inline dd_real &dd_real::operator/=(double a) { + *this = *this / a; + return *this; +} + +/* double-double /= double-double */ +inline dd_real &dd_real::operator/=(const dd_real &a) { + *this = *this / a; + return *this; +} + +/********** Remainder **********/ +inline dd_real drem(const dd_real &a, const dd_real &b) { + dd_real n = nint(a / b); + return (a - n * b); +} + +inline dd_real divrem(const dd_real &a, const dd_real &b, dd_real &r) { + dd_real n = nint(a / b); + r = a - n * b; + return n; +} + +/*********** Squaring **********/ +inline dd_real sqr(const dd_real &a) { + double p1, p2; + double s1, s2; + p1 = qd::two_sqr(a.x[0], p2); + p2 += 2.0 * a.x[0] * a.x[1]; + p2 += a.x[1] * a.x[1]; + s1 = qd::quick_two_sum(p1, p2, s2); + return dd_real(s1, s2); +} + +inline dd_real dd_real::sqr(double a) { + double p1, p2; + p1 = qd::two_sqr(a, p2); + return dd_real(p1, p2); +} + + +/********** Exponentiation **********/ +inline dd_real dd_real::operator^(int n) { + return npwr(*this, n); +} + + +/*********** Assignments ************/ +/* double-double = double */ +inline dd_real &dd_real::operator=(double a) { + x[0] = a; + x[1] = 0.0; + return *this; +} + +/*********** Equality Comparisons ************/ +/* double-double == double */ +inline bool operator==(const dd_real &a, double b) { + return (a.x[0] == b && a.x[1] == 0.0); +} + +/* double-double == double-double */ +inline bool operator==(const dd_real &a, const dd_real &b) { + return (a.x[0] == b.x[0] && a.x[1] == b.x[1]); +} + +/* double == double-double */ +inline bool operator==(double a, const dd_real &b) { + return (a == b.x[0] && b.x[1] == 0.0); +} + +/*********** Greater-Than Comparisons ************/ +/* double-double > double */ +inline bool operator>(const dd_real &a, double b) { + return (a.x[0] > b || (a.x[0] == b && a.x[1] > 0.0)); +} + +/* double-double > double-double */ +inline bool operator>(const dd_real &a, const dd_real &b) { + return (a.x[0] > b.x[0] || (a.x[0] == b.x[0] && a.x[1] > b.x[1])); +} + +/* double > double-double */ +inline bool operator>(double a, const dd_real &b) { + return (a > b.x[0] || (a == b.x[0] && b.x[1] < 0.0)); +} + +/*********** Less-Than Comparisons ************/ +/* double-double < double */ +inline bool operator<(const dd_real &a, double b) { + return (a.x[0] < b || (a.x[0] == b && a.x[1] < 0.0)); +} + +/* double-double < double-double */ +inline bool operator<(const dd_real &a, const dd_real &b) { + return (a.x[0] < b.x[0] || (a.x[0] == b.x[0] && a.x[1] < b.x[1])); +} + +/* double < double-double */ +inline bool operator<(double a, const dd_real &b) { + return (a < b.x[0] || (a == b.x[0] && b.x[1] > 0.0)); +} + +/*********** Greater-Than-Or-Equal-To Comparisons ************/ +/* double-double >= double */ +inline bool operator>=(const dd_real &a, double b) { + return (a.x[0] > b || (a.x[0] == b && a.x[1] >= 0.0)); +} + +/* double-double >= double-double */ +inline bool operator>=(const dd_real &a, const dd_real &b) { + return (a.x[0] > b.x[0] || (a.x[0] == b.x[0] && a.x[1] >= b.x[1])); +} + +/* double >= double-double */ +inline bool operator>=(double a, const dd_real &b) { + return (b <= a); +} + +/*********** Less-Than-Or-Equal-To Comparisons ************/ +/* double-double <= double */ +inline bool operator<=(const dd_real &a, double b) { + return (a.x[0] < b || (a.x[0] == b && a.x[1] <= 0.0)); +} + +/* double-double <= double-double */ +inline bool operator<=(const dd_real &a, const dd_real &b) { + return (a.x[0] < b.x[0] || (a.x[0] == b.x[0] && a.x[1] <= b.x[1])); +} + +/* double <= double-double */ +inline bool operator<=(double a, const dd_real &b) { + return (b >= a); +} + +/*********** Not-Equal-To Comparisons ************/ +/* double-double != double */ +inline bool operator!=(const dd_real &a, double b) { + return (a.x[0] != b || a.x[1] != 0.0); +} + +/* double-double != double-double */ +inline bool operator!=(const dd_real &a, const dd_real &b) { + return (a.x[0] != b.x[0] || a.x[1] != b.x[1]); +} + +/* double != double-double */ +inline bool operator!=(double a, const dd_real &b) { + return (a != b.x[0] || b.x[1] != 0.0); +} + +/*********** Micellaneous ************/ +/* this == 0 */ +inline bool dd_real::is_zero() const { + return (x[0] == 0.0); +} + +/* this == 1 */ +inline bool dd_real::is_one() const { + return (x[0] == 1.0 && x[1] == 0.0); +} + +/* this > 0 */ +inline bool dd_real::is_positive() const { + return (x[0] > 0.0); +} + +/* this < 0 */ +inline bool dd_real::is_negative() const { + return (x[0] < 0.0); +} + +inline dd_real::operator bool() const { + return (x[0] != 0.0); +} + +inline dd_real::operator double() const { + return to_double(*this); +} + +/* Absolute value */ +inline dd_real abs(const dd_real &a) { + return (a.x[0] < 0.0) ? -a : a; +} + +inline dd_real fabs(const dd_real &a) { + return abs(a); +} + +/* Round to Nearest integer */ +inline dd_real nint(const dd_real &a) { + double hi = qd::nint(a.x[0]); + double lo; + + if (hi == a.x[0]) { + /* High word is an integer already. Round the low word.*/ + lo = qd::nint(a.x[1]); + + /* Renormalize. This is needed if x[0] = some integer, x[1] = 1/2.*/ + hi = qd::quick_two_sum(hi, lo, lo); + } else { + /* High word is not an integer. */ + lo = 0.0; + if (std::abs(hi-a.x[0]) == 0.5 && a.x[1] < 0.0) { + /* There is a tie in the high word, consult the low word + to break the tie. */ + hi -= 1.0; /* NOTE: This does not cause INEXACT. */ + } + } + + return dd_real(hi, lo); +} + +inline dd_real floor(const dd_real &a) { + double hi = std::floor(a.x[0]); + double lo = 0.0; + + if (hi == a.x[0]) { + /* High word is integer already. Round the low word. */ + lo = std::floor(a.x[1]); + hi = qd::quick_two_sum(hi, lo, lo); + } + + return dd_real(hi, lo); +} + +inline dd_real ceil(const dd_real &a) { + double hi = std::ceil(a.x[0]); + double lo = 0.0; + + if (hi == a.x[0]) { + /* High word is integer already. Round the low word. */ + lo = std::ceil(a.x[1]); + hi = qd::quick_two_sum(hi, lo, lo); + } + + return dd_real(hi, lo); +} + +inline dd_real aint(const dd_real &a) { + return (a.x[0] >= 0.0) ? floor(a) : ceil(a); +} + +/* Cast to double. */ +inline double to_double(const dd_real &a) { + return a.x[0]; +} + +/* Cast to int. */ +inline int to_int(const dd_real &a) { + return static_cast(a.x[0]); +} + +/* Random number generator */ +inline dd_real dd_real::rand() { + return ddrand(); +} + +#endif /* _QD_DD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/dd_real.cc b/src/external/PackedCSparse/qd/dd_real.cc new file mode 100644 index 00000000..ff4d5223 --- /dev/null +++ b/src/external/PackedCSparse/qd/dd_real.cc @@ -0,0 +1,1303 @@ +/* + * src/dd_real.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + * + * Contains implementation of non-inlined functions of double-double + * package. Inlined functions are found in dd_inline.h (in include directory). + */ +#include +#include +#include +#include +#include +#include +#include + +#include "qd_config.h" +#include "dd_real.h" +#include "util.h" + +#include "bits.h" + +#ifndef QD_INLINE +#include "dd_inline.h" +#endif + +using std::cout; +using std::cerr; +using std::endl; +using std::ostream; +using std::istream; +using std::ios_base; +using std::string; +using std::setw; + +/* This routine is called whenever a fatal error occurs. */ +void dd_real::error(const char *msg) { + //if (msg) { cerr << "ERROR " << msg << endl; } +} + +/* Computes the square root of the double-double number dd. + NOTE: dd must be a non-negative number. */ +QD_API dd_real sqrt(const dd_real &a) { + /* Strategy: Use Karp's trick: if x is an approximation + to sqrt(a), then + + sqrt(a) = a*x + [a - (a*x)^2] * x / 2 (approx) + + The approximation is accurate to twice the accuracy of x. + Also, the multiplication (a*x) and [-]*x can be done with + only half the precision. + */ + + if (a.is_zero()) + return 0.0; + + if (a.is_negative()) { + dd_real::error("(dd_real::sqrt): Negative argument."); + return dd_real::_nan; + } + + double x = 1.0 / std::sqrt(a.x[0]); + double ax = a.x[0] * x; + return dd_real::add(ax, (a - dd_real::sqr(ax)).x[0] * (x * 0.5)); +} + +/* Computes the square root of a double in double-double precision. + NOTE: d must not be negative. */ +dd_real dd_real::sqrt(double d) { + return ::sqrt(dd_real(d)); +} + +/* Computes the n-th root of the double-double number a. + NOTE: n must be a positive integer. + NOTE: If n is even, then a must not be negative. */ +dd_real nroot(const dd_real &a, int n) { + /* Strategy: Use Newton iteration for the function + + f(x) = x^(-n) - a + + to find its root a^{-1/n}. The iteration is thus + + x' = x + x * (1 - a * x^n) / n + + which converges quadratically. We can then find + a^{1/n} by taking the reciprocal. + */ + + if (n <= 0) { + dd_real::error("(dd_real::nroot): N must be positive."); + return dd_real::_nan; + } + + if (n%2 == 0 && a.is_negative()) { + dd_real::error("(dd_real::nroot): Negative argument."); + return dd_real::_nan; + } + + if (n == 1) { + return a; + } + if (n == 2) { + return sqrt(a); + } + + if (a.is_zero()) + return 0.0; + + /* Note a^{-1/n} = exp(-log(a)/n) */ + dd_real r = abs(a); + dd_real x = std::exp(-std::log(r.x[0]) / n); + + /* Perform Newton's iteration. */ + x += x * (1.0 - r * npwr(x, n)) / static_cast(n); + if (a.x[0] < 0.0) + x = -x; + return 1.0/x; +} + +/* Computes the n-th power of a double-double number. + NOTE: 0^0 causes an error. */ +dd_real npwr(const dd_real &a, int n) { + + if (n == 0) { + if (a.is_zero()) { + dd_real::error("(dd_real::npwr): Invalid argument."); + return dd_real::_nan; + } + return 1.0; + } + + dd_real r = a; + dd_real s = 1.0; + int N = std::abs(n); + + if (N > 1) { + /* Use binary exponentiation */ + while (N > 0) { + if (N % 2 == 1) { + s *= r; + } + N /= 2; + if (N > 0) + r = sqr(r); + } + } else { + s = r; + } + + /* Compute the reciprocal if n is negative. */ + if (n < 0) + return (1.0 / s); + + return s; +} + +dd_real pow(const dd_real &a, int n) { + return npwr(a, n); +} + +dd_real pow(const dd_real &a, const dd_real &b) { + return exp(b * log(a)); +} + +static const int n_inv_fact = 15; +static const double inv_fact[n_inv_fact][2] = { + { 1.66666666666666657e-01, 9.25185853854297066e-18}, + { 4.16666666666666644e-02, 2.31296463463574266e-18}, + { 8.33333333333333322e-03, 1.15648231731787138e-19}, + { 1.38888888888888894e-03, -5.30054395437357706e-20}, + { 1.98412698412698413e-04, 1.72095582934207053e-22}, + { 2.48015873015873016e-05, 2.15119478667758816e-23}, + { 2.75573192239858925e-06, -1.85839327404647208e-22}, + { 2.75573192239858883e-07, 2.37677146222502973e-23}, + { 2.50521083854417202e-08, -1.44881407093591197e-24}, + { 2.08767569878681002e-09, -1.20734505911325997e-25}, + { 1.60590438368216133e-10, 1.25852945887520981e-26}, + { 1.14707455977297245e-11, 2.06555127528307454e-28}, + { 7.64716373181981641e-13, 7.03872877733453001e-30}, + { 4.77947733238738525e-14, 4.39920548583408126e-31}, + { 2.81145725434552060e-15, 1.65088427308614326e-31} +}; + +/* Exponential. Computes exp(x) in double-double precision. */ +dd_real exp(const dd_real &a) { + /* Strategy: We first reduce the size of x by noting that + + exp(kr + m * log(2)) = 2^m * exp(r)^k + + where m and k are integers. By choosing m appropriately + we can make |kr| <= log(2) / 2 = 0.347. Then exp(r) is + evaluated using the familiar Taylor series. Reducing the + argument substantially speeds up the convergence. */ + + const double k = 512.0; + const double inv_k = 1.0 / k; + + if (a.x[0] <= -709.0) + return 0.0; + + if (a.x[0] >= 709.0) + return dd_real::_inf; + + if (a.is_zero()) + return 1.0; + + if (a.is_one()) + return dd_real::_e; + + double m = std::floor(a.x[0] / dd_real::_log2.x[0] + 0.5); + dd_real r = mul_pwr2(a - dd_real::_log2 * m, inv_k); + dd_real s, t, p; + + p = sqr(r); + s = r + mul_pwr2(p, 0.5); + p *= r; + t = p * dd_real(inv_fact[0][0], inv_fact[0][1]); + int i = 0; + do { + s += t; + p *= r; + ++i; + t = p * dd_real(inv_fact[i][0], inv_fact[i][1]); + } while (std::abs(to_double(t)) > inv_k * dd_real::_eps && i < 5); + + s += t; + + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s += 1.0; + + return ldexp(s, static_cast(m)); +} + +/* Logarithm. Computes log(x) in double-double precision. + This is a natural logarithm (i.e., base e). */ +dd_real log(const dd_real &a) { + /* Strategy. The Taylor series for log converges much more + slowly than that of exp, due to the lack of the factorial + term in the denominator. Hence this routine instead tries + to determine the root of the function + + f(x) = exp(x) - a + + using Newton iteration. The iteration is given by + + x' = x - f(x)/f'(x) + = x - (1 - a * exp(-x)) + = x + a * exp(-x) - 1. + + Only one iteration is needed, since Newton's iteration + approximately doubles the number of digits per iteration. */ + + if (a.is_one()) { + return 0.0; + } + + if (a.x[0] <= 0.0) { + dd_real::error("(dd_real::log): Non-positive argument."); + return dd_real::_nan; + } + + dd_real x = std::log(a.x[0]); /* Initial approximation */ + + x = x + a * exp(-x) - 1.0; + return x; +} + +dd_real log10(const dd_real &a) { + return log(a) / dd_real::_log10; +} + +static const dd_real _pi16 = dd_real(1.963495408493620697e-01, + 7.654042494670957545e-18); + +/* Table of sin(k * pi/16) and cos(k * pi/16). */ +static const double sin_table [4][2] = { + {1.950903220161282758e-01, -7.991079068461731263e-18}, + {3.826834323650897818e-01, -1.005077269646158761e-17}, + {5.555702330196021776e-01, 4.709410940561676821e-17}, + {7.071067811865475727e-01, -4.833646656726456726e-17} +}; + +static const double cos_table [4][2] = { + {9.807852804032304306e-01, 1.854693999782500573e-17}, + {9.238795325112867385e-01, 1.764504708433667706e-17}, + {8.314696123025452357e-01, 1.407385698472802389e-18}, + {7.071067811865475727e-01, -4.833646656726456726e-17} +}; + +/* Computes sin(a) using Taylor series. + Assumes |a| <= pi/32. */ +static dd_real sin_taylor(const dd_real &a) { + const double thresh = 0.5 * std::abs(to_double(a)) * dd_real::_eps; + dd_real r, s, t, x; + + if (a.is_zero()) { + return 0.0; + } + + int i = 0; + x = -sqr(a); + s = a; + r = a; + do { + r *= x; + t = r * dd_real(inv_fact[i][0], inv_fact[i][1]); + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + return s; +} + +static dd_real cos_taylor(const dd_real &a) { + const double thresh = 0.5 * dd_real::_eps; + dd_real r, s, t, x; + + if (a.is_zero()) { + return 1.0; + } + + x = -sqr(a); + r = x; + s = 1.0 + mul_pwr2(r, 0.5); + int i = 1; + do { + r *= x; + t = r * dd_real(inv_fact[i][0], inv_fact[i][1]); + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + return s; +} + +static void sincos_taylor(const dd_real &a, + dd_real &sin_a, dd_real &cos_a) { + if (a.is_zero()) { + sin_a = 0.0; + cos_a = 1.0; + return; + } + + sin_a = sin_taylor(a); + cos_a = sqrt(1.0 - sqr(sin_a)); +} + + +dd_real sin(const dd_real &a) { + + /* Strategy. To compute sin(x), we choose integers a, b so that + + x = s + a * (pi/2) + b * (pi/16) + + and |s| <= pi/32. Using the fact that + + sin(pi/16) = 0.5 * sqrt(2 - sqrt(2 + sqrt(2))) + + we can compute sin(x) from sin(s), cos(s). This greatly + increases the convergence of the sine Taylor series. */ + + if (a.is_zero()) { + return 0.0; + } + + // approximately reduce modulo 2*pi + dd_real z = nint(a / dd_real::_2pi); + dd_real r = a - dd_real::_2pi * z; + + // approximately reduce modulo pi/2 and then modulo pi/16. + dd_real t; + double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); + t = r - dd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi16.x[0] + 0.5); + t -= _pi16 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + dd_real::error("(dd_real::sin): Cannot reduce modulo pi/2."); + return dd_real::_nan; + } + + if (abs_k > 4) { + dd_real::error("(dd_real::sin): Cannot reduce modulo pi/16."); + return dd_real::_nan; + } + + if (k == 0) { + switch (j) { + case 0: + return sin_taylor(t); + case 1: + return cos_taylor(t); + case -1: + return -cos_taylor(t); + default: + return -sin_taylor(t); + } + } + + dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); + dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); + dd_real sin_t, cos_t; + sincos_taylor(t, sin_t, cos_t); + if (j == 0) { + if (k > 0) { + r = u * sin_t + v * cos_t; + } else { + r = u * sin_t - v * cos_t; + } + } else if (j == 1) { + if (k > 0) { + r = u * cos_t - v * sin_t; + } else { + r = u * cos_t + v * sin_t; + } + } else if (j == -1) { + if (k > 0) { + r = v * sin_t - u * cos_t; + } else if (k < 0) { + r = -u * cos_t - v * sin_t; + } + } else { + if (k > 0) { + r = -u * sin_t - v * cos_t; + } else { + r = v * cos_t - u * sin_t; + } + } + + return r; +} + +dd_real cos(const dd_real &a) { + + if (a.is_zero()) { + return 1.0; + } + + // approximately reduce modulo 2*pi + dd_real z = nint(a / dd_real::_2pi); + dd_real r = a - z * dd_real::_2pi; + + // approximately reduce modulo pi/2 and then modulo pi/16 + dd_real t; + double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); + t = r - dd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi16.x[0] + 0.5); + t -= _pi16 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + dd_real::error("(dd_real::cos): Cannot reduce modulo pi/2."); + return dd_real::_nan; + } + + if (abs_k > 4) { + dd_real::error("(dd_real::cos): Cannot reduce modulo pi/16."); + return dd_real::_nan; + } + + if (k == 0) { + switch (j) { + case 0: + return cos_taylor(t); + case 1: + return -sin_taylor(t); + case -1: + return sin_taylor(t); + default: + return -cos_taylor(t); + } + } + + dd_real sin_t, cos_t; + sincos_taylor(t, sin_t, cos_t); + dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); + dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); + + if (j == 0) { + if (k > 0) { + r = u * cos_t - v * sin_t; + } else { + r = u * cos_t + v * sin_t; + } + } else if (j == 1) { + if (k > 0) { + r = - u * sin_t - v * cos_t; + } else { + r = v * cos_t - u * sin_t; + } + } else if (j == -1) { + if (k > 0) { + r = u * sin_t + v * cos_t; + } else { + r = u * sin_t - v * cos_t; + } + } else { + if (k > 0) { + r = v * sin_t - u * cos_t; + } else { + r = - u * cos_t - v * sin_t; + } + } + + return r; +} + +void sincos(const dd_real &a, dd_real &sin_a, dd_real &cos_a) { + + if (a.is_zero()) { + sin_a = 0.0; + cos_a = 1.0; + return; + } + + // approximately reduce modulo 2*pi + dd_real z = nint(a / dd_real::_2pi); + dd_real r = a - dd_real::_2pi * z; + + // approximately reduce module pi/2 and pi/16 + dd_real t; + double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); + t = r - dd_real::_pi2 * q; + int j = static_cast(q); + int abs_j = std::abs(j); + q = std::floor(t.x[0] / _pi16.x[0] + 0.5); + t -= _pi16 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (abs_j > 2) { + dd_real::error("(dd_real::sincos): Cannot reduce modulo pi/2."); + cos_a = sin_a = dd_real::_nan; + return; + } + + if (abs_k > 4) { + dd_real::error("(dd_real::sincos): Cannot reduce modulo pi/16."); + cos_a = sin_a = dd_real::_nan; + return; + } + + dd_real sin_t, cos_t; + dd_real s, c; + + sincos_taylor(t, sin_t, cos_t); + + if (abs_k == 0) { + s = sin_t; + c = cos_t; + } else { + dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); + dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); + + if (k > 0) { + s = u * sin_t + v * cos_t; + c = u * cos_t - v * sin_t; + } else { + s = u * sin_t - v * cos_t; + c = u * cos_t + v * sin_t; + } + } + + if (abs_j == 0) { + sin_a = s; + cos_a = c; + } else if (j == 1) { + sin_a = c; + cos_a = -s; + } else if (j == -1) { + sin_a = -c; + cos_a = s; + } else { + sin_a = -s; + cos_a = -c; + } + +} + +dd_real atan(const dd_real &a) { + return atan2(a, dd_real(1.0)); +} + +dd_real atan2(const dd_real &y, const dd_real &x) { + /* Strategy: Instead of using Taylor series to compute + arctan, we instead use Newton's iteration to solve + the equation + + sin(z) = y/r or cos(z) = x/r + + where r = sqrt(x^2 + y^2). + The iteration is given by + + z' = z + (y - sin(z)) / cos(z) (for equation 1) + z' = z - (x - cos(z)) / sin(z) (for equation 2) + + Here, x and y are normalized so that x^2 + y^2 = 1. + If |x| > |y|, then first iteration is used since the + denominator is larger. Otherwise, the second is used. + */ + + if (x.is_zero()) { + + if (y.is_zero()) { + /* Both x and y is zero. */ + dd_real::error("(dd_real::atan2): Both arguments zero."); + return dd_real::_nan; + } + + return (y.is_positive()) ? dd_real::_pi2 : -dd_real::_pi2; + } else if (y.is_zero()) { + return (x.is_positive()) ? dd_real(0.0) : dd_real::_pi; + } + + if (x == y) { + return (y.is_positive()) ? dd_real::_pi4 : -dd_real::_3pi4; + } + + if (x == -y) { + return (y.is_positive()) ? dd_real::_3pi4 : -dd_real::_pi4; + } + + dd_real r = sqrt(sqr(x) + sqr(y)); + dd_real xx = x / r; + dd_real yy = y / r; + + /* Compute double precision approximation to atan. */ + dd_real z = std::atan2(to_double(y), to_double(x)); + dd_real sin_z, cos_z; + + if (std::abs(xx.x[0]) > std::abs(yy.x[0])) { + /* Use Newton iteration 1. z' = z + (y - sin(z)) / cos(z) */ + sincos(z, sin_z, cos_z); + z += (yy - sin_z) / cos_z; + } else { + /* Use Newton iteration 2. z' = z - (x - cos(z)) / sin(z) */ + sincos(z, sin_z, cos_z); + z -= (xx - cos_z) / sin_z; + } + + return z; +} + +dd_real tan(const dd_real &a) { + dd_real s, c; + sincos(a, s, c); + return s/c; +} + +dd_real asin(const dd_real &a) { + dd_real abs_a = abs(a); + + if (abs_a > 1.0) { + dd_real::error("(dd_real::asin): Argument out of domain."); + return dd_real::_nan; + } + + if (abs_a.is_one()) { + return (a.is_positive()) ? dd_real::_pi2 : -dd_real::_pi2; + } + + return atan2(a, sqrt(1.0 - sqr(a))); +} + +dd_real acos(const dd_real &a) { + dd_real abs_a = abs(a); + + if (abs_a > 1.0) { + dd_real::error("(dd_real::acos): Argument out of domain."); + return dd_real::_nan; + } + + if (abs_a.is_one()) { + return (a.is_positive()) ? dd_real(0.0) : dd_real::_pi; + } + + return atan2(sqrt(1.0 - sqr(a)), a); +} + +dd_real sinh(const dd_real &a) { + if (a.is_zero()) { + return 0.0; + } + + if (abs(a) > 0.05) { + dd_real ea = exp(a); + return mul_pwr2(ea - inv(ea), 0.5); + } + + /* since a is small, using the above formula gives + a lot of cancellation. So use Taylor series. */ + dd_real s = a; + dd_real t = a; + dd_real r = sqr(t); + double m = 1.0; + double thresh = std::abs((to_double(a)) * dd_real::_eps); + + do { + m += 2.0; + t *= r; + t /= (m-1) * m; + + s += t; + } while (abs(t) > thresh); + + return s; + +} + +dd_real cosh(const dd_real &a) { + if (a.is_zero()) { + return 1.0; + } + + dd_real ea = exp(a); + return mul_pwr2(ea + inv(ea), 0.5); +} + +dd_real tanh(const dd_real &a) { + if (a.is_zero()) { + return 0.0; + } + + if (std::abs(to_double(a)) > 0.05) { + dd_real ea = exp(a); + dd_real inv_ea = inv(ea); + return (ea - inv_ea) / (ea + inv_ea); + } else { + dd_real s, c; + s = sinh(a); + c = sqrt(1.0 + sqr(s)); + return s / c; + } +} + +void sincosh(const dd_real &a, dd_real &s, dd_real &c) { + if (std::abs(to_double(a)) <= 0.05) { + s = sinh(a); + c = sqrt(1.0 + sqr(s)); + } else { + dd_real ea = exp(a); + dd_real inv_ea = inv(ea); + s = mul_pwr2(ea - inv_ea, 0.5); + c = mul_pwr2(ea + inv_ea, 0.5); + } +} + +dd_real asinh(const dd_real &a) { + return log(a + sqrt(sqr(a) + 1.0)); +} + +dd_real acosh(const dd_real &a) { + if (a < 1.0) { + dd_real::error("(dd_real::acosh): Argument out of domain."); + return dd_real::_nan; + } + + return log(a + sqrt(sqr(a) - 1.0)); +} + +dd_real atanh(const dd_real &a) { + if (abs(a) >= 1.0) { + dd_real::error("(dd_real::atanh): Argument out of domain."); + return dd_real::_nan; + } + + return mul_pwr2(log((1.0 + a) / (1.0 - a)), 0.5); +} + +QD_API dd_real fmod(const dd_real &a, const dd_real &b) { + dd_real n = aint(a / b); + return (a - b * n); +} + +QD_API dd_real ddrand() { + static const double m_const = 4.6566128730773926e-10; /* = 2^{-31} */ + double m = m_const; + dd_real r = 0.0; + double d; + + /* Strategy: Generate 31 bits at a time, using lrand48 + random number generator. Shift the bits, and reapeat + 4 times. */ + + for (int i = 0; i < 4; i++, m *= m_const) { +// d = lrand48() * m; + d = std::rand() * m; + r += d; + } + + return r; +} + +/* polyeval(c, n, x) + Evaluates the given n-th degree polynomial at x. + The polynomial is given by the array of (n+1) coefficients. */ +dd_real polyeval(const dd_real *c, int n, const dd_real &x) { + /* Just use Horner's method of polynomial evaluation. */ + dd_real r = c[n]; + + for (int i = n-1; i >= 0; i--) { + r *= x; + r += c[i]; + } + + return r; +} + +/* polyroot(c, n, x0) + Given an n-th degree polynomial, finds a root close to + the given guess x0. Note that this uses simple Newton + iteration scheme, and does not work for multiple roots. */ +QD_API dd_real polyroot(const dd_real *c, int n, + const dd_real &x0, int max_iter, double thresh) { + dd_real x = x0; + dd_real f; + dd_real *d = new dd_real[n]; + bool conv = false; + int i; + double max_c = std::abs(to_double(c[0])); + double v; + + if (thresh == 0.0) thresh = dd_real::_eps; + + /* Compute the coefficients of the derivatives. */ + for (i = 1; i <= n; i++) { + v = std::abs(to_double(c[i])); + if (v > max_c) max_c = v; + d[i-1] = c[i] * static_cast(i); + } + thresh *= max_c; + + /* Newton iteration. */ + for (i = 0; i < max_iter; i++) { + f = polyeval(c, n, x); + + if (abs(f) < thresh) { + conv = true; + break; + } + x -= (f / polyeval(d, n-1, x)); + } + delete [] d; + + if (!conv) { + dd_real::error("(dd_real::polyroot): Failed to converge."); + return dd_real::_nan; + } + + return x; +} + + +/* Constructor. Reads a double-double number from the string s + and constructs a double-double number. */ +dd_real::dd_real(const char *s) { + if (dd_real::read(s, *this)) { + dd_real::error("(dd_real::dd_real): INPUT ERROR."); + *this = dd_real::_nan; + } +} + +dd_real &dd_real::operator=(const char *s) { + if (dd_real::read(s, *this)) { + dd_real::error("(dd_real::operator=): INPUT ERROR."); + *this = dd_real::_nan; + } + return *this; +} + +/* Outputs the double-double number dd. */ +ostream &operator<<(ostream &os, const dd_real &dd) { + bool showpos = (os.flags() & ios_base::showpos) != 0; + bool uppercase = (os.flags() & ios_base::uppercase) != 0; + return os << dd.to_string((int)os.precision(), (int)os.width(), os.flags(), + showpos, uppercase, os.fill()); +} + +/* Reads in the double-double number a. */ +istream &operator>>(istream &s, dd_real &a) { + char str[255]; + s >> str; + a = dd_real(str); + return s; +} + +void dd_real::to_digits(char *s, int &expn, int precision) const { + int D = precision + 1; /* number of digits to compute */ + + dd_real r = abs(*this); + int e; /* exponent */ + int i, d; + + if (x[0] == 0.0) { + /* this == 0.0 */ + expn = 0; + for (i = 0; i < precision; i++) s[i] = '0'; + return; + } + + /* First determine the (approximate) exponent. */ + e = to_int(std::floor(std::log10(std::abs(x[0])))); + + if (e < -300) { + r *= dd_real(10.0) ^ 300; + r /= dd_real(10.0) ^ (e + 300); + } else if (e > 300) { + r = ldexp(r, -53); + r /= dd_real(10.0) ^ e; + r = ldexp(r, 53); + } else { + r /= dd_real(10.0) ^ e; + } + + /* Fix exponent if we are off by one */ + if (r >= 10.0) { + r /= 10.0; + e++; + } else if (r < 1.0) { + r *= 10.0; + e--; + } + + if (r >= 10.0 || r < 1.0) { + dd_real::error("(dd_real::to_digits): can't compute exponent."); + return; + } + + /* Extract the digits */ + for (i = 0; i < D; i++) { + d = static_cast(r.x[0]); + r -= d; + r *= 10.0; + + s[i] = static_cast(d + '0'); + } + + /* Fix out of range digits. */ + for (i = D-1; i > 0; i--) { + if (s[i] < '0') { + s[i-1]--; + s[i] += 10; + } else if (s[i] > '9') { + s[i-1]++; + s[i] -= 10; + } + } + + if (s[0] <= '0') { + dd_real::error("(dd_real::to_digits): non-positive leading digit."); + return; + } + + /* Round, handle carry */ + if (s[D-1] >= '5') { + s[D-2]++; + + i = D-2; + while (i > 0 && s[i] > '9') { + s[i] -= 10; + s[--i]++; + } + } + + /* If first digit is 10, shift everything. */ + if (s[0] > '9') { + e++; + for (i = precision; i >= 2; i--) s[i] = s[i-1]; + s[0] = '1'; + s[1] = '0'; + } + + s[precision] = 0; + expn = e; +} + +/* Writes the double-double number into the character array s of length len. + The integer d specifies how many significant digits to write. + The string s must be able to hold at least (d+8) characters. + showpos indicates whether to use the + sign, and uppercase indicates + whether the E or e is to be used for the exponent. */ +void dd_real::write(char *s, int len, int precision, + bool showpos, bool uppercase) const { + string str = to_string(precision, 0, ios_base::scientific, showpos, uppercase); + std::strncpy(s, str.c_str(), len-1); + s[len-1] = 0; +} + + +void round_string(char *s, int precision, int *offset){ + /* + Input string must be all digits or errors will occur. + */ + + int i; + int D = precision ; + + /* Round, handle carry */ + if (D>0 && s[D] >= '5') { + s[D-1]++; + + i = D-1; + while (i > 0 && s[i] > '9') { + s[i] -= 10; + s[--i]++; + } + } + + /* If first digit is 10, shift everything. */ + if (s[0] > '9') { + // e++; // don't modify exponent here + for (i = precision; i >= 1; i--) s[i+1] = s[i]; + s[0] = '1'; + s[1] = '0'; + + (*offset)++ ; // now offset needs to be increased by one + precision++ ; + } + + s[precision] = 0; // add terminator for array +} + +string dd_real::to_string(int precision, int width, ios_base::fmtflags fmt, + bool showpos, bool uppercase, char fill) const { + string s; + bool fixed = (fmt & ios_base::fixed) != 0; + bool sgn = true; + int i, e = 0; + + if (isnan()) { + s = uppercase ? "NAN" : "nan"; + sgn = false; + } else { + if (*this < 0.0) + s += '-'; + else if (showpos) + s += '+'; + else + sgn = false; + + if (isinf()) { + s += uppercase ? "INF" : "inf"; + } else if (*this == 0.0) { + /* Zero case */ + s += '0'; + if (precision > 0) { + s += '.'; + s.append(precision, '0'); + } + } else { + /* Non-zero case */ + int off = (fixed ? (1 + to_int(floor(log10(abs(*this))))) : 1); + int d = precision + off; + + int d_with_extra = d; + if(fixed) + d_with_extra = std::max(60, d); // longer than the max accuracy for DD + + // highly special case - fixed mode, precision is zero, abs(*this) < 1.0 + // without this trap a number like 0.9 printed fixed with 0 precision prints as 0 + // should be rounded to 1. + if(fixed && (precision == 0) && (abs(*this) < 1.0)){ + if(abs(*this) >= 0.5) + s += '1'; + else + s += '0'; + + return s; + } + + // handle near zero to working precision (but not exactly zero) + if (fixed && d <= 0) { + s += '0'; + if (precision > 0) { + s += '.'; + s.append(precision, '0'); + } + } else { // default + + char *t; // = new char[d+1]; + int j; + + if(fixed){ + t = new char[d_with_extra+1]; + to_digits(t, e, d_with_extra); + } + else{ + t = new char[d+1]; + to_digits(t, e, d); + } + + off = e + 1; + + if (fixed) { + // fix the string if it's been computed incorrectly + // round here in the decimal string if required + round_string(t, d, &off); + + if (off > 0) { + for (i = 0; i < off; i++) s += t[i]; + if (precision > 0) { + s += '.'; + for (j = 0; j < precision; j++, i++) s += t[i]; + } + } else { + s += "0."; + if (off < 0) s.append(-off, '0'); + for (i = 0; i < d; i++) s += t[i]; + } + } else { + s += t[0]; + if (precision > 0) s += '.'; + + for (i = 1; i <= precision; i++) + s += t[i]; + + } + delete [] t; + } + } + + // trap for improper offset with large values + // without this trap, output of values of the for 10^j - 1 fail for j > 28 + // and are output with the point in the wrong place, leading to a dramatically off value + if(fixed && (precision > 0)){ + // make sure that the value isn't dramatically larger + double from_string = atof(s.c_str()); + + // if this ratio is large, then we've got problems + if( fabs( from_string / this->x[0] ) > 3.0 ){ + + // loop on the string, find the point, move it up one + // don't act on the first character + for(i=1; i < (int)s.length(); i++){ + if(s[i] == '.'){ + s[i] = s[i-1] ; + s[i-1] = '.' ; + break; + } + } + + from_string = atof(s.c_str()); + // if this ratio is large, then the string has not been fixed + if( fabs( from_string / this->x[0] ) > 3.0 ){ + dd_real::error("Re-rounding unsuccessful in large number fixed point trap.") ; + } + } + } + + + if (!fixed && !isinf()) { + /* Fill in exponent part */ + s += uppercase ? 'E' : 'e'; + append_expn(s, e); + } + } + + /* Fill in the blanks */ + int len = s.length(); + if (len < width) { + int delta = width - len; + if (fmt & ios_base::internal) { + if (sgn) + s.insert(static_cast(1), delta, fill); + else + s.insert(static_cast(0), delta, fill); + } else if (fmt & ios_base::left) { + s.append(delta, fill); + } else { + s.insert(static_cast(0), delta, fill); + } + } + + return s; +} + +/* Reads in a double-double number from the string s. */ +int dd_real::read(const char *s, dd_real &a) { + const char *p = s; + char ch; + int sign = 0; + int point = -1; + int nd = 0; + int e = 0; + bool done = false; + dd_real r = 0.0; + int nread; + + /* Skip any leading spaces */ + while (*p == ' ') + p++; + + while (!done && (ch = *p) != '\0') { + if (ch >= '0' && ch <= '9') { + int d = ch - '0'; + r *= 10.0; + r += static_cast(d); + nd++; + } else { + + switch (ch) { + + case '.': + if (point >= 0) + return -1; + point = nd; + break; + + case '-': + case '+': + if (sign != 0 || nd > 0) + return -1; + sign = (ch == '-') ? -1 : 1; + break; + + case 'E': + case 'e': + nread = std::sscanf(p+1, "%d", &e); + done = true; + if (nread != 1) + return -1; + break; + + default: + return -1; + } + } + + p++; + } + + if (point >= 0) { + e -= (nd - point); + } + + if (e != 0) { + r *= (dd_real(10.0) ^ e); + } + + a = (sign == -1) ? -r : r; + return 0; +} + +/* Debugging routines */ +void dd_real::dump(const string &name, std::ostream &os) const { + std::ios_base::fmtflags old_flags = os.flags(); + std::streamsize old_prec = os.precision(19); + os << std::scientific; + + if (name.length() > 0) os << name << " = "; + os << "[ " << setw(27) << x[0] << ", " << setw(27) << x[1] << " ]" << endl; + + os.precision(old_prec); + os.flags(old_flags); +} + +void dd_real::dump_bits(const string &name, std::ostream &os) const { + string::size_type len = name.length(); + if (len > 0) { + os << name << " = "; + len +=3; + } + os << "[ "; + len += 2; + print_double_info(os, x[0]); + os << endl; + for (string::size_type i = 0; i < len; i++) os << ' '; + print_double_info(os, x[1]); + os << " ]" << endl; +} + +dd_real dd_real::debug_rand() { + + if (std::rand() % 2 == 0) + return ddrand(); + + int expn = 0; + dd_real a = 0.0; + double d; + for (int i = 0; i < 2; i++) { + d = std::ldexp(static_cast(std::rand()) / RAND_MAX, -expn); + a += d; + expn = expn + 54 + std::rand() % 200; + } + return a; +} diff --git a/src/external/PackedCSparse/dd_real.h b/src/external/PackedCSparse/qd/dd_real.h similarity index 92% rename from src/external/PackedCSparse/dd_real.h rename to src/external/PackedCSparse/qd/dd_real.h index 5fe3491e..e16438aa 100644 --- a/src/external/PackedCSparse/dd_real.h +++ b/src/external/PackedCSparse/qd/dd_real.h @@ -1,12 +1,3 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from QD library -//(https://www.davidhbailey.com/dhbsoftware) by Ioannis Iakovidis - /* * include/dd_real.h * @@ -18,14 +9,14 @@ * * Double-double precision (>= 106-bit significand) floating point * arithmetic package based on David Bailey's Fortran-90 double-double - * package, with some changes. See + * package, with some changes. See * * http://www.nersc.gov/~dhbailey/mpdist/mpdist.html - * + * * for the original Fortran-90 version. * * Overall structure is similar to that of Keith Brigg's C++ double-double - * package. See + * package. See * * http://www-epidem.plansci.cam.ac.uk/~kbriggs/doubledouble.html * @@ -42,8 +33,8 @@ #include #include #include -#include "qd/qd_config.h" -#include "qd/fpu.h" +#include "qd_config.h" +#include "fpu.h" // Some compilers define isnan, isfinite, and isinf as macros, even for // C++ codes, which cause havoc when overloading these functions. We undef @@ -70,11 +61,7 @@ struct QD_API dd_real { double x[2]; - inline operator bool() const // new - { - return (x[0] != 0.0); - } dd_real(double hi, double lo) { x[0] = hi; x[1] = lo; } dd_real() {x[0] = 0.0; x[1] = 0.0; } dd_real(double h) { x[0] = h; x[1] = 0.0; } @@ -136,7 +123,7 @@ struct QD_API dd_real { static dd_real div(double a, double b); static dd_real sloppy_div(const dd_real &a, const dd_real &b); static dd_real accurate_div(const dd_real &a, const dd_real &b); - + dd_real &operator/=(double a); dd_real &operator/=(const dd_real &a); @@ -147,26 +134,28 @@ struct QD_API dd_real { static dd_real sqr(double d); static dd_real sqrt(double a); - + bool is_zero() const; bool is_one() const; bool is_positive() const; bool is_negative() const; + explicit operator bool() const; // new + explicit operator double() const; // new static dd_real rand(void); void to_digits(char *s, int &expn, int precision = _ndigits) const; - void write(char *s, int len, int precision = _ndigits, + void write(char *s, int len, int precision = _ndigits, bool showpos = false, bool uppercase = false) const; - std::string to_string(int precision = _ndigits, int width = 0, - std::ios_base::fmtflags fmt = static_cast(0), + std::string to_string(int precision = _ndigits, int width = 0, + std::ios_base::fmtflags fmt = static_cast(0), bool showpos = false, bool uppercase = false, char fill = ' ') const; int read(const char *s, dd_real &a); /* Debugging Methods */ void dump(const std::string &name, std::ostream &os = std::cerr) const; - void dump_bits(const std::string &name, + void dump_bits(const std::string &name, std::ostream &os = std::cerr) const; static dd_real debug_rand(); @@ -190,7 +179,7 @@ QD_API dd_real ddrand(void); QD_API dd_real sqrt(const dd_real &a); QD_API dd_real polyeval(const dd_real *c, int n, const dd_real &x); -QD_API dd_real polyroot(const dd_real *c, int n, +QD_API dd_real polyroot(const dd_real *c, int n, const dd_real &x0, int max_iter = 32, double thresh = 0.0); QD_API inline bool isnan(const dd_real &a) { return a.isnan(); } @@ -282,7 +271,7 @@ QD_API dd_real atan2(const dd_real &y, const dd_real &x); QD_API dd_real sinh(const dd_real &a); QD_API dd_real cosh(const dd_real &a); QD_API dd_real tanh(const dd_real &a); -QD_API void sincosh(const dd_real &a, +QD_API void sincosh(const dd_real &a, dd_real &sinh_a, dd_real &cosh_a); QD_API dd_real asinh(const dd_real &a); @@ -297,7 +286,8 @@ QD_API dd_real fmod(const dd_real &a, const dd_real &b); QD_API std::ostream& operator<<(std::ostream &s, const dd_real &a); QD_API std::istream& operator>>(std::istream &s, dd_real &a); #ifdef QD_INLINE -#include "qd/dd_inline.h" +#include "dd_inline.h" #endif #endif /* _QD_DD_REAL_H */ + diff --git a/src/external/PackedCSparse/qd/dd_real.o b/src/external/PackedCSparse/qd/dd_real.o new file mode 100644 index 0000000000000000000000000000000000000000..7faf865a382556ee1c54148539943bcd9f4aab68 GIT binary patch literal 279272 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 literal 0 HcmV?d00001 diff --git a/src/external/PackedCSparse/qd/fpu.cc b/src/external/PackedCSparse/qd/fpu.cc new file mode 100644 index 00000000..96ddc488 --- /dev/null +++ b/src/external/PackedCSparse/qd/fpu.cc @@ -0,0 +1,124 @@ +/* + * src/fpu.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains functions to set and restore the round-to-double flag in the + * control word of a x86 FPU. + */ + +#include "qd_config.h" +#include "fpu.h" + +#ifdef X86 +#ifdef _WIN32 +#include +#else + +#ifdef HAVE_FPU_CONTROL_H +#include +#endif + +#ifndef _FPU_GETCW +#define _FPU_GETCW(x) asm volatile ("fnstcw %0":"=m" (x)); +#endif + +#ifndef _FPU_SETCW +#define _FPU_SETCW(x) asm volatile ("fldcw %0": :"m" (x)); +#endif + +#ifndef _FPU_EXTENDED +#define _FPU_EXTENDED 0x0300 +#endif + +#ifndef _FPU_DOUBLE +#define _FPU_DOUBLE 0x0200 +#endif + +#endif +#endif /* X86 */ + +extern "C" { + +void fpu_fix_start(unsigned int *old_cw) { +#ifdef X86 +#ifdef _WIN32 +#ifdef __BORLANDC__ + /* Win 32 Borland C */ + unsigned short cw = _control87(0, 0); + _control87(0x0200, 0x0300); + if (old_cw) { + *old_cw = cw; + } +#else + /* Win 32 MSVC */ + unsigned int cw = _control87(0, 0); + _control87(0x00010000, 0x00030000); + if (old_cw) { + *old_cw = cw; + } +#endif +#else + /* Linux */ + volatile unsigned short cw, new_cw; + _FPU_GETCW(cw); + + new_cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE; + _FPU_SETCW(new_cw); + + if (old_cw) { + *old_cw = cw; + } +#endif +#endif +} + +void fpu_fix_end(unsigned int *old_cw) { +#ifdef X86 +#ifdef _WIN32 + +#ifdef __BORLANDC__ + /* Win 32 Borland C */ + if (old_cw) { + unsigned short cw = (unsigned short) *old_cw; + _control87(cw, 0xFFFF); + } +#else + /* Win 32 MSVC */ + if (old_cw) { + _control87(*old_cw, 0xFFFFFFFF); + } +#endif + +#else + /* Linux */ + if (old_cw) { + int cw; + cw = *old_cw; + _FPU_SETCW(cw); + } +#endif +#endif +} + +#ifdef HAVE_FORTRAN + +#define f_fpu_fix_start FC_FUNC_(f_fpu_fix_start, F_FPU_FIX_START) +#define f_fpu_fix_end FC_FUNC_(f_fpu_fix_end, F_FPU_FIX_END) + +void f_fpu_fix_start(unsigned int *old_cw) { + fpu_fix_start(old_cw); +} + +void f_fpu_fix_end(unsigned int *old_cw) { + fpu_fix_end(old_cw); +} + +#endif + +} + diff --git a/src/external/PackedCSparse/qd/fpu.h b/src/external/PackedCSparse/qd/fpu.h new file mode 100644 index 00000000..35eab18c --- /dev/null +++ b/src/external/PackedCSparse/qd/fpu.h @@ -0,0 +1,39 @@ +/* + * include/fpu.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2001 + * + * Contains functions to set and restore the round-to-double flag in the + * control word of a x86 FPU. The algorithms in the double-double and + * quad-double package does not function with the extended mode found in + * these FPU. + */ +#ifndef _QD_FPU_H +#define _QD_FPU_H + +#include "qd_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Set the round-to-double flag, and save the old control word in old_cw. + * If old_cw is NULL, the old control word is not saved. + */ +QD_API void fpu_fix_start(unsigned int *old_cw); + +/* + * Restore the control word. + */ +QD_API void fpu_fix_end(unsigned int *old_cw); + +#ifdef __cplusplus +} +#endif + +#endif /* _QD_FPU_H */ diff --git a/src/external/PackedCSparse/qd/fpu.o b/src/external/PackedCSparse/qd/fpu.o new file mode 100644 index 0000000000000000000000000000000000000000..eec0bb9bff461a747768916c6447c39062ef81ad GIT binary patch literal 15136 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 Ge*PC62!ELX literal 0 HcmV?d00001 diff --git a/src/external/PackedCSparse/qd/inline.h b/src/external/PackedCSparse/qd/inline.h new file mode 100644 index 00000000..52425545 --- /dev/null +++ b/src/external/PackedCSparse/qd/inline.h @@ -0,0 +1,143 @@ +/* + * include/inline.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * This file contains the basic functions used both by double-double + * and quad-double package. These are declared as inline functions as + * they are the smallest building blocks of the double-double and + * quad-double arithmetic. + */ +#ifndef _QD_INLINE_H +#define _QD_INLINE_H + +#define _QD_SPLITTER 134217729.0 // = 2^27 + 1 +#define _QD_SPLIT_THRESH 6.69692879491417e+299 // = 2^996 + +#ifdef QD_VACPP_BUILTINS_H +/* For VisualAge C++ __fmadd */ +#include +#endif + +#include +#include + +namespace qd { + +static const double _d_nan = std::numeric_limits::quiet_NaN(); +static const double _d_inf = std::numeric_limits::infinity(); + +/*********** Basic Functions ************/ +/* Computes fl(a+b) and err(a+b). Assumes |a| >= |b|. */ +inline double quick_two_sum(double a, double b, double &err) { + double s = a + b; + err = b - (s - a); + return s; +} + +/* Computes fl(a-b) and err(a-b). Assumes |a| >= |b| */ +inline double quick_two_diff(double a, double b, double &err) { + double s = a - b; + err = (a - s) - b; + return s; +} + +/* Computes fl(a+b) and err(a+b). */ +inline double two_sum(double a, double b, double &err) { + double s = a + b; + double bb = s - a; + err = (a - (s - bb)) + (b - bb); + return s; +} + +/* Computes fl(a-b) and err(a-b). */ +inline double two_diff(double a, double b, double &err) { + double s = a - b; + double bb = s - a; + err = (a - (s - bb)) - (b + bb); + return s; +} + +#ifndef QD_FMS +/* Computes high word and lo word of a */ +inline void split(double a, double &hi, double &lo) { + double temp; + if (a > _QD_SPLIT_THRESH || a < -_QD_SPLIT_THRESH) { + a *= 3.7252902984619140625e-09; // 2^-28 + temp = _QD_SPLITTER * a; + hi = temp - (temp - a); + lo = a - hi; + hi *= 268435456.0; // 2^28 + lo *= 268435456.0; // 2^28 + } else { + temp = _QD_SPLITTER * a; + hi = temp - (temp - a); + lo = a - hi; + } +} +#endif + +/* Computes fl(a*b) and err(a*b). */ +inline double two_prod(double a, double b, double &err) { +#ifdef QD_FMS + double p = a * b; + err = QD_FMS(a, b, p); + return p; +#else + double a_hi, a_lo, b_hi, b_lo; + double p = a * b; + split(a, a_hi, a_lo); + split(b, b_hi, b_lo); + err = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo; + return p; +#endif +} + +/* Computes fl(a*a) and err(a*a). Faster than the above method. */ +inline double two_sqr(double a, double &err) { +#ifdef QD_FMS + double p = a * a; + err = QD_FMS(a, a, p); + return p; +#else + double hi, lo; + double q = a * a; + split(a, hi, lo); + err = ((hi * hi - q) + 2.0 * hi * lo) + lo * lo; + return q; +#endif +} + +/* Computes the nearest integer to d. */ +inline double nint(double d) { + if (d == std::floor(d)) + return d; + return std::floor(d + 0.5); +} + +/* Computes the truncated integer. */ +inline double aint(double d) { + return (d >= 0.0) ? std::floor(d) : std::ceil(d); +} + +/* These are provided to give consistent + interface for double with double-double and quad-double. */ +inline void sincosh(double t, double &sinh_t, double &cosh_t) { + sinh_t = std::sinh(t); + cosh_t = std::cosh(t); +} + +inline double sqr(double t) { + return t * t; +} + +inline double to_double(double a) { return a; } +inline int to_int(double a) { return static_cast(a); } + +} + +#endif /* _QD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/libqd.a b/src/external/PackedCSparse/qd/libqd.a new file mode 100644 index 0000000000000000000000000000000000000000..1e0fb470e9a95016039e19ced0fc44ece180f425 GIT binary patch literal 1196610 zcmeFa349er*7x6iZ{OsGgd`ATiJ-w7Fj2CSu!SH?fFOuKKtPd8R)}UZxnUC}Dx+io zML}iUKtaWkQFL5z8`+}Zii#uSK7xvh3W^ShBk%8=I^DNZ;5^SfGoR1ON^9%pDJ}BZMQXULJ|v~MtX_GFOB$4? zrf!a)n)=#WQL90vMbGh7RMpmsW@&k)6u7E(rqY8aD^q#Vc*@gIS))9qwUSw=SV*xG zSL{R>(B~*^SP$SbDyy>3 z$+T3i*s!kZqew)(Qsu=qxRbV0QzpZa2&?R<2cN7u;H|4eecG;0^*^W%_x*6*b~0q* zP8H4_qscCd8Yq+r28ydXGHzUI9IGmzhLIiDP+nD`hBwknKwVyUSf!-sa0{wHD_w5J zur8|7D$q)os~wJZszRBl^0$iQ0gfWcuos_W(U}9N)M^e;J~U%*fN*b8R@I~c6`2&s zCzAqs)l>jqvE#ER1wfe;;8T-=^6;bpubLF#Q3D?yH7?;%!yX=)6v*d9%A`O{CIxt9 z)Wau(9zGfKq!^ASJ2c>lh85IAAPvbAgy_hm0PO~QCV=1NRg(gkObYPHq#!<5kCI8j z?o$87q(Fm5#&b?EyRLFL4<{kLgBr*I$Zn-VB&QY04ffVicCcJi!KG?#rw?7dtjtzw zIl-FZ8javqsFCX8xvfb5U_)_5c~HkW!ScF>R*G4b_wPlFQ>EjHFKCMV!Rah5vN}J0=u$6W$4@C`~U^Azpy0$E=r0UeCtc-@+ zY?QrUU2WC8^4Y~zJy2tzp^HXB?74gPtHt@763X63wax zEcwD^A{{65{-N4nLkLU6nL~3+=g!T_8eUmh6w2xy997#898n#rs4AY>P*Osjv~?os z(&I`eR8mK*M;?_E>ebf9pX%qLiD-QQFP``A-^(rN~|__ z3T-`i(p??BTjFrBbNQa$3gytfy%i~&9S;73l_`xpvFsXoLbQ?BdR^Cgu}fLV%q)qy zi`%xH)iah)&T`Y0w=CURQY{TU^E6O|M)_ekj9HD5TF z;NLKr{c2mIOWPl*@G+w@+{0DNgc~BexU{sfzBp7)A2IkcwMzPr6qPA^#S(61XL;gk zMp-a~m3pf?cNl~h48ojSu)Sai>pBYtK~j>k`toYMrhQN?9jn`8Z?(P%Tea60Zlagc zUSGI$WPRZxvK9yv*ZM-BQ#ogS5!QCr7jATdO?!PIN~#I^Tz1m+h3K4Yec_Ih^@X5+ z;(mr3cUEy-9d2O!xM{-wc0XOC)PE*pAt8%Uh03*?fEl1?C+7iqf)T{Zmj7Rmf8)Nk!c01u%7N z>Xze_cx%JquU1qnpEP9hkPC)P8Zxn{@R|et3Y(9W6)v%Qq(Hy0xga^Sa7kq0lEVFC zmXyW$3tudV^CwUYNWqe-xXuO5Utaaev0siKzv?Bbbo0cx&V@?`?}uV!{HaiEIhtCK z9KRnv^%M|;1;yc zpu%OjlXFBkqVSW4R#3vuFHVyr&==}kx)|Ar4pdSG?zuAQ3mfXXs+^h4)>`%^r^aB7NHUVnrVjyC_)L@35NfQbAd7Moc@4v7_>E2oPlKBW zcNN@vxR>DOqMuw1w*qb*+~aU>!tI6gcrftc^5BNU-3GS~?jg7z;UZx>8Ey!iX<9}* zv)F3a-dbxGueRFvK%mE!9H7XIkXg*~9B(^gwRO&jwsl54HF{iPyM?E&w#Ht<{E{op zairE{?l;Xz)_(IRpIK!6v|tqzTS7255c>9YdN^qw}XN^e9aMvrP4lV{%o4l z$oH7}v(KESD#GA7^B13au>u#9#(wj9k2%g-?zs~}fuBupyQJ0D&*pMeHjSeQP^8rO zxMu^Zs=#|5RQf$?32K7X)C8-&>%3;E9nDSsp64@9+u5xy0x5IcYA>u^KRZPakZ(r|t>A6lwdTFP&& z^=(9u%>UK1|P zN*LJXoG#h;4|t@{7?qHNSU-6TBdQ~WQZkG-mWe=*=x8!)7||)P)D?H=7*^_`Xj>153MjfLbQA>;{1YFw?MapF>}VpPUCAy^rBI8+46dPJoSIRG zXBgeWK$$X()6$LBO=uXWyR5)^Qd<;TU59=Mc27nv2SrE2dj=`mO&NfiTrN^e?&ON= zVWQOyqi1)Nvo9jgBpcDugWyd`$3+caK*LCl7QUw83#axJEhY#@?hB2O$}op-M;dLI;IBo1U=^zZmhv}63568H!+N%b}sg%9ELXH z9#bGOj@{wuX1i0{M-1*fze^Ub!w8?@IfFbSbHr1aZjMYqCoKaUMZHjspmQ+Ib^XtiXyjuUW5an!hl-{ycRuA6?)Kv-$8d0bH!~}NiZ&&BYV>4WfeAjN zYg;N>Q8%M?3|7{j&J;7rmBvSDE`UvrnT*77q%tKM^NC?hCCy0z+xCUg+zN(q5w%Z_ z8kE!A#im)~Vt2;492VAgKEw3Gi#2n1I5}_wR|oFi;ltzgTC}J!&-L7GL|Q&yM1Y&mgk!GC zpr_@zE&6mLI?0Ug?ukCbiVl!svQq4!6k{$x)l;p4wtG;b6z|z)Y~)PZ99wGKVfml zF&1|Q+LN)n`m$Ivg2g>ESgf7RV%=|9+_#g(1AAF)FwwJOBOf}I#l{R4j~1}_V+o7L z8d*HPn8g#fvv~3$7Mr%Pcxp-qvhs8Vi)XH6@$4-uo_mPJpZ?6^`7c;(iN;hB8@VmO zVtXEomWeE0sAlovZ&>VD$ztapSiIE2;^hxmyz&E!SA8AH*lQhGyq?Np*FY9;oX_IT z5{S6SXfp=4;<(5dGnr*ua|p{*O)5`Z%hDFxZEV6H6k#i4=+~2e4^C*kH zPqXO1mBoNJS>%1fV&Hcy26=EZh;4sPdlo|ySqx2QF>DZvg0U<{OlL8&f<<8si&3*# zjJ}e^nCn@bcNdGX53v~cCl=#hBXLUZpIP+T!=mq3Bsvd1!eU4SW|r8_Lp!h-b_R=r zOcuiju^2Ii#mEa;6wYEXYBq~=7qJ+96N@oxSWJEhqTSE2r{=ss;jRncX3_K^i>r-9 zg4af{Xzt2lX)hMb@>pCyn#Bzlu~@#A#f_U--1I7o6@Ou|@>3Q!|G?rFAD$J&cD*%` z#qZ8yaoag8ZlA*9&UzMWma$m-I~MEKvbc8>i~F{-xc?It>yNQ`phH(O{a`YS4f!k% zj%V>*1B=58S^V%O_13OG-oT+F>sTE9jKxonu{icBi=TfY(Y+)VpCe9hgnqbWKr`Wi`v61>Y`2~)0dsbqJAighG{H9Ar_6- zvzYw{7IU6sG54=5<{e^jhFi;E|)m|n~xSjS?vnaWfMd=0> zWzVoEe~CrKhb(4(&0^NiEGnbVK(=QMOk|PYi^ZUCXr?`D@E{Hi8OdVkMJ$HZvM9KQ z#qgV0jChd6$fsEp?j(_3^f`-)A6d-v1qd$fz~b%{7LWC1@!|*;@6wVZHvQ`|h`4ma zY+#vSUd=MayoF_+`7q0|<~Eka=9?@-=3iMhnLn|-*NjajJDbdIEMGQzvHZ}?V|mCt zmu0FomE~Dh3ClF=QkLn~e3ltjGs`S%70YbvewMwhCs^iMEiC(3yIJk~Nm)1=cK+PPOh} zd7jIV)Rt3wM)@+uQ*0n4z zv2JF0skM$}jrB*CwbnM4b=F%f>#e;k8?5hHhAeLi{oS-)rb8|!J7*I6&ITx`9|vf28QBmP@Uu zR4T_Zt24_RtTS0IxB9ZY(HhBeg*BPwN~?cZ?$e=xypKw!E*@3snAuC+>7uCwY{-fLaW@_y?kmg}v1SUzAq%JM;LE6WYm zJ1qZT9boyeWnv{Am%h>J$np{EY?gnt2D5z3I-lj^)^wInTGcE!Sy!-p%3225J|Wr@ zg?&< z#gpjCS~v+2X5@u9`-3B< zW*3>F*!2ugz>^&`{nsP0e+5oP6q!i&WKS}?_3-rcWM5+5X~!vrZYiErPj<}-_$<%a zp6uBseGu{ALrM5lNV#_FT1J{!Z+SikPf!VE}r@tqAr}-nraT|0S z;K}o3zpAQ8+A7Iu13mek>^E4k+aS+5p6s_(A~tBW-GPrX7kbY!Pxp=VSefr6jqp$& zneU*(jPac3$%^*yHBuL+<5(MXJfD2j?8YhMJmWp-mwKqlJ5C@UcaKx1Q1Y32as@h0 zrKpYg5q&d)!zONDLGN+n6yCf zlSv~bH)_9!gBOAssX?4$X5uuxt?w+~#e3Rh1^SR;o8CsCZv&dEO&@=tAA9=72l}(8 zU+2I8_ViB*>1cEFo-?*g9GQVXK-O)FnflK3k+e;&?$kTb1930 z8G(X@TyM{dD z7YD{YK%S|~0}~!0&qb>O6E~4(+M2)xoW;fK1CyU8GJRuU%4YHeHw7-@yk=|;OnZe$ z$#x@^nl$wagw7t8O6?OvpOy=wS9L!(($jrRgc)vg(@4D+dcDqXb2clDkN#ci6O^zh z^*Iug(qg#Z#iVzi8tD(G>tLh=V<(%3pyU6EPE-j0arlhXA93zJ!)K*aY&u=4l;Wr# z)rF%tKVQ9+lBgIjr|N~yrIMy+!JFC#rwmd|DUBM#ot#rhA1I3|Qj9Y5Q`$@;tGpuW z#uI4D%&0G1-6eZg)VCZ=sf;?;q}KC4NIA3bY50tE>gK7BFGksB~hs= z1=m$emU3xS4>d6zA}gssk^xRu75jiISr>fZb?0^(!l{&HN_EuVPH3K*sK1Aqky0CV zv8o`KfXgg(ue&U2m5SnqR5YbNs)Lt@N_f1aG{g>5<#d|PzZPXMQrF|$+c&p!PKXAc zk<&=R%9%}l&7U)eM7)tTZ+_H^S|h9UDVN8#Q-)g?PQ_*|xFYH`t$>p5%Gi9Bj!VvE z$y(SH^|DKwVaipp*Q$oR7}JKIJCV9#G!}EOj(s`Y5y_O1drj0O+_$Jh`RQcjE{eKF zN56@krzgBN>RE2waKe=9q9%HoX|?-^rm*yVIH!JrQwDu(ILo)9UtDBhG368&Wdxep z)5Z$?mOat_z!LVv!~~YIr)_*-8GB+o1g>XKT<5?I?1}FhSk9hyNr4+_1jltq3EV^- zChn9>BX?!gM@~tN+?!)RXk8WVX-4iXQPpIF+d!Qtk&%0A>^&#bPgxba&DGMcVEN$g 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 bv;ArT$iBzC_!{kzjvEIHmg@~K+5Z0nSX{b7 literal 0 HcmV?d00001 diff --git a/src/external/PackedCSparse/qd/qd_inline.h b/src/external/PackedCSparse/qd/qd_inline.h new file mode 100644 index 00000000..89ba275b --- /dev/null +++ b/src/external/PackedCSparse/qd/qd_inline.h @@ -0,0 +1,1047 @@ +/* + * include/qd_inline.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains small functions (suitable for inlining) in the quad-double + * arithmetic package. + */ +#ifndef _QD_QD_INLINE_H +#define _QD_QD_INLINE_H + +#include +#include "inline.h" + +#ifndef QD_INLINE +#define inline +#endif + +/********** Constructors **********/ +inline qd_real::qd_real(double x0, double x1, double x2, double x3) { + x[0] = x0; + x[1] = x1; + x[2] = x2; + x[3] = x3; +} + +inline qd_real::qd_real(const double *xx) { + x[0] = xx[0]; + x[1] = xx[1]; + x[2] = xx[2]; + x[3] = xx[3]; +} + +inline qd_real::qd_real(double x0) { + x[0] = x0; + x[1] = x[2] = x[3] = 0.0; +} + +inline qd_real::qd_real() { + x[0] = 0.0; + x[1] = 0.0; + x[2] = 0.0; + x[3] = 0.0; +} + +inline qd_real::qd_real(const dd_real &a) { + x[0] = a._hi(); + x[1] = a._lo(); + x[2] = x[3] = 0.0; +} + +inline qd_real::qd_real(int i) { + x[0] = static_cast(i); + x[1] = x[2] = x[3] = 0.0; +} + +/********** Accessors **********/ +inline double qd_real::operator[](int i) const { + return x[i]; +} + +inline double &qd_real::operator[](int i) { + return x[i]; +} + +inline bool qd_real::isnan() const { + return QD_ISNAN(x[0]) || QD_ISNAN(x[1]) || QD_ISNAN(x[2]) || QD_ISNAN(x[3]); +} + +/********** Renormalization **********/ +namespace qd { +inline void quick_renorm(double &c0, double &c1, + double &c2, double &c3, double &c4) { + double t0, t1, t2, t3; + double s; + s = qd::quick_two_sum(c3, c4, t3); + s = qd::quick_two_sum(c2, s , t2); + s = qd::quick_two_sum(c1, s , t1); + c0 = qd::quick_two_sum(c0, s , t0); + + s = qd::quick_two_sum(t2, t3, t2); + s = qd::quick_two_sum(t1, s , t1); + c1 = qd::quick_two_sum(t0, s , t0); + + s = qd::quick_two_sum(t1, t2, t1); + c2 = qd::quick_two_sum(t0, s , t0); + + c3 = t0 + t1; +} + +inline void renorm(double &c0, double &c1, + double &c2, double &c3) { + double s0, s1, s2 = 0.0, s3 = 0.0; + + if (QD_ISINF(c0)) return; + + s0 = qd::quick_two_sum(c2, c3, c3); + s0 = qd::quick_two_sum(c1, s0, c2); + c0 = qd::quick_two_sum(c0, s0, c1); + + s0 = c0; + s1 = c1; + if (s1 != 0.0) { + s1 = qd::quick_two_sum(s1, c2, s2); + if (s2 != 0.0) + s2 = qd::quick_two_sum(s2, c3, s3); + else + s1 = qd::quick_two_sum(s1, c3, s2); + } else { + s0 = qd::quick_two_sum(s0, c2, s1); + if (s1 != 0.0) + s1 = qd::quick_two_sum(s1, c3, s2); + else + s0 = qd::quick_two_sum(s0, c3, s1); + } + + c0 = s0; + c1 = s1; + c2 = s2; + c3 = s3; +} + +inline void renorm(double &c0, double &c1, + double &c2, double &c3, double &c4) { + double s0, s1, s2 = 0.0, s3 = 0.0; + + if (QD_ISINF(c0)) return; + + s0 = qd::quick_two_sum(c3, c4, c4); + s0 = qd::quick_two_sum(c2, s0, c3); + s0 = qd::quick_two_sum(c1, s0, c2); + c0 = qd::quick_two_sum(c0, s0, c1); + + s0 = c0; + s1 = c1; + + if (s1 != 0.0) { + s1 = qd::quick_two_sum(s1, c2, s2); + if (s2 != 0.0) { + s2 = qd::quick_two_sum(s2, c3, s3); + if (s3 != 0.0) + s3 += c4; + else + s2 = qd::quick_two_sum(s2, c4, s3); + } else { + s1 = qd::quick_two_sum(s1, c3, s2); + if (s2 != 0.0) + s2 = qd::quick_two_sum(s2, c4, s3); + else + s1 = qd::quick_two_sum(s1, c4, s2); + } + } else { + s0 = qd::quick_two_sum(s0, c2, s1); + if (s1 != 0.0) { + s1 = qd::quick_two_sum(s1, c3, s2); + if (s2 != 0.0) + s2 = qd::quick_two_sum(s2, c4, s3); + else + s1 = qd::quick_two_sum(s1, c4, s2); + } else { + s0 = qd::quick_two_sum(s0, c3, s1); + if (s1 != 0.0) + s1 = qd::quick_two_sum(s1, c4, s2); + else + s0 = qd::quick_two_sum(s0, c4, s1); + } + } + + c0 = s0; + c1 = s1; + c2 = s2; + c3 = s3; +} +} + +inline void qd_real::renorm() { + qd::renorm(x[0], x[1], x[2], x[3]); +} + +inline void qd_real::renorm(double &e) { + qd::renorm(x[0], x[1], x[2], x[3], e); +} + + +/********** Additions ************/ +namespace qd { + +inline void three_sum(double &a, double &b, double &c) { + double t1, t2, t3; + t1 = qd::two_sum(a, b, t2); + a = qd::two_sum(c, t1, t3); + b = qd::two_sum(t2, t3, c); +} + +inline void three_sum2(double &a, double &b, double &c) { + double t1, t2, t3; + t1 = qd::two_sum(a, b, t2); + a = qd::two_sum(c, t1, t3); + b = t2 + t3; +} + +} + +/* quad-double + double */ +inline qd_real operator+(const qd_real &a, double b) { + double c0, c1, c2, c3; + double e; + + c0 = qd::two_sum(a[0], b, e); + c1 = qd::two_sum(a[1], e, e); + c2 = qd::two_sum(a[2], e, e); + c3 = qd::two_sum(a[3], e, e); + + qd::renorm(c0, c1, c2, c3, e); + + return qd_real(c0, c1, c2, c3); +} + +/* quad-double + double-double */ +inline qd_real operator+(const qd_real &a, const dd_real &b) { + + double s0, s1, s2, s3; + double t0, t1; + + s0 = qd::two_sum(a[0], b._hi(), t0); + s1 = qd::two_sum(a[1], b._lo(), t1); + + s1 = qd::two_sum(s1, t0, t0); + + s2 = a[2]; + qd::three_sum(s2, t0, t1); + + s3 = qd::two_sum(t0, a[3], t0); + t0 += t1; + + qd::renorm(s0, s1, s2, s3, t0); + return qd_real(s0, s1, s2, s3); +} + + +/* double + quad-double */ +inline qd_real operator+(double a, const qd_real &b) { + return (b + a); +} + +/* double-double + quad-double */ +inline qd_real operator+(const dd_real &a, const qd_real &b) { + return (b + a); +} + +namespace qd { + +/* s = quick_three_accum(a, b, c) adds c to the dd-pair (a, b). + * If the result does not fit in two doubles, then the sum is + * output into s and (a,b) contains the remainder. Otherwise + * s is zero and (a,b) contains the sum. */ +inline double quick_three_accum(double &a, double &b, double c) { + double s; + bool za, zb; + + s = qd::two_sum(b, c, b); + s = qd::two_sum(a, s, a); + + za = (a != 0.0); + zb = (b != 0.0); + + if (za && zb) + return s; + + if (!zb) { + b = a; + a = s; + } else { + a = s; + } + + return 0.0; +} + +} + +inline qd_real qd_real::ieee_add(const qd_real &a, const qd_real &b) { + int i, j, k; + double s, t; + double u, v; /* double-length accumulator */ + double x[4] = {0.0, 0.0, 0.0, 0.0}; + + i = j = k = 0; + if (std::abs(a[i]) > std::abs(b[j])) + u = a[i++]; + else + u = b[j++]; + if (std::abs(a[i]) > std::abs(b[j])) + v = a[i++]; + else + v = b[j++]; + + u = qd::quick_two_sum(u, v, v); + + while (k < 4) { + if (i >= 4 && j >= 4) { + x[k] = u; + if (k < 3) + x[++k] = v; + break; + } + + if (i >= 4) + t = b[j++]; + else if (j >= 4) + t = a[i++]; + else if (std::abs(a[i]) > std::abs(b[j])) { + t = a[i++]; + } else + t = b[j++]; + + s = qd::quick_three_accum(u, v, t); + + if (s != 0.0) { + x[k++] = s; + } + } + + /* add the rest. */ + for (k = i; k < 4; k++) + x[3] += a[k]; + for (k = j; k < 4; k++) + x[3] += b[k]; + + qd::renorm(x[0], x[1], x[2], x[3]); + return qd_real(x[0], x[1], x[2], x[3]); +} + +inline qd_real qd_real::sloppy_add(const qd_real &a, const qd_real &b) { + /* + double s0, s1, s2, s3; + double t0, t1, t2, t3; + + s0 = qd::two_sum(a[0], b[0], t0); + s1 = qd::two_sum(a[1], b[1], t1); + s2 = qd::two_sum(a[2], b[2], t2); + s3 = qd::two_sum(a[3], b[3], t3); + + s1 = qd::two_sum(s1, t0, t0); + qd::three_sum(s2, t0, t1); + qd::three_sum2(s3, t0, t2); + t0 = t0 + t1 + t3; + + qd::renorm(s0, s1, s2, s3, t0); + return qd_real(s0, s1, s2, s3, t0); + */ + + /* Same as above, but addition re-organized to minimize + data dependency ... unfortunately some compilers are + not very smart to do this automatically */ + double s0, s1, s2, s3; + double t0, t1, t2, t3; + + double v0, v1, v2, v3; + double u0, u1, u2, u3; + double w0, w1, w2, w3; + + s0 = a[0] + b[0]; + s1 = a[1] + b[1]; + s2 = a[2] + b[2]; + s3 = a[3] + b[3]; + + v0 = s0 - a[0]; + v1 = s1 - a[1]; + v2 = s2 - a[2]; + v3 = s3 - a[3]; + + u0 = s0 - v0; + u1 = s1 - v1; + u2 = s2 - v2; + u3 = s3 - v3; + + w0 = a[0] - u0; + w1 = a[1] - u1; + w2 = a[2] - u2; + w3 = a[3] - u3; + + u0 = b[0] - v0; + u1 = b[1] - v1; + u2 = b[2] - v2; + u3 = b[3] - v3; + + t0 = w0 + u0; + t1 = w1 + u1; + t2 = w2 + u2; + t3 = w3 + u3; + + s1 = qd::two_sum(s1, t0, t0); + qd::three_sum(s2, t0, t1); + qd::three_sum2(s3, t0, t2); + t0 = t0 + t1 + t3; + + /* renormalize */ + qd::renorm(s0, s1, s2, s3, t0); + return qd_real(s0, s1, s2, s3); +} + +/* quad-double + quad-double */ +inline qd_real operator+(const qd_real &a, const qd_real &b) { +#ifndef QD_IEEE_ADD + return qd_real::sloppy_add(a, b); +#else + return qd_real::ieee_add(a, b); +#endif +} + + + +/********** Self-Additions ************/ +/* quad-double += double */ +inline qd_real &qd_real::operator+=(double a) { + *this = *this + a; + return *this; +} + +/* quad-double += double-double */ +inline qd_real &qd_real::operator+=(const dd_real &a) { + *this = *this + a; + return *this; +} + +/* quad-double += quad-double */ +inline qd_real &qd_real::operator+=(const qd_real &a) { + *this = *this + a; + return *this; +} + +/********** Unary Minus **********/ +inline qd_real qd_real::operator-() const { + return qd_real(-x[0], -x[1], -x[2], -x[3]); +} + +/********** Subtractions **********/ +inline qd_real operator-(const qd_real &a, double b) { + return (a + (-b)); +} + +inline qd_real operator-(double a, const qd_real &b) { + return (a + (-b)); +} + +inline qd_real operator-(const qd_real &a, const dd_real &b) { + return (a + (-b)); +} + +inline qd_real operator-(const dd_real &a, const qd_real &b) { + return (a + (-b)); +} + +inline qd_real operator-(const qd_real &a, const qd_real &b) { + return (a + (-b)); +} + +/********** Self-Subtractions **********/ +inline qd_real &qd_real::operator-=(double a) { + return ((*this) += (-a)); +} + +inline qd_real &qd_real::operator-=(const dd_real &a) { + return ((*this) += (-a)); +} + +inline qd_real &qd_real::operator-=(const qd_real &a) { + return ((*this) += (-a)); +} + + +inline qd_real operator*(double a, const qd_real &b) { + return (b * a); +} + +inline qd_real operator*(const dd_real &a, const qd_real &b) { + return (b * a); +} + +inline qd_real mul_pwr2(const qd_real &a, double b) { + return qd_real(a[0] * b, a[1] * b, a[2] * b, a[3] * b); +} + +/********** Multiplications **********/ +inline qd_real operator*(const qd_real &a, double b) { + double p0, p1, p2, p3; + double q0, q1, q2; + double s0, s1, s2, s3, s4; + + p0 = qd::two_prod(a[0], b, q0); + p1 = qd::two_prod(a[1], b, q1); + p2 = qd::two_prod(a[2], b, q2); + p3 = a[3] * b; + + s0 = p0; + + s1 = qd::two_sum(q0, p1, s2); + + qd::three_sum(s2, q1, p2); + + qd::three_sum2(q1, q2, p3); + s3 = q1; + + s4 = q2 + p2; + + qd::renorm(s0, s1, s2, s3, s4); + return qd_real(s0, s1, s2, s3); + +} + +/* quad-double * double-double */ +/* a0 * b0 0 + a0 * b1 1 + a1 * b0 2 + a1 * b1 3 + a2 * b0 4 + a2 * b1 5 + a3 * b0 6 + a3 * b1 7 */ +inline qd_real operator*(const qd_real &a, const dd_real &b) { + double p0, p1, p2, p3, p4; + double q0, q1, q2, q3, q4; + double s0, s1, s2; + double t0, t1; + + p0 = qd::two_prod(a[0], b._hi(), q0); + p1 = qd::two_prod(a[0], b._lo(), q1); + p2 = qd::two_prod(a[1], b._hi(), q2); + p3 = qd::two_prod(a[1], b._lo(), q3); + p4 = qd::two_prod(a[2], b._hi(), q4); + + qd::three_sum(p1, p2, q0); + + /* Five-Three-Sum */ + qd::three_sum(p2, p3, p4); + q1 = qd::two_sum(q1, q2, q2); + s0 = qd::two_sum(p2, q1, t0); + s1 = qd::two_sum(p3, q2, t1); + s1 = qd::two_sum(s1, t0, t0); + s2 = t0 + t1 + p4; + p2 = s0; + + p3 = a[2] * b._hi() + a[3] * b._lo() + q3 + q4; + qd::three_sum2(p3, q0, s1); + p4 = q0 + s2; + + qd::renorm(p0, p1, p2, p3, p4); + return qd_real(p0, p1, p2, p3); +} + +/* quad-double * quad-double */ +/* a0 * b0 0 + a0 * b1 1 + a1 * b0 2 + a0 * b2 3 + a1 * b1 4 + a2 * b0 5 + a0 * b3 6 + a1 * b2 7 + a2 * b1 8 + a3 * b0 9 */ +inline qd_real qd_real::sloppy_mul(const qd_real &a, const qd_real &b) { + double p0, p1, p2, p3, p4, p5; + double q0, q1, q2, q3, q4, q5; + double t0, t1; + double s0, s1, s2; + + p0 = qd::two_prod(a[0], b[0], q0); + + p1 = qd::two_prod(a[0], b[1], q1); + p2 = qd::two_prod(a[1], b[0], q2); + + p3 = qd::two_prod(a[0], b[2], q3); + p4 = qd::two_prod(a[1], b[1], q4); + p5 = qd::two_prod(a[2], b[0], q5); + + /* Start Accumulation */ + qd::three_sum(p1, p2, q0); + + /* Six-Three Sum of p2, q1, q2, p3, p4, p5. */ + qd::three_sum(p2, q1, q2); + qd::three_sum(p3, p4, p5); + /* compute (s0, s1, s2) = (p2, q1, q2) + (p3, p4, p5). */ + s0 = qd::two_sum(p2, p3, t0); + s1 = qd::two_sum(q1, p4, t1); + s2 = q2 + p5; + s1 = qd::two_sum(s1, t0, t0); + s2 += (t0 + t1); + + /* O(eps^3) order terms */ + s1 += a[0]*b[3] + a[1]*b[2] + a[2]*b[1] + a[3]*b[0] + q0 + q3 + q4 + q5; + qd::renorm(p0, p1, s0, s1, s2); + return qd_real(p0, p1, s0, s1); +} + +inline qd_real qd_real::accurate_mul(const qd_real &a, const qd_real &b) { + double p0, p1, p2, p3, p4, p5; + double q0, q1, q2, q3, q4, q5; + double p6, p7, p8, p9; + double q6, q7, q8, q9; + double r0, r1; + double t0, t1; + double s0, s1, s2; + + p0 = qd::two_prod(a[0], b[0], q0); + + p1 = qd::two_prod(a[0], b[1], q1); + p2 = qd::two_prod(a[1], b[0], q2); + + p3 = qd::two_prod(a[0], b[2], q3); + p4 = qd::two_prod(a[1], b[1], q4); + p5 = qd::two_prod(a[2], b[0], q5); + + /* Start Accumulation */ + qd::three_sum(p1, p2, q0); + + /* Six-Three Sum of p2, q1, q2, p3, p4, p5. */ + qd::three_sum(p2, q1, q2); + qd::three_sum(p3, p4, p5); + /* compute (s0, s1, s2) = (p2, q1, q2) + (p3, p4, p5). */ + s0 = qd::two_sum(p2, p3, t0); + s1 = qd::two_sum(q1, p4, t1); + s2 = q2 + p5; + s1 = qd::two_sum(s1, t0, t0); + s2 += (t0 + t1); + + /* O(eps^3) order terms */ + p6 = qd::two_prod(a[0], b[3], q6); + p7 = qd::two_prod(a[1], b[2], q7); + p8 = qd::two_prod(a[2], b[1], q8); + p9 = qd::two_prod(a[3], b[0], q9); + + /* Nine-Two-Sum of q0, s1, q3, q4, q5, p6, p7, p8, p9. */ + q0 = qd::two_sum(q0, q3, q3); + q4 = qd::two_sum(q4, q5, q5); + p6 = qd::two_sum(p6, p7, p7); + p8 = qd::two_sum(p8, p9, p9); + /* Compute (t0, t1) = (q0, q3) + (q4, q5). */ + t0 = qd::two_sum(q0, q4, t1); + t1 += (q3 + q5); + /* Compute (r0, r1) = (p6, p7) + (p8, p9). */ + r0 = qd::two_sum(p6, p8, r1); + r1 += (p7 + p9); + /* Compute (q3, q4) = (t0, t1) + (r0, r1). */ + q3 = qd::two_sum(t0, r0, q4); + q4 += (t1 + r1); + /* Compute (t0, t1) = (q3, q4) + s1. */ + t0 = qd::two_sum(q3, s1, t1); + t1 += q4; + + /* O(eps^4) terms -- Nine-One-Sum */ + t1 += a[1] * b[3] + a[2] * b[2] + a[3] * b[1] + q6 + q7 + q8 + q9 + s2; + + qd::renorm(p0, p1, s0, t0, t1); + return qd_real(p0, p1, s0, t0); +} + +inline qd_real operator*(const qd_real &a, const qd_real &b) { +#ifdef QD_SLOPPY_MUL + return qd_real::sloppy_mul(a, b); +#else + return qd_real::accurate_mul(a, b); +#endif +} + +/* quad-double ^ 2 = (x0 + x1 + x2 + x3) ^ 2 + = x0 ^ 2 + 2 x0 * x1 + (2 x0 * x2 + x1 ^ 2) + + (2 x0 * x3 + 2 x1 * x2) */ +inline qd_real sqr(const qd_real &a) { + double p0, p1, p2, p3, p4, p5; + double q0, q1, q2, q3; + double s0, s1; + double t0, t1; + + p0 = qd::two_sqr(a[0], q0); + p1 = qd::two_prod(2.0 * a[0], a[1], q1); + p2 = qd::two_prod(2.0 * a[0], a[2], q2); + p3 = qd::two_sqr(a[1], q3); + + p1 = qd::two_sum(q0, p1, q0); + + q0 = qd::two_sum(q0, q1, q1); + p2 = qd::two_sum(p2, p3, p3); + + s0 = qd::two_sum(q0, p2, t0); + s1 = qd::two_sum(q1, p3, t1); + + s1 = qd::two_sum(s1, t0, t0); + t0 += t1; + + s1 = qd::quick_two_sum(s1, t0, t0); + p2 = qd::quick_two_sum(s0, s1, t1); + p3 = qd::quick_two_sum(t1, t0, q0); + + p4 = 2.0 * a[0] * a[3]; + p5 = 2.0 * a[1] * a[2]; + + p4 = qd::two_sum(p4, p5, p5); + q2 = qd::two_sum(q2, q3, q3); + + t0 = qd::two_sum(p4, q2, t1); + t1 = t1 + p5 + q3; + + p3 = qd::two_sum(p3, t0, p4); + p4 = p4 + q0 + t1; + + qd::renorm(p0, p1, p2, p3, p4); + return qd_real(p0, p1, p2, p3); + +} + +/********** Self-Multiplication **********/ +/* quad-double *= double */ +inline qd_real &qd_real::operator*=(double a) { + *this = (*this * a); + return *this; +} + +/* quad-double *= double-double */ +inline qd_real &qd_real::operator*=(const dd_real &a) { + *this = (*this * a); + return *this; +} + +/* quad-double *= quad-double */ +inline qd_real &qd_real::operator*=(const qd_real &a) { + *this = *this * a; + return *this; +} + +inline qd_real operator/ (const qd_real &a, const dd_real &b) { +#ifdef QD_SLOPPY_DIV + return qd_real::sloppy_div(a, b); +#else + return qd_real::accurate_div(a, b); +#endif +} + +inline qd_real operator/(const qd_real &a, const qd_real &b) { +#ifdef QD_SLOPPY_DIV + return qd_real::sloppy_div(a, b); +#else + return qd_real::accurate_div(a, b); +#endif +} + +/* double / quad-double */ +inline qd_real operator/(double a, const qd_real &b) { + return qd_real(a) / b; +} + +/* double-double / quad-double */ +inline qd_real operator/(const dd_real &a, const qd_real &b) { + return qd_real(a) / b; +} + +/********** Self-Divisions **********/ +/* quad-double /= double */ +inline qd_real &qd_real::operator/=(double a) { + *this = (*this / a); + return *this; +} + +/* quad-double /= double-double */ +inline qd_real &qd_real::operator/=(const dd_real &a) { + *this = (*this / a); + return *this; +} + +/* quad-double /= quad-double */ +inline qd_real &qd_real::operator/=(const qd_real &a) { + *this = (*this / a); + return *this; +} + + +/********** Exponentiation **********/ +inline qd_real qd_real::operator^(int n) const { + return pow(*this, n); +} + +/********** Miscellaneous **********/ +inline qd_real abs(const qd_real &a) { + return (a[0] < 0.0) ? -a : a; +} + +inline qd_real fabs(const qd_real &a) { + return abs(a); +} + +/* Quick version. May be off by one when qd is very close + to the middle of two integers. */ +inline qd_real quick_nint(const qd_real &a) { + qd_real r = qd_real(qd::nint(a[0]), qd::nint(a[1]), + qd::nint(a[2]), qd::nint(a[3])); + r.renorm(); + return r; +} + +/*********** Assignments ************/ +/* quad-double = double */ +inline qd_real &qd_real::operator=(double a) { + x[0] = a; + x[1] = x[2] = x[3] = 0.0; + return *this; +} + +/* quad-double = double-double */ +inline qd_real &qd_real::operator=(const dd_real &a) { + x[0] = a._hi(); + x[1] = a._lo(); + x[2] = x[3] = 0.0; + return *this; +} + +/********** Equality Comparison **********/ +inline bool operator==(const qd_real &a, double b) { + return (a[0] == b && a[1] == 0.0 && a[2] == 0.0 && a[3] == 0.0); +} + +inline bool operator==(double a, const qd_real &b) { + return (b == a); +} + +inline bool operator==(const qd_real &a, const dd_real &b) { + return (a[0] == b._hi() && a[1] == b._lo() && + a[2] == 0.0 && a[3] == 0.0); +} + +inline bool operator==(const dd_real &a, const qd_real &b) { + return (b == a); +} + +inline bool operator==(const qd_real &a, const qd_real &b) { + return (a[0] == b[0] && a[1] == b[1] && + a[2] == b[2] && a[3] == b[3]); +} + + +/********** Less-Than Comparison ***********/ +inline bool operator<(const qd_real &a, double b) { + return (a[0] < b || (a[0] == b && a[1] < 0.0)); +} + +inline bool operator<(double a, const qd_real &b) { + return (b > a); +} + +inline bool operator<(const qd_real &a, const dd_real &b) { + return (a[0] < b._hi() || + (a[0] == b._hi() && (a[1] < b._lo() || + (a[1] == b._lo() && a[2] < 0.0)))); +} + +inline bool operator<(const dd_real &a, const qd_real &b) { + return (b > a); +} + +inline bool operator<(const qd_real &a, const qd_real &b) { + return (a[0] < b[0] || + (a[0] == b[0] && (a[1] < b[1] || + (a[1] == b[1] && (a[2] < b[2] || + (a[2] == b[2] && a[3] < b[3])))))); +} + +/********** Greater-Than Comparison ***********/ +inline bool operator>(const qd_real &a, double b) { + return (a[0] > b || (a[0] == b && a[1] > 0.0)); +} + +inline bool operator>(double a, const qd_real &b) { + return (b < a); +} + +inline bool operator>(const qd_real &a, const dd_real &b) { + return (a[0] > b._hi() || + (a[0] == b._hi() && (a[1] > b._lo() || + (a[1] == b._lo() && a[2] > 0.0)))); +} + +inline bool operator>(const dd_real &a, const qd_real &b) { + return (b < a); +} + +inline bool operator>(const qd_real &a, const qd_real &b) { + return (a[0] > b[0] || + (a[0] == b[0] && (a[1] > b[1] || + (a[1] == b[1] && (a[2] > b[2] || + (a[2] == b[2] && a[3] > b[3])))))); +} + + +/********** Less-Than-Or-Equal-To Comparison **********/ +inline bool operator<=(const qd_real &a, double b) { + return (a[0] < b || (a[0] == b && a[1] <= 0.0)); +} + +inline bool operator<=(double a, const qd_real &b) { + return (b >= a); +} + +inline bool operator<=(const qd_real &a, const dd_real &b) { + return (a[0] < b._hi() || + (a[0] == b._hi() && (a[1] < b._lo() || + (a[1] == b._lo() && a[2] <= 0.0)))); +} + +inline bool operator<=(const dd_real &a, const qd_real &b) { + return (b >= a); +} + +inline bool operator<=(const qd_real &a, const qd_real &b) { + return (a[0] < b[0] || + (a[0] == b[0] && (a[1] < b[1] || + (a[1] == b[1] && (a[2] < b[2] || + (a[2] == b[2] && a[3] <= b[3])))))); +} + +/********** Greater-Than-Or-Equal-To Comparison **********/ +inline bool operator>=(const qd_real &a, double b) { + return (a[0] > b || (a[0] == b && a[1] >= 0.0)); +} + +inline bool operator>=(double a, const qd_real &b) { + return (b <= a); +} + +inline bool operator>=(const qd_real &a, const dd_real &b) { + return (a[0] > b._hi() || + (a[0] == b._hi() && (a[1] > b._lo() || + (a[1] == b._lo() && a[2] >= 0.0)))); +} + +inline bool operator>=(const dd_real &a, const qd_real &b) { + return (b <= a); +} + +inline bool operator>=(const qd_real &a, const qd_real &b) { + return (a[0] > b[0] || + (a[0] == b[0] && (a[1] > b[1] || + (a[1] == b[1] && (a[2] > b[2] || + (a[2] == b[2] && a[3] >= b[3])))))); +} + + + +/********** Not-Equal-To Comparison **********/ +inline bool operator!=(const qd_real &a, double b) { + return !(a == b); +} + +inline bool operator!=(double a, const qd_real &b) { + return !(a == b); +} + +inline bool operator!=(const qd_real &a, const dd_real &b) { + return !(a == b); +} + +inline bool operator!=(const dd_real &a, const qd_real &b) { + return !(a == b); +} + +inline bool operator!=(const qd_real &a, const qd_real &b) { + return !(a == b); +} + + + +inline qd_real aint(const qd_real &a) { + return (a[0] >= 0) ? floor(a) : ceil(a); +} + +inline bool qd_real::is_zero() const { + return (x[0] == 0.0); +} + +inline bool qd_real::is_one() const { + return (x[0] == 1.0 && x[1] == 0.0 && x[2] == 0.0 && x[3] == 0.0); +} + +inline bool qd_real::is_positive() const { + return (x[0] > 0.0); +} + +inline bool qd_real::is_negative() const { + return (x[0] < 0.0); +} + +inline qd_real::operator bool() const { + return (x[0] != 0.0); +} + +inline qd_real::operator double() const { + return to_double(*this); +} + +inline dd_real to_dd_real(const qd_real &a) { + return dd_real(a[0], a[1]); +} + +inline double to_double(const qd_real &a) { + return a[0]; +} + +inline int to_int(const qd_real &a) { + return static_cast(a[0]); +} + +inline qd_real inv(const qd_real &qd) { + return 1.0 / qd; +} + +inline qd_real max(const qd_real &a, const qd_real &b) { + return (a > b) ? a : b; +} + +inline qd_real max(const qd_real &a, const qd_real &b, + const qd_real &c) { + return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); +} + +inline qd_real min(const qd_real &a, const qd_real &b) { + return (a < b) ? a : b; +} + +inline qd_real min(const qd_real &a, const qd_real &b, + const qd_real &c) { + return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); +} + +/* Random number generator */ +inline qd_real qd_real::rand() { + return qdrand(); +} + +inline qd_real ldexp(const qd_real &a, int n) { + return qd_real(std::ldexp(a[0], n), std::ldexp(a[1], n), + std::ldexp(a[2], n), std::ldexp(a[3], n)); +} + +#endif /* _QD_QD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/qd_real.cc b/src/external/PackedCSparse/qd/qd_real.cc new file mode 100644 index 00000000..02cb7aa3 --- /dev/null +++ b/src/external/PackedCSparse/qd/qd_real.cc @@ -0,0 +1,2624 @@ +/* + * src/qd_real.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + * + * Contains implementation of non-inlined functions of quad-double + * package. Inlined functions are found in qd_inline.h (in include directory). + */ +#include +#include +#include +#include +#include +#include +#include + +#include "qd_config.h" +#include "qd_real.h" +#include "util.h" + +#include "bits.h" + +#ifndef QD_INLINE +#include "qd_inline.h" +#endif + +using std::cout; +using std::cerr; +using std::endl; +using std::istream; +using std::ostream; +using std::ios_base; +using std::string; +using std::setw; + +using namespace qd; + +void qd_real::error(const char *msg) { + //if (msg) { cerr << "ERROR " << msg << endl; } +} + +/********** Multiplications **********/ + +qd_real nint(const qd_real &a) { + double x0, x1, x2, x3; + + x0 = nint(a[0]); + x1 = x2 = x3 = 0.0; + + if (x0 == a[0]) { + /* First double is already an integer. */ + x1 = nint(a[1]); + + if (x1 == a[1]) { + /* Second double is already an integer. */ + x2 = nint(a[2]); + + if (x2 == a[2]) { + /* Third double is already an integer. */ + x3 = nint(a[3]); + } else { + if (std::abs(x2 - a[2]) == 0.5 && a[3] < 0.0) { + x2 -= 1.0; + } + } + + } else { + if (std::abs(x1 - a[1]) == 0.5 && a[2] < 0.0) { + x1 -= 1.0; + } + } + + } else { + /* First double is not an integer. */ + if (std::abs(x0 - a[0]) == 0.5 && a[1] < 0.0) { + x0 -= 1.0; + } + } + + renorm(x0, x1, x2, x3); + return qd_real(x0, x1, x2, x3); +} + +qd_real floor(const qd_real &a) { + double x0, x1, x2, x3; + x1 = x2 = x3 = 0.0; + x0 = std::floor(a[0]); + + if (x0 == a[0]) { + x1 = std::floor(a[1]); + + if (x1 == a[1]) { + x2 = std::floor(a[2]); + + if (x2 == a[2]) { + x3 = std::floor(a[3]); + } + } + + renorm(x0, x1, x2, x3); + return qd_real(x0, x1, x2, x3); + } + + return qd_real(x0, x1, x2, x3); +} + +qd_real ceil(const qd_real &a) { + double x0, x1, x2, x3; + x1 = x2 = x3 = 0.0; + x0 = std::ceil(a[0]); + + if (x0 == a[0]) { + x1 = std::ceil(a[1]); + + if (x1 == a[1]) { + x2 = std::ceil(a[2]); + + if (x2 == a[2]) { + x3 = std::ceil(a[3]); + } + } + + renorm(x0, x1, x2, x3); + return qd_real(x0, x1, x2, x3); + } + + return qd_real(x0, x1, x2, x3); +} + + + +/********** Divisions **********/ +/* quad-double / double */ +qd_real operator/(const qd_real &a, double b) { + /* Strategy: compute approximate quotient using high order + doubles, and then correct it 3 times using the remainder. + (Analogous to long division.) */ + double t0, t1; + double q0, q1, q2, q3; + qd_real r; + + q0 = a[0] / b; /* approximate quotient */ + + /* Compute the remainder a - q0 * b */ + t0 = two_prod(q0, b, t1); + r = a - dd_real(t0, t1); + + /* Compute the first correction */ + q1 = r[0] / b; + t0 = two_prod(q1, b, t1); + r -= dd_real(t0, t1); + + /* Second correction to the quotient. */ + q2 = r[0] / b; + t0 = two_prod(q2, b, t1); + r -= dd_real(t0, t1); + + /* Final correction to the quotient. */ + q3 = r[0] / b; + + renorm(q0, q1, q2, q3); + return qd_real(q0, q1, q2, q3); +} + +qd_real::qd_real(const char *s) { + if (qd_real::read(s, *this)) { + qd_real::error("(qd_real::qd_real): INPUT ERROR."); + *this = qd_real::_nan; + } +} + +qd_real &qd_real::operator=(const char *s) { + if (qd_real::read(s, *this)) { + qd_real::error("(qd_real::operator=): INPUT ERROR."); + *this = qd_real::_nan; + } + return *this; +} + +istream &operator>>(istream &s, qd_real &qd) { + char str[255]; + s >> str; + qd = qd_real(str); + return s; +} + +ostream &operator<<(ostream &os, const qd_real &qd) { + bool showpos = (os.flags() & ios_base::showpos) != 0; + bool uppercase = (os.flags() & ios_base::uppercase) != 0; + return os << qd.to_string((int)os.precision(), (int)os.width(), os.flags(), + showpos, uppercase, os.fill()); +} + +/* Read a quad-double from s. */ +int qd_real::read(const char *s, qd_real &qd) { + const char *p = s; + char ch; + int sign = 0; + int point = -1; /* location of decimal point */ + int nd = 0; /* number of digits read */ + int e = 0; /* exponent. */ + bool done = false; + qd_real r = 0.0; /* number being read */ + + /* Skip any leading spaces */ + while (*p == ' ') p++; + + while (!done && (ch = *p) != '\0') { + if (ch >= '0' && ch <= '9') { + /* It's a digit */ + int d = ch - '0'; + r *= 10.0; + r += static_cast(d); + nd++; + } else { + /* Non-digit */ + switch (ch) { + case '.': + if (point >= 0) + return -1; /* we've already encountered a decimal point. */ + point = nd; + break; + case '-': + case '+': + if (sign != 0 || nd > 0) + return -1; /* we've already encountered a sign, or if its + not at first position. */ + sign = (ch == '-') ? -1 : 1; + break; + case 'E': + case 'e': + int nread; + nread = std::sscanf(p+1, "%d", &e); + done = true; + if (nread != 1) + return -1; /* read of exponent failed. */ + break; + case ' ': + done = true; + break; + default: + return -1; + + } + } + + p++; + } + + + + /* Adjust exponent to account for decimal point */ + if (point >= 0) { + e -= (nd - point); + } + + /* Multiply the the exponent */ + if (e != 0) { + r *= (qd_real(10.0) ^ e); + } + + qd = (sign < 0) ? -r : r; + return 0; +} + +void qd_real::to_digits(char *s, int &expn, int precision) const { + int D = precision + 1; /* number of digits to compute */ + + qd_real r = abs(*this); + int e; /* exponent */ + int i, d; + + if (x[0] == 0.0) { + /* this == 0.0 */ + expn = 0; + for (i = 0; i < precision; i++) s[i] = '0'; + return; + } + + /* First determine the (approximate) exponent. */ + e = static_cast(std::floor(std::log10(std::abs(x[0])))); + + if (e < -300) { + r *= qd_real(10.0) ^ 300; + r /= qd_real(10.0) ^ (e + 300); + } else if (e > 300) { + r = ldexp(r, -53); + r /= qd_real(10.0) ^ e; + r = ldexp(r, 53); + } else { + r /= qd_real(10.0) ^ e; + } + + /* Fix exponent if we are off by one */ + if (r >= 10.0) { + r /= 10.0; + e++; + } else if (r < 1.0) { + r *= 10.0; + e--; + } + + if (r >= 10.0 || r < 1.0) { + qd_real::error("(qd_real::to_digits): can't compute exponent."); + return; + } + + /* Extract the digits */ + for (i = 0; i < D; i++) { + d = static_cast(r[0]); + r -= d; + r *= 10.0; + + s[i] = static_cast(d + '0'); + } + + /* Fix out of range digits. */ + for (i = D-1; i > 0; i--) { + if (s[i] < '0') { + s[i-1]--; + s[i] += 10; + } else if (s[i] > '9') { + s[i-1]++; + s[i] -= 10; + } + } + + if (s[0] <= '0') { + qd_real::error("(qd_real::to_digits): non-positive leading digit."); + return; + } + + /* Round, handle carry */ + if (s[D-1] >= '5') { + s[D-2]++; + + i = D-2; + while (i > 0 && s[i] > '9') { + s[i] -= 10; + s[--i]++; + } + } + + /* If first digit is 10, shift everything. */ + if (s[0] > '9') { + e++; + for (i = precision; i >= 2; i--) s[i] = s[i-1]; + s[0] = '1'; + s[1] = '0'; + } + + s[precision] = 0; + expn = e; +} + +/* Writes the quad-double number into the character array s of length len. + The integer d specifies how many significant digits to write. + The string s must be able to hold at least (d+8) characters. + showpos indicates whether to use the + sign, and uppercase indicates + whether the E or e is to be used for the exponent. */ +void qd_real::write(char *s, int len, int precision, + bool showpos, bool uppercase) const { + string str = to_string(precision, 0, ios_base::scientific, showpos, uppercase); + strncpy(s, str.c_str(), len-1); + s[len-1] = 0; +} + +void round_string_qd(char *s, int precision, int *offset){ + /* + Input string must be all digits or errors will occur. + */ + + int i; + int D = precision ; + + /* Round, handle carry */ + if (D>0 && s[D] >= '5') { + s[D-1]++; + + i = D-1; + while (i > 0 && s[i] > '9') { + s[i] -= 10; + s[--i]++; + } + } + + /* If first digit is 10, shift everything. */ + if (s[0] > '9') { + // e++; // don't modify exponent here + for (i = precision; i >= 1; i--) s[i+1] = s[i]; + s[0] = '1'; + s[1] = '0'; + + (*offset)++ ; // now offset needs to be increased by one + precision++ ; + } + + s[precision] = 0; // add terminator for array +} + + +string qd_real::to_string(int precision, int width, ios_base::fmtflags fmt, + bool showpos, bool uppercase, char fill) const { + string s; + bool fixed = (fmt & ios_base::fixed) != 0; + bool sgn = true; + int i, e = 0; + + if (isinf()) { + if (*this < 0.0) + s += '-'; + else if (showpos) + s += '+'; + else + sgn = false; + s += uppercase ? "INF" : "inf"; + } else if (isnan()) { + s = uppercase ? "NAN" : "nan"; + sgn = false; + } else { + if (*this < 0.0) + s += '-'; + else if (showpos) + s += '+'; + else + sgn = false; + + if (*this == 0.0) { + /* Zero case */ + s += '0'; + if (precision > 0) { + s += '.'; + s.append(precision, '0'); + } + } else { + /* Non-zero case */ + int off = (fixed ? (1 + to_int(floor(log10(abs(*this))))) : 1); + int d = precision + off; + + int d_with_extra = d; + if(fixed) + d_with_extra = std::max(120, d); // longer than the max accuracy for DD + + // highly special case - fixed mode, precision is zero, abs(*this) < 1.0 + // without this trap a number like 0.9 printed fixed with 0 precision prints as 0 + // should be rounded to 1. + if(fixed && (precision == 0) && (abs(*this) < 1.0)){ + if(abs(*this) >= 0.5) + s += '1'; + else + s += '0'; + + return s; + } + + // handle near zero to working precision (but not exactly zero) + if (fixed && d <= 0) { + s += '0'; + if (precision > 0) { + s += '.'; + s.append(precision, '0'); + } + } else { // default + + char *t ; // = new char[d+1]; + int j; + + if(fixed){ + t = new char[d_with_extra+1]; + to_digits(t, e, d_with_extra); + } + else{ + t = new char[d+1]; + to_digits(t, e, d); + } + + off = e + 1; + + if (fixed) { + // fix the string if it's been computed incorrectly + // round here in the decimal string if required + round_string_qd(t, d, &off); + + if (off > 0) { + for (i = 0; i < off; i++) s += t[i]; + if (precision > 0) { + s += '.'; + for (j = 0; j < precision; j++, i++) s += t[i]; + } + } else { + s += "0."; + if (off < 0) s.append(-off, '0'); + for (i = 0; i < d; i++) s += t[i]; + } + } else { + s += t[0]; + if (precision > 0) s += '.'; + + for (i = 1; i <= precision; i++) + s += t[i]; + + } + delete [] t; + } + } + + // trap for improper offset with large values + // without this trap, output of values of the for 10^j - 1 fail for j > 28 + // and are output with the point in the wrong place, leading to a dramatically off value + if(fixed && (precision > 0)){ + // make sure that the value isn't dramatically larger + double from_string = atof(s.c_str()); + + // if this ratio is large, then we've got problems + if( fabs( from_string / this->x[0] ) > 3.0 ){ + + // loop on the string, find the point, move it up one + // don't act on the first character + for(i=1; i < (int)s.length(); i++){ + if(s[i] == '.'){ + s[i] = s[i-1] ; + s[i-1] = '.' ; + break; + } + } + + from_string = atof(s.c_str()); + // if this ratio is large, then the string has not been fixed + if( fabs( from_string / this->x[0] ) > 3.0 ){ + dd_real::error("Re-rounding unsuccessful in large number fixed point trap.") ; + } + } + } + + if (!fixed) { + /* Fill in exponent part */ + s += uppercase ? 'E' : 'e'; + append_expn(s, e); + } + } + + /* Fill in the blanks */ + size_t len = s.length(); + if (len < width) { + int delta = width - len; + if (fmt & ios_base::internal) { + if (sgn) + s.insert(static_cast(1), delta, fill); + else + s.insert(static_cast(0), delta, fill); + } else if (fmt & ios_base::left) { + s.append(delta, fill); + } else { + s.insert(static_cast(0), delta, fill); + } + } + + return s; +} + +/* Computes qd^n, where n is an integer. */ +qd_real pow(const qd_real &a, int n) { + if (n == 0) + return 1.0; + + qd_real r = a; /* odd-case multiplier */ + qd_real s = 1.0; /* current answer */ + int N = std::abs(n); + + if (N > 1) { + + /* Use binary exponentiation. */ + while (N > 0) { + if (N % 2 == 1) { + /* If odd, multiply by r. Note eventually N = 1, so this + eventually executes. */ + s *= r; + } + N /= 2; + if (N > 0) + r = sqr(r); + } + + } else { + s = r; + } + + if (n < 0) + return (1.0 / s); + + return s; +} + +qd_real pow(const qd_real &a, const qd_real &b) { + return exp(b * log(a)); +} + +qd_real npwr(const qd_real &a, int n) { + return pow(a, n); +} + +/* Debugging routines */ +void qd_real::dump_bits(const string &name, std::ostream &os) const { + string::size_type len = name.length(); + if (len > 0) { + os << name << " = "; + len += 3; + } + os << "[ "; + len += 2; + for (int j = 0; j < 4; j++) { + if (j > 0) for (string::size_type i = 0; i < len; i++) os << ' '; + print_double_info(os, x[j]); + if (j < 3) + os << endl; + else + os << " ]" << endl; + } +} + +void qd_real::dump(const string &name, std::ostream &os) const { + std::ios_base::fmtflags old_flags = os.flags(); + std::streamsize old_prec = os.precision(19); + os << std::scientific; + + string::size_type len = name.length(); + if (len > 0) { + os << name << " = "; + len += 3; + } + os << "[ "; + len += 2; + os << setw(27) << x[0] << ", " << setw(26) << x[1] << "," << endl; + for (string::size_type i = 0; i < len; i++) os << ' '; + os << setw(27) << x[2] << ", " << setw(26) << x[3] << " ]" << endl; + + os.precision(old_prec); + os.flags(old_flags); +} + +/* Divisions */ +/* quad-double / double-double */ +qd_real qd_real::sloppy_div(const qd_real &a, const dd_real &b) { + double q0, q1, q2, q3; + qd_real r; + qd_real qd_b(b); + + q0 = a[0] / b._hi(); + r = a - q0 * qd_b; + + q1 = r[0] / b._hi(); + r -= (q1 * qd_b); + + q2 = r[0] / b._hi(); + r -= (q2 * qd_b); + + q3 = r[0] / b._hi(); + + ::renorm(q0, q1, q2, q3); + return qd_real(q0, q1, q2, q3); +} + +qd_real qd_real::accurate_div(const qd_real &a, const dd_real &b) { + double q0, q1, q2, q3, q4; + qd_real r; + qd_real qd_b(b); + + q0 = a[0] / b._hi(); + r = a - q0 * qd_b; + + q1 = r[0] / b._hi(); + r -= (q1 * qd_b); + + q2 = r[0] / b._hi(); + r -= (q2 * qd_b); + + q3 = r[0] / b._hi(); + r -= (q3 * qd_b); + + q4 = r[0] / b._hi(); + + ::renorm(q0, q1, q2, q3, q4); + return qd_real(q0, q1, q2, q3); +} + +/* quad-double / quad-double */ +qd_real qd_real::sloppy_div(const qd_real &a, const qd_real &b) { + double q0, q1, q2, q3; + + qd_real r; + + q0 = a[0] / b[0]; + r = a - (b * q0); + + q1 = r[0] / b[0]; + r -= (b * q1); + + q2 = r[0] / b[0]; + r -= (b * q2); + + q3 = r[0] / b[0]; + + ::renorm(q0, q1, q2, q3); + + return qd_real(q0, q1, q2, q3); +} + +qd_real qd_real::accurate_div(const qd_real &a, const qd_real &b) { + double q0, q1, q2, q3; + + qd_real r; + + q0 = a[0] / b[0]; + r = a - (b * q0); + + q1 = r[0] / b[0]; + r -= (b * q1); + + q2 = r[0] / b[0]; + r -= (b * q2); + + q3 = r[0] / b[0]; + + r -= (b * q3); + double q4 = r[0] / b[0]; + + ::renorm(q0, q1, q2, q3, q4); + + return qd_real(q0, q1, q2, q3); +} + +QD_API qd_real sqrt(const qd_real &a) { + /* Strategy: + + Perform the following Newton iteration: + + x' = x + (1 - a * x^2) * x / 2; + + which converges to 1/sqrt(a), starting with the + double precision approximation to 1/sqrt(a). + Since Newton's iteration more or less doubles the + number of correct digits, we only need to perform it + twice. + */ + + if (a.is_zero()) + return 0.0; + + if (a.is_negative()) { + qd_real::error("(qd_real::sqrt): Negative argument."); + return qd_real::_nan; + } + + qd_real r = (1.0 / std::sqrt(a[0])); + qd_real h = mul_pwr2(a, 0.5); + + r += ((0.5 - h * sqr(r)) * r); + r += ((0.5 - h * sqr(r)) * r); + r += ((0.5 - h * sqr(r)) * r); + + r *= a; + return r; +} + + +/* Computes the n-th root of a */ +qd_real nroot(const qd_real &a, int n) { + /* Strategy: Use Newton's iteration to solve + + 1/(x^n) - a = 0 + + Newton iteration becomes + + x' = x + x * (1 - a * x^n) / n + + Since Newton's iteration converges quadratically, + we only need to perform it twice. + + */ + if (n <= 0) { + qd_real::error("(qd_real::nroot): N must be positive."); + return qd_real::_nan; + } + + if (n % 2 == 0 && a.is_negative()) { + qd_real::error("(qd_real::nroot): Negative argument."); + return qd_real::_nan; + } + + if (n == 1) { + return a; + } + if (n == 2) { + return sqrt(a); + } + if (a.is_zero()) { + return qd_real(0.0); + } + + + /* Note a^{-1/n} = exp(-log(a)/n) */ + qd_real r = abs(a); + qd_real x = std::exp(-std::log(r.x[0]) / n); + + /* Perform Newton's iteration. */ + double dbl_n = static_cast(n); + x += x * (1.0 - r * npwr(x, n)) / dbl_n; + x += x * (1.0 - r * npwr(x, n)) / dbl_n; + x += x * (1.0 - r * npwr(x, n)) / dbl_n; + if (a[0] < 0.0){ + x = -x; + } + return 1.0 / x; +} + +static const int n_inv_fact = 15; +static const qd_real inv_fact[n_inv_fact] = { + qd_real( 1.66666666666666657e-01, 9.25185853854297066e-18, + 5.13581318503262866e-34, 2.85094902409834186e-50), + qd_real( 4.16666666666666644e-02, 2.31296463463574266e-18, + 1.28395329625815716e-34, 7.12737256024585466e-51), + qd_real( 8.33333333333333322e-03, 1.15648231731787138e-19, + 1.60494162032269652e-36, 2.22730392507682967e-53), + qd_real( 1.38888888888888894e-03, -5.30054395437357706e-20, + -1.73868675534958776e-36, -1.63335621172300840e-52), + qd_real( 1.98412698412698413e-04, 1.72095582934207053e-22, + 1.49269123913941271e-40, 1.29470326746002471e-58), + qd_real( 2.48015873015873016e-05, 2.15119478667758816e-23, + 1.86586404892426588e-41, 1.61837908432503088e-59), + qd_real( 2.75573192239858925e-06, -1.85839327404647208e-22, + 8.49175460488199287e-39, -5.72661640789429621e-55), + qd_real( 2.75573192239858883e-07, 2.37677146222502973e-23, + -3.26318890334088294e-40, 1.61435111860404415e-56), + qd_real( 2.50521083854417202e-08, -1.44881407093591197e-24, + 2.04267351467144546e-41, -8.49632672007163175e-58), + qd_real( 2.08767569878681002e-09, -1.20734505911325997e-25, + 1.70222792889287100e-42, 1.41609532150396700e-58), + qd_real( 1.60590438368216133e-10, 1.25852945887520981e-26, + -5.31334602762985031e-43, 3.54021472597605528e-59), + qd_real( 1.14707455977297245e-11, 2.06555127528307454e-28, + 6.88907923246664603e-45, 5.72920002655109095e-61), + qd_real( 7.64716373181981641e-13, 7.03872877733453001e-30, + -7.82753927716258345e-48, 1.92138649443790242e-64), + qd_real( 4.77947733238738525e-14, 4.39920548583408126e-31, + -4.89221204822661465e-49, 1.20086655902368901e-65), + qd_real( 2.81145725434552060e-15, 1.65088427308614326e-31, + -2.87777179307447918e-50, 4.27110689256293549e-67) +}; + +qd_real exp(const qd_real &a) { + /* Strategy: We first reduce the size of x by noting that + + exp(kr + m * log(2)) = 2^m * exp(r)^k + + where m and k are integers. By choosing m appropriately + we can make |kr| <= log(2) / 2 = 0.347. Then exp(r) is + evaluated using the familiar Taylor series. Reducing the + argument substantially speeds up the convergence. */ + + const double k = ldexp(1.0, 16); + const double inv_k = 1.0 / k; + + if (a[0] <= -709.0) + return 0.0; + + if (a[0] >= 709.0) + return qd_real::_inf; + + if (a.is_zero()) + return 1.0; + + if (a.is_one()) + return qd_real::_e; + + double m = std::floor(a.x[0] / qd_real::_log2.x[0] + 0.5); + qd_real r = mul_pwr2(a - qd_real::_log2 * m, inv_k); + qd_real s, p, t; + double thresh = inv_k * qd_real::_eps; + + p = sqr(r); + s = r + mul_pwr2(p, 0.5); + int i = 0; + do { + p *= r; + t = p * inv_fact[i++]; + s += t; + } while (std::abs(to_double(t)) > thresh && i < 9); + + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s += 1.0; + return ldexp(s, static_cast(m)); +} + +/* Logarithm. Computes log(x) in quad-double precision. + This is a natural logarithm (i.e., base e). */ +qd_real log(const qd_real &a) { + /* Strategy. The Taylor series for log converges much more + slowly than that of exp, due to the lack of the factorial + term in the denominator. Hence this routine instead tries + to determine the root of the function + + f(x) = exp(x) - a + + using Newton iteration. The iteration is given by + + x' = x - f(x)/f'(x) + = x - (1 - a * exp(-x)) + = x + a * exp(-x) - 1. + + Two iteration is needed, since Newton's iteration + approximately doubles the number of digits per iteration. */ + + if (a.is_one()) { + return 0.0; + } + + if (a[0] <= 0.0) { + qd_real::error("(qd_real::log): Non-positive argument."); + return qd_real::_nan; + } + + if (a[0] == 0.0) { + return -qd_real::_inf; + } + + qd_real x = std::log(a[0]); /* Initial approximation */ + + x = x + a * exp(-x) - 1.0; + x = x + a * exp(-x) - 1.0; + x = x + a * exp(-x) - 1.0; + + return x; +} + +qd_real log10(const qd_real &a) { + return log(a) / qd_real::_log10; +} + +static const qd_real _pi1024 = qd_real( + 3.067961575771282340e-03, 1.195944139792337116e-19, + -2.924579892303066080e-36, 1.086381075061880158e-52); + +/* Table of sin(k * pi/1024) and cos(k * pi/1024). */ +static const qd_real sin_table [] = { + qd_real( 3.0679567629659761e-03, 1.2690279085455925e-19, + 5.2879464245328389e-36, -1.7820334081955298e-52), + qd_real( 6.1358846491544753e-03, 9.0545257482474933e-20, + 1.6260113133745320e-37, -9.7492001208767410e-55), + qd_real( 9.2037547820598194e-03, -1.2136591693535934e-19, + 5.5696903949425567e-36, 1.2505635791936951e-52), + qd_real( 1.2271538285719925e-02, 6.9197907640283170e-19, + -4.0203726713435555e-36, -2.0688703606952816e-52), + qd_real( 1.5339206284988102e-02, -8.4462578865401696e-19, + 4.6535897505058629e-35, -1.3923682978570467e-51), + qd_real( 1.8406729905804820e-02, 7.4195533812833160e-19, + 3.9068476486787607e-35, 3.6393321292898614e-52), + qd_real( 2.1474080275469508e-02, -4.5407960207688566e-19, + -2.2031770119723005e-35, 1.2709814654833741e-51), + qd_real( 2.4541228522912288e-02, -9.1868490125778782e-20, + 4.8706148704467061e-36, -2.8153947855469224e-52), + qd_real( 2.7608145778965743e-02, -1.5932358831389269e-18, + -7.0475416242776030e-35, -2.7518494176602744e-51), + qd_real( 3.0674803176636626e-02, -1.6936054844107918e-20, + -2.0039543064442544e-36, -1.6267505108658196e-52), + qd_real( 3.3741171851377587e-02, -2.0096074292368340e-18, + -1.3548237016537134e-34, 6.5554881875899973e-51), + qd_real( 3.6807222941358832e-02, 6.1060088803529842e-19, + -4.0448721259852727e-35, -2.1111056765671495e-51), + qd_real( 3.9872927587739811e-02, 4.6657453481183289e-19, + 3.4119333562288684e-35, 2.4007534726187511e-51), + qd_real( 4.2938256934940820e-02, 2.8351940588660907e-18, + 1.6991309601186475e-34, 6.8026536098672629e-51), + qd_real( 4.6003182130914630e-02, -1.1182813940157788e-18, + 7.5235020270378946e-35, 4.1187304955493722e-52), + qd_real( 4.9067674327418015e-02, -6.7961037205182801e-19, + -4.4318868124718325e-35, -9.9376628132525316e-52), + qd_real( 5.2131704680283324e-02, -2.4243695291953779e-18, + -1.3675405320092298e-34, -8.3938137621145070e-51), + qd_real( 5.5195244349689941e-02, -1.3340299860891103e-18, + -3.4359574125665608e-35, 1.1911462755409369e-51), + qd_real( 5.8258264500435759e-02, 2.3299905496077492e-19, + 1.9376108990628660e-36, -5.1273775710095301e-53), + qd_real( 6.1320736302208578e-02, -5.1181134064638108e-19, + -4.2726335866706313e-35, 2.6368495557440691e-51), + qd_real( 6.4382630929857465e-02, -4.2325997000052705e-18, + 3.3260117711855937e-35, 1.4736267706718352e-51), + qd_real( 6.7443919563664065e-02, -6.9221796556983636e-18, + 1.5909286358911040e-34, -7.8828946891835218e-51), + qd_real( 7.0504573389613870e-02, -6.8552791107342883e-18, + -1.9961177630841580e-34, 2.0127129580485300e-50), + qd_real( 7.3564563599667426e-02, -2.7784941506273593e-18, + -9.1240375489852821e-35, -1.9589752023546795e-51), + qd_real( 7.6623861392031492e-02, 2.3253700287958801e-19, + -1.3186083921213440e-36, -4.9927872608099673e-53), + qd_real( 7.9682437971430126e-02, -4.4867664311373041e-18, + 2.8540789143650264e-34, 2.8491348583262741e-51), + qd_real( 8.2740264549375692e-02, 1.4735983530877760e-18, + 3.7284093452233713e-35, 2.9024430036724088e-52), + qd_real( 8.5797312344439894e-02, -3.3881893830684029e-18, + -1.6135529531508258e-34, 7.7294651620588049e-51), + qd_real( 8.8853552582524600e-02, -3.7501775830290691e-18, + 3.7543606373911573e-34, 2.2233701854451859e-50), + qd_real( 9.1908956497132724e-02, 4.7631594854274564e-18, + 1.5722874642939344e-34, -4.8464145447831456e-51), + qd_real( 9.4963495329639006e-02, -6.5885886400417564e-18, + -2.1371116991641965e-34, 1.3819370559249300e-50), + qd_real( 9.8017140329560604e-02, -1.6345823622442560e-18, + -1.3209238810006454e-35, -3.5691060049117942e-52), + qd_real( 1.0106986275482782e-01, 3.3164325719308656e-18, + -1.2004224885132282e-34, 7.2028828495418631e-51), + qd_real( 1.0412163387205457e-01, 6.5760254085385100e-18, + 1.7066246171219214e-34, -4.9499340996893514e-51), + qd_real( 1.0717242495680884e-01, 6.4424044279026198e-18, + -8.3956976499698139e-35, -4.0667730213318321e-51), + qd_real( 1.1022220729388306e-01, -5.6789503537823233e-19, + 1.0380274792383233e-35, 1.5213997918456695e-52), + qd_real( 1.1327095217756435e-01, 2.7100481012132900e-18, + 1.5323292999491619e-35, 4.9564432810360879e-52), + qd_real( 1.1631863091190477e-01, 1.0294914877509705e-18, + -9.3975734948993038e-35, 1.3534827323719708e-52), + qd_real( 1.1936521481099137e-01, -3.9500089391898506e-18, + 3.5317349978227311e-34, 1.8856046807012275e-51), + qd_real( 1.2241067519921620e-01, 2.8354501489965335e-18, + 1.8151655751493305e-34, -2.8716592177915192e-51), + qd_real( 1.2545498341154623e-01, 4.8686751763148235e-18, + 5.9878105258097936e-35, -3.3534629098722107e-51), + qd_real( 1.2849811079379317e-01, 3.8198603954988802e-18, + -1.8627501455947798e-34, -2.4308161133527791e-51), + qd_real( 1.3154002870288312e-01, -5.0039708262213813e-18, + -1.2983004159245552e-34, -4.6872034915794122e-51), + qd_real( 1.3458070850712620e-01, -9.1670359171480699e-18, + 1.5916493007073973e-34, 4.0237002484366833e-51), + qd_real( 1.3762012158648604e-01, 6.6253255866774482e-18, + -2.3746583031401459e-34, -9.3703876173093250e-52), + qd_real( 1.4065823933284924e-01, -7.9193932965524741e-18, + 6.0972464202108397e-34, 2.4566623241035797e-50), + qd_real( 1.4369503315029444e-01, 1.1472723016618666e-17, + -5.1884954557576435e-35, -4.2220684832186607e-51), + qd_real( 1.4673047445536175e-01, 3.7269471470465677e-18, + 3.7352398151250827e-34, -4.0881822289508634e-51), + qd_real( 1.4976453467732151e-01, 8.0812114131285151e-18, + 1.2979142554917325e-34, 9.9380667487736254e-51), + qd_real( 1.5279718525844344e-01, -7.6313573938416838e-18, + 5.7714690450284125e-34, -3.7731132582986687e-50), + qd_real( 1.5582839765426523e-01, 3.0351307187678221e-18, + -1.0976942315176184e-34, 7.8734647685257867e-51), + qd_real( 1.5885814333386145e-01, -4.0163200573859079e-18, + -9.2840580257628812e-35, -2.8567420029274875e-51), + qd_real( 1.6188639378011183e-01, 1.1850519643573528e-17, + -5.0440990519162957e-34, 3.0510028707928009e-50), + qd_real( 1.6491312048996992e-01, -7.0405288319166738e-19, + 3.3211107491245527e-35, 8.6663299254686031e-52), + qd_real( 1.6793829497473117e-01, 5.4284533721558139e-18, + -3.3263339336181369e-34, -1.8536367335123848e-50), + qd_real( 1.7096188876030122e-01, 9.1919980181759094e-18, + -6.7688743940982606e-34, -1.0377711384318389e-50), + qd_real( 1.7398387338746382e-01, 5.8151994618107928e-18, + -1.6751014298301606e-34, -6.6982259797164963e-51), + qd_real( 1.7700422041214875e-01, 6.7329300635408167e-18, + 2.8042736644246623e-34, 3.6786888232793599e-51), + qd_real( 1.8002290140569951e-01, 7.9701826047392143e-18, + -7.0765920110524977e-34, 1.9622512608461784e-50), + qd_real( 1.8303988795514095e-01, 7.7349918688637383e-18, + -4.4803769968145083e-34, 1.1201148793328890e-50), + qd_real( 1.8605515166344666e-01, -1.2564893007679552e-17, + 7.5953844248530810e-34, -3.8471695132415039e-51), + qd_real( 1.8906866414980622e-01, -7.6208955803527778e-18, + -4.4792298656662981e-34, -4.4136824096645007e-50), + qd_real( 1.9208039704989244e-01, 4.3348343941174903e-18, + -2.3404121848139937e-34, 1.5789970962611856e-50), + qd_real( 1.9509032201612828e-01, -7.9910790684617313e-18, + 6.1846270024220713e-34, -3.5840270918032937e-50), + qd_real( 1.9809841071795359e-01, -1.8434411800689445e-18, + 1.4139031318237285e-34, 1.0542811125343809e-50), + qd_real( 2.0110463484209190e-01, 1.1010032669300739e-17, + -3.9123576757413791e-34, 2.4084852500063531e-51), + qd_real( 2.0410896609281687e-01, 6.0941297773957752e-18, + -2.8275409970449641e-34, 4.6101008563532989e-51), + qd_real( 2.0711137619221856e-01, -1.0613362528971356e-17, + 2.2456805112690884e-34, 1.3483736125280904e-50), + qd_real( 2.1011183688046961e-01, 1.1561548476512844e-17, + 6.0355905610401254e-34, 3.3329909618405675e-50), + qd_real( 2.1311031991609136e-01, 1.2031873821063860e-17, + -3.4142699719695635e-34, -1.2436262780241778e-50), + qd_real( 2.1610679707621952e-01, -1.0111196082609117e-17, + 7.2789545335189643e-34, -2.9347540365258610e-50), + qd_real( 2.1910124015686980e-01, -3.6513812299150776e-19, + -2.3359499418606442e-35, 3.1785298198458653e-52), + qd_real( 2.2209362097320354e-01, -3.0337210995812162e-18, + 6.6654668033632998e-35, 2.0110862322656942e-51), + qd_real( 2.2508391135979283e-01, 3.9507040822556510e-18, + 2.4287993958305375e-35, 5.6662797513020322e-52), + qd_real( 2.2807208317088573e-01, 8.2361837339258012e-18, + 6.9786781316397937e-34, -6.4122962482639504e-51), + qd_real( 2.3105810828067111e-01, 1.0129787149761869e-17, + -6.9359234615816044e-34, -2.8877355604883782e-50), + qd_real( 2.3404195858354343e-01, -6.9922402696101173e-18, + -5.7323031922750280e-34, 5.3092579966872727e-51), + qd_real( 2.3702360599436720e-01, 8.8544852285039918e-18, + 1.3588480826354134e-34, 1.0381022520213867e-50), + qd_real( 2.4000302244874150e-01, -1.2137758975632164e-17, + -2.6448807731703891e-34, -1.9929733800670473e-51), + qd_real( 2.4298017990326390e-01, -8.7514315297196632e-18, + -6.5723260373079431e-34, -1.0333158083172177e-50), + qd_real( 2.4595505033579462e-01, -1.1129044052741832e-17, + 4.3805998202883397e-34, 1.2219399554686291e-50), + qd_real( 2.4892760574572018e-01, -8.1783436100020990e-18, + 5.5666875261111840e-34, 3.8080473058748167e-50), + qd_real( 2.5189781815421697e-01, -1.7591436032517039e-17, + -1.0959681232525285e-33, 5.6209426020232456e-50), + qd_real( 2.5486565960451457e-01, -1.3602299806901461e-19, + -6.0073844642762535e-36, -3.0072751311893878e-52), + qd_real( 2.5783110216215899e-01, 1.8480038630879957e-17, + 3.3201664714047599e-34, -5.5547819290576764e-51), + qd_real( 2.6079411791527551e-01, 4.2721420983550075e-18, + 5.6782126934777920e-35, 3.1428338084365397e-51), + qd_real( 2.6375467897483140e-01, -1.8837947680038700e-17, + 1.3720129045754794e-33, -8.2763406665966033e-50), + qd_real( 2.6671275747489837e-01, 2.0941222578826688e-17, + -1.1303466524727989e-33, 1.9954224050508963e-50), + qd_real( 2.6966832557291509e-01, 1.5765657618133259e-17, + -6.9696142173370086e-34, -4.0455346879146776e-50), + qd_real( 2.7262135544994898e-01, 7.8697166076387850e-18, + 6.6179388602933372e-35, -2.7642903696386267e-51), + qd_real( 2.7557181931095814e-01, 1.9320328962556582e-17, + 1.3932094180100280e-33, 1.3617253920018116e-50), + qd_real( 2.7851968938505312e-01, -1.0030273719543544e-17, + 7.2592115325689254e-34, -1.0068516296655851e-50), + qd_real( 2.8146493792575800e-01, -1.2322299641274009e-17, + -1.0564788706386435e-34, 7.5137424251265885e-51), + qd_real( 2.8440753721127182e-01, 2.2209268510661475e-17, + -9.1823095629523708e-34, -5.2192875308892218e-50), + qd_real( 2.8734745954472951e-01, 1.5461117367645717e-17, + -6.3263973663444076e-34, -2.2982538416476214e-50), + qd_real( 2.9028467725446239e-01, -1.8927978707774251e-17, + 1.1522953157142315e-33, 7.4738655654716596e-50), + qd_real( 2.9321916269425863e-01, 2.2385430811901833e-17, + 1.3662484646539680e-33, -4.2451325253996938e-50), + qd_real( 2.9615088824362384e-01, -2.0220736360876938e-17, + -7.9252212533920413e-35, -2.8990577729572470e-51), + qd_real( 2.9907982630804048e-01, 1.6701181609219447e-18, + 8.6091151117316292e-35, 3.9931286230012102e-52), + qd_real( 3.0200594931922808e-01, -1.7167666235262474e-17, + 2.3336182149008069e-34, 8.3025334555220004e-51), + qd_real( 3.0492922973540243e-01, -2.2989033898191262e-17, + -1.4598901099661133e-34, 3.7760487693121827e-51), + qd_real( 3.0784964004153487e-01, 2.7074088527245185e-17, + 1.2568858206899284e-33, 7.2931815105901645e-50), + qd_real( 3.1076715274961147e-01, 2.0887076364048513e-17, + -3.0130590791065942e-34, 1.3876739009935179e-51), + qd_real( 3.1368174039889146e-01, 1.4560447299968912e-17, + 3.6564186898011595e-34, 1.1654264734999375e-50), + qd_real( 3.1659337555616585e-01, 2.1435292512726283e-17, + 1.2338169231377316e-33, 3.3963542100989293e-50), + qd_real( 3.1950203081601569e-01, -1.3981562491096626e-17, + 8.1730000697411350e-34, -7.7671096270210952e-50), + qd_real( 3.2240767880106985e-01, -4.0519039937959398e-18, + 3.7438302780296796e-34, 8.7936731046639195e-51), + qd_real( 3.2531029216226293e-01, 7.9171249463765892e-18, + -6.7576622068146391e-35, 2.3021655066929538e-51), + qd_real( 3.2820984357909255e-01, -2.6693140719641896e-17, + 7.8928851447534788e-34, 2.5525163821987809e-51), + qd_real( 3.3110630575987643e-01, -2.7469465474778694e-17, + -1.3401245916610206e-33, 6.5531762489976163e-50), + qd_real( 3.3399965144200938e-01, 2.2598986806288142e-17, + 7.8063057192586115e-34, 2.0427600895486683e-50), + qd_real( 3.3688985339222005e-01, -4.2000940033475092e-19, + -2.9178652969985438e-36, -1.1597376437036749e-52), + qd_real( 3.3977688440682685e-01, 6.6028679499418282e-18, + 1.2575009988669683e-34, 2.5569067699008304e-51), + qd_real( 3.4266071731199438e-01, 1.9261518449306319e-17, + -9.2754189135990867e-34, 8.5439996687390166e-50), + qd_real( 3.4554132496398904e-01, 2.7251143672916123e-17, + 7.0138163601941737e-34, -1.4176292197454015e-50), + qd_real( 3.4841868024943456e-01, 3.6974420514204918e-18, + 3.5532146878499996e-34, 1.9565462544501322e-50), + qd_real( 3.5129275608556715e-01, -2.2670712098795844e-17, + -1.6994216673139631e-34, -1.2271556077284517e-50), + qd_real( 3.5416352542049040e-01, -1.6951763305764860e-17, + 1.2772331777814617e-33, -3.3703785435843310e-50), + qd_real( 3.5703096123343003e-01, -4.8218191137919166e-19, + -4.1672436994492361e-35, -7.1531167149364352e-52), + qd_real( 3.5989503653498817e-01, -1.7601687123839282e-17, + 1.3375125473046791e-33, 7.9467815593584340e-50), + qd_real( 3.6275572436739723e-01, -9.1668352663749849e-18, + -7.4317843956936735e-34, -2.0199582511804564e-50), + qd_real( 3.6561299780477385e-01, 1.6217898770457546e-17, + 1.1286970151961055e-33, -7.1825287318139010e-50), + qd_real( 3.6846682995337232e-01, 1.0463640796159268e-17, + 2.0554984738517304e-35, 1.0441861305618769e-51), + qd_real( 3.7131719395183754e-01, 3.4749239648238266e-19, + -7.5151053042866671e-37, -2.8153468438650851e-53), + qd_real( 3.7416406297145799e-01, 8.0114103761962118e-18, + 5.3429599813406052e-34, 1.0351378796539210e-50), + qd_real( 3.7700741021641826e-01, -2.7255302041956930e-18, + 6.3646586445018137e-35, 8.3048657176503559e-52), + qd_real( 3.7984720892405116e-01, 9.9151305855172370e-18, + 4.8761409697224886e-34, 1.4025084000776705e-50), + qd_real( 3.8268343236508978e-01, -1.0050772696461588e-17, + -2.0605316302806695e-34, -1.2717724698085205e-50), + qd_real( 3.8551605384391885e-01, 1.5177665396472313e-17, + 1.4198230518016535e-33, 5.8955167159904235e-50), + qd_real( 3.8834504669882630e-01, -1.0053770598398717e-17, + 7.5942999255057131e-34, -3.1967974046654219e-50), + qd_real( 3.9117038430225387e-01, 1.7997787858243995e-17, + -1.0613482402609856e-33, -5.4582148817791032e-50), + qd_real( 3.9399204006104810e-01, 9.7649241641239336e-18, + -2.1233599441284617e-34, -5.5529836795340819e-51), + qd_real( 3.9680998741671031e-01, 2.0545063670840126e-17, + 6.1347058801922842e-34, 1.0733788150636430e-50), + qd_real( 3.9962419984564684e-01, -1.5065497476189372e-17, + -9.9653258881867298e-34, -5.7524323712725355e-50), + qd_real( 4.0243465085941843e-01, 1.0902619339328270e-17, + 7.3998528125989765e-34, 2.2745784806823499e-50), + qd_real( 4.0524131400498986e-01, 9.9111401942899884e-18, + -2.5169070895434648e-34, 9.2772984818436573e-53), + qd_real( 4.0804416286497869e-01, -7.0006015137351311e-18, + -1.4108207334268228e-34, 1.5175546997577136e-52), + qd_real( 4.1084317105790397e-01, -2.4219835190355499e-17, + -1.1418902925313314e-33, -2.0996843165093468e-50), + qd_real( 4.1363831223843456e-01, -1.0393984940597871e-17, + -1.1481681174503880e-34, -2.0281052851028680e-51), + qd_real( 4.1642956009763721e-01, -2.5475580413131732e-17, + -3.4482678506112824e-34, 7.1788619351865480e-51), + qd_real( 4.1921688836322396e-01, -4.2232463750110590e-18, + -3.6053023045255790e-34, -2.2209673210025631e-50), + qd_real( 4.2200027079979968e-01, 4.3543266994128527e-18, + 3.1734310272251190e-34, -1.3573247980738668e-50), + qd_real( 4.2477968120910881e-01, 2.7462312204277281e-17, + -4.6552847802111948e-34, 6.5961781099193122e-51), + qd_real( 4.2755509343028208e-01, 9.4111898162954726e-18, + -1.7446682426598801e-34, -2.2054492626480169e-51), + qd_real( 4.3032648134008261e-01, 2.2259686974092690e-17, + 8.5972591314085075e-34, -2.9420897889003020e-50), + qd_real( 4.3309381885315196e-01, 1.1224283329847517e-17, + 5.3223748041075651e-35, 5.3926192627014212e-51), + qd_real( 4.3585707992225547e-01, 1.6230515450644527e-17, + -6.4371449063579431e-35, -6.9102436481386757e-51), + qd_real( 4.3861623853852766e-01, -2.0883315831075090e-17, + -1.4259583540891877e-34, 6.3864763590657077e-52), + qd_real( 4.4137126873171667e-01, 2.2360783886964969e-17, + 1.1864769603515770e-34, -3.8087003266189232e-51), + qd_real( 4.4412214457042926e-01, -2.4218874422178315e-17, + 2.2205230838703907e-34, 9.2133035911356258e-51), + qd_real( 4.4686884016237421e-01, -1.9222136142309382e-17, + -4.4425678589732049e-35, -1.3673609292149535e-51), + qd_real( 4.4961132965460660e-01, 4.8831924232035243e-18, + 2.7151084498191381e-34, -1.5653993171613154e-50), + qd_real( 4.5234958723377089e-01, -1.4827977472196122e-17, + -7.6947501088972324e-34, 1.7656856882031319e-50), + qd_real( 4.5508358712634384e-01, -1.2379906758116472e-17, + 5.5289688955542643e-34, -8.5382312840209386e-51), + qd_real( 4.5781330359887723e-01, -8.4554254922295949e-18, + -6.3770394246764263e-34, 3.1778253575564249e-50), + qd_real( 4.6053871095824001e-01, 1.8488777492177872e-17, + -1.0527732154209725e-33, 3.3235593490947102e-50), + qd_real( 4.6325978355186020e-01, -7.3514924533231707e-18, + 6.7175396881707035e-34, 3.9594127612123379e-50), + qd_real( 4.6597649576796618e-01, -3.3023547778235135e-18, + 3.4904677050476886e-35, 3.4483855263874246e-51), + qd_real( 4.6868882203582796e-01, -2.2949251681845054e-17, + -1.1364757641823658e-33, 6.8840522501918612e-50), + qd_real( 4.7139673682599764e-01, 6.5166781360690130e-18, + 2.9457546966235984e-34, -6.2159717738836630e-51), + qd_real( 4.7410021465055002e-01, -8.1451601548978075e-18, + -3.4789448555614422e-34, -1.1681943974658508e-50), + qd_real( 4.7679923006332214e-01, -1.0293515338305794e-17, + -3.6582045008369952e-34, 1.7424131479176475e-50), + qd_real( 4.7949375766015301e-01, 1.8419999662684771e-17, + -1.3040838621273312e-33, 1.0977131822246471e-50), + qd_real( 4.8218377207912277e-01, -2.5861500925520442e-17, + -6.2913197606500007e-36, 4.0802359808684726e-52), + qd_real( 4.8486924800079112e-01, -1.8034004203262245e-17, + -3.5244276906958044e-34, -1.7138318654749246e-50), + qd_real( 4.8755016014843594e-01, 1.4231090931273653e-17, + -1.8277733073262697e-34, -1.5208291790429557e-51), + qd_real( 4.9022648328829116e-01, -5.1496145643440404e-18, + -3.6903027405284104e-34, 1.5172940095151304e-50), + qd_real( 4.9289819222978404e-01, -1.0257831676562186e-18, + 6.9520817760885069e-35, -2.4260961214090389e-51), + qd_real( 4.9556526182577254e-01, -9.4323241942365362e-18, + 3.1212918657699143e-35, 4.2009072375242736e-52), + qd_real( 4.9822766697278187e-01, -1.6126383830540798e-17, + -1.5092897319298871e-33, 1.1049298890895917e-50), + qd_real( 5.0088538261124083e-01, -3.9604015147074639e-17, + -2.2208395201898007e-33, 1.3648202735839417e-49), + qd_real( 5.0353838372571758e-01, -1.6731308204967497e-17, + -1.0140233644074786e-33, 4.0953071937671477e-50), + qd_real( 5.0618664534515534e-01, -4.8321592986493711e-17, + 9.2858107226642252e-34, 4.2699802401037005e-50), + qd_real( 5.0883014254310699e-01, 4.7836968268014130e-17, + -1.0727022928806035e-33, 2.7309374513672757e-50), + qd_real( 5.1146885043797041e-01, -1.3088001221007579e-17, + 4.0929033363366899e-34, -3.7952190153477926e-50), + qd_real( 5.1410274419322177e-01, -4.5712707523615624e-17, + 1.5488279442238283e-33, -2.5853959305521130e-50), + qd_real( 5.1673179901764987e-01, 8.3018617233836515e-18, + 5.8251027467695202e-34, -2.2812397190535076e-50), + qd_real( 5.1935599016558964e-01, -5.5331248144171145e-17, + -3.1628375609769026e-35, -2.4091972051188571e-51), + qd_real( 5.2197529293715439e-01, -4.6555795692088883e-17, + 4.6378980936850430e-34, -3.3470542934689532e-51), + qd_real( 5.2458968267846895e-01, -4.3068869040082345e-17, + -4.2013155291932055e-34, -1.5096069926700274e-50), + qd_real( 5.2719913478190139e-01, -4.2202983480560619e-17, + 8.5585916184867295e-34, 7.9974339336732307e-50), + qd_real( 5.2980362468629472e-01, -4.8067841706482342e-17, + 5.8309721046630296e-34, -8.9740761521756660e-51), + qd_real( 5.3240312787719801e-01, -4.1020306135800895e-17, + -1.9239996374230821e-33, -1.5326987913812184e-49), + qd_real( 5.3499761988709726e-01, -5.3683132708358134e-17, + -1.3900569918838112e-33, 2.7154084726474092e-50), + qd_real( 5.3758707629564551e-01, -2.2617365388403054e-17, + -5.9787279033447075e-34, 3.1204419729043625e-51), + qd_real( 5.4017147272989285e-01, 2.7072447965935839e-17, + 1.1698799709213829e-33, -5.9094668515881500e-50), + qd_real( 5.4275078486451589e-01, 1.7148261004757101e-17, + -1.3525905925200870e-33, 4.9724411290727323e-50), + qd_real( 5.4532498842204646e-01, -4.1517817538384258e-17, + -1.5318930219385941e-33, 6.3629921101413974e-50), + qd_real( 5.4789405917310019e-01, -2.4065878297113363e-17, + -3.5639213669362606e-36, -2.6013270854271645e-52), + qd_real( 5.5045797293660481e-01, -8.3319903015807663e-18, + -2.3058454035767633e-34, -2.1611290432369010e-50), + qd_real( 5.5301670558002758e-01, -4.7061536623798204e-17, + -1.0617111545918056e-33, -1.6196316144407379e-50), + qd_real( 5.5557023301960218e-01, 4.7094109405616768e-17, + -2.0640520383682921e-33, 1.2290163188567138e-49), + qd_real( 5.5811853122055610e-01, 1.3481176324765226e-17, + -5.5016743873011438e-34, -2.3484822739335416e-50), + qd_real( 5.6066157619733603e-01, -7.3956418153476152e-18, + 3.9680620611731193e-34, 3.1995952200836223e-50), + qd_real( 5.6319934401383409e-01, 2.3835775146854829e-17, + 1.3511793173769814e-34, 9.3201311581248143e-51), + qd_real( 5.6573181078361323e-01, -3.4096079596590466e-17, + -1.7073289744303546e-33, 8.9147089975404507e-50), + qd_real( 5.6825895267013160e-01, -5.0935673642769248e-17, + -1.6274356351028249e-33, 9.8183151561702966e-51), + qd_real( 5.7078074588696726e-01, 2.4568151455566208e-17, + -1.2844481247560350e-33, -1.8037634376936261e-50), + qd_real( 5.7329716669804220e-01, 8.5176611669306400e-18, + -6.4443208788026766e-34, 2.2546105543273003e-50), + qd_real( 5.7580819141784534e-01, -3.7909495458942734e-17, + -2.7433738046854309e-33, 1.1130841524216795e-49), + qd_real( 5.7831379641165559e-01, -2.6237691512372831e-17, + 1.3679051680738167e-33, -3.1409808935335900e-50), + qd_real( 5.8081395809576453e-01, 1.8585338586613408e-17, + 2.7673843114549181e-34, 1.9605349619836937e-50), + qd_real( 5.8330865293769829e-01, 3.4516601079044858e-18, + 1.8065977478946306e-34, -6.3953958038544646e-51), + qd_real( 5.8579785745643886e-01, -3.7485501964311294e-18, + 2.7965403775536614e-34, -7.1816936024157202e-51), + qd_real( 5.8828154822264533e-01, -2.9292166725006846e-17, + -2.3744954603693934e-33, -1.1571631191512480e-50), + qd_real( 5.9075970185887428e-01, -4.7013584170659542e-17, + 2.4808417611768356e-33, 1.2598907673643198e-50), + qd_real( 5.9323229503979980e-01, 1.2892320944189053e-17, + 5.3058364776359583e-34, 4.1141674699390052e-50), + qd_real( 5.9569930449243336e-01, -1.3438641936579467e-17, + -6.7877687907721049e-35, -5.6046937531684890e-51), + qd_real( 5.9816070699634227e-01, 3.8801885783000657e-17, + -1.2084165858094663e-33, -4.0456610843430061e-50), + qd_real( 6.0061647938386897e-01, -4.6398198229461932e-17, + -1.6673493003710801e-33, 5.1982824378491445e-50), + qd_real( 6.0306659854034816e-01, 3.7323357680559650e-17, + 2.7771920866974305e-33, -1.6194229649742458e-49), + qd_real( 6.0551104140432555e-01, -3.1202672493305677e-17, + 1.2761267338680916e-33, -4.0859368598379647e-50), + qd_real( 6.0794978496777363e-01, 3.5160832362096660e-17, + -2.5546242776778394e-34, -1.4085313551220694e-50), + qd_real( 6.1038280627630948e-01, -2.2563265648229169e-17, + 1.3185575011226730e-33, 8.2316691420063460e-50), + qd_real( 6.1281008242940971e-01, -4.2693476568409685e-18, + 2.5839965886650320e-34, 1.6884412005622537e-50), + qd_real( 6.1523159058062682e-01, 2.6231417767266950e-17, + -1.4095366621106716e-33, 7.2058690491304558e-50), + qd_real( 6.1764730793780398e-01, -4.7478594510902452e-17, + -7.2986558263123996e-34, -3.0152327517439154e-50), + qd_real( 6.2005721176328921e-01, -2.7983410837681118e-17, + 1.1649951056138923e-33, -5.4539089117135207e-50), + qd_real( 6.2246127937414997e-01, 5.2940728606573002e-18, + -4.8486411215945827e-35, 1.2696527641980109e-52), + qd_real( 6.2485948814238634e-01, 3.3671846037243900e-17, + -2.7846053391012096e-33, 5.6102718120012104e-50), + qd_real( 6.2725181549514408e-01, 3.0763585181253225e-17, + 2.7068930273498138e-34, -1.1172240309286484e-50), + qd_real( 6.2963823891492698e-01, 4.1115334049626806e-17, + -1.9167473580230747e-33, 1.1118424028161730e-49), + qd_real( 6.3201873593980906e-01, -4.0164942296463612e-17, + -7.2208643641736723e-34, 3.7828920470544344e-50), + qd_real( 6.3439328416364549e-01, 1.0420901929280035e-17, + 4.1174558929280492e-34, -1.4464152986630705e-51), + qd_real( 6.3676186123628420e-01, 3.1419048711901611e-17, + -2.2693738415126449e-33, -1.6023584204297388e-49), + qd_real( 6.3912444486377573e-01, 1.2416796312271043e-17, + -6.2095419626356605e-34, 2.7762065999506603e-50), + qd_real( 6.4148101280858316e-01, -9.9883430115943310e-18, + 4.1969230376730128e-34, 5.6980543799257597e-51), + qd_real( 6.4383154288979150e-01, -3.2084798795046886e-17, + -1.2595311907053305e-33, -4.0205885230841536e-50), + qd_real( 6.4617601298331639e-01, -2.9756137382280815e-17, + -1.0275370077518259e-33, 8.0852478665893014e-51), + qd_real( 6.4851440102211244e-01, 3.9870270313386831e-18, + 1.9408388509540788e-34, -5.1798420636193190e-51), + qd_real( 6.5084668499638088e-01, 3.9714670710500257e-17, + 2.9178546787002963e-34, 3.8140635508293278e-51), + qd_real( 6.5317284295377676e-01, 8.5695642060026238e-18, + -6.9165322305070633e-34, 2.3873751224185395e-50), + qd_real( 6.5549285299961535e-01, 3.5638734426385005e-17, + 1.2695365790889811e-33, 4.3984952865412050e-50), + qd_real( 6.5780669329707864e-01, 1.9580943058468545e-17, + -1.1944272256627192e-33, 2.8556402616436858e-50), + qd_real( 6.6011434206742048e-01, -1.3960054386823638e-19, + 6.1515777931494047e-36, 5.3510498875622660e-52), + qd_real( 6.6241577759017178e-01, -2.2615508885764591e-17, + 5.0177050318126862e-34, 2.9162532399530762e-50), + qd_real( 6.6471097820334490e-01, -3.6227793598034367e-17, + -9.0607934765540427e-34, 3.0917036342380213e-50), + qd_real( 6.6699992230363747e-01, 3.5284364997428166e-17, + -1.0382057232458238e-33, 7.3812756550167626e-50), + qd_real( 6.6928258834663612e-01, -5.4592652417447913e-17, + -2.5181014709695152e-33, -1.6867875999437174e-49), + qd_real( 6.7155895484701844e-01, -4.0489037749296692e-17, + 3.1995835625355681e-34, -1.4044414655670960e-50), + qd_real( 6.7382900037875604e-01, 2.3091901236161086e-17, + 5.7428037192881319e-34, 1.1240668354625977e-50), + qd_real( 6.7609270357531592e-01, 3.7256902248049466e-17, + 1.7059417895764375e-33, 9.7326347795300652e-50), + qd_real( 6.7835004312986147e-01, 1.8302093041863122e-17, + 9.5241675746813072e-34, 5.0328101116133503e-50), + qd_real( 6.8060099779545302e-01, 2.8473293354522047e-17, + 4.1331805977270903e-34, 4.2579030510748576e-50), + qd_real( 6.8284554638524808e-01, -1.2958058061524531e-17, + 1.8292386959330698e-34, 3.4536209116044487e-51), + qd_real( 6.8508366777270036e-01, 2.5948135194645137e-17, + -8.5030743129500702e-34, -6.9572086141009930e-50), + qd_real( 6.8731534089175916e-01, -5.5156158714917168e-17, + 1.1896489854266829e-33, -7.8505896218220662e-51), + qd_real( 6.8954054473706694e-01, -1.5889323294806790e-17, + 9.1242356240205712e-34, 3.8315454152267638e-50), + qd_real( 6.9175925836415775e-01, 2.7406078472410668e-17, + 1.3286508943202092e-33, 1.0651869129580079e-51), + qd_real( 6.9397146088965400e-01, 7.4345076956280137e-18, + 7.5061528388197460e-34, -1.5928000240686583e-50), + qd_real( 6.9617713149146299e-01, -4.1224081213582889e-17, + -3.1838716762083291e-35, -3.9625587412119131e-51), + qd_real( 6.9837624940897280e-01, 4.8988282435667768e-17, + 1.9134010413244152e-33, 2.6161153243793989e-50), + qd_real( 7.0056879394324834e-01, 3.1027960192992922e-17, + 9.5638250509179997e-34, 4.5896916138107048e-51), + qd_real( 7.0275474445722530e-01, 2.5278294383629822e-18, + -8.6985561210674942e-35, -5.6899862307812990e-51), + qd_real( 7.0493408037590488e-01, 2.7608725585748502e-17, + 2.9816599471629137e-34, 1.1533044185111206e-50), + qd_real( 7.0710678118654757e-01, -4.8336466567264567e-17, + 2.0693376543497068e-33, 2.4677734957341755e-50) +}; + +static const qd_real cos_table [] = { + qd_real( 9.9999529380957619e-01, -1.9668064285322189e-17, + -6.3053955095883481e-34, 5.3266110855726731e-52), + qd_real( 9.9998117528260111e-01, 3.3568103522895585e-17, + -1.4740132559368063e-35, 9.8603097594755596e-52), + qd_real( 9.9995764455196390e-01, -3.1527836866647287e-17, + 2.6363251186638437e-33, 1.0007504815488399e-49), + qd_real( 9.9992470183914450e-01, 3.7931082512668012e-17, + -8.5099918660501484e-35, -4.9956973223295153e-51), + qd_real( 9.9988234745421256e-01, -3.5477814872408538e-17, + 1.7102001035303974e-33, -1.0725388519026542e-49), + qd_real( 9.9983058179582340e-01, 1.8825140517551119e-17, + -5.1383513457616937e-34, -3.8378827995403787e-50), + qd_real( 9.9976940535121528e-01, 4.2681177032289012e-17, + 1.9062302359737099e-33, -6.0221153262881160e-50), + qd_real( 9.9969881869620425e-01, -2.9851486403799753e-17, + -1.9084787370733737e-33, 5.5980260344029202e-51), + qd_real( 9.9961882249517864e-01, -4.1181965521424734e-17, + 2.0915365593699916e-33, 8.1403390920903734e-50), + qd_real( 9.9952941750109314e-01, 2.0517917823755591e-17, + -4.7673802585706520e-34, -2.9443604198656772e-50), + qd_real( 9.9943060455546173e-01, 3.9644497752257798e-17, + -2.3757223716722428e-34, -1.2856759011361726e-51), + qd_real( 9.9932238458834954e-01, -4.2858538440845682e-17, + 3.3235101605146565e-34, -8.3554272377057543e-51), + qd_real( 9.9920475861836389e-01, 9.1796317110385693e-18, + 5.5416208185868570e-34, 8.0267046717615311e-52), + qd_real( 9.9907772775264536e-01, 2.1419007653587032e-17, + -7.9048203318529618e-34, -5.3166296181112712e-50), + qd_real( 9.9894129318685687e-01, -2.0610641910058638e-17, + -1.2546525485913485e-33, -7.5175888806157064e-50), + qd_real( 9.9879545620517241e-01, -1.2291693337075465e-17, + 2.4468446786491271e-34, 1.0723891085210268e-50), + qd_real( 9.9864021818026527e-01, -4.8690254312923302e-17, + -2.9470881967909147e-34, -1.3000650761346907e-50), + qd_real( 9.9847558057329477e-01, -2.2002931182778795e-17, + -1.2371509454944992e-33, -2.4911225131232065e-50), + qd_real( 9.9830154493389289e-01, -5.1869402702792278e-17, + 1.0480195493633452e-33, -2.8995649143155511e-50), + qd_real( 9.9811811290014918e-01, 2.7935487558113833e-17, + 2.4423341255830345e-33, -6.7646699175334417e-50), + qd_real( 9.9792528619859600e-01, 1.7143659778886362e-17, + 5.7885840902887460e-34, -9.2601432603894597e-51), + qd_real( 9.9772306664419164e-01, -2.6394475274898721e-17, + -1.6176223087661783e-34, -9.9924942889362281e-51), + qd_real( 9.9751145614030345e-01, 5.6007205919806937e-18, + -5.9477673514685690e-35, -1.4166807162743627e-54), + qd_real( 9.9729045667869021e-01, 9.1647695371101735e-18, + 6.7824134309739296e-34, -8.6191392795543357e-52), + qd_real( 9.9706007033948296e-01, 1.6734093546241963e-17, + -1.3169951440780028e-33, 1.0311048767952477e-50), + qd_real( 9.9682029929116567e-01, 4.7062820708615655e-17, + 2.8412041076474937e-33, -8.0006155670263622e-50), + qd_real( 9.9657114579055484e-01, 1.1707179088390986e-17, + -7.5934413263024663e-34, 2.8474848436926008e-50), + qd_real( 9.9631261218277800e-01, 1.1336497891624735e-17, + 3.4002458674414360e-34, 7.7419075921544901e-52), + qd_real( 9.9604470090125197e-01, 2.2870031707670695e-17, + -3.9184839405013148e-34, -3.7081260416246375e-50), + qd_real( 9.9576741446765982e-01, -2.3151908323094359e-17, + -1.6306512931944591e-34, -1.5925420783863192e-51), + qd_real( 9.9548075549192694e-01, 3.2084621412226554e-18, + -4.9501292146013023e-36, -2.7811428850878516e-52), + qd_real( 9.9518472667219693e-01, -4.2486913678304410e-17, + 1.3315510772504614e-33, 6.7927987417051888e-50), + qd_real( 9.9487933079480562e-01, 4.2130813284943662e-18, + -4.2062597488288452e-35, 2.5157064556087620e-51), + qd_real( 9.9456457073425542e-01, 3.6745069641528058e-17, + -3.0603304105471010e-33, 1.0397872280487526e-49), + qd_real( 9.9424044945318790e-01, 4.4129423472462673e-17, + -3.0107231708238066e-33, 7.4201582906861892e-50), + qd_real( 9.9390697000235606e-01, -1.8964849471123746e-17, + -1.5980853777937752e-35, -8.5374807150597082e-52), + qd_real( 9.9356413552059530e-01, 2.9752309927797428e-17, + -4.5066707331134233e-34, -3.3548191633805036e-50), + qd_real( 9.9321194923479450e-01, 3.3096906261272262e-17, + 1.5592811973249567e-33, 1.4373977733253592e-50), + qd_real( 9.9285041445986510e-01, -1.4094517733693302e-17, + -1.1954558131616916e-33, 1.8761873742867983e-50), + qd_real( 9.9247953459870997e-01, 3.1093055095428906e-17, + -1.8379594757818019e-33, -3.9885758559381314e-51), + qd_real( 9.9209931314219180e-01, -3.9431926149588778e-17, + -6.2758062911047230e-34, -1.2960929559212390e-50), + qd_real( 9.9170975366909953e-01, -2.3372891311883661e-18, + 2.7073298824968591e-35, -1.2569459441802872e-51), + qd_real( 9.9131085984611544e-01, -2.5192111583372105e-17, + -1.2852471567380887e-33, 5.2385212584310483e-50), + qd_real( 9.9090263542778001e-01, 1.5394565094566704e-17, + -1.0799984133184567e-33, 2.7451115960133595e-51), + qd_real( 9.9048508425645709e-01, -5.5411437553780867e-17, + -1.4614017210753585e-33, -3.8339374397387620e-50), + qd_real( 9.9005821026229712e-01, -1.7055485906233963e-17, + 1.3454939685758777e-33, 7.3117589137300036e-50), + qd_real( 9.8962201746320089e-01, -5.2398217968132530e-17, + 1.3463189211456219e-33, 5.8021640554894872e-50), + qd_real( 9.8917650996478101e-01, -4.0987309937047111e-17, + -4.4857560552048437e-34, -3.9414504502871125e-50), + qd_real( 9.8872169196032378e-01, -1.0976227206656125e-17, + 3.2311342577653764e-34, 9.6367946583575041e-51), + qd_real( 9.8825756773074946e-01, 2.7030607784372632e-17, + 7.7514866488601377e-35, 2.1019644956864938e-51), + qd_real( 9.8778414164457218e-01, -2.3600693397159021e-17, + -1.2323283769707861e-33, 3.0130900716803339e-50), + qd_real( 9.8730141815785843e-01, -5.2332261255715652e-17, + -2.7937644333152473e-33, 1.2074160567958408e-49), + qd_real( 9.8680940181418553e-01, -5.0287214351061075e-17, + -2.2681526238144461e-33, 4.4003694320169133e-50), + qd_real( 9.8630809724459867e-01, -2.1520877103013341e-17, + 1.1866528054187716e-33, -7.8532199199813836e-50), + qd_real( 9.8579750916756748e-01, -5.1439452979953012e-17, + 2.6276439309996725e-33, 7.5423552783286347e-50), + qd_real( 9.8527764238894122e-01, 2.3155637027900207e-17, + -7.5275971545764833e-34, 1.0582231660456094e-50), + qd_real( 9.8474850180190421e-01, 1.0548144061829957e-17, + 2.8786145266267306e-34, -3.6782210081466112e-51), + qd_real( 9.8421009238692903e-01, 4.7983922627050691e-17, + 2.2597419645070588e-34, 1.7573875814863400e-50), + qd_real( 9.8366241921173025e-01, 1.9864948201635255e-17, + -1.0743046281211033e-35, 1.7975662796558100e-52), + qd_real( 9.8310548743121629e-01, 4.2170007522888628e-17, + 8.2396265656440904e-34, -8.0803700139096561e-50), + qd_real( 9.8253930228744124e-01, 1.5149580813777224e-17, + -4.1802771422186237e-34, -2.2150174326226160e-50), + qd_real( 9.8196386910955524e-01, 2.1108443711513084e-17, + -1.5253013442896054e-33, -6.8388082079337969e-50), + qd_real( 9.8137919331375456e-01, 1.3428163260355633e-17, + -6.5294290469962986e-34, 2.7965412287456268e-51), + qd_real( 9.8078528040323043e-01, 1.8546939997825006e-17, + -1.0696564445530757e-33, 6.6668174475264961e-50), + qd_real( 9.8018213596811743e-01, -3.6801786963856159e-17, + 6.3245171387992842e-34, 1.8600176137175971e-50), + qd_real( 9.7956976568544052e-01, 1.5573991584990420e-17, + -1.3401066029782990e-33, -1.7263702199862149e-50), + qd_real( 9.7894817531906220e-01, -2.3817727961148053e-18, + -1.0694750370381661e-34, -8.2293047196087462e-51), + qd_real( 9.7831737071962765e-01, -2.1623082233344895e-17, + 1.0970403012028032e-33, 7.7091923099369339e-50), + qd_real( 9.7767735782450993e-01, 5.0514136167059628e-17, + -1.3254751701428788e-33, 7.0161254312124538e-50), + qd_real( 9.7702814265775439e-01, -4.3353875751555997e-17, + 5.4948839831535478e-34, -9.2755263105377306e-51), + qd_real( 9.7636973133002114e-01, 9.3093931526213780e-18, + -4.1184949155685665e-34, -3.1913926031393690e-50), + qd_real( 9.7570213003852857e-01, -2.5572556081259686e-17, + -9.3174244508942223e-34, -8.3675863211646863e-51), + qd_real( 9.7502534506699412e-01, 2.6642660651899135e-17, + 1.7819392739353853e-34, -3.3159625385648947e-51), + qd_real( 9.7433938278557586e-01, 2.3041221476151512e-18, + 1.0758686005031430e-34, 5.1074116432809478e-51), + qd_real( 9.7364424965081198e-01, -5.1729808691005871e-17, + -1.5508473005989887e-33, -1.6505125917675401e-49), + qd_real( 9.7293995220556018e-01, -3.1311211122281800e-17, + -2.6874087789006141e-33, -2.1652434818822145e-51), + qd_real( 9.7222649707893627e-01, 3.6461169785938221e-17, + 3.0309636883883133e-33, -1.2702716907967306e-51), + qd_real( 9.7150389098625178e-01, -7.9865421122289046e-18, + -4.3628417211263380e-34, 3.4307517798759352e-51), + qd_real( 9.7077214072895035e-01, -4.7992163325114922e-17, + 3.0347528910975783e-33, 8.5989199506479701e-50), + qd_real( 9.7003125319454397e-01, 1.8365300348428844e-17, + -1.4311097571944918e-33, 8.5846781998740697e-51), + qd_real( 9.6928123535654853e-01, -4.5663660261927896e-17, + 9.6147526917239387e-34, 8.1267605207871330e-51), + qd_real( 9.6852209427441727e-01, 4.9475074918244771e-17, + 2.8558738351911241e-33, 6.2948422316507461e-50), + qd_real( 9.6775383709347551e-01, -4.5512132825515820e-17, + -1.4127617988719093e-33, -8.4620609089704578e-50), + qd_real( 9.6697647104485207e-01, 3.8496228837337864e-17, + -5.3881631542745647e-34, -3.5221863171458959e-50), + qd_real( 9.6619000344541250e-01, 5.1298840401665493e-17, + 1.4564075904769808e-34, 1.0095973971377432e-50), + qd_real( 9.6539444169768940e-01, -2.3745389918392156e-17, + 5.9221515590053862e-34, -3.8811192556231094e-50), + qd_real( 9.6458979328981276e-01, -3.4189470735959786e-17, + 2.2982074155463522e-33, -4.5128791045607634e-50), + qd_real( 9.6377606579543984e-01, 2.6463950561220029e-17, + -2.9073234590199323e-36, -1.2938328629395601e-52), + qd_real( 9.6295326687368388e-01, 8.9341960404313634e-18, + -3.9071244661020126e-34, 1.6212091116847394e-50), + qd_real( 9.6212140426904158e-01, 1.5236770453846305e-17, + -1.3050173525597142e-33, 7.9016122394092666e-50), + qd_real( 9.6128048581132064e-01, 2.0933955216674039e-18, + 1.0768607469015692e-34, -5.9453639304361774e-51), + qd_real( 9.6043051941556579e-01, 2.4653904815317185e-17, + -1.3792169410906322e-33, -4.7726598378506903e-51), + qd_real( 9.5957151308198452e-01, 1.1000640085000957e-17, + -4.2036030828223975e-34, 4.0023704842606573e-51), + qd_real( 9.5870347489587160e-01, -4.3685014392372053e-17, + 2.2001800662729131e-33, -1.0553721324358075e-49), + qd_real( 9.5782641302753291e-01, -1.7696710075371263e-17, + 1.9164034110382190e-34, 8.1489235071754813e-51), + qd_real( 9.5694033573220882e-01, 4.0553869861875701e-17, + -1.7147013364302149e-33, 2.5736745295329455e-50), + qd_real( 9.5604525134999641e-01, 3.7705045279589067e-17, + 1.9678699997347571e-33, 8.5093177731230180e-50), + qd_real( 9.5514116830577067e-01, 5.0088652955014668e-17, + -2.6983181838059211e-33, 1.0102323575596493e-49), + qd_real( 9.5422809510910567e-01, -3.7545901690626874e-17, + 1.4951619241257764e-33, -8.2717333151394973e-50), + qd_real( 9.5330604035419386e-01, -2.5190738779919934e-17, + -1.4272239821134379e-33, -4.6717286809283155e-50), + qd_real( 9.5237501271976588e-01, -2.0269300462299272e-17, + -1.0635956887246246e-33, -3.5514537666487619e-50), + qd_real( 9.5143502096900834e-01, 3.1350584123266695e-17, + -2.4824833452737813e-33, 9.5450335525380613e-51), + qd_real( 9.5048607394948170e-01, 1.9410097562630436e-17, + -8.1559393949816789e-34, -1.0501209720164562e-50), + qd_real( 9.4952818059303667e-01, -7.5544151928043298e-18, + -5.1260245024046686e-34, 1.8093643389040406e-50), + qd_real( 9.4856134991573027e-01, 2.0668262262333232e-17, + -5.9440730243667306e-34, 1.4268853111554300e-50), + qd_real( 9.4758559101774109e-01, 4.3417993852125991e-17, + -2.7728667889840373e-34, 5.5709160196519968e-51), + qd_real( 9.4660091308328353e-01, 3.5056800210680730e-17, + 9.8578536940318117e-34, 6.6035911064585197e-50), + qd_real( 9.4560732538052128e-01, 4.6019102478523738e-17, + -6.2534384769452059e-34, 1.5758941215779961e-50), + qd_real( 9.4460483726148026e-01, 8.8100545476641165e-18, + 5.2291695602757842e-34, -3.3487256018407123e-50), + qd_real( 9.4359345816196039e-01, -2.4093127844404214e-17, + 1.0283279856803939e-34, -2.3398232614531355e-51), + qd_real( 9.4257319760144687e-01, 1.3235564806436886e-17, + -5.7048262885386911e-35, 3.9947050442753744e-51), + qd_real( 9.4154406518302081e-01, -2.7896379547698341e-17, + 1.6273236356733898e-33, -5.3075944708471203e-51), + qd_real( 9.4050607059326830e-01, 2.8610421567116268e-17, + 2.9261501147538827e-33, -2.6849867690896925e-50), + qd_real( 9.3945922360218992e-01, -7.0152867943098655e-18, + -5.6395693818011210e-34, 3.5568142678987651e-50), + qd_real( 9.3840353406310806e-01, 5.4242545044795490e-17, + -1.9039966607859759e-33, -1.5627792988341215e-49), + qd_real( 9.3733901191257496e-01, -3.6570926284362776e-17, + -1.1902940071273247e-33, -1.1215082331583223e-50), + qd_real( 9.3626566717027826e-01, -1.3013766145497654e-17, + 5.2229870061990595e-34, -3.3972777075634108e-51), + qd_real( 9.3518350993894761e-01, -3.2609395302485065e-17, + -8.1813015218875245e-34, 5.5642140024928139e-50), + qd_real( 9.3409255040425887e-01, 4.4662824360767511e-17, + -2.5903243047396916e-33, 8.1505209004343043e-50), + qd_real( 9.3299279883473885e-01, 4.2041415555384355e-17, + 9.0285896495521276e-34, 5.3019984977661259e-50), + qd_real( 9.3188426558166815e-01, -4.0785944377318095e-17, + 1.7631450298754169e-33, 2.5776403305507453e-50), + qd_real( 9.3076696107898371e-01, 1.9703775102838329e-17, + 6.5657908718278205e-34, -1.9480347966259524e-51), + qd_real( 9.2964089584318121e-01, 5.1282530016864107e-17, + 2.3719739891916261e-34, -1.7230065426917127e-50), + qd_real( 9.2850608047321559e-01, -2.3306639848485943e-17, + -7.7799084333208503e-34, -5.8597558009300305e-50), + qd_real( 9.2736252565040111e-01, -2.7677111692155437e-17, + 2.2110293450199576e-34, 2.0349190819680613e-50), + qd_real( 9.2621024213831138e-01, -3.7303754586099054e-17, + 2.0464457809993405e-33, 1.3831799631231817e-49), + qd_real( 9.2504924078267758e-01, 6.0529447412576159e-18, + -8.8256517760278541e-35, 1.8285462122388328e-51), + qd_real( 9.2387953251128674e-01, 1.7645047084336677e-17, + -5.0442537321586818e-34, -4.0478677716823890e-50), + qd_real( 9.2270112833387852e-01, 5.2963798918539814e-17, + -5.7135699628876685e-34, 3.0163671797219087e-50), + qd_real( 9.2151403934204190e-01, 4.1639843390684644e-17, + 1.1891485604702356e-33, 2.0862437594380324e-50), + qd_real( 9.2031827670911059e-01, -2.7806888779036837e-17, + 2.7011013677071274e-33, 1.1998578792455499e-49), + qd_real( 9.1911385169005777e-01, -2.6496484622344718e-17, + 6.5403604763461920e-34, -2.8997180201186078e-50), + qd_real( 9.1790077562139050e-01, -3.9074579680849515e-17, + 2.3004636541490264e-33, 3.9851762744443107e-50), + qd_real( 9.1667905992104270e-01, -4.1733978698287568e-17, + 1.2094444804381172e-33, 4.9356916826097816e-50), + qd_real( 9.1544871608826783e-01, -1.3591056692900894e-17, + 5.9923027475594735e-34, 2.1403295925962879e-50), + qd_real( 9.1420975570353069e-01, -3.6316182527814423e-17, + -1.9438819777122554e-33, 2.8340679287728316e-50), + qd_real( 9.1296219042839821e-01, -4.7932505228039469e-17, + -1.7753551889428638e-33, 4.0607782903868160e-51), + qd_real( 9.1170603200542988e-01, -2.6913273175034130e-17, + -5.1928101916162528e-35, 1.1338175936090630e-51), + qd_real( 9.1044129225806725e-01, -5.0433041673313820e-17, + 1.0938746257404305e-33, 9.5378272084170731e-51), + qd_real( 9.0916798309052238e-01, -3.6878564091359894e-18, + 2.9951330310507693e-34, -1.2225666136919926e-50), + qd_real( 9.0788611648766626e-01, -4.9459964301225840e-17, + -1.6599682707075313e-33, -5.1925202712634716e-50), + qd_real( 9.0659570451491533e-01, 3.0506718955442023e-17, + -1.4478836557141204e-33, 1.8906373784448725e-50), + qd_real( 9.0529675931811882e-01, -4.1153099826889901e-17, + 2.9859368705184223e-33, 5.1145293917439211e-50), + qd_real( 9.0398929312344334e-01, -6.6097544687484308e-18, + 1.2728013034680357e-34, -4.3026097234014823e-51), + qd_real( 9.0267331823725883e-01, -1.9250787033961483e-17, + 1.3242128993244527e-33, -5.2971030688703665e-50), + qd_real( 9.0134884704602203e-01, -1.3524789367698682e-17, + 6.3605353115880091e-34, 3.6227400654573828e-50), + qd_real( 9.0001589201616028e-01, -5.0639618050802273e-17, + 1.0783525384031576e-33, 2.8130016326515111e-50), + qd_real( 8.9867446569395382e-01, 2.6316906461033013e-17, + 3.7003137047796840e-35, -2.3447719900465938e-51), + qd_real( 8.9732458070541832e-01, -3.6396283314867290e-17, + -2.3611649895474815e-33, 1.1837247047900082e-49), + qd_real( 8.9596624975618511e-01, 4.9025099114811813e-17, + -1.9440489814795326e-33, -1.7070486667767033e-49), + qd_real( 8.9459948563138270e-01, -1.7516226396814919e-17, + -1.3200670047246923e-33, -1.5953009884324695e-50), + qd_real( 8.9322430119551532e-01, -4.1161239151908913e-18, + 2.5380253805715999e-34, 4.2849455510516192e-51), + qd_real( 8.9184070939234272e-01, 4.6690228137124547e-18, + 1.6150254286841982e-34, -3.9617448820725012e-51), + qd_real( 8.9044872324475788e-01, 1.1781931459051803e-17, + -1.3346142209571930e-34, -9.4982373530733431e-51), + qd_real( 8.8904835585466457e-01, -1.1164514966766675e-17, + -3.4797636107798736e-34, -1.5605079997040631e-50), + qd_real( 8.8763962040285393e-01, 1.2805091918587960e-17, + 3.9948742059584459e-35, 3.8940716325338136e-51), + qd_real( 8.8622253014888064e-01, -6.7307369600274315e-18, + 1.2385593432917413e-34, 2.0364014759133320e-51), + qd_real( 8.8479709843093779e-01, -9.4331469628972690e-18, + -5.7106541478701439e-34, 1.8260134111907397e-50), + qd_real( 8.8336333866573158e-01, 1.5822643380255127e-17, + -7.8921320007588250e-34, -1.4782321016179836e-50), + qd_real( 8.8192126434835505e-01, -1.9843248405890562e-17, + -7.0412114007673834e-34, -1.0636770169389104e-50), + qd_real( 8.8047088905216075e-01, 1.6311096602996350e-17, + -5.7541360594724172e-34, -4.0128611862170021e-50), + qd_real( 8.7901222642863353e-01, -4.7356837291118011e-17, + 1.4388771297975192e-33, -2.9085554304479134e-50), + qd_real( 8.7754529020726124e-01, 5.0113311846499550e-17, + 2.8382769008739543e-34, 1.5550640393164140e-50), + qd_real( 8.7607009419540660e-01, 5.8729024235147677e-18, + 2.7941144391738458e-34, -1.8536073846509828e-50), + qd_real( 8.7458665227817611e-01, -5.7216617730397065e-19, + -2.9705811503689596e-35, 8.7389593969796752e-52), + qd_real( 8.7309497841829009e-01, 7.8424672990129903e-18, + -4.8685015839797165e-34, -2.2815570587477527e-50), + qd_real( 8.7159508665595109e-01, -5.5272998038551050e-17, + -2.2104090204984907e-33, -9.7749763187643172e-50), + qd_real( 8.7008699110871146e-01, -4.1888510868549968e-17, + 7.0900185861878415e-34, 3.7600251115157260e-50), + qd_real( 8.6857070597134090e-01, 2.7192781689782903e-19, + -1.6710140396932428e-35, -1.2625514734637969e-51), + qd_real( 8.6704624551569265e-01, 3.0267859550930567e-18, + -1.1559438782171572e-34, -5.3580556397808012e-52), + qd_real( 8.6551362409056909e-01, -6.3723113549628899e-18, + 2.3725520321746832e-34, 1.5911880348395175e-50), + qd_real( 8.6397285612158670e-01, 4.1486355957361607e-17, + 2.2709976932210266e-33, -8.1228385659479984e-50), + qd_real( 8.6242395611104050e-01, 3.7008992527383130e-17, + 5.2128411542701573e-34, 2.6945600081026861e-50), + qd_real( 8.6086693863776731e-01, -3.0050048898573656e-17, + -8.8706183090892111e-34, 1.5005320558097301e-50), + qd_real( 8.5930181835700836e-01, 4.2435655816850687e-17, + 7.6181814059912025e-34, -3.9592127850658708e-50), + qd_real( 8.5772861000027212e-01, -4.8183447936336620e-17, + -1.1044130517687532e-33, -8.7400233444645562e-50), + qd_real( 8.5614732837519447e-01, 9.1806925616606261e-18, + 5.6328649785951470e-34, 2.3326646113217378e-51), + qd_real( 8.5455798836540053e-01, -1.2991124236396092e-17, + 1.2893407722948080e-34, -3.6506925747583053e-52), + qd_real( 8.5296060493036363e-01, 2.7152984251981370e-17, + 7.4336483283120719e-34, 4.2162417622350668e-50), + qd_real( 8.5135519310526520e-01, -5.3279874446016209e-17, + 2.2281156380919942e-33, -4.0281886404138477e-50), + qd_real( 8.4974176800085244e-01, 5.1812347659974015e-17, + 3.0810626087331275e-33, -2.5931308201994965e-50), + qd_real( 8.4812034480329723e-01, 1.8762563415239981e-17, + 1.4048773307919617e-33, -2.4915221509958691e-50), + qd_real( 8.4649093877405213e-01, -4.7969419958569345e-17, + -2.7518267097886703e-33, -7.3518959727313350e-50), + qd_real( 8.4485356524970712e-01, -4.3631360296879637e-17, + -2.0307726853367547e-33, 4.3097229819851761e-50), + qd_real( 8.4320823964184544e-01, 9.6536707005959077e-19, + 2.8995142431556364e-36, 9.6715076811480284e-53), + qd_real( 8.4155497743689844e-01, -3.4095465391321557e-17, + -8.4130208607579595e-34, -4.9447283960568686e-50), + qd_real( 8.3989379419599952e-01, -1.6673694881511411e-17, + -1.4759184141750289e-33, -7.5795098161914058e-50), + qd_real( 8.3822470555483808e-01, -3.5560085052855026e-17, + 1.1689791577022643e-33, -5.8627347359723411e-50), + qd_real( 8.3654772722351201e-01, -2.0899059027066533e-17, + -9.8104097821002585e-35, -3.1609177868229853e-51), + qd_real( 8.3486287498638001e-01, 4.6048430609159657e-17, + -5.1827423265239912e-34, -7.0505343435504109e-51), + qd_real( 8.3317016470191319e-01, 1.3275129507229764e-18, + 4.8589164115370863e-35, 4.5422281300506859e-51), + qd_real( 8.3146961230254524e-01, 1.4073856984728024e-18, + 4.6951315383980830e-35, 5.1431906049905658e-51), + qd_real( 8.2976123379452305e-01, -2.9349109376485597e-18, + 1.1496917934149818e-34, 3.5186665544980233e-51), + qd_real( 8.2804504525775580e-01, -4.4196593225871532e-17, + 2.7967864855211251e-33, 1.0030777287393502e-49), + qd_real( 8.2632106284566353e-01, -5.3957485453612902e-17, + 6.8976896130138550e-34, 3.8106164274199196e-50), + qd_real( 8.2458930278502529e-01, -2.6512360488868275e-17, + 1.6916964350914386e-34, 6.7693974813562649e-51), + qd_real( 8.2284978137582632e-01, 1.5193019034505495e-17, + 9.6890547246521685e-34, 5.6994562923653264e-50), + qd_real( 8.2110251499110465e-01, 3.0715131609697682e-17, + -1.7037168325855879e-33, -1.1149862443283853e-49), + qd_real( 8.1934752007679701e-01, -4.8200736995191133e-17, + -1.5574489646672781e-35, -9.5647853614522216e-53), + qd_real( 8.1758481315158371e-01, -1.4883149812426772e-17, + -7.8273262771298917e-34, 4.1332149161031594e-50), + qd_real( 8.1581441080673378e-01, 8.2652693782130871e-18, + -2.3028778135179471e-34, 1.5102071387249843e-50), + qd_real( 8.1403632970594841e-01, -5.2127351877042624e-17, + -1.9047670611316360e-33, -1.6937269585941507e-49), + qd_real( 8.1225058658520388e-01, 3.1054545609214803e-17, + 2.2649541922707251e-34, -7.4221684154649405e-51), + qd_real( 8.1045719825259477e-01, 2.3520367349840499e-17, + -7.7530070904846341e-34, -7.2792616357197140e-50), + qd_real( 8.0865618158817498e-01, 9.3251597879721674e-18, + -7.1823301933068394e-34, 2.3925440846132106e-50), + qd_real( 8.0684755354379922e-01, 4.9220603766095546e-17, + 2.9796016899903487e-33, 1.5220754223615788e-49), + qd_real( 8.0503133114296355e-01, 5.1368289568212149e-17, + 6.3082807402256524e-34, 7.3277646085129827e-51), + qd_real( 8.0320753148064494e-01, -3.3060609804814910e-17, + -1.2242726252420433e-33, 2.8413673268630117e-50), + qd_real( 8.0137617172314024e-01, -2.0958013413495834e-17, + -4.3798162198006931e-34, 2.0235690497752515e-50), + qd_real( 7.9953726910790501e-01, 2.0356723822005431e-17, + -9.7448513696896360e-34, 5.3608109599696008e-52), + qd_real( 7.9769084094339116e-01, -4.6730759884788944e-17, + 2.3075897077191757e-33, 3.1605567774640253e-51), + qd_real( 7.9583690460888357e-01, -3.0062724851910721e-17, + -2.2496210832042235e-33, -6.5881774117183040e-50), + qd_real( 7.9397547755433717e-01, -7.4194631759921416e-18, + 2.4124341304631069e-34, -4.9956808616244972e-51), + qd_real( 7.9210657730021239e-01, -3.7087850202326467e-17, + -1.4874457267228264e-33, 2.9323097289153505e-50), + qd_real( 7.9023022143731003e-01, 2.3056905954954492e-17, + 1.4481080533260193e-33, -7.6725237057203488e-50), + qd_real( 7.8834642762660623e-01, 3.4396993154059708e-17, + 1.7710623746737170e-33, 1.7084159098417402e-49), + qd_real( 7.8645521359908577e-01, -9.7841429939305265e-18, + 3.3906063272445472e-34, 5.7269505320382577e-51), + qd_real( 7.8455659715557524e-01, -8.5627965423173476e-18, + -2.1106834459001849e-34, -1.6890322182469603e-50), + qd_real( 7.8265059616657573e-01, 9.0745866975808825e-18, + 6.7623847404278666e-34, -1.7173237731987271e-50), + qd_real( 7.8073722857209449e-01, -9.9198782066678806e-18, + -2.1265794012162715e-36, 3.0772165598957647e-54), + qd_real( 7.7881651238147598e-01, -2.4891385579973807e-17, + 6.7665497024807980e-35, -6.5218594281701332e-52), + qd_real( 7.7688846567323244e-01, 7.7418602570672864e-18, + -5.9986517872157897e-34, 3.0566548232958972e-50), + qd_real( 7.7495310659487393e-01, -5.2209083189826433e-17, + -9.6653593393686612e-34, 3.7027750076562569e-50), + qd_real( 7.7301045336273699e-01, -3.2565907033649772e-17, + 1.3860807251523929e-33, -3.9971329917586022e-50), + qd_real( 7.7106052426181382e-01, -4.4558442347769265e-17, + -2.9863565614083783e-33, -6.8795262083596236e-50), + qd_real( 7.6910333764557959e-01, 5.1546455184564817e-17, + 2.6142829553524292e-33, -1.6199023632773298e-49), + qd_real( 7.6713891193582040e-01, -1.8885903683750782e-17, + -1.3659359331495433e-33, -2.2538834962921934e-50), + qd_real( 7.6516726562245896e-01, -3.2707225612534598e-17, + 1.1177117747079528e-33, -3.7005182280175715e-50), + qd_real( 7.6318841726338127e-01, 2.6314748416750748e-18, + 1.4048039063095910e-34, 8.9601886626630321e-52), + qd_real( 7.6120238548426178e-01, 3.5315510881690551e-17, + 1.2833566381864357e-33, 8.6221435180890613e-50), + qd_real( 7.5920918897838807e-01, -3.8558842175523123e-17, + 2.9720241208332759e-34, -1.2521388928220163e-50), + qd_real( 7.5720884650648457e-01, -1.9909098777335502e-17, + 3.9409283266158482e-34, 2.0744254207802976e-50), + qd_real( 7.5520137689653655e-01, -1.9402238001823017e-17, + -3.7756206444727573e-34, -2.1212242308178287e-50), + qd_real( 7.5318679904361252e-01, -3.7937789838736540e-17, + -6.7009539920231559e-34, -6.7128562115050214e-51), + qd_real( 7.5116513190968637e-01, 4.3499761158645868e-17, + 2.5227718971102212e-33, -6.5969709212757102e-50), + qd_real( 7.4913639452345937e-01, -4.4729078447011889e-17, + -2.4206025249983768e-33, 1.1336681351116422e-49), + qd_real( 7.4710060598018013e-01, 1.1874824875965430e-17, + 2.1992523849833518e-34, 1.1025018564644483e-50), + qd_real( 7.4505778544146595e-01, 1.5078686911877863e-17, + 8.0898987212942471e-34, 8.2677958765323532e-50), + qd_real( 7.4300795213512172e-01, -2.5144629669719265e-17, + 7.1128989512526157e-34, 3.0181629077821220e-50), + qd_real( 7.4095112535495911e-01, -1.4708616952297345e-17, + -4.9550433827142032e-34, 3.1434132533735671e-50), + qd_real( 7.3888732446061511e-01, 3.4324874808225091e-17, + -1.3706639444717610e-33, -3.3520827530718938e-51), + qd_real( 7.3681656887736990e-01, -2.8932468101656295e-17, + -3.4649887126202378e-34, -1.8484474476291476e-50), + qd_real( 7.3473887809596350e-01, -3.4507595976263941e-17, + -2.3718000676666409e-33, -3.9696090387165402e-50), + qd_real( 7.3265427167241282e-01, 1.8918673481573520e-17, + -1.5123719544119886e-33, -9.7922152011625728e-51), + qd_real( 7.3056276922782759e-01, -2.9689959904476928e-17, + -1.1276871244239744e-33, -3.0531520961539007e-50), + qd_real( 7.2846439044822520e-01, 1.1924642323370718e-19, + 5.9001892316611011e-36, 1.2178089069502704e-52), + qd_real( 7.2635915508434601e-01, -3.1917502443460542e-17, + 7.7047912412039396e-34, 4.1455880160182123e-50), + qd_real( 7.2424708295146689e-01, 2.9198471334403004e-17, + 2.3027324968739464e-33, -1.2928820533892183e-51), + qd_real( 7.2212819392921535e-01, -2.3871262053452047e-17, + 1.0636125432862273e-33, -4.4598638837802517e-50), + qd_real( 7.2000250796138165e-01, -2.5689658854462333e-17, + -9.1492566948567925e-34, 4.4403780801267786e-50), + qd_real( 7.1787004505573171e-01, 2.7006476062511453e-17, + -2.2854956580215348e-34, 9.1726903890287867e-51), + qd_real( 7.1573082528381871e-01, -5.1581018476410262e-17, + -1.3736271349300259e-34, -1.2734611344111297e-50), + qd_real( 7.1358486878079364e-01, -4.2342504403133584e-17, + -4.2690366101617268e-34, -2.6352370883066522e-50), + qd_real( 7.1143219574521643e-01, 7.9643298613856813e-18, + 2.9488239510721469e-34, 1.6985236437666356e-50), + qd_real( 7.0927282643886569e-01, -3.7597359110245730e-17, + 1.0613125954645119e-34, 8.9465480185486032e-51), + qd_real( 7.0710678118654757e-01, -4.8336466567264567e-17, + 2.0693376543497068e-33, 2.4677734957341755e-50) +}; + +/* Computes sin(a) and cos(a) using Taylor series. + Assumes |a| <= pi/2048. */ +static void sincos_taylor(const qd_real &a, + qd_real &sin_a, qd_real &cos_a) { + const double thresh = 0.5 * qd_real::_eps * std::abs(to_double(a)); + qd_real p, s, t, x; + + if (a.is_zero()) { + sin_a = 0.0; + cos_a = 1.0; + return; + } + + x = -sqr(a); + s = a; + p = a; + int i = 0; + do { + p *= x; + t = p * inv_fact[i]; + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + sin_a = s; + cos_a = sqrt(1.0 - sqr(s)); +} + +static qd_real sin_taylor(const qd_real &a) { + const double thresh = 0.5 * qd_real::_eps * std::abs(to_double(a)); + qd_real p, s, t, x; + + if (a.is_zero()) { + return 0.0; + } + + x = -sqr(a); + s = a; + p = a; + int i = 0; + do { + p *= x; + t = p * inv_fact[i]; + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + return s; +} + +static qd_real cos_taylor(const qd_real &a) { + const double thresh = 0.5 * qd_real::_eps; + qd_real p, s, t, x; + + if (a.is_zero()) { + return 1.0; + } + + x = -sqr(a); + s = 1.0 + mul_pwr2(x, 0.5); + p = x; + int i = 1; + do { + p *= x; + t = p * inv_fact[i]; + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + return s; +} + +qd_real sin(const qd_real &a) { + + /* Strategy. To compute sin(x), we choose integers a, b so that + + x = s + a * (pi/2) + b * (pi/1024) + + and |s| <= pi/2048. Using a precomputed table of + sin(k pi / 1024) and cos(k pi / 1024), we can compute + sin(x) from sin(s) and cos(s). This greatly increases the + convergence of the sine Taylor series. */ + + if (a.is_zero()) { + return 0.0; + } + + // approximately reduce modulo 2*pi + qd_real z = nint(a / qd_real::_2pi); + qd_real r = a - qd_real::_2pi * z; + + // approximately reduce modulo pi/2 and then modulo pi/1024 + double q = std::floor(r.x[0] / qd_real::_pi2[0] + 0.5); + qd_real t = r - qd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi1024[0] + 0.5); + t -= _pi1024 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + qd_real::error("(qd_real::sin): Cannot reduce modulo pi/2."); + return qd_real::_nan; + } + + if (abs_k > 256) { + qd_real::error("(qd_real::sin): Cannot reduce modulo pi/1024."); + return qd_real::_nan; + } + + if (k == 0) { + switch (j) { + case 0: + return sin_taylor(t); + case 1: + return cos_taylor(t); + case -1: + return -cos_taylor(t); + default: + return -sin_taylor(t); + } + } + + qd_real sin_t, cos_t; + qd_real u = cos_table[abs_k-1]; + qd_real v = sin_table[abs_k-1]; + sincos_taylor(t, sin_t, cos_t); + + if (j == 0) { + if (k > 0) { + r = u * sin_t + v * cos_t; + } else { + r = u * sin_t - v * cos_t; + } + } else if (j == 1) { + if (k > 0) { + r = u * cos_t - v * sin_t; + } else { + r = u * cos_t + v * sin_t; + } + } else if (j == -1) { + if (k > 0) { + r = v * sin_t - u * cos_t; + } else { + r = - u * cos_t - v * sin_t; + } + } else { + if (k > 0) { + r = - u * sin_t - v * cos_t; + } else { + r = v * cos_t - u * sin_t; + } + } + + return r; +} + +qd_real cos(const qd_real &a) { + + if (a.is_zero()) { + return 1.0; + } + + // approximately reduce modulo 2*pi + qd_real z = nint(a / qd_real::_2pi); + qd_real r = a - qd_real::_2pi * z; + + // approximately reduce modulo pi/2 and then modulo pi/1024 + double q = std::floor(r.x[0] / qd_real::_pi2.x[0] + 0.5); + qd_real t = r - qd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi1024.x[0] + 0.5); + t -= _pi1024 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + qd_real::error("(qd_real::cos): Cannot reduce modulo pi/2."); + return qd_real::_nan; + } + + if (abs_k > 256) { + qd_real::error("(qd_real::cos): Cannot reduce modulo pi/1024."); + return qd_real::_nan; + } + + if (k == 0) { + switch (j) { + case 0: + return cos_taylor(t); + case 1: + return -sin_taylor(t); + case -1: + return sin_taylor(t); + default: + return -cos_taylor(t); + } + } + + qd_real sin_t, cos_t; + sincos_taylor(t, sin_t, cos_t); + + qd_real u = cos_table[abs_k-1]; + qd_real v = sin_table[abs_k-1]; + + if (j == 0) { + if (k > 0) { + r = u * cos_t - v * sin_t; + } else { + r = u * cos_t + v * sin_t; + } + } else if (j == 1) { + if (k > 0) { + r = - u * sin_t - v * cos_t; + } else { + r = v * cos_t - u * sin_t; + } + } else if (j == -1) { + if (k > 0) { + r = u * sin_t + v * cos_t; + } else { + r = u * sin_t - v * cos_t; + } + } else { + if (k > 0) { + r = v * sin_t - u * cos_t; + } else { + r = - u * cos_t - v * sin_t; + } + } + + return r; +} + +void sincos(const qd_real &a, qd_real &sin_a, qd_real &cos_a) { + + if (a.is_zero()) { + sin_a = 0.0; + cos_a = 1.0; + return; + } + + // approximately reduce by 2*pi + qd_real z = nint(a / qd_real::_2pi); + qd_real t = a - qd_real::_2pi * z; + + // approximately reduce by pi/2 and then by pi/1024. + double q = std::floor(t.x[0] / qd_real::_pi2.x[0] + 0.5); + t -= qd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi1024.x[0] + 0.5); + t -= _pi1024 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + qd_real::error("(qd_real::sincos): Cannot reduce modulo pi/2."); + cos_a = sin_a = qd_real::_nan; + return; + } + + if (abs_k > 256) { + qd_real::error("(qd_real::sincos): Cannot reduce modulo pi/1024."); + cos_a = sin_a = qd_real::_nan; + return; + } + + qd_real sin_t, cos_t; + sincos_taylor(t, sin_t, cos_t); + + if (k == 0) { + if (j == 0) { + sin_a = sin_t; + cos_a = cos_t; + } else if (j == 1) { + sin_a = cos_t; + cos_a = -sin_t; + } else if (j == -1) { + sin_a = -cos_t; + cos_a = sin_t; + } else { + sin_a = -sin_t; + cos_a = -cos_t; + } + return; + } + + qd_real u = cos_table[abs_k-1]; + qd_real v = sin_table[abs_k-1]; + + if (j == 0) { + if (k > 0) { + sin_a = u * sin_t + v * cos_t; + cos_a = u * cos_t - v * sin_t; + } else { + sin_a = u * sin_t - v * cos_t; + cos_a = u * cos_t + v * sin_t; + } + } else if (j == 1) { + if (k > 0) { + cos_a = - u * sin_t - v * cos_t; + sin_a = u * cos_t - v * sin_t; + } else { + cos_a = v * cos_t - u * sin_t; + sin_a = u * cos_t + v * sin_t; + } + } else if (j == -1) { + if (k > 0) { + cos_a = u * sin_t + v * cos_t; + sin_a = v * sin_t - u * cos_t; + } else { + cos_a = u * sin_t - v * cos_t; + sin_a = - u * cos_t - v * sin_t; + } + } else { + if (k > 0) { + sin_a = - u * sin_t - v * cos_t; + cos_a = v * sin_t - u * cos_t; + } else { + sin_a = v * cos_t - u * sin_t; + cos_a = - u * cos_t - v * sin_t; + } + } +} + +qd_real atan(const qd_real &a) { + return atan2(a, qd_real(1.0)); +} + +qd_real atan2(const qd_real &y, const qd_real &x) { + /* Strategy: Instead of using Taylor series to compute + arctan, we instead use Newton's iteration to solve + the equation + + sin(z) = y/r or cos(z) = x/r + + where r = sqrt(x^2 + y^2). + The iteration is given by + + z' = z + (y - sin(z)) / cos(z) (for equation 1) + z' = z - (x - cos(z)) / sin(z) (for equation 2) + + Here, x and y are normalized so that x^2 + y^2 = 1. + If |x| > |y|, then first iteration is used since the + denominator is larger. Otherwise, the second is used. + */ + + if (x.is_zero()) { + + if (y.is_zero()) { + /* Both x and y is zero. */ + qd_real::error("(qd_real::atan2): Both arguments zero."); + return qd_real::_nan; + } + + return (y.is_positive()) ? qd_real::_pi2 : -qd_real::_pi2; + } else if (y.is_zero()) { + return (x.is_positive()) ? qd_real(0.0) : qd_real::_pi; + } + + if (x == y) { + return (y.is_positive()) ? qd_real::_pi4 : -qd_real::_3pi4; + } + + if (x == -y) { + return (y.is_positive()) ? qd_real::_3pi4 : -qd_real::_pi4; + } + + qd_real r = sqrt(sqr(x) + sqr(y)); + qd_real xx = x / r; + qd_real yy = y / r; + + /* Compute double precision approximation to atan. */ + qd_real z = std::atan2(to_double(y), to_double(x)); + qd_real sin_z, cos_z; + + if (std::abs(xx.x[0]) > std::abs(yy.x[0])) { + /* Use Newton iteration 1. z' = z + (y - sin(z)) / cos(z) */ + sincos(z, sin_z, cos_z); + z += (yy - sin_z) / cos_z; + sincos(z, sin_z, cos_z); + z += (yy - sin_z) / cos_z; + sincos(z, sin_z, cos_z); + z += (yy - sin_z) / cos_z; + } else { + /* Use Newton iteration 2. z' = z - (x - cos(z)) / sin(z) */ + sincos(z, sin_z, cos_z); + z -= (xx - cos_z) / sin_z; + sincos(z, sin_z, cos_z); + z -= (xx - cos_z) / sin_z; + sincos(z, sin_z, cos_z); + z -= (xx - cos_z) / sin_z; + } + + return z; +} + + +qd_real drem(const qd_real &a, const qd_real &b) { + qd_real n = nint(a/b); + return (a - n * b); +} + +qd_real divrem(const qd_real &a, const qd_real &b, qd_real &r) { + qd_real n = nint(a/b); + r = a - n * b; + return n; +} + +qd_real tan(const qd_real &a) { + qd_real s, c; + sincos(a, s, c); + return s/c; +} + +qd_real asin(const qd_real &a) { + qd_real abs_a = abs(a); + + if (abs_a > 1.0) { + qd_real::error("(qd_real::asin): Argument out of domain."); + return qd_real::_nan; + } + + if (abs_a.is_one()) { + return (a.is_positive()) ? qd_real::_pi2 : -qd_real::_pi2; + } + + return atan2(a, sqrt(1.0 - sqr(a))); +} + +qd_real acos(const qd_real &a) { + qd_real abs_a = abs(a); + + if (abs_a > 1.0) { + qd_real::error("(qd_real::acos): Argument out of domain."); + return qd_real::_nan; + } + + if (abs_a.is_one()) { + return (a.is_positive()) ? qd_real(0.0) : qd_real::_pi; + } + + return atan2(sqrt(1.0 - sqr(a)), a); +} + +qd_real sinh(const qd_real &a) { + if (a.is_zero()) { + return 0.0; + } + + if (abs(a) > 0.05) { + qd_real ea = exp(a); + return mul_pwr2(ea - inv(ea), 0.5); + } + + /* Since a is small, using the above formula gives + a lot of cancellation. So use Taylor series. */ + qd_real s = a; + qd_real t = a; + qd_real r = sqr(t); + double m = 1.0; + double thresh = std::abs(to_double(a) * qd_real::_eps); + + do { + m += 2.0; + t *= r; + t /= (m-1) * m; + + s += t; + } while (abs(t) > thresh); + + return s; +} + +qd_real cosh(const qd_real &a) { + if (a.is_zero()) { + return 1.0; + } + + qd_real ea = exp(a); + return mul_pwr2(ea + inv(ea), 0.5); +} + +qd_real tanh(const qd_real &a) { + if (a.is_zero()) { + return 0.0; + } + + if (std::abs(to_double(a)) > 0.05) { + qd_real ea = exp(a); + qd_real inv_ea = inv(ea); + return (ea - inv_ea) / (ea + inv_ea); + } else { + qd_real s, c; + s = sinh(a); + c = sqrt(1.0 + sqr(s)); + return s / c; + } +} + +void sincosh(const qd_real &a, qd_real &s, qd_real &c) { + if (std::abs(to_double(a)) <= 0.05) { + s = sinh(a); + c = sqrt(1.0 + sqr(s)); + } else { + qd_real ea = exp(a); + qd_real inv_ea = inv(ea); + s = mul_pwr2(ea - inv_ea, 0.5); + c = mul_pwr2(ea + inv_ea, 0.5); + } +} + +qd_real asinh(const qd_real &a) { + return log(a + sqrt(sqr(a) + 1.0)); +} + +qd_real acosh(const qd_real &a) { + if (a < 1.0) { + qd_real::error("(qd_real::acosh): Argument out of domain."); + return qd_real::_nan; + } + + return log(a + sqrt(sqr(a) - 1.0)); +} + +qd_real atanh(const qd_real &a) { + if (abs(a) >= 1.0) { + qd_real::error("(qd_real::atanh): Argument out of domain."); + return qd_real::_nan; + } + + return mul_pwr2(log((1.0 + a) / (1.0 - a)), 0.5); +} + +QD_API qd_real fmod(const qd_real &a, const qd_real &b) { + qd_real n = aint(a / b); + return (a - b * n); +} + +QD_API qd_real qdrand() { + static const double m_const = 4.6566128730773926e-10; /* = 2^{-31} */ + double m = m_const; + qd_real r = 0.0; + double d; + + /* Strategy: Generate 31 bits at a time, using lrand48 + random number generator. Shift the bits, and repeat + 7 times. */ + + for (int i = 0; i < 7; i++, m *= m_const) { + d = std::rand() * m; + r += d; + } + + return r; +} + + +/* polyeval(c, n, x) + Evaluates the given n-th degree polynomial at x. + The polynomial is given by the array of (n+1) coefficients. */ +qd_real polyeval(const qd_real *c, int n, const qd_real &x) { + /* Just use Horner's method of polynomial evaluation. */ + qd_real r = c[n]; + + for (int i = n-1; i >= 0; i--) { + r *= x; + r += c[i]; + } + + return r; +} + +/* polyroot(c, n, x0) + Given an n-th degree polynomial, finds a root close to + the given guess x0. Note that this uses simple Newton + iteration scheme, and does not work for multiple roots. */ +QD_API qd_real polyroot(const qd_real *c, int n, + const qd_real &x0, int max_iter, double thresh) { + qd_real x = x0; + qd_real f; + qd_real *d = new qd_real[n]; + bool conv = false; + int i; + double max_c = std::abs(to_double(c[0])); + double v; + + if (thresh == 0.0) thresh = qd_real::_eps; + + /* Compute the coefficients of the derivatives. */ + for (i = 1; i <= n; i++) { + v = std::abs(to_double(c[i])); + if (v > max_c) max_c = v; + d[i-1] = c[i] * static_cast(i); + } + thresh *= max_c; + + /* Newton iteration. */ + for (i = 0; i < max_iter; i++) { + f = polyeval(c, n, x); + + if (abs(f) < thresh) { + conv = true; + break; + } + x -= (f / polyeval(d, n-1, x)); + } + delete [] d; + + if (!conv) { + qd_real::error("(qd_real::polyroot): Failed to converge."); + return qd_real::_nan; + } + + return x; +} + +qd_real qd_real::debug_rand() { + if (std::rand() % 2 == 0) + return qdrand(); + + int expn = 0; + qd_real a = 0.0; + double d; + for (int i = 0; i < 4; i++) { + d = std::ldexp(std::rand() / static_cast(RAND_MAX), -expn); + a += d; + expn = expn + 54 + std::rand() % 200; + } + return a; +} + diff --git a/src/external/PackedCSparse/qd/qd_real.h b/src/external/PackedCSparse/qd/qd_real.h new file mode 100644 index 00000000..1ecfc732 --- /dev/null +++ b/src/external/PackedCSparse/qd/qd_real.h @@ -0,0 +1,296 @@ +/* + * include/qd_real.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + * + * Quad-double precision (>= 212-bit significand) floating point arithmetic + * package, written in ANSI C++, taking full advantage of operator overloading. + * Uses similar techniques as that of David Bailey's double-double package + * and that of Jonathan Shewchuk's adaptive precision floating point + * arithmetic package. See + * + * http://www.nersc.gov/~dhbailey/mpdist/mpdist.html + * http://www.cs.cmu.edu/~quake/robust.html + * + * for more details. + * + * Yozo Hida + */ +#ifndef _QD_QD_REAL_H +#define _QD_QD_REAL_H + +#include +#include +#include +#include "qd_config.h" +#include "dd_real.h" + +struct QD_API qd_real { + double x[4]; /* The Components. */ + + /* Eliminates any zeros in the middle component(s). */ + void zero_elim(); + void zero_elim(double &e); + + void renorm(); + void renorm(double &e); + + void quick_accum(double d, double &e); + void quick_prod_accum(double a, double b, double &e); + + qd_real(double x0, double x1, double x2, double x3); + explicit qd_real(const double *xx); + + static const qd_real _2pi; + static const qd_real _pi; + static const qd_real _3pi4; + static const qd_real _pi2; + static const qd_real _pi4; + static const qd_real _e; + static const qd_real _log2; + static const qd_real _log10; + static const qd_real _nan; + static const qd_real _inf; + + static const double _eps; + static const double _min_normalized; + static const qd_real _max; + static const qd_real _safe_max; + static const int _ndigits; + + qd_real(); + qd_real(const char *s); + qd_real(const dd_real &dd); + qd_real(double d); + qd_real(int i); + + double operator[](int i) const; + double &operator[](int i); + + static void error(const char *msg); + + bool isnan() const; + bool isfinite() const { return QD_ISFINITE(x[0]); } + bool isinf() const { return QD_ISINF(x[0]); } + + static qd_real ieee_add(const qd_real &a, const qd_real &b); + static qd_real sloppy_add(const qd_real &a, const qd_real &b); + + qd_real &operator+=(double a); + qd_real &operator+=(const dd_real &a); + qd_real &operator+=(const qd_real &a); + + qd_real &operator-=(double a); + qd_real &operator-=(const dd_real &a); + qd_real &operator-=(const qd_real &a); + + static qd_real sloppy_mul(const qd_real &a, const qd_real &b); + static qd_real accurate_mul(const qd_real &a, const qd_real &b); + + qd_real &operator*=(double a); + qd_real &operator*=(const dd_real &a); + qd_real &operator*=(const qd_real &a); + + static qd_real sloppy_div(const qd_real &a, const dd_real &b); + static qd_real accurate_div(const qd_real &a, const dd_real &b); + static qd_real sloppy_div(const qd_real &a, const qd_real &b); + static qd_real accurate_div(const qd_real &a, const qd_real &b); + + qd_real &operator/=(double a); + qd_real &operator/=(const dd_real &a); + qd_real &operator/=(const qd_real &a); + + qd_real operator^(int n) const; + + qd_real operator-() const; + + qd_real &operator=(double a); + qd_real &operator=(const dd_real &a); + qd_real &operator=(const char *s); + + bool is_zero() const; + bool is_one() const; + bool is_positive() const; + bool is_negative() const; + + explicit operator bool() const; // new + explicit operator double() const; // new + + static qd_real rand(void); + + void to_digits(char *s, int &expn, int precision = _ndigits) const; + void write(char *s, int len, int precision = _ndigits, + bool showpos = false, bool uppercase = false) const; + std::string to_string(int precision = _ndigits, int width = 0, + std::ios_base::fmtflags fmt = static_cast(0), + bool showpos = false, bool uppercase = false, char fill = ' ') const; + static int read(const char *s, qd_real &a); + + /* Debugging methods */ + void dump(const std::string &name, std::ostream &os = std::cerr) const; + void dump_bits(const std::string &name, + std::ostream &os = std::cerr) const; + + static qd_real debug_rand(); + +}; + +namespace std { + template <> + class numeric_limits : public numeric_limits { + public: + inline static double epsilon() { return qd_real::_eps; } + inline static double min() { return qd_real::_min_normalized; } + inline static qd_real max() { return qd_real::_max; } + inline static qd_real safe_max() { return qd_real::_safe_max; } + static const int digits = 209; + static const int digits10 = 62; + }; +} + +QD_API qd_real polyeval(const qd_real *c, int n, const qd_real &x); +QD_API qd_real polyroot(const qd_real *c, int n, + const qd_real &x0, int max_iter = 64, double thresh = 0.0); + +QD_API qd_real qdrand(void); +QD_API qd_real sqrt(const qd_real &a); + +QD_API inline bool isnan(const qd_real &a) { return a.isnan(); } +QD_API inline bool isfinite(const qd_real &a) { return a.isfinite(); } +QD_API inline bool isinf(const qd_real &a) { return a.isinf(); } + +/* Computes qd * d where d is known to be a power of 2. + This can be done component wise. */ +QD_API qd_real mul_pwr2(const qd_real &qd, double d); + +QD_API qd_real operator+(const qd_real &a, const qd_real &b); +QD_API qd_real operator+(const dd_real &a, const qd_real &b); +QD_API qd_real operator+(const qd_real &a, const dd_real &b); +QD_API qd_real operator+(const qd_real &a, double b); +QD_API qd_real operator+(double a, const qd_real &b); + +QD_API qd_real operator-(const qd_real &a, const qd_real &b); +QD_API qd_real operator-(const dd_real &a, const qd_real &b); +QD_API qd_real operator-(const qd_real &a, const dd_real &b); +QD_API qd_real operator-(const qd_real &a, double b); +QD_API qd_real operator-(double a, const qd_real &b); + +QD_API qd_real operator*(const qd_real &a, const qd_real &b); +QD_API qd_real operator*(const dd_real &a, const qd_real &b); +QD_API qd_real operator*(const qd_real &a, const dd_real &b); +QD_API qd_real operator*(const qd_real &a, double b); +QD_API qd_real operator*(double a, const qd_real &b); + +QD_API qd_real operator/(const qd_real &a, const qd_real &b); +QD_API qd_real operator/(const dd_real &a, const qd_real &b); +QD_API qd_real operator/(const qd_real &a, const dd_real &b); +QD_API qd_real operator/(const qd_real &a, double b); +QD_API qd_real operator/(double a, const qd_real &b); + +QD_API qd_real sqr(const qd_real &a); +QD_API qd_real sqrt(const qd_real &a); +QD_API qd_real pow(const qd_real &a, int n); +QD_API qd_real pow(const qd_real &a, const qd_real &b); +QD_API qd_real npwr(const qd_real &a, int n); + +QD_API qd_real nroot(const qd_real &a, int n); + +QD_API qd_real rem(const qd_real &a, const qd_real &b); +QD_API qd_real drem(const qd_real &a, const qd_real &b); +QD_API qd_real divrem(const qd_real &a, const qd_real &b, qd_real &r); + +dd_real to_dd_real(const qd_real &a); +double to_double(const qd_real &a); +int to_int(const qd_real &a); + +QD_API bool operator==(const qd_real &a, const qd_real &b); +QD_API bool operator==(const qd_real &a, const dd_real &b); +QD_API bool operator==(const dd_real &a, const qd_real &b); +QD_API bool operator==(double a, const qd_real &b); +QD_API bool operator==(const qd_real &a, double b); + +QD_API bool operator<(const qd_real &a, const qd_real &b); +QD_API bool operator<(const qd_real &a, const dd_real &b); +QD_API bool operator<(const dd_real &a, const qd_real &b); +QD_API bool operator<(double a, const qd_real &b); +QD_API bool operator<(const qd_real &a, double b); + +QD_API bool operator>(const qd_real &a, const qd_real &b); +QD_API bool operator>(const qd_real &a, const dd_real &b); +QD_API bool operator>(const dd_real &a, const qd_real &b); +QD_API bool operator>(double a, const qd_real &b); +QD_API bool operator>(const qd_real &a, double b); + +QD_API bool operator<=(const qd_real &a, const qd_real &b); +QD_API bool operator<=(const qd_real &a, const dd_real &b); +QD_API bool operator<=(const dd_real &a, const qd_real &b); +QD_API bool operator<=(double a, const qd_real &b); +QD_API bool operator<=(const qd_real &a, double b); + +QD_API bool operator>=(const qd_real &a, const qd_real &b); +QD_API bool operator>=(const qd_real &a, const dd_real &b); +QD_API bool operator>=(const dd_real &a, const qd_real &b); +QD_API bool operator>=(double a, const qd_real &b); +QD_API bool operator>=(const qd_real &a, double b); + +QD_API bool operator!=(const qd_real &a, const qd_real &b); +QD_API bool operator!=(const qd_real &a, const dd_real &b); +QD_API bool operator!=(const dd_real &a, const qd_real &b); +QD_API bool operator!=(double a, const qd_real &b); +QD_API bool operator!=(const qd_real &a, double b); + +QD_API qd_real fabs(const qd_real &a); +QD_API qd_real abs(const qd_real &a); /* same as fabs */ + +QD_API qd_real ldexp(const qd_real &a, int n); + +QD_API qd_real nint(const qd_real &a); +QD_API qd_real quick_nint(const qd_real &a); +QD_API qd_real floor(const qd_real &a); +QD_API qd_real ceil(const qd_real &a); +QD_API qd_real aint(const qd_real &a); + +QD_API qd_real sin(const qd_real &a); +QD_API qd_real cos(const qd_real &a); +QD_API qd_real tan(const qd_real &a); +QD_API void sincos(const qd_real &a, qd_real &s, qd_real &c); + +QD_API qd_real asin(const qd_real &a); +QD_API qd_real acos(const qd_real &a); +QD_API qd_real atan(const qd_real &a); +QD_API qd_real atan2(const qd_real &y, const qd_real &x); + +QD_API qd_real exp(const qd_real &a); +QD_API qd_real log(const qd_real &a); +QD_API qd_real log10(const qd_real &a); + +QD_API qd_real sinh(const qd_real &a); +QD_API qd_real cosh(const qd_real &a); +QD_API qd_real tanh(const qd_real &a); +QD_API void sincosh(const qd_real &a, qd_real &sin_qd, qd_real &cos_qd); + +QD_API qd_real asinh(const qd_real &a); +QD_API qd_real acosh(const qd_real &a); +QD_API qd_real atanh(const qd_real &a); + +QD_API qd_real qdrand(void); + +QD_API qd_real max(const qd_real &a, const qd_real &b); +QD_API qd_real max(const qd_real &a, const qd_real &b, const qd_real &c); +QD_API qd_real min(const qd_real &a, const qd_real &b); +QD_API qd_real min(const qd_real &a, const qd_real &b, const qd_real &c); + +QD_API qd_real fmod(const qd_real &a, const qd_real &b); + +QD_API std::ostream &operator<<(std::ostream &s, const qd_real &a); +QD_API std::istream &operator>>(std::istream &s, qd_real &a); +#ifdef QD_INLINE +#include "qd_inline.h" +#endif + +#endif /* _QD_QD_REAL_H */ + diff --git a/src/external/PackedCSparse/qd/qd_real.o b/src/external/PackedCSparse/qd/qd_real.o new file mode 100644 index 0000000000000000000000000000000000000000..5a1b530e0efbfadc62f38023b9fd892a1e3dd1f2 GIT binary patch literal 472184 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%0DchhVnyl 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 P7JDV#dL^eI`~Uv|&y!BH literal 0 HcmV?d00001 diff --git a/src/external/PackedCSparse/qd/util.cc b/src/external/PackedCSparse/qd/util.cc new file mode 100644 index 00000000..ab962081 --- /dev/null +++ b/src/external/PackedCSparse/qd/util.cc @@ -0,0 +1,22 @@ +#include +#include "util.h" + +void append_expn(std::string &str, int expn) { + int k; + + str += (expn < 0 ? '-' : '+'); + expn = std::abs(expn); + + if (expn >= 100) { + k = (expn / 100); + str += '0' + k; + expn -= 100*k; + } + + k = (expn / 10); + str += '0' + k; + expn -= 10*k; + + str += '0' + expn; +} + diff --git a/src/external/PackedCSparse/qd/util.h b/src/external/PackedCSparse/qd/util.h new file mode 100644 index 00000000..7de35836 --- /dev/null +++ b/src/external/PackedCSparse/qd/util.h @@ -0,0 +1,4 @@ +#include + +void append_expn(std::string &str, int expn); + diff --git a/src/external/PackedCSparse/qd/util.o b/src/external/PackedCSparse/qd/util.o new file mode 100644 index 0000000000000000000000000000000000000000..9f2092ea458236a0146a8795956863882e7e7a4d GIT binary patch literal 33808 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 z Date: Tue, 27 Feb 2024 22:11:59 +0200 Subject: [PATCH 08/17] Remove unused eta parameter in sample_points --- src/sample_points.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/sample_points.cpp b/src/sample_points.cpp index 5afb99fc..f8a21577 100644 --- a/src/sample_points.cpp +++ b/src/sample_points.cpp @@ -54,7 +54,7 @@ void sample_from_polytope(Polytope &P, int type, RNGType &rng, PointList &randPo unsigned int const& walkL, unsigned int const& numpoints, bool const& gaussian, NT const& a, NT const& L, Point const& c, Point const& StartingPoint, unsigned int const& nburns, - bool const& set_L, NT const& eta, random_walks walk, + bool const& set_L, random_walks walk, NegativeGradientFunctor *F=NULL, NegativeLogprobFunctor *f=NULL, ode_solvers solver_type = no_solver) { @@ -428,7 +428,7 @@ Rcpp::NumericMatrix sample_points(Rcpp::Reference P, Rcpp::Function negative_logprob = Rcpp::as(distribution)["negative_logprob"]; Rcpp::Function negative_logprob_gradient = Rcpp::as(distribution)["negative_logprob_gradient"]; - NT L_, m, eta; + NT L_, m; if (Rcpp::as(distribution).containsElementNamed("L_")) { L_ = Rcpp::as(Rcpp::as(distribution)["L_"]); @@ -447,8 +447,6 @@ Rcpp::NumericMatrix sample_points(Rcpp::Reference P, if (eta <= NT(0)) { throw Rcpp::exception("Step size must be positive"); } - } else { - eta = NT(-1); } if (Rcpp::as(random_walk).containsElementNamed("solver")) { @@ -644,11 +642,11 @@ Rcpp::NumericMatrix sample_points(Rcpp::Reference P, } if (functor_defined) { sample_from_polytope(HP, type, rng, randPoints, walkL, numpoints, gaussian, a, L, c, - StartingPoint, nburns, set_L, eta, walk, F, f, solver); + StartingPoint, nburns, set_L, walk, F, f, solver); } else { sample_from_polytope(HP, type, rng, randPoints, walkL, numpoints, gaussian, a, L, c, - StartingPoint, nburns, set_L, eta, walk, G, g, solver); + StartingPoint, nburns, set_L, walk, G, g, solver); } break; } @@ -670,7 +668,7 @@ Rcpp::NumericMatrix sample_points(Rcpp::Reference P, VP.shift(mode.getCoefficients()); } sample_from_polytope(VP, type, rng, randPoints, walkL, numpoints, gaussian, a, L, c, - StartingPoint, nburns, set_L, eta, walk, F, f, solver); + StartingPoint, nburns, set_L, walk, F, f, solver); break; } case 3: { @@ -691,7 +689,7 @@ Rcpp::NumericMatrix sample_points(Rcpp::Reference P, ZP.shift(mode.getCoefficients()); } sample_from_polytope(ZP, type, rng, randPoints, walkL, numpoints, gaussian, a, L, c, - StartingPoint, nburns, set_L, eta, walk, F, f, solver); + StartingPoint, nburns, set_L, walk, F, f, solver); break; } case 4: { @@ -714,7 +712,7 @@ Rcpp::NumericMatrix sample_points(Rcpp::Reference P, VPcVP.shift(mode.getCoefficients()); } sample_from_polytope(VPcVP, type, rng, randPoints, walkL, numpoints, gaussian, a, L, c, - StartingPoint, nburns, set_L, eta, walk, F, f, solver); + StartingPoint, nburns, set_L, walk, F, f, solver); break; } } From 69fc49ee6f253f17b7677e029209e638620ec62a Mon Sep 17 00:00:00 2001 From: vfisikop Date: Tue, 27 Feb 2024 22:19:48 +0200 Subject: [PATCH 09/17] Add ode_solve example --- src/ode_solve.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ode_solve.cpp b/src/ode_solve.cpp index af20bed4..eb062342 100644 --- a/src/ode_solve.cpp +++ b/src/ode_solve.cpp @@ -75,7 +75,9 @@ void run_ode_solver( //' @return A list which contains elements "x_1", ..., "x_n" representing each derivative results. Each "x_i" corresponds to a d x n matrix where each column represents a certain timestep of the solver. //' //' @examples -//' # Please visit the examples directory on examples demonstrating usage of the ODE solvers. +//' F <- function (x) (-x) +//' initial_conditions <- list("x_1" = c(0), "x_2" = c(1)) +//' states <- ode_solve(dimension=1, n=1000, F=F, initial_time=0, step_size=0.01, order=2, method="leapfrog", initial_conditions=initial_conditions, domains = list()) //' //' @export // [[Rcpp::export]] From 003ccb0df985db6f8cc672a8dde9817effd743b0 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Tue, 27 Feb 2024 22:54:14 +0200 Subject: [PATCH 10/17] Fixing some R examples --- man/examples/data/polytope_e_coli.mat | Bin 0 -> 19249 bytes man/examples/data/samples_hnr_iAB_PLT_283.txt | 24 ++++ man/examples/generalized_hyperbolic.R | 66 ++++++++++ man/examples/metabolic.R | 113 ++++++++++++++++++ man/examples/nuts_rand_poly.R | 55 +++++++++ man/examples/simple_crhmc.R | 49 ++++++++ man/examples/simple_hmc.R | 62 ++++++++++ man/examples/simple_hmc_rand_poly.R | 58 +++++++++ man/examples/simple_ode.R | 31 +++++ man/examples/simple_ode_truncated.R | 34 ++++++ man/examples/sparse_crhmc.R | 98 +++++++++++++++ 11 files changed, 590 insertions(+) create mode 100644 man/examples/data/polytope_e_coli.mat create mode 100644 man/examples/data/samples_hnr_iAB_PLT_283.txt create mode 100644 man/examples/generalized_hyperbolic.R create mode 100644 man/examples/metabolic.R create mode 100644 man/examples/nuts_rand_poly.R create mode 100644 man/examples/simple_crhmc.R create mode 100644 man/examples/simple_hmc.R create mode 100644 man/examples/simple_hmc_rand_poly.R create mode 100644 man/examples/simple_ode.R create mode 100644 man/examples/simple_ode_truncated.R create mode 100644 man/examples/sparse_crhmc.R diff --git a/man/examples/data/polytope_e_coli.mat b/man/examples/data/polytope_e_coli.mat new file mode 100644 index 0000000000000000000000000000000000000000..cc0dee3ef3704839a7fb5dbf0d738bd650d1249d GIT binary patch literal 19249 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 literal 0 HcmV?d00001 diff --git a/man/examples/data/samples_hnr_iAB_PLT_283.txt b/man/examples/data/samples_hnr_iAB_PLT_283.txt new file mode 100644 index 00000000..20ef4363 --- /dev/null +++ b/man/examples/data/samples_hnr_iAB_PLT_283.txt @@ -0,0 +1,24 @@ +-0.561720279923928 -0.980595872206212 -1.42576436471775 -1.38746518258463 -1.40328952255995 -1.32594518129933 -1.17920763919491 -1.13976931175442 -1.44704190760872 -1.80758528855703 +0.105627903577331 -0.0224760977406341 -0.033346419075399 0.0583011517226242 0.128718992901148 0.352155379240605 0.30198127014967 0.346164763337832 0.852750853220236 0.81239896064913 +0.162117647512197 0.61663482915398 0.400878181575701 0.366695795608074 0.278393392324771 0.0730857562461404 0.166434815191386 0.189330647411269 -0.125279993617079 0.126627300269534 +-0.0406942552122779 -0.576900617964391 -0.313842145078441 -0.367136023164517 -0.382100222118444 -0.354039859671709 -0.137353128426152 -0.160383552642979 -0.519155392281931 -0.475450377654029 +-0.40362251190118 -0.286473185314161 -0.0899560295412541 -0.0965770833139645 -0.172949550426655 -0.125412547613036 0.150417355582264 0.114588798098553 0.245979092609726 -0.382835955162689 +0.878246951042944 0.90514099306536 1.23213880865026 1.31832983761677 1.25986701034999 1.39995300408615 1.09368733517267 1.09091168360099 1.08437305769252 1.12958522955154 +0.498400783342133 0.587765513506474 0.253033066193859 0.370366369595461 0.449724976200575 0.365044359880781 0.387907991494453 0.383922539609155 0.188591625404994 -0.412681707489972 +0.221653648829914 0.242994434466489 -0.603741680289462 -0.481123603140553 -0.430823255733856 -0.435302829856887 -0.0456562561348847 -0.0520577661000839 -0.0385949396913763 -0.224243660239918 +-0.265084561759286 0.0918819311680734 0.33493706624183 0.3813425051495 0.403035021178183 0.477504121863839 0.532006724734535 0.545519555275899 0.766384404185368 0.831916014250039 +-0.624759045353021 -0.66962164463356 -1.13959552334022 -1.27820632084631 -1.28015141876924 -1.32693076635632 -0.909064222616852 -0.937550642541083 -0.261982584282485 -0.111118755041501 +-0.0720190591786999 -0.450621402053479 -0.863523249181356 -0.872842112502145 -1.09801753225393 -1.06373666474452 -1.11037218155514 -1.10146557900737 -0.80036781143047 -0.372168139391873 +-0.00857855953622452 0.354989948682959 1.27837817189419 1.2276458819467 1.28672546515841 1.17197787379145 1.16780775465139 1.17376822972155 1.40385625815708 1.62149324528253 +-0.229761733242758 -0.0219462023841505 -0.390687969122839 -0.302494898576672 -0.448911892337499 -0.604566355138878 -0.916877016960797 -0.906457424708054 -1.1126000897522 -1.86130665917621 +-0.589488920208699 -0.721350422835282 -0.198899309272137 -0.181407705029833 -0.118642488162701 -0.168910827232443 -0.0742514161618894 -0.0793110793398157 -0.142750010906859 0.198798486949491 +-0.0284299839046239 0.120862725181517 -0.186276678178437 -0.160980196642537 -0.293834629711641 -0.243113408459892 -0.472778364589375 -0.477458926549254 -0.153555736410121 -0.255890019644184 +-0.10591001233745 -0.362589685776349 0.325478604785015 0.378715486938594 0.26636852369326 0.161411001959766 0.60597685791033 0.610078516181616 0.572140131942936 0.511051653616906 +0.305051871362991 0.118813574717376 -0.527714492647065 -0.536667992026614 -0.580328770509275 -0.649984435188841 -0.710711338095119 -0.658502482012635 -0.902318910541349 -0.764435899578719 +0.536106973675048 1.02057897403551 0.605598476112832 0.50868937957064 0.650724511213557 0.751611789150715 0.897137728286995 0.850360925500868 1.41770846041982 1.7846819452796 +0.0564993149832159 0.463479099414294 0.460736813395207 0.474848735635176 0.426112416628774 0.390581106871229 0.793645778910023 0.806735588287548 0.844742715658839 1.09151995921733 +-0.067723281679949 0.564285422804652 0.788229511696293 0.788617644079093 0.89513355500255 0.876105020354802 0.677547485945377 0.694822145749253 1.17466687016968 0.593832946037088 +0.225364328151606 0.103238565260935 -0.0517032463701438 -0.0662367018241913 -0.0940887051913717 0.0333532293444299 0.210733515664954 0.178213696887622 -0.423217117197478 0.456888013184721 +0.0473026615725538 -0.0456028508287293 -0.177770714303978 -0.139356053949194 0.0277153774848011 -0.0567070095383378 0.00732111889985342 0.0162173446637164 0.110044897825425 -0.195869011808354 +-0.356841619301375 -0.852050102759202 -1.56144734571101 -1.61182361556797 -1.54063437363926 -1.62074713993949 -1.79194559791364 -1.8512488891935 -1.76010649453676 -1.45638353758425 +0.341165476595727 -0.498516965260338 0.0176858700381507 0.0956453734897127 0.0912542311909078 -0.0992749262782443 -0.336170653055667 -0.335003690028184 -0.639956591017614 -0.55941334662177 diff --git a/man/examples/generalized_hyperbolic.R b/man/examples/generalized_hyperbolic.R new file mode 100644 index 00000000..17f4c048 --- /dev/null +++ b/man/examples/generalized_hyperbolic.R @@ -0,0 +1,66 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou + +# Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for sampling from a Generalized Hyperbolic density + +# Import required libraries +library(ggplot2) +library(volesti) +library(numDeriv) +library(GeneralizedHyperbolic) + +A = matrix(c(1, -1), ncol=1, nrow=2, byrow=TRUE) +b = c(4,4) + +f <- function(x) (-log(dghyp(x))) +grad_f <- function(x) (-ddghyp(x)/dghyp(x)) + +x_min = matrix(0, 1, 1) + +# Create domain of truncation +P <- volesti::Hpolytope(A = A, b = b) + +# Smoothness and strong-convexity +L <- estimtate_lipschitz_constant(grad_f, P, 1000) +m <- L + +# Warm start point from truncated Gaussian +warm_start <- sample_points(P, n = 1, random_walk = list("nburns" = 5000), distribution = list("density" = "gaussian", "variance" = 1/L, "mode" = x_min)) + +# Sample points +n_samples <- 10000 +n_burns <- n_samples / 2 + +pts <- sample_points(P, n = n_samples, random_walk = list("walk" = "HMC", "step_size" = 0.5, "nburns" = n_burns, "walk_length" = 1, "solver" = "leapfrog", "starting_point" = warm_start[,1]), distribution = list("density" = "logconcave", "negative_logprob" = f, "negative_logprob_gradient" = grad_f, "L_" = L, "m" = m)) + +# Plot histogram +hist(pts, + probability=TRUE, + breaks = 100, + border="blue", + main="Genrealized Hyperbolic Density with lambda = 1, alpha = 1, beta = 0, delta = 1, mu = 0", + xlab="Samples", + ylab="Density" +) + +cat("Sample mean is: ") +sample_mean <- mean(pts) +cat(sample_mean) +cat("\n") +cat("Sample variance is: ") +sample_variance <- mean((pts - sample_mean)^2) +cat(sample_variance) + +n_ess = min(ess(pts)) +psrf = max(psrf_univariate(pts)) + +cat("\nEffective sample size: ", n_ess, append=TRUE) +cat("\nPSRF: ", psrf, append=TRUE) +cat("\n") diff --git a/man/examples/metabolic.R b/man/examples/metabolic.R new file mode 100644 index 00000000..0eed7b0e --- /dev/null +++ b/man/examples/metabolic.R @@ -0,0 +1,113 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou + +# Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for using the logconcave sampling methods + +# Import required libraries +library(ggplot2) +library(volesti) +library(R.matlab) + +# Sampling from logconcave density example + +# Helper function +norm_vec <- function(x) sqrt(sum(x^2)) + + +# Load polytopes from mat file +root <- rprojroot::find_root_file(criterion = rprojroot::has_file("DESCRIPTION")) +metabolic_polytope_mat <- readMat(paste(root , '/man/examples/data/polytope_e_coli.mat', sep="")) +A <- as.matrix(metabolic_polytope_mat$polytope[[1]]) +b <- as.matrix(metabolic_polytope_mat$polytope[[2]]) +center <- as.matrix(metabolic_polytope_mat$polytope[[3]]) +radius <- as.numeric(metabolic_polytope_mat$polytope[[4]]) +sigma <- 1 +dimension <- dim(A)[2] + + +# Negative log-probability oracle +f <- function(x) (norm_vec(x)^2 / (2 * sigma^2)) + +# Negative log-probability gradient oracle +grad_f <- function(x) (x / sigma^2) + + +# Smoothness and strong-convexity +L <- 1 / sigma^2 +m <- 1 / sigma^2 + +# Center polytope +b_new <- b - A %*% center + +# Create volesti polytope +P <- Hpolytope(A = A, b = c(b_new)) + +# Rounding +#Tr <- rounding(H) + +#P <- Hpolytope$new(A = Tr$Mat[1:nrow(Tr$Mat), 2:ncol(Tr$Mat)], b = Tr$Mat[,1]) + +# Center is origin (after shift) +x_min = matrix(0, dimension, 1) + +# Generate samples with HNR +start_time <- Sys.time() +rdhr_samples <- sample_points(P, n = 10, random_walk = list("walk" = "RDHR", "nburns" = 10, "walk_length" = 1), distribution = list("density" = "gaussian", "variance" = 1/L, "mode" = x_min)) +end_time <- Sys.time() + +# Calculate Effective Sample size +rdhr_ess = ess(rdhr_samples) +min_ess <- min(rdhr_ess) + +# Calculate PSRF +rdhr_psrfs = psrf_univariate(rdhr_samples) +max_psrf = max(rdhr_psrfs) +elapsed_time <- end_time - start_time + +# Print results +cat('Min Effective Sample Size: ') +cat(min_ess) +cat('\n') +cat('Maximum PSRF: ') +cat(max_psrf) +cat('\n') +cat('Time per independent sample: ') +cat(elapsed_time / min_ess) +cat('sec') + +outfile <- paste(root , '/man/examples/data/samples_hnr_iAB_PLT_283.txt', sep="") + +write.table(rdhr_samples, file=outfile, row.names=FALSE, col.names=FALSE) + +start_time <- Sys.time() +hmc_samples <- sample_points(P, n = 10, random_walk = list("walk" = "HMC", "step_size" = 0.07, "nburns" = 10, "walk_length" = 30, "solver" = "leapfrog", "starting_point" = rdhr_samples[, ncol(rdhr_samples)]), distribution = list("density" = "logconcave", "negative_logprob" = f, "negative_logprob_gradient" = grad_f, "L_" = L, "m" = m)) +end_time <- Sys.time() + +# Calculate Effective Sample size +hmc_ess = ess(hmc_samples) +min_ess <- min(hmc_ess) + +# Calculate PSRF +hmc_psrfs = psrf_univariate(hmc_samples) +max_psrf = max(hmc_psrfs) +elapsed_time <- end_time - start_time + +# Print results +cat('HMC\n') +cat('Min Effective Sample Size: ') +cat(min_ess) +cat('\n') +cat('Maximum PSRF: ') +cat(max_psrf) +cat('\n') +cat('Time per independent sample: ') +cat(elapsed_time / min_ess) +cat('sec') +cat('\n') diff --git a/man/examples/nuts_rand_poly.R b/man/examples/nuts_rand_poly.R new file mode 100644 index 00000000..faa8b3d7 --- /dev/null +++ b/man/examples/nuts_rand_poly.R @@ -0,0 +1,55 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou + +# Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for using the logconcave sampling methods + +# Import required libraries +library(ggplot2) +library(volesti) + +# Sampling from logconcave density example + +# Helper function +norm_vec <- function(x) sqrt(sum(x^2)) + +# Negative log-probability oracle +f <- function(x) (norm_vec(x)^2 + sum(x)) + +# Negative log-probability gradient oracle +grad_f <- function(x) (2 * x + 1) + +dimension <- 50 +facets <- 200 + +# Create domain of truncation +H <- gen_rand_hpoly(dimension, facets) + +# Rounding +Tr <- rounding(H, seed = 127) + +P <- Hpolytope(A = Tr$Mat[1:nrow(Tr$Mat), 2:ncol(Tr$Mat)], b = Tr$Mat[,1]) + +x_min = matrix(0, dimension, 1) + +# Warm start point from truncated Gaussian +warm_start <- sample_points(P, n = 1, random_walk = list("nburns" = 5000), distribution = list("density" = "gaussian", "variance" = 1/2, "mode" = x_min)) + +# Sample points +n_samples <- 20000 + +samples <- sample_points(P, n = n_samples, random_walk = list("walk" = "NUTS", "solver" = "leapfrog", "starting_point" = warm_start[,1]), + distribution = list("density" = "logconcave", "negative_logprob" = f, "negative_logprob_gradient" = grad_f)) + +# Plot histogram +hist(samples[1,], probability=TRUE, breaks = 100) + +psrfs <- psrf_univariate(samples) +n_ess <- ess(samples) + diff --git a/man/examples/simple_crhmc.R b/man/examples/simple_crhmc.R new file mode 100644 index 00000000..5fee2b8b --- /dev/null +++ b/man/examples/simple_crhmc.R @@ -0,0 +1,49 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou +# Copyright (c) 2022-2022 Ioannis Iakovidis + +# Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of Code 2022 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for using the logconcave sampling methods + +# Import required libraries +library(volesti) + +# Sampling from uniform density example + +logconcave_sample<- function(P,distribution, n_samples ,n_burns){ + if (distribution == "uniform"){ + f <- function(x) (0) + grad_f <- function(x) (0) + L=1 + m=1 + pts <- sample_points(P, n = n_samples, random_walk = list("walk" = "CRHMC", "nburns" = n_burns, "walk_length" = 1, "solver" = "implicit_midpoint"), distribution = list("density" = "logconcave", "negative_logprob" = f, "negative_logprob_gradient" = grad_f, "L_" = L, "m" = m)) + return(max(psrf_univariate(pts, "interval"))) + } + else if(distribution == "gaussian"){ + pts <- sample_points(P, n = n_samples, random_walk = list("walk" = "CRHMC", "nburns" = n_burns, "walk_length" = 1, "solver" = "implicit_midpoint"), distribution = list("density" = "logconcave", "variance"=8)) + return(max(psrf_univariate(pts, "interval"))) + } +} + +for (i in 1:2) { + + if (i==1) { + distribution = 'gaussian' + cat("Gaussian ") + } else { + distribution = 'uniform' + cat("Uniform ") + } + + P = gen_simplex(10, 'H') + psrf = logconcave_sample(P,distribution,5000,2000) + cat("psrf = ") + cat(psrf) + cat("\n") +} diff --git a/man/examples/simple_hmc.R b/man/examples/simple_hmc.R new file mode 100644 index 00000000..af06b522 --- /dev/null +++ b/man/examples/simple_hmc.R @@ -0,0 +1,62 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou + +# Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for using the logconcave sampling methods + +# Import required libraries +library(ggplot2) +library(volesti) + +# Sampling from logconcave density example + +# Helper function +norm_vec <- function(x) sqrt(sum(x^2)) + +# Negative log-probability oracle +f <- function(x) (norm_vec(x)^2 + sum(x)) + +# Negative log-probability gradient oracle +grad_f <- function(x) (2 * x + 1) + +# Interval [-1, 1] +A = matrix(c(1, -1), ncol=1, nrow=2, byrow=TRUE) +b = c(2,1) + +# Create domain of truncation +P <- volesti::Hpolytope(A = A, b = b) + +# Mode of logconcave density +x_min <- c(-0.5) + +# Smoothness and strong-convexity +L <- 2 +m <- 2 + +# Warm start point from truncated Gaussian +warm_start <- sample_points(P, n = 1, random_walk = list("nburns" = 5000), distribution = list("density" = "gaussian", "variance" = 1/L, "mode" = x_min)) + +# Sample points +n_samples <- 20000 +n_burns <- n_samples / 2 + +pts <- sample_points(P, n = n_samples, random_walk = list("walk" = "HMC", "step_size" = 0.3, "nburns" = n_burns, "walk_length" = 3, "solver" = "leapfrog", "starting_point" = warm_start[,1]), distribution = list("density" = "logconcave", "negative_logprob" = f, "negative_logprob_gradient" = grad_f, "L_" = L, "m" = m)) +# pts <- sample_points(P, n = n_samples, random_walk = list("walk" = "HMC", "step_size" = 0.3, "nburns" = n_burns, "walk_length" = 3, "solver" = "leapfrog", "starting_point" = warm_start[,1]), distribution = list("density" = "logconcave", "mode" = x_min, "variance" = 1)) + +# Plot histogram +hist(pts, probability=TRUE, breaks = 100) + +cat("Sample mean is: ") +sample_mean <- mean(pts) +cat(sample_mean) +cat("\n") +cat("Sample variance is: ") +sample_variance <- mean((pts - sample_mean)^2) +cat(sample_variance) +cat("\n") diff --git a/man/examples/simple_hmc_rand_poly.R b/man/examples/simple_hmc_rand_poly.R new file mode 100644 index 00000000..975062d4 --- /dev/null +++ b/man/examples/simple_hmc_rand_poly.R @@ -0,0 +1,58 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou + +# Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for using the logconcave sampling methods + +# Import required libraries +library(ggplot2) +library(volesti) + +# Sampling from logconcave density example + +# Helper function +norm_vec <- function(x) sqrt(sum(x^2)) + +# Negative log-probability oracle +f <- function(x) (norm_vec(x)^2 + sum(x)) + +# Negative log-probability gradient oracle +grad_f <- function(x) (2 * x + 1) + +dimension <- 50 +facets <- 200 + +# Create domain of truncation +H <- gen_rand_hpoly(dimension, facets) + +# Rounding +Tr <- rounding(H) + +P <- Hpolytope(A = Tr$Mat[1:nrow(Tr$Mat), 2:ncol(Tr$Mat)], b = Tr$Mat[,1]) + +x_min = matrix(0, dimension, 1) + +# Smoothness and strong-convexity +L <- 2 +m <- 2 + +# Warm start point from truncated Gaussian +warm_start <- sample_points(P, n = 1, random_walk = list("nburns" = 5000), distribution = list("density" = "gaussian", "variance" = 1/L, "mode" = x_min)) + +# Sample points +n_samples <- 20000 +n_burns <- n_samples / 2 + +samples <- sample_points(P, n = n_samples, random_walk = list("walk" = "HMC", "step_size" = 0.03, "nburns" = n_burns, "walk_length" = 3, "solver" = "leapfrog", "starting_point" = warm_start[,1]), distribution = list("density" = "logconcave", "negative_logprob" = f, "negative_logprob_gradient" = grad_f, "L_" = L, "m" = m)) + +# Plot histogram +hist(samples[1,], probability=TRUE, breaks = 100) + +psrfs <- psrf_univariate(samples) +n_ess <- ess(samples) \ No newline at end of file diff --git a/man/examples/simple_ode.R b/man/examples/simple_ode.R new file mode 100644 index 00000000..10d0c6d8 --- /dev/null +++ b/man/examples/simple_ode.R @@ -0,0 +1,31 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou + +# Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for ODE solvers +library(volesti) + +F <- function (x) (-x) +order <- 2 +step_size <- 0.01 +n <- 1000 +initial_conditions <- list("x_1" = c(0), "x_2" = c(1)) +initial_time <- 0 + +# Do not impose constraints +domains <- list() + +# Call the ode solver +states <- volesti::ode_solve(dimension=1, n=n, F=F, initial_time=initial_time, step_size=step_size, order=order, method="leapfrog", initial_conditions=initial_conditions, domains = list()) + +x <- states[["x_1"]] +v <- states[["x_2"]] + +plot(x, v) + diff --git a/man/examples/simple_ode_truncated.R b/man/examples/simple_ode_truncated.R new file mode 100644 index 00000000..17470a3c --- /dev/null +++ b/man/examples/simple_ode_truncated.R @@ -0,0 +1,34 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou + +# Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for truncated ODE solvers +library(volesti) + +F <- function (x) (x) +order <- 1 +step_size <- 0.01 +n <- 1000 +initial_conditions <- list("x_1" = c(0.1)) +initial_time <- 0 + +A <- matrix(c(1, -1), ncol=1, nrow=2, byrow=TRUE) +b <- c(1, 0) + +# Create domain of truncation +P_1 <- volesti::Hpolytope(A = A, b = b) +domains <- list("P_1" = P_1) + +# Call the ode solver +states <- ode_solve(dimension=1, n=n, F=F, initial_time=initial_time, step_size=step_size, order=order, method="euler", initial_conditions=initial_conditions, domains = domains) + +x <- states[["x_1"]] +t <- step_size * seq(0, n - 1) + +plot(t, x) diff --git a/man/examples/sparse_crhmc.R b/man/examples/sparse_crhmc.R new file mode 100644 index 00000000..37337198 --- /dev/null +++ b/man/examples/sparse_crhmc.R @@ -0,0 +1,98 @@ +# VolEsti (volume computation and sampling library) + +# Copyright (c) 2012-2020 Vissarion Fisikopoulos +# Copyright (c) 2018-2020 Apostolos Chalkis +# Copyright (c) 2020-2020 Marios Papachristou +# Copyright (c) 2022-2022 Ioannis Iakovidis + +# Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of Code 2022 program. + +# Licensed under GNU LGPL.3, see LICENCE file + +# Example script for using the logconcave sampling methods + +# Import required libraries +library(ggplot2) +library(volesti) + +# Sampling from logconcave density example + +# Helper function +norm_vec <- function(x) sqrt(sum(x^2)) + +# Negative log-probability oracle +f <- function(x) (norm_vec(x)^2 + sum(x)) + +# Negative log-probability gradient oracle +grad_f <- function(x) (2 * x + 1) + +# Interval [-1, 1] +A = matrix(c(1, -1), ncol=1, nrow=2, byrow=TRUE) +b = c(2,1) + +# Create domain of truncation +P <- volesti::Hpolytope$new(A, b) + +# Mode of logconcave density +x_min <- c(-0.5) + +# Smoothness and strong-convexity +L <- 2 +m <- 2 + +# Sample points +n_samples <- 80000 +n_burns <- n_samples / 2 +cat("---Sampling without hessian\n") +pts <- sample_points(P, n = n_samples, random_walk = list("walk" = "CRHMC", "step_size" = 0.3, "nburns" = n_burns, "walk_length" = 1, "solver" = "implicit_midpoint"), distribution = list("density" = "logconcave", "negative_logprob" = f, "negative_logprob_gradient" = grad_f, "L_" = L, "m" = m)) +jpeg("histogram_without_hessian.jpg") +# Plot histogram +hist(pts, probability=TRUE, breaks = 100) + +cat("Sample mean is: ") +sample_mean <- mean(pts) +cat(sample_mean) +cat("\n") +cat("Sample variance is: ") +sample_variance <- mean((pts - sample_mean)^2) +cat(sample_variance) +cat("\n") +invisible(capture.output(dev.off())) + +# Negative log-probability hessian oracle +hess_f <- function(x) (2) +cat("---Sampling with hessian\n") +pts <- sample_points(P, n = n_samples, random_walk = list("walk" = "CRHMC", "step_size" = 0.3, "nburns" = n_burns, "walk_length" = 1, "solver" = "implicit_midpoint"), distribution = list("density" = "logconcave", "negative_logprob" = f, "negative_logprob_gradient" = grad_f,"negative_logprob_hessian" = hess_f, "L_" = L, "m" = m)) +jpeg("histogram_with_hessian.jpg") +# Plot histogram +hist(pts, probability=TRUE, breaks = 100) + +cat("Sample mean is: ") +sample_mean <- mean(pts) +cat(sample_mean) +cat("\n") +cat("Sample variance is: ") +sample_variance <- mean((pts - sample_mean)^2) +cat(sample_variance) +cat("\n") +invisible(capture.output(dev.off())) + +walk="CRHMC" +library(Matrix) +bineq=matrix(c(10,10,10,10,10), nrow=5, ncol=1, byrow=TRUE) +Aineq = matrix(c(1,0,-0.25,-1,2.5,1,0.4,-1,-0.9,0.5), nrow=5, ncol=2, byrow = TRUE) +Aineq = as( Aineq, 'dgCMatrix' ) +beq=matrix(,nrow=0, ncol=1, byrow=TRUE) +Aeq = matrix(, nrow=0, ncol=2, byrow = TRUE) +Aeq=as( Aeq, 'dgCMatrix' ) +lb=-100000*c(1,1); +ub=100000*c(1,1); +cat("---Sampling the normal distribution in a pentagon\n") +P <- volesti::sparse_constraint_problem$new(Aineq, bineq,Aeq, beq, lb, ub) +points <- sample_points(P, n = n_samples, random_walk = list("walk" = "CRHMC", "step_size" = 0.3, "nburns" = n_burns, "walk_length" = 1, "solver" = "implicit_midpoint"), distribution = list("density" = "logconcave", "variance" = 8)) +jpeg("pentagon.jpg") +plot(ggplot(data.frame( x=points[1,], y=points[2,] )) + +geom_point( aes(x=x, y=y, color=walk)) + coord_fixed(xlim = c(-15,15), +ylim = c(-15,15)) + ggtitle(sprintf("Sampling a random pentagon with walk %s", walk))) +invisible(capture.output(dev.off())) +write.table(points, file="pentagon.txt", row.names=FALSE, col.names=FALSE) From 583ac2c334107e8bf959457e7ba3712390a6f976 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Wed, 28 Feb 2024 11:09:54 +0200 Subject: [PATCH 11/17] Remove unused files (object files and outputs of examples) --- man/examples/data/samples_hnr_iAB_PLT_283.txt | 24 ------------------ src/external/PackedCSparse/qd/bits.o | Bin 48136 -> 0 bytes src/external/PackedCSparse/qd/c_dd.o | Bin 91656 -> 0 bytes src/external/PackedCSparse/qd/c_qd.o | Bin 156768 -> 0 bytes src/external/PackedCSparse/qd/dd_const.o | Bin 45776 -> 0 bytes src/external/PackedCSparse/qd/dd_real.o | Bin 279272 -> 0 bytes src/external/PackedCSparse/qd/fpu.o | Bin 15136 -> 0 bytes src/external/PackedCSparse/qd/libqd.a | Bin 1196610 -> 0 bytes src/external/PackedCSparse/qd/qd_const.o | Bin 47400 -> 0 bytes src/external/PackedCSparse/qd/qd_real.o | Bin 472184 -> 0 bytes src/external/PackedCSparse/qd/util.o | Bin 33808 -> 0 bytes 11 files changed, 24 deletions(-) delete mode 100644 man/examples/data/samples_hnr_iAB_PLT_283.txt delete mode 100644 src/external/PackedCSparse/qd/bits.o delete mode 100644 src/external/PackedCSparse/qd/c_dd.o delete mode 100644 src/external/PackedCSparse/qd/c_qd.o delete mode 100644 src/external/PackedCSparse/qd/dd_const.o delete mode 100644 src/external/PackedCSparse/qd/dd_real.o delete mode 100644 src/external/PackedCSparse/qd/fpu.o delete mode 100644 src/external/PackedCSparse/qd/libqd.a delete mode 100644 src/external/PackedCSparse/qd/qd_const.o delete mode 100644 src/external/PackedCSparse/qd/qd_real.o delete mode 100644 src/external/PackedCSparse/qd/util.o diff --git a/man/examples/data/samples_hnr_iAB_PLT_283.txt b/man/examples/data/samples_hnr_iAB_PLT_283.txt deleted file mode 100644 index 20ef4363..00000000 --- a/man/examples/data/samples_hnr_iAB_PLT_283.txt +++ /dev/null @@ -1,24 +0,0 @@ --0.561720279923928 -0.980595872206212 -1.42576436471775 -1.38746518258463 -1.40328952255995 -1.32594518129933 -1.17920763919491 -1.13976931175442 -1.44704190760872 -1.80758528855703 -0.105627903577331 -0.0224760977406341 -0.033346419075399 0.0583011517226242 0.128718992901148 0.352155379240605 0.30198127014967 0.346164763337832 0.852750853220236 0.81239896064913 -0.162117647512197 0.61663482915398 0.400878181575701 0.366695795608074 0.278393392324771 0.0730857562461404 0.166434815191386 0.189330647411269 -0.125279993617079 0.126627300269534 --0.0406942552122779 -0.576900617964391 -0.313842145078441 -0.367136023164517 -0.382100222118444 -0.354039859671709 -0.137353128426152 -0.160383552642979 -0.519155392281931 -0.475450377654029 --0.40362251190118 -0.286473185314161 -0.0899560295412541 -0.0965770833139645 -0.172949550426655 -0.125412547613036 0.150417355582264 0.114588798098553 0.245979092609726 -0.382835955162689 -0.878246951042944 0.90514099306536 1.23213880865026 1.31832983761677 1.25986701034999 1.39995300408615 1.09368733517267 1.09091168360099 1.08437305769252 1.12958522955154 -0.498400783342133 0.587765513506474 0.253033066193859 0.370366369595461 0.449724976200575 0.365044359880781 0.387907991494453 0.383922539609155 0.188591625404994 -0.412681707489972 -0.221653648829914 0.242994434466489 -0.603741680289462 -0.481123603140553 -0.430823255733856 -0.435302829856887 -0.0456562561348847 -0.0520577661000839 -0.0385949396913763 -0.224243660239918 --0.265084561759286 0.0918819311680734 0.33493706624183 0.3813425051495 0.403035021178183 0.477504121863839 0.532006724734535 0.545519555275899 0.766384404185368 0.831916014250039 --0.624759045353021 -0.66962164463356 -1.13959552334022 -1.27820632084631 -1.28015141876924 -1.32693076635632 -0.909064222616852 -0.937550642541083 -0.261982584282485 -0.111118755041501 --0.0720190591786999 -0.450621402053479 -0.863523249181356 -0.872842112502145 -1.09801753225393 -1.06373666474452 -1.11037218155514 -1.10146557900737 -0.80036781143047 -0.372168139391873 --0.00857855953622452 0.354989948682959 1.27837817189419 1.2276458819467 1.28672546515841 1.17197787379145 1.16780775465139 1.17376822972155 1.40385625815708 1.62149324528253 --0.229761733242758 -0.0219462023841505 -0.390687969122839 -0.302494898576672 -0.448911892337499 -0.604566355138878 -0.916877016960797 -0.906457424708054 -1.1126000897522 -1.86130665917621 --0.589488920208699 -0.721350422835282 -0.198899309272137 -0.181407705029833 -0.118642488162701 -0.168910827232443 -0.0742514161618894 -0.0793110793398157 -0.142750010906859 0.198798486949491 --0.0284299839046239 0.120862725181517 -0.186276678178437 -0.160980196642537 -0.293834629711641 -0.243113408459892 -0.472778364589375 -0.477458926549254 -0.153555736410121 -0.255890019644184 --0.10591001233745 -0.362589685776349 0.325478604785015 0.378715486938594 0.26636852369326 0.161411001959766 0.60597685791033 0.610078516181616 0.572140131942936 0.511051653616906 -0.305051871362991 0.118813574717376 -0.527714492647065 -0.536667992026614 -0.580328770509275 -0.649984435188841 -0.710711338095119 -0.658502482012635 -0.902318910541349 -0.764435899578719 -0.536106973675048 1.02057897403551 0.605598476112832 0.50868937957064 0.650724511213557 0.751611789150715 0.897137728286995 0.850360925500868 1.41770846041982 1.7846819452796 -0.0564993149832159 0.463479099414294 0.460736813395207 0.474848735635176 0.426112416628774 0.390581106871229 0.793645778910023 0.806735588287548 0.844742715658839 1.09151995921733 --0.067723281679949 0.564285422804652 0.788229511696293 0.788617644079093 0.89513355500255 0.876105020354802 0.677547485945377 0.694822145749253 1.17466687016968 0.593832946037088 -0.225364328151606 0.103238565260935 -0.0517032463701438 -0.0662367018241913 -0.0940887051913717 0.0333532293444299 0.210733515664954 0.178213696887622 -0.423217117197478 0.456888013184721 -0.0473026615725538 -0.0456028508287293 -0.177770714303978 -0.139356053949194 0.0277153774848011 -0.0567070095383378 0.00732111889985342 0.0162173446637164 0.110044897825425 -0.195869011808354 --0.356841619301375 -0.852050102759202 -1.56144734571101 -1.61182361556797 -1.54063437363926 -1.62074713993949 -1.79194559791364 -1.8512488891935 -1.76010649453676 -1.45638353758425 -0.341165476595727 -0.498516965260338 0.0176858700381507 0.0956453734897127 0.0912542311909078 -0.0992749262782443 -0.336170653055667 -0.335003690028184 -0.639956591017614 -0.55941334662177 diff --git a/src/external/PackedCSparse/qd/bits.o b/src/external/PackedCSparse/qd/bits.o deleted file mode 100644 index 9e82380cd48d53fa8aa361392c2c792257fe3229..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48136 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 diff --git a/src/external/PackedCSparse/qd/c_dd.o b/src/external/PackedCSparse/qd/c_dd.o deleted file mode 100644 index 35d1e5ae98a28d928b4053513f9e3f87a4e59624..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 91656 zcmeFa33wF6)<0a;(@7?KGJ!0FKmvp%K#~br2q3Z(lCTB{5M(k*CJ@L*7B)o@1mq$L zqM)LpqNvvmH{9<96hU#p6&3dl7w&amaru7dR8My&pqKmp-|zXp=lL5wb*j!eb+)Rm zuCAUL&Kq4kMw3$b|D+fytd^fD#D!;B)L~2>Cb~g#|B0v|z(gmC*!?GFys`g-+3y05 z?>{kn|NhTszr6qR8GH7BGh>1i#h(2i%s9CJo7pdcxo`i8=n%jN=i}AY)#;I4FJP@u z6A8(~==wU~14usu>BpgZFJP<_`bfo#hKdNN7y}jmi~T7csGa=+?7zl7oS6Ltd^!Z0 z;lqj9W7<1n#G?G4-PZpPCM?T_863h*21&%^55z=x0^giAB-r5R7{|7Omf zcAEW@t^IsGaf(>xo_;Lvl_J&|kN-h9hEXfu$R7SD ztU8`tuw5a&%_u!~~h<9JBds)U4A1n-gAsACFiDG@?18?Y22!7$* z_Y?$=1xLM(tYRi&x%goA;m?O6nJ@1DbSevjlgbJDwDiS_uYQ1Ti8*}`)!?XS*jPBR zmBGEl!Vu8>nQp&cyTV_r4L_CcflIG7$C3+&LQp@{n!ct z)53;<#X|T~Z(0rfmxOKoRr7-~aG3fZj@-5<^0K|jdnxd}lp$Kb-%wXE4g2rwqV^yG z!BH+yeWefqy~JTeQ`wOt>38|(Y19#NtL--lnfrgl z9pcA6qk1HBNmI$(G!$%T>5*KpVQP;kHJ^U$&0^R@P%Y|-y-T74ty?y>5}B<|Y$XPk z4WHxROsM_-|JU~Y<5RHiH_KmI(#)OFkG=IC`+tmfSnQK_Mz;MRk{Q45OUPy$yR8-LUz?-}iWigVXSZ&Ygl(iHE{(J*1;#}a~X7#aUatmoav`ltfq>UKY+ zG|x|-FIYRYMHQ@v69wz1BB=Fe(Eflqeqb=nt&K@XQ z0{%8~)ug1);m!BM+iiIpOhK-k1vbY{h%1@4j2Nowrs!@D9At162LUeV;=qR zHluI!e!O7)$sPSx)%t_I3Ub$*_cfT1d-wksW9)rZgP#5D``?m+bthOm_7Q4FTh{*h zlJ*l35B`on>?RH_j#t~E6H#-pCraPj8oG>jEpIGxtkWKUkRE!8y7+5sPt_Lfqu_n} z_&bkFU_ZSre)mk({_>x{|1KlN1R*9|D340 z1sgh&M8UeZ%RcpsGER;aB0xCB3ABzoP}hYB2Xq7^0a5{3fI)x}fD%9*pcSwjuo|!d zumx}}U?<>Czyp9ofKLEl1AYMnA~jKfPJkYObihEs2tYBQ2G9&x4pV5n19}270fPXe05brsfb#(B0Gk2V0B#1{4R{2wAMiZjIN*Cg zP>>MufNp?XKndV1z+AvWz}bM+fJ*>70CxZ$0K5!%8}JF>YrrpnK&KGh0n-8Hfcb#4 z0jmL*0Ime=0Neq10I(Nu2=EHv9l)o6Zva8T$QK|TPz0C;@BnH6&4A^AwSY~4ZGf8q zcL5#(JPvpk@E+hdfD@}sCSVX?6kr;_1E>Ks1C|5U0yY7*0d4}^1$YSXIN({pdjLnM z5Xpcu0Am4@0TqB+z;eJQz)gSyfDZvb0>Z)&1E2^n53mAo72rm|oq$7tR{&oCegcGH zRmuen1Iz+k0Js=%3*b?}+kg{*$Os|afC+#Kzy*No0rvxr0KNq{BZWu7Re?B5blJqi8 zk(Gp_N69L}9Y;wY;nYF03O!^6HPQwt;Vh#)X`b}z^VUjzhxBeIfl13PGt1XXo1Li; zC5fq3N|nDoW2v$`d#zN4YnLm*T7$9Bz^xEqCpOAkB+T9-4_J0L%EzQ-`7y9;rZ3|T zWZYi6(NJg6u8MkpuD*qij_aNo7}t2+WjQQP92tOG|PcC+D6Uhz-#goL&ATA|bP#hC?pxSGW&mC%f-$Dmv z=`EA?fmtKn%Ipe#s}5sZ^;>l_l@~CL^x13Mbt}&85VtvSp}tL5W;f{Db!B#gN!x7J z$ci$%OWzn^soNNERe)`@9@LN8Y#r6V&;zSCJF7N3QQH_0psLgdRbxSURKF-dd9X)6 zqN|)g1vS!#;;H~Y!Owc$6nY6dWi`cmRR7#?jw~wLJJgp~qz|Yzm*l?DR|KdazA?EM z1Z*^D7XSMb zr;Hfyx7GF=_AS~&u%jY-jA|UuvjJ}ya?hIF7pcZ^zYPaev~Qc%-X|{4{uO}_1R}Br z0zVBjBU=&pPM|fk?|@~C>;mq z2J&+B#mL?aK|jII+GYxEhe{Re&8AT@qiDCmFBRckRO3wiGw^vtLq`8;a$iC;iuRJ} z$Pwal{MQBT3zF0HeLmjLTGj!SMZBu z=EdL>mYH3_N65?(MN?*&HW{?<$jo=a-y3GWhmbP!Bzjq9sI$$?ijeK07IS;(t)Vp2 zD%GO+_mHC@NXt7R?}cEUeJ^BNs4P3EUUUk_FD;Hl6fc5V6*;_wi>6Z$VhhHC9BZVWpRW(9B{?Afp&+Am>E)1M7H z5*CP1jP-3hcxvFcqmNCEegJ~X)VpC@!!2!F!>&oZRNfGA zD8f{^BI4!Wrl^;cZ zY^eMgLQ3UsNLVTlfNoRyW#pw%PHv_e32dmB*r1M?>Z6=rzQG$~DpJL7>22g@mQ@7&_Zj zZj8Ps8Y=IJK5D7l82vyrR6Y>>3aNZ0`c*^as}NEucS6Ea`2grPmG4HcbV21x*KID- z%Xg#Kx}b8c>n0ae-sIXzEO@ySLQ3WLkg!y)1>L4{m+M7R`J(HDrE-_+2&p{c`i@k7 z=lb4I`8|Y`$|oUVsXPL@P34N1J7b{o&X_l1OfOf&+#LgzcPpA&u2#hCj**k~-7zo6 zz|6}ruaG!a#aAGt%xr;#Wo9?%HZ$+Wtn5I^=x|#H)6BauYdcUfI^5KOlF?x&u_zf3 zGLiuaOXXV7Z7O$lc#%}T*x`hwa#x2Vr1D6I??~l$9lkeIeh(od8IZ749s%8^az)1* zIzr_Q9S?Ohylu6Cy50wpM;PRI3z5UH-m0ddAQ@(r1I;I+hR?X zhdcg2Du3vBc`Q_39=nBDP`M>`8wh-nLc&t{13KGOUKhJB7Ap6}er~C}F7`kyR33=^ zh*W+Q`>~<&V+bjgyCGqzJOH{)<(ILS#zEz!agW5AUVa(7H4ZAb#@!zWmG{T(CKkNh z4I!m+P2AQvOXXJ3Z7TP~{Xi;zhv7qwa_y<9t!1u(R zACGBve*8W0a*lqFlE5@$i8&?&PTM=7*Y-~PI>|};KE+ZO-NsJ$ zcY=ra6KkrUHJNuQ8#_JFNlwupAl76*Yl_;ybh;=JMlMQxqqFJS{fQ5QMv*+0xVkgK zy}R>npi>f_Nc=j{5goFfWvD_@&m_K(h@iL|XK!QDl}WI2Wzvo$(03%=m}DBe3@mG2 zTnUzKUfh>-Fv&r8`GZMEnT{L1el+QAMSMHy{4OMHY-AoxdN#>{eU1Js38O*}LM%)~ z7FI-ZabuU~yI|Mze3y5-z!h~oa#@!fyRd>AyX@{_?2z=`T^>@zhahU(HeCq?K`>7H z5LOeY>ZJVLF59|7`L?dNcQut0&6FpOP5#3!-zpp5c3IigZ+K<}NNtLXic3tzZK!*5Tuag$@qY;_E@sHPDA7;6Pae zhbn=wVqAW^L`wr1T1z2!W*TJ(TF*>KPQlxe zqyThK6d}6A$lOpzPtFRG*foPP3avQArwYVCO))hiazjfuLklQ4;!GDt4vo~{bQzLqidDZ}!O?$v)xWsEIHT+%&6sW&*|C7wP2)Rkr)to0Z`A zhp|+aXUgKC@=_Y5V8Dgeq)BTR9 zsZmP7jE%unP=y%ru`hCpuJf5#Ym5kRww^A@G|S;+KV3#Ax&nvAFH2A&v_Q-b5L&#| z0Tn+wyWnfWH8j`u@0+w4q>=^JORH9&L@r-UskkgH@oMZ1`i z$r)q4OU&GJb7_H4Nm)jktsu{)sgbP~z;i6`>p{bt8=DtMtO%F}5RwlIq)4$oU9Ex1 zf7UqynwrC*)7w;8s9lfSP(VOnU=f-tv{_OFZNs(RMc4A3!O|I`IYV`4n9~^^<%~#n zM(z>Ls1?!~y+U)kR_M-{6;5Y|6;aNPE0UeDE5M9f0dD**=}dT9I6GaSITKevtTVmX z4RR)}z~2%P+*MBw*$yMR9cSky!Ql@5tkss`!vVO$mg}gnjiY;SNVP zhtUX&bVNBEKaIrg#ZYrS)I>WRtD@r^@eW5{B^N`6@Ymr^6z2tu@30UZ)&y?QS9knL z`&rAX-3pIo_|+(5u4m^-;DnYnR>TI655`NV*x(5R7)_*CRKRR5!)VI2j7sRW zRc!Fo=NL_UkJ0pR8I?M*T*d~^h+{OfC!<+I8I?_EG`pVBSt}XM*}}+kC!_McjJ&Tf zs`wNr?xxuA>>#}Kj*U1kmeGo?j8=|hw5EX3y0aKvRLf|?GDa7#V|2+Cj5fZ;Xwz{< zmkMl#Vk0((GP*35(dDU(wq!HfTEOVaQbt!TV6<%$qig=o=(;Bu?RcKijju7f={TdE zKQOwv1J;h%h+FzGx^)zz+e#SSzKqeAw_`YCBfdV&=-Z==zI%>&-+#ufAAe)?)9_9t z`Exj<6RC`TnL#L~q=8ZOaz;znGrH(XM%UlY=)Q*;9XQD7jaM1{>q8)HdF0oOW2C@3 z5FgW9#xNc%`!b#&hcfocV#Y0U7ULDNiSbQxHRA{6X2yr)^^D(^cQO7-KE}9@eu#0J z{u1N<`ccN|`lpOD^dA_z^?=UgN0uJLcz~Y5I7iQ5oU5O~I8QHPoUhMhJV>9*c(C5e z_zZm&%zh^vF55NaB;$z0?9T*qt-5D3@ zS&YZ)!x>M|Co!I=dl*mF>lshcS1~TpuV6e)zk~5~{Q%=q{XNDr^%IO|=}}$CUYVZC z_$+-8<2m|7#vZ+ju~%QpxI*8+xKh8Ku}{CBakajm@m&2C#x?p!jOXj$GG3rNx{{rG z-Nm>;@5Z=M&tlx9k6_%aPhs4mS1?|zH!)tKpU-%yzLD|S`gX?W=yx(+u0O{3JpCEQ zEA*p`&(}X=yh{Ix@dbKtGWoGuPhh-O@5A^)J)iM9eH`QU`b@?b>GK$G&=)hlL|?;r zqrQdlCVeO4&H8S}m+6NXU#`E)c&q*$<16%#6ta7z-j(q-J)7~>`Z&hd=yMov*P9t% zr*B|E(=f z>8*_K*8j%%UVS^``}8{*->*N$_yPSH#t-UmGJZ%u4jdad>=#BGCUmFPC8dlu&SkVI z1P?c{ahLXCw0S6_%SsqsK9A9sa~W;j!sv>fjIP|p=&F|)ZTp7N)#2D>$HrZg%jnu7 zM%R@wx?vHc9TzjYaR;NFpD?;r??wG?@6PDXGa2og%IKa2jP6~{=)Nl%-G2w8-Fq25 z@Cu^`KV|e#r{1LO;XaHW8Nuk$GDeTJF#5+ejQ)8yqrHb2J^m4+Cw^nJKfVuXc`}R9 zfg(l+%NRY?%IN8f86CQr(KCA(J^KQq=RRcg{Ev(dhozCS7m^vhm<<#k7b?dy4wDs( z!{s@Q6D9c*AJ;`va^mAsB&8)ju7{+A#K-kE`t>pTrE$N6XJx17WNeqIlY#|obs04j zIPrOTSaz8dLv<0GoUsK4Vv{p(VdVZNqpTMgWq-gZ=QyL>?-=F%#wb6iFZCOgz-Vw^ zMnleEG;|`PGrf$4&u28^97ZEAW;E(LMq}<_H1-ij1y3*<_bj8LR~Z$5!f5=@j3$Kj zBeRpbFq)i6sC!O6que2k@+J}LIcyH2;dP8goWp43dPbwJVl?_zMq?giH1+_af>#-h z`-oBDw~UG${YhuBi&1F`q23jlj4JaP`6e)`_Ar`T&uHGcjA|}sH2)e#3+`Z4`vRl7 z&l%N=bkf-n%c!w0qlLp5HF+2{FJ;uSiBaoqj21o3Xz?42mi)wMX>bN9JJ-eNye^DZ zWH35^6r+_Tj8;`Lx}b^C>NSkkT*_$eO^hzw&1l^#jQ;jHqxFtVvalhH(IttDHfA!~ zG>*}wXEAzc5u=AUF?!?;T} zMwv`<_iMS=%BTlO^sRQHF#!`GAN`Q_u$KFUR4P~*>xp_qdsNHqu7!p}b0kS7XQ8@V z%e_}dJ*NFb%gxdrA=8RSPZH6N+?%Bc%)sXQ;=DAO@|E_r<`@up4mx)|4E_sR*M(5= zrDjCX?HfP4+qaq{JMw(8(k0*vP3D(Kqet|M+Dlr#N5;$qGiDC^LAqYn@~6qDSF~5P z{Q3M`!aOAqbwqnj%dbC){JQprmcK~SV_3{QQpW{FC8CaMZ)*ALBsG{UqTbTp*77&Y zwt|L%ceHo4{Oz)ilyG6w^q%&BYZ!>%Fq4trM|G1<^OcTdRj()7= zPnS{0v`@7Br{viY;BgM3KGick2~$`9I) zTGlS@BxHW4}4K{(Zl8F8RK|Z%eh#F zFC{HBxns^HD?;>g2I##~g7n@2IZ^u1KHaYc4-emIJ@27PNm?()Q`34go|e{!@$|Gb z#-(X}8P7=T$9QI1f5x-Y(ixYfWiXzdmiYkbo0H~d>`BXFdU@Ia#@@8-M~Pq2H%A{H zz8BRPx^lS?pp9S})ka!eYK~G(8f0pYQ%$;#M86!pP&HqsX8*i_`h*zfq~{OPCz1Z} z*NKxpc!)mP);wdVUd0dTiaTt$?z1&VjMS@b%~7NEc{^a;=5P9#v3d>D-iP`aDnxq0 zIDLM5T49l1dyL#L0~YDUMS7h@Q+>xz&>Py@m^evaXvvZHB7O2Cy~(1fzEeu{7V2w+ zI(3@9sJ*J`rTP-9n^HBSR9|Y*l&YDt^t0RdEt{>M!!!~xLOpAazPvrnQ?8$9=~Fhm z<@ySXrfgJH>MJdplB=rJS6MVQg1%~fHQBHYfBM{c`Wn+ekzO-jztGlPP^=>c4cEei_r$74{XSglA^| zv(M2lw{eKboEXx|g-Bn1uD;bP4XujHa>FsSmYiAw($8P1UqxJNzQ<6CME_M6=+`Jq z-;-*A+lc7D_Co#I_QcF}f77ogw}qQx$+{DTCVKVIodZ&xLD|H~CPp@y$)RS>A!^d% z()xZd7DDIu?QF3Uv<4S zB;TT2#`89Ztl%Z_Zql|Jg&0KbK{VC!E(^MvMX8~dkG#u+cC;sL2^rr`#o(<$sgmq+ zC)IQC6+t^B%Ta$Z_{yNImWar^Dr9Y2B`UPMZ9&`H+B4`^hs-{e{NQVX`dDfW%hv`~ zDa%%ndE0|_vV$hc(0g6b7iP3-0uR1EXo$3ER1<@52nw;$R75+1N^LZj({3CRIw)PE z9y`M(IXDs)?$8CtayN&ac}9m`sN5Ddc4$CU*vLM&oQYQ0Xwm2ITp8mOy>1O~o_TxN zbZzh*VY4(=Pzo85GaKcw!FPsvY{a~~A`@ud8gcImo3HjN%ICYo8YHPz+`N0DI#`p$ zoGF}!n1{(k;NvLo4a;=o97JU}+csyU7<^w?SB({L9(k4xo)tFRLC2QXB`ikVAJ(X; zOpe?g)>n(6K4S0#VdrZ+fXcTA!zMe}H!8&7hr;GKx{@9n?cuP_mOU}}k+2(7K1qQw zc#lRmvNNl26v>lW(Fa|KcFqPAy&enD2md4NCe^c@*vNl|jd8GhDhaHLGcjUM*dil_ zQA@&}Q|GQ9sB_m3)q(3r>cI7wI)(j&&Rx@w%!+u06vRB^FK`HO zLOu24!`pbaj*koAr}x<2cd19GUFy;4ZuRJNk9u^vmmZx)2Rx_Aftki`ehjUiW7p%6 zK!okl_G$yY^!yQ%fGwgZ*rz?N4XldkPFqPT#JDH4{o25}bQ2l(BtG0fa6VTLYENkc z>$v)~c1Ro8$kk`GXSIRNRE?kql>4-S`4RM7vRfNCIHDIS(T;(G@G9Xx1a&BEGF^=0 z``khyhE2r9$dyUB7@rPsj(8^Q9AhpFel~2X!^*?p=fWx+xBJt=2@->!4_hQzgy(tQ z;jpK;)qdb#2z%}fq__RR_j(a`gO|e2nc9XYdcCacd9Q?>x1b&QRXr;2HCGPLJ1Rup z>#jUo>kU`Ft##Bj*lelYX5O2w?Iua&z2zEeYrXB_CrU%|9hb+(d)IY?t@WPkCR^)$ z*DYqt==_1J!Xov=`M1cSiJJGJ%V*JvBJw_R%`-_N?_<{jTkDvs-fYFxu^MXUed4;v zqEk)eed-!!l0@ETu5q^3eAgwm*5|Gfw$^c1k=5!z)qi!ku>%dqzdP*6`@%KN;^uwn zn#SWqg~jjPlq`K_zeCi$IfrcLsD*G!w_53ZRu$sb);*d%{) zU15{_*>#0Y@`US2Q_>iMUtCX`;xSYcdB3`zH2cI*P2~ONI%akhdB3}knSDf|kgj8< zyeO2?^@$~0sG(^ScA#k!)}?EuO*jBen{XhSHsK)YT5J<`qG=NjM$-~53X!gH(v()g z7KIYg?4!ah3L~P~Qw3ZUE?raGbvJZG5T~sULs2Ag+A1=%MM>BAcG^TyG!fe>5=AZ| zw$&kuVx()*N!>dTv3>WBL~Pf+Fjl%Yx64Ff9BA!27si9uu5VF-bS0|218ERNoy<1% z7Db6>n|g}E&eHX%X-gC)p=p{CgG)J?iZ+vU5P zZ6)8sY%6C{rE8hZnVx9coau$8WnQbLMz-+U8VYYM~VrTf)Qka7#JENMpXtFch zEXtFvKGs|kMFXjA`CgPyZJYChq$|*pH?D!fL^L}Y(|-sN&7Q{0KSR2lw(jh3(NH2< z-4*dnBDU{7OuAxh-BtdE6Vd8UHBmHzi0!+Nl&;)%-DCJRQ#6V=ZFLx&(Zp%1$l#2T zE{CN}6pf{}sY4VMP}}S&ipEJ-f4h7kwQcf6)V9eNOINBT&s$zmG@giNCxbYFh-OcN zI8nOdY~A^`Su}}=R(D05Ohl_Y6APzE*S~F3x&%$LpD3J)rrARjO_MISWlI!Ir?w?t zR7!12J|Y0`Rmin)dpu4 z&r`J_1B+`^?Tlf?^HptFLGc1r8$PMHR@Fw#D6W%t^}weqN0k>h$Oou4W?pfl@};1$ zc%iC|YbkD$j}o_VNpXwZPqpzYiWkYJsWxF{@e=tw)h4bgUMi1LZSsZ1XUmVMHgjX~ za`|tnm0eMMp8TF_v#%>YUs*frmg1F?9#cBa*;TxXpN~6v9xT2<*)88wyjuQ7Ju03M zIrpJ5pLTbNrCBCkF|kz!X%iRAKxqwz6glrgcG5E0sV@tjBNMO8`G&fmD|1c| zUSSjOAc`)ONu`pW#bWr$i{@WU54>a*gJ{T#@VVOW)dKU(cff3n0oZ#=NuL< zS}zld)nhLS857_lnNXsLBoss9lSR}9d9h5`bP~Pj5}EL+ZMsa^C=q3ttd3=0-*mI^ zV5o;pWx?lnGZTCTC93i>{Ih z9UOd_Sl3F?HkmY1`D>;w<}tV;a`vGt3Aj$iPraHiO)>Qv#`@H2c|tj-Zf6`NqOO-W z$b|x#(7z zv_@r@pNx4LabgSqE|U&fGmkWh!rNrRe4c~!*c?NJD7;-JtZGlYLnbY4-}g?L@F=I- z>RWV|Oql9mV!I&m@o~)YaFjVN6xneoChU@hcguu#OeLc59+~u3TRZBpzVKd|P}kNe zgK?isy5Uqkitd+5kJ}Ql83ygs>SMB!tDFL)3+A z6+I{u&Qoi?#V&kECcM$k6Yf{^uuQl^ZE8$u<9dBWCfpatCF?D;7=3JtY%< zvuHMFo|Xyc8#h=ej8Qp0BoiK1`{Q<*EP6&J?3H}5(2f(a9Vay}%C7MdbaQz@CZv251on(K;f#cEa}@@$(@vu}{Pspu8@-Ci0^HnoR7+I^K{e zgP3wurVQcAn=<7LuDm5vhDJo;^AGRHl)h~FZJC%I(G8UMWJ*7_{H{#QjmQS&1DVpF zjlM4v2S$tmUwpU9Nbh-R=plilJY0x(-Xm5Dtg zR)ca}c1wt$o9X8=v3JB)Q2s5ub>fZ0zhq)s#I2xwDZ3>`&|LgNCids9quoEAB2%*2=n0vK!zWOFlPT4#`B#~!Z2c}%W=#-Uq11XTmwlzbw z)D~MaOiOLGHN&;kMYd*ymb%Q=jMP$>+nP~Y>PoB0d$wq;S5U-={y34-(xyffp%SC@ ziJ0KhrgzlRT#lGopokp${7PZ0mUa*KW?E64mexV-7gRf5ON+I&3lp@ok8R?ev^1%% zWF?-crTwVd`^e}4vNMQSgPuW=Lm!nX>a6t*3+I2q5tT3V_yWb-CjOS?o7dF!E+rf7YW)mqN4eP~fqYq?@~)6#<5*^FUM zRClcha&xljR4wf(D`s`XPELrTo?6z6@xruWs-dN|Zuh@yU4T9tCqNKAh%t*7c`C8jW4>ouAb_KIl)2*fP3qm%b>`c$n(q7Wkyonm1Wov2o+pSK|(b5Ve=Zyzd6y<7Z z#kSO0Bdu-5A4);}{R43%uUnea+C zPRr<7Sg2)q*m=I27Zzz5K3lU`%ed6m9Is`}vo$AZ8S2F#Z)53`SE6vDmZ8={rEij! zq1H3ioUCQ2B}z4?Xc@;bjaHf`VtF1Xp>z_?>YH}8qX&Kun1l25^t+htsr_1m`t{G=(6M&NDXnG-k zl7nAHCwR2fa?Pt{X3>LtQiZ=#>916w;v0akBKfq;0VFfCTFc}&+9Gwnwm{45;qRy_ z=%^~_*u&p3m1IO}qqb1X9ORc*709a!cp=<{A}|I!~+7+#~6wJ*>G$`sKMI%H$}7E=yA!B7U}R}{xW{^i#U4^Dc zdUudS(ETKVztmcaH^m=sia*{IO5Dsk&E1VF^+rXM1>~WZEbZnHNh$+Llz|E-5|Biq z$ORIR#0dyS0=k$9pr2^Ohq|l;tjWf^k$w3Bkklmf$P!2MT zgAC^&Bdj2UILK6|H`*Sf%ZM?xfkG_yhgeKr(iAABSVfsr1=QUkx>dwrL^)uDhJd;m zgNuN=aX=jqPWue0myDaVD z5NT={)2xYXRB0loaoAlEc3(5>P!7AFHI6j9!xYh)$P;q$K^)pAh<-8UJ?5iTp4yCZ z&Oad?&LOW3@+LoxmN(@kl;zz%qO$wHQL4Ec!69E5xt9@g=VnB_)F1Iuf5b~U zP)uAt>7R*-%N6{1q8jH^hsaP7XYj=Bjd9MfCT<1?+y?)Fp48Q+v)Ag5%%K<`*VcpR)o_y9AZsdqe|0u2*)@SF`i+LBKH?QSN-w6>JRe>R}eT?L|G>q16kVJA%?4Xhx4QziAg)0 zMAd3LoWmW3a7UQocH+oJTH$_+C=%&ixELAZ9qk+h$HzufSiXvL`-7y3)%%JQ}it*hy*l4(C$&m7ChtVvb{sxuhA5!W4HLJQ{O-0DEw68;ys1YmSE7pfeUSeIbB|Ko&7_h14 zfc4_BnPv@G!x{LQiZM_prp0*Yu12eT-X&DlTuJ4E9aJ{nLFK{+sBGFxW%D7FK3cEv zqZUYiHwl%M_6vGG=by8i#@X#IgS@qz-8#;0J!iMU%5GoIZeL`##h%?(BfBXVP;$Ly za=n}?x;DI=W>MC!hSSo|A!bx8!nHrcy7p&S*ZvGn%VMNurkR$0oR(SEwg1dee0sNx z(lRT=yW};rd^C%hzCiLkUM+bduaV|VPH#Vx&*7Q8+?>hj zr=H1YTQfPsKag{FN_)aDl>YfF%Ccxt zm8Jb1qFl|ia%*iUr$maf0pztTO?QZ@+0u)&S6OMVQX<(!ERx|6^DEX#h|K5t`ZtLQ zJD-PRy{y*60&_fa$Td+L?%(xRzd8)hS##wyZ4>D8R%`S1qa>_8_+E3NHqX7voEWQk zI#l7hxdQ$rfA@e@pTR@5+#0G2M_>n} zhH9>y>cDSkVdwrgZM~+yW)7Kp3sZKcvOB;b&ac=)cF$+KTgmSEZ1)PXdp_H}lI-%+ z2iqOSc2`B1Q$&o!=Y-qay-3@j>2J2Po3as)@$NomTKjNXt2SxwG>ztN)uo!dFV{rb zh+B~)S(@z-YgMY(TB%;keqBX=@fRN0uMs>s)|vU36AQmaZlP4KbCz$D{m?AGTIK@z zCSoRxOaTXmBe8zY;+a9zQB2LyLaH~_H?uIo%)&&Kg_*Ww2;Twyxki#w0I+x!dXA^UWtR^NFrlPSZWofQMY*MalvRv83uG~nj z@K;E9=;+@Mz^~eyQ)&(wD9&c0v-~Edf2X0pY`jvR=MYv8fxF1S4W@w-Hn77? zPxS&Am}(lhO8Z!M*bPim1_n9A&Wc@RU?&^6n+)t^1NV@Dn@t1L*}yGU1kb=gscB%F z_Kq&>24*M&gB{}bihIex?QGyaGH^Q^xStH%VH%jp2Jp27<9MvPKgMvDY2a$@Q$4_L zpiCJU;t+RN>?Q+uvw;W5z};-%K{9ZUX<#-RxYsgpF$|n#8n{M#OAoXgn4=7w;Sjqk z9wGy~*}%hOU^g3hgbX}j8t|}z2Q33%z(Bca;9Bi-J;-jrs|*Zvh({|PB?FJLfyc8r(Jj1pQO6(h-HEk{6`|5M% zCBfHuEgs&4c&?NGytVpDI8cXsAvQ1U2F}-OZ_tkF;g%a}BU4tdf*s)yFIGH7zP!l3 zJWam5$POH$U|%vFXy9O9wu1c~wi?Y~cW9sKk(L!vwon-u=@3UMo*@HA*ub-7;0POd zjtuafnuBR#1Fu^K$_w$SEYrY^+S_`R-9U>nFv=m`tazRbyvYU*lYuwczzbyHEi-~v zHt@D(U@r_TG7a3M9oM7n1{Ny=qaEV?iWkYi`)uGPGVnedc$o}*U>aD$20k>$sJa_8 zEHw@6)V|g`+6^pI2F5tVv5Hs7z%e%PDj7J&29A({PfP=6vw=@71FN9n9Mizf+827P z-N15XV5~zNuXv3N9A^WslY!%G;0-eHFVld!jsM#+@F6suXBxOg`%aIu87RF~8;U6= zip^IzqHKj+!n3@5i*G}`??`%s+Iti{B$3kwp=86`UOgd|E?x->6Zc@$1a}8(2BS&)Es`aQd;d7%IfVW)L4%qKANze!*|4@eime@mE00{yGAJ-=X*b zE$I9XsGD%$EC2LiU6DgVxxb^xrZ4yBP>qX)xoE~sh zd^Ea#-dxS13zd%KZ((;1J&p|W8FGZjepr3)G< zqJD$?8Az5EJ4CQ-_?%`_FwZ8QDHp4^gCR1?KT~{zF?N@zOCUssdymW6AovQ9Fg(6O zm1AIJ-{vf<85I&Gd~49&=x6799GjxH)8OJ^_ii95LgYa2za$M}%d-*q+F#bfVDG=> zF;wTj0iq-8Zmpa53;7x9^WzY;K471W@_s4%pe}q7h-s5dK^5fQ9$7*I)*8|UCAc#o zb1DCxA+%sYoYV{gF{XjPS<=G*>^s;g8X(`9Cf(895zKTNP+uRWM?xHf8N_rNejf$9 zSp^p(1DQ@&gYeA*_cA{th8Z;A;JgHMx#IMcI&)}ze0N#&emK6QJlzKplK&54uQl1Z zU&?T1(-Z-FD-i#2R9pTaDn&mzsxA0O@JH|D&iB`wyd9 zBqv>om368dL^pwEI9q>}><%PL$2&y0T+A=M!+GK^=C|JAymX5CZJY{OI>8}avf(S5 z-!5h?`C4L8jp0Rg=`T}+xLS&Fog9e{{RFMEDV-fj4);_1dn0s?@|1Zlt7;;pi=(T< z{g5f}kSU;QqW&5=nLV6{7w?9L@iq_r^~q(#r>}b~`G#CdV3(GBOE+{?=S%S#{0n3F zOOT=FrATY}wdxAR_n-WG$7Mk!^b4MyWJq&w)V51Haub~~#P!$7NvO)wNqBK=c+^eZ zI=-WwL^nR?LjK@UH|5dyNzFyMtrRYhfZ!qp! zlO3X;Z1|DJp`T%nUbOV{CkCebDbqiZ>Hf;}&tzKF+4K#{G`_&(PX?O~66uEN9m;ej znZ8ldsg*39;t;t;)VXSQoSp1A=O)>Uxh0O}$EV^S+2iIQkgfsHKjjz<+ymeS;80i4 zR%`~q2WSGE2e=4;-wgjL=+_|qeCHbehpOmz*ZJquiPNS*zqf93=y%r5#$!p3C-G08 zQ=colZ11Ae&zMu=(=K0h!A}FO>?$1c>8=iPvjqz;t+;>v{gr{gGVoUh{>s2#8Tcy$ ze`VmW4E&XWzcTPw2L8&x|2r9&R?%AD(wbaZ>#d)gyvWzoT+>jW?9NWlOwa7^Ze^xB z-7P9TRaNPgl_FzaL!B>UQB8BRw+WINiyN92G&g!HeHl|0HPrf=TWT_zn<_JWOImzQ z_1@ZyN#4o@zN(QWjozkaU&g{J(cDrcJf50{n);d+QPa@usqi-Y#5ihsW|g!Q544!| zAeDG38X9W7wT<(<)Tr`R5~{9Q;)9wJ&ph7}LN)cu9Mx-m)!eV4l2KF5+<9DSuB`FZ zx71YEu!iP&4U5?_dQiO)4kA*PRM$3mEfz&uUDr}w>z&)|@zt|<8(N)LMPiq$tEm^BaiEMbdLna`OP09Z z0+wnjJI48ts`nbJA#!M|iRtYlMej zKx0id)v~BS4f3@ObJ0TS&J><{FCwU^N9KHu$Rq;t)HgKMd24Hy(ExJL&E9ICs?}H3 z%!TKos>;*k^VSNA2D4&9oJWSBm4xrlsmJzwxO|cX&XU{WEX8L9$h6GFdJ!H`<~H`MMmtT0nM!ym90pL&tn(16YZL-rrW~LXyYDO{_y?oc}r~4ZG^$0S!o+?U&H7c zGgEWyoB_=Xo1h1!otlkyiy~tkvd!zfnr6>3UlUDYqt=F<3t~gPnx<4~!*sjBZER?+ zX{lLci1=G4%hmhldM!DBtBqXcq6*viC!ciHR4%Hr^ZiSRbrEDYdFx4pP#ewpEe#kj zy81^?s+>|&Bg}Riv7E(CbWKjGtf{G}5Zq|PCpBtS9FfB-<>;E4k{0)XHmhhwMJ1_M za=0PUA}aigGpX~BcQ#g`DjL`+xR$8i##VM!Yh5Ge$b^!XJY!vUXW19#ag{Lbwk^ry zDo2-iQH&lvrKCYncVi)MfD=A%-Jh=HShEeWHeTeD8czi!?*Ggc)?ts{omu6pXr1dZ zhRkYM_YPj5>wI;U^O`JTZ3EKTj0N7)B8Z152{kSyRM)V`N4VM7LYc=si4kO|)lgf@ z7_!uCZmcJ))_PQGecY}$dexI^kn2e|up!>kuz*@is+;NvlW#zc6-`X`w?V?aZEk%l z_7sQ`b%nhZxc}5Fg6|7k8(IVh)8wlYwTkGiXlQB?SYDQ3!{Tl6ENrc*T;MTU4b>nu z)KF0?Dw=&>7^7QuLmjq1p6Yri#Z4A>TEQKvn|wYo*Vp2!UnFYDF4+*cIg&&jZoc&> zn_C(dS2l~qY8rt+ZM-HXH*Ch~eo5V$`N~3YEe*8S0^*FeG+~89jMQk3Z$P$&l8tewZ0L(-38bvkb3#uEMaOEOY`9_=LymYm}hKnFe2<9Fd3ewN(>`4YS|Cas3&NIK;!OQ(r`l;T&uun zP*Ke#`P#UY_E%;F41-jQy-PI#7u=%lOm!uP4zlAlj_{HqPnW4 zjte9hRS@dD1ZZLcQL%&zZZ9DMGC&0pYU*pnyrqqp-dt8IR4ZzbwF*^MD^x3L=X&d) zTA^wKgItSMlV%1XkQp<%p%qIz+Ju=w6>u;h7$7P^CB(EWs%J3;k)mF~1rsVjP*w~h zgovh0_$@H56|~g^R8xT#F20JTso@R6iy`unMtzayTuEJ14c z8O`2BJ{t#*m#w({@$(o|GM7c`yiE)6G|Mijy6|8Pr6;QD8Z51N9N+-5gr}jl%F|F? zO}8WuRS>BMdq||IwzaO_TH5fOh|AowsKtx5+26x>isCyVJxMmH2Su#K3)nVQNTJ<8 zZZ?}}z|-s^u;6#4zrMJp3OiRUD@?Yv=w{L6YxdOEG^2~Z#Sf*PMsHOP9;To}cv|aQ zn|)P?3R=gED;|xI81blK$!u(;!DECL89>>n<|7`xn;R=4wk3ILHYpm75pUOv#<{*_ zAg(9_8J8(h-MNa)nB@j!DGFnz@WlvOKlW66oBisz?pMMMe}8VM>zEAT)XCP%Y_I{v zGqo_&a!7Gawam4gQ(V(E=2}*9lULE06{H9hkEhkhg&!*lTP2cVo~mritSC9QKs2*r zCWWY0R?L>E#hw@Dnz?A(Qig1)vDh}}Y{{|sMs_TM<)uA8#8-J~TT>~jt!%)q8y5oB zXD^Pq6k=jcv$wXswGL0)>e#WSxw0P59%%CeD)8JUZ{s{*EJe8c*EI9;Nf`H6Oha6< zI1mNqvqIn{sv|BH?tn{#4iUkqz^Us3p_b{fo@zXs^XM5B163j_dIl2Jm9-5xcdYhd z4X9QRMb*`{t=P5F;*LcfB~&m*iDMdU`HZEV7JRkrgG2XgDp>+^)L8V5HQ$eXz6FAA zimbB5hpW0;J#e!zDrjv-AUKl~h8ea(1+!XlmWS;RJ@PgA>b+R|nta$w;4#{7Q`s^~ zAKyA*aS`3tR910vXv!L53boWV(tRAOX(Ke_Ce2@0LG4(B~R+{|S zViSs)q223Oalh%>oB7blTKdJH0n<&jeCH4Ol_mB4XK?H zJhx_vW;au-EZv5>fuyIZrn=g4i(25;)mXyhNLva;RaetiU)>+Lo+cXI0uNy-4{Wiq zseul4jYLpOG?GS}2DVUZx6mXOy2V1$Q?;nAZ~i1@wBwPEszr7ko>_b{Xmy8#H9B;X z=pP(DPo%@eHnIcopxlO#UEjFaCQ>7)4W&8tO$`k#mM9oRFwAAwV>!j~Bq5NvX~_y4 zy7K{hNekIS6dt_In3av~woRj;CXwlJ-qsnm`ANPFX#k!D+YoZ-p)s=!344RawghZM z+Y;~`-?99x!TdFbE+WC-M`noV&V2|NVi-1)yh&CK(R=SLQD zLe)gO_f5hxoKL5qAA7u9lWV-H8mCT02O4xAjz=>+b$%s2oZw*M;D%}IhgH@%W2D-D zrFT-%A{@rlH@A2eHPqm_)lmNDI_NO8jeCx4!6Pae=lgpNe%l|rTmXMC#pAG!_5 zYxdRpDqC=_k0-?X7F<6*dMw53Ar7FawUTz&hSJlLwdYX1Cgk^qW@gRzgGh&UIOopx zcvz&(fK`rm=h16Yn~#l4$r)OF+tVr3(@>a+j))d_PaLg7QON+-Wspu!Nfvp9CA!gz z7aDj_r{vKawPt!&r%C$9-2bTo+T;AO0UP%;4Nw}|M8F9-*>xhHo=QAf;eB=OV((JC ze_q(?tyPJzQ~nD@^$qGNj3QHt>=Z2j_SrJzR8y>9GVKwElFk3Yl9NqGBrvQ_N=8D{ zRMK?v`<7f|)TdNV!TUMY;P=WN+NhnnPnK^XW^pAgoxX*CAX3}n7pdi2@hnN$hubvOsd7W|a? z$)-yK@6%X}>Zca-rxB|jzvVnRTz3{;snL*fVv!tvg=Z4z;rvfh?slWMUxYGCFX;U3 zQ@iAL`xcraWKWHkNji0}0eE=AyrD;^Q|2QN^VmZ&6o64T=8Nr96~Z%>ThLL`QrrB; zFZ!k+Q3KRFLF47!xT&70nV!)ld7eLeU00IrDar9nb$dpSHaFBmhWzRK!9V<@2hKEc z^QYIH6!QP%V&gj(o|bU~3bUf5PxB31^yZn3yANwv)7# z^muR|_u%D(2Pgm3>%Waur4b*%Yid|J8v2k^)qqOl!p*I!qPN4iYchtNTHDhZ-A??E zr9;4*`v*LlwRm6anOoaXflrJr!ivwQ|MUq#`qUDCRFEpvwztq$Jo*R{OQT{F;+82y zpCe)+RBS@rGKJ`UISbLtdCMHPOd-Cju@r9u{Z8QHHij2?aAr2WQsxRj5vv+M0;?)+ zM~0kPQ!k*ItzJ=+6L?Nla{R!ms(4UU6+D5d3O$i=M?BW43OxoIHIqq?nFb9tya*=) zkejD!kTYuZaA*ixZ1qZ;`;a0-0DM!wsv8VG7E~70K_Ay`ReUB$+29WuFqbwpeqZo8 z6l)BK;h{O8xb$JcvBeWd3@^rX#ITR^P>y zs}-(MN{D+7OJ;I9n)w;4Fi5Bc-2M?kk+HbuxKQ8F2Da9O0L$Z+vQfAf_9lf%QZ0BxW%aVvC-&f zTj?al#h#;RG2R6(?ya0|I&0Z)0XsfF*;pSrGMLh9$?0 zc7>&Y21-3BFpZn6fNR{%sfGnQ>O79^-k`ds zNch9|y|*K*jrT|=g?{S( zZ{gqVBkuY(3Zj()BZf$d!5z>&&~c?y(%+mrBOtg4v`&tT&aJj0cXUymR&l2|_U%7)>8`r0;VzNvYh<9~GsD*#&b3+G z$O`=yY5sIPd03FvUnK$wD+_S>NAK8jdt`l;#a0#B5{XPiI$kymKKTE%cK>lz)%PF3 zFI+B>QBgUiVueOYrihA0j`~t!qLT4VGcxM(Tkg8T4X#QPjf{$wtGlwo=8P6CE541& z6*IQTT#<8)%CGWW)~H;vvTszZsI0!9_c^ck_1u@R{n7V4lylDWywB(JIiK_Mo}X5R zrcO&kM=Y>rjvt7cf2HS`OHAITE=^7m38&i$W1+3J6aKza66=0x&HPyRbJ1p9x@4qD znt@96j2d;4C(f*6qhc4Bc7Z1<_DYXyuP1KQNeettH+fu_*b`kGS71V9U5kr#k6Y() zU+Ia8GlOH@cim7C>;Bla*^Vo;R$_bcNGdQDb*#H3T9%@t-S4cC#VXeQKy-n<4*YVB zV;yksa@j?C!cJ&ORIK~AH^>+##=0MjsW7>R>@|IFecXwjs0ALg_q4Vf`OczEa~ADJ zDcX%uDB65yT&6Sb>$N8CR*lQiNr;biPb}~pWtR4-CUoT+c=IYS*;?HeWlyZV+`FH@ z!Oqi~*icMJs@h`~>e@8VD4*nMATQSaU#lxbUx;zC%!6&KE9`XdjQZ5tmS9V^Eu5|y z?Q^%8g_7DfJGQ6Z6vr9;TW9o5F_F>T;pi5-+JAF{vp(N&IV-eJD)d3E(8nSodm|#> zl*nIeZ$(7zk;pcUd>|t7y@<#jiF{Hc?~jP=kBBr|1~aoX@}7vuk0K&plE_Yt zyfY&5vxvwpiF`pLZ;OZ=iimtpB45(TniHLkgB6n%zcWdpM_r#mEV!l~RHk%o0 zR?AnTvK;GWyNvUCRGU4wzQj0opPh-{I_ zcQx`K5s@1sBJY>T0~)zMB64#?fsRth7hV zW<+XioxKVSMs2?G0ka`{Kvtrku3Z=F{xYiDRG1m*l@Uk!+BwoX8L1(<+jGpQxc_q0 z{x@q(32ruT8@4u4FRhKslAKMh$C)j2lo{u%HF3#iW9z;(`cujL%QXe21koWoe*4-u zS>+}E%vksHQD)fZYxa0fb>9}97320K#kz}3K!W=vv%x;j-89Dh=8#0Xi?_v0^Zd{( z{GP0s>7LWvKVKU(TcMBZ*O}v_te6b5=XSpkn`SmA$HY$Y#9iq*{w5QDvb%Hb9?wbc zF>R8x`yms3p8LcCll)lO*Js7d@I=R&BZf3HL)S`9rqs8aDVy$zc0aOKYSLuAu)r?Y z2WCF}>#_X|`ggS~cTx zKU1G(Hm_&7ce*!w(xtM5HcRSoMt&-EOf0L65^0=0I z+-ApacG1yM@(#jTjsxx!V$9BFlzXQuIcAp3x@T_KA3fFteebtjOj%J|Go{2=H6<-= zir-fmH&q)2}M%H@-%W?03u-U2eke*4Fih)6Be5rwzX{(mT={q(Js(7()UirlwT z`@L%Ah-t8E+ODSmhe9NENa6A|&VftvG=`OXNO*Xeo$Q?eW-|97OQ$TZn zqV@Ed>9b~=Y1@yZY1lN=5%GYJ={H3*NliBYt4yDnT0$uFz!6GkRkP8YIEit6^4)MT zZu^`|#GtW<&#XlFv6kbclw-*2)>(}sFTw7!JjOb|CX#DA#KKV3r221jlYev%O^^PJzp_j>4e}qr(ZMUNqh_3I_Ga} ziOEmS;c_!)1b@^_r=&B0bhI8;!_!T9rP1kagcrdz-;d4s!{z&?8CTNR^WPf(<_Pic zBi|~uhAmGD;(I9ms1f4j+tN~=Yo>QN zFV}iF&Wvc9Gwhb@4R{Eydv00JhD+xYED!C7*XxDBdtm1zozqr!=3Rb znfW4(pEZK}9Bw8(9Dkj|rBl-hIb72J8ujpojBFY)^Yx2x{1*;)mh%kkpCn$6 zku?3e4tK`aI$VxHH2yw^JL6w;c%~!%fWw{fCt(4Q^2~C?pX+dEe5u2;9Pzh0+!_D0 z!{uWYTK>HbcgDN1f0FWKJK`re+!?>f;TJjLS2^4n|CGaX9PxV`?u>V1|0LzP*bzU$ z;m-I)4$pPOuX4CE{vn6Ycf`N!aA*8y4wr3=*5_F4x1^lT_}LC$=!jqDaA*A84wr44 zrvI$No$=A}Cdf3BulZ(8I6lMS&iHi>mt%iTf2+ft@oza?_7NH%h5Z~)?*$GoaKsll z+?oE3@QEf?j-fRDO^*1h9R8#uzR=;%IO1iWsOj`M{2GUU>2R;Zqp-h`=_+#g4;)_X z@M#V&arn6ocTVp@hda|*;_y;OI+bwQuF5K+({+y{zQW;8V?QVLd9A}`dZdwbWYRSq z%wB8x@jG09=Se!t9j^ONNvF!;XP9njB%f;YU(*+p#_SI5do`>+++hMkk*y(m20&Ib z>BeTt^#g0DorWGe2x-0&FJ;#F2{O<$;<6vs={!f!G~!Z)>N=kIiRQmHI$T}T)qT7A z6pWx{QxCus(N+(lU++^=Ujt93_*>AQLVi2?Q_1f~|0MFw=uanq9Q_&OPoZD-WzuLl zpR;>ivJa9*eY@T3%0^rLRrKeO??Hbq`CI7EBYz+L`Q(G>UrcT-c*bN~B#oBmSXm%V zBij&Z)Dz)FmLB%;@G|nr@JjMoa6kDxxGoo(?-IB!7wTp3I!dPwUQfOr-ay_0ZzO*L z-bDTkd_DOPJVfr14T5Pl*sXAVj)gaqpA6qbo(yjx&w#g*p9kMUem=a7d^WtDJQu!| z{1SKvc|N?8T+VYz(`EfVy!>1Z-$8CZduBD=ms2-o#q*CXr4kco}K z@~a-}wiAdWKL)P%uxtDoa6O-^F6T9*8BOV!PeNObZdWwke7VEypbyxxni<$NBVN%1@3S>$iQv&r>5ZVve%;&aJkV(fazBbW0~ z(&Uqmhc70d4lf`-A1>#eq|xcs-_J$lixIEeV~sC^%lSrUI@NGLxw)~yY69f{tgKr?83U4O=5quN*CGZyVtKqHW z%ivqcuZOphuYtFd>-mMPggESg{0$k7eshVOpL&@Ft3X8@`_W@9+@$NAL~g|AaS_AA)ZpkHdMu7V-pmEBR>n7V=ZzZRDrJ z+sRYlTgg-59pqEso#Zp%UF2Et9pv-j-Q<_TcablK_mE!+-%VZ&??;~Fc?;~Fg z-%qaBKl{mVMf^eX_3#1mpTh^qo8d#`o8gDZAA#%fzAop#fydx@Q~eou9C;@^p8REa z0{LIy`mCd-|KIRLir)uMB7YB_O#UG}h5S=^D*4xN{avEviIxu2q*Hu6JcE1;T%XU> zbWVe3QG5zKn|vZXhg`33=aSDvd>;7)@O<*Q@Wtdy;pWrfRwKP~ey`HN28M1oxBQ0}qhj4-b+*0Iwr|7+z2Q7`%b}33wy<)9@zpXW{F~JK-Vn?eGoc zdL5;i{LhHrME(Z6g?ul(mHaLE7V>xDZRGF6+sQwGZzca2-a)R{fjY^D5Z^@}b&S3I z?I1rI-c5cyd>8o`cn|px;k(JtfcKJ5gzqDt3hyI77rvkT0(d|99QZ-<`S1bqeE1;w z68I3g7k-G`2e&Rnm@1dqA?tfBT+b`0uY$+ncd&XrJf8eUcmnw?@X_S!;fdsT!IQ}E zgC~>!5}rc-5ImLqQTQbCc6d6uUSG^0??iki`F3~~c{e1^2{55zk`5t&4`I~S( zPowke9k`ySQ9l4TPncMZ`ayUh`KRzA@*#K``M2;&a!iRw&uJ5K$PlLzdyqfwf zcszLyJc0aD_-OK_@I>-rcoMk}o=konJcWEEJe9m2K8buCJe|A=uJ7W|dbkswN%8l> z_5Lo6-we;D_($M5k{B3v<`F?mA`G@dI z@=xJ@@~_|l@@U-85hRa;*O4C&uO~kh-avjPypenoyop@zYgtcz9^ymf+3*eIm%y9J zm%=xZ>-{b*PlfL%&xZGt=fMw>FM$t`7s3b0m%)d~tKo;p zuZQcSQ@TA_2amz^0rflJapW7}@#K%d^|=mB=eO|D6u%vwNd5{uiTp3{Wb$5k3i&(m zRPuiKB=V2p>ExfoGswSzXOg=U?Bm-k@_2YQ`48ba!s>{gX{HP^#kyDiXVU{kPpE}lk3f- ziR1~m$ux<4EIgU~GAe- z@~h$N$(O@JF7om49`Y&h-Q?%Pd&w8T z_mN)-?<4oY_mi)J_mell50c*pA0XcXA0&SkK1ALPKSaJ4t`7p}_TfEv4DLHn{|FvO z{slaq{2O=z`B8X4VKn)P@I>-c;7R1;;mPC^;VIcmw(0;Em)5;7#No!Pk?21`m;c3Ew~- zHOAiVHIttJ-$b4WZy`So-b#KJd<*#`cpLe2csqF}d@Ff2yn}o$ypwz(yo-Dhd>^7o+Rf-{5-RnR-7wp5i}&>-}gNua9)={b=f=@ThkprE>~A ziF_P9nfx5M-lwMNXTkM8HT4|$BuZx?T;Fq{@mIhzD82}uN$!K|eQlaf4LqCTSHpA2 z8{xU+KZECy-wV$te*muc$7y-CzzZn;Nq8aoHh2;Fi|{h?Kf^1@_rm?;eeeMJKj1;~ zPvCXrL-2a?QHl2Uwt@V3cq91<@Fw!n@b%=W@DO=Ad;|G3cr$qhd=q&lyoG!gyp{ZX z_!jd2fVYwVC%m2fD)?6NmGBPoMtCRr26z|weefOR_rtr%AA|2Ae;(dL-VNVP{#SS} z`3LZQwS^BJt>1HQ2a{xXz~VlB6$d&MBV~VCVvX9_gBhbImPftcq+yJ89s^p zZMfcdsp)(S&!G5dJn)@KeiS^5{8V^0`8aqE`4o6Ac?LX>{9<@M`8@by@;rC}`4V^` z`BHchxgTCeejU7${3f`c{9br~{1JGNd>g!u{7>+D@_q0I@{iz+?t_mee+ZsP z{scUU{4=<|XHCn0JYHRyLh*~?spJ9pB=RTV>EtiMGsq9YGs)wV?fS_gp99Y(zZ{-J z-T==f{{uXa{6)ClFRb;m6TX<@KY|yK55fz{zl9f(pEk~AHD%=E;FaVT!u{kq@BsOb z;X(2mcpdrO@Otw5;SJ<}fH#uA2yY_)JA6I)2k;Pi{CK;5HjtkHZzi7(-$Z^DyoJ0H z-b#KQd<%Iqyp6mS-cJ51d@K21;T`0k!#l~pf_IT8onhC{4)PRuH~CEXF7gZDJ>eTWdcU^DC&LpceiD2%`E+<9c_BQBd<|Uh@78>8gr`vaMtCau zf5Rt{e+5q`&pykp2Yqjt=9>r4r1%g#i~JYxZ1P9pIppubbIH${V3#M4{5*I*`CRy7 z@~hzm3d~BflH2_pNKa?S=a({#|&0{2%Zj`5?TG{A;-0=dSr4n`+lv z1I3SpHveyp{ZJ_!ja_@HXUYB9$T!0EIRlM< z2(Ir1R(}FMn$qcjCz5Z6Cz0=hCzJ1mr;xu3PbL2dK8gHGcshCPL~Q@b6X2QTN$@Q4 z3Gi(4bhti0G0J2ozsqL9b16Oto=3h2o=;u^UrhdExW0dK6!N_hUP$pb!HdXmftQgt z!7Ischx^IzfCtE%;X(3E@Hl(+xS~uOA(r4aO3R}WBQ2c94g1j;mW;NfBk`IRHaFq; zG{mJ-d_MX!$rqzPoBTTT=aPreujy%dHoyxg{z>!~k@uj#l6*J%1LVEvuOt5o{SD*^ zQZUmr*{yK>jE3uUX!)nYn<+jY-a@_@zJ>fccsqFr-a)vx*9)fQn*XRG+$)7=d2l>nJE^>X&yqo+>#P^UV#Mt%LOP&tzBcBEDCoh5z zkgtRfk>3i}^+N{BtZj$KVLq$B1WzE}3r{3>dF<&*CO;aUNzU@e2(IgsdO19s z($VLva>;*>_D^NA80+kn3{-P2?{jK19A3-c0T~ z$}VRM`55>X@(J*E@_cv)c?G#A%6z(z2q;$`^fb^&VKUPID2{r$j8Em z$g|dW8>SdZ29zO+R0Un4%5{8@M^x!(VjPX0OKGs%y_0aiA7Iy{&B zVt79J74QP`26z$qz3@u%7I=VM-*;3;uGjk;$UjFqP2@+7vgdDzJRROlelfg-{0jIM z@&n>-#YhDgN)sH<|oHcq(};epl;$Pt(taXHxuC@NDvOcrN+1@O<)H z;05G*K1KJdS|0r!UPaFiTqJ`i2OBpGx<007IF`s=iNd+ z3f@jW8s0&E0lbTRHoTi$pUdtc*XP!I$%~LqAGvvw^Y$!|k^D!G0yDBa=MG}s$H{FU>3et8b-C2z ze|0^+9i;U2xHL`zO{4L8{Fy|q$C=sWdfZq>uE&8*_wS=IKh<@Aok_0y z-$HWTpXzpA)6xB%z86Sc_eZ*&RM-8HzQ;ygx1YKlQ`hZh3br%qx}D4<*X>>&xo+Qz z$aOmwbhvXn_6vvWwndg#U9Pn}y5IbrBOX5qB-}OL`fEQ&k4Hf)Ig9*4^iL#jM1LCj zF7!_$e--^R$=^i(EOHOl&kM*?;TMtX`L~P7???Q6^3ULl$!AFgn&wKo<&wIW`nd>x z4fz6iDY<@EqLREE@yp4p;Wgx|;j75k!RyKOI}taMZ$SJl-oCBlNTcX1M+hC$K>*TN@)h|R(LtT3H~Md z?eK5O_5Gpp{X}WB{0|{Mj{I@Be$Pka^*x>^QhX=k$CAGYPa@avT#O@s4e@7@?}ML1 zuJ6rEC)fBH}M*XQv6p!jd$IpoJ;Kd0Xv(saha7gD@_M`IEBM8p@6&xBu1 zelfg+d-Jd7e;wjiQoL?&8{pEys%xeH34ULSOIq4ocg$I(@l}Pc zQm^?meQ9;zdRJOaxxYBgSLF+Oi)(6%uXlNuE(%Va>GcLHYpPdyONvcgacyv=JxZBx zr7Nwhykx~PZ*fUUP5DX_KC`T>rnst1I?Md|(ptD+QE*0S@cKacywW*Sr+Y8)uJVKPUO6}6S<3ugMNYfWCYjI9dNvjPO*92D>T2@nSTA1I#ZmjSehZT;IakhnuV*Tyx0<%`UshR z1el^nL}W;5B4j#4hLkQcgkgr{Tp1B#mKBm2k`)CVAu}X13OYij`>U5lWH!UV)bt3l zSFCC?W6O(|o3){~ye2rW)acaG%HkStu%_4-tTn65oXd<&H>-oM)QjkOi@l4}y>k}L z^yV*EG~MfWrBzi2%hTpAynM1X7wq{G@R^w~!&>?eXDpT>3!0Pc1|(B-GQ*Jlty~9`3Ue z5BFJ#OP_ac-lZ2^m}k}{lifV8OhsC0shML7!)wub!D`tS_^Os&G^4bxZtB!IKA$Wf z-g#!5dGYdKg}-=NZApnWlCF7XOPRmGl10_i11o}aO6?+=Tq4q*VaS|azBy)|SC^T!Ev>XRnCa}9I@2|G_Uwo`o-y5?Bw0D9xzh4x=g*sZp7(Iq zR4hPNdO25^rL7{(>s?k?=M9wC)K*s&`+dRdy(`nhqiWYoFS;@^OqaD>S*x<{L%&aZ zthsK6LzswUF2#otXF7junh5OP!?7>GOfnHoHu`qrJf99s&%%>DK(Uy>W!`vk{P6;#L+q;+Bkd)t9%Da#^?GAVEhT;NV|i}v)7C~T>Yi~!;Y`R_~T4lW~G)p5q^ak zUyPT5`;d{8&Dn6jgvqI59$(*+r}=3b+%K0whAV#y%CF^=^2>3fj;}t&6o|_td9|SY zDR^&ym^2)F{xBwSJI<27F4tz7F+9G^_#@1WS2Ece-F5Wkc3b~)?AHc{oBj?=e+VOv zGi|A7p8nI#_?)JUa_u_#>vBD4x59rpR(`jzNpSp4_9%6@exeC*to$AtuKc+l*kiQh z+wF0tb%OcN<-fy>FKMRRVd1}xnDB=thRN!XSZTDq9pl@2`0s@0>~1}er$cl+?f<=j z;mY5SAAlijzs8vnWL@U+e_+O!>Eb52*B1Qjamn}r^I!6kM%y1^`{Sk!NKuy+7S7M^ zJDk7vzi-C3(xUO}=9_|KB$DgDvdf@pQtejwubt}Q`j_ADYT+Q$ z5$baG9q#7X$4udk1*rIk$EWpA%daUuY{r-A&sIP?gz4AoUE_?)qoX>$*2`^n{xr5J z|4TNV@Nbs?n@7mM=~8=2yHI|~Umkhn{5P5~iRAi!@C`fv8)V`fjh*V@I={jUcjtXq sZ9*CxVXj>4zjgO5`AehY+kGzA>85;K_cAOU%Wpe=tjl<^DdqD2A0Q~?;Q#;t diff --git a/src/external/PackedCSparse/qd/c_qd.o b/src/external/PackedCSparse/qd/c_qd.o deleted file mode 100644 index b51cf89e01a9fbaec79376ba80fdad0b69d9af7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156768 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 LBK>ev)!+XEmNauG diff --git a/src/external/PackedCSparse/qd/dd_const.o b/src/external/PackedCSparse/qd/dd_const.o deleted file mode 100644 index 58d29f408bdacf60408e9f7c433f5d88b357c9be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45776 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 diff --git a/src/external/PackedCSparse/qd/dd_real.o b/src/external/PackedCSparse/qd/dd_real.o deleted file mode 100644 index 7faf865a382556ee1c54148539943bcd9f4aab68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 279272 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 diff --git a/src/external/PackedCSparse/qd/fpu.o b/src/external/PackedCSparse/qd/fpu.o deleted file mode 100644 index eec0bb9bff461a747768916c6447c39062ef81ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15136 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 Ge*PC62!ELX diff --git a/src/external/PackedCSparse/qd/libqd.a b/src/external/PackedCSparse/qd/libqd.a deleted file mode 100644 index 1e0fb470e9a95016039e19ced0fc44ece180f425..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1196610 zcmeFa349er*7x6iZ{OsGgd`ATiJ-w7Fj2CSu!SH?fFOuKKtPd8R)}UZxnUC}Dx+io zML}iUKtaWkQFL5z8`+}Zii#uSK7xvh3W^ShBk%8=I^DNZ;5^SfGoR1ON^9%pDJ}BZMQXULJ|v~MtX_GFOB$4? zrf!a)n)=#WQL90vMbGh7RMpmsW@&k)6u7E(rqY8aD^q#Vc*@gIS))9qwUSw=SV*xG zSL{R>(B~*^SP$SbDyy>3 z$+T3i*s!kZqew)(Qsu=qxRbV0QzpZa2&?R<2cN7u;H|4eecG;0^*^W%_x*6*b~0q* zP8H4_qscCd8Yq+r28ydXGHzUI9IGmzhLIiDP+nD`hBwknKwVyUSf!-sa0{wHD_w5J zur8|7D$q)os~wJZszRBl^0$iQ0gfWcuos_W(U}9N)M^e;J~U%*fN*b8R@I~c6`2&s zCzAqs)l>jqvE#ER1wfe;;8T-=^6;bpubLF#Q3D?yH7?;%!yX=)6v*d9%A`O{CIxt9 z)Wau(9zGfKq!^ASJ2c>lh85IAAPvbAgy_hm0PO~QCV=1NRg(gkObYPHq#!<5kCI8j z?o$87q(Fm5#&b?EyRLFL4<{kLgBr*I$Zn-VB&QY04ffVicCcJi!KG?#rw?7dtjtzw zIl-FZ8javqsFCX8xvfb5U_)_5c~HkW!ScF>R*G4b_wPlFQ>EjHFKCMV!Rah5vN}J0=u$6W$4@C`~U^Azpy0$E=r0UeCtc-@+ zY?QrUU2WC8^4Y~zJy2tzp^HXB?74gPtHt@763X63wax zEcwD^A{{65{-N4nLkLU6nL~3+=g!T_8eUmh6w2xy997#898n#rs4AY>P*Osjv~?os z(&I`eR8mK*M;?_E>ebf9pX%qLiD-QQFP``A-^(rN~|__ z3T-`i(p??BTjFrBbNQa$3gytfy%i~&9S;73l_`xpvFsXoLbQ?BdR^Cgu}fLV%q)qy zi`%xH)iah)&T`Y0w=CURQY{TU^E6O|M)_ekj9HD5TF z;NLKr{c2mIOWPl*@G+w@+{0DNgc~BexU{sfzBp7)A2IkcwMzPr6qPA^#S(61XL;gk zMp-a~m3pf?cNl~h48ojSu)Sai>pBYtK~j>k`toYMrhQN?9jn`8Z?(P%Tea60Zlagc zUSGI$WPRZxvK9yv*ZM-BQ#ogS5!QCr7jATdO?!PIN~#I^Tz1m+h3K4Yec_Ih^@X5+ z;(mr3cUEy-9d2O!xM{-wc0XOC)PE*pAt8%Uh03*?fEl1?C+7iqf)T{Zmj7Rmf8)Nk!c01u%7N z>Xze_cx%JquU1qnpEP9hkPC)P8Zxn{@R|et3Y(9W6)v%Qq(Hy0xga^Sa7kq0lEVFC zmXyW$3tudV^CwUYNWqe-xXuO5Utaaev0siKzv?Bbbo0cx&V@?`?}uV!{HaiEIhtCK z9KRnv^%M|;1;yc zpu%OjlXFBkqVSW4R#3vuFHVyr&==}kx)|Ar4pdSG?zuAQ3mfXXs+^h4)>`%^r^aB7NHUVnrVjyC_)L@35NfQbAd7Moc@4v7_>E2oPlKBW zcNN@vxR>DOqMuw1w*qb*+~aU>!tI6gcrftc^5BNU-3GS~?jg7z;UZx>8Ey!iX<9}* zv)F3a-dbxGueRFvK%mE!9H7XIkXg*~9B(^gwRO&jwsl54HF{iPyM?E&w#Ht<{E{op zairE{?l;Xz)_(IRpIK!6v|tqzTS7255c>9YdN^qw}XN^e9aMvrP4lV{%o4l z$oH7}v(KESD#GA7^B13au>u#9#(wj9k2%g-?zs~}fuBupyQJ0D&*pMeHjSeQP^8rO zxMu^Zs=#|5RQf$?32K7X)C8-&>%3;E9nDSsp64@9+u5xy0x5IcYA>u^KRZPakZ(r|t>A6lwdTFP&& z^=(9u%>UK1|P zN*LJXoG#h;4|t@{7?qHNSU-6TBdQ~WQZkG-mWe=*=x8!)7||)P)D?H=7*^_`Xj>153MjfLbQA>;{1YFw?MapF>}VpPUCAy^rBI8+46dPJoSIRG zXBgeWK$$X()6$LBO=uXWyR5)^Qd<;TU59=Mc27nv2SrE2dj=`mO&NfiTrN^e?&ON= zVWQOyqi1)Nvo9jgBpcDugWyd`$3+caK*LCl7QUw83#axJEhY#@?hB2O$}op-M;dLI;IBo1U=^zZmhv}63568H!+N%b}sg%9ELXH z9#bGOj@{wuX1i0{M-1*fze^Ub!w8?@IfFbSbHr1aZjMYqCoKaUMZHjspmQ+Ib^XtiXyjuUW5an!hl-{ycRuA6?)Kv-$8d0bH!~}NiZ&&BYV>4WfeAjN zYg;N>Q8%M?3|7{j&J;7rmBvSDE`UvrnT*77q%tKM^NC?hCCy0z+xCUg+zN(q5w%Z_ z8kE!A#im)~Vt2;492VAgKEw3Gi#2n1I5}_wR|oFi;ltzgTC}J!&-L7GL|Q&yM1Y&mgk!GC zpr_@zE&6mLI?0Ug?ukCbiVl!svQq4!6k{$x)l;p4wtG;b6z|z)Y~)PZ99wGKVfml zF&1|Q+LN)n`m$Ivg2g>ESgf7RV%=|9+_#g(1AAF)FwwJOBOf}I#l{R4j~1}_V+o7L z8d*HPn8g#fvv~3$7Mr%Pcxp-qvhs8Vi)XH6@$4-uo_mPJpZ?6^`7c;(iN;hB8@VmO zVtXEomWeE0sAlovZ&>VD$ztapSiIE2;^hxmyz&E!SA8AH*lQhGyq?Np*FY9;oX_IT z5{S6SXfp=4;<(5dGnr*ua|p{*O)5`Z%hDFxZEV6H6k#i4=+~2e4^C*kH zPqXO1mBoNJS>%1fV&Hcy26=EZh;4sPdlo|ySqx2QF>DZvg0U<{OlL8&f<<8si&3*# zjJ}e^nCn@bcNdGX53v~cCl=#hBXLUZpIP+T!=mq3Bsvd1!eU4SW|r8_Lp!h-b_R=r zOcuiju^2Ii#mEa;6wYEXYBq~=7qJ+96N@oxSWJEhqTSE2r{=ss;jRncX3_K^i>r-9 zg4af{Xzt2lX)hMb@>pCyn#Bzlu~@#A#f_U--1I7o6@Ou|@>3Q!|G?rFAD$J&cD*%` z#qZ8yaoag8ZlA*9&UzMWma$m-I~MEKvbc8>i~F{-xc?It>yNQ`phH(O{a`YS4f!k% zj%V>*1B=58S^V%O_13OG-oT+F>sTE9jKxonu{icBi=TfY(Y+)VpCe9hgnqbWKr`Wi`v61>Y`2~)0dsbqJAighG{H9Ar_6- zvzYw{7IU6sG54=5<{e^jhFi;E|)m|n~xSjS?vnaWfMd=0> zWzVoEe~CrKhb(4(&0^NiEGnbVK(=QMOk|PYi^ZUCXr?`D@E{Hi8OdVkMJ$HZvM9KQ z#qgV0jChd6$fsEp?j(_3^f`-)A6d-v1qd$fz~b%{7LWC1@!|*;@6wVZHvQ`|h`4ma zY+#vSUd=MayoF_+`7q0|<~Eka=9?@-=3iMhnLn|-*NjajJDbdIEMGQzvHZ}?V|mCt zmu0FomE~Dh3ClF=QkLn~e3ltjGs`S%70YbvewMwhCs^iMEiC(3yIJk~Nm)1=cK+PPOh} zd7jIV)Rt3wM)@+uQ*0n4z zv2JF0skM$}jrB*CwbnM4b=F%f>#e;k8?5hHhAeLi{oS-)rb8|!J7*I6&ITx`9|vf28QBmP@Uu zR4T_Zt24_RtTS0IxB9ZY(HhBeg*BPwN~?cZ?$e=xypKw!E*@3snAuC+>7uCwY{-fLaW@_y?kmg}v1SUzAq%JM;LE6WYm zJ1qZT9boyeWnv{Am%h>J$np{EY?gnt2D5z3I-lj^)^wInTGcE!Sy!-p%3225J|Wr@ zg?&< z#gpjCS~v+2X5@u9`-3B< zW*3>F*!2ugz>^&`{nsP0e+5oP6q!i&WKS}?_3-rcWM5+5X~!vrZYiErPj<}-_$<%a zp6uBseGu{ALrM5lNV#_FT1J{!Z+SikPf!VE}r@tqAr}-nraT|0S z;K}o3zpAQ8+A7Iu13mek>^E4k+aS+5p6s_(A~tBW-GPrX7kbY!Pxp=VSefr6jqp$& zneU*(jPac3$%^*yHBuL+<5(MXJfD2j?8YhMJmWp-mwKqlJ5C@UcaKx1Q1Y32as@h0 zrKpYg5q&d)!zONDLGN+n6yCf zlSv~bH)_9!gBOAssX?4$X5uuxt?w+~#e3Rh1^SR;o8CsCZv&dEO&@=tAA9=72l}(8 zU+2I8_ViB*>1cEFo-?*g9GQVXK-O)FnflK3k+e;&?$kTb1930 z8G(X@TyM{dD z7YD{YK%S|~0}~!0&qb>O6E~4(+M2)xoW;fK1CyU8GJRuU%4YHeHw7-@yk=|;OnZe$ z$#x@^nl$wagw7t8O6?OvpOy=wS9L!(($jrRgc)vg(@4D+dcDqXb2clDkN#ci6O^zh z^*Iug(qg#Z#iVzi8tD(G>tLh=V<(%3pyU6EPE-j0arlhXA93zJ!)K*aY&u=4l;Wr# z)rF%tKVQ9+lBgIjr|N~yrIMy+!JFC#rwmd|DUBM#ot#rhA1I3|Qj9Y5Q`$@;tGpuW z#uI4D%&0G1-6eZg)VCZ=sf;?;q}KC4NIA3bY50tE>gK7BFGksB~hs= z1=m$emU3xS4>d6zA}gssk^xRu75jiISr>fZb?0^(!l{&HN_EuVPH3K*sK1Aqky0CV zv8o`KfXgg(ue&U2m5SnqR5YbNs)Lt@N_f1aG{g>5<#d|PzZPXMQrF|$+c&p!PKXAc zk<&=R%9%}l&7U)eM7)tTZ+_H^S|h9UDVN8#Q-)g?PQ_*|xFYH`t$>p5%Gi9Bj!VvE z$y(SH^|DKwVaipp*Q$oR7}JKIJCV9#G!}EOj(s`Y5y_O1drj0O+_$Jh`RQcjE{eKF zN56@krzgBN>RE2waKe=9q9%HoX|?-^rm*yVIH!JrQwDu(ILo)9UtDBhG368&Wdxep z)5Z$?mOat_z!LVv!~~YIr)_*-8GB+o1g>XKT<5?I?1}FhSk9hyNr4+_1jltq3EV^- zChn9>BX?!gM@~tN+?!)RXk8WVX-4iXQPpIF+d!Qtk&%0A>^&#bPgxba&DGMcVEN$g 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

TEz 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≺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ⅆ{J7$` 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 bv;ArT$iBzC_!{kzjvEIHmg@~K+5Z0nSX{b7 diff --git a/src/external/PackedCSparse/qd/qd_real.o b/src/external/PackedCSparse/qd/qd_real.o deleted file mode 100644 index 5a1b530e0efbfadc62f38023b9fd892a1e3dd1f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 472184 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%0DchhVnyl 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 P7JDV#dL^eI`~Uv|&y!BH diff --git a/src/external/PackedCSparse/qd/util.o b/src/external/PackedCSparse/qd/util.o deleted file mode 100644 index 9f2092ea458236a0146a8795956863882e7e7a4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33808 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 z Date: Wed, 28 Feb 2024 14:19:46 +0200 Subject: [PATCH 12/17] Update submodules to use volesti's develop and make use of git sparse-checkout --- .github/workflows/R-CMD-check-macOS.yml | 6 +- .github/workflows/R-CMD-check-ubuntu.yml | 6 +- .github/workflows/R-CMD-check-windows.yml | 6 +- .gitmodules | 5 +- R/RcppExports.R | 4 +- man/ode_solve.Rd | 4 +- src/Makevars | 14 +- src/Makevars.win | 14 +- src/external/ChebTools/ChebTools.cpp | 892 ------ src/external/ChebTools/ChebTools.h | 565 ---- src/external/ChebTools/speed_tests.h | 14 - src/external/PackedCSparse/FloatArray.h | 307 -- src/external/PackedCSparse/FloatArrayAVX2.h | 222 -- src/external/PackedCSparse/PackedChol.h | 308 -- src/external/PackedCSparse/SparseMatrix.h | 230 -- src/external/PackedCSparse/add.h | 102 - src/external/PackedCSparse/chol.h | 247 -- src/external/PackedCSparse/leverage.h | 65 - src/external/PackedCSparse/leverageJL.h | 148 - src/external/PackedCSparse/multiply.h | 142 - src/external/PackedCSparse/outerprod.h | 86 - src/external/PackedCSparse/projinv.h | 90 - src/external/PackedCSparse/qd/COPYING | 16 - src/external/PackedCSparse/qd/Makefile | 15 - src/external/PackedCSparse/qd/NEWS | 181 -- src/external/PackedCSparse/qd/README | 437 --- src/external/PackedCSparse/qd/bits.cc | 85 - src/external/PackedCSparse/qd/bits.h | 32 - src/external/PackedCSparse/qd/c_dd.cc | 314 -- src/external/PackedCSparse/qd/c_dd.h | 98 - src/external/PackedCSparse/qd/c_qd.cc | 450 --- src/external/PackedCSparse/qd/c_qd.h | 119 - src/external/PackedCSparse/qd/dd_const.cc | 40 - src/external/PackedCSparse/qd/dd_inline.h | 621 ---- src/external/PackedCSparse/qd/dd_real.cc | 1303 -------- src/external/PackedCSparse/qd/dd_real.h | 293 -- src/external/PackedCSparse/qd/fpu.cc | 124 - src/external/PackedCSparse/qd/fpu.h | 39 - src/external/PackedCSparse/qd/inline.h | 143 - src/external/PackedCSparse/qd/qd.pdf | Bin 194502 -> 0 bytes src/external/PackedCSparse/qd/qd_config.h | 92 - src/external/PackedCSparse/qd/qd_const.cc | 62 - src/external/PackedCSparse/qd/qd_inline.h | 1047 ------- src/external/PackedCSparse/qd/qd_real.cc | 2624 ----------------- src/external/PackedCSparse/qd/qd_real.h | 296 -- src/external/PackedCSparse/qd/util.cc | 22 - src/external/PackedCSparse/qd/util.h | 4 - src/external/PackedCSparse/transpose.h | 90 - src/external/Padua/padua.cpp | 1868 ------------ src/external/Padua/padua.h | 36 - .../Spectra/include/Spectra/GenEigsBase.h | 479 --- .../Spectra/GenEigsComplexShiftSolver.h | 156 - .../include/Spectra/GenEigsRealShiftSolver.h | 90 - .../Spectra/include/Spectra/GenEigsSolver.h | 160 - .../Spectra/include/Spectra/LinAlg/Arnoldi.h | 283 -- .../Spectra/include/Spectra/LinAlg/BKLDLT.h | 522 ---- .../include/Spectra/LinAlg/DoubleShiftQR.h | 378 --- .../Spectra/include/Spectra/LinAlg/Lanczos.h | 170 -- .../include/Spectra/LinAlg/TridiagEigen.h | 219 -- .../Spectra/LinAlg/UpperHessenbergEigen.h | 317 -- .../Spectra/LinAlg/UpperHessenbergQR.h | 670 ----- .../include/Spectra/MatOp/DenseCholesky.h | 110 - .../Spectra/MatOp/DenseGenComplexShiftSolve.h | 104 - .../include/Spectra/MatOp/DenseGenMatProd.h | 82 - .../Spectra/MatOp/DenseGenRealShiftSolve.h | 90 - .../include/Spectra/MatOp/DenseSymMatProd.h | 75 - .../Spectra/MatOp/DenseSymShiftSolve.h | 94 - .../include/Spectra/MatOp/SparseCholesky.h | 111 - .../include/Spectra/MatOp/SparseGenMatProd.h | 76 - .../Spectra/MatOp/SparseGenRealShiftSolve.h | 95 - .../Spectra/MatOp/SparseRegularInverse.h | 102 - .../include/Spectra/MatOp/SparseSymMatProd.h | 75 - .../Spectra/MatOp/SparseSymShiftSolve.h | 97 - .../Spectra/MatOp/internal/ArnoldiOp.h | 155 - .../MatOp/internal/SymGEigsCholeskyOp.h | 77 - .../Spectra/MatOp/internal/SymGEigsRegInvOp.h | 74 - .../Spectra/include/Spectra/SymEigsBase.h | 447 --- .../include/Spectra/SymEigsShiftSolver.h | 205 -- .../Spectra/include/Spectra/SymEigsSolver.h | 174 -- .../Spectra/include/Spectra/SymGEigsSolver.h | 334 --- .../Spectra/include/Spectra/Util/CompInfo.h | 37 - .../Spectra/include/Spectra/Util/GEigsMode.h | 34 - .../include/Spectra/Util/SelectionRule.h | 277 -- .../include/Spectra/Util/SimpleRandom.h | 93 - .../Spectra/include/Spectra/Util/TypeTraits.h | 73 - .../include/Spectra/contrib/LOBPCGSolver.h | 501 ---- .../Spectra/contrib/PartialSVDSolver.h | 203 -- src/external/arpack++/include/README | 232 -- src/external/arpack++/include/arbgcomp.h | 204 -- src/external/arpack++/include/arbgnsym.h | 243 -- src/external/arpack++/include/arbgsym.h | 233 -- src/external/arpack++/include/arbnsmat.h | 431 --- src/external/arpack++/include/arbnspen.h | 470 --- src/external/arpack++/include/arbscomp.h | 165 -- src/external/arpack++/include/arbsmat.h | 378 --- src/external/arpack++/include/arbsnsym.h | 163 - src/external/arpack++/include/arbspen.h | 303 -- src/external/arpack++/include/arbssym.h | 159 - src/external/arpack++/include/arcgsym.h | 242 -- src/external/arpack++/include/arch.h | 95 - src/external/arpack++/include/arcomp.h | 46 - src/external/arpack++/include/arcsmat.h | 473 --- src/external/arpack++/include/arcspen.h | 434 --- src/external/arpack++/include/arcssym.h | 166 -- src/external/arpack++/include/ardfmat.h | 453 --- src/external/arpack++/include/ardgcomp.h | 204 -- src/external/arpack++/include/ardgnsym.h | 244 -- src/external/arpack++/include/ardgsym.h | 233 -- src/external/arpack++/include/ardnsmat.h | 622 ---- src/external/arpack++/include/ardnspen.h | 324 -- src/external/arpack++/include/ardscomp.h | 166 -- src/external/arpack++/include/ardsmat.h | 357 --- src/external/arpack++/include/ardsnsym.h | 163 - src/external/arpack++/include/ardspen.h | 237 -- src/external/arpack++/include/ardssym.h | 159 - src/external/arpack++/include/arerror.h | 348 --- src/external/arpack++/include/argcomp.h | 126 - src/external/arpack++/include/argeig.h | 233 -- src/external/arpack++/include/argnsym.h | 362 --- src/external/arpack++/include/argsym.h | 326 -- src/external/arpack++/include/arhbmat.h | 407 --- src/external/arpack++/include/arlcomp.h | 153 - src/external/arpack++/include/arlgcomp.h | 204 -- src/external/arpack++/include/arlgnsym.h | 252 -- src/external/arpack++/include/arlgsym.h | 235 -- src/external/arpack++/include/arlnames.h | 464 --- src/external/arpack++/include/arlnsmat.h | 753 ----- src/external/arpack++/include/arlnspen.h | 774 ----- src/external/arpack++/include/arlscomp.h | 188 -- src/external/arpack++/include/arlsmat.h | 765 ----- src/external/arpack++/include/arlsnsym.h | 182 -- src/external/arpack++/include/arlspdef.h | 610 ---- src/external/arpack++/include/arlspen.h | 679 ----- src/external/arpack++/include/arlssym.h | 179 -- src/external/arpack++/include/arlsupm.h | 185 -- src/external/arpack++/include/arlutil.h | 432 --- src/external/arpack++/include/armat.h | 56 - src/external/arpack++/include/arpackf.h | 150 - src/external/arpack++/include/arrgcomp.h | 110 - src/external/arpack++/include/arrgeig.h | 103 - src/external/arpack++/include/arrgnsym.h | 244 -- src/external/arpack++/include/arrgsym.h | 236 -- src/external/arpack++/include/arrscomp.h | 375 --- src/external/arpack++/include/arrseig.h | 1515 ---------- src/external/arpack++/include/arrsnsym.h | 861 ------ src/external/arpack++/include/arrssym.h | 424 --- src/external/arpack++/include/arscomp.h | 118 - src/external/arpack++/include/arseig.h | 237 -- src/external/arpack++/include/arsnsym.h | 117 - src/external/arpack++/include/arssym.h | 118 - src/external/arpack++/include/arugcomp.h | 204 -- src/external/arpack++/include/arugnsym.h | 245 -- src/external/arpack++/include/arugsym.h | 234 -- src/external/arpack++/include/arunsmat.h | 646 ---- src/external/arpack++/include/arunspen.h | 527 ---- src/external/arpack++/include/aruscomp.h | 166 -- src/external/arpack++/include/arusmat.h | 743 ----- src/external/arpack++/include/arusnsym.h | 162 - src/external/arpack++/include/aruspen.h | 543 ---- src/external/arpack++/include/arussym.h | 158 - src/external/arpack++/include/blas1c.h | 367 --- src/external/arpack++/include/blas1f.h | 221 -- src/external/arpack++/include/caupp.h | 320 -- src/external/arpack++/include/ceupp.h | 224 -- src/external/arpack++/include/cholmodc.h | 161 - src/external/arpack++/include/debug.h | 135 - src/external/arpack++/include/lapackc.h | 306 -- src/external/arpack++/include/lapackf.h | 207 -- src/external/arpack++/include/naupp.h | 333 --- src/external/arpack++/include/neupp.h | 270 -- src/external/arpack++/include/saupp.h | 321 -- src/external/arpack++/include/seupp.h | 190 -- src/external/arpack++/include/superluc.h | 165 -- src/external/arpack++/include/umfpackc.h | 216 -- src/external/cmake-files/Boost.cmake | 36 - src/external/cmake-files/Eigen.cmake | 32 - src/external/cmake-files/LPSolve.cmake | 32 - src/external/cmake-files/QD.cmake | 54 - src/external/minimum_ellipsoid/bnmin_main.h | 87 - src/external/minimum_ellipsoid/khach.h | 220 -- src/external/minimum_ellipsoid/mcpoint.h | 477 --- src/include | 1 - .../lpsolve/build/lp_solve/Makefile | 0 .../lpsolve/build/lp_solve/colamd.c | 0 .../lpsolve/build/lp_solve/commonlib.c | 0 .../lpsolve/build/lp_solve/ini.c | 0 .../lpsolve/build/lp_solve/lp_BFP1.c | 0 .../lpsolve/build/lp_solve/lp_BFP2.c | 0 .../lpsolve/build/lp_solve/lp_Hash.c | 0 .../lpsolve/build/lp_solve/lp_LUSOL.c | 0 .../lpsolve/build/lp_solve/lp_MDO.c | 0 .../lpsolve/build/lp_solve/lp_MPS.c | 0 .../lpsolve/build/lp_solve/lp_SOS.c | 0 .../lpsolve/build/lp_solve/lp_crash.c | 0 .../lpsolve/build/lp_solve/lp_lib.c | 0 .../lpsolve/build/lp_solve/lp_matrix.c | 0 .../lpsolve/build/lp_solve/lp_mipbb.c | 0 .../lpsolve/build/lp_solve/lp_params.c | 0 .../lpsolve/build/lp_solve/lp_presolve.c | 0 .../lpsolve/build/lp_solve/lp_price.c | 0 .../lpsolve/build/lp_solve/lp_pricePSE.c | 0 .../lpsolve/build/lp_solve/lp_report.c | 0 .../lpsolve/build/lp_solve/lp_rlp.c | 0 .../lpsolve/build/lp_solve/lp_scale.c | 0 .../lpsolve/build/lp_solve/lp_simplex.c | 0 .../lpsolve/build/lp_solve/lp_utils.c | 0 .../lpsolve/build/lp_solve/lp_wlp.c | 0 .../lpsolve/build/lp_solve/lusol.c | 0 .../lpsolve/build/lp_solve/lusol1.c | 0 .../lpsolve/build/lp_solve/lusol2.c | 0 .../lpsolve/build/lp_solve/lusol6a.c | 0 .../lpsolve/build/lp_solve/lusol6l0.c | 0 .../lpsolve/build/lp_solve/lusol6u.c | 0 .../lpsolve/build/lp_solve/lusol7a.c | 0 .../lpsolve/build/lp_solve/lusol8a.c | 0 .../lpsolve/build/lp_solve/mmio.c | 0 .../lpsolve/build/lp_solve/myblas.c | 0 .../lpsolve/build/lp_solve/yacc_read.c | 0 .../lpsolve/headers/include/RlpSolve.h | 0 .../lpsolve/headers/include/RlpSolveLink.h | 0 .../lpsolve/headers/include/colamd.h | 0 .../lpsolve/headers/include/commonlib.h | 0 .../lpsolve/headers/include/ini.h | 0 .../lpsolve/headers/include/lp_BFP.h | 0 .../lpsolve/headers/include/lp_Hash.h | 0 .../lpsolve/headers/include/lp_LUSOL.h | 0 .../lpsolve/headers/include/lp_MDO.h | 0 .../lpsolve/headers/include/lp_MPS.h | 0 .../lpsolve/headers/include/lp_SOS.h | 0 .../lpsolve/headers/include/lp_bit.h | 0 .../lpsolve/headers/include/lp_crash.h | 0 .../lpsolve/headers/include/lp_lib.h | 0 .../lpsolve/headers/include/lp_matrix.h | 0 .../lpsolve/headers/include/lp_mipbb.h | 0 .../lpsolve/headers/include/lp_presolve.h | 0 .../lpsolve/headers/include/lp_price.h | 0 .../lpsolve/headers/include/lp_pricePSE.h | 0 .../lpsolve/headers/include/lp_report.h | 0 .../lpsolve/headers/include/lp_rlp.h | 0 .../lpsolve/headers/include/lp_scale.h | 0 .../lpsolve/headers/include/lp_simplex.h | 0 .../lpsolve/headers/include/lp_types.h | 0 .../lpsolve/headers/include/lp_utils.h | 0 .../lpsolve/headers/include/lp_wlp.h | 0 .../lpsolve/headers/include/lpkit.h | 0 .../lpsolve/headers/include/lusol.h | 0 .../lpsolve/headers/include/mmio.h | 0 .../lpsolve/headers/include/myblas.h | 0 .../lpsolve/headers/include/yacc_read.h | 0 .../lpsolve/headers/run_headers/colamd.h | 0 .../lpsolve/headers/run_headers/commonlib.h | 0 .../lpsolve/headers/run_headers/declare.h | 0 .../lpsolve/headers/run_headers/fortify.h | 0 .../lpsolve/headers/run_headers/hbio.h | 0 .../lpsolve/headers/run_headers/ini.h | 0 .../lpsolve/headers/run_headers/lp_BFP.h | 0 .../lpsolve/headers/run_headers/lp_BFP1.h | 0 .../lpsolve/headers/run_headers/lp_BFP2.h | 0 .../lpsolve/headers/run_headers/lp_Hash.c | 0 .../lpsolve/headers/run_headers/lp_Hash.h | 0 .../lpsolve/headers/run_headers/lp_LUSOL.h | 0 .../lpsolve/headers/run_headers/lp_MDO.h | 0 .../lpsolve/headers/run_headers/lp_MPS.h | 0 .../lpsolve/headers/run_headers/lp_SOS.h | 0 .../lpsolve/headers/run_headers/lp_crash.c | 0 .../lpsolve/headers/run_headers/lp_crash.h | 0 .../lpsolve/headers/run_headers/lp_explicit.h | 0 .../lpsolve/headers/run_headers/lp_fortify.h | 0 .../lpsolve/headers/run_headers/lp_lib.h | 0 .../lpsolve/headers/run_headers/lp_matrix.h | 0 .../lpsolve/headers/run_headers/lp_mipbb.h | 0 .../lpsolve/headers/run_headers/lp_presolve.h | 0 .../lpsolve/headers/run_headers/lp_price.h | 0 .../lpsolve/headers/run_headers/lp_pricePSE.h | 0 .../lpsolve/headers/run_headers/lp_report.h | 0 .../lpsolve/headers/run_headers/lp_rlp.h | 0 .../lpsolve/headers/run_headers/lp_scale.h | 0 .../lpsolve/headers/run_headers/lp_simplex.h | 0 .../lpsolve/headers/run_headers/lp_types.h | 0 .../lpsolve/headers/run_headers/lp_utils.h | 0 .../lpsolve/headers/run_headers/lp_wlp.h | 0 .../lpsolve/headers/run_headers/lpkit.h | 0 .../lpsolve/headers/run_headers/lpsolve.h | 0 .../lpsolve/headers/run_headers/lusol.h | 0 .../lpsolve/headers/run_headers/lusol1.h | 0 .../lpsolve/headers/run_headers/lusol2.h | 0 .../lpsolve/headers/run_headers/lusol6a.h | 0 .../lpsolve/headers/run_headers/lusol6l0.h | 0 .../lpsolve/headers/run_headers/lusol6u.h | 0 .../lpsolve/headers/run_headers/lusol7a.h | 0 .../lpsolve/headers/run_headers/lusol8a.h | 0 .../lpsolve/headers/run_headers/lusolio.h | 0 .../lpsolve/headers/run_headers/mmio.h | 0 .../lpsolve/headers/run_headers/myblas.h | 0 .../lpsolve/headers/run_headers/sparselib.h | 0 .../lpsolve/headers/run_headers/ufortify.h | 0 .../lpsolve/headers/run_headers/yacc_read.h | 0 src/volesti | 1 + 298 files changed, 38 insertions(+), 49253 deletions(-) delete mode 100644 src/external/ChebTools/ChebTools.cpp delete mode 100644 src/external/ChebTools/ChebTools.h delete mode 100644 src/external/ChebTools/speed_tests.h delete mode 100644 src/external/PackedCSparse/FloatArray.h delete mode 100644 src/external/PackedCSparse/FloatArrayAVX2.h delete mode 100644 src/external/PackedCSparse/PackedChol.h delete mode 100644 src/external/PackedCSparse/SparseMatrix.h delete mode 100644 src/external/PackedCSparse/add.h delete mode 100644 src/external/PackedCSparse/chol.h delete mode 100644 src/external/PackedCSparse/leverage.h delete mode 100644 src/external/PackedCSparse/leverageJL.h delete mode 100644 src/external/PackedCSparse/multiply.h delete mode 100644 src/external/PackedCSparse/outerprod.h delete mode 100644 src/external/PackedCSparse/projinv.h delete mode 100644 src/external/PackedCSparse/qd/COPYING delete mode 100644 src/external/PackedCSparse/qd/Makefile delete mode 100644 src/external/PackedCSparse/qd/NEWS delete mode 100644 src/external/PackedCSparse/qd/README delete mode 100644 src/external/PackedCSparse/qd/bits.cc delete mode 100644 src/external/PackedCSparse/qd/bits.h delete mode 100644 src/external/PackedCSparse/qd/c_dd.cc delete mode 100644 src/external/PackedCSparse/qd/c_dd.h delete mode 100644 src/external/PackedCSparse/qd/c_qd.cc delete mode 100644 src/external/PackedCSparse/qd/c_qd.h delete mode 100644 src/external/PackedCSparse/qd/dd_const.cc delete mode 100644 src/external/PackedCSparse/qd/dd_inline.h delete mode 100644 src/external/PackedCSparse/qd/dd_real.cc delete mode 100644 src/external/PackedCSparse/qd/dd_real.h delete mode 100644 src/external/PackedCSparse/qd/fpu.cc delete mode 100644 src/external/PackedCSparse/qd/fpu.h delete mode 100644 src/external/PackedCSparse/qd/inline.h delete mode 100644 src/external/PackedCSparse/qd/qd.pdf delete mode 100644 src/external/PackedCSparse/qd/qd_config.h delete mode 100644 src/external/PackedCSparse/qd/qd_const.cc delete mode 100644 src/external/PackedCSparse/qd/qd_inline.h delete mode 100644 src/external/PackedCSparse/qd/qd_real.cc delete mode 100644 src/external/PackedCSparse/qd/qd_real.h delete mode 100644 src/external/PackedCSparse/qd/util.cc delete mode 100644 src/external/PackedCSparse/qd/util.h delete mode 100644 src/external/PackedCSparse/transpose.h delete mode 100644 src/external/Padua/padua.cpp delete mode 100644 src/external/Padua/padua.h delete mode 100644 src/external/Spectra/include/Spectra/GenEigsBase.h delete mode 100644 src/external/Spectra/include/Spectra/GenEigsComplexShiftSolver.h delete mode 100644 src/external/Spectra/include/Spectra/GenEigsRealShiftSolver.h delete mode 100644 src/external/Spectra/include/Spectra/GenEigsSolver.h delete mode 100644 src/external/Spectra/include/Spectra/LinAlg/Arnoldi.h delete mode 100644 src/external/Spectra/include/Spectra/LinAlg/BKLDLT.h delete mode 100644 src/external/Spectra/include/Spectra/LinAlg/DoubleShiftQR.h delete mode 100644 src/external/Spectra/include/Spectra/LinAlg/Lanczos.h delete mode 100644 src/external/Spectra/include/Spectra/LinAlg/TridiagEigen.h delete mode 100644 src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergEigen.h delete mode 100644 src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergQR.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseCholesky.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseGenMatProd.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseSymMatProd.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseSymShiftSolve.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseCholesky.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseGenMatProd.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseRegularInverse.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseSymMatProd.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseSymShiftSolve.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/internal/ArnoldiOp.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h delete mode 100644 src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h delete mode 100644 src/external/Spectra/include/Spectra/SymEigsBase.h delete mode 100644 src/external/Spectra/include/Spectra/SymEigsShiftSolver.h delete mode 100644 src/external/Spectra/include/Spectra/SymEigsSolver.h delete mode 100644 src/external/Spectra/include/Spectra/SymGEigsSolver.h delete mode 100644 src/external/Spectra/include/Spectra/Util/CompInfo.h delete mode 100644 src/external/Spectra/include/Spectra/Util/GEigsMode.h delete mode 100644 src/external/Spectra/include/Spectra/Util/SelectionRule.h delete mode 100644 src/external/Spectra/include/Spectra/Util/SimpleRandom.h delete mode 100644 src/external/Spectra/include/Spectra/Util/TypeTraits.h delete mode 100644 src/external/Spectra/include/Spectra/contrib/LOBPCGSolver.h delete mode 100644 src/external/Spectra/include/Spectra/contrib/PartialSVDSolver.h delete mode 100644 src/external/arpack++/include/README delete mode 100644 src/external/arpack++/include/arbgcomp.h delete mode 100644 src/external/arpack++/include/arbgnsym.h delete mode 100644 src/external/arpack++/include/arbgsym.h delete mode 100644 src/external/arpack++/include/arbnsmat.h delete mode 100644 src/external/arpack++/include/arbnspen.h delete mode 100644 src/external/arpack++/include/arbscomp.h delete mode 100644 src/external/arpack++/include/arbsmat.h delete mode 100644 src/external/arpack++/include/arbsnsym.h delete mode 100644 src/external/arpack++/include/arbspen.h delete mode 100644 src/external/arpack++/include/arbssym.h delete mode 100644 src/external/arpack++/include/arcgsym.h delete mode 100644 src/external/arpack++/include/arch.h delete mode 100644 src/external/arpack++/include/arcomp.h delete mode 100644 src/external/arpack++/include/arcsmat.h delete mode 100644 src/external/arpack++/include/arcspen.h delete mode 100644 src/external/arpack++/include/arcssym.h delete mode 100644 src/external/arpack++/include/ardfmat.h delete mode 100644 src/external/arpack++/include/ardgcomp.h delete mode 100644 src/external/arpack++/include/ardgnsym.h delete mode 100644 src/external/arpack++/include/ardgsym.h delete mode 100644 src/external/arpack++/include/ardnsmat.h delete mode 100644 src/external/arpack++/include/ardnspen.h delete mode 100644 src/external/arpack++/include/ardscomp.h delete mode 100644 src/external/arpack++/include/ardsmat.h delete mode 100644 src/external/arpack++/include/ardsnsym.h delete mode 100644 src/external/arpack++/include/ardspen.h delete mode 100644 src/external/arpack++/include/ardssym.h delete mode 100644 src/external/arpack++/include/arerror.h delete mode 100644 src/external/arpack++/include/argcomp.h delete mode 100644 src/external/arpack++/include/argeig.h delete mode 100644 src/external/arpack++/include/argnsym.h delete mode 100644 src/external/arpack++/include/argsym.h delete mode 100644 src/external/arpack++/include/arhbmat.h delete mode 100644 src/external/arpack++/include/arlcomp.h delete mode 100644 src/external/arpack++/include/arlgcomp.h delete mode 100644 src/external/arpack++/include/arlgnsym.h delete mode 100644 src/external/arpack++/include/arlgsym.h delete mode 100644 src/external/arpack++/include/arlnames.h delete mode 100644 src/external/arpack++/include/arlnsmat.h delete mode 100644 src/external/arpack++/include/arlnspen.h delete mode 100644 src/external/arpack++/include/arlscomp.h delete mode 100644 src/external/arpack++/include/arlsmat.h delete mode 100644 src/external/arpack++/include/arlsnsym.h delete mode 100644 src/external/arpack++/include/arlspdef.h delete mode 100644 src/external/arpack++/include/arlspen.h delete mode 100644 src/external/arpack++/include/arlssym.h delete mode 100644 src/external/arpack++/include/arlsupm.h delete mode 100644 src/external/arpack++/include/arlutil.h delete mode 100644 src/external/arpack++/include/armat.h delete mode 100644 src/external/arpack++/include/arpackf.h delete mode 100644 src/external/arpack++/include/arrgcomp.h delete mode 100644 src/external/arpack++/include/arrgeig.h delete mode 100644 src/external/arpack++/include/arrgnsym.h delete mode 100644 src/external/arpack++/include/arrgsym.h delete mode 100644 src/external/arpack++/include/arrscomp.h delete mode 100644 src/external/arpack++/include/arrseig.h delete mode 100644 src/external/arpack++/include/arrsnsym.h delete mode 100644 src/external/arpack++/include/arrssym.h delete mode 100644 src/external/arpack++/include/arscomp.h delete mode 100644 src/external/arpack++/include/arseig.h delete mode 100644 src/external/arpack++/include/arsnsym.h delete mode 100644 src/external/arpack++/include/arssym.h delete mode 100644 src/external/arpack++/include/arugcomp.h delete mode 100644 src/external/arpack++/include/arugnsym.h delete mode 100644 src/external/arpack++/include/arugsym.h delete mode 100644 src/external/arpack++/include/arunsmat.h delete mode 100644 src/external/arpack++/include/arunspen.h delete mode 100644 src/external/arpack++/include/aruscomp.h delete mode 100644 src/external/arpack++/include/arusmat.h delete mode 100644 src/external/arpack++/include/arusnsym.h delete mode 100644 src/external/arpack++/include/aruspen.h delete mode 100644 src/external/arpack++/include/arussym.h delete mode 100644 src/external/arpack++/include/blas1c.h delete mode 100644 src/external/arpack++/include/blas1f.h delete mode 100644 src/external/arpack++/include/caupp.h delete mode 100644 src/external/arpack++/include/ceupp.h delete mode 100644 src/external/arpack++/include/cholmodc.h delete mode 100644 src/external/arpack++/include/debug.h delete mode 100644 src/external/arpack++/include/lapackc.h delete mode 100644 src/external/arpack++/include/lapackf.h delete mode 100644 src/external/arpack++/include/naupp.h delete mode 100644 src/external/arpack++/include/neupp.h delete mode 100644 src/external/arpack++/include/saupp.h delete mode 100644 src/external/arpack++/include/seupp.h delete mode 100644 src/external/arpack++/include/superluc.h delete mode 100644 src/external/arpack++/include/umfpackc.h delete mode 100644 src/external/cmake-files/Boost.cmake delete mode 100644 src/external/cmake-files/Eigen.cmake delete mode 100644 src/external/cmake-files/LPSolve.cmake delete mode 100644 src/external/cmake-files/QD.cmake delete mode 100644 src/external/minimum_ellipsoid/bnmin_main.h delete mode 100644 src/external/minimum_ellipsoid/khach.h delete mode 100644 src/external/minimum_ellipsoid/mcpoint.h delete mode 160000 src/include rename src/{external => }/lpsolve/build/lp_solve/Makefile (100%) rename src/{external => }/lpsolve/build/lp_solve/colamd.c (100%) rename src/{external => }/lpsolve/build/lp_solve/commonlib.c (100%) rename src/{external => }/lpsolve/build/lp_solve/ini.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_BFP1.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_BFP2.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_Hash.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_LUSOL.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_MDO.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_MPS.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_SOS.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_crash.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_lib.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_matrix.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_mipbb.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_params.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_presolve.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_price.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_pricePSE.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_report.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_rlp.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_scale.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_simplex.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_utils.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lp_wlp.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lusol.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lusol1.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lusol2.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lusol6a.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lusol6l0.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lusol6u.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lusol7a.c (100%) rename src/{external => }/lpsolve/build/lp_solve/lusol8a.c (100%) rename src/{external => }/lpsolve/build/lp_solve/mmio.c (100%) rename src/{external => }/lpsolve/build/lp_solve/myblas.c (100%) rename src/{external => }/lpsolve/build/lp_solve/yacc_read.c (100%) rename src/{external => }/lpsolve/headers/include/RlpSolve.h (100%) rename src/{external => }/lpsolve/headers/include/RlpSolveLink.h (100%) rename src/{external => }/lpsolve/headers/include/colamd.h (100%) rename src/{external => }/lpsolve/headers/include/commonlib.h (100%) rename src/{external => }/lpsolve/headers/include/ini.h (100%) rename src/{external => }/lpsolve/headers/include/lp_BFP.h (100%) rename src/{external => }/lpsolve/headers/include/lp_Hash.h (100%) rename src/{external => }/lpsolve/headers/include/lp_LUSOL.h (100%) rename src/{external => }/lpsolve/headers/include/lp_MDO.h (100%) rename src/{external => }/lpsolve/headers/include/lp_MPS.h (100%) rename src/{external => }/lpsolve/headers/include/lp_SOS.h (100%) rename src/{external => }/lpsolve/headers/include/lp_bit.h (100%) rename src/{external => }/lpsolve/headers/include/lp_crash.h (100%) rename src/{external => }/lpsolve/headers/include/lp_lib.h (100%) rename src/{external => }/lpsolve/headers/include/lp_matrix.h (100%) rename src/{external => }/lpsolve/headers/include/lp_mipbb.h (100%) rename src/{external => }/lpsolve/headers/include/lp_presolve.h (100%) rename src/{external => }/lpsolve/headers/include/lp_price.h (100%) rename src/{external => }/lpsolve/headers/include/lp_pricePSE.h (100%) rename src/{external => }/lpsolve/headers/include/lp_report.h (100%) rename src/{external => }/lpsolve/headers/include/lp_rlp.h (100%) rename src/{external => }/lpsolve/headers/include/lp_scale.h (100%) rename src/{external => }/lpsolve/headers/include/lp_simplex.h (100%) rename src/{external => }/lpsolve/headers/include/lp_types.h (100%) rename src/{external => }/lpsolve/headers/include/lp_utils.h (100%) rename src/{external => }/lpsolve/headers/include/lp_wlp.h (100%) rename src/{external => }/lpsolve/headers/include/lpkit.h (100%) rename src/{external => }/lpsolve/headers/include/lusol.h (100%) rename src/{external => }/lpsolve/headers/include/mmio.h (100%) rename src/{external => }/lpsolve/headers/include/myblas.h (100%) rename src/{external => }/lpsolve/headers/include/yacc_read.h (100%) rename src/{external => }/lpsolve/headers/run_headers/colamd.h (100%) rename src/{external => }/lpsolve/headers/run_headers/commonlib.h (100%) rename src/{external => }/lpsolve/headers/run_headers/declare.h (100%) rename src/{external => }/lpsolve/headers/run_headers/fortify.h (100%) rename src/{external => }/lpsolve/headers/run_headers/hbio.h (100%) rename src/{external => }/lpsolve/headers/run_headers/ini.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_BFP.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_BFP1.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_BFP2.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_Hash.c (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_Hash.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_LUSOL.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_MDO.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_MPS.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_SOS.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_crash.c (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_crash.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_explicit.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_fortify.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_lib.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_matrix.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_mipbb.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_presolve.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_price.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_pricePSE.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_report.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_rlp.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_scale.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_simplex.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_types.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_utils.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lp_wlp.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lpkit.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lpsolve.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusol.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusol1.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusol2.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusol6a.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusol6l0.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusol6u.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusol7a.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusol8a.h (100%) rename src/{external => }/lpsolve/headers/run_headers/lusolio.h (100%) rename src/{external => }/lpsolve/headers/run_headers/mmio.h (100%) rename src/{external => }/lpsolve/headers/run_headers/myblas.h (100%) rename src/{external => }/lpsolve/headers/run_headers/sparselib.h (100%) rename src/{external => }/lpsolve/headers/run_headers/ufortify.h (100%) rename src/{external => }/lpsolve/headers/run_headers/yacc_read.h (100%) create mode 160000 src/volesti diff --git a/.github/workflows/R-CMD-check-macOS.yml b/.github/workflows/R-CMD-check-macOS.yml index 4ab68f87..05b3cf25 100644 --- a/.github/workflows/R-CMD-check-macOS.yml +++ b/.github/workflows/R-CMD-check-macOS.yml @@ -38,7 +38,11 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - name: Fetch and update submodule - run: git submodule update --init --recursive + run: git submodule update --init --recursive; + cd src/volesti; + git config core.sparseCheckoutCone 'false'; + git sparse-checkout disable; + git sparse-checkout set include external; - name: Install dependencies run: Rscript -e "install.packages(c('devtools', dependencies=TRUE))" -e "install.packages(c('rcmdcheck', 'devtools', 'Rcpp', 'RcppEigen', 'BH', 'testthat', 'downloader', 'xfun'))"; diff --git a/.github/workflows/R-CMD-check-ubuntu.yml b/.github/workflows/R-CMD-check-ubuntu.yml index 445c5446..57780b1c 100644 --- a/.github/workflows/R-CMD-check-ubuntu.yml +++ b/.github/workflows/R-CMD-check-ubuntu.yml @@ -40,7 +40,11 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - name: Fetch and update submodule - run: git submodule update --init --recursive + run: git submodule update --init --recursive; + cd src/volesti; + git config core.sparseCheckoutCone 'false'; + git sparse-checkout disable; + git sparse-checkout set include external; - name: Install dependencies run: Rscript -e "install.packages(c('testthat', 'pkgload', 'rcmdcheck', 'devtools', 'Rcpp', 'RcppEigen', 'BH', 'downloader', 'xfun', dependencies=TRUE))"; diff --git a/.github/workflows/R-CMD-check-windows.yml b/.github/workflows/R-CMD-check-windows.yml index 099f7458..ae3f3bef 100644 --- a/.github/workflows/R-CMD-check-windows.yml +++ b/.github/workflows/R-CMD-check-windows.yml @@ -37,7 +37,11 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - name: Fetch and update submodule - run: git submodule update --init --recursive + run: git submodule update --init --recursive; + cd src/volesti; + git config core.sparseCheckoutCone 'false'; + git sparse-checkout disable; + git sparse-checkout set include external; - name: Install dependencies run: Rscript -e "install.packages(c('devtools', dependencies=TRUE))" -e "install.packages(c('rcmdcheck', 'devtools', 'Rcpp', 'RcppEigen', 'BH', 'testthat', 'downloader', 'xfun'))" diff --git a/.gitmodules b/.gitmodules index 05748818..42627744 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,3 @@ -[submodule "volesti_cran_include"] - path = src/include +[submodule "src/volesti"] + path = src/volesti url = https://github.com/GeomScale/volesti.git - branch = cran_include diff --git a/R/RcppExports.R b/R/RcppExports.R index d77ee54f..f16e5f11 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -202,7 +202,9 @@ load_sdpa_format_file <- function(input_file = NULL) { #' @return A list which contains elements "x_1", ..., "x_n" representing each derivative results. Each "x_i" corresponds to a d x n matrix where each column represents a certain timestep of the solver. #' #' @examples -#' # Please visit the examples directory on examples demonstrating usage of the ODE solvers. +#' F <- function (x) (-x) +#' initial_conditions <- list("x_1" = c(0), "x_2" = c(1)) +#' states <- ode_solve(dimension=1, n=1000, F=F, initial_time=0, step_size=0.01, order=2, method="leapfrog", initial_conditions=initial_conditions, domains = list()) #' #' @export ode_solve <- function(n, step_size, order, dimension, initial_time, F, method, domains = NULL, initial_conditions = NULL) { diff --git a/man/ode_solve.Rd b/man/ode_solve.Rd index b4e8e4b9..2836f342 100644 --- a/man/ode_solve.Rd +++ b/man/ode_solve.Rd @@ -42,6 +42,8 @@ A list which contains elements "x_1", ..., "x_n" representing each derivative re Solve an ODE of the form dx^n / dt^n = F(x, t) } \examples{ -# Please visit the examples directory on examples demonstrating usage of the ODE solvers. +F <- function (x) (-x) +initial_conditions <- list("x_1" = c(0), "x_2" = c(1)) +states <- ode_solve(dimension=1, n=1000, F=F, initial_time=0, step_size=0.01, order=2, method="leapfrog", initial_conditions=initial_conditions, domains = list()) } diff --git a/src/Makevars b/src/Makevars index 4d2b7288..9fd194e7 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,16 +1,16 @@ -PKG_CPPFLAGS=-Iexternal -Iexternal/lpsolve/headers/run_headers -Iexternal/minimum_ellipsoid -Iinclude -Iinclude/convex_bodies/spectrahedra +PKG_CPPFLAGS=-Ivolesti/external -Ilpsolve/headers/run_headers -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra PKG_CXXFLAGS= -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve -Lexternal/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-Llpsolve/build/lp_solve -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): external/lpsolve/build/lp_solve/liblp_solve.a external/PackedCSparse/qd/libqd.a +$(SHLIB): lpsolve/build/lp_solve/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a -external/lpsolve/build/lp_solve/liblp_solve.a: - @(cd external/lpsolve/build/lp_solve && $(MAKE) liblp_solve.a \ +lpsolve/build/lp_solve/liblp_solve.a: + @(cd lpsolve/build/lp_solve && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" RANLIB="$(RANLIB)") -external/PackedCSparse/qd/libqd.a: - @(cd external/PackedCSparse/qd && $(MAKE) libqd.a \ +volesti/external/PackedCSparse/qd/libqd.a: + @(cd volesti/external/PackedCSparse/qd && $(MAKE) libqd.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)") diff --git a/src/Makevars.win b/src/Makevars.win index dbd3bac5..093db7e9 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,17 +1,17 @@ -PKG_CPPFLAGS=-Iexternal -Iexternal/lpsolve/headers/run_headers -Iexternal/minimum_ellipsoid -Iinclude -Iinclude/convex_bodies/spectrahedra +PKG_CPPFLAGS=-Ivolesti/external -Ilpsolve/headers/run_headers -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra PKG_CXXFLAGS= -lm -ldl -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve -Lexternal/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-Llpsolve/build/lp_solve -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): external/lpsolve/build/lp_solve/liblp_solve.a external/PackedCSparse/qd/libqd.a +$(SHLIB): lpsolve/build/lp_solve/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a -external/lpsolve/build/lp_solve/liblp_solve.a: - @(cd external/lpsolve/build/lp_solve && $(MAKE) liblp_solve.a \ +lpsolve/build/lp_solve/liblp_solve.a: + @(cd lpsolve/build/lp_solve && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS) -DUSRDLL -DINLINE=static" \ CFLAGS="$(CFLAGS)" CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" \ RANLIB="$(RANLIB)") -external/PackedCSparse/qd/libqd.a: - @(cd external/PackedCSparse/qd && $(MAKE) libqd.a \ +volesti/external/PackedCSparse/qd/libqd.a: + @(cd volesti/external/PackedCSparse/qd && $(MAKE) libqd.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)") \ No newline at end of file diff --git a/src/external/ChebTools/ChebTools.cpp b/src/external/ChebTools/ChebTools.cpp deleted file mode 100644 index 2813822f..00000000 --- a/src/external/ChebTools/ChebTools.cpp +++ /dev/null @@ -1,892 +0,0 @@ -//Copyright 2018* United States Secretary of Commerce, NIST. -// -//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// -//The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Credit to https://github.com/usnistgov/ChebTools - -#include "ChebTools/ChebTools.h" -#include "Eigen/Dense" -#include - -#include -#include - -#include -#include -#include -#include - -#ifndef DBL_EPSILON -#define DBL_EPSILON std::numeric_limits::epsilon() -#endif - -#ifndef DBL_MAX -#define DBL_MAX std::numeric_limits::max() -#endif - -template T POW2(T x){ return x*x; } -template bool inbetween(T bound1, T bound2, T val){ return val > std::min(bound1,bound2) && val < std::max(bound1,bound2); } - -/// See python code in https://en.wikipedia.org/wiki/Binomial_coefficient#Binomial_coefficient_in_programming_languages -/// This is a direct translation of that code to C++ -double binomialCoefficient(const double n, const double k) { - if (k < 0 || k > n) { - return 0; - } - if (k == 0 || k == n) { - return 1; - } - double _k = std::min(k, n - k); //# take advantage of symmetry - double c = 1; - for (double i = 0; i < _k; ++i) { - c *= (n - i) / (i + 1); - } - return c; -} - -namespace ChebTools { - - inline bool ValidNumber(double x){ - // Idea from http://www.johndcook.com/IEEE_exceptions_in_cpp.html - return (x <= DBL_MAX && x >= -DBL_MAX); - }; - - void balance_matrix(const Eigen::MatrixXd &A, Eigen::MatrixXd &Aprime, Eigen::MatrixXd &D) { - // https://arxiv.org/pdf/1401.5766.pdf (Algorithm #3) - const int p = 2; - double beta = 2; // Radix base (2?) - int iter = 0; - Aprime = A; - D = Eigen::MatrixXd::Identity(A.rows(), A.cols()); - bool converged = false; - do { - converged = true; - for (Eigen::Index i = 0; i < A.rows(); ++i) { - double c = Aprime.col(i).lpNorm

(); - double r = Aprime.row(i).lpNorm

(); - double s = pow(c, p) + pow(r, p); - double f = 1; - //if (!ValidNumber(c)){ - // std::cout << A << std::endl; - // throw std::range_error("c is not a valid number in balance_matrix"); } - //if (!ValidNumber(r)) { throw std::range_error("r is not a valid number in balance_matrix"); } - while (c < r/beta) { - c *= beta; - r /= beta; - f *= beta; - } - while (c >= r*beta) { - c /= beta; - r *= beta; - f /= beta; - } - if (pow(c, p) + pow(r, p) < 0.95*s) { - converged = false; - D(i, i) *= f; - Aprime.col(i) *= f; - Aprime.row(i) /= f; - } - } - iter++; - if (iter > 50) { - break; - } - } while (!converged); - } - - /** - * @brief A library that stores the Chebyshev-Lobatto nodes in the domain [-1,1] - * @note The Chebyshev-Lobatto nodes are a function of degree, but not of the coefficients - */ - class ChebyshevLobattoNodesLibrary { - private: - std::map vectors; - void build(std::size_t N) { - double NN = static_cast(N); // just a cast - vectors[N] = (Eigen::VectorXd::LinSpaced(N + 1, 0, NN).array()*EIGEN_PI / N).cos(); - } - public: - /// Get the Chebyshev-Lobatto nodes for expansion of degree \f$N\f$ - const Eigen::VectorXd & get(std::size_t N) { - auto it = vectors.find(N); - if (it != vectors.end()) { - return it->second; - } - else { - build(N); - return vectors.find(N)->second; - } - } - }; - static ChebyshevLobattoNodesLibrary CLnodes_library; - const Eigen::VectorXd &get_CLnodes(std::size_t N){ - return CLnodes_library.get(N); - } - - // DEPRECATED: but held around for future reference - //class ChebyshevRootsLibrary { - //private: - // std::map vectors; - // void build(std::size_t N) { - // double NN = static_cast(N); // just a cast - // vectors[N] = ((Eigen::VectorXd::LinSpaced(N, 0, NN - 1).array() + 0.5)*EIGEN_PI / NN).cos(); - // } - //public: - // const Eigen::VectorXd & get(std::size_t N) { - // auto it = vectors.find(N); - // if (it != vectors.end()) { - // return it->second; - // } - // else { - // build(N); - // return vectors.find(N)->second; - // } - // } - //}; - //static ChebyshevRootsLibrary roots_library; - - // From CoolProp - template bool is_in_closed_range(T x1, T x2, T x) { return (x >= std::min(x1, x2) && x <= std::max(x1, x2)); }; - - ChebyshevExpansion ChebyshevExpansion::operator+(const ChebyshevExpansion &ce2) const { - if (m_c.size() == ce2.coef().size()) { - // Both are the same size, nothing creative to do, just add the coefficients - return ChebyshevExpansion(std::move(ce2.coef() + m_c), m_xmin, m_xmax); - } - else{ - if (m_c.size() > ce2.coef().size()) { - Eigen::VectorXd c(m_c.size()); c.setZero(); c.head(ce2.coef().size()) = ce2.coef(); - return ChebyshevExpansion(c+m_c, m_xmin, m_xmax); - } - else { - std::size_t n = ce2.coef().size(); - Eigen::VectorXd c(n); c.setZero(); c.head(m_c.size()) = m_c; - return ChebyshevExpansion(c + ce2.coef(), m_xmin, m_xmax); - } - } - }; - ChebyshevExpansion& ChebyshevExpansion::operator+=(const ChebyshevExpansion &donor) { - std::size_t Ndonor = donor.coef().size(), N1 = m_c.size(); - std::size_t Nmin = std::min(N1, Ndonor), Nmax = std::max(N1, Ndonor); - // The first Nmin terms overlap between the two vectors - m_c.head(Nmin) += donor.coef().head(Nmin); - // If the donor vector is longer than the current vector, resizing is needed - if (Ndonor > N1) { - // Resize but leave values as they were - m_c.conservativeResize(Ndonor); - // Copy the last Nmax-Nmin values from the donor - m_c.tail(Nmax - Nmin) = donor.coef().tail(Nmax - Nmin); - } - return *this; - } - ChebyshevExpansion& ChebyshevExpansion::operator-=(const ChebyshevExpansion& donor) { - std::size_t Ndonor = donor.coef().size(), N1 = m_c.size(); - std::size_t Nmin = std::min(N1, Ndonor), Nmax = std::max(N1, Ndonor); - // The first Nmin terms overlap between the two vectors - m_c.head(Nmin) -= donor.coef().head(Nmin); - // If the donor vector is longer than the current vector, resizing is needed - if (Ndonor > N1) { - // Resize but leave values as they were - m_c.conservativeResize(Ndonor); - // Copy the last Nmax-Nmin values from the donor - m_c.tail(Nmax - Nmin) = -donor.coef().tail(Nmax - Nmin); - } - return *this; - } - ChebyshevExpansion ChebyshevExpansion::operator-(const ChebyshevExpansion& ce2) const { - if (m_c.size() == ce2.coef().size()) { - // Both are the same size, nothing creative to do, just subtract the coefficients - return ChebyshevExpansion(std::move(ce2.coef() - m_c), m_xmin, m_xmax); - } - else { - if (m_c.size() > ce2.coef().size()) { - Eigen::VectorXd c(m_c.size()); c.setZero(); c.head(ce2.coef().size()) = ce2.coef(); - return ChebyshevExpansion(m_c - c, m_xmin, m_xmax); - } - else { - std::size_t n = ce2.coef().size(); - Eigen::VectorXd c(n); c.setZero(); c.head(m_c.size()) = m_c; - return ChebyshevExpansion(c - ce2.coef(), m_xmin, m_xmax); - } - } - }; - ChebyshevExpansion ChebyshevExpansion::operator*(double value) const { - return ChebyshevExpansion(m_c*value, m_xmin, m_xmax); - } - ChebyshevExpansion ChebyshevExpansion::operator+(double value) const { - Eigen::VectorXd c = m_c; - c(0) += value; - return ChebyshevExpansion(c, m_xmin, m_xmax); - } - ChebyshevExpansion ChebyshevExpansion::operator-(double value) const { - Eigen::VectorXd c = m_c; - c(0) -= value; - return ChebyshevExpansion(c, m_xmin, m_xmax); - } - ChebyshevExpansion ChebyshevExpansion::operator-() const{ - Eigen::VectorXd c = m_c; - return ChebyshevExpansion(-c, m_xmin, m_xmax); - } - ChebyshevExpansion& ChebyshevExpansion::operator*=(double value) { - m_c *= value; - return *this; - } - ChebyshevExpansion& ChebyshevExpansion::operator+=(double value) { - m_c(0) += value; - return *this; - } - ChebyshevExpansion& ChebyshevExpansion::operator-=(double value) { - m_c(0) -= value; - return *this; - } - ChebyshevExpansion ChebyshevExpansion::operator*(const ChebyshevExpansion &ce2) const { - - std::size_t order1 = this->m_c.size()-1, - order2 = ce2.coef().size()-1; - // The order of the product is the sum of the orders of the two expansions - std::size_t Norder_product = order1 + order2; - - // Create padded vectors, and copy into them the coefficients from this instance - // and that of the donor - Eigen::VectorXd a = Eigen::VectorXd::Zero(Norder_product+1), - b = Eigen::VectorXd::Zero(Norder_product+1); - a.head(order1+1) = this->m_c; b.head(order2+1) = ce2.coef(); - - // Get the matrices U and V from the libraries - const Eigen::MatrixXd &U = u_matrix_library.get(Norder_product); - const Eigen::MatrixXd &V = l_matrix_library.get(Norder_product); - - // Carry out the calculation of the final coefficients - // U*a is the functional values at the Chebyshev-Lobatto nodes for the first expansion - // U*b is the functional values at the Chebyshev-Lobatto nodes for the second expansion - // The functional values are multiplied together in an element-wise sense - this is why both products are turned into arrays - // The pre-multiplication by V takes us back to coefficients - return ChebyshevExpansion(V*((U*a).array() * (U*b).array()).matrix(), m_xmin, m_xmax); - }; - ChebyshevExpansion ChebyshevExpansion::times_x() const { - // First we treat the of chi*A multiplication in the domain [-1,1] - Eigen::Index N = m_c.size()-1; // N is the order of A - Eigen::VectorXd cc(N+2); // Order of x*A is one higher than that of A - if (N > 1) { - cc(0) = m_c(1)/2.0; - } - if (N > 2) { - cc(1) = m_c(0) + m_c(2)/2.0; - } - for (Eigen::Index i = 2; i < cc.size(); ++i) { - cc(i) = (i+1 <= N) ? 0.5*(m_c(i-1) + m_c(i+1)) : 0.5*(m_c(i - 1)); - } - // Scale the values into the real world, which is given by - // C_scaled = (b-a)/2*(chi*A) + ((b+a)/2)*A - // where the coefficients in the second term need to be padded with a zero to have - // the same order as the product of x*A - Eigen::VectorXd c_padded(N+2); c_padded << m_c, 0; - Eigen::VectorXd coefs = (((m_xmax - m_xmin)/2.0)*cc).array() + (m_xmax + m_xmin)/2.0*c_padded.array(); - return ChebyshevExpansion(coefs, m_xmin, m_xmax); - }; - ChebyshevExpansion& ChebyshevExpansion::times_x_inplace() { - Eigen::Index N = m_c.size() - 1; // N is the order of A - double diff = ((m_xmax - m_xmin) / 2.0), plus = (m_xmax + m_xmin) / 2.0; - double cim1old = 0, ciold = 0; - m_c.conservativeResize(N+2); - m_c(N+1) = 0.0; // Fill the last entry with a zero - if (N > 1) { - // 0-th element - cim1old = m_c(0); // store the current 0-th element in temporary variable - m_c(0) = diff*(0.5*m_c(1)) + plus*m_c(0); - } - if (N > 2) { - // 1-th element - ciold = m_c(1); // store the current 1-th element in temporary variable - m_c(1) = diff*(cim1old + 0.5*m_c(2)) + plus*m_c(1); - cim1old = ciold; - } - for (Eigen::Index i = 2; i <= N-1; ++i) { - ciold = m_c(i); // store the current i-th element in temporary variable - m_c(i) = diff*(0.5*(cim1old + m_c(i + 1)))+plus*m_c(i); - cim1old = ciold; - } - for (Eigen::Index i = N; i <= N + 1; ++i) { - ciold = m_c(i); // store the current i-th element in temporary variable - m_c(i) = diff*(0.5*cim1old) + plus*m_c(i); - cim1old = ciold; - } - return *this; - }; - ChebyshevExpansion ChebyshevExpansion::reciprocal() const{ - // 1. Transform Chebyshev-Lobatto node function values by the function f(y) -> 1/y - // 2. Go backwards to coefficients from node values c2 = V/y - const auto Ndegree = m_c.size() - 1; - const Eigen::MatrixXd& V = l_matrix_library.get(Ndegree); - // Values at the nodes in the x range of [-1, 1] - Eigen::VectorXd c = V*(1.0/get_node_function_values().array()).matrix(); - - return ChebyshevExpansion(c, xmin(), xmax()); - } - ChebyshevExpansion ChebyshevExpansion::apply(std::function &f) const{ - // 1. Transform Chebyshev-Lobatto node function values by the function f(y) -> y2 - // 2. Go backwards to coefficients from node values c2 = V*y2 - const auto Ndegree = m_c.size()-1; - const Eigen::MatrixXd &V = l_matrix_library.get(Ndegree); - return ChebyshevExpansion(V*f(get_node_function_values()).matrix(), xmin(), xmax()); - } - bool ChebyshevExpansion::is_monotonic() const { - auto yvals = get_node_function_values(); - auto N = yvals.size(); - Eigen::ArrayXd diff = yvals.tail(N - 1) - yvals.head(N - 1); - return (diff < 0.0).all() || (diff > 0.0).all(); - } - - const vectype &ChebyshevExpansion::coef() const { - return m_c; - }; - /** - * @brief Do a single input/single output evaluation of the Chebyshev expansion with the inputs scaled in [xmin, xmax] - * @param x A value scaled in the domain [xmin,xmax] - */ - double ChebyshevExpansion::y_recurrence(const double x) { - // Use the recurrence relationships to evaluate the Chebyshev expansion - std::size_t Norder = m_c.size() - 1; - // Scale x linearly into the domain [-1, 1] - double xscaled = (2 * x - (m_xmax + m_xmin)) / (m_xmax - m_xmin); - // Short circuit if not using recursive solution - if (Norder == 0){ return m_c[0]; } - if (Norder == 1) { return m_c[0] + m_c[1]*xscaled; } - - vectype &o = m_recurrence_buffer; - o(0) = 1; - o(1) = xscaled; - for (int n = 1; n < Norder; ++n) { - o(n + 1) = 2 * xscaled*o(n) - o(n - 1); - } - return m_c.dot(o); - } - double ChebyshevExpansion::y_Clenshaw_xscaled(const double xscaled) const { - // See https://en.wikipedia.org/wiki/Clenshaw_algorithm#Special_case_for_Chebyshev_series - std::size_t Norder = m_c.size() - 1; - double u_k = 0, u_kp1 = m_c[Norder], u_kp2 = 0; - int k = 0; - for (k = static_cast(Norder) - 1; k >= 1; --k) { - // Do the recurrent calculation - u_k = 2.0 * xscaled * u_kp1 - u_kp2 + m_c(k); - // Update the values - u_kp2 = u_kp1; u_kp1 = u_k; - } - return m_c(0) + xscaled * u_kp1 - u_kp2; - } - /** - * @brief Do a vectorized evaluation of the Chebyshev expansion with the inputs scaled in [xmin, xmax] - * @param x A vectype of values in the domain [xmin,xmax] - */ - vectype ChebyshevExpansion::y(const vectype &x) const { - // Scale x linearly into the domain [-1, 1] - const vectype xscaled = (2 * x.array() - (m_xmax + m_xmin)) / (m_xmax - m_xmin); - // Then call the function that takes the scaled x values - return y_recurrence_xscaled(xscaled); - } - /** - * @brief Do a vectorized evaluation of the Chebyshev expansion with the input scaled in the domain [-1,1] - * @param xscaled A vectype of values scaled to the domain [-1,1] (the domain of the Chebyshev basis functions) - * @returns y A vectype of values evaluated from the expansion - * - * By using vectorizable types like Eigen::MatrixXd, without - * any additional work, "magical" vectorization is happening - * under the hood, giving a significant speed improvement. From naive - * testing, the increase was a factor of about 10x. - */ - vectype ChebyshevExpansion::y_recurrence_xscaled(const vectype &xscaled) const { - const std::size_t Norder = m_c.size() - 1; - - Eigen::MatrixXd A(xscaled.size(), Norder + 1); - - if (Norder == 0) { return m_c[0]*Eigen::MatrixXd::Ones(A.rows(), A.cols()); } - if (Norder == 1) { return m_c[0] + m_c[1]*xscaled.array(); } - - // Use the recurrence relationships to evaluate the Chebyshev expansion - // In this case we do column-wise evaluations of the recurrence rule - A.col(0).fill(1); - A.col(1) = xscaled; - for (int n = 1; n < Norder; ++n) { - A.col(n + 1).array() = 2 * xscaled.array()*A.col(n).array() - A.col(n - 1).array(); - } - // In this form, the matrix-vector product will yield the y values - return A*m_c; - } - vectype ChebyshevExpansion::y_Clenshaw_xscaled(const vectype &xscaled) const { - const std::size_t Norder = m_c.size() - 1; - vectype u_k, u_kp1(xscaled.size()), u_kp2(xscaled.size()); - u_kp1.fill(m_c[Norder]); u_kp2.fill(0); - for (int k = static_cast(Norder) - 1; k >= 1; --k) { - u_k = 2 * xscaled.array()*u_kp1.array() - u_kp2.array() + m_c(k); - // Update summation values for all but the last step - if (k > 1) { - u_kp2 = u_kp1; u_kp1 = u_k; - } - } - return xscaled.array()*u_k.array() - u_kp1.array() + m_c(0); - } - - Eigen::MatrixXd ChebyshevExpansion::companion_matrix(const Eigen::VectorXd &coeffs) const { - Eigen::VectorXd new_mc = reduce_zeros(coeffs); - std::size_t Norder = new_mc.size() - 1; - Eigen::MatrixXd A = Eigen::MatrixXd::Zero(Norder, Norder); - // c_wrap wraps the first 0...Norder elements of the coefficient vector - Eigen::Map c_wrap(&(new_mc[0]), Norder); - // First row - A(0, 1) = 1; - // Last row - A.row(Norder - 1) = -c_wrap / (2.0*new_mc(Norder)); - A(Norder - 1, Norder - 2) += 0.5; - // All the other rows - for (int j = 1; j < Norder - 1; ++j) { - A(j, j - 1) = 0.5; - A(j, j + 1) = 0.5; - } - return A; - } - std::vector ChebyshevExpansion::real_roots2(bool only_in_domain) const { - //vector of roots to be returned - std::vector roots; - - auto N = m_c.size()-1; - auto Ndegree_scaled = N*2; - Eigen::VectorXd xscaled = get_CLnodes(Ndegree_scaled), yy = y_Clenshaw_xscaled(xscaled); - - // a,b,c can also be obtained by solving the matrix system: - // [x_k^2, x_k, 1] = [b_k] for k in 1,2,3 - for (auto i = 0; i+2 < Ndegree_scaled+1; i += 2){ - const double &x_1 = xscaled[i + 0], &y_1 = yy[i + 0], - &x_2 = xscaled[i + 1], &y_2 = yy[i + 1], - &x_3 = xscaled[i + 2], &y_3 = yy[i + 2]; - double d = (x_3 - x_2)*(x_2 - x_1)*(x_3 - x_1); - double a = ((x_3 - x_2)*y_1 - (x_3 - x_1)*y_2 + (x_2 - x_1)*y_3) / d; - double b = (-(POW2(x_3) - POW2(x_2))*y_1 + (POW2(x_3) - POW2(x_1))*y_2 - (POW2(x_2) - POW2(x_1))*y_3) / d; - double c = ((x_3 - x_2)*x_2*x_3*y_1 - (x_3 - x_1)*x_1*x_3*y_2 + (x_2 - x_1)*x_2*x_1*y_3) / d; - - // Discriminant of quadratic - double D = b*b - 4*a*c; - if (D >= 0) { - double root1, root2; - if (a == 0){ - // Linear - root1 = -c/b; - root2 = -1000; // Something outside the domain; we are in scaled coordinates so this will definitely get rejected - } - else if (D == 0) { // Unlikely due to numerical precision - // Two equal real roots - root1 = -b/(2*a); - root2 = -1000; // Something outside the domain; we are in scaled coordinates so this will definitely get rejected - } - else { - // Numerically stable method for solving quadratic - // From https://people.csail.mit.edu/bkph/articles/Quadratics.pdf - double sqrtD = sqrt(D); - if (b >= 0){ - root1 = (-b - sqrtD)/(2*a); - root2 = 2*c/(-b-sqrtD); - } - else { - root1 = 2*c/(-b+sqrtD); - root2 = (-b+sqrtD)/(2*a); - } - } - bool in1 = inbetween(x_1, x_3, root1), in2 = inbetween(x_1, x_3, root2); - const ChebyshevExpansion &e = *this; - auto secant = [e](double a, double ya, double b, double yb, double yeps = 1e-14, double xeps = 1e-14) { - auto c = b - yb*(b - a) / (yb - ya); - auto yc = e.y_Clenshaw_xscaled(c); - for (auto i = 0; i < 50; ++i){ - if (yc*ya > 0) { - a=c; ya=yc; - } - else { - b=c; yb=yc; - } - if (std::abs(b - a) < xeps) { break; } - if (std::abs(yc) < yeps){ break; } - c = b - yb*(b - a) / (yb - ya); - yc = e.y_Clenshaw_xscaled(c); - } - return c; - }; - int Nroots_inside = static_cast(in1) + static_cast(in2); - if (Nroots_inside == 2) { - // Split the domain at the midline of the quadratic, polish each root against the underlying expansion - double x_m = -b/(2*a), y_m = e.y_Clenshaw_xscaled(x_m); - root1 = secant(x_1, y_1, x_m, y_m); - root2 = secant(x_m, y_m, x_3, y_3); - // Rescale back into real-world values in [xmin,xmax] from [-1,1] - roots.push_back(((m_xmax - m_xmin)*root1 + (m_xmax + m_xmin)) / 2.0); - roots.push_back(((m_xmax - m_xmin)*root2 + (m_xmax + m_xmin)) / 2.0); - } - else if(Nroots_inside == 1) { - root1 = secant(x_1, y_1, x_3, y_3); - roots.push_back(((m_xmax - m_xmin)*root1 + (m_xmax + m_xmin)) / 2.0); - } - else {} - } - } - return roots; - } - std::vector ChebyshevExpansion::real_roots(bool only_in_domain) const { - //vector of roots to be returned - std::vector roots; - Eigen::VectorXd new_mc = reduce_zeros(m_c); - //if the Chebyshev polynomial is just a constant, then there are no roots - //if a_0=0 then there are infinite roots, but for our purposes, infinite roots doesnt make sense - if (new_mc.size()<=1){ //we choose <=1 to account for the case of no coefficients - return roots; //roots is empty - } - - //if the Chebyshev polynomial is linear, then the only possible root is -a_0/a_1 - //we have this else if block because eigen is not a fan of 1x1 matrices - else if (new_mc.size()==2){ - double val_n11 = -new_mc(0)/new_mc(1); - const bool is_in_domain = (val_n11 >= -1.0 && val_n11 <= 1.0); - // Keep it if it is in domain, or if you just want all real roots - if (!only_in_domain || is_in_domain) { - // Rescale back into real-world values in [xmin,xmax] from [-1,1] - double x = ((m_xmax - m_xmin)*val_n11 + (m_xmax + m_xmin)) / 2.0; - roots.push_back(x); - } - } - - //this for all cases of higher order polynomials - else{ - // The companion matrix is definitely lower Hessenberg, so we can skip the Hessenberg - // decomposition, and get the real eigenvalues directly. These eigenvalues are defined - // in the domain [-1, 1], but it might also include values outside [-1, 1] - Eigen::VectorXcd eigs = eigenvalues(companion_matrix(new_mc), /* balance = */ true); - - - for (Eigen::Index i = 0; i < eigs.size(); ++i) { - if (std::abs(eigs(i).imag() / eigs(i).real()) < 1e-15) { - double val_n11 = eigs(i).real(); - const bool is_in_domain = (val_n11 >= -1.0 && val_n11 <= 1.0); - // Keep it if it is in domain, or if you just want all real roots - if (!only_in_domain || is_in_domain) { - // Rescale back into real-world values in [xmin,xmax] from [-1,1] - double x = ((m_xmax - m_xmin)*val_n11 + (m_xmax + m_xmin)) / 2.0; - roots.push_back(x); - } - } - } - } - return roots; - - //// The companion matrix is definitely lower Hessenberg, so we can skip the Hessenberg - //// decomposition, and get the real eigenvalues directly. These eigenvalues are defined - //// in the domain [-1, 1], but it might also include values outside [-1, 1] - //Eigen::VectorXd real_eigs = eigenvalues_upperHessenberg(companion_matrix().transpose(), /* balance = */ true); - // - //std::vector roots; - //for (Eigen::Index i = 0; i < real_eigs.size(); ++i){ - // double val_n11 = real_eigs(i); - // const bool is_in_domain = (val_n11 >= -1.0 && val_n11 <= 1.0); - // // Keep it if it is in domain, or if you just want all real roots - // if (!only_in_domain || is_in_domain){ - // // Rescale back into real-world values in [xmin,xmax] from [-1,1] - // double x = ((m_xmax - m_xmin)*val_n11 + (m_xmax + m_xmin)) / 2.0; - // roots.push_back(x); - // } - //} - //return roots; - } - double ChebyshevExpansion::monotonic_solvex(double y) { - /* - Function is known to be monotonic, so we can shortcut some of the solving steps used - We don't use the eigenvalue method because it is too slow - */ - auto& e = *this; - auto secant = [e,y](double a, double ya, double b, double yb, double yeps = 1e-14, double xeps = 1e-14) { - double c, yc; - for (auto i = 0; i < 50; ++i) { - c = b - yb * (b - a) / (yb - ya); - yc = e.y_Clenshaw_xscaled(c)-y; - if (yc * ya > 0) { - a = c; ya = yc; - } - else { - b = c; yb = yc; - } - if (std::abs(b - a) < xeps) { break; } - if (std::abs(yc) < yeps) { break; } - } - return c; - }; - // Determine if the function is monotonically increasing or decreasing - auto ynodes = get_node_function_values(); - auto nodes = get_nodes_n11(); - bool increasing = ynodes[ynodes.size() - 1] > ynodes[0]; // Nodes go from 1 to -1 (stuck with this), but increasing says whether the value at the last *index* (x=-1) is greater than that of the first index (x=1). - if (increasing) { - if (y > ynodes[ynodes.size() - 1]) { - throw std::invalid_argument("Argument is outside the range of the expansion"); - } - if (y < ynodes[0]) { - throw std::invalid_argument("Argument is outside the range of the expansion"); - } - } - else { - if (y < ynodes[ynodes.size() - 1]) { - throw std::invalid_argument("Argument is outside the range of the expansion"); - } - if (y > ynodes[0]) { - throw std::invalid_argument("Argument is outside the range of the expansion"); - } - } - - // Interval bisection to find the Chebyshev-Lobatto nodes that bound the solution by bisection - int N = static_cast(ynodes.size()); - Eigen::Index i = (increasing) ? get_increasingleftofval(ynodes, y, N) : get_decreasingleftofval(ynodes, y, N); - auto xscaled = secant(nodes(i), ynodes(i)-y, nodes(i + 1), ynodes(i + 1)-y); - return unscale_x(xscaled); - } - - std::vector ChebyshevExpansion::subdivide(std::size_t Nintervals, const std::size_t Norder) const { - - if (Nintervals == 1) { - return std::vector(1, *this); - } - - std::vector segments; - double deltax = (m_xmax - m_xmin) / (Nintervals - 1); - - // Vector of values in the range [-1,1] as roots of a high-order Chebyshev - double NN = static_cast(Norder); - Eigen::VectorXd xpts_n11 = (Eigen::VectorXd::LinSpaced(Norder + 1, 0, NN)*EIGEN_PI/NN).array().cos(); - - for (std::size_t i = 0; i < Nintervals - 1; ++i) { - double xmin = m_xmin + i*deltax, xmax = m_xmin + (i + 1)*deltax; - Eigen::VectorXd xrealworld = ((xmax - xmin)*xpts_n11.array() + (xmax + xmin)) / 2.0; - segments.push_back(factoryf(Norder, y(xrealworld), xmin, xmax)); - } - return segments; - } - std::vector ChebyshevExpansion::real_roots_intervals(const std::vector &segments, bool only_in_domain) { - std::vector roots; - for (auto &seg : segments) { - const auto segroots = seg.real_roots(only_in_domain); - roots.insert(roots.end(), segroots.cbegin(), segroots.cend()); - } - return roots; - } - std::vector ChebyshevExpansion::real_roots_approx(long Npoints) - { - std::vector roots; - // Vector of values in the range [-1,1] as roots of a high-order Chebyshev - Eigen::VectorXd xpts_n11 = (Eigen::VectorXd::LinSpaced(Npoints + 1, 0, Npoints)*EIGEN_PI / Npoints).array().cos(); - // Scale values into real-world values - Eigen::VectorXd ypts = y_recurrence_xscaled(xpts_n11); - // Eigen::MatrixXd buf(Npoints+1, 2); buf.col(0) = xpts; buf.col(1) = ypts; std::cout << buf << std::endl; - for (size_t i = 0; i < Npoints - 1; ++i) { - // The change of sign guarantees at least one root between indices i and i+1 - double y1 = ypts(i), y2 = ypts(i + 1); - bool signchange = (std::signbit(y1) != std::signbit(y2)); - if (signchange) { - double xscaled = xpts_n11(i); - - // Fit a quadratic given three points; i and i+1 bracket the root, so need one more constraint - // i0 is the leftmost of the three indices that will be used; when i == 0, use - // indices i,i+1,i+2, otherwise i-1,i,i+1 - size_t i0 = (i >= 1) ? i - 1 : i; - Eigen::Vector3d r; - r << ypts(i0), ypts(i0 + 1), ypts(i0 + 2); - Eigen::Matrix3d A; - for (std::size_t irow = 0; irow < 3; ++irow) { - double _x = xpts_n11(i0 + irow); - A.row(irow) << _x*_x, _x, 1; - } - // abc holds the coefficients a,b,c for y = a*x^2 + b*x + c - Eigen::VectorXd abc = A.colPivHouseholderQr().solve(r); - double a = abc[0], b = abc[1], c = abc[2]; - - // Solve the quadratic and find the root you want - double x1 = (-b + sqrt(b*b - 4 * a*c)) / (2 * a); - double x2 = (-b - sqrt(b*b - 4 * a*c)) / (2 * a); - bool x1_in_range = is_in_closed_range(xpts_n11(i), xpts_n11(i + 1), x1); - bool x2_in_range = is_in_closed_range(xpts_n11(i), xpts_n11(i + 1), x2); - - // Double check that only one root is within the range - if (x1_in_range && !x2_in_range) { - xscaled = x1; - } - else if (x2_in_range && !x1_in_range) { - xscaled = x2; - } - else { - xscaled = 1e99; - } - - // Rescale back into real-world values - double x = ((m_xmax - m_xmin)*xscaled + (m_xmax + m_xmin)) / 2.0; - roots.push_back(x); - } - else { - // TODO: locate other roots based on derivative considerations - } - } - return roots; - } - - /// Chebyshev-Lobatto nodes \f$ \cos(\pi j/N), j = 0,..., N \f$ in the range [-1,1] - Eigen::VectorXd ChebyshevExpansion::get_nodes_n11() { - std::size_t N = m_c.size()-1; - return CLnodes_library.get(N); - } - /// Chebyshev-Lobatto nodes \f$\cos(\pi j/N), j = 0,..., N \f$ mapped to the range [xmin, xmax] - Eigen::VectorXd ChebyshevExpansion::get_nodes_realworld() { - return ((m_xmax - m_xmin)*get_nodes_n11().array() + (m_xmax + m_xmin))*0.5; - } - /// Values of the function at the Chebyshev-Lobatto nodes - Eigen::VectorXd ChebyshevExpansion::get_node_function_values() const{ - if (m_nodal_value_cache.size() > 0) { - return m_nodal_value_cache; - } - else { - std::size_t N = m_c.size() - 1; - return u_matrix_library.get(N) * m_c; - } - } - ChebyshevExpansion ChebyshevExpansion::factoryf(const std::size_t N, const Eigen::VectorXd &f, const double xmin, const double xmax) { - // Step 3: Get coefficients for the L matrix from the library of coefficients - const Eigen::MatrixXd &L = l_matrix_library.get(N); - // Step 4: Obtain coefficients from vector - matrix product - return ChebyshevExpansion(L*f, xmin, xmax); - } - ChebyshevExpansion ChebyshevExpansion::factoryfFFT(const std::size_t N, const Eigen::VectorXd& f, const double xmin, const double xmax) { - - Eigen::VectorXd valsUnitDisc(2 * f.size() - 2); - // Starting at x = 1, going to -1, then the same nodes, not including x=-1 and x=1, in the opposite order - valsUnitDisc.head(f.size()) = f; - valsUnitDisc.tail(f.size() - 2) = f.reverse().segment(1, f.size() - 2); - - Eigen::FFT fft; - Eigen::VectorXcd FourierCoeffs(2 * f.size() - 2); - fft.fwd(FourierCoeffs, valsUnitDisc); - auto n = f.size() - 1; - Eigen::ArrayXd ChebCoeffs = FourierCoeffs.real().head(n+1)/n; - ChebCoeffs[0] /= 2; - ChebCoeffs[ChebCoeffs.size()-1] /= 2; - - return ChebyshevExpansion(ChebCoeffs, xmin, xmax); - } - ChebyshevExpansion ChebyshevExpansion::from_powxn(const std::size_t n, const double xmin, const double xmax) { - if (xmin != -1) { - throw std::invalid_argument("xmin must be -1"); - } - if (xmax != 1) { - throw std::invalid_argument("xmax must be 1"); - } - Eigen::VectorXd c = Eigen::VectorXd::Zero(n + 1); - for (std::size_t k = 0; k <= n / 2; ++k) { - std::size_t index = n - 2 * k; - double coeff = binomialCoefficient(static_cast(n), static_cast(k)); - if (index == 0) { - coeff /= 2.0; - } - c(index) = coeff; - } - return pow(2, 1-static_cast(n))*ChebyshevExpansion(c, xmin, xmax); - } - ChebyshevExpansion ChebyshevExpansion::deriv(std::size_t Nderiv) const { - // See Mason and Handscomb, p. 34, Eq. 2.52 - // and example in https ://github.com/numpy/numpy/blob/master/numpy/polynomial/chebyshev.py#L868-L964 - vectype c = m_c; - for (std::size_t deriv_counter = 0; deriv_counter < Nderiv; ++deriv_counter) { - std::size_t N = c.size() - 1, ///< Order of the expansion - Nd = N - 1; ///< Order of the derivative expansion - vectype cd(N); - for (std::size_t r = 0; r <= Nd; ++r) { - cd(r) = 0; - for (std::size_t k = r + 1; k <= N; ++k) { - // Terms where k-r is odd have values, otherwise, they are zero - if ((k - r) % 2 == 1) { - cd(r) += 2*k*c(k); - } - } - // The first term with r = 0 is divided by 2 (the single prime in Mason and Handscomb, p. 34, Eq. 2.52) - if (r == 0) { - cd(r) /= 2; - } - // Rescale the values if the range is not [-1,1]. Arrives from the derivative of d(xreal)/d(x_{-1,1}) - cd(r) /= (m_xmax-m_xmin)/2.0; - } - if (Nderiv == 1) { - return ChebyshevExpansion(std::move(cd), m_xmin, m_xmax); - } - else{ - c = cd; - } - } - return ChebyshevExpansion(std::move(c), m_xmin, m_xmax); - }; - ChebyshevExpansion ChebyshevExpansion::integrate(std::size_t Nintegral) const { - // See Mason and Handscomb, p. 33, Eq. 2.44 & 2.45 - // and example in https ://github.com/numpy/numpy/blob/master/numpy/polynomial/chebyshev.py#L868-L964 - if (Nintegral != 1) { throw std::invalid_argument("Only support one integral for now"); } - vectype c(m_c.size() + 1); - double width = m_xmax - m_xmin; - for (auto i = 1; i < m_c.size()+1; ++i) { - if (i == 1) { - // This special case is needed because the prime on the summation in Mason indicates the first coefficient - // is to be divided by two - c[i] = (2*m_c[i - 1] - m_c[i + 1]) / (2 * i); - } - else if (i + 1 > m_c.size()-1) { - c[i] = (m_c[i - 1]) / (2 * i); - } - else { - c[i] = (m_c[i - 1] - m_c[i + 1]) / (2 * i); - } - } - c(0) = 0; // This is the arbitrary constant; - c *= width / 2; - return ChebyshevExpansion(std::move(c), m_xmin, m_xmax); - } - - Eigen::VectorXd eigenvalues_upperHessenberg(const Eigen::MatrixXd &A, bool balance){ - Eigen::VectorXd roots(A.cols()); - Eigen::RealSchur schur; - - if (balance) { - Eigen::MatrixXd Abalanced, D; - balance_matrix(A, Abalanced, D); - schur.computeFromHessenberg(Abalanced, Eigen::MatrixXd::Zero(Abalanced.rows(), Abalanced.cols()), false); - } - else { - schur.computeFromHessenberg(A, Eigen::MatrixXd::Zero(A.rows(), A.cols()), false); - } - - const Eigen::MatrixXd &T = schur.matrixT(); - Eigen::Index j = 0; - for (int i = 0; i < T.cols(); ++i) { - if (i+1 < T.cols()-1 && std::abs(T(i+1,i)) > DBL_EPSILON){ - // Nope, this is a 2x2 block, keep moving - i += 1; - } - else{ - // This is a 1x1 block, keep this (real) eigenvalue - roots(j) = T(i, i); - j++; - } - } - roots.conservativeResize(j-1); - return roots; - } - - Eigen::VectorXcd eigenvalues(const Eigen::MatrixXd &A, bool balance) { - if (balance) { - Eigen::MatrixXd Abalanced, D; - balance_matrix(A, Abalanced, D); - return Abalanced.eigenvalues(); - } - else { - return A.eigenvalues(); - } - } - -}; /* namespace ChebTools */ diff --git a/src/external/ChebTools/ChebTools.h b/src/external/ChebTools/ChebTools.h deleted file mode 100644 index 816e273f..00000000 --- a/src/external/ChebTools/ChebTools.h +++ /dev/null @@ -1,565 +0,0 @@ -//Copyright 2018* United States Secretary of Commerce, NIST. -// -//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// -//The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Credit to https://github.com/usnistgov/ChebTools - -#ifndef CHEBTOOLS_H -#define CHEBTOOLS_H - -#include "Eigen/Dense" -#include -#include - -#include - - -namespace ChebTools{ - - // https://proquest.safaribooksonline.com/9780321637413 - // https://web.stanford.edu/class/archive/cs/cs107/cs107.1202/lab1/ - static int midpoint_Knuth(int x, int y) { - return (x & y) + ((x ^ y) >> 1); - }; - - - /** - * @brief This class stores sets of L matrices (because they are a function only of the degree of the expansion) - * - * The L matrix is used to convert from functional values to coefficients, as in \f[ \vec{c} = \mathbf{L}\vec{f} \f] - */ - class LMatrixLibrary { - private: - std::map matrices; - void build(std::size_t N) { - Eigen::MatrixXd L(N + 1, N + 1); ///< Matrix of coefficients - for (int j = 0; j <= N; ++j) { - for (int k = j; k <= N; ++k) { - double p_j = (j == 0 || j == N) ? 2 : 1; - double p_k = (k == 0 || k == N) ? 2 : 1; - L(j, k) = 2.0 / (p_j*p_k*N)*cos((j*EIGEN_PI*k) / N); - // Exploit symmetry to fill in the symmetric elements in the matrix - L(k, j) = L(j, k); - } - } - matrices[N] = L; - } - public: - /// Get the \f$\mathbf{L}\f$ matrix of degree N - const Eigen::MatrixXd & get(std::size_t N) { - auto it = matrices.find(N); - if (it != matrices.end()) { - return it->second; - } - else { - build(N); - return matrices.find(N)->second; - } - } - }; - static LMatrixLibrary l_matrix_library; - - /** - * @brief This class stores sets of U matrices (because they are a function only of the degree of the expansion) - * - * The U matrix is used to convert from coefficients to functional values, as in \f[ \vec{f} = \mathbf{U}\vec{c} \f] - */ - class UMatrixLibrary { - private: - std::map matrices; - void build(std::size_t N) { - Eigen::MatrixXd U(N + 1, N + 1); ///< Matrix of coefficients - for (int j = 0; j <= N; ++j) { - for (int k = j; k <= N; ++k) { - U(j, k) = cos((j*EIGEN_PI*k) / N); - // Exploit symmetry to fill in the symmetric elements in the matrix - U(k, j) = U(j, k); - } - } - matrices[N] = U; - } - public: - /// Get the \f$\mathbf{U}\f$ matrix of degree N - const Eigen::MatrixXd & get(std::size_t N) { - auto it = matrices.find(N); - if (it != matrices.end()) { - return it->second; - } - else { - build(N); - return matrices.find(N)->second; - } - } - }; - static UMatrixLibrary u_matrix_library; - - /** - For a monotonically increasing vector, find the left index of the interval bracketing the given value - */ - template - int get_increasingleftofval(const VecType& breakpoints, double x, int N) { - int iL = 0, iR = N - 1, iM; - while (iR - iL > 1) { - iM = midpoint_Knuth(iL, iR); - if (x >= breakpoints[iM]) { - iL = iM; - } - else { - iR = iM; - } - } - return iL; - }; - - /** - For a monotonically decreasing vector, find the left index of the interval bracketing the given value - */ - template - int get_decreasingleftofval(const VecType& breakpoints, double x, int N) { - int iL = 0, iR = N - 1, iM; - while (iR - iL > 1) { - iM = midpoint_Knuth(iL, iR); - if (x <= breakpoints[iM]) { - iL = iM; - } - else { - iR = iM; - } - } - return iL; - }; - - typedef Eigen::VectorXd vectype; - - /// Get the Chebyshev-Lobatto nodes for an expansion of degree \f$N\f$ - const Eigen::VectorXd &get_CLnodes(std::size_t N); - - Eigen::VectorXcd eigenvalues(const Eigen::MatrixXd &A, bool balance); - Eigen::VectorXd eigenvalues_upperHessenberg(const Eigen::MatrixXd &A, bool balance); - - /** - * @brief This is the main underlying object that makes all of the code of ChebTools work. - * - * This class has accessor methods for getting things from the object, and static factory - * functions for generating new expansions. It also has methods for calculating derivatives, - * roots, etc. - */ - class ChebyshevExpansion { - private: - vectype m_c; - double m_xmin, m_xmax; - - vectype m_recurrence_buffer; - vectype m_nodal_value_cache; - void resize() { - m_recurrence_buffer.resize(m_c.size()); - } - - //reduce_zeros changes the m_c field so that our companion matrix doesnt have nan values in it - //all this does is truncate m_c such that there are no trailing zero values - static Eigen::VectorXd reduce_zeros(const Eigen:: VectorXd &chebCoeffs){ - //these give us a threshold for what coefficients are large enough - double largeTerm = 1e-15; - if (chebCoeffs.size()>=1 && std::abs(chebCoeffs(0))>largeTerm){ - largeTerm = chebCoeffs(0); - } - //if the second coefficient is larger than the first, then make our tolerance - //based on the second coefficient, this is useful for functions whose mean value - //is zero on the interval - if (chebCoeffs.size()>=2 && std::abs(chebCoeffs(1))>largeTerm){ - largeTerm = chebCoeffs(1); - } - double tol = largeTerm*(1e-15); - int neededSize = static_cast(chebCoeffs.size()); - //loop over m_c backwards, if we run into large enough coefficient, then record the size and break - for (int i=static_cast(chebCoeffs.size())-1; i>=0; i--){ - if (std::abs(chebCoeffs(i))>tol){ - neededSize = i+1; - break; - } - neededSize--; - } - //neededSize gives us the number of coefficients that are nonzero - //we will resize m_c such that there are essentially no trailing zeros - return chebCoeffs.head(neededSize); - } - - public: - /// Initializer with coefficients, and optionally a range provided - ChebyshevExpansion(const vectype &c, double xmin = -1, double xmax = 1) : m_c(c), m_xmin(xmin), m_xmax(xmax) { resize(); }; - /// Initializer with coefficients, and optionally a range provided - ChebyshevExpansion(const std::vector &c, double xmin = -1, double xmax = 1) : m_xmin(xmin), m_xmax(xmax) { - m_c = Eigen::Map(&(c[0]), c.size()); - resize(); - }; - /// Move constructor (C++11 only) - ChebyshevExpansion(const vectype &&c, double xmin = -1, double xmax = 1) : m_c(c), m_xmin(xmin), m_xmax(xmax) { resize(); }; - - /// Cache nodal function values - void cache_nodal_function_values(vectype values) { - m_nodal_value_cache = values; - } - /// Get the minimum value of \f$x\f$ for the expansion - double xmin() const{ return m_xmin; } - /// Get the maximum value of \f$x\f$ for the expansion - double xmax() const{ return m_xmax; } - /// Go from a value in [xmin,xmax] to a value in [-1,1] - double scale_x(const double x) const { - return (2 * x - (m_xmax + m_xmin)) / (m_xmax - m_xmin); - } - /// Map from a value in [-1,1] to a value in [xmin,xmax] - double unscale_x(const double xscaled) const { - return ((m_xmax - m_xmin)*xscaled + (m_xmax + m_xmin))/2; - } - - /// Get the vector of coefficients in increasing order - const vectype &coef() const; - - /// Return the N-th derivative of this expansion, where N must be >= 1 - ChebyshevExpansion deriv(std::size_t Nderiv) const; - /// Return the indefinite integral of this function - ChebyshevExpansion integrate(std::size_t Nintegral = 1) const; - /// Get the Chebyshev-Lobatto nodes in the domain [-1,1] - Eigen::VectorXd get_nodes_n11(); - /// Get the Chebyshev-Lobatto nodes in the domain [-1,1]; thread-safe const variant - Eigen::VectorXd get_nodes_n11() const { - Eigen::Index N = m_c.size() - 1; - double NN = static_cast(N); - return (Eigen::VectorXd::LinSpaced(N + 1, 0, NN).array() * EIGEN_PI / N).cos(); - } - /// Get the Chebyshev-Lobatto nodes in the domain [xmin, xmax] - Eigen::VectorXd get_nodes_realworld(); - /// Get the Chebyshev-Lobatto nodes in the domain [xmin, xmax]; thread-safe const variant - Eigen::VectorXd get_nodes_realworld() const { - return ((m_xmax - m_xmin) * get_nodes_n11().array() + (m_xmax + m_xmin)) * 0.5; - } - - /// Values of the function at the Chebyshev-Lobatto nodes - Eigen::VectorXd get_node_function_values() const; - /// Return true if the function values at the Chebyshev-Lobatto nodes are monotonic with the independent variable - bool is_monotonic() const; - - // ****************************************************************** - // *********************** OPERATORS *********************** - // ****************************************************************** - - /// A ChebyshevExpansion plus another ChebyshevExpansion yields a new ChebyheveExpansion - ChebyshevExpansion operator+(const ChebyshevExpansion &ce2) const ; - /** - * @brief An inplace addition of two expansions - * @note The lower degree one is right-padded with zeros to have the same degree as the higher degree one - * @param donor The other expansion in the summation - */ - ChebyshevExpansion& operator+=(const ChebyshevExpansion &donor); - /// Multiplication of an expansion by a constant - ChebyshevExpansion operator*(double value) const; - /// Addition of a constant to an expansion - ChebyshevExpansion operator+(double value) const; - /// Subtraction of a constant from an expansion - ChebyshevExpansion operator-(double value) const; - /// An inplace multiplication of an expansion by a constant - ChebyshevExpansion& operator*=(double value); - /// An inplace addition of a constant to an expansion - ChebyshevExpansion& operator+=(double value); - /// An inplace subtraction of a constant from an expansion - ChebyshevExpansion& operator-=(double value); - /// Unary negation operator - ChebyshevExpansion operator-() const; - /// An inplace subtraction of an expansion by another expansion - ChebyshevExpansion& operator-=(const ChebyshevExpansion &ce2); - /// An inplace subtraction of an expansion by another expansion - ChebyshevExpansion operator-(const ChebyshevExpansion& ce2) const; - /** - * @brief Multiply two Chebyshev expansions together; thanks to Julia code from Bradley Alpert, NIST - * - * Converts padded expansions to nodal functional values, functional values are multiplied together, - * and then inverse transformation is used to return to coefficients of the product - * @param ce2 The other expansion - */ - ChebyshevExpansion operator*(const ChebyshevExpansion &ce2) const; - - /** - * @brief Divide two expansions by each other. Right's reciprocal is taken, multiplied by this expansion - * - * @param ce2 The other expansion - */ - ChebyshevExpansion operator/(const ChebyshevExpansion& ce2) const { - return (*this) * ce2.reciprocal(); - } - /** - * @brief Multiply a Chebyshev expansion by its independent variable \f$x\f$ - */ - ChebyshevExpansion times_x() const; - - /** - * @brief Multiply a Chebyshev expansion by its independent variable \f$x\f$ in-place - * - * This operation is carried out in-place to minimize the amount of memory re-allocation - * which proved during profiling to be a major source of inefficiency - */ - ChebyshevExpansion& times_x_inplace(); - - ChebyshevExpansion reciprocal() const; - - /// Friend function that allows for pre-multiplication by a constant value - friend ChebyshevExpansion operator*(double value, const ChebyshevExpansion &ce){ - return ChebyshevExpansion(std::move(ce.coef()*value),ce.m_xmin, ce.m_xmax); - }; - /// Friend function that allows expansion to be the denominator in division with double - friend ChebyshevExpansion operator/(double value, const ChebyshevExpansion& ce) { - return value * ce.reciprocal(); - }; - /// Friend function that allows pre-subtraction of expansion (value-expansion) - friend ChebyshevExpansion operator-(double value, const ChebyshevExpansion& ce) { - return -ce+value; - }; - /// Friend function that allows pre-addition of expansion (value+expansion) - friend ChebyshevExpansion operator+(double value, const ChebyshevExpansion& ce) { - return ce + value; - }; - - /** - * @brief Apply a function to the expansion - * - * This function first converts the expansion to functional values at the - * Chebyshev-Lobatto nodes, applies the function to the nodal values, and then - * does the inverse transformation to arrive at the coefficients of the expansion - * after applying the transformation - */ - ChebyshevExpansion apply(std::function &f) const; - - // ****************************************************************** - // ********************** EVALUATORS *********************** - // ****************************************************************** - - /** - * @brief Do a single input/single output evaluation of the Chebyshev expansion with the inputs scaled in [xmin, xmax] - * @param x A value scaled in the domain [xmin,xmax] - */ - double y_recurrence(const double x); - /** - * @brief Do a single input/single output evaluation of the Chebyshev expansion with the inputs scaled in [xmin, xmax] - * @param x A value scaled in the domain [xmin,xmax] - */ - double y_Clenshaw(const double x) const { return y_Clenshaw_xscaled(scale_x(x)); } - /** - * @brief Do a single input/single output evaluation of the Chebyshev expansion with the inputs scaled in [-1,1] - * @param x A value scaled in the domain [-1,1] - */ - double y_Clenshaw_xscaled(const double x) const; - /** - * @brief Do a vectorized evaluation of the Chebyshev expansion with the inputs scaled in [xmin, xmax] - * @param x A vectype of values in the domain [xmin,xmax] - */ - vectype y(const vectype &x) const; - /** - * @brief Do a vectorized evaluation of the Chebyshev expansion with the inputs scaled in [xmin, xmax] - * @param x A value scaled in the domain [xmin,xmax] - */ - double y(const double x) const{ return y_Clenshaw(x); } - /** - * @brief Do a vectorized evaluation of the Chebyshev expansion with the input scaled in the domain [-1,1] - * @param xscaled A vectype of values scaled to the domain [-1,1] (the domain of the Chebyshev basis functions) - * @returns y A vectype of values evaluated from the expansion - * - * By using vectorizable types like Eigen::MatrixXd, without - * any additional work, "magical" vectorization is happening - * under the hood, giving a significant speed improvement. From naive - * testing, the increase was a factor of about 10x. - */ - vectype y_recurrence_xscaled(const vectype &xscaled) const ; - /** - * @brief Do a vectorized evaluation of the Chebyshev expansion with the input scaled in the domain [-1,1] with Clenshaw's method - * @param xscaled A vectype of values scaled to the domain [-1,1] (the domain of the Chebyshev basis functions) - * @returns y A vectype of values evaluated from the expansion - */ - vectype y_Clenshaw_xscaled(const vectype &xscaled) const ; - - /** - * @brief Construct and return the companion matrix of the Chebyshev expansion - * @returns A The companion matrix of the expansion - * - * See Boyd, SIAM review, 2013, http://dx.doi.org/10.1137/110838297, Appendix A.2 - */ - Eigen::MatrixXd companion_matrix(const Eigen::VectorXd &coeffs) const ; - /** - * @brief Return the real roots of the Chebyshev expansion - * @param only_in_domain If true, only real roots that are within the domain - * of the expansion will be returned, otherwise all real roots - * - * The roots are obtained based on the fact that the eigenvalues of the - * companion matrix are the roots of the Chebyshev expansion. Thus - * this function is relatively slow, because an eigenvalue solve is required, - * which takes O(n^3) FLOPs. But it is numerically rather reliable. - * - * As the order of the expansion increases, the eigenvalue solver in Eigen becomes - * progressively less and less able to obtain the roots properly. The eigenvalue - * solver in numpy tends to be more reliable. - */ - std::vector real_roots(bool only_in_domain = true) const ; - /** - * @brief The second-generation rootfinder of ChebyshevExpansions - * @param only_in_domain True: only keep roots that are in the domain of the expansion. False: all real roots - */ - std::vector real_roots2(bool only_in_domain = true) const; - - /** - * @brief Calculate the value (only one) of x in [xmin, xmax] for which the expansion value is equal to given value - * - * Functionally the use is similar to real_roots except that: - * 1) nodal values are cached - * 2) only one solution is possible - * - Warning: the monotonicity of the expansion is assumed, but not checked - * - * @param yval Given value for which value of x is to be obtained - */ - double monotonic_solvex(double yval); - - /** - * @brief Subdivide the original interval into a set of subintervals that are linearly spaced - * @note A vector of ChebyshevExpansions are returned - * @param Nintervals The number of intervals - * @param Ndegree The degree of the Chebyshev expansion in each interval - */ - std::vector subdivide(std::size_t Nintervals, std::size_t Ndegree) const ; - - /** - * @brief For a vector of ChebyshevExpansions, find all roots in each interval - * @param segments The vector of ChebyshevExpansions - * @param only_in_domain True: only keep roots that are in the domain of the expansion. False: all real roots - */ - static std::vector real_roots_intervals(const std::vector &segments, bool only_in_domain = true); - - /** - * @brief Time how long (in seconds) it takes to evaluate the roots - * @param N How many repeats to do (maybe a million? It's pretty fast for small degrees) - */ - double real_roots_time(long N); - - /// A DEPRECATED function for approximating the roots (do not use) - std::vector real_roots_approx(long Npoints); - - // ****************************************************************** - // *********************** BUILDERS *********************** - // ****************************************************************** - - /** - * @brief Given a set of values at the Chebyshev-Lobatto nodes, perhaps obtained from the ChebyshevExpansion::factory function, - * get the expansion, using the discrete cosine transform (DCT) approach - * - * @param N The degree of the expansion - * @param f The set of values at the Chebyshev-Lobatto nodes - * @param xmin The minimum value of x for the expansion - * @param xmax The maximum value of x for the expansion - */ - static ChebyshevExpansion factoryf(const std::size_t N, const Eigen::VectorXd &f, const double xmin, const double xmax) ; - - /** - * @brief Given a set of values at the Chebyshev-Lobatto nodes, build the expansion, using the FFT approach - * - * See this clear example: https://www.mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/23972/versions/22/previews/chebfun/examples/approx/html/ChebfunFFT.html - * - * @param N The degree of the expansion - * @param f The set of values at the Chebyshev-Lobatto nodes - * @param xmin The minimum value of x for the expansion - * @param xmax The maximum value of x for the expansion - */ - static ChebyshevExpansion factoryfFFT(const std::size_t N, const Eigen::VectorXd& f, const double xmin, const double xmax); - - /** - * @brief Given a callable function, construct the N-th order Chebyshev expansion in [xmin, xmax] - * @param N The order of the expansion; there will be N+1 coefficients - * @param func A callable object, taking the x value (in [xmin,xmax]) and returning the y value - * @param xmin The minimum x value for the fit - * @param xmax The maximum x value for the fit - * - * See Boyd, SIAM review, 2013, http://dx.doi.org/10.1137/110838297, Appendix A. - */ - template - static ChebyshevExpansion factory(const std::size_t N, double_function func, const double xmin, const double xmax) - { - // Get the precalculated Chebyshev-Lobatto nodes - const Eigen::VectorXd & x_nodes_n11 = get_CLnodes(N); - - // Step 1&2: Grid points functional values (function evaluated at the - // extrema of the Chebyshev polynomial of order N - there are N+1 of them) - Eigen::VectorXd f(N + 1); - for (int k = 0; k <= N; ++k) { - // The extrema in [-1,1] scaled to real-world coordinates - double x_k = ((xmax - xmin)*x_nodes_n11(k) + (xmax + xmin)) / 2.0; - f(k) = func(x_k); - } - return factoryf(N, f, xmin, xmax); - }; - - /// Convert a monomial term in the form \f$x^n\f$ to a Chebyshev expansion - static ChebyshevExpansion from_powxn(const std::size_t n, const double xmin, const double xmax); - - /** - * @brief Convert a polynomial expansion in monomial form to a Chebyshev expansion - * - * The monomial expansion is of the form \f$ y = \displaystyle\sum_{i=0}^N c_ix_i\f$ - * - * This transformation can be carried out analytically. For convenience we repetitively use - * calls to ChebyshevExpansion::from_powxn to build up the expansion. This is probably not - * the most efficient option, but it works. - * - * @param c The vector of coefficients of the monomial expansion in *increasing* degree: - * @param xmin The minimum value of \f$x\f$ for the expansion - * @param xmax The maximum value of \f$x\f$ for the expansion - */ - template - static ChebyshevExpansion from_polynomial(vector_type c, const double xmin, const double xmax) { - vectype c0(1); c0 << 0; - ChebyshevExpansion s(c0, xmin, xmax); - for (std::size_t i = 0; i < static_cast(c.size()); ++i) { - s += c(i)*from_powxn(i, xmin, xmax); - } - return s; - } - - static auto dyadic_splitting(const std::size_t N, const std::function& func, const double xmin, const double xmax, - const int M, const double tol, const int max_refine_passes = 8, - const std::function&)>&callback = {}) - { - - // Convenience function to get the M-element norm - auto get_err = [M](const ChebyshevExpansion& ce) { return ce.coef().tail(M).norm() / ce.coef().head(M).norm(); }; - - // Start off with the full domain from xmin to xmax - std::deque expansions; - expansions.emplace_back(ChebyshevExpansion::factory(N, func, xmin, xmax)); - - // Now enter into refinement passes - for (int refine_pass = 0; refine_pass < max_refine_passes; ++refine_pass) { - bool all_converged = true; - // Start at the right and move left because insertions will make the length increase - for (int iexpansion = static_cast(expansions.size())-1; iexpansion >= 0; --iexpansion) { - auto& expan = expansions[iexpansion]; - auto err = get_err(expan); - if (err > tol) { - // Splitting is required, do a dyadic split - auto xmid = (expan.xmin() + expan.xmax()) / 2; - auto newleft = ChebyshevExpansion::factory(N, func, expan.xmin(), xmid); - auto newright = ChebyshevExpansion::factory(N, func, xmid, expan.xmax()); - std::swap(expan, newleft); - expansions.insert(expansions.begin() + iexpansion+1, newright); - all_converged = false; - } - } - if (all_converged) { break; } - if (callback != nullptr) { - callback(refine_pass, expansions); - } - } - return expansions; - } - }; - -}; /* namespace ChebTools */ -#endif diff --git a/src/external/ChebTools/speed_tests.h b/src/external/ChebTools/speed_tests.h deleted file mode 100644 index cc5944d5..00000000 --- a/src/external/ChebTools/speed_tests.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SPEED_TESTS_H -#define SPEED_TESTS_H - -#include "ChebTools/ChebTools.h" -#include - -double real_roots_time(ChebTools::ChebyshevExpansion &ce, long N); -double plus_by_inplace(ChebTools::ChebyshevExpansion &ce, const ChebTools::ChebyshevExpansion &ce2, int N); -double mult_by_inplace(ChebTools::ChebyshevExpansion &ce, double val, int N); -void mult_by(ChebTools::ChebyshevExpansion &ce, double val, int N); -std::map evaluation_speed_test(ChebTools::ChebyshevExpansion &cee, const Eigen::VectorXd &xpts, long N) ; -Eigen::MatrixXd eigs_speed_test(std::vector &Nvec, std::size_t Nrepeats); - -#endif \ No newline at end of file diff --git a/src/external/PackedCSparse/FloatArray.h b/src/external/PackedCSparse/FloatArray.h deleted file mode 100644 index 28d1c5da..00000000 --- a/src/external/PackedCSparse/FloatArray.h +++ /dev/null @@ -1,307 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -#pragma once -#include -#include -#include -namespace PackedCSparse { - template - struct BaseImpl - { - T x[k]; - - BaseImpl() {}; - - BaseImpl(const T& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] = rhs; - } - - BaseImpl operator+(const BaseImpl& rhs) const - { - BaseImpl lhs; - for (size_t i = 0; i < k; i++) - lhs.x[i] = x[i] + rhs.x[i]; - return lhs; - } - - BaseImpl operator-(const BaseImpl& rhs) const - { - BaseImpl lhs; - for (size_t i = 0; i < k; i++) - lhs.x[i] = x[i] - rhs.x[i]; - return lhs; - } - - BaseImpl operator*(const BaseImpl& rhs) const - { - BaseImpl lhs; - for (size_t i = 0; i < k; i++) - lhs.x[i] = x[i] * rhs.x[i]; - return lhs; - } - - BaseImpl operator/(const BaseImpl& rhs) const - { - BaseImpl lhs; - for (size_t i = 0; i < k; i++) - lhs.x[i] = x[i] / rhs.x[i]; - return lhs; - } - - BaseImpl& operator+=(const BaseImpl& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] += rhs.x[i]; - return *this; - } - - BaseImpl& operator-=(const BaseImpl& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] -= rhs.x[i]; - return *this; - } - - BaseImpl& operator*=(const BaseImpl& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] *= rhs.x[i]; - return *this; - } - - BaseImpl& operator/=(const BaseImpl& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] /= rhs.x[i]; - return *this; - } - - explicit operator bool() const - { - bool ret = false; - for (size_t i = 0; i < k; i++) - ret = ret || bool(x[i]); - return ret; - } - - static T get(const BaseImpl& a, size_t index) - { - return a.x[index]; - } - - static void set(BaseImpl& a, size_t index, const T& value) - { - a.x[index] = value; - } - - static BaseImpl abs(const BaseImpl& a) - { - BaseImpl out; - for (size_t i = 0; i < k; i++) - out.x[i] = std::abs(a.x[i]); - return out; - } - - static BaseImpl log(const BaseImpl& a) - { - BaseImpl out; - for (size_t i = 0; i < k; i++) - out.x[i] = std::log(a.x[i]); - return out; - } - - static void fmadd(BaseImpl& a, const BaseImpl& b, const BaseImpl& c) - { - for (size_t i = 0; i < k; i++) - a.x[i] += b.x[i] * c.x[i]; - } - - static void fnmadd(BaseImpl& a, const BaseImpl& b, const BaseImpl& c) - { - for (size_t i = 0; i < k; i++) - a.x[i] -= b.x[i] * c.x[i]; - } - - static void fmadd(BaseImpl& a, const BaseImpl& b, const T& c) - { - for (size_t i = 0; i < k; i++) - a.x[i] += b.x[i] * c; - } - - static void fnmadd(BaseImpl& a, const BaseImpl& b, const T& c) - { - for (size_t i = 0; i < k; i++) - a.x[i] -= b.x[i] * c; - } - - static BaseImpl clipped_sqrt(const BaseImpl& a, const T nonpos_output) - { - BaseImpl out; - for (size_t i = 0; i < k; i++) - { - T r = a.x[i]; - if (r > 0) - out.x[i] = sqrt(r); - else - out.x[i] = nonpos_output; - } - return out; - } - - static BaseImpl sign(std::mt19937_64& gen) - { - BaseImpl out; - unsigned long long seed = gen(); - for (size_t i = 0; i < k; i++) - { - out.x[i] = T((2 * ((seed >> i) & 1)) - 1.0); - if ((i & 63) == 63) seed = gen(); - } - return out; - } - - }; - - template - struct BaseScalarImpl - { - static T get(const T& x, size_t index) - { - return x; - } - - static void set(T& x, size_t index, T& value) - { - x = value; - } - - static T abs(const T &x) - { - return ::abs(x); - } - - static T log(const T &x) - { - return ::log(x); - } - - static void fmadd(T& a, const T& b, const T& c) - { - a += b * c; - } - - static void fnmadd(T& a, const T& b, const T& c) - { - a -= b * c; - } - - static T clipped_sqrt(const T& x, const T& nonpos_output) - { - if (x > 0.0) - return sqrt(x); - else - return nonpos_output; - } - - static T sign(std::mt19937_64& gen) - { - unsigned long long seed = gen(); - return T((2 * (seed & 1)) - 1.0); - } - }; - - template - struct FloatTypeSelector - { - using type = typename std::conditional>::type; - using funcImpl = typename std::conditional, BaseImpl>::type; - }; - - #ifdef __AVX2__ - #include "FloatArrayAVX2.h" - #else - template - struct FloatTypeSelector - { - using type = typename std::conditional< k == 1, double, BaseImpl>::type; - using funcImpl = typename std::conditional< k == 1, BaseScalarImpl, BaseImpl>::type; - }; - - template - struct FloatTypeSelector, l> - { - using type = BaseImpl; - using funcImpl = BaseImpl; - }; - #endif - - template - struct FloatTypeSelector, l> - { - using type = BaseImpl; - using funcImpl = BaseImpl; - }; - - template - using FloatArray = typename FloatTypeSelector::type; - - template - using FloatArrayFunc = typename FloatTypeSelector::funcImpl; - - template - auto get(const T& a, size_t index) -> decltype(FloatArrayFunc::get(a, index)) - { - return FloatArrayFunc::get(a, index); - } - - template - void set(T1& a, size_t index, T2 value) - { - FloatArrayFunc::set(a, index, value); - } - - template - void fmadd(T1& a, const T2& b, const T3& c) - { - FloatArrayFunc::fmadd(a, b, c); - } - - template - void fnmadd(T1& a, const T2& b, const T3& c) - { - FloatArrayFunc::fnmadd(a, b, c); - } - - template - T1 clipped_sqrt(const T1& a, const T2 b) - { - return FloatArrayFunc::clipped_sqrt(a, b); - } - - template - T abs(const T& a) - { - return FloatArrayFunc::abs(a); - } - - template - T log(const T& a) - { - return FloatArrayFunc::log(a); - } - - template - T sign(std::mt19937_64& gen) - { - return FloatArrayFunc::sign(gen); - } -} diff --git a/src/external/PackedCSparse/FloatArrayAVX2.h b/src/external/PackedCSparse/FloatArrayAVX2.h deleted file mode 100644 index 3ae7592a..00000000 --- a/src/external/PackedCSparse/FloatArrayAVX2.h +++ /dev/null @@ -1,222 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -template - struct m256dArray -{ - __m256d x[k]; - - m256dArray() {}; - - m256dArray(const double rhs) - { - for (size_t i = 0; i < k; i++) - x[i] = _mm256_set1_pd(rhs); - } - - template - m256dArray(const m256dArray& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] = rhs.x[i % k2]; - } - - m256dArray operator+(const m256dArray& rhs) const - { - m256dArray out; - for (size_t i = 0; i < k; i++) - out.x[i] = _mm256_add_pd(x[i], rhs.x[i]); - return out; - } - - m256dArray operator-(const m256dArray& rhs) const - { - m256dArray out; - for (size_t i = 0; i < k; i++) - out.x[i] = _mm256_sub_pd(x[i], rhs.x[i]); - return out; - } - - m256dArray operator*(const m256dArray& rhs) const - { - m256dArray out; - for (size_t i = 0; i < k; i++) - out.x[i] = _mm256_mul_pd(x[i], rhs.x[i]); - return out; - } - - m256dArray operator/(const m256dArray& rhs) const - { - m256dArray out; - for (size_t i = 0; i < k; i++) - out.x[i] = _mm256_div_pd(x[i], rhs.x[i]); - return out; - } - - m256dArray& operator+=(const m256dArray& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] = _mm256_add_pd(x[i], rhs.x[i]); - return *this; - } - - m256dArray& operator-=(const m256dArray& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] = _mm256_sub_pd(x[i], rhs.x[i]); - return *this; - } - - m256dArray& operator*=(const m256dArray& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] = _mm256_mul_pd(x[i], rhs.x[i]); - return *this; - } - - m256dArray& operator/=(const m256dArray& rhs) - { - for (size_t i = 0; i < k; i++) - x[i] = _mm256_div_pd(x[i], rhs.x[i]); - return *this; - } - - explicit operator bool() const - { - bool ret = false; - __m256d z = _mm256_set1_pd(0.0); - for (size_t i = 0; i < k; i++) - { - __m256d c = _mm256_cmp_pd(x[i], z, _CMP_EQ_OQ); - ret = ret || (_mm256_movemask_pd(c) != 0xf); - } - return ret; - } - - static double get(const m256dArray& x, size_t index) - { - double y[4]; - _mm256_store_pd(y, x.x[index / 4]); - return y[index & 3]; - } - - static void set(m256dArray& x, size_t index, double value) - { - __m256d v = _mm256_broadcast_sd(&value); - switch (index & 3) - { - case 0: x.x[index / 4] = _mm256_blend_pd(x.x[index / 4], v, 1); break; - case 1: x.x[index / 4] = _mm256_blend_pd(x.x[index / 4], v, 2); break; - case 2: x.x[index / 4] = _mm256_blend_pd(x.x[index / 4], v, 4); break; - default: x.x[index / 4] = _mm256_blend_pd(x.x[index / 4], v, 8); break; - } - } - - static m256dArray abs(const m256dArray& x) - { - const __m256d mask = _mm256_castsi256_pd(_mm256_set1_epi64x(0x7FFFFFFFFFFFFFFF)); - - m256dArray out; - for (size_t i = 0; i < k; i++) - out.x[i] = _mm256_and_pd(x.x[i], mask); - return out; - } - - static m256dArray log(const m256dArray& x) - { - // gcc does not support _mm256_log_pd - // Do it sequentially instead - - //m256dArray out; - //for (size_t i = 0; i < k; i++) - // out.x[i] = _mm256_log_pd(x.x[i]); - - m256dArray out; - for (size_t i = 0; i < 4*k; i++) - set(out, i, std::log(get(x,i))); - return out; - } - - static void fmadd(m256dArray& a, const m256dArray& b, const double& c) - { - auto cx = _mm256_set1_pd(c); - for (size_t i = 0; i < k; i++) - a.x[i] = _mm256_fmadd_pd(b.x[i], cx, a.x[i]); - } - - static void fnmadd(m256dArray& a, const m256dArray& b, const double& c) - { - auto cx = _mm256_set1_pd(c); - for (size_t i = 0; i < k; i++) - a.x[i] = _mm256_fnmadd_pd(b.x[i], cx, a.x[i]); - } - - static void fmadd(m256dArray& a, const m256dArray& b, const m256dArray& c) - { - for (size_t i = 0; i < k; i++) - a.x[i] = _mm256_fmadd_pd(b.x[i], c.x[i], a.x[i]); - } - - static void fnmadd(m256dArray& a, const m256dArray& b, const m256dArray& c) - { - for (size_t i = 0; i < k; i++) - a.x[i] = _mm256_fnmadd_pd(b.x[i], c.x[i], a.x[i]); - } - - static m256dArray clipped_sqrt(const m256dArray& x, const double nonpos_output) - { - m256dArray out; - - const __m256d large = { nonpos_output, nonpos_output, nonpos_output, nonpos_output }; - const __m256d zero = _mm256_setzero_pd(); - for (size_t i = 0; i < k; i++) - { - __m256d xi = x.x[i]; - __m256d mask = _mm256_cmp_pd(xi, zero, _CMP_LE_OS); // mask = (rhs.x[i]<= 0) ? -1 : 0 - out.x[i] = _mm256_blendv_pd(_mm256_sqrt_pd(xi), large, mask); - } - return out; - } - - static m256dArray sign(std::mt19937_64& gen) - { - m256dArray out; - const __m256i bits = _mm256_set_epi64x(1, 2, 4, 8); - const __m256d zero = _mm256_setzero_pd(); - const __m256d pos = _mm256_set_pd(1.0, 1.0, 1.0, 1.0); - const __m256d neg = _mm256_set_pd(-1.0, -1.0, -1.0, -1.0); - - unsigned long long seed = gen(); - for (size_t i = 0; i < k; i++) - { - __m256i s = _mm256_set1_epi64x((seed >> (4 * i)) & 15); - __m256i xi = _mm256_and_si256(s, bits); - __m256d x = _mm256_castsi256_pd(xi); - __m256d mask = _mm256_cmp_pd(x, zero, _CMP_EQ_OQ); // mask = (rhs.x[i] == 0) ? -1 : 0 - out.x[i] = _mm256_blendv_pd(pos, neg, mask); - if ((i & 63) == 63) seed = gen(); - } - return out; - } -}; - -template - struct FloatTypeSelector -{ - static_assert(k == 1 || k % 4 == 0, "Array assumes k = 1 or a multiple of 4"); - using type = typename std::conditional< k == 1, double, m256dArray>::type; - using funcImpl = typename std::conditional< k == 1, BaseScalarImpl, m256dArray>::type; -}; - -template - struct FloatTypeSelector, l> -{ - using type = m256dArray; - using funcImpl = m256dArray; -}; diff --git a/src/external/PackedCSparse/PackedChol.h b/src/external/PackedCSparse/PackedChol.h deleted file mode 100644 index 97d10220..00000000 --- a/src/external/PackedCSparse/PackedChol.h +++ /dev/null @@ -1,308 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -#pragma once -#include "Eigen/Eigen" -#include "SparseMatrix.h" -#include "chol.h" -#include "leverage.h" -#include "leverageJL.h" -#include "multiply.h" -#include "qd/dd_real.h" -#include -#include -using namespace PackedCSparse; - -template -void get_slice(Tout *out, Tin *in, size_t n, size_t idx) { - for (size_t j = 0; j < n; j++) - out[j] = to_double(get(in[j], idx)); -} - -template -void set_slice(Tout *out, Tin *in, size_t n, size_t idx) { - for (size_t j = 0; j < n; j++) - set(out[j], idx, to_double(in[j])); -} - -template struct PackedChol { - using Tx = double; - using Tx2 = FloatArray; - using Te = dd_real; - - // parameters - SparseMatrix A; - SparseMatrix At; - UniqueAlignedPtr w; - Tx accuracyThreshold = 1e-6; - std::vector - exactIdx; // k size array. Indices we perform high precision calculation - std::vector - numExact; // number of times we perform high precision decompose (length - // k+1, the last one records how many times we do decompose) - bool decomposed = false; - - // preprocess info for different CSparse operations (PackedDouble) - MultiplyOutput H; // cache for H = A W A' - CholOutput L; // cache for L = chol(H) - LeverageOutput diagP; // cache for L = chol(H) - LeverageJLOutput diagPJL; // cache for L = chol(H) - - // preprocess info for different CSparse operations (dd_real) - MultiplyOutput H_exact; // cache for H = A W A' - CholOutput L_exact; // cache for L = chol(H) - LeverageOutput diagP_exact; // cache for L = chol(H) - LeverageJLOutput diagPJL_exact; // cache for L = chol(H) - SparseMatrix Le[k]; // store output of L_exact - - PackedChol(const SparseMatrix &A_) { - A = std::move(A_.clone()); - At = transpose(A); - w.reset(pcs_aligned_new(A.n)); - numExact.resize(k + 1); - } - - void setSeed(unsigned long long seed) { - diagPJL.gen.seed(seed); - diagPJL_exact.gen.seed(seed); - } - - bool allExact() { return exactIdx.size() == k; } - - bool hasExact() { return exactIdx.size() > 0; } - - template Tx2 decompose(const Tv2_ *w_in) { - - Tx2 acc = Tx2(0.0); - - // record w - Ti n = A.n; - - for (Ti j = 0; j < n; j++) { - w[j] = w_in[j]; - } - // compute chol - ++numExact[k]; - if (accuracyThreshold > 0.0 || - !decomposed) // the first time we call, always run the double chol. - { - multiply(H, A, w.get(), At); - chol(L, H); - decomposed = true; - - exactIdx.clear(); - acc = estimateAccuracy(); - for (size_t i = 0; i < k; i++) { - if (get(acc, i) >= - accuracyThreshold) // >= is important for the case accuracyThreshold - // = 0.0, we need to compute everything exactly - exactIdx.push_back(i); - } - } else if (!allExact()) { - exactIdx.clear(); - for (size_t i = 0; i < k; i++) - exactIdx.push_back(i); - } - - if (hasExact()) { - Te *w_exact = new Te[n]; - - for (size_t i : exactIdx) { - ++numExact[i]; - get_slice(w_exact, w.get(), n, i); - multiply(H_exact, A, w_exact, At); - chol(L_exact, H_exact); - - // copy result to Le[i] - if (!Le[i].initialized()) - Le[i] = std::move(L_exact.template clone()); - else { - Ti nz = L_exact.nnz(); - for (Ti s = 0; s < nz; ++s) - Le[i].x[s] = (L_exact.x[s]); - } - } - - delete[] w_exact; - } - return acc; - } - - Tx2 logdet() { - pcs_assert(decomposed, "logdet: Need to call decompose first."); - - Ti m = A.m; - Tx2 ret = Tx2(0); - - if (!allExact()) { - Ti *Lp = L.p.get(); - Tx2 *Lx = L.x.get(); - for (Ti j = 0; j < m; j++) - ret += log(Lx[Lp[j]]); - } - - if (hasExact()) { - for (size_t i : exactIdx) { - Te ret_e = 0.0; - Ti *Lp = Le[i].p.get(); - Te *Lx = Le[i].x.get(); - - for (Ti j = 0; j < m; j++) - ret_e += log(Lx[Lp[j]]); - - set(ret, i, to_double(ret_e)); - } - } - - return ret * Tx2(2.0); - } - - void diagL(Tx2 *out) { - pcs_assert(decomposed, "diagL: Need to call decompose first."); - - Ti m = A.m; - - if (!allExact()) { - Ti *Li = L.i.get(), *Lp = L.p.get(); - Tx2 *Lx = L.x.get(); - for (Ti j = 0; j < m; j++) - out[j] = Lx[Lp[j]]; - } - - if (hasExact()) { - for (size_t i : exactIdx) { - Ti *Lp = Le[i].p.get(); - Te *Lx = Le[i].x.get(); - - for (Ti j = 0; j < m; j++) - set(out[j], i, to_double(Lx[Lp[j]])); - } - } - } - - SparseMatrix getL(Ti i) { - pcs_assert(decomposed, "getL: Need to call decompose first."); - - Ti m = L.m, n = L.n, nz = L.nnz(); - SparseMatrix out(m, n, nz); - - Ti *outp = out.p.get(), *Lp = L.p.get(); - Ti *outi = out.i.get(), *Li = L.i.get(); - - for (Ti s = 0; s <= n; s++) - outp[s] = Lp[s]; - - for (Ti s = 0; s < nz; s++) - outi[s] = Li[s]; - - bool isExact = false; - for (size_t i_ : exactIdx) { - if (i_ == i) - isExact = true; - } - - double *outx = out.x.get(); - if (isExact) { - Te *Lx = Le[i].x.get(); - for (Ti s = 0; s < nz; s++) - outx[s] = to_double(Lx[s]); - } else { - Tx2 *Lx = L.x.get(); - for (Ti s = 0; s < nz; s++) - outx[s] = get(Lx[s], i); - } - - return std::move(out); - } - - void solve(Tx2 *b, Tx2 *out) { - pcs_assert(decomposed, "solve: Need to call decompose first."); - - if (!allExact()) { - lsolve(L, b, out); - ltsolve(L, out, out); - } - - if (hasExact()) { - Ti m = A.m; - Te *b_exact = new Te[m]; - Te *out_exact = new Te[m]; - - for (size_t i : exactIdx) { - get_slice(b_exact, b, m, i); - lsolve(Le[i], b_exact, out_exact); - ltsolve(Le[i], out_exact, out_exact); - set_slice(out, out_exact, m, i); - } - - delete[] b_exact; - delete[] out_exact; - } - }; - - void leverageScoreComplement(Tx2 *out) { - pcs_assert(decomposed, - "leverageScoreComplement: Need to call decompose first."); - - Ti n = A.n, m = A.m; - - if (!allExact()) { - Tx2 T1 = Tx2(1.0), T2 = Tx2(2.0); - leverage(diagP, L, A, At); - - Tx2 *tau = diagP.x.get(); - for (Ti j = 0; j < n; j++) - out[j] = T1 - tau[j] * w[j]; - } - - if (hasExact()) { - Te T1 = Te(1.0), T2 = Te(2.0); - for (size_t i : exactIdx) { - leverage(diagP_exact, Le[i], A, At); - - Te *tau = diagP_exact.x.get(); - for (Ti j = 0; j < n; j++) - set(out[j], i, to_double(T1 - tau[j] * get(w[j], i))); - } - } - } - - void leverageScoreComplementJL(Tx2 *out, size_t JL_k) { - pcs_assert(decomposed, - "leverageScoreComplementJL: Need to call decompose first."); - - Ti m = A.m, n = A.n; - - if (!allExact()) { - Tx2 T1 = Tx2(1.0), T2 = Tx2(2.0); - leverageJL(diagPJL, L, A, At, JL_k); - - Tx2 *tau = diagPJL.x.get(); - for (Ti j = 0; j < n; j++) - out[j] = T1 - tau[j] * w[j]; - } - - if (hasExact()) { - Te T1 = Te(1.0), T2 = Te(2.0); - for (size_t i : exactIdx) { - leverageJL(diagPJL_exact, Le[i], A, At, JL_k); - - Te *tau = diagPJL_exact.x.get(); - for (Ti j = 0; j < n; j++) - set(out[j], i, to_double(T1 - tau[j] * get(w[j], i))); - } - } - } - - Tx2 estimateAccuracy() { - pcs_assert(decomposed, "estimateAccuracy: Need to call decompose first."); - - return cholAccuracy(diagPJL, L, A, At, w.get()); - } -}; diff --git a/src/external/PackedCSparse/SparseMatrix.h b/src/external/PackedCSparse/SparseMatrix.h deleted file mode 100644 index 5dca4286..00000000 --- a/src/external/PackedCSparse/SparseMatrix.h +++ /dev/null @@ -1,230 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis -#pragma once -#include -#include -#include "FloatArray.h" - -namespace PackedCSparse { - static void pcs_assert(bool value, const char* message) - { - if (value == false) - throw std::logic_error(message); - } - - template - T* pcs_aligned_new(size_t size) - { - int alignment = 64; // size of memory cache line - int offset = alignment - 1 + sizeof(void*); - void* p1 = (void*)new char[size * sizeof(T) + offset]; - void** p2 = (void**)(((size_t)(p1)+offset) & ~(alignment - 1)); - p2[-1] = p1; - return (T*)p2; - } - - template - struct AlignedDeleter - { - void operator()(T* p) const - { - delete[](char*)(((void**)p)[-1]); - } - }; - - template - using UniqueAlignedPtr = std::unique_ptr>; - - template - using UniquePtr = std::unique_ptr; - - // Tx = Type for entries, Ti = Type for indices. - // if Tx == bool, the matrix stores only sparsity information - template - struct SparseMatrix - { - Ti m = 0; /* number of rows */ - Ti n = 0; /* number of columns */ - UniquePtr p; /* column pointers (size n+1) */ - UniquePtr i; /* row indices, size nnz */ - UniqueAlignedPtr x; /* numerical values, size nnz */ - - SparseMatrix() = default; - - SparseMatrix(Ti m_, Ti n_, Ti nzmax_) - { - initialize(m_, n_, nzmax_); - } - - bool initialized() const - { - return p && i; - } - - void initialize(Ti m_, Ti n_, Ti nzmax) - { - if (nzmax < 1) nzmax = 1; - m = m_; n = n_; - p.reset(new Ti[n + 1]); - i.reset(new Ti[nzmax]); - if (!std::is_same::value) - x.reset(pcs_aligned_new(nzmax)); - } - - Ti nnz() const - { - return p[n]; - } - - template - SparseMatrix clone() const - { - SparseMatrix C(m, n, nnz()); - Ti* Ap = p.get(), * Ai = i.get(); Tx* Ax = x.get(); - Ti2* Cp = C.p.get(), * Ci = C.i.get(); Tx2* Cx = C.x.get(); - - for (Ti s = 0; s <= n; s++) - Cp[s] = Ti2(Ap[s]); - - Ti nz = nnz(); - for (Ti s = 0; s < nz; s++) - Ci[s] = Ti2(Ai[s]); - - if (Cx) - { - for (Ti s = 0; s < nz; s++) - Cx[s] = Ax? Tx2(Ax[s]): Tx2(1.0); - } - - return C; - } - }; - - template - struct DenseVector - { - Ti n = 0; /* number of columns */ - UniqueAlignedPtr x; /* numerical values, size nnz */ - - DenseVector() = default; - - DenseVector(Ti n_) - { - initialize(n_); - } - - bool initialized() const - { - return bool(x); - } - - void initialize(Ti n_) - { - n = n_; - x.reset(pcs_aligned_new(n_)); - } - }; - - - // basic functions - template - SparseMatrix speye(Ti n, Tx* d = nullptr) - { - SparseMatrix D(n, n, n); - - for (Ti k = 0; k < n; k++) - { - D.i[k] = k; - D.p[k] = k; - } - D.p[n] = n; - - Tx Tx1 = Tx(1.0); - for (Ti k = 0; k < n; k++) - D.x[k] = (d ? d[k] : Tx1); - return D; - } - - // Solve L out = x - // Input: L in Tx^{n by n}, x in Tx2^{n} - // Output: out in Tx2^{n}. - // If out is provided, we will output to out. Else, output to x. - template - void lsolve(const SparseMatrix& L, Tx2* x, Tx2* out = nullptr) - { - pcs_assert(L.initialized(), "lsolve: bad inputs."); - pcs_assert(L.n == L.m, "lsolve: dimensions mismatch."); - - Ti n = L.n, * Lp = L.p.get(), * Li = L.i.get(); Tx* Lx = L.x.get(); - - if (!out) out = x; - if (x != out) std::copy(x, x + n, out); - - for (Ti j = 0; j < n; j++) - { - Tx2 out_j = out[j] / Lx[Lp[j]]; - out[j] = out_j; - - Ti p_start = Lp[j] + 1, p_end = Lp[j + 1]; - for (Ti p = p_start; p < p_end; p++) - { //out[Li[p]] -= Lx[p] * out[j]; - fnmadd(out[Li[p]], out_j, Lx[p]); - } - } - } - - // Solve L' out = x - // Input: L in Tx^{n by n}, x in Tx2^{n} - // Output: out in Tx2^{n}. - // If out is provided, we will output to out. Else, output to x. - template - void ltsolve(const SparseMatrix& L, Tx2* x, Tx2* out = nullptr) - { - pcs_assert(L.initialized(), "ltsolve: bad inputs."); - pcs_assert(L.n == L.m, "ltsolve: dimensions mismatch."); - - Ti n = L.n, * Lp = L.p.get(), * Li = L.i.get(); Tx* Lx = L.x.get(); - - if (!out) out = x; - if (x != out) std::copy(x, x + n, out); - - for (Ti j = n - 1; j != -1; j--) - { - Tx2 out_j = out[j]; - - Ti p_start = Lp[j] + 1, p_end = Lp[j + 1]; - for (Ti p = p_start; p < p_end; p++) - { //out[j] -= Lx[p] * out[Li[p]]; - fnmadd(out_j, out[Li[p]], Lx[p]); - } - - out[j] = out_j / Tx2(Lx[Lp[j]]); - } - } - - // Update y <-- y + A x - // Input: A in Tx^{n by n}, x, y in Tx2^{n} - template - void gaxpy(const SparseMatrix& A, const Tx2* x, Tx2* y) - { - pcs_assert(A.initialized(), "gaxpy: bad inputs."); - Ti m = A.m, n = A.n, * Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); - - for (Ti j = 0; j < n; j++) - { - Tx2 x_j = x[j]; - - Ti p_start = Ap[j], p_end = Ap[j + 1]; - for (Ti p = p_start; p < p_end; p++) - { //y[Ai[p]] += Ax[p] * x[j]; - fmadd(y[Ai[p]], x_j, Ax[p]); - } - } - } -}; diff --git a/src/external/PackedCSparse/add.h b/src/external/PackedCSparse/add.h deleted file mode 100644 index 16195722..00000000 --- a/src/external/PackedCSparse/add.h +++ /dev/null @@ -1,102 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -#pragma once -#include -#include "SparseMatrix.h" - -// Problem: -// Compute M = A + B - -// Algorithm: -// M = 0 -// M(A != 0) += A(A != 0) -// M(B != 0) += B(A != 0) - -namespace PackedCSparse { - template - struct AddOutput : SparseMatrix - { - UniquePtr forwardA; - UniquePtr forwardB; - - template - void initialize(const SparseMatrix& A, const SparseMatrix& B) - { - pcs_assert(A.initialized() && B.initialized(), "add: bad inputs."); - pcs_assert(A.n == B.n && A.m == B.m, "add: dimensions mismatch."); - - Ti m = A.m, n = A.n; - Ti Anz = A.nnz(); Ti* Ap = A.p.get(), * Ai = A.i.get(); - Ti Bnz = B.nnz(); Ti* Bp = B.p.get(), * Bi = B.i.get(); - this->m = A.m; this->n = A.n; - - std::vector Ci; - Ti* Cp = new Ti[n + 1]; - forwardA.reset(new Ti[Anz]); - forwardB.reset(new Ti[Bnz]); - - Cp[0] = 0; - for (Ti i = 0; i < n; i++) - { - Ti s1 = Ap[i], s2 = Bp[i], end1 = Ap[i + 1], end2 = Bp[i + 1]; - while ((s1 < end1) || (s2 < end2)) - { - Ti q = Ti(Ci.size()); - Ti i1 = (s1 < end1) ? Ai[s1] : m; - Ti i2 = (s2 < end2) ? Bi[s2] : m; - Ti min_i = std::min(i1, i2); - Ci.push_back(min_i); - - if (i1 == min_i) - forwardA[s1++] = q; - - if (i2 == min_i) - forwardB[s2++] = q; - } - Cp[i + 1] = Ti(Ci.size()); - } - - this->p.reset(Cp); - this->i.reset(new Ti[Ci.size()]); - this->x.reset(pcs_aligned_new(Ci.size())); - std::copy(Ci.begin(), Ci.end(), this->i.get()); - } - }; - - template - void add(AddOutput& o, const SparseMatrix& A, const SparseMatrix& B) - { - if (!o.initialized()) - o.initialize(A, B); - - Ti m = o.m, n = o.n; - Ti Anz = A.nnz(); Ti* Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); - Ti Bnz = B.nnz(); Ti* Bp = B.p.get(), * Bi = B.i.get(); Tx* Bx = B.x.get(); - Ti Cnz = o.nnz(); Ti* Cp = o.p.get(), * Ci = o.i.get(); Tx2* Cx = o.x.get(); - Ti* forwardA = o.forwardA.get(), *forwardB = o.forwardB.get(); - - for (Ti s = 0; s < Cnz; s++) - Cx[s] = 0; - - for (Ti s = 0; s < Anz; s++) - Cx[forwardA[s]] = Ax[s]; - - for (Ti s = 0; s < Bnz; s++) - Cx[forwardB[s]] += Bx[s]; - } - - template - AddOutput add(const SparseMatrix& A, const SparseMatrix& B) - { - AddOutput o; - add(o, A, B); - return o; - } -} diff --git a/src/external/PackedCSparse/chol.h b/src/external/PackedCSparse/chol.h deleted file mode 100644 index d6001ae2..00000000 --- a/src/external/PackedCSparse/chol.h +++ /dev/null @@ -1,247 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -#pragma once -#include -#include -#include "SparseMatrix.h" -#include "transpose.h" - -// Problem: -// Compute chol(A) - -// Algorithm: -// We need to study this later as this is the bottleneck. -// Document it as a lyx. -// chol_up_looking: -// Compute L row by row -// This is faster when it is compute bound. -// -// chol_left_looking: -// Compute L col by col -// This is faster when it is memory bound. - - -namespace PackedCSparse { - template - struct CholOutput : SparseMatrix - { - TransposeOutput Lt; // sparsity pattern of the Lt - UniquePtr diag; // the index for diagonal element. Ax[diag[k]] is A_kk - UniquePtr c; // c[i] = index the last nonzero on column i in the current L - UniqueAlignedPtr w; // the row of L we are computing - - // The cost of this is roughly 3 times larger than chol - // One can optimize it by using other data structure - void initialize(const SparseMatrix& A) - { - pcs_assert(A.initialized(), "chol: bad inputs."); - pcs_assert(A.n == A.m, "chol: dimensions mismatch."); - - Ti n = A.n, * Ap = A.p.get(), * Ai = A.i.get(); - - // initialize - this->diag.reset(new Ti[n]); - this->c.reset(new Ti[n]); - this->w.reset(pcs_aligned_new(n)); - - // compute the sparsity pattern of L and diag - using queue = std::priority_queue, std::greater>; - queue q; // sol'n of the current row of L - Ti* mark = new Ti[n]; // used to prevent same indices push to q twice - std::vector* cols = new std::vector[n]; // stores the non-zeros of each col of L - Ti nz = 0, Anz = Ap[n]; - - for (Ti i = 0; i < n; i++) - mark[i] = -1; - - // for each row of A - for (Ti i = 0; i < n; i++) - { // push i-th row of A, called a_12, into mark - Ti s; - for (s = Ap[i]; s < Ap[i + 1]; s++) - { - Ti j = Ai[s]; - if (j >= i) break; - - q.push(j); - mark[j] = i; - } - if (s >= Anz) // this case happens only if the diagonal is 0. No cholesky in this case. - this->diag[i] = 0; - else - this->diag[i] = s; - - // Solve L_11 l_12 = a_12 - while (!q.empty()) - { - Ti j = q.top(); - - for (Ti k : cols[j]) - { - if (mark[k] != i) - { - q.push(k); - mark[k] = i; - } - } - q.pop(); - - // update j col - cols[j].push_back(i); - ++nz; - } - - // diag - cols[i].push_back(i); - ++nz; - } - delete[] mark; - - // write it as the compress form - SparseMatrix::initialize(n, n, nz); - - Ti s_start = 0; Ti s = 0; - for (Ti i = 0; i < n; i++) - { - this->p[i] = s_start; - for (Ti k : cols[i]) - this->i[s++] = k; - s_start += Ti(cols[i].size()); - } - this->p[n] = s_start; - delete[] cols; - - this->Lt = transpose(*this); - - // initialize w to 0 - Tx Tv0 = Tx(0); - for (Ti k = 0; k < n; k++) - w[k] = Tv0; - } - }; - - template - void chol(CholOutput& o, const SparseMatrix& A) - { - if (!o.initialized()) - o.initialize(A); - - //chol_up_looking(o, A); - chol_left_looking(o, A); - } - - template - void chol_up_looking(CholOutput& o, const SparseMatrix& A) - { - Ti *Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); - Ti nzmax = o.nzmax; Ti n = A.n; - Ti *Lp = o.p.get(); Ti* Li = o.i.get(); - Ti *Ltp = o.Lt.p.get(); Ti* Lti = o.Lt.i.get(); - - Tx T0 = Tx(0); - Tx* Lx = o.x.get(); Tx* w = o.w.get(); Ti* c = o.c.get(); - Ti* diag = o.diag.get(); - - Ti* Lti_ptr = Lti; - for (Ti k = 0; k < n; ++k) - { - c[k] = Lp[k]; - - Ti s_end = diag[k]; - for (Ti s = Ap[k]; s < s_end; ++s) - w[Ai[s]] = Ax[s]; - - // Solve L_11 l_12 = a_12 - Tx d = Ax[s_end]; Ti i; - for (; (i = *(Lti_ptr++)) < k;) - { - Ti dLi = Lp[i], ci = c[i]++; - Tx Lki = w[i] / Lx[dLi]; - w[i] = T0; // maintain x = 0 for the (k+1) iteration - - for (Ti q = dLi + 1; q < ci; ++q) - fnmadd(w[Li[q]], Lx[q], Lki); - - d -= Lki * Lki; - Lx[ci] = Lki; - } - - // l_22 = sqrt(a22 - ) - Lx[c[k]++] = clipped_sqrt(d); - } - } - - template - void chol_left_looking(CholOutput& o, const SparseMatrix& A) - { - Ti* Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); - Ti nzmax = o.nnz(); Ti n = A.n; - Ti* Lp = o.p.get(); Ti* Li = o.i.get(); - Ti* Ltp = o.Lt.p.get(); Ti* Lti = o.Lt.i.get(); - - Tx T0 = Tx(0), T1 = Tx(1); - Tx* Lx = o.x.get(); - Tx* w = o.w.get(); Ti* c = o.c.get(); - Ti* diag = o.diag.get(); - - for (Ti j = 0; j < n; ++j) - { - c[j] = Lp[j]; - - // x = A_{j:n, j} - { - Ti is_start = diag[j], is_end = Ap[j + 1]; - for (Ti is = is_start; is < is_end; ++is) - w[Ai[is]] = Ax[is]; - } - - // for each p in L_{j, 1:j-1} - Ti ps_start = Ltp[j], ps_end = Ltp[j + 1] - 1; - for (Ti ps = ps_start; ps < ps_end; ++ps) - { - Ti p = Lti[ps]; - Ti cp = c[p]++; - Tx Ljp = Lx[cp]; - - // for each i in L_{j:n,p} - Ti is_start = cp, is_end = Lp[p + 1]; - for (Ti is = is_start; is < is_end; ++is) - { - Ti i = Li[is]; - fnmadd(w[i], Lx[is], Ljp); - } - } - - Tx Ljj = clipped_sqrt(w[j], 1e128); - Lx[c[j]++] = Ljj; - Tx inv_Ljj = T1 / Ljj; - w[j] = T0; - - // for each i in L_{:,j} - { - Ti is_start = Lp[j] + 1, is_end = Lp[j + 1]; - for (Ti is = is_start; is < is_end; ++is) - { - Ti i = Li[is]; - Lx[is] = w[i] * inv_Ljj; - w[i] = T0; - } - } - } - } - - template - CholOutput chol(const SparseMatrix& A) - { - CholOutput o; - chol(o, A); - return o; - } -} diff --git a/src/external/PackedCSparse/leverage.h b/src/external/PackedCSparse/leverage.h deleted file mode 100644 index 69549308..00000000 --- a/src/external/PackedCSparse/leverage.h +++ /dev/null @@ -1,65 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -#pragma once -#include "SparseMatrix.h" -#include "projinv.h" -#include "outerprod.h" - -// Problem: -// Compute M = diag(A' inv(LL') A) - -namespace PackedCSparse { - template - struct LeverageOutput : DenseVector - { - ProjinvOutput Hinv; // Hinv = inv(H)|_L - OuterprodOutput tau; // tau = diag(A' Hinv A) - - template - void initialize(const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) - { - pcs_assert(L.initialized() && A.initialized() && At.initialized(), "leverage: bad inputs."); - pcs_assert(L.m == L.n && L.n == A.m && L.n == At.n && A.n == At.m, "leverage: dimensions mismatch."); - DenseVector::initialize(A.n); - Hinv.initialize(L); - tau.initialize(A, Hinv, At); - } - }; - - template - void leverage(LeverageOutput& o, const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) - { - if (!o.initialized()) - o.initialize(L, A, At); - - Tx T1 = Tx(1.0), T2 = Tx(2.0); - projinv(o.Hinv, L); - - Ti m = A.m, n = A.n; - Ti* Sp = o.Hinv.p.get(); Tx* Sv = o.Hinv.x.get(); - for (Ti k = 0; k < m; ++k) - Sv[Sp[k]] /= T2; - - outerprod(o.tau, A, o.Hinv, At); - - Tx* x = o.x.get(), * tau = o.tau.x.get(); - for (Ti j = 0; j < n; j++) - x[j] = T2 * tau[j]; - } - - - template - LeverageOutput leverage(const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) - { - LeverageOutput o; - leverage(o, L, A, At); - return o; - } -} diff --git a/src/external/PackedCSparse/leverageJL.h b/src/external/PackedCSparse/leverageJL.h deleted file mode 100644 index a5001bcd..00000000 --- a/src/external/PackedCSparse/leverageJL.h +++ /dev/null @@ -1,148 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -#pragma once -#include -#include "SparseMatrix.h" - -// Problem: -// Approximate M = diag(A' inv(LL') A) -namespace PackedCSparse { - const size_t JLPackedSize = 4; - - template - struct LeverageJLOutput : DenseVector - { - UniqueAlignedPtr d; // random direction d - UniqueAlignedPtr L_d; // random direction d - UniqueAlignedPtr AtL_d; // A' L^{-1} d - Ti m = 0; - std::mt19937_64 gen; - - template - void initialize(const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) - { - pcs_assert(L.initialized() && A.initialized() && At.initialized(), "leverageJL: bad inputs."); - pcs_assert(L.m == L.n && L.n == A.m && L.n == At.n && A.n == At.m, "leverageJL: dimensions mismatch."); - this->n = A.n; this->m = A.m; - this->x.reset(pcs_aligned_new(this->n)); - this->d.reset(pcs_aligned_new(this->m * 2 * JLPackedSize)); - this->L_d.reset(pcs_aligned_new(this->m * 2 * JLPackedSize)); - this->AtL_d.reset(pcs_aligned_new(this->n * 2 * JLPackedSize)); - } - }; - - // compute sum_{j=1}^{k} (A' L^{-T} u_j) .* (A' L^{-T} u_j) - template - void projectionJL(LeverageJLOutput& o, const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) - { - Ti m = A.m, n = A.n; - Tx T0 = Tx(0.0), T1 = Tx(1.0); - Tx* d = o.d.get(), * L_d = o.L_d.get(), * AtL_d = o.AtL_d.get(), * x = o.x.get(); - - for (Ti i = 0; i < m * k; i++) - d[i] = sign(o.gen); - - for (Ti i = 0; i < n * k; i++) - AtL_d[i] = T0; - - ltsolve(L, (BaseImpl*)d, (BaseImpl*)L_d); - gaxpy(At, (BaseImpl*)L_d, (BaseImpl*)AtL_d); - - for (Ti i = 0; i < n; i++) - { - Tx ret_i = T0; - for (Ti j = 0; j < k; j++) - ret_i += AtL_d[i * k + j] * AtL_d[i * k + j]; - - x[i] += ret_i; - } - } - - template - void leverageJL(LeverageJLOutput& o, const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At, size_t k) - { - if (!o.initialized()) - o.initialize(L, A, At); - - Ti n = A.n; Tx* x = o.x.get(); - for (Ti i = 0; i < n; i++) - x[i] = Tx(0.0); - - constexpr size_t k_step = JLPackedSize; - for(size_t i = 1; i <= k / k_step; i++) - projectionJL(o, L, A, At); - - for (size_t i = 1; i <= k % k_step; i++) - projectionJL<1>(o, L, A, At); - - Tx ratio = Tx(1 / double(k)); - for (Ti i = 0; i < n; i++) - x[i] *= ratio; - } - - template - LeverageJLOutput leverageJL(const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At, size_t k) - { - LeverageJLOutput o; - leverageJL(o, L, A, At, k); - return o; - } - - - // compute (A' L^{-T} u_j) .* (A' L^{-T} v_j) for j = 1, 2, ... k - template - Tx cholAccuracy(LeverageJLOutput& o, const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At, const Tx* w) - { - if (!o.initialized()) - o.initialize(L, A, At); - - constexpr Ti k = JLPackedSize; - constexpr Ti k_ = 2 * k; - - - Ti m = A.m, n = A.n; - Tx T0 = Tx(0.0), T1 = Tx(1.0); - Tx* d = o.d.get(), * L_d = o.L_d.get(), * AtL_d = o.AtL_d.get(), * x = o.x.get(); - - std::uniform_real_distribution distribution(-sqrt(3.0),sqrt(3.0)); - for (Ti i = 0; i < m * k_; i++) - d[i] = Tx(distribution(o.gen)); // roughly uniform distribution with variance 1 - - for (Ti i = 0; i < n * k_; i++) - AtL_d[i] = T0; - - ltsolve(L, (BaseImpl*)d, (BaseImpl*)L_d); - gaxpy(At, (BaseImpl*)L_d, (BaseImpl*)AtL_d); - - Tx result[k]; - for (Ti j = 0; j < k; j++) - result[j] = Tx(0.0); - - for (Ti i = 0; i < m; i++) - { - Tx* d = o.d.get() + i * (2 * k); - for (Ti j = 0; j < k; j++) - result[j] -= d[j] * d[j + k]; - } - - for (Ti i = 0; i < n; i++) - { - Tx w_i = w[i]; - for (Ti j = 0; j < k; j++) - result[j] += AtL_d[i * k_ + j] * AtL_d[i * k_ + j + k] * w_i; - } - - Tx est = Tx(0.0); - for (Ti j = 0; j < k; j++) - est += result[j] * result[j]; - - return clipped_sqrt(est/Tx(double(k)), 0.0); - } -} diff --git a/src/external/PackedCSparse/multiply.h b/src/external/PackedCSparse/multiply.h deleted file mode 100644 index cfd53375..00000000 --- a/src/external/PackedCSparse/multiply.h +++ /dev/null @@ -1,142 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -#pragma once -#include -#include -#include "SparseMatrix.h" - -// Problem: -// Compute M = A diag(w) B - -// Algorithm: -// Compute M col by col - -namespace PackedCSparse { - template - struct MultiplyOutput : SparseMatrix - { - UniqueAlignedPtr c; - - template - void initialize(const SparseMatrix& A, const SparseMatrix& B) - { - pcs_assert(A.initialized() && B.initialized(), "multiply: bad inputs."); - pcs_assert(A.n == B.m, "multiply: dimensions mismatch."); - - Ti m = A.m, n = B.n; - Ti* Ap = A.p.get(), * Ai = A.i.get(); - Ti* Bp = B.p.get(), * Bi = B.i.get(); - - this->c.reset(pcs_aligned_new(m)); - - Ti* last_j = new Ti[m]; - for (Ti i = 0; i < m; i++) - { - last_j[i] = -1; - this->c[i] = Tx(0.0); - } - - Ti* Cp = new Ti[size_t(n)+1]; - std::vector Ci; - - Cp[0] = 0; - for (Ti j1 = 0; j1 < n; j1++) - { - for (Ti p1 = Bp[j1]; p1 < Bp[j1 + 1]; p1++) - { - Ti j2 = Bi[p1]; - for (Ti p2 = Ap[j2]; p2 < Ap[j2 + 1]; p2++) - { - Ti i = Ai[p2]; - if (last_j[i] != j1) - { - last_j[i] = j1; - Ci.push_back(i); - } - } - } - Cp[j1 + 1] = Ti(Ci.size()); - } - delete[] last_j; - - for (Ti j = 0; j < n; j++) - std::sort(Ci.begin() + Cp[j], Ci.begin() + Cp[j + 1]); - - this->m = m; this->n = n; - this->x.reset(pcs_aligned_new(Ci.size())); - this->p.reset(Cp); - this->i.reset(new Ti[Ci.size()]); - std::copy(Ci.begin(), Ci.end(), this->i.get()); - } - }; - - template - void multiply_general(MultiplyOutput& o, const SparseMatrix& A, const Tx2* w, const SparseMatrix& B) - { - if (!o.initialized()) - o.initialize(A, B); - - Ti m = o.m, n = o.n; - Ti* Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); - Ti* Bp = B.p.get(), * Bi = B.i.get(); Tx* Bx = B.x.get(); - Ti* Cp = o.p.get(), * Ci = o.i.get(); Tx2* Cx = o.x.get(); - Tx2* c = o.c.get(); // initialized to 0 - - const Tx2 T0 = Tx2(0); - for (Ti j1 = 0; j1 < n; j1++) - { - for (Ti p1 = Bp[j1]; p1 < Bp[j1 + 1]; p1++) - { - Ti j2 = Bi[p1]; - Tx2 beta = has_weight? (Tx2(Bx[p1]) * w[j2]) : Tx2(Bx[p1]); - - for (Ti p2 = Ap[j2]; p2 < Ap[j2 + 1]; p2++) - { - //x[Ai[p2]] += beta * Ax[p2]; - fmadd(c[Ai[p2]], beta, Ax[p2]); - } - } - - for (Ti p1 = Cp[j1]; p1 < Cp[j1 + 1]; p1++) - { - Cx[p1] = c[Ci[p1]]; - c[Ci[p1]] = T0; // ensure c is 0 after the call - } - } - } - - template - void multiply(MultiplyOutput& o, const SparseMatrix& A, const SparseMatrix& B) - { - multiply_general(o, A, nullptr, B); - } - - template - void multiply(MultiplyOutput& o, const SparseMatrix& A, const Tx2* w, const SparseMatrix& B) - { - multiply_general(o, A, w, B); - } - - template - MultiplyOutput multiply(const SparseMatrix& A, const Tx2* w, const SparseMatrix& B) - { - MultiplyOutput o; - multiply(o, A, w, B); - return o; - } - - template - MultiplyOutput multiply(const SparseMatrix& A, const SparseMatrix& B) - { - MultiplyOutput o; - multiply(o, A, B); - return o; - } -} diff --git a/src/external/PackedCSparse/outerprod.h b/src/external/PackedCSparse/outerprod.h deleted file mode 100644 index 6f0e98e3..00000000 --- a/src/external/PackedCSparse/outerprod.h +++ /dev/null @@ -1,86 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis -#pragma once -#include "SparseMatrix.h" - -// Problem: -// Compute x = diag(At S Bt) - -// Algorithm: -// Note that x = diag(B St A) = grad_H Tr(St A H B) -// We run autodiff on the function Tr(St A H B). -// Hence, the algorithm is essentially same as multiply(A, B) with the same runtime. - -namespace PackedCSparse { - template - struct OuterprodOutput : DenseVector - { - UniqueAlignedPtr s_col; - UniquePtr s_mark; - - template - void initialize(const SparseMatrix& A, const SparseMatrix& S, const SparseMatrix& B) - { - pcs_assert(A.initialized() && B.initialized() && S.initialized(), "outerprod: bad inputs."); - pcs_assert(A.m == S.m && S.n == B.n, "outerprod: dimensions mismatch."); - - DenseVector::initialize(A.n); - s_col.reset(pcs_aligned_new(S.m)); - s_mark.reset(new Ti[S.m]); - } - }; - - template - void outerprod(OuterprodOutput& o, const SparseMatrix& A, const SparseMatrix& S, const SparseMatrix& B) - { - if (!o.initialized()) - o.initialize(A, S, B); - - Ti Sn = S.n, Sm = S.m, An = A.n; - Ti* Ap = A.p.get(), * Ai = A.i.get(); Tx2* Ax = A.x.get(); - Ti* Bp = B.p.get(), * Bi = B.i.get(); Tx2* Bx = B.x.get(); - Ti* Sp = S.p.get(), * Si = S.i.get(); Tx* Sx = S.x.get(); - Tx* s_col = o.s_col.get(); - Ti* s_mark = o.s_mark.get(); - Tx* x = o.x.get(); - - std::fill(s_mark, s_mark + Sm, Ti(-1)); - std::fill(x, x + An, Tx(0.0)); - - for (Ti j = 0; j < Sn; j++) - { - for (Ti p = Sp[j]; p < Sp[j + 1]; p++) - { - s_col[Si[p]] = Sx[p]; - s_mark[Si[p]] = j; - } - - for (Ti p = Bp[j]; p < Bp[j + 1]; p++) - { - Ti i = Bi[p]; Tx b = Bx[p]; - for (Ti q = Ap[i]; q < Ap[i + 1]; q++) - { - Tx a = Ax[q]; Ti a_i = Ai[q]; - if (s_mark[a_i] == j) - { //x[i] += s_col[a_i] * a * b; - fmadd(x[i], s_col[a_i], a * b); - } - } - } - } - } - - template - OuterprodOutput outerprod(const SparseMatrix& A, const SparseMatrix& S, const SparseMatrix& B) - { - OuterprodOutput o; - outerprod(o, A, S, B); - return o; - } -} diff --git a/src/external/PackedCSparse/projinv.h b/src/external/PackedCSparse/projinv.h deleted file mode 100644 index a319a47e..00000000 --- a/src/external/PackedCSparse/projinv.h +++ /dev/null @@ -1,90 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis - -#pragma once -#include "SparseMatrix.h" - -// Problem: -// Compute inv(L L') restricted on L - -// Algorithm: -// We need to study this later as this is the bottleneck. -// Document it as a lyx. - -namespace PackedCSparse { - template - struct ProjinvOutput : SparseMatrix - { - TransposeOutput Lt; // sparsity pattern of the Lt - UniqueAlignedPtr w; // the row of L we are computing - UniquePtr c; // c[i] = index the last nonzero on column i in the current L - - void initialize(const SparseMatrix& L) - { - pcs_assert(L.initialized(), "chol: bad inputs."); - pcs_assert(L.n == L.m, "chol: dimensions mismatch."); - - // Copy the sparsity of L - SparseMatrix::operator=(std::move(L.clone())); - - // allocate workspaces - Ti n = L.n; - w.reset(pcs_aligned_new(n)); - c.reset(new Ti[n]); - Lt = transpose(L); - } - }; - - template - void projinv(ProjinvOutput& o, const SparseMatrix& L) - { - if (!o.initialized()) - o.initialize(L); - - Tx* Sx = o.x.get(); Ti n = o.n; - Ti* Li = L.i.get(), * Lp = L.p.get(); Tx* Lv = L.x.get(); - Ti* Lti = o.Lt.i.get(), * Ltp = o.Lt.p.get(); - Tx* w = o.w.get(); - Ti* c = o.c.get(); - Tx T0 = Tx(0), T1 = Tx(1); - - for (Ti k = 0; k < n; k++) - c[k] = Lp[k + 1] - 1; - - for (Ti k = n - 1; k != -1; k--) - { - for (Ti p = Lp[k] + 1; p < Lp[k + 1]; p++) - w[Li[p]] = Sx[p]; - - Tx sum = T1 / Lv[Lp[k]]; - for (Ti p = Ltp[k + 1] - 1; p != Ltp[k] - 1; p--) - { - Ti i = Lti[p], Lpi = Lp[i]; - - for (Ti q = Lp[i + 1] - 1; q != Lpi; q--) - fnmadd(sum, Lv[q], w[Li[q]]); - //sum -= Lv[q] * w[Li[q]]; - - sum = sum / Lv[Lpi]; - w[i] = sum; - Sx[c[i]] = sum; - c[i]--; - sum = T0; - } - } - } - - template - ProjinvOutput projinv(const SparseMatrix& L) - { - ProjinvOutput o; - projinv(o, L); - return o; - } -} diff --git a/src/external/PackedCSparse/qd/COPYING b/src/external/PackedCSparse/qd/COPYING deleted file mode 100644 index a20ad70e..00000000 --- a/src/external/PackedCSparse/qd/COPYING +++ /dev/null @@ -1,16 +0,0 @@ -This work was supported by the Director, Office of Science, Division -of Mathematical, Information, and Computational Sciences of the -U.S. Department of Energy under contract numbers DE-AC03-76SF00098 and -DE-AC02-05CH11231. - -Copyright (c) 2003-2009, The Regents of the University of California, -through Lawrence Berkeley National Laboratory (subject to receipt of -any required approvals from U.S. Dept. of Energy) All rights reserved. - -By downloading or using this software you are agreeing to the modified -BSD license that is in file "BSD-LBNL-License.doc" in the main ARPREC -directory. If you wish to use the software for commercial purposes -please contact the Technology Transfer Department at TTD@lbl.gov or -call 510-286-6457." - - diff --git a/src/external/PackedCSparse/qd/Makefile b/src/external/PackedCSparse/qd/Makefile deleted file mode 100644 index 791e92b9..00000000 --- a/src/external/PackedCSparse/qd/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -QD_CPPFLAGS=$(CPPFLAGS) -I$(R_INCLUDE_DIR) -march=native - -QD_SOURCES= bits.cc c_dd.cc c_qd.cc dd_const.cc dd_real.cc fpu.cc \ - qd_const.cc qd_real.cc util.cc - -QD_OBJECTS=$(QD_SOURCES:.cc=.o) - -libqd.a: $(QD_OBJECTS) - $(AR) rc libqd.a $(QD_OBJECTS) - -.cc.o: - $(CC) $(CFLAGS) $(CPICFLAGS) $(QD_CPPFLAGS) -c $< -o $@ - -clean: - rm -rf $(QD_OBJECTS) libqd.a diff --git a/src/external/PackedCSparse/qd/NEWS b/src/external/PackedCSparse/qd/NEWS deleted file mode 100644 index f32a7575..00000000 --- a/src/external/PackedCSparse/qd/NEWS +++ /dev/null @@ -1,181 +0,0 @@ -Changes for 2.3.22 - Made changes suggested by Vasiliy Sotnikov - -Changes for 2.3.21 - Changed renorm in include/qd/qd_inline.h - -Changes for 2.3.20 - added #include to quadt_test.cpp - changed references to 2.3.20 from 2.3.18 - -Changes for 2.3.19 - - Updated qd_real.cpp and dd_real.cpp to fix a buffer overflow problem. - -Changes for 2.3.18 - - Updated qd_real.cpp and dd_real.cpp to fix a problem in output. - -Changes for 2.3.17 - - updated qd_real.cpp, to fix a problem with improper treatment of - negative arguments in nroot. - -Changes for 2.3.16 - - Updated dd_real.cpp, to fix a problem with inaccurate values of - tanh for small arguments. - -Changes for 2.3.15 - - Updated qd_real.cpp, to fix a problem with static definitions. - -Changes for 2.3.14 - - Updated autoconfig (replaced config.sub and config.guess) - -Changes for 2.3.7 - - Fixed bug in to_digits where digits larger than 10 - where output occasionally. - -Changes for 2.3.6 - - Added fmod (C++) and mod (Fortran) functions. - -Changes for 2.3.5 - - Fixed bug in division of qd_real by dd_real. - - Fixed bug in ddoutc (Fortran ddmod.f). - - Now compiles with g++ 4.3. - - Distribute tests/coeff.dat. - -Changes for 2.3.4 - - Fixed bug in Makefile for cygwin / mingw systems. - -Changes for 2.3.3 - - Fixed bug in atan2. - -Changes for 2.3.2 - - Fixed bug in sin / cos / sincos where too much accuracy was - lost for (moderately) large angles. - - Use fused-multiply add intrinsics on IA-64 platforms if - compiled by Intel compiler. - - Fixed bug in c_dd_write and c_qd_write. - - Fixed bug were qdext.mod was not being installed. - -Changes for 2.3.1 - - Fixed bug in sincos and cos_taylor. This affected the result - of trigonometric functions in some cases. - -Changes for 2.3.0 - This is a fairly significant change, breaking API compatibility. - - Moved C++ main entry in libqdmod.a to libqd_f_main.a. - This allows to link Fortran code using QD with custom - C++ main function. Pure Fortran code will need to be linked - with qd_f_main library in addition to qdmod and qd library. - - Constructors accepting pointers made explicit. - - Fortran routines labeled as elemental or pure, where appropriate. - - Write() is now to_string(), and now takes a single fmtflag. - - dd_real addition and multiplication made commutative. - - dd_real now represented as array of two doubles, instead of - two discrete scalars. - - New Fortran generic routines to read / write, operations with - complex and integers. - - Improved exp, sin, and cos functions. - - Removed unused constants and obscure constants only used internally - from public interface. - -Changes for 2.2.6 - - Fixed bug in mixed precision multiplication: qd_real * dd_real. - -Changes for 2.2.5 - - Bug fix in qd_real addition when --enable-ieee-add is specified. - - Debugging routines dump and dump_bits updated; - dump_components removed (just use dump). - - Fortran support for Fortran strings. Use character arrays instead. - - Return NaN under error conditions. - - Added _inf constant; exp now returns Inf when argument is too large. - - Output formatting fixes for Inf and NaNs. - - Added more real-complex mixed arithmetic routines in Fortran - interface. - -Changes for 2.2.4 - - Added random_number interface for Fortran modules. - - Use slightly more conservative values for eps. - - Avoid unnecessary overflow near overflow threshold. - - Added radix, digits, min/maxexponent, range, and precision - intrinsics to Fortran interface. - - Added safe_max (C++) and safe_huge (Fortran). - -Changes for 2.2.3 - - Fix sign function bug in Fortran modules. - -Changes for 2.2.2 - - Do not bother setting uninitialized dd_real and qd_reals to zero. - - Use clock_gettime if available for timing. - - Fortran I/O should be more consistent with C++ version. - - fpu.h is now included with dd_real.h. - -Changes for 2.2.1 - - Minor fixes when printing in scientific format. - - Change search order of C++ compilers in Apple systems to avoid - case insensitive filesystems. - -Changes for 2.2.0 - - Added F95 interface for complex types. - - Renamed dd.h and qd.h to dd_real.h and qd_real.h, respectively. - This will break older C++ code using 2.1.x library, but it was - conflicting with QuickDraw libraries on Macs. (Hence the version - bump to 2.2). - - Removed overloaded typecast operators for int and double. These - permitted *automatic* conversion of dd_real/qd_real to double or - int, which is somewhat dangerous. Instead to_int and to_double - routines are added. - -Changes for 2.1.214 - - Updated pslq_test. - - Implmented numeric_limits<>. - - Better polyroot. - - Added isnan, isfinite, isinf functions. - - Fix / improve input output functions. - - Drop Microsoft Visual C++ 6.0 support. - - More efficient dd_real::sin. - -Changes for 2.1.213 - - Support for x86_64 platforms. - - Drop libtool support for now. - -Changes for 2.1.212 - - Support for pathCC compiler. - - Added accurate and sloppy versions of add / sub / mul / div avaialble. - - Added autodetection of fma functions. - -Changes for 2.1 (2003-12-30) - - added automake scripts. - - use libtool to compile / link and build libraries. - - supports standard installation targets (make install). - - support for Intel C++ compilers (icc / ecc). - - Fortran programs are now linked by C++ compiler. - - support for building shared library. - - minor bug fixes. - -Changes for 2.0 (2003-12-08) - - all header files are in "include/qd" directory. - - added autoconf scripts. - - added config.h and qd_config.h to store configuration information. - - renamed x86_* routines to fpu_* routines. - - added separate Fortran interface (f_* routines). - - options for sloppy multiply and sloppy divison separated. - - fixed C interface to be actually in C syntax. - - updated / added README, AUTHORS, NEWS, and LICENSE files. - - minor bug fixes. - -Changes for 1.2 (2003-12-04) - - added "dist-clean" target in Makefile - - initialize dd and qd variables to zero - - increases tolerance for qd / dd tests - - changed .cc extension to .cpp - - updated README, COPYING, and NEWS files - - added ChangeLog file - - fixed bug in '-all' flag in qd_test - - minor bug fixes - -Changes for 1.1 (2002-10-22) - - added "Changes" file (this file) - - fixed to - - fixed constant (3/4) * pi - - fixed exp(x) to return zero if x is a large negative number - - removed "docs" target in Makefile - diff --git a/src/external/PackedCSparse/qd/README b/src/external/PackedCSparse/qd/README deleted file mode 100644 index 8a085d72..00000000 --- a/src/external/PackedCSparse/qd/README +++ /dev/null @@ -1,437 +0,0 @@ -Quad Double computation package -Copyright (C) 2003-2019 -================================================ - -Revision date: 26 February 2019 - -Authors: -Yozo Hida U.C. Berkeley yozo@cs.berkeley.edu -Xiaoye S. Li Lawrence Berkeley Natl Lab xiaoye@nersc.gov -David H. Bailey Lawrence Berkeley Natl Lab dhbailey@lbl.gov - -C++ usage guide: -Alex Kaiser Lawrence Berkeley Natl Lab adkaiser@lbl.gov - -This work was supported by the Director, Office of Science, Division of Mathematical, -Information, and Computational Sciences of the U.S. Department of Energy under contract -number DE-AC02-05CH11231. - -This work was supported by the Director, Office of Science, Division of Mathematical, -Information, and Computational Sciences of the U.S. Department of Energy under contract -numbers DE-AC03-76SF00098 and DE-AC02-05CH11231. - -*** IMPORTANT NOTES: - -See the file COPYING for modified BSD license information. -See the file INSTALL for installation instructions. -See the file NEWS for recent revisions. -See the file docs/qd.pdf for additional information. - -Outline: - -I. Introduction -II. Installation of package, and linking and executing user files -III. C++ Usage -IV. Fortran Usage -V. Note on x86-Based Processors (MOST systems in use today) - - -I. Introduction - -This package provides numeric types of twice the precision of IEEE double (106 mantissa -bits, or approximately 32 decimal digits) and four times the precision of IEEE double (212 -mantissa bits, or approximately 64 decimal digits). Due to features such as operator and -function overloading, these facilities can be utilized with only minor modifications to -conventional C++ and Fortran-90 programs. - -In addition to the basic arithmetic operations (add, subtract, multiply, divide, square root), -common transcendental functions such as the exponential, logarithm, trigonometric and -hyperbolic functions are also included. A detailed description of the algorithms used is -available in the docs subdirectory (see docs/qd.pdf). An abridged version of this paper, -which was presented at the ARITH-15 conference, is also available at: - -Yozo Hida, Xiaoye S. Li and David H. Bailey, "Algorithms for quad-double precision - floating point arithmetic," 15th IEEE Symposium on Computer Arithmetic, IEEE Computer - Society, 2001, pg. 155-162, available at - https://www.davidhbailey.com/dhbpapers/arith15.pdf. - - -II. Installation of package, and linking and executing user files - -A. Directories - -There are six directories and several files in the main directory of this distribution, -described below - -src This contains the source code of the quad-double and double-double - library. This source code does not include inline functions, - which are found in the header files in the include directory. - -include This directory contains the header files. - -fortran This directory contains Fortran-90 files. - -tests This directory contains some simple (not comprehensive) tests. - -docs This directory contains a technical paper describing the algorithms. - -config This directory contains various scripts used by the configure - script and the Makefile. - -Please note that all commands refer to a Unix-type environment such as Mac OSX or Ubuntu -Linux using the bash shell. - -B. Installing and building - -To build the library, first run the included configure script by typing - - ./configure - -This script automatically generates makefiles for building the library and selects compilers -and necessary flags and libraries to include. If the user wishes to specify compilers or flags -they may use the following options. - - CXX C++ compiler to use - CXXFLAGS C++ compiler flags to use - CC C compiler to use (for C demo program) - CFLAGS C compiler flags to use (for C demo program) - FC Fortran 90 compiler - FCFLAGS Fortran 90 compiler flags to use - FCLIBS Fortran 90 libraries needed to link with C++ code. - -For example, if one is using GNU compilers, configure with: - - ./configure CXX=g++ FC=gfortran - -The Fortran and C++ compilers must produce compatible binaries. On some systems -additional flags must be included to ensure that portions of the -library are not built with 32 and 64 bit object files. For example, on -64-Bit Mac OSX 10.6 (Snow Leopard) and 10.7 (Lion) the correct -configure line using GNU compilers is: - - ./configure CXX=g++ FC=gfortran FCFLAGS=-m64 - -To build the library, simply type - - make - -and the automatically generated makefiles will build the library including archive files. - -To allow for easy linking to the library, the user may also wish to -install the archive files to a standard place. To do this type: - - make install - -This will also build the library if it has not already been built. Many systems, including Mac -and Ubuntu Linux systems, require administrator privileges to install the library at such -standard places. On such systems, one may type: - - sudo make install - -instead if one has sufficient access. - -The directory "tests" contains programs for high precision quadrature and integer-relation -detection. To build such programs, type: - - make demo - -in the "tests" directory. - -C. Linking and executing user programs - -C++ source files: - -The simplest way to link to the library is to install it to a standard place as described above, and use the -l option. For example - - g++ compileExample.cpp -o compileExample -l qd - -One can also use this method to build with make. A file called "compileExample.cpp" and the -associated makefile "makeCompileExample" illustrate the process. - -A third alternative is to use a link script. If one types "make demo" in the test -directory, the output produced gives guidance as to how to build the files. By -following the structure of the compiling commands one may copy the appropriate portions, -perhaps replacing the filename with an argument that the user can include at link time. -An example of such a script is as follows: - -g++ -DHAVE_CONFIG_H -I.. -I../include -I../include -O2 -MT $1.o -MD -MP -MF -.deps/qd_test.Tpo -c -o $1.o $1.cpp -mv -f .deps/$1.Tpo .deps/$1.Po -g++ -O2 -o $1 $1.o ../src/libqd.a -lm - -To use the link script, make it executable (by typing "chmod +x link.scr) and then type: - -./link.scr compileExample - -Note that the file extension is not included because the script handles all extensions, -expecting the source file to have the extension ".cpp". - -Fortran-90 source files: - -Similarly, a script for compiling fortran programs may be constructed as follows. -In the fortran directory, type "make quadtsq". This compiles the Fortran program -tquadts.f, links with all necessary library files, and produces the executable -"quadts". As this is being done, all flags and linked libraries are displayed. -For instance, on a 2019-era Apple Macintosh system, where the library was installed -as above with g++ for C++ and gfortran for Fortran-90, the following is output: - -gfortran -m64 -ffree-form -c -o tquadtsq.o tquadtsq.f -/bin/sh ../libtool --tag=CXX --mode=link g++ -O2 -o quadtsq tquadtsq.o second.o -libqdmod.la libqd_f_main.la ../src/libqd.la --L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0 --L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0/../../.. --lgfortran -lquadmath -lm -lm - -Thus a general compile-link script is the following: - -gfortran -m64 -ffree-form -c -o $1.o $1.f90 -/bin/sh ../libtool --tag=CXX --mode=link g++ -O2 -o $1 $1.o second.o \ - libqdmod.la libqd_f_main.la ../src/libqd.la \ - -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0 \ - -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0/../../.. \ - -lgfortran -lquadmath -lm -lm - -Note that if the .f90 suffix is used for Fortran-90 source files, the --ffree-form flag may be omitted, but the first line above should end with -"$1.f90" (as shown above). After forming the script, name file, "complink.scr", -and then type "chmod +x complink.scr". To use this script compile and link a -program named "prog.f90", type "./complink.scr prog". - - -III. C++ usage - -As much as possible, operator overloading is included to make basic programming as much -like using standard typed floating-point arithmetic. Changing many codes should be as -simple as changing type statements and a few other lines. - -i. Constructors - -To create dd_real and qd_real variables calculated to the proper precision, one must use -care to use the included constructors properly. Many computations in which variables are -not explicitly typed to multiple-precision may be evaluated with double-precision -arithmetic. The user must take care to ensure that this does not cause errors. In particular, -an expression such as 1.0/3.0 will be evaluated to double precision before assignment or -further arithmetic. Upon assignment to a multi-precision variable, the value will be zero -padded. This problem is serious and potentially difficult to debug. To avoid this, use the -included constructors to force arithmetic to be performed in the full precision requested. - -For a table with descriptions, please see the documentation file qd.pdf in the docs directory. - -ii. Included functions and Constants - -Supported functions include assignment operators, comparisons, arithmetic and -assignment operators, and increments for integer types. Standard C math functions such as -exponentiation, trigonometric, logarithmic, hyperbolic, exponential and rounding functions -are included. As in assignment statements, one must be careful with implied typing of -constants when using these functions. Many codes need particular conversion for the power -function, which is frequently used with constants that must be explicitly typed for multi- -precision codes. - -Many constants are included, which are global and calculated upon initialization. The -following list of constants is calculated for both the dd_real and qd_real classes separately. -Use care to select the correct value. - -For a table with descriptions, please see the included file README.pdf - -ii. Conversion of types - -Static casts may be used to convert constants between types. One may also use constructors -to return temporary multi-precision types within expressions, but should be careful, as this -will waste memory if done repeatedly. For example: - - qd_real y ; - y = sin( qd_real(4.0) / 3.0 ) ; - -C-style casts may be used, but are not recommended. Dynamic and reinterpret casts are -not supported and should be considered unreliable. Casting between multi-precision and -standard precision types can be dangerous, and care must be taken to ensure that programs -are working properly and accuracy has not degraded by use of a misplaced type-conversion. - -D. Available precision, Control of Precision Levels, - -The library provides greatly extended accuracy when compared to standard double -precision. The type dd_real provides for 106 mantissa bits, or about 32 decimal digits. The -type qd_real provides for 212 mantissa bits, or about 64 decimal digits. - -Both the dd_real and qd_real values use the exponent from the highest double-precision -word for arithmetic, and as such do not extend the total range of values available. That -means that the maximum absolute value for either data type is the same as that of double- -precision, or approximately 10^308. The precision near this range, however, is greatly -increased. - -E. I/O - -The standard I/O stream routines have been overloaded to be fully compatible with all -included data types. One may need to manually reset the precision of the stream to obtain -full output. For example, if 60 digits are desired, use: - -cout.precision(60) ; - -When reading values using cin, each input numerical value must start on a separate -line. Two formats are acceptable: - - 1. Write the full constant - 3. Mantissa e exponent - -Here are three valid examples: - - 1.1 - 3.14159 26535 89793 - 123.123123e50 - -When read using cin, these constants will be converted using full multi-precision accuracy. - - -IV. Fortran-90 Usage - -NEW (2007-01-10): The Fortran translation modules now support the complex datatypes -"dd_complex" and "qd_complex". - -Since the quad-double library is written in C++, it must be linked in with a C++ compiler (so -that C++ specific things such as static initializations are correctly handled). Thus the main -program must be written in C/C++ and call the Fortran 90 subroutine. The Fortran 90 -subroutine should be called f_main. - -Here is a sample Fortran-90 program, equivalent to the above C++ program: - - subroutine f_main - use qdmodule - implicit none - type (qd_real) a, b - a = 1.d0 - b = cos(a)**2 + sin(a)**2 - 1.d0 - call qdwrite(6, b) - stop - end subroutine - -This verifies that cos^2(1) + sin^2(1) = 1 to 64 digit accuracy. - -Most operators and generic function references, including many mixed-mode type -combinations with double-precision (ie real*8), have been overloaded (extended) to work -with double-double and quad-double data. It is important, however, that users keep in -mind the fact that expressions are evaluated strictly according to conventional Fortran -operator precedence rules. Thus some subexpressions may be evaluated only to 15-digit -accuracy. For example, with the code - - real*8 d1 - type (dd_real) t1, t2 - ... - t1 = cos (t2) + d1/3.d0 - -the expression d1/3.d0 is computed to real*8 accuracy only (about 15 digits), since both d1 -and 3.d0 have type real*8. This result is then converted to dd_real by zero extension before -being added to cos(t2). So, for example, if d1 held the value 1.d0, then the quotient d1/3.d0 -would only be accurate to 15 digits. If a fully accurate double-double quotient is required, -this should be written: - - real*8 d1 - type (dd_real) t1, t2 - ... - t1 = cos (t2) + ddreal (d1) / 3.d0 - -which forces all operations to be performed with double-double arithmetic. - -Along this line, a constant such as 1.1 appearing in an expression is evaluated only to real*4 -accuracy, and a constant such as 1.1d0 is evaluated only to real*8 accuracy (this is -according to standard Fortran conventions). If full quad-double accuracy is required, for -instance, one should write - - type (qd_real) t1 - ... - t1 = '1.1' - -The quotes enclosing 1.1 specify to the compiler that the constant is to be converted to -binary using quad-double arithmetic, before assignment to t1. Quoted constants may only -appear in assignment statements such as this. - -To link a Fortran-90 program with the C++ qd library, it is recommended to link with the -C++ compiler used to generate the library. The Fortran 90 interface (along with a C-style -main function calling f_main) is found in qdmod library. The qd-config script installed -during "make install" can be used to determine which flags to pass to compile and link your -programs: - - "qd-config --fcflags" displays compiler flags needed to compile your Fortran files. - "qd-config --fclibs" displays linker flags needed by the C++ linker to link in all the -necessary libraries. - -A sample Makefile that can be used as a template for compiling Fortran programs using -quad-double library is found in fortran/Makefile.sample. - -F90 functions defined with dd_real arguments: - Arithmetic: + - * / ** - Comparison tests: == < > <= >= /= - Others: abs, acos, aint, anint, asin, atan, atan2, cos, cosh, dble, erf, - erfc, exp, int, log, log10, max, min, mod, ddcsshf (cosh and sinh), - ddcssnf (cos and sin), ddranf (random number generator in (0,1)), - ddnrtf (n-th root), sign, sin, sinh, sqr, sqrt, tan, tanh - -Similar functions are provided for qd_real arguments with function names qdcsshf, -qdcssnf, qdranf and qdnrtf instead of the names in the list above. - -Input and output of double-double and quad-double data is done using the special -subroutines ddread, ddwrite, qdread and qdwrite. The first argument of these subroutines -is the Fortran I/O unit number, while additional arguments (as many as needed, up to 9 -arguments) are scalar variables or array elements of the appropriate type. Example: - - integer n - type (qd_real) qda, qdb, qdc(n) - ... - call qdwrite (6, qda, qdb) - do j = 1, n - call qdwrite (6, qdc(j)) - enddo - -Each input values must be on a separate line, and may include D or E exponents. Double- -double and quad-double constants may also be specified in assignment statements by -enclosing them in quotes, as in - - ... - type (qd_real) pi - ... - pi = -"3.14159265358979323846264338327950288419716939937510582097494459230" - ... - -Sample Fortran-90 programs illustrating some of these features are provided in the f90 -subdirectory. - - -V. Note on x86-Based Processors (MOST systems in use today) - -The algorithms in this library assume IEEE double precision floating point arithmetic. Since -Intel x86 processors have extended (80-bit) floating point registers, some compilers, -albeit a declining number, may generate commands for the 80-bit instructions. The QD -library does NOT work correctly with 80-bit instructions, so if one's code does not operate -correctly, this may be the reason. To avoid such problems, the round-to-double flag must be -enabled in the control word of the FPU for this library to function properly. The following -functions contains appropriate code to facilitate manipulation of this flag. For non-x86 -systems these functions do nothing (but still exist). - -fpu_fix_start This turns on the round-to-double bit in the control word. -fpu_fix_end This restores the control flag. - -These functions must be called by the main program, as follows: - - int main() { - unsigned int old_cw; - fpu_fix_start(&old_cw); - - ... user code using quad-double library ... - - fpu_fix_end(&old_cw); - } - -A Fortran-90 example is the following: - - subroutine f_main - use qdmodule - implicit none - integer*4 old_cw - - call f_fpu_fix_start(old_cw) - - ... user code using quad-double library ... - - call f_fpu_fix_end(old_cw) - end subroutine - diff --git a/src/external/PackedCSparse/qd/bits.cc b/src/external/PackedCSparse/qd/bits.cc deleted file mode 100644 index 4eaf9a26..00000000 --- a/src/external/PackedCSparse/qd/bits.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * src/bits.cc - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Defines various routines to get / set bits of a IEEE floating point - * number. This used by the library for debugging purposes. - */ - -#include -#include -#include -#include - -#include "qd_config.h" -#include "inline.h" -#include "bits.h" - -#ifdef HAVE_IEEEFP_H -#include -#endif - -using std::setw; - -int get_double_expn(double x) { - if (x == 0.0) - return INT_MIN; - if (QD_ISINF(x) || QD_ISNAN(x)) - return INT_MAX; - - double y = std::abs(x); - int i = 0; - if (y < 1.0) { - while (y < 1.0) { - y *= 2.0; - i++; - } - return -i; - } else if (y >= 2.0) { - while (y >= 2.0) { - y *= 0.5; - i++; - } - return i; - } - return 0; -} - -void print_double_info(std::ostream &os, double x) { - std::streamsize old_prec = os.precision(19); - std::ios_base::fmtflags old_flags = os.flags(); - os << std::scientific; - - os << setw(27) << x << ' '; - if (QD_ISNAN(x) || QD_ISINF(x) || (x == 0.0)) { - os << " "; - } else { - - x = std::abs(x); - int expn = get_double_expn(x); - double d = std::ldexp(1.0, expn); - os << setw(5) << expn << " "; - for (int i = 0; i < 53; i++) { - if (x >= d) { - x -= d; - os << '1'; - } else - os << '0'; - d *= 0.5; - } - - if (x != 0.0) { - // should not happen - os << " +trailing stuff"; - } - } - - os.precision(old_prec); - os.flags(old_flags); -} - diff --git a/src/external/PackedCSparse/qd/bits.h b/src/external/PackedCSparse/qd/bits.h deleted file mode 100644 index 58570aac..00000000 --- a/src/external/PackedCSparse/qd/bits.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * include/bits.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * This file defines various routines to get / set bits of a IEEE floating - * point number. This is used by the library for debugging purposes. - */ - -#ifndef _QD_BITS_H -#define _QD_BITS_H - -#include -#include "qd_config.h" - -/* Returns the exponent of the double precision number. - Returns INT_MIN is x is zero, and INT_MAX if x is INF or NaN. */ -int get_double_expn(double x); - -/* Prints - SIGN EXPN MANTISSA - of the given double. If x is NaN, INF, or Zero, this - prints out the strings NaN, +/- INF, and 0. */ -void print_double_info(std::ostream &os, double x); - - -#endif /* _QD_BITS_H */ - diff --git a/src/external/PackedCSparse/qd/c_dd.cc b/src/external/PackedCSparse/qd/c_dd.cc deleted file mode 100644 index 0a7c12ac..00000000 --- a/src/external/PackedCSparse/qd/c_dd.cc +++ /dev/null @@ -1,314 +0,0 @@ -/* - * src/c_dd.cc - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Contains the C wrapper functions for double-double precision arithmetic. - * This can be used from Fortran code. - */ -#include - -#include "qd_config.h" -#include "dd_real.h" -#include "c_dd.h" - -#define TO_DOUBLE_PTR(a, ptr) ptr[0] = a.x[0]; ptr[1] = a.x[1]; - -extern "C" { - -/* add */ -void c_dd_add(const double *a, const double *b, double *c) { - dd_real cc; - cc = dd_real(a) + dd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_dd_add_dd_d(const double *a, double b, double *c) { - dd_real cc; - cc = dd_real(a) + b; - TO_DOUBLE_PTR(cc, c); -} -void c_dd_add_d_dd(double a, const double *b, double *c) { - dd_real cc; - cc = a + dd_real(b); - TO_DOUBLE_PTR(cc, c); -} - - -/* sub */ -void c_dd_sub(const double *a, const double *b, double *c) { - dd_real cc; - cc = dd_real(a) - dd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_dd_sub_dd_d(const double *a, double b, double *c) { - dd_real cc; - cc = dd_real(a) - b; - TO_DOUBLE_PTR(cc, c); -} -void c_dd_sub_d_dd(double a, const double *b, double *c) { - dd_real cc; - cc = a - dd_real(b); - TO_DOUBLE_PTR(cc, c); -} - - -/* mul */ -void c_dd_mul(const double *a, const double *b, double *c) { - dd_real cc; - cc = dd_real(a) * dd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_dd_mul_dd_d(const double *a, double b, double *c) { - dd_real cc; - cc = dd_real(a) * b; - TO_DOUBLE_PTR(cc, c); -} -void c_dd_mul_d_dd(double a, const double *b, double *c) { - dd_real cc; - cc = a * dd_real(b); - TO_DOUBLE_PTR(cc, c); -} - - -/* div */ -void c_dd_div(const double *a, const double *b, double *c) { - dd_real cc; - cc = dd_real(a) / dd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_dd_div_dd_d(const double *a, double b, double *c) { - dd_real cc; - cc = dd_real(a) / b; - TO_DOUBLE_PTR(cc, c); -} -void c_dd_div_d_dd(double a, const double *b, double *c) { - dd_real cc; - cc = a / dd_real(b); - TO_DOUBLE_PTR(cc, c); -} - - -/* copy */ -void c_dd_copy(const double *a, double *b) { - b[0] = a[0]; - b[1] = a[1]; -} -void c_dd_copy_d(double a, double *b) { - b[0] = a; - b[1] = 0.0; -} - - -void c_dd_sqrt(const double *a, double *b) { - dd_real bb; - bb = sqrt(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_sqr(const double *a, double *b) { - dd_real bb; - bb = sqr(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_abs(const double *a, double *b) { - dd_real bb; - bb = abs(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_npwr(const double *a, int n, double *b) { - dd_real bb; - bb = npwr(dd_real(a), n); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_nroot(const double *a, int n, double *b) { - dd_real bb; - bb = nroot(dd_real(a), n); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_nint(const double *a, double *b) { - dd_real bb; - bb = nint(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_aint(const double *a, double *b) { - dd_real bb; - bb = aint(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_floor(const double *a, double *b) { - dd_real bb; - bb = floor(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_ceil(const double *a, double *b) { - dd_real bb; - bb = ceil(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_log(const double *a, double *b) { - dd_real bb; - bb = log(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_log10(const double *a, double *b) { - dd_real bb; - bb = log10(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_exp(const double *a, double *b) { - dd_real bb; - bb = exp(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_sin(const double *a, double *b) { - dd_real bb; - bb = sin(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_cos(const double *a, double *b) { - dd_real bb; - bb = cos(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_tan(const double *a, double *b) { - dd_real bb; - bb = tan(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_asin(const double *a, double *b) { - dd_real bb; - bb = asin(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_acos(const double *a, double *b) { - dd_real bb; - bb = acos(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_atan(const double *a, double *b) { - dd_real bb; - bb = atan(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_atan2(const double *a, const double *b, double *c) { - dd_real cc; - cc = atan2(dd_real(a), dd_real(b)); - TO_DOUBLE_PTR(cc, c); -} - -void c_dd_sinh(const double *a, double *b) { - dd_real bb; - bb = sinh(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_cosh(const double *a, double *b) { - dd_real bb; - bb = cosh(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_tanh(const double *a, double *b) { - dd_real bb; - bb = tanh(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_asinh(const double *a, double *b) { - dd_real bb; - bb = asinh(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_acosh(const double *a, double *b) { - dd_real bb; - bb = acosh(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_dd_atanh(const double *a, double *b) { - dd_real bb; - bb = atanh(dd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_dd_sincos(const double *a, double *s, double *c) { - dd_real ss, cc; - sincos(dd_real(a), ss, cc); - TO_DOUBLE_PTR(ss, s); - TO_DOUBLE_PTR(cc, c); -} - -void c_dd_sincosh(const double *a, double *s, double *c) { - dd_real ss, cc; - sincosh(dd_real(a), ss, cc); - TO_DOUBLE_PTR(ss, s); - TO_DOUBLE_PTR(cc, c); -} - -void c_dd_read(const char *s, double *a) { - dd_real aa(s); - TO_DOUBLE_PTR(aa, a); -} - -void c_dd_swrite(const double *a, int precision, char *s, int len) { - dd_real(a).write(s, len, precision); -} - -void c_dd_write(const double *a) { - std::cout << dd_real(a).to_string(dd_real::_ndigits) << std::endl; -} - -void c_dd_neg(const double *a, double *b) { - b[0] = -a[0]; - b[1] = -a[1]; -} - -void c_dd_rand(double *a) { - dd_real aa; - aa = ddrand(); - TO_DOUBLE_PTR(aa, a); -} - -void c_dd_comp(const double *a, const double *b, int *result) { - dd_real aa(a), bb(b); - if (aa < bb) - *result = -1; - else if (aa > bb) - *result = 1; - else - *result = 0; -} - -void c_dd_comp_dd_d(const double *a, double b, int *result) { - dd_real aa(a), bb(b); - if (aa < bb) - *result = -1; - else if (aa > bb) - *result = 1; - else - *result = 0; -} - -void c_dd_comp_d_dd(double a, const double *b, int *result) { - dd_real aa(a), bb(b); - if (aa < bb) - *result = -1; - else if (aa > bb) - *result = 1; - else - *result = 0; -} - -void c_dd_pi(double *a) { - TO_DOUBLE_PTR(dd_real::_pi, a); -} - -} diff --git a/src/external/PackedCSparse/qd/c_dd.h b/src/external/PackedCSparse/qd/c_dd.h deleted file mode 100644 index 310162ed..00000000 --- a/src/external/PackedCSparse/qd/c_dd.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * include/c_dd.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Contains C wrapper function prototypes for double-double precision - * arithmetic. This can also be used from fortran code. - */ -#ifndef _QD_C_DD_H -#define _QD_C_DD_H - -#include "qd_config.h" -#include "fpu.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* add */ -void c_dd_add(const double *a, const double *b, double *c); -void c_dd_add_d_dd(double a, const double *b, double *c); -void c_dd_add_dd_d(const double *a, double b, double *c); - -/* sub */ -void c_dd_sub(const double *a, const double *b, double *c); -void c_dd_sub_d_dd(double a, const double *b, double *c); -void c_dd_sub_dd_d(const double *a, double b, double *c); - -/* mul */ -void c_dd_mul(const double *a, const double *b, double *c); -void c_dd_mul_d_dd(double a, const double *b, double *c); -void c_dd_mul_dd_d(const double *a, double b, double *c); - -/* div */ -void c_dd_div(const double *a, const double *b, double *c); -void c_dd_div_d_dd(double a, const double *b, double *c); -void c_dd_div_dd_d(const double *a, double b, double *c); - -/* copy */ -void c_dd_copy(const double *a, double *b); -void c_dd_copy_d(double a, double *b); - -void c_dd_sqrt(const double *a, double *b); -void c_dd_sqr(const double *a, double *b); - -void c_dd_abs(const double *a, double *b); - -void c_dd_npwr(const double *a, int b, double *c); -void c_dd_nroot(const double *a, int b, double *c); - -void c_dd_nint(const double *a, double *b); -void c_dd_aint(const double *a, double *b); -void c_dd_floor(const double *a, double *b); -void c_dd_ceil(const double *a, double *b); - -void c_dd_exp(const double *a, double *b); -void c_dd_log(const double *a, double *b); -void c_dd_log10(const double *a, double *b); - -void c_dd_sin(const double *a, double *b); -void c_dd_cos(const double *a, double *b); -void c_dd_tan(const double *a, double *b); - -void c_dd_asin(const double *a, double *b); -void c_dd_acos(const double *a, double *b); -void c_dd_atan(const double *a, double *b); -void c_dd_atan2(const double *a, const double *b, double *c); - -void c_dd_sinh(const double *a, double *b); -void c_dd_cosh(const double *a, double *b); -void c_dd_tanh(const double *a, double *b); - -void c_dd_asinh(const double *a, double *b); -void c_dd_acosh(const double *a, double *b); -void c_dd_atanh(const double *a, double *b); - -void c_dd_sincos(const double *a, double *s, double *c); -void c_dd_sincosh(const double *a, double *s, double *c); - -void c_dd_read(const char *s, double *a); -void c_dd_swrite(const double *a, int precision, char *s, int len); -void c_dd_write(const double *a); -void c_dd_neg(const double *a, double *b); -void c_dd_rand(double *a); -void c_dd_comp(const double *a, const double *b, int *result); -void c_dd_comp_dd_d(const double *a, double b, int *result); -void c_dd_comp_d_dd(double a, const double *b, int *result); -void c_dd_pi(double *a); - -#ifdef __cplusplus -} -#endif - -#endif /* _QD_C_DD_H */ diff --git a/src/external/PackedCSparse/qd/c_qd.cc b/src/external/PackedCSparse/qd/c_qd.cc deleted file mode 100644 index fd50e922..00000000 --- a/src/external/PackedCSparse/qd/c_qd.cc +++ /dev/null @@ -1,450 +0,0 @@ -/* - * src/c_qd.cc - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Contains C wrapper function for quad-double precision arithmetic. - * This can be used from fortran code. - */ -#include - -#include "qd_config.h" -#include "qd_real.h" -#include "c_qd.h" - -#define TO_DOUBLE_PTR(a, ptr) ptr[0] = a.x[0]; ptr[1] = a.x[1]; \ - ptr[2] = a.x[2]; ptr[3] = a.x[3]; - -extern "C" { - - - -/* add */ -void c_qd_add(const double *a, const double *b, double *c) { - qd_real cc; - cc = qd_real(a) + qd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_add_qd_dd(const double *a, const double *b, double *c) { - qd_real cc; - cc = qd_real(a) + dd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_add_dd_qd(const double *a, const double *b, double *c) { - qd_real cc; - cc = dd_real(a) + qd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_add_qd_d(const double *a, double b, double *c) { - qd_real cc; - cc = qd_real(a) + b; - TO_DOUBLE_PTR(cc, c); -} -void c_qd_add_d_qd(double a, const double *b, double *c) { - qd_real cc; - cc = a + qd_real(b); - TO_DOUBLE_PTR(cc, c); -} - - - -/* sub */ -void c_qd_sub(const double *a, const double *b, double *c) { - qd_real cc; - cc = qd_real(a) - qd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_sub_qd_dd(const double *a, const double *b, double *c) { - qd_real cc; - cc = qd_real(a) - dd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_sub_dd_qd(const double *a, const double *b, double *c) { - qd_real cc; - cc = dd_real(a) - qd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_sub_qd_d(const double *a, double b, double *c) { - qd_real cc; - cc = qd_real(a) - b; - TO_DOUBLE_PTR(cc, c); -} -void c_qd_sub_d_qd(double a, const double *b, double *c) { - qd_real cc; - cc = a - qd_real(b); - TO_DOUBLE_PTR(cc, c); -} - - - -/* mul */ -void c_qd_mul(const double *a, const double *b, double *c) { - qd_real cc; - cc = qd_real(a) * qd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_mul_qd_dd(const double *a, const double *b, double *c) { - qd_real cc; - cc = qd_real(a) * dd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_mul_dd_qd(const double *a, const double *b, double *c) { - qd_real cc; - cc = dd_real(a) * qd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_mul_qd_d(const double *a, double b, double *c) { - qd_real cc; - cc = qd_real(a) * b; - TO_DOUBLE_PTR(cc, c); -} -void c_qd_mul_d_qd(double a, const double *b, double *c) { - qd_real cc; - cc = a * qd_real(b); - TO_DOUBLE_PTR(cc, c); -} - - - -/* div */ -void c_qd_div(const double *a, const double *b, double *c) { - qd_real cc; - cc = qd_real(a) / qd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_div_qd_dd(const double *a, const double *b, double *c) { - qd_real cc; - cc = qd_real(a) / dd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_div_dd_qd(const double *a, const double *b, double *c) { - qd_real cc; - cc = dd_real(a) / qd_real(b); - TO_DOUBLE_PTR(cc, c); -} -void c_qd_div_qd_d(const double *a, double b, double *c) { - qd_real cc; - cc = qd_real(a) / b; - TO_DOUBLE_PTR(cc, c); -} -void c_qd_div_d_qd(double a, const double *b, double *c) { - qd_real cc; - cc = a / qd_real(b); - TO_DOUBLE_PTR(cc, c); -} - - - - -/* selfadd */ -void c_qd_selfadd(const double *a, double *b) { - qd_real bb(b); - bb += qd_real(a); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_selfadd_dd(const double *a, double *b) { - qd_real bb(b); - bb += dd_real(a); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_selfadd_d(double a, double *b) { - qd_real bb(b); - bb += a; - TO_DOUBLE_PTR(bb, b); -} - - - -/* selfsub */ -void c_qd_selfsub(const double *a, double *b) { - qd_real bb(b); - bb -= qd_real(a); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_selfsub_dd(const double *a, double *b) { - qd_real bb(b); - bb -= dd_real(a); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_selfsub_d(double a, double *b) { - qd_real bb(b); - bb -= a; - TO_DOUBLE_PTR(bb, b); -} - - - -/* selfmul */ -void c_qd_selfmul(const double *a, double *b) { - qd_real bb(b); - bb *= qd_real(a); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_selfmul_dd(const double *a, double *b) { - qd_real bb(b); - bb *= dd_real(a); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_selfmul_d(double a, double *b) { - qd_real bb(b); - bb *= a; - TO_DOUBLE_PTR(bb, b); -} - - - -/* selfdiv */ -void c_qd_selfdiv(const double *a, double *b) { - qd_real bb(b); - bb /= qd_real(a); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_selfdiv_dd(const double *a, double *b) { - qd_real bb(b); - bb /= dd_real(a); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_selfdiv_d(double a, double *b) { - qd_real bb(b); - bb /= a; - TO_DOUBLE_PTR(bb, b); -} - - - -/* copy */ -void c_qd_copy(const double *a, double *b) { - b[0] = a[0]; - b[1] = a[1]; - b[2] = a[2]; - b[3] = a[3]; -} -void c_qd_copy_dd(const double *a, double *b) { - b[0] = a[0]; - b[1] = a[1]; - b[2] = 0.0; - b[3] = 0.0; -} -void c_qd_copy_d(double a, double *b) { - b[0] = a; - b[1] = 0.0; - b[2] = 0.0; - b[3] = 0.0; -} - - -void c_qd_sqrt(const double *a, double *b) { - qd_real bb; - bb = sqrt(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_sqr(const double *a, double *b) { - qd_real bb; - bb = sqr(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_abs(const double *a, double *b) { - qd_real bb; - bb = abs(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_npwr(const double *a, int n, double *b) { - qd_real bb; - bb = npwr(qd_real(a), n); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_nroot(const double *a, int n, double *b) { - qd_real bb; - bb = nroot(qd_real(a), n); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_nint(const double *a, double *b) { - qd_real bb; - bb = nint(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_aint(const double *a, double *b) { - qd_real bb; - bb = aint(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_floor(const double *a, double *b) { - qd_real bb; - bb = floor(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_ceil(const double *a, double *b) { - qd_real bb; - bb = ceil(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_log(const double *a, double *b) { - qd_real bb; - bb = log(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_log10(const double *a, double *b) { - qd_real bb; - bb = log10(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_exp(const double *a, double *b) { - qd_real bb; - bb = exp(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_sin(const double *a, double *b) { - qd_real bb; - bb = sin(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_cos(const double *a, double *b) { - qd_real bb; - bb = cos(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_tan(const double *a, double *b) { - qd_real bb; - bb = tan(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_asin(const double *a, double *b) { - qd_real bb; - bb = asin(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_acos(const double *a, double *b) { - qd_real bb; - bb = acos(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_atan(const double *a, double *b) { - qd_real bb; - bb = atan(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_atan2(const double *a, const double *b, double *c) { - qd_real cc; - cc = atan2(qd_real(a), qd_real(b)); - TO_DOUBLE_PTR(cc, c); -} - -void c_qd_sinh(const double *a, double *b) { - qd_real bb; - bb = sinh(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_cosh(const double *a, double *b) { - qd_real bb; - bb = cosh(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_tanh(const double *a, double *b) { - qd_real bb; - bb = tanh(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_asinh(const double *a, double *b) { - qd_real bb; - bb = asinh(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_acosh(const double *a, double *b) { - qd_real bb; - bb = acosh(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} -void c_qd_atanh(const double *a, double *b) { - qd_real bb; - bb = atanh(qd_real(a)); - TO_DOUBLE_PTR(bb, b); -} - -void c_qd_sincos(const double *a, double *s, double *c) { - qd_real ss, cc; - sincos(qd_real(a), ss, cc); - TO_DOUBLE_PTR(cc, c); - TO_DOUBLE_PTR(ss, s); -} - -void c_qd_sincosh(const double *a, double *s, double *c) { - qd_real ss, cc; - sincosh(qd_real(a), ss, cc); - TO_DOUBLE_PTR(cc, c); - TO_DOUBLE_PTR(ss, s); -} - -void c_qd_read(const char *s, double *a) { - qd_real aa(s); - TO_DOUBLE_PTR(aa, a); -} - -void c_qd_swrite(const double *a, int precision, char *s, int len) { - qd_real(a).write(s, len, precision); -} - -void c_qd_write(const double *a) { - std::cout << qd_real(a).to_string(qd_real::_ndigits) << std::endl; -} - -void c_qd_neg(const double *a, double *b) { - b[0] = -a[0]; - b[1] = -a[1]; - b[2] = -a[2]; - b[3] = -a[3]; -} - -void c_qd_rand(double *a) { - qd_real aa; - aa = qdrand(); - TO_DOUBLE_PTR(aa, a); -} - -void c_qd_comp(const double *a, const double *b, int *result) { - qd_real aa(a), bb(b); - if (aa < bb) - *result = -1; - else if (aa > bb) - *result = 1; - else - *result = 0; -} - -void c_qd_comp_qd_d(const double *a, double b, int *result) { - qd_real aa(a); - if (aa < b) - *result = -1; - else if (aa > b) - *result = 1; - else - *result = 0; -} - -void c_qd_comp_d_qd(double a, const double *b, int *result) { - qd_real bb(b); - if (a < bb) - *result = -1; - else if (a > bb) - *result = 1; - else - *result = 0; -} - -void c_qd_pi(double *a) { - TO_DOUBLE_PTR(qd_real::_pi, a); -} - -} diff --git a/src/external/PackedCSparse/qd/c_qd.h b/src/external/PackedCSparse/qd/c_qd.h deleted file mode 100644 index d11a7ff1..00000000 --- a/src/external/PackedCSparse/qd/c_qd.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * include/c_qd.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Contains C wrapper function prototypes for quad-double precision - * arithmetic. This can also be used from fortran code. - */ -#ifndef _QD_C_QD_H -#define _QD_C_QD_H - -#include "c_dd.h" -#include "qd_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* add */ -void c_qd_add(const double *a, const double *b, double *c); -void c_qd_add_dd_qd(const double *a, const double *b, double *c); -void c_qd_add_qd_dd(const double *a, const double *b, double *c); -void c_qd_add_d_qd(double a, const double *b, double *c); -void c_qd_add_qd_d(const double *a, double b, double *c); -void c_qd_selfadd(const double *a, double *b); -void c_qd_selfadd_dd(const double *a, double *b); -void c_qd_selfadd_d(double a, double *b); - -/* sub */ -void c_qd_sub(const double *a, const double *b, double *c); -void c_qd_sub_dd_qd(const double *a, const double *b, double *c); -void c_qd_sub_qd_dd(const double *a, const double *b, double *c); -void c_qd_sub_d_qd(double a, const double *b, double *c); -void c_qd_sub_qd_d(const double *a, double b, double *c); -void c_qd_selfsub(const double *a, double *b); -void c_qd_selfsub_dd(const double *a, double *b); -void c_qd_selfsub_d(double a, double *b); - -/* mul */ -void c_qd_mul(const double *a, const double *b, double *c); -void c_qd_mul_dd_qd(const double *a, const double *b, double *c); -void c_qd_mul_qd_dd(const double *a, const double *b, double *c); -void c_qd_mul_d_qd(double a, const double *b, double *c); -void c_qd_mul_qd_d(const double *a, double b, double *c); -void c_qd_selfmul(const double *a, double *b); -void c_qd_selfmul_dd(const double *a, double *b); -void c_qd_selfmul_d(double a, double *b); - -/* div */ -void c_qd_div(const double *a, const double *b, double *c); -void c_qd_div_dd_qd(const double *a, const double *b, double *c); -void c_qd_div_qd_dd(const double *a, const double *b, double *c); -void c_qd_div_d_qd(double a, const double *b, double *c); -void c_qd_div_qd_d(const double *a, double b, double *c); -void c_qd_selfdiv(const double *a, double *b); -void c_qd_selfdiv_dd(const double *a, double *b); -void c_qd_selfdiv_d(double a, double *b); - -/* copy */ -void c_qd_copy(const double *a, double *b); -void c_qd_copy_dd(const double *a, double *b); -void c_qd_copy_d(double a, double *b); - -void c_qd_sqrt(const double *a, double *b); -void c_qd_sqr(const double *a, double *b); - -void c_qd_abs(const double *a, double *b); - -void c_qd_npwr(const double *a, int b, double *c); -void c_qd_nroot(const double *a, int b, double *c); - -void c_qd_nint(const double *a, double *b); -void c_qd_aint(const double *a, double *b); -void c_qd_floor(const double *a, double *b); -void c_qd_ceil(const double *a, double *b); - -void c_qd_exp(const double *a, double *b); -void c_qd_log(const double *a, double *b); -void c_qd_log10(const double *a, double *b); - -void c_qd_sin(const double *a, double *b); -void c_qd_cos(const double *a, double *b); -void c_qd_tan(const double *a, double *b); - -void c_qd_asin(const double *a, double *b); -void c_qd_acos(const double *a, double *b); -void c_qd_atan(const double *a, double *b); -void c_qd_atan2(const double *a, const double *b, double *c); - -void c_qd_sinh(const double *a, double *b); -void c_qd_cosh(const double *a, double *b); -void c_qd_tanh(const double *a, double *b); - -void c_qd_asinh(const double *a, double *b); -void c_qd_acosh(const double *a, double *b); -void c_qd_atanh(const double *a, double *b); - -void c_qd_sincos(const double *a, double *s, double *c); -void c_qd_sincosh(const double *a, double *s, double *c); - -void c_qd_read(const char *s, double *a); -void c_qd_swrite(const double *a, int precision, char *s, int len); -void c_qd_write(const double *a); -void c_qd_neg(const double *a, double *b); -void c_qd_rand(double *a); -void c_qd_comp(const double *a, const double *b, int *result); -void c_qd_comp_qd_d(const double *a, double b, int *result); -void c_qd_comp_d_qd(double a, const double *b, int *result); -void c_qd_pi(double *a); - -#ifdef __cplusplus -} -#endif - -#endif /* _QD_C_QD_H */ diff --git a/src/external/PackedCSparse/qd/dd_const.cc b/src/external/PackedCSparse/qd/dd_const.cc deleted file mode 100644 index 38b7b5ae..00000000 --- a/src/external/PackedCSparse/qd/dd_const.cc +++ /dev/null @@ -1,40 +0,0 @@ -/* - * src/dd_const.cc - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2007 - */ -#include "qd_config.h" -#include "dd_real.h" - -const dd_real dd_real::_2pi = dd_real(6.283185307179586232e+00, - 2.449293598294706414e-16); -const dd_real dd_real::_pi = dd_real(3.141592653589793116e+00, - 1.224646799147353207e-16); -const dd_real dd_real::_pi2 = dd_real(1.570796326794896558e+00, - 6.123233995736766036e-17); -const dd_real dd_real::_pi4 = dd_real(7.853981633974482790e-01, - 3.061616997868383018e-17); -const dd_real dd_real::_3pi4 = dd_real(2.356194490192344837e+00, - 9.1848509936051484375e-17); -const dd_real dd_real::_e = dd_real(2.718281828459045091e+00, - 1.445646891729250158e-16); -const dd_real dd_real::_log2 = dd_real(6.931471805599452862e-01, - 2.319046813846299558e-17); -const dd_real dd_real::_log10 = dd_real(2.302585092994045901e+00, - -2.170756223382249351e-16); -const dd_real dd_real::_nan = dd_real(qd::_d_nan, qd::_d_nan); -const dd_real dd_real::_inf = dd_real(qd::_d_inf, qd::_d_inf); - -const double dd_real::_eps = 4.93038065763132e-32; // 2^-104 -const double dd_real::_min_normalized = 2.0041683600089728e-292; // = 2^(-1022 + 53) -const dd_real dd_real::_max = - dd_real(1.79769313486231570815e+308, 9.97920154767359795037e+291); -const dd_real dd_real::_safe_max = - dd_real(1.7976931080746007281e+308, 9.97920154767359795037e+291); -const int dd_real::_ndigits = 31; - - diff --git a/src/external/PackedCSparse/qd/dd_inline.h b/src/external/PackedCSparse/qd/dd_inline.h deleted file mode 100644 index 89bc24f2..00000000 --- a/src/external/PackedCSparse/qd/dd_inline.h +++ /dev/null @@ -1,621 +0,0 @@ -/* - * include/dd_inline.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Contains small functions (suitable for inlining) in the double-double - * arithmetic package. - */ -#ifndef _QD_DD_INLINE_H -#define _QD_DD_INLINE_H - -#include -#include "inline.h" - -#ifndef QD_INLINE -#define inline -#endif - - -/*********** Additions ************/ -/* double-double = double + double */ -inline dd_real dd_real::add(double a, double b) { - double s, e; - s = qd::two_sum(a, b, e); - return dd_real(s, e); -} - -/* double-double + double */ -inline dd_real operator+(const dd_real &a, double b) { - double s1, s2; - s1 = qd::two_sum(a.x[0], b, s2); - s2 += a.x[1]; - s1 = qd::quick_two_sum(s1, s2, s2); - return dd_real(s1, s2); -} - -/* double-double + double-double */ -inline dd_real dd_real::ieee_add(const dd_real &a, const dd_real &b) { - /* This one satisfies IEEE style error bound, - due to K. Briggs and W. Kahan. */ - double s1, s2, t1, t2; - - s1 = qd::two_sum(a.x[0], b.x[0], s2); - t1 = qd::two_sum(a.x[1], b.x[1], t2); - s2 += t1; - s1 = qd::quick_two_sum(s1, s2, s2); - s2 += t2; - s1 = qd::quick_two_sum(s1, s2, s2); - return dd_real(s1, s2); -} - -inline dd_real dd_real::sloppy_add(const dd_real &a, const dd_real &b) { - /* This is the less accurate version ... obeys Cray-style - error bound. */ - double s, e; - - s = qd::two_sum(a.x[0], b.x[0], e); - e += (a.x[1] + b.x[1]); - s = qd::quick_two_sum(s, e, e); - return dd_real(s, e); -} - -inline dd_real operator+(const dd_real &a, const dd_real &b) { -#ifndef QD_IEEE_ADD - return dd_real::sloppy_add(a, b); -#else - return dd_real::ieee_add(a, b); -#endif -} - -/* double + double-double */ -inline dd_real operator+(double a, const dd_real &b) { - return (b + a); -} - - -/*********** Self-Additions ************/ -/* double-double += double */ -inline dd_real &dd_real::operator+=(double a) { - double s1, s2; - s1 = qd::two_sum(x[0], a, s2); - s2 += x[1]; - x[0] = qd::quick_two_sum(s1, s2, x[1]); - return *this; -} - -/* double-double += double-double */ -inline dd_real &dd_real::operator+=(const dd_real &a) { -#ifndef QD_IEEE_ADD - double s, e; - s = qd::two_sum(x[0], a.x[0], e); - e += x[1]; - e += a.x[1]; - x[0] = qd::quick_two_sum(s, e, x[1]); - return *this; -#else - double s1, s2, t1, t2; - s1 = qd::two_sum(x[0], a.x[0], s2); - t1 = qd::two_sum(x[1], a.x[1], t2); - s2 += t1; - s1 = qd::quick_two_sum(s1, s2, s2); - s2 += t2; - x[0] = qd::quick_two_sum(s1, s2, x[1]); - return *this; -#endif -} - -/*********** Subtractions ************/ -/* double-double = double - double */ -inline dd_real dd_real::sub(double a, double b) { - double s, e; - s = qd::two_diff(a, b, e); - return dd_real(s, e); -} - -/* double-double - double */ -inline dd_real operator-(const dd_real &a, double b) { - double s1, s2; - s1 = qd::two_diff(a.x[0], b, s2); - s2 += a.x[1]; - s1 = qd::quick_two_sum(s1, s2, s2); - return dd_real(s1, s2); -} - -/* double-double - double-double */ -inline dd_real operator-(const dd_real &a, const dd_real &b) { -#ifndef QD_IEEE_ADD - double s, e; - s = qd::two_diff(a.x[0], b.x[0], e); - e += a.x[1]; - e -= b.x[1]; - s = qd::quick_two_sum(s, e, e); - return dd_real(s, e); -#else - double s1, s2, t1, t2; - s1 = qd::two_diff(a.x[0], b.x[0], s2); - t1 = qd::two_diff(a.x[1], b.x[1], t2); - s2 += t1; - s1 = qd::quick_two_sum(s1, s2, s2); - s2 += t2; - s1 = qd::quick_two_sum(s1, s2, s2); - return dd_real(s1, s2); -#endif -} - -/* double - double-double */ -inline dd_real operator-(double a, const dd_real &b) { - double s1, s2; - s1 = qd::two_diff(a, b.x[0], s2); - s2 -= b.x[1]; - s1 = qd::quick_two_sum(s1, s2, s2); - return dd_real(s1, s2); -} - -/*********** Self-Subtractions ************/ -/* double-double -= double */ -inline dd_real &dd_real::operator-=(double a) { - double s1, s2; - s1 = qd::two_diff(x[0], a, s2); - s2 += x[1]; - x[0] = qd::quick_two_sum(s1, s2, x[1]); - return *this; -} - -/* double-double -= double-double */ -inline dd_real &dd_real::operator-=(const dd_real &a) { -#ifndef QD_IEEE_ADD - double s, e; - s = qd::two_diff(x[0], a.x[0], e); - e += x[1]; - e -= a.x[1]; - x[0] = qd::quick_two_sum(s, e, x[1]); - return *this; -#else - double s1, s2, t1, t2; - s1 = qd::two_diff(x[0], a.x[0], s2); - t1 = qd::two_diff(x[1], a.x[1], t2); - s2 += t1; - s1 = qd::quick_two_sum(s1, s2, s2); - s2 += t2; - x[0] = qd::quick_two_sum(s1, s2, x[1]); - return *this; -#endif -} - -/*********** Unary Minus ***********/ -inline dd_real dd_real::operator-() const { - return dd_real(-x[0], -x[1]); -} - -/*********** Multiplications ************/ -/* double-double = double * double */ -inline dd_real dd_real::mul(double a, double b) { - double p, e; - p = qd::two_prod(a, b, e); - return dd_real(p, e); -} - -/* double-double * (2.0 ^ exp) */ -inline dd_real ldexp(const dd_real &a, int exp) { - return dd_real(std::ldexp(a.x[0], exp), std::ldexp(a.x[1], exp)); -} - -/* double-double * double, where double is a power of 2. */ -inline dd_real mul_pwr2(const dd_real &a, double b) { - return dd_real(a.x[0] * b, a.x[1] * b); -} - -/* double-double * double */ -inline dd_real operator*(const dd_real &a, double b) { - double p1, p2; - - p1 = qd::two_prod(a.x[0], b, p2); - p2 += (a.x[1] * b); - p1 = qd::quick_two_sum(p1, p2, p2); - return dd_real(p1, p2); -} - -/* double-double * double-double */ -inline dd_real operator*(const dd_real &a, const dd_real &b) { - double p1, p2; - - p1 = qd::two_prod(a.x[0], b.x[0], p2); - p2 += (a.x[0] * b.x[1] + a.x[1] * b.x[0]); - p1 = qd::quick_two_sum(p1, p2, p2); - return dd_real(p1, p2); -} - -/* double * double-double */ -inline dd_real operator*(double a, const dd_real &b) { - return (b * a); -} - -/*********** Self-Multiplications ************/ -/* double-double *= double */ -inline dd_real &dd_real::operator*=(double a) { - double p1, p2; - p1 = qd::two_prod(x[0], a, p2); - p2 += x[1] * a; - x[0] = qd::quick_two_sum(p1, p2, x[1]); - return *this; -} - -/* double-double *= double-double */ -inline dd_real &dd_real::operator*=(const dd_real &a) { - double p1, p2; - p1 = qd::two_prod(x[0], a.x[0], p2); - p2 += a.x[1] * x[0]; - p2 += a.x[0] * x[1]; - x[0] = qd::quick_two_sum(p1, p2, x[1]); - return *this; -} - -/*********** Divisions ************/ -inline dd_real dd_real::div(double a, double b) { - double q1, q2; - double p1, p2; - double s, e; - - q1 = a / b; - - /* Compute a - q1 * b */ - p1 = qd::two_prod(q1, b, p2); - s = qd::two_diff(a, p1, e); - e -= p2; - - /* get next approximation */ - q2 = (s + e) / b; - - s = qd::quick_two_sum(q1, q2, e); - - return dd_real(s, e); -} - -/* double-double / double */ -inline dd_real operator/(const dd_real &a, double b) { - - double q1, q2; - double p1, p2; - double s, e; - dd_real r; - - q1 = a.x[0] / b; /* approximate quotient. */ - - /* Compute this - q1 * d */ - p1 = qd::two_prod(q1, b, p2); - s = qd::two_diff(a.x[0], p1, e); - e += a.x[1]; - e -= p2; - - /* get next approximation. */ - q2 = (s + e) / b; - - /* renormalize */ - r.x[0] = qd::quick_two_sum(q1, q2, r.x[1]); - - return r; -} - -inline dd_real dd_real::sloppy_div(const dd_real &a, const dd_real &b) { - double s1, s2; - double q1, q2; - dd_real r; - - q1 = a.x[0] / b.x[0]; /* approximate quotient */ - - /* compute this - q1 * dd */ - r = b * q1; - s1 = qd::two_diff(a.x[0], r.x[0], s2); - s2 -= r.x[1]; - s2 += a.x[1]; - - /* get next approximation */ - q2 = (s1 + s2) / b.x[0]; - - /* renormalize */ - r.x[0] = qd::quick_two_sum(q1, q2, r.x[1]); - return r; -} - -inline dd_real dd_real::accurate_div(const dd_real &a, const dd_real &b) { - double q1, q2, q3; - dd_real r; - - q1 = a.x[0] / b.x[0]; /* approximate quotient */ - - r = a - q1 * b; - - q2 = r.x[0] / b.x[0]; - r -= (q2 * b); - - q3 = r.x[0] / b.x[0]; - - q1 = qd::quick_two_sum(q1, q2, q2); - r = dd_real(q1, q2) + q3; - return r; -} - -/* double-double / double-double */ -inline dd_real operator/(const dd_real &a, const dd_real &b) { -#ifdef QD_SLOPPY_DIV - return dd_real::sloppy_div(a, b); -#else - return dd_real::accurate_div(a, b); -#endif -} - -/* double / double-double */ -inline dd_real operator/(double a, const dd_real &b) { - return dd_real(a) / b; -} - -inline dd_real inv(const dd_real &a) { - return 1.0 / a; -} - -/*********** Self-Divisions ************/ -/* double-double /= double */ -inline dd_real &dd_real::operator/=(double a) { - *this = *this / a; - return *this; -} - -/* double-double /= double-double */ -inline dd_real &dd_real::operator/=(const dd_real &a) { - *this = *this / a; - return *this; -} - -/********** Remainder **********/ -inline dd_real drem(const dd_real &a, const dd_real &b) { - dd_real n = nint(a / b); - return (a - n * b); -} - -inline dd_real divrem(const dd_real &a, const dd_real &b, dd_real &r) { - dd_real n = nint(a / b); - r = a - n * b; - return n; -} - -/*********** Squaring **********/ -inline dd_real sqr(const dd_real &a) { - double p1, p2; - double s1, s2; - p1 = qd::two_sqr(a.x[0], p2); - p2 += 2.0 * a.x[0] * a.x[1]; - p2 += a.x[1] * a.x[1]; - s1 = qd::quick_two_sum(p1, p2, s2); - return dd_real(s1, s2); -} - -inline dd_real dd_real::sqr(double a) { - double p1, p2; - p1 = qd::two_sqr(a, p2); - return dd_real(p1, p2); -} - - -/********** Exponentiation **********/ -inline dd_real dd_real::operator^(int n) { - return npwr(*this, n); -} - - -/*********** Assignments ************/ -/* double-double = double */ -inline dd_real &dd_real::operator=(double a) { - x[0] = a; - x[1] = 0.0; - return *this; -} - -/*********** Equality Comparisons ************/ -/* double-double == double */ -inline bool operator==(const dd_real &a, double b) { - return (a.x[0] == b && a.x[1] == 0.0); -} - -/* double-double == double-double */ -inline bool operator==(const dd_real &a, const dd_real &b) { - return (a.x[0] == b.x[0] && a.x[1] == b.x[1]); -} - -/* double == double-double */ -inline bool operator==(double a, const dd_real &b) { - return (a == b.x[0] && b.x[1] == 0.0); -} - -/*********** Greater-Than Comparisons ************/ -/* double-double > double */ -inline bool operator>(const dd_real &a, double b) { - return (a.x[0] > b || (a.x[0] == b && a.x[1] > 0.0)); -} - -/* double-double > double-double */ -inline bool operator>(const dd_real &a, const dd_real &b) { - return (a.x[0] > b.x[0] || (a.x[0] == b.x[0] && a.x[1] > b.x[1])); -} - -/* double > double-double */ -inline bool operator>(double a, const dd_real &b) { - return (a > b.x[0] || (a == b.x[0] && b.x[1] < 0.0)); -} - -/*********** Less-Than Comparisons ************/ -/* double-double < double */ -inline bool operator<(const dd_real &a, double b) { - return (a.x[0] < b || (a.x[0] == b && a.x[1] < 0.0)); -} - -/* double-double < double-double */ -inline bool operator<(const dd_real &a, const dd_real &b) { - return (a.x[0] < b.x[0] || (a.x[0] == b.x[0] && a.x[1] < b.x[1])); -} - -/* double < double-double */ -inline bool operator<(double a, const dd_real &b) { - return (a < b.x[0] || (a == b.x[0] && b.x[1] > 0.0)); -} - -/*********** Greater-Than-Or-Equal-To Comparisons ************/ -/* double-double >= double */ -inline bool operator>=(const dd_real &a, double b) { - return (a.x[0] > b || (a.x[0] == b && a.x[1] >= 0.0)); -} - -/* double-double >= double-double */ -inline bool operator>=(const dd_real &a, const dd_real &b) { - return (a.x[0] > b.x[0] || (a.x[0] == b.x[0] && a.x[1] >= b.x[1])); -} - -/* double >= double-double */ -inline bool operator>=(double a, const dd_real &b) { - return (b <= a); -} - -/*********** Less-Than-Or-Equal-To Comparisons ************/ -/* double-double <= double */ -inline bool operator<=(const dd_real &a, double b) { - return (a.x[0] < b || (a.x[0] == b && a.x[1] <= 0.0)); -} - -/* double-double <= double-double */ -inline bool operator<=(const dd_real &a, const dd_real &b) { - return (a.x[0] < b.x[0] || (a.x[0] == b.x[0] && a.x[1] <= b.x[1])); -} - -/* double <= double-double */ -inline bool operator<=(double a, const dd_real &b) { - return (b >= a); -} - -/*********** Not-Equal-To Comparisons ************/ -/* double-double != double */ -inline bool operator!=(const dd_real &a, double b) { - return (a.x[0] != b || a.x[1] != 0.0); -} - -/* double-double != double-double */ -inline bool operator!=(const dd_real &a, const dd_real &b) { - return (a.x[0] != b.x[0] || a.x[1] != b.x[1]); -} - -/* double != double-double */ -inline bool operator!=(double a, const dd_real &b) { - return (a != b.x[0] || b.x[1] != 0.0); -} - -/*********** Micellaneous ************/ -/* this == 0 */ -inline bool dd_real::is_zero() const { - return (x[0] == 0.0); -} - -/* this == 1 */ -inline bool dd_real::is_one() const { - return (x[0] == 1.0 && x[1] == 0.0); -} - -/* this > 0 */ -inline bool dd_real::is_positive() const { - return (x[0] > 0.0); -} - -/* this < 0 */ -inline bool dd_real::is_negative() const { - return (x[0] < 0.0); -} - -inline dd_real::operator bool() const { - return (x[0] != 0.0); -} - -inline dd_real::operator double() const { - return to_double(*this); -} - -/* Absolute value */ -inline dd_real abs(const dd_real &a) { - return (a.x[0] < 0.0) ? -a : a; -} - -inline dd_real fabs(const dd_real &a) { - return abs(a); -} - -/* Round to Nearest integer */ -inline dd_real nint(const dd_real &a) { - double hi = qd::nint(a.x[0]); - double lo; - - if (hi == a.x[0]) { - /* High word is an integer already. Round the low word.*/ - lo = qd::nint(a.x[1]); - - /* Renormalize. This is needed if x[0] = some integer, x[1] = 1/2.*/ - hi = qd::quick_two_sum(hi, lo, lo); - } else { - /* High word is not an integer. */ - lo = 0.0; - if (std::abs(hi-a.x[0]) == 0.5 && a.x[1] < 0.0) { - /* There is a tie in the high word, consult the low word - to break the tie. */ - hi -= 1.0; /* NOTE: This does not cause INEXACT. */ - } - } - - return dd_real(hi, lo); -} - -inline dd_real floor(const dd_real &a) { - double hi = std::floor(a.x[0]); - double lo = 0.0; - - if (hi == a.x[0]) { - /* High word is integer already. Round the low word. */ - lo = std::floor(a.x[1]); - hi = qd::quick_two_sum(hi, lo, lo); - } - - return dd_real(hi, lo); -} - -inline dd_real ceil(const dd_real &a) { - double hi = std::ceil(a.x[0]); - double lo = 0.0; - - if (hi == a.x[0]) { - /* High word is integer already. Round the low word. */ - lo = std::ceil(a.x[1]); - hi = qd::quick_two_sum(hi, lo, lo); - } - - return dd_real(hi, lo); -} - -inline dd_real aint(const dd_real &a) { - return (a.x[0] >= 0.0) ? floor(a) : ceil(a); -} - -/* Cast to double. */ -inline double to_double(const dd_real &a) { - return a.x[0]; -} - -/* Cast to int. */ -inline int to_int(const dd_real &a) { - return static_cast(a.x[0]); -} - -/* Random number generator */ -inline dd_real dd_real::rand() { - return ddrand(); -} - -#endif /* _QD_DD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/dd_real.cc b/src/external/PackedCSparse/qd/dd_real.cc deleted file mode 100644 index ff4d5223..00000000 --- a/src/external/PackedCSparse/qd/dd_real.cc +++ /dev/null @@ -1,1303 +0,0 @@ -/* - * src/dd_real.cc - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2007 - * - * Contains implementation of non-inlined functions of double-double - * package. Inlined functions are found in dd_inline.h (in include directory). - */ -#include -#include -#include -#include -#include -#include -#include - -#include "qd_config.h" -#include "dd_real.h" -#include "util.h" - -#include "bits.h" - -#ifndef QD_INLINE -#include "dd_inline.h" -#endif - -using std::cout; -using std::cerr; -using std::endl; -using std::ostream; -using std::istream; -using std::ios_base; -using std::string; -using std::setw; - -/* This routine is called whenever a fatal error occurs. */ -void dd_real::error(const char *msg) { - //if (msg) { cerr << "ERROR " << msg << endl; } -} - -/* Computes the square root of the double-double number dd. - NOTE: dd must be a non-negative number. */ -QD_API dd_real sqrt(const dd_real &a) { - /* Strategy: Use Karp's trick: if x is an approximation - to sqrt(a), then - - sqrt(a) = a*x + [a - (a*x)^2] * x / 2 (approx) - - The approximation is accurate to twice the accuracy of x. - Also, the multiplication (a*x) and [-]*x can be done with - only half the precision. - */ - - if (a.is_zero()) - return 0.0; - - if (a.is_negative()) { - dd_real::error("(dd_real::sqrt): Negative argument."); - return dd_real::_nan; - } - - double x = 1.0 / std::sqrt(a.x[0]); - double ax = a.x[0] * x; - return dd_real::add(ax, (a - dd_real::sqr(ax)).x[0] * (x * 0.5)); -} - -/* Computes the square root of a double in double-double precision. - NOTE: d must not be negative. */ -dd_real dd_real::sqrt(double d) { - return ::sqrt(dd_real(d)); -} - -/* Computes the n-th root of the double-double number a. - NOTE: n must be a positive integer. - NOTE: If n is even, then a must not be negative. */ -dd_real nroot(const dd_real &a, int n) { - /* Strategy: Use Newton iteration for the function - - f(x) = x^(-n) - a - - to find its root a^{-1/n}. The iteration is thus - - x' = x + x * (1 - a * x^n) / n - - which converges quadratically. We can then find - a^{1/n} by taking the reciprocal. - */ - - if (n <= 0) { - dd_real::error("(dd_real::nroot): N must be positive."); - return dd_real::_nan; - } - - if (n%2 == 0 && a.is_negative()) { - dd_real::error("(dd_real::nroot): Negative argument."); - return dd_real::_nan; - } - - if (n == 1) { - return a; - } - if (n == 2) { - return sqrt(a); - } - - if (a.is_zero()) - return 0.0; - - /* Note a^{-1/n} = exp(-log(a)/n) */ - dd_real r = abs(a); - dd_real x = std::exp(-std::log(r.x[0]) / n); - - /* Perform Newton's iteration. */ - x += x * (1.0 - r * npwr(x, n)) / static_cast(n); - if (a.x[0] < 0.0) - x = -x; - return 1.0/x; -} - -/* Computes the n-th power of a double-double number. - NOTE: 0^0 causes an error. */ -dd_real npwr(const dd_real &a, int n) { - - if (n == 0) { - if (a.is_zero()) { - dd_real::error("(dd_real::npwr): Invalid argument."); - return dd_real::_nan; - } - return 1.0; - } - - dd_real r = a; - dd_real s = 1.0; - int N = std::abs(n); - - if (N > 1) { - /* Use binary exponentiation */ - while (N > 0) { - if (N % 2 == 1) { - s *= r; - } - N /= 2; - if (N > 0) - r = sqr(r); - } - } else { - s = r; - } - - /* Compute the reciprocal if n is negative. */ - if (n < 0) - return (1.0 / s); - - return s; -} - -dd_real pow(const dd_real &a, int n) { - return npwr(a, n); -} - -dd_real pow(const dd_real &a, const dd_real &b) { - return exp(b * log(a)); -} - -static const int n_inv_fact = 15; -static const double inv_fact[n_inv_fact][2] = { - { 1.66666666666666657e-01, 9.25185853854297066e-18}, - { 4.16666666666666644e-02, 2.31296463463574266e-18}, - { 8.33333333333333322e-03, 1.15648231731787138e-19}, - { 1.38888888888888894e-03, -5.30054395437357706e-20}, - { 1.98412698412698413e-04, 1.72095582934207053e-22}, - { 2.48015873015873016e-05, 2.15119478667758816e-23}, - { 2.75573192239858925e-06, -1.85839327404647208e-22}, - { 2.75573192239858883e-07, 2.37677146222502973e-23}, - { 2.50521083854417202e-08, -1.44881407093591197e-24}, - { 2.08767569878681002e-09, -1.20734505911325997e-25}, - { 1.60590438368216133e-10, 1.25852945887520981e-26}, - { 1.14707455977297245e-11, 2.06555127528307454e-28}, - { 7.64716373181981641e-13, 7.03872877733453001e-30}, - { 4.77947733238738525e-14, 4.39920548583408126e-31}, - { 2.81145725434552060e-15, 1.65088427308614326e-31} -}; - -/* Exponential. Computes exp(x) in double-double precision. */ -dd_real exp(const dd_real &a) { - /* Strategy: We first reduce the size of x by noting that - - exp(kr + m * log(2)) = 2^m * exp(r)^k - - where m and k are integers. By choosing m appropriately - we can make |kr| <= log(2) / 2 = 0.347. Then exp(r) is - evaluated using the familiar Taylor series. Reducing the - argument substantially speeds up the convergence. */ - - const double k = 512.0; - const double inv_k = 1.0 / k; - - if (a.x[0] <= -709.0) - return 0.0; - - if (a.x[0] >= 709.0) - return dd_real::_inf; - - if (a.is_zero()) - return 1.0; - - if (a.is_one()) - return dd_real::_e; - - double m = std::floor(a.x[0] / dd_real::_log2.x[0] + 0.5); - dd_real r = mul_pwr2(a - dd_real::_log2 * m, inv_k); - dd_real s, t, p; - - p = sqr(r); - s = r + mul_pwr2(p, 0.5); - p *= r; - t = p * dd_real(inv_fact[0][0], inv_fact[0][1]); - int i = 0; - do { - s += t; - p *= r; - ++i; - t = p * dd_real(inv_fact[i][0], inv_fact[i][1]); - } while (std::abs(to_double(t)) > inv_k * dd_real::_eps && i < 5); - - s += t; - - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s += 1.0; - - return ldexp(s, static_cast(m)); -} - -/* Logarithm. Computes log(x) in double-double precision. - This is a natural logarithm (i.e., base e). */ -dd_real log(const dd_real &a) { - /* Strategy. The Taylor series for log converges much more - slowly than that of exp, due to the lack of the factorial - term in the denominator. Hence this routine instead tries - to determine the root of the function - - f(x) = exp(x) - a - - using Newton iteration. The iteration is given by - - x' = x - f(x)/f'(x) - = x - (1 - a * exp(-x)) - = x + a * exp(-x) - 1. - - Only one iteration is needed, since Newton's iteration - approximately doubles the number of digits per iteration. */ - - if (a.is_one()) { - return 0.0; - } - - if (a.x[0] <= 0.0) { - dd_real::error("(dd_real::log): Non-positive argument."); - return dd_real::_nan; - } - - dd_real x = std::log(a.x[0]); /* Initial approximation */ - - x = x + a * exp(-x) - 1.0; - return x; -} - -dd_real log10(const dd_real &a) { - return log(a) / dd_real::_log10; -} - -static const dd_real _pi16 = dd_real(1.963495408493620697e-01, - 7.654042494670957545e-18); - -/* Table of sin(k * pi/16) and cos(k * pi/16). */ -static const double sin_table [4][2] = { - {1.950903220161282758e-01, -7.991079068461731263e-18}, - {3.826834323650897818e-01, -1.005077269646158761e-17}, - {5.555702330196021776e-01, 4.709410940561676821e-17}, - {7.071067811865475727e-01, -4.833646656726456726e-17} -}; - -static const double cos_table [4][2] = { - {9.807852804032304306e-01, 1.854693999782500573e-17}, - {9.238795325112867385e-01, 1.764504708433667706e-17}, - {8.314696123025452357e-01, 1.407385698472802389e-18}, - {7.071067811865475727e-01, -4.833646656726456726e-17} -}; - -/* Computes sin(a) using Taylor series. - Assumes |a| <= pi/32. */ -static dd_real sin_taylor(const dd_real &a) { - const double thresh = 0.5 * std::abs(to_double(a)) * dd_real::_eps; - dd_real r, s, t, x; - - if (a.is_zero()) { - return 0.0; - } - - int i = 0; - x = -sqr(a); - s = a; - r = a; - do { - r *= x; - t = r * dd_real(inv_fact[i][0], inv_fact[i][1]); - s += t; - i += 2; - } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); - - return s; -} - -static dd_real cos_taylor(const dd_real &a) { - const double thresh = 0.5 * dd_real::_eps; - dd_real r, s, t, x; - - if (a.is_zero()) { - return 1.0; - } - - x = -sqr(a); - r = x; - s = 1.0 + mul_pwr2(r, 0.5); - int i = 1; - do { - r *= x; - t = r * dd_real(inv_fact[i][0], inv_fact[i][1]); - s += t; - i += 2; - } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); - - return s; -} - -static void sincos_taylor(const dd_real &a, - dd_real &sin_a, dd_real &cos_a) { - if (a.is_zero()) { - sin_a = 0.0; - cos_a = 1.0; - return; - } - - sin_a = sin_taylor(a); - cos_a = sqrt(1.0 - sqr(sin_a)); -} - - -dd_real sin(const dd_real &a) { - - /* Strategy. To compute sin(x), we choose integers a, b so that - - x = s + a * (pi/2) + b * (pi/16) - - and |s| <= pi/32. Using the fact that - - sin(pi/16) = 0.5 * sqrt(2 - sqrt(2 + sqrt(2))) - - we can compute sin(x) from sin(s), cos(s). This greatly - increases the convergence of the sine Taylor series. */ - - if (a.is_zero()) { - return 0.0; - } - - // approximately reduce modulo 2*pi - dd_real z = nint(a / dd_real::_2pi); - dd_real r = a - dd_real::_2pi * z; - - // approximately reduce modulo pi/2 and then modulo pi/16. - dd_real t; - double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); - t = r - dd_real::_pi2 * q; - int j = static_cast(q); - q = std::floor(t.x[0] / _pi16.x[0] + 0.5); - t -= _pi16 * q; - int k = static_cast(q); - int abs_k = std::abs(k); - - if (j < -2 || j > 2) { - dd_real::error("(dd_real::sin): Cannot reduce modulo pi/2."); - return dd_real::_nan; - } - - if (abs_k > 4) { - dd_real::error("(dd_real::sin): Cannot reduce modulo pi/16."); - return dd_real::_nan; - } - - if (k == 0) { - switch (j) { - case 0: - return sin_taylor(t); - case 1: - return cos_taylor(t); - case -1: - return -cos_taylor(t); - default: - return -sin_taylor(t); - } - } - - dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); - dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); - dd_real sin_t, cos_t; - sincos_taylor(t, sin_t, cos_t); - if (j == 0) { - if (k > 0) { - r = u * sin_t + v * cos_t; - } else { - r = u * sin_t - v * cos_t; - } - } else if (j == 1) { - if (k > 0) { - r = u * cos_t - v * sin_t; - } else { - r = u * cos_t + v * sin_t; - } - } else if (j == -1) { - if (k > 0) { - r = v * sin_t - u * cos_t; - } else if (k < 0) { - r = -u * cos_t - v * sin_t; - } - } else { - if (k > 0) { - r = -u * sin_t - v * cos_t; - } else { - r = v * cos_t - u * sin_t; - } - } - - return r; -} - -dd_real cos(const dd_real &a) { - - if (a.is_zero()) { - return 1.0; - } - - // approximately reduce modulo 2*pi - dd_real z = nint(a / dd_real::_2pi); - dd_real r = a - z * dd_real::_2pi; - - // approximately reduce modulo pi/2 and then modulo pi/16 - dd_real t; - double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); - t = r - dd_real::_pi2 * q; - int j = static_cast(q); - q = std::floor(t.x[0] / _pi16.x[0] + 0.5); - t -= _pi16 * q; - int k = static_cast(q); - int abs_k = std::abs(k); - - if (j < -2 || j > 2) { - dd_real::error("(dd_real::cos): Cannot reduce modulo pi/2."); - return dd_real::_nan; - } - - if (abs_k > 4) { - dd_real::error("(dd_real::cos): Cannot reduce modulo pi/16."); - return dd_real::_nan; - } - - if (k == 0) { - switch (j) { - case 0: - return cos_taylor(t); - case 1: - return -sin_taylor(t); - case -1: - return sin_taylor(t); - default: - return -cos_taylor(t); - } - } - - dd_real sin_t, cos_t; - sincos_taylor(t, sin_t, cos_t); - dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); - dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); - - if (j == 0) { - if (k > 0) { - r = u * cos_t - v * sin_t; - } else { - r = u * cos_t + v * sin_t; - } - } else if (j == 1) { - if (k > 0) { - r = - u * sin_t - v * cos_t; - } else { - r = v * cos_t - u * sin_t; - } - } else if (j == -1) { - if (k > 0) { - r = u * sin_t + v * cos_t; - } else { - r = u * sin_t - v * cos_t; - } - } else { - if (k > 0) { - r = v * sin_t - u * cos_t; - } else { - r = - u * cos_t - v * sin_t; - } - } - - return r; -} - -void sincos(const dd_real &a, dd_real &sin_a, dd_real &cos_a) { - - if (a.is_zero()) { - sin_a = 0.0; - cos_a = 1.0; - return; - } - - // approximately reduce modulo 2*pi - dd_real z = nint(a / dd_real::_2pi); - dd_real r = a - dd_real::_2pi * z; - - // approximately reduce module pi/2 and pi/16 - dd_real t; - double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); - t = r - dd_real::_pi2 * q; - int j = static_cast(q); - int abs_j = std::abs(j); - q = std::floor(t.x[0] / _pi16.x[0] + 0.5); - t -= _pi16 * q; - int k = static_cast(q); - int abs_k = std::abs(k); - - if (abs_j > 2) { - dd_real::error("(dd_real::sincos): Cannot reduce modulo pi/2."); - cos_a = sin_a = dd_real::_nan; - return; - } - - if (abs_k > 4) { - dd_real::error("(dd_real::sincos): Cannot reduce modulo pi/16."); - cos_a = sin_a = dd_real::_nan; - return; - } - - dd_real sin_t, cos_t; - dd_real s, c; - - sincos_taylor(t, sin_t, cos_t); - - if (abs_k == 0) { - s = sin_t; - c = cos_t; - } else { - dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); - dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); - - if (k > 0) { - s = u * sin_t + v * cos_t; - c = u * cos_t - v * sin_t; - } else { - s = u * sin_t - v * cos_t; - c = u * cos_t + v * sin_t; - } - } - - if (abs_j == 0) { - sin_a = s; - cos_a = c; - } else if (j == 1) { - sin_a = c; - cos_a = -s; - } else if (j == -1) { - sin_a = -c; - cos_a = s; - } else { - sin_a = -s; - cos_a = -c; - } - -} - -dd_real atan(const dd_real &a) { - return atan2(a, dd_real(1.0)); -} - -dd_real atan2(const dd_real &y, const dd_real &x) { - /* Strategy: Instead of using Taylor series to compute - arctan, we instead use Newton's iteration to solve - the equation - - sin(z) = y/r or cos(z) = x/r - - where r = sqrt(x^2 + y^2). - The iteration is given by - - z' = z + (y - sin(z)) / cos(z) (for equation 1) - z' = z - (x - cos(z)) / sin(z) (for equation 2) - - Here, x and y are normalized so that x^2 + y^2 = 1. - If |x| > |y|, then first iteration is used since the - denominator is larger. Otherwise, the second is used. - */ - - if (x.is_zero()) { - - if (y.is_zero()) { - /* Both x and y is zero. */ - dd_real::error("(dd_real::atan2): Both arguments zero."); - return dd_real::_nan; - } - - return (y.is_positive()) ? dd_real::_pi2 : -dd_real::_pi2; - } else if (y.is_zero()) { - return (x.is_positive()) ? dd_real(0.0) : dd_real::_pi; - } - - if (x == y) { - return (y.is_positive()) ? dd_real::_pi4 : -dd_real::_3pi4; - } - - if (x == -y) { - return (y.is_positive()) ? dd_real::_3pi4 : -dd_real::_pi4; - } - - dd_real r = sqrt(sqr(x) + sqr(y)); - dd_real xx = x / r; - dd_real yy = y / r; - - /* Compute double precision approximation to atan. */ - dd_real z = std::atan2(to_double(y), to_double(x)); - dd_real sin_z, cos_z; - - if (std::abs(xx.x[0]) > std::abs(yy.x[0])) { - /* Use Newton iteration 1. z' = z + (y - sin(z)) / cos(z) */ - sincos(z, sin_z, cos_z); - z += (yy - sin_z) / cos_z; - } else { - /* Use Newton iteration 2. z' = z - (x - cos(z)) / sin(z) */ - sincos(z, sin_z, cos_z); - z -= (xx - cos_z) / sin_z; - } - - return z; -} - -dd_real tan(const dd_real &a) { - dd_real s, c; - sincos(a, s, c); - return s/c; -} - -dd_real asin(const dd_real &a) { - dd_real abs_a = abs(a); - - if (abs_a > 1.0) { - dd_real::error("(dd_real::asin): Argument out of domain."); - return dd_real::_nan; - } - - if (abs_a.is_one()) { - return (a.is_positive()) ? dd_real::_pi2 : -dd_real::_pi2; - } - - return atan2(a, sqrt(1.0 - sqr(a))); -} - -dd_real acos(const dd_real &a) { - dd_real abs_a = abs(a); - - if (abs_a > 1.0) { - dd_real::error("(dd_real::acos): Argument out of domain."); - return dd_real::_nan; - } - - if (abs_a.is_one()) { - return (a.is_positive()) ? dd_real(0.0) : dd_real::_pi; - } - - return atan2(sqrt(1.0 - sqr(a)), a); -} - -dd_real sinh(const dd_real &a) { - if (a.is_zero()) { - return 0.0; - } - - if (abs(a) > 0.05) { - dd_real ea = exp(a); - return mul_pwr2(ea - inv(ea), 0.5); - } - - /* since a is small, using the above formula gives - a lot of cancellation. So use Taylor series. */ - dd_real s = a; - dd_real t = a; - dd_real r = sqr(t); - double m = 1.0; - double thresh = std::abs((to_double(a)) * dd_real::_eps); - - do { - m += 2.0; - t *= r; - t /= (m-1) * m; - - s += t; - } while (abs(t) > thresh); - - return s; - -} - -dd_real cosh(const dd_real &a) { - if (a.is_zero()) { - return 1.0; - } - - dd_real ea = exp(a); - return mul_pwr2(ea + inv(ea), 0.5); -} - -dd_real tanh(const dd_real &a) { - if (a.is_zero()) { - return 0.0; - } - - if (std::abs(to_double(a)) > 0.05) { - dd_real ea = exp(a); - dd_real inv_ea = inv(ea); - return (ea - inv_ea) / (ea + inv_ea); - } else { - dd_real s, c; - s = sinh(a); - c = sqrt(1.0 + sqr(s)); - return s / c; - } -} - -void sincosh(const dd_real &a, dd_real &s, dd_real &c) { - if (std::abs(to_double(a)) <= 0.05) { - s = sinh(a); - c = sqrt(1.0 + sqr(s)); - } else { - dd_real ea = exp(a); - dd_real inv_ea = inv(ea); - s = mul_pwr2(ea - inv_ea, 0.5); - c = mul_pwr2(ea + inv_ea, 0.5); - } -} - -dd_real asinh(const dd_real &a) { - return log(a + sqrt(sqr(a) + 1.0)); -} - -dd_real acosh(const dd_real &a) { - if (a < 1.0) { - dd_real::error("(dd_real::acosh): Argument out of domain."); - return dd_real::_nan; - } - - return log(a + sqrt(sqr(a) - 1.0)); -} - -dd_real atanh(const dd_real &a) { - if (abs(a) >= 1.0) { - dd_real::error("(dd_real::atanh): Argument out of domain."); - return dd_real::_nan; - } - - return mul_pwr2(log((1.0 + a) / (1.0 - a)), 0.5); -} - -QD_API dd_real fmod(const dd_real &a, const dd_real &b) { - dd_real n = aint(a / b); - return (a - b * n); -} - -QD_API dd_real ddrand() { - static const double m_const = 4.6566128730773926e-10; /* = 2^{-31} */ - double m = m_const; - dd_real r = 0.0; - double d; - - /* Strategy: Generate 31 bits at a time, using lrand48 - random number generator. Shift the bits, and reapeat - 4 times. */ - - for (int i = 0; i < 4; i++, m *= m_const) { -// d = lrand48() * m; - d = std::rand() * m; - r += d; - } - - return r; -} - -/* polyeval(c, n, x) - Evaluates the given n-th degree polynomial at x. - The polynomial is given by the array of (n+1) coefficients. */ -dd_real polyeval(const dd_real *c, int n, const dd_real &x) { - /* Just use Horner's method of polynomial evaluation. */ - dd_real r = c[n]; - - for (int i = n-1; i >= 0; i--) { - r *= x; - r += c[i]; - } - - return r; -} - -/* polyroot(c, n, x0) - Given an n-th degree polynomial, finds a root close to - the given guess x0. Note that this uses simple Newton - iteration scheme, and does not work for multiple roots. */ -QD_API dd_real polyroot(const dd_real *c, int n, - const dd_real &x0, int max_iter, double thresh) { - dd_real x = x0; - dd_real f; - dd_real *d = new dd_real[n]; - bool conv = false; - int i; - double max_c = std::abs(to_double(c[0])); - double v; - - if (thresh == 0.0) thresh = dd_real::_eps; - - /* Compute the coefficients of the derivatives. */ - for (i = 1; i <= n; i++) { - v = std::abs(to_double(c[i])); - if (v > max_c) max_c = v; - d[i-1] = c[i] * static_cast(i); - } - thresh *= max_c; - - /* Newton iteration. */ - for (i = 0; i < max_iter; i++) { - f = polyeval(c, n, x); - - if (abs(f) < thresh) { - conv = true; - break; - } - x -= (f / polyeval(d, n-1, x)); - } - delete [] d; - - if (!conv) { - dd_real::error("(dd_real::polyroot): Failed to converge."); - return dd_real::_nan; - } - - return x; -} - - -/* Constructor. Reads a double-double number from the string s - and constructs a double-double number. */ -dd_real::dd_real(const char *s) { - if (dd_real::read(s, *this)) { - dd_real::error("(dd_real::dd_real): INPUT ERROR."); - *this = dd_real::_nan; - } -} - -dd_real &dd_real::operator=(const char *s) { - if (dd_real::read(s, *this)) { - dd_real::error("(dd_real::operator=): INPUT ERROR."); - *this = dd_real::_nan; - } - return *this; -} - -/* Outputs the double-double number dd. */ -ostream &operator<<(ostream &os, const dd_real &dd) { - bool showpos = (os.flags() & ios_base::showpos) != 0; - bool uppercase = (os.flags() & ios_base::uppercase) != 0; - return os << dd.to_string((int)os.precision(), (int)os.width(), os.flags(), - showpos, uppercase, os.fill()); -} - -/* Reads in the double-double number a. */ -istream &operator>>(istream &s, dd_real &a) { - char str[255]; - s >> str; - a = dd_real(str); - return s; -} - -void dd_real::to_digits(char *s, int &expn, int precision) const { - int D = precision + 1; /* number of digits to compute */ - - dd_real r = abs(*this); - int e; /* exponent */ - int i, d; - - if (x[0] == 0.0) { - /* this == 0.0 */ - expn = 0; - for (i = 0; i < precision; i++) s[i] = '0'; - return; - } - - /* First determine the (approximate) exponent. */ - e = to_int(std::floor(std::log10(std::abs(x[0])))); - - if (e < -300) { - r *= dd_real(10.0) ^ 300; - r /= dd_real(10.0) ^ (e + 300); - } else if (e > 300) { - r = ldexp(r, -53); - r /= dd_real(10.0) ^ e; - r = ldexp(r, 53); - } else { - r /= dd_real(10.0) ^ e; - } - - /* Fix exponent if we are off by one */ - if (r >= 10.0) { - r /= 10.0; - e++; - } else if (r < 1.0) { - r *= 10.0; - e--; - } - - if (r >= 10.0 || r < 1.0) { - dd_real::error("(dd_real::to_digits): can't compute exponent."); - return; - } - - /* Extract the digits */ - for (i = 0; i < D; i++) { - d = static_cast(r.x[0]); - r -= d; - r *= 10.0; - - s[i] = static_cast(d + '0'); - } - - /* Fix out of range digits. */ - for (i = D-1; i > 0; i--) { - if (s[i] < '0') { - s[i-1]--; - s[i] += 10; - } else if (s[i] > '9') { - s[i-1]++; - s[i] -= 10; - } - } - - if (s[0] <= '0') { - dd_real::error("(dd_real::to_digits): non-positive leading digit."); - return; - } - - /* Round, handle carry */ - if (s[D-1] >= '5') { - s[D-2]++; - - i = D-2; - while (i > 0 && s[i] > '9') { - s[i] -= 10; - s[--i]++; - } - } - - /* If first digit is 10, shift everything. */ - if (s[0] > '9') { - e++; - for (i = precision; i >= 2; i--) s[i] = s[i-1]; - s[0] = '1'; - s[1] = '0'; - } - - s[precision] = 0; - expn = e; -} - -/* Writes the double-double number into the character array s of length len. - The integer d specifies how many significant digits to write. - The string s must be able to hold at least (d+8) characters. - showpos indicates whether to use the + sign, and uppercase indicates - whether the E or e is to be used for the exponent. */ -void dd_real::write(char *s, int len, int precision, - bool showpos, bool uppercase) const { - string str = to_string(precision, 0, ios_base::scientific, showpos, uppercase); - std::strncpy(s, str.c_str(), len-1); - s[len-1] = 0; -} - - -void round_string(char *s, int precision, int *offset){ - /* - Input string must be all digits or errors will occur. - */ - - int i; - int D = precision ; - - /* Round, handle carry */ - if (D>0 && s[D] >= '5') { - s[D-1]++; - - i = D-1; - while (i > 0 && s[i] > '9') { - s[i] -= 10; - s[--i]++; - } - } - - /* If first digit is 10, shift everything. */ - if (s[0] > '9') { - // e++; // don't modify exponent here - for (i = precision; i >= 1; i--) s[i+1] = s[i]; - s[0] = '1'; - s[1] = '0'; - - (*offset)++ ; // now offset needs to be increased by one - precision++ ; - } - - s[precision] = 0; // add terminator for array -} - -string dd_real::to_string(int precision, int width, ios_base::fmtflags fmt, - bool showpos, bool uppercase, char fill) const { - string s; - bool fixed = (fmt & ios_base::fixed) != 0; - bool sgn = true; - int i, e = 0; - - if (isnan()) { - s = uppercase ? "NAN" : "nan"; - sgn = false; - } else { - if (*this < 0.0) - s += '-'; - else if (showpos) - s += '+'; - else - sgn = false; - - if (isinf()) { - s += uppercase ? "INF" : "inf"; - } else if (*this == 0.0) { - /* Zero case */ - s += '0'; - if (precision > 0) { - s += '.'; - s.append(precision, '0'); - } - } else { - /* Non-zero case */ - int off = (fixed ? (1 + to_int(floor(log10(abs(*this))))) : 1); - int d = precision + off; - - int d_with_extra = d; - if(fixed) - d_with_extra = std::max(60, d); // longer than the max accuracy for DD - - // highly special case - fixed mode, precision is zero, abs(*this) < 1.0 - // without this trap a number like 0.9 printed fixed with 0 precision prints as 0 - // should be rounded to 1. - if(fixed && (precision == 0) && (abs(*this) < 1.0)){ - if(abs(*this) >= 0.5) - s += '1'; - else - s += '0'; - - return s; - } - - // handle near zero to working precision (but not exactly zero) - if (fixed && d <= 0) { - s += '0'; - if (precision > 0) { - s += '.'; - s.append(precision, '0'); - } - } else { // default - - char *t; // = new char[d+1]; - int j; - - if(fixed){ - t = new char[d_with_extra+1]; - to_digits(t, e, d_with_extra); - } - else{ - t = new char[d+1]; - to_digits(t, e, d); - } - - off = e + 1; - - if (fixed) { - // fix the string if it's been computed incorrectly - // round here in the decimal string if required - round_string(t, d, &off); - - if (off > 0) { - for (i = 0; i < off; i++) s += t[i]; - if (precision > 0) { - s += '.'; - for (j = 0; j < precision; j++, i++) s += t[i]; - } - } else { - s += "0."; - if (off < 0) s.append(-off, '0'); - for (i = 0; i < d; i++) s += t[i]; - } - } else { - s += t[0]; - if (precision > 0) s += '.'; - - for (i = 1; i <= precision; i++) - s += t[i]; - - } - delete [] t; - } - } - - // trap for improper offset with large values - // without this trap, output of values of the for 10^j - 1 fail for j > 28 - // and are output with the point in the wrong place, leading to a dramatically off value - if(fixed && (precision > 0)){ - // make sure that the value isn't dramatically larger - double from_string = atof(s.c_str()); - - // if this ratio is large, then we've got problems - if( fabs( from_string / this->x[0] ) > 3.0 ){ - - // loop on the string, find the point, move it up one - // don't act on the first character - for(i=1; i < (int)s.length(); i++){ - if(s[i] == '.'){ - s[i] = s[i-1] ; - s[i-1] = '.' ; - break; - } - } - - from_string = atof(s.c_str()); - // if this ratio is large, then the string has not been fixed - if( fabs( from_string / this->x[0] ) > 3.0 ){ - dd_real::error("Re-rounding unsuccessful in large number fixed point trap.") ; - } - } - } - - - if (!fixed && !isinf()) { - /* Fill in exponent part */ - s += uppercase ? 'E' : 'e'; - append_expn(s, e); - } - } - - /* Fill in the blanks */ - int len = s.length(); - if (len < width) { - int delta = width - len; - if (fmt & ios_base::internal) { - if (sgn) - s.insert(static_cast(1), delta, fill); - else - s.insert(static_cast(0), delta, fill); - } else if (fmt & ios_base::left) { - s.append(delta, fill); - } else { - s.insert(static_cast(0), delta, fill); - } - } - - return s; -} - -/* Reads in a double-double number from the string s. */ -int dd_real::read(const char *s, dd_real &a) { - const char *p = s; - char ch; - int sign = 0; - int point = -1; - int nd = 0; - int e = 0; - bool done = false; - dd_real r = 0.0; - int nread; - - /* Skip any leading spaces */ - while (*p == ' ') - p++; - - while (!done && (ch = *p) != '\0') { - if (ch >= '0' && ch <= '9') { - int d = ch - '0'; - r *= 10.0; - r += static_cast(d); - nd++; - } else { - - switch (ch) { - - case '.': - if (point >= 0) - return -1; - point = nd; - break; - - case '-': - case '+': - if (sign != 0 || nd > 0) - return -1; - sign = (ch == '-') ? -1 : 1; - break; - - case 'E': - case 'e': - nread = std::sscanf(p+1, "%d", &e); - done = true; - if (nread != 1) - return -1; - break; - - default: - return -1; - } - } - - p++; - } - - if (point >= 0) { - e -= (nd - point); - } - - if (e != 0) { - r *= (dd_real(10.0) ^ e); - } - - a = (sign == -1) ? -r : r; - return 0; -} - -/* Debugging routines */ -void dd_real::dump(const string &name, std::ostream &os) const { - std::ios_base::fmtflags old_flags = os.flags(); - std::streamsize old_prec = os.precision(19); - os << std::scientific; - - if (name.length() > 0) os << name << " = "; - os << "[ " << setw(27) << x[0] << ", " << setw(27) << x[1] << " ]" << endl; - - os.precision(old_prec); - os.flags(old_flags); -} - -void dd_real::dump_bits(const string &name, std::ostream &os) const { - string::size_type len = name.length(); - if (len > 0) { - os << name << " = "; - len +=3; - } - os << "[ "; - len += 2; - print_double_info(os, x[0]); - os << endl; - for (string::size_type i = 0; i < len; i++) os << ' '; - print_double_info(os, x[1]); - os << " ]" << endl; -} - -dd_real dd_real::debug_rand() { - - if (std::rand() % 2 == 0) - return ddrand(); - - int expn = 0; - dd_real a = 0.0; - double d; - for (int i = 0; i < 2; i++) { - d = std::ldexp(static_cast(std::rand()) / RAND_MAX, -expn); - a += d; - expn = expn + 54 + std::rand() % 200; - } - return a; -} diff --git a/src/external/PackedCSparse/qd/dd_real.h b/src/external/PackedCSparse/qd/dd_real.h deleted file mode 100644 index e16438aa..00000000 --- a/src/external/PackedCSparse/qd/dd_real.h +++ /dev/null @@ -1,293 +0,0 @@ -/* - * include/dd_real.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2007 - * - * Double-double precision (>= 106-bit significand) floating point - * arithmetic package based on David Bailey's Fortran-90 double-double - * package, with some changes. See - * - * http://www.nersc.gov/~dhbailey/mpdist/mpdist.html - * - * for the original Fortran-90 version. - * - * Overall structure is similar to that of Keith Brigg's C++ double-double - * package. See - * - * http://www-epidem.plansci.cam.ac.uk/~kbriggs/doubledouble.html - * - * for more details. In particular, the fix for x86 computers is borrowed - * from his code. - * - * Yozo Hida - */ - -#ifndef _QD_DD_REAL_H -#define _QD_DD_REAL_H - -#include -#include -#include -#include -#include "qd_config.h" -#include "fpu.h" - -// Some compilers define isnan, isfinite, and isinf as macros, even for -// C++ codes, which cause havoc when overloading these functions. We undef -// them here. -#ifdef isnan -#undef isnan -#endif - -#ifdef isfinite -#undef isfinite -#endif - -#ifdef isinf -#undef isinf -#endif - -#ifdef max -#undef max -#endif - -#ifdef min -#undef min -#endif - -struct QD_API dd_real { - double x[2]; - - dd_real(double hi, double lo) { x[0] = hi; x[1] = lo; } - dd_real() {x[0] = 0.0; x[1] = 0.0; } - dd_real(double h) { x[0] = h; x[1] = 0.0; } - dd_real(int h) { - x[0] = (static_cast(h)); - x[1] = 0.0; - } - - dd_real (const char *s); - explicit dd_real (const double *d) { - x[0] = d[0]; x[1] = d[1]; - } - - static void error(const char *msg); - - double _hi() const { return x[0]; } - double _lo() const { return x[1]; } - - static const dd_real _2pi; - static const dd_real _pi; - static const dd_real _3pi4; - static const dd_real _pi2; - static const dd_real _pi4; - static const dd_real _e; - static const dd_real _log2; - static const dd_real _log10; - static const dd_real _nan; - static const dd_real _inf; - - static const double _eps; - static const double _min_normalized; - static const dd_real _max; - static const dd_real _safe_max; - static const int _ndigits; - - bool isnan() const { return QD_ISNAN(x[0]) || QD_ISNAN(x[1]); } - bool isfinite() const { return QD_ISFINITE(x[0]); } - bool isinf() const { return QD_ISINF(x[0]); } - - static dd_real add(double a, double b); - static dd_real ieee_add(const dd_real &a, const dd_real &b); - static dd_real sloppy_add(const dd_real &a, const dd_real &b); - - dd_real &operator+=(double a); - dd_real &operator+=(const dd_real &a); - - static dd_real sub(double a, double b); - - dd_real &operator-=(double a); - dd_real &operator-=(const dd_real &a); - - dd_real operator-() const; - - static dd_real mul(double a, double b); - - dd_real &operator*=(double a); - dd_real &operator*=(const dd_real &a); - - static dd_real div(double a, double b); - static dd_real sloppy_div(const dd_real &a, const dd_real &b); - static dd_real accurate_div(const dd_real &a, const dd_real &b); - - dd_real &operator/=(double a); - dd_real &operator/=(const dd_real &a); - - dd_real &operator=(double a); - dd_real &operator=(const char *s); - - dd_real operator^(int n); - static dd_real sqr(double d); - - static dd_real sqrt(double a); - - bool is_zero() const; - bool is_one() const; - bool is_positive() const; - bool is_negative() const; - - explicit operator bool() const; // new - explicit operator double() const; // new - - static dd_real rand(void); - - void to_digits(char *s, int &expn, int precision = _ndigits) const; - void write(char *s, int len, int precision = _ndigits, - bool showpos = false, bool uppercase = false) const; - std::string to_string(int precision = _ndigits, int width = 0, - std::ios_base::fmtflags fmt = static_cast(0), - bool showpos = false, bool uppercase = false, char fill = ' ') const; - int read(const char *s, dd_real &a); - - /* Debugging Methods */ - void dump(const std::string &name, std::ostream &os = std::cerr) const; - void dump_bits(const std::string &name, - std::ostream &os = std::cerr) const; - - static dd_real debug_rand(); -}; - - -namespace std { - template <> - class numeric_limits : public numeric_limits { - public: - inline static double epsilon() { return dd_real::_eps; } - inline static dd_real max() { return dd_real::_max; } - inline static dd_real safe_max() { return dd_real::_safe_max; } - inline static double min() { return dd_real::_min_normalized; } - static const int digits = 104; - static const int digits10 = 31; - }; -} - -QD_API dd_real ddrand(void); -QD_API dd_real sqrt(const dd_real &a); - -QD_API dd_real polyeval(const dd_real *c, int n, const dd_real &x); -QD_API dd_real polyroot(const dd_real *c, int n, - const dd_real &x0, int max_iter = 32, double thresh = 0.0); - -QD_API inline bool isnan(const dd_real &a) { return a.isnan(); } -QD_API inline bool isfinite(const dd_real &a) { return a.isfinite(); } -QD_API inline bool isinf(const dd_real &a) { return a.isinf(); } - -/* Computes dd * d where d is known to be a power of 2. */ -QD_API dd_real mul_pwr2(const dd_real &dd, double d); - -QD_API dd_real operator+(const dd_real &a, double b); -QD_API dd_real operator+(double a, const dd_real &b); -QD_API dd_real operator+(const dd_real &a, const dd_real &b); - -QD_API dd_real operator-(const dd_real &a, double b); -QD_API dd_real operator-(double a, const dd_real &b); -QD_API dd_real operator-(const dd_real &a, const dd_real &b); - -QD_API dd_real operator*(const dd_real &a, double b); -QD_API dd_real operator*(double a, const dd_real &b); -QD_API dd_real operator*(const dd_real &a, const dd_real &b); - -QD_API dd_real operator/(const dd_real &a, double b); -QD_API dd_real operator/(double a, const dd_real &b); -QD_API dd_real operator/(const dd_real &a, const dd_real &b); - -QD_API dd_real inv(const dd_real &a); - -QD_API dd_real rem(const dd_real &a, const dd_real &b); -QD_API dd_real drem(const dd_real &a, const dd_real &b); -QD_API dd_real divrem(const dd_real &a, const dd_real &b, dd_real &r); - -QD_API dd_real pow(const dd_real &a, int n); -QD_API dd_real pow(const dd_real &a, const dd_real &b); -QD_API dd_real npwr(const dd_real &a, int n); -QD_API dd_real sqr(const dd_real &a); - -QD_API dd_real sqrt(const dd_real &a); -QD_API dd_real nroot(const dd_real &a, int n); - -QD_API bool operator==(const dd_real &a, double b); -QD_API bool operator==(double a, const dd_real &b); -QD_API bool operator==(const dd_real &a, const dd_real &b); - -QD_API bool operator<=(const dd_real &a, double b); -QD_API bool operator<=(double a, const dd_real &b); -QD_API bool operator<=(const dd_real &a, const dd_real &b); - -QD_API bool operator>=(const dd_real &a, double b); -QD_API bool operator>=(double a, const dd_real &b); -QD_API bool operator>=(const dd_real &a, const dd_real &b); - -QD_API bool operator<(const dd_real &a, double b); -QD_API bool operator<(double a, const dd_real &b); -QD_API bool operator<(const dd_real &a, const dd_real &b); - -QD_API bool operator>(const dd_real &a, double b); -QD_API bool operator>(double a, const dd_real &b); -QD_API bool operator>(const dd_real &a, const dd_real &b); - -QD_API bool operator!=(const dd_real &a, double b); -QD_API bool operator!=(double a, const dd_real &b); -QD_API bool operator!=(const dd_real &a, const dd_real &b); - -QD_API dd_real nint(const dd_real &a); -QD_API dd_real floor(const dd_real &a); -QD_API dd_real ceil(const dd_real &a); -QD_API dd_real aint(const dd_real &a); - -QD_API dd_real ddrand(void); - -double to_double(const dd_real &a); -int to_int(const dd_real &a); - -QD_API dd_real exp(const dd_real &a); -QD_API dd_real ldexp(const dd_real &a, int exp); -QD_API dd_real log(const dd_real &a); -QD_API dd_real log10(const dd_real &a); - -QD_API dd_real sin(const dd_real &a); -QD_API dd_real cos(const dd_real &a); -QD_API dd_real tan(const dd_real &a); -QD_API void sincos(const dd_real &a, dd_real &sin_a, dd_real &cos_a); - -QD_API dd_real asin(const dd_real &a); -QD_API dd_real acos(const dd_real &a); -QD_API dd_real atan(const dd_real &a); -QD_API dd_real atan2(const dd_real &y, const dd_real &x); - -QD_API dd_real sinh(const dd_real &a); -QD_API dd_real cosh(const dd_real &a); -QD_API dd_real tanh(const dd_real &a); -QD_API void sincosh(const dd_real &a, - dd_real &sinh_a, dd_real &cosh_a); - -QD_API dd_real asinh(const dd_real &a); -QD_API dd_real acosh(const dd_real &a); -QD_API dd_real atanh(const dd_real &a); - -QD_API dd_real fabs(const dd_real &a); -QD_API dd_real abs(const dd_real &a); /* same as fabs */ - -QD_API dd_real fmod(const dd_real &a, const dd_real &b); - -QD_API std::ostream& operator<<(std::ostream &s, const dd_real &a); -QD_API std::istream& operator>>(std::istream &s, dd_real &a); -#ifdef QD_INLINE -#include "dd_inline.h" -#endif - -#endif /* _QD_DD_REAL_H */ - diff --git a/src/external/PackedCSparse/qd/fpu.cc b/src/external/PackedCSparse/qd/fpu.cc deleted file mode 100644 index 96ddc488..00000000 --- a/src/external/PackedCSparse/qd/fpu.cc +++ /dev/null @@ -1,124 +0,0 @@ -/* - * src/fpu.cc - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Contains functions to set and restore the round-to-double flag in the - * control word of a x86 FPU. - */ - -#include "qd_config.h" -#include "fpu.h" - -#ifdef X86 -#ifdef _WIN32 -#include -#else - -#ifdef HAVE_FPU_CONTROL_H -#include -#endif - -#ifndef _FPU_GETCW -#define _FPU_GETCW(x) asm volatile ("fnstcw %0":"=m" (x)); -#endif - -#ifndef _FPU_SETCW -#define _FPU_SETCW(x) asm volatile ("fldcw %0": :"m" (x)); -#endif - -#ifndef _FPU_EXTENDED -#define _FPU_EXTENDED 0x0300 -#endif - -#ifndef _FPU_DOUBLE -#define _FPU_DOUBLE 0x0200 -#endif - -#endif -#endif /* X86 */ - -extern "C" { - -void fpu_fix_start(unsigned int *old_cw) { -#ifdef X86 -#ifdef _WIN32 -#ifdef __BORLANDC__ - /* Win 32 Borland C */ - unsigned short cw = _control87(0, 0); - _control87(0x0200, 0x0300); - if (old_cw) { - *old_cw = cw; - } -#else - /* Win 32 MSVC */ - unsigned int cw = _control87(0, 0); - _control87(0x00010000, 0x00030000); - if (old_cw) { - *old_cw = cw; - } -#endif -#else - /* Linux */ - volatile unsigned short cw, new_cw; - _FPU_GETCW(cw); - - new_cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE; - _FPU_SETCW(new_cw); - - if (old_cw) { - *old_cw = cw; - } -#endif -#endif -} - -void fpu_fix_end(unsigned int *old_cw) { -#ifdef X86 -#ifdef _WIN32 - -#ifdef __BORLANDC__ - /* Win 32 Borland C */ - if (old_cw) { - unsigned short cw = (unsigned short) *old_cw; - _control87(cw, 0xFFFF); - } -#else - /* Win 32 MSVC */ - if (old_cw) { - _control87(*old_cw, 0xFFFFFFFF); - } -#endif - -#else - /* Linux */ - if (old_cw) { - int cw; - cw = *old_cw; - _FPU_SETCW(cw); - } -#endif -#endif -} - -#ifdef HAVE_FORTRAN - -#define f_fpu_fix_start FC_FUNC_(f_fpu_fix_start, F_FPU_FIX_START) -#define f_fpu_fix_end FC_FUNC_(f_fpu_fix_end, F_FPU_FIX_END) - -void f_fpu_fix_start(unsigned int *old_cw) { - fpu_fix_start(old_cw); -} - -void f_fpu_fix_end(unsigned int *old_cw) { - fpu_fix_end(old_cw); -} - -#endif - -} - diff --git a/src/external/PackedCSparse/qd/fpu.h b/src/external/PackedCSparse/qd/fpu.h deleted file mode 100644 index 35eab18c..00000000 --- a/src/external/PackedCSparse/qd/fpu.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * include/fpu.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2001 - * - * Contains functions to set and restore the round-to-double flag in the - * control word of a x86 FPU. The algorithms in the double-double and - * quad-double package does not function with the extended mode found in - * these FPU. - */ -#ifndef _QD_FPU_H -#define _QD_FPU_H - -#include "qd_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Set the round-to-double flag, and save the old control word in old_cw. - * If old_cw is NULL, the old control word is not saved. - */ -QD_API void fpu_fix_start(unsigned int *old_cw); - -/* - * Restore the control word. - */ -QD_API void fpu_fix_end(unsigned int *old_cw); - -#ifdef __cplusplus -} -#endif - -#endif /* _QD_FPU_H */ diff --git a/src/external/PackedCSparse/qd/inline.h b/src/external/PackedCSparse/qd/inline.h deleted file mode 100644 index 52425545..00000000 --- a/src/external/PackedCSparse/qd/inline.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * include/inline.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * This file contains the basic functions used both by double-double - * and quad-double package. These are declared as inline functions as - * they are the smallest building blocks of the double-double and - * quad-double arithmetic. - */ -#ifndef _QD_INLINE_H -#define _QD_INLINE_H - -#define _QD_SPLITTER 134217729.0 // = 2^27 + 1 -#define _QD_SPLIT_THRESH 6.69692879491417e+299 // = 2^996 - -#ifdef QD_VACPP_BUILTINS_H -/* For VisualAge C++ __fmadd */ -#include -#endif - -#include -#include - -namespace qd { - -static const double _d_nan = std::numeric_limits::quiet_NaN(); -static const double _d_inf = std::numeric_limits::infinity(); - -/*********** Basic Functions ************/ -/* Computes fl(a+b) and err(a+b). Assumes |a| >= |b|. */ -inline double quick_two_sum(double a, double b, double &err) { - double s = a + b; - err = b - (s - a); - return s; -} - -/* Computes fl(a-b) and err(a-b). Assumes |a| >= |b| */ -inline double quick_two_diff(double a, double b, double &err) { - double s = a - b; - err = (a - s) - b; - return s; -} - -/* Computes fl(a+b) and err(a+b). */ -inline double two_sum(double a, double b, double &err) { - double s = a + b; - double bb = s - a; - err = (a - (s - bb)) + (b - bb); - return s; -} - -/* Computes fl(a-b) and err(a-b). */ -inline double two_diff(double a, double b, double &err) { - double s = a - b; - double bb = s - a; - err = (a - (s - bb)) - (b + bb); - return s; -} - -#ifndef QD_FMS -/* Computes high word and lo word of a */ -inline void split(double a, double &hi, double &lo) { - double temp; - if (a > _QD_SPLIT_THRESH || a < -_QD_SPLIT_THRESH) { - a *= 3.7252902984619140625e-09; // 2^-28 - temp = _QD_SPLITTER * a; - hi = temp - (temp - a); - lo = a - hi; - hi *= 268435456.0; // 2^28 - lo *= 268435456.0; // 2^28 - } else { - temp = _QD_SPLITTER * a; - hi = temp - (temp - a); - lo = a - hi; - } -} -#endif - -/* Computes fl(a*b) and err(a*b). */ -inline double two_prod(double a, double b, double &err) { -#ifdef QD_FMS - double p = a * b; - err = QD_FMS(a, b, p); - return p; -#else - double a_hi, a_lo, b_hi, b_lo; - double p = a * b; - split(a, a_hi, a_lo); - split(b, b_hi, b_lo); - err = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo; - return p; -#endif -} - -/* Computes fl(a*a) and err(a*a). Faster than the above method. */ -inline double two_sqr(double a, double &err) { -#ifdef QD_FMS - double p = a * a; - err = QD_FMS(a, a, p); - return p; -#else - double hi, lo; - double q = a * a; - split(a, hi, lo); - err = ((hi * hi - q) + 2.0 * hi * lo) + lo * lo; - return q; -#endif -} - -/* Computes the nearest integer to d. */ -inline double nint(double d) { - if (d == std::floor(d)) - return d; - return std::floor(d + 0.5); -} - -/* Computes the truncated integer. */ -inline double aint(double d) { - return (d >= 0.0) ? std::floor(d) : std::ceil(d); -} - -/* These are provided to give consistent - interface for double with double-double and quad-double. */ -inline void sincosh(double t, double &sinh_t, double &cosh_t) { - sinh_t = std::sinh(t); - cosh_t = std::cosh(t); -} - -inline double sqr(double t) { - return t * t; -} - -inline double to_double(double a) { return a; } -inline int to_int(double a) { return static_cast(a); } - -} - -#endif /* _QD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/qd.pdf b/src/external/PackedCSparse/qd/qd.pdf deleted file mode 100644 index 9525b8cac490f4c3229bc7074e37075cebe7eae0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 194502 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 SmE72tYykD$gkxvSUpTr@w^W=dn^OhAde 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 -#include - -/* include/qd/qd_config.h. Generated from qd_config.h.in by configure. */ -#ifndef _QD_QD_CONFIG_H -#define _QD_QD_CONFIG_H 1 - -#ifndef QD_API -#define QD_API /**/ -#endif - -/* Set to 1 if using VisualAge C++ compiler for __fmadd builtin. */ -#ifndef QD_VACPP_BUILTINS_H -/* #undef QD_VACPP_BUILTINS_H */ -#endif - -/* If fused multiply-add is available, define to correct macro for - using it. It is invoked as QD_FMA(a, b, c) to compute fl(a * b + c). - If correctly rounded multiply-add is not available (or if unsure), - keep it undefined.*/ -#ifndef QD_FMA -/* #undef QD_FMA */ -#endif - -/* If fused multiply-subtract is available, define to correct macro for - using it. It is invoked as QD_FMS(a, b, c) to compute fl(a * b - c). - If correctly rounded multiply-add is not available (or if unsure), - keep it undefined.*/ -#ifndef QD_FMS -#define QD_FMS(a, b, c) std::fma(a,b,-c) -/* #undef QD_FMS */ -#endif - -/* Set the following to 1 to define commonly used function - to be inlined. This should be set to 1 unless the compiler - does not support the "inline" keyword, or if building for - debugging purposes. */ -#ifndef QD_INLINE -#define QD_INLINE 1 -#endif - -/* Set the following to 1 to use ANSI C++ standard header files - such as cmath, iostream, etc. If set to zero, it will try to - include math.h, iostream.h, etc, instead. */ -#ifndef QD_HAVE_STD -#define QD_HAVE_STD 1 -#endif - -/* Set the following to 1 to make the addition and subtraction - to satisfy the IEEE-style error bound - - fl(a + b) = (1 + d) * (a + b) - - where |d| <= eps. If set to 0, the addition and subtraction - will satisfy the weaker Cray-style error bound - - fl(a + b) = (1 + d1) * a + (1 + d2) * b - - where |d1| <= eps and |d2| eps. */ -#ifndef QD_IEEE_ADD -/* #undef QD_IEEE_ADD */ -#endif - -/* Set the following to 1 to use slightly inaccurate but faster - version of multiplication. */ -#ifndef QD_SLOPPY_MUL -#define QD_SLOPPY_MUL 1 -#endif - -/* Set the following to 1 to use slightly inaccurate but faster - version of division. */ -#ifndef QD_SLOPPY_DIV -#define QD_SLOPPY_DIV 1 -#endif - -/* Define this macro to be the isfinite(x) function. */ -#ifndef QD_ISFINITE -#define QD_ISFINITE(x) std::isfinite(x) -#endif - -/* Define this macro to be the isinf(x) function. */ -#ifndef QD_ISINF -#define QD_ISINF(x) std::isinf(x) -#endif - -/* Define this macro to be the isnan(x) function. */ -#ifndef QD_ISNAN -#define QD_ISNAN(x) std::isnan(x) -#endif - - -#endif /* _QD_QD_CONFIG_H */ diff --git a/src/external/PackedCSparse/qd/qd_const.cc b/src/external/PackedCSparse/qd/qd_const.cc deleted file mode 100644 index 6f4e01d2..00000000 --- a/src/external/PackedCSparse/qd/qd_const.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * src/qd_const.cc - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Defines constants used in quad-double package. - */ -#include "qd_config.h" -#include "qd_real.h" - -/* Some useful constants. */ -const qd_real qd_real::_2pi = qd_real(6.283185307179586232e+00, - 2.449293598294706414e-16, - -5.989539619436679332e-33, - 2.224908441726730563e-49); -const qd_real qd_real::_pi = qd_real(3.141592653589793116e+00, - 1.224646799147353207e-16, - -2.994769809718339666e-33, - 1.112454220863365282e-49); -const qd_real qd_real::_pi2 = qd_real(1.570796326794896558e+00, - 6.123233995736766036e-17, - -1.497384904859169833e-33, - 5.562271104316826408e-50); -const qd_real qd_real::_pi4 = qd_real(7.853981633974482790e-01, - 3.061616997868383018e-17, - -7.486924524295849165e-34, - 2.781135552158413204e-50); -const qd_real qd_real::_3pi4 = qd_real(2.356194490192344837e+00, - 9.1848509936051484375e-17, - 3.9168984647504003225e-33, - -2.5867981632704860386e-49); -const qd_real qd_real::_e = qd_real(2.718281828459045091e+00, - 1.445646891729250158e-16, - -2.127717108038176765e-33, - 1.515630159841218954e-49); -const qd_real qd_real::_log2 = qd_real(6.931471805599452862e-01, - 2.319046813846299558e-17, - 5.707708438416212066e-34, - -3.582432210601811423e-50); -const qd_real qd_real::_log10 = qd_real(2.302585092994045901e+00, - -2.170756223382249351e-16, - -9.984262454465776570e-33, - -4.023357454450206379e-49); -const qd_real qd_real::_nan = qd_real(qd::_d_nan, qd::_d_nan, - qd::_d_nan, qd::_d_nan); -const qd_real qd_real::_inf = qd_real(qd::_d_inf, qd::_d_inf, - qd::_d_inf, qd::_d_inf); - -const double qd_real::_eps = 1.21543267145725e-63; // = 2^-209 -const double qd_real::_min_normalized = 1.6259745436952323e-260; // = 2^(-1022 + 3*53) -const qd_real qd_real::_max = qd_real( - 1.79769313486231570815e+308, 9.97920154767359795037e+291, - 5.53956966280111259858e+275, 3.07507889307840487279e+259); -const qd_real qd_real::_safe_max = qd_real( - 1.7976931080746007281e+308, 9.97920154767359795037e+291, - 5.53956966280111259858e+275, 3.07507889307840487279e+259); -const int qd_real::_ndigits = 62; - diff --git a/src/external/PackedCSparse/qd/qd_inline.h b/src/external/PackedCSparse/qd/qd_inline.h deleted file mode 100644 index 89ba275b..00000000 --- a/src/external/PackedCSparse/qd/qd_inline.h +++ /dev/null @@ -1,1047 +0,0 @@ -/* - * include/qd_inline.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2001 - * - * Contains small functions (suitable for inlining) in the quad-double - * arithmetic package. - */ -#ifndef _QD_QD_INLINE_H -#define _QD_QD_INLINE_H - -#include -#include "inline.h" - -#ifndef QD_INLINE -#define inline -#endif - -/********** Constructors **********/ -inline qd_real::qd_real(double x0, double x1, double x2, double x3) { - x[0] = x0; - x[1] = x1; - x[2] = x2; - x[3] = x3; -} - -inline qd_real::qd_real(const double *xx) { - x[0] = xx[0]; - x[1] = xx[1]; - x[2] = xx[2]; - x[3] = xx[3]; -} - -inline qd_real::qd_real(double x0) { - x[0] = x0; - x[1] = x[2] = x[3] = 0.0; -} - -inline qd_real::qd_real() { - x[0] = 0.0; - x[1] = 0.0; - x[2] = 0.0; - x[3] = 0.0; -} - -inline qd_real::qd_real(const dd_real &a) { - x[0] = a._hi(); - x[1] = a._lo(); - x[2] = x[3] = 0.0; -} - -inline qd_real::qd_real(int i) { - x[0] = static_cast(i); - x[1] = x[2] = x[3] = 0.0; -} - -/********** Accessors **********/ -inline double qd_real::operator[](int i) const { - return x[i]; -} - -inline double &qd_real::operator[](int i) { - return x[i]; -} - -inline bool qd_real::isnan() const { - return QD_ISNAN(x[0]) || QD_ISNAN(x[1]) || QD_ISNAN(x[2]) || QD_ISNAN(x[3]); -} - -/********** Renormalization **********/ -namespace qd { -inline void quick_renorm(double &c0, double &c1, - double &c2, double &c3, double &c4) { - double t0, t1, t2, t3; - double s; - s = qd::quick_two_sum(c3, c4, t3); - s = qd::quick_two_sum(c2, s , t2); - s = qd::quick_two_sum(c1, s , t1); - c0 = qd::quick_two_sum(c0, s , t0); - - s = qd::quick_two_sum(t2, t3, t2); - s = qd::quick_two_sum(t1, s , t1); - c1 = qd::quick_two_sum(t0, s , t0); - - s = qd::quick_two_sum(t1, t2, t1); - c2 = qd::quick_two_sum(t0, s , t0); - - c3 = t0 + t1; -} - -inline void renorm(double &c0, double &c1, - double &c2, double &c3) { - double s0, s1, s2 = 0.0, s3 = 0.0; - - if (QD_ISINF(c0)) return; - - s0 = qd::quick_two_sum(c2, c3, c3); - s0 = qd::quick_two_sum(c1, s0, c2); - c0 = qd::quick_two_sum(c0, s0, c1); - - s0 = c0; - s1 = c1; - if (s1 != 0.0) { - s1 = qd::quick_two_sum(s1, c2, s2); - if (s2 != 0.0) - s2 = qd::quick_two_sum(s2, c3, s3); - else - s1 = qd::quick_two_sum(s1, c3, s2); - } else { - s0 = qd::quick_two_sum(s0, c2, s1); - if (s1 != 0.0) - s1 = qd::quick_two_sum(s1, c3, s2); - else - s0 = qd::quick_two_sum(s0, c3, s1); - } - - c0 = s0; - c1 = s1; - c2 = s2; - c3 = s3; -} - -inline void renorm(double &c0, double &c1, - double &c2, double &c3, double &c4) { - double s0, s1, s2 = 0.0, s3 = 0.0; - - if (QD_ISINF(c0)) return; - - s0 = qd::quick_two_sum(c3, c4, c4); - s0 = qd::quick_two_sum(c2, s0, c3); - s0 = qd::quick_two_sum(c1, s0, c2); - c0 = qd::quick_two_sum(c0, s0, c1); - - s0 = c0; - s1 = c1; - - if (s1 != 0.0) { - s1 = qd::quick_two_sum(s1, c2, s2); - if (s2 != 0.0) { - s2 = qd::quick_two_sum(s2, c3, s3); - if (s3 != 0.0) - s3 += c4; - else - s2 = qd::quick_two_sum(s2, c4, s3); - } else { - s1 = qd::quick_two_sum(s1, c3, s2); - if (s2 != 0.0) - s2 = qd::quick_two_sum(s2, c4, s3); - else - s1 = qd::quick_two_sum(s1, c4, s2); - } - } else { - s0 = qd::quick_two_sum(s0, c2, s1); - if (s1 != 0.0) { - s1 = qd::quick_two_sum(s1, c3, s2); - if (s2 != 0.0) - s2 = qd::quick_two_sum(s2, c4, s3); - else - s1 = qd::quick_two_sum(s1, c4, s2); - } else { - s0 = qd::quick_two_sum(s0, c3, s1); - if (s1 != 0.0) - s1 = qd::quick_two_sum(s1, c4, s2); - else - s0 = qd::quick_two_sum(s0, c4, s1); - } - } - - c0 = s0; - c1 = s1; - c2 = s2; - c3 = s3; -} -} - -inline void qd_real::renorm() { - qd::renorm(x[0], x[1], x[2], x[3]); -} - -inline void qd_real::renorm(double &e) { - qd::renorm(x[0], x[1], x[2], x[3], e); -} - - -/********** Additions ************/ -namespace qd { - -inline void three_sum(double &a, double &b, double &c) { - double t1, t2, t3; - t1 = qd::two_sum(a, b, t2); - a = qd::two_sum(c, t1, t3); - b = qd::two_sum(t2, t3, c); -} - -inline void three_sum2(double &a, double &b, double &c) { - double t1, t2, t3; - t1 = qd::two_sum(a, b, t2); - a = qd::two_sum(c, t1, t3); - b = t2 + t3; -} - -} - -/* quad-double + double */ -inline qd_real operator+(const qd_real &a, double b) { - double c0, c1, c2, c3; - double e; - - c0 = qd::two_sum(a[0], b, e); - c1 = qd::two_sum(a[1], e, e); - c2 = qd::two_sum(a[2], e, e); - c3 = qd::two_sum(a[3], e, e); - - qd::renorm(c0, c1, c2, c3, e); - - return qd_real(c0, c1, c2, c3); -} - -/* quad-double + double-double */ -inline qd_real operator+(const qd_real &a, const dd_real &b) { - - double s0, s1, s2, s3; - double t0, t1; - - s0 = qd::two_sum(a[0], b._hi(), t0); - s1 = qd::two_sum(a[1], b._lo(), t1); - - s1 = qd::two_sum(s1, t0, t0); - - s2 = a[2]; - qd::three_sum(s2, t0, t1); - - s3 = qd::two_sum(t0, a[3], t0); - t0 += t1; - - qd::renorm(s0, s1, s2, s3, t0); - return qd_real(s0, s1, s2, s3); -} - - -/* double + quad-double */ -inline qd_real operator+(double a, const qd_real &b) { - return (b + a); -} - -/* double-double + quad-double */ -inline qd_real operator+(const dd_real &a, const qd_real &b) { - return (b + a); -} - -namespace qd { - -/* s = quick_three_accum(a, b, c) adds c to the dd-pair (a, b). - * If the result does not fit in two doubles, then the sum is - * output into s and (a,b) contains the remainder. Otherwise - * s is zero and (a,b) contains the sum. */ -inline double quick_three_accum(double &a, double &b, double c) { - double s; - bool za, zb; - - s = qd::two_sum(b, c, b); - s = qd::two_sum(a, s, a); - - za = (a != 0.0); - zb = (b != 0.0); - - if (za && zb) - return s; - - if (!zb) { - b = a; - a = s; - } else { - a = s; - } - - return 0.0; -} - -} - -inline qd_real qd_real::ieee_add(const qd_real &a, const qd_real &b) { - int i, j, k; - double s, t; - double u, v; /* double-length accumulator */ - double x[4] = {0.0, 0.0, 0.0, 0.0}; - - i = j = k = 0; - if (std::abs(a[i]) > std::abs(b[j])) - u = a[i++]; - else - u = b[j++]; - if (std::abs(a[i]) > std::abs(b[j])) - v = a[i++]; - else - v = b[j++]; - - u = qd::quick_two_sum(u, v, v); - - while (k < 4) { - if (i >= 4 && j >= 4) { - x[k] = u; - if (k < 3) - x[++k] = v; - break; - } - - if (i >= 4) - t = b[j++]; - else if (j >= 4) - t = a[i++]; - else if (std::abs(a[i]) > std::abs(b[j])) { - t = a[i++]; - } else - t = b[j++]; - - s = qd::quick_three_accum(u, v, t); - - if (s != 0.0) { - x[k++] = s; - } - } - - /* add the rest. */ - for (k = i; k < 4; k++) - x[3] += a[k]; - for (k = j; k < 4; k++) - x[3] += b[k]; - - qd::renorm(x[0], x[1], x[2], x[3]); - return qd_real(x[0], x[1], x[2], x[3]); -} - -inline qd_real qd_real::sloppy_add(const qd_real &a, const qd_real &b) { - /* - double s0, s1, s2, s3; - double t0, t1, t2, t3; - - s0 = qd::two_sum(a[0], b[0], t0); - s1 = qd::two_sum(a[1], b[1], t1); - s2 = qd::two_sum(a[2], b[2], t2); - s3 = qd::two_sum(a[3], b[3], t3); - - s1 = qd::two_sum(s1, t0, t0); - qd::three_sum(s2, t0, t1); - qd::three_sum2(s3, t0, t2); - t0 = t0 + t1 + t3; - - qd::renorm(s0, s1, s2, s3, t0); - return qd_real(s0, s1, s2, s3, t0); - */ - - /* Same as above, but addition re-organized to minimize - data dependency ... unfortunately some compilers are - not very smart to do this automatically */ - double s0, s1, s2, s3; - double t0, t1, t2, t3; - - double v0, v1, v2, v3; - double u0, u1, u2, u3; - double w0, w1, w2, w3; - - s0 = a[0] + b[0]; - s1 = a[1] + b[1]; - s2 = a[2] + b[2]; - s3 = a[3] + b[3]; - - v0 = s0 - a[0]; - v1 = s1 - a[1]; - v2 = s2 - a[2]; - v3 = s3 - a[3]; - - u0 = s0 - v0; - u1 = s1 - v1; - u2 = s2 - v2; - u3 = s3 - v3; - - w0 = a[0] - u0; - w1 = a[1] - u1; - w2 = a[2] - u2; - w3 = a[3] - u3; - - u0 = b[0] - v0; - u1 = b[1] - v1; - u2 = b[2] - v2; - u3 = b[3] - v3; - - t0 = w0 + u0; - t1 = w1 + u1; - t2 = w2 + u2; - t3 = w3 + u3; - - s1 = qd::two_sum(s1, t0, t0); - qd::three_sum(s2, t0, t1); - qd::three_sum2(s3, t0, t2); - t0 = t0 + t1 + t3; - - /* renormalize */ - qd::renorm(s0, s1, s2, s3, t0); - return qd_real(s0, s1, s2, s3); -} - -/* quad-double + quad-double */ -inline qd_real operator+(const qd_real &a, const qd_real &b) { -#ifndef QD_IEEE_ADD - return qd_real::sloppy_add(a, b); -#else - return qd_real::ieee_add(a, b); -#endif -} - - - -/********** Self-Additions ************/ -/* quad-double += double */ -inline qd_real &qd_real::operator+=(double a) { - *this = *this + a; - return *this; -} - -/* quad-double += double-double */ -inline qd_real &qd_real::operator+=(const dd_real &a) { - *this = *this + a; - return *this; -} - -/* quad-double += quad-double */ -inline qd_real &qd_real::operator+=(const qd_real &a) { - *this = *this + a; - return *this; -} - -/********** Unary Minus **********/ -inline qd_real qd_real::operator-() const { - return qd_real(-x[0], -x[1], -x[2], -x[3]); -} - -/********** Subtractions **********/ -inline qd_real operator-(const qd_real &a, double b) { - return (a + (-b)); -} - -inline qd_real operator-(double a, const qd_real &b) { - return (a + (-b)); -} - -inline qd_real operator-(const qd_real &a, const dd_real &b) { - return (a + (-b)); -} - -inline qd_real operator-(const dd_real &a, const qd_real &b) { - return (a + (-b)); -} - -inline qd_real operator-(const qd_real &a, const qd_real &b) { - return (a + (-b)); -} - -/********** Self-Subtractions **********/ -inline qd_real &qd_real::operator-=(double a) { - return ((*this) += (-a)); -} - -inline qd_real &qd_real::operator-=(const dd_real &a) { - return ((*this) += (-a)); -} - -inline qd_real &qd_real::operator-=(const qd_real &a) { - return ((*this) += (-a)); -} - - -inline qd_real operator*(double a, const qd_real &b) { - return (b * a); -} - -inline qd_real operator*(const dd_real &a, const qd_real &b) { - return (b * a); -} - -inline qd_real mul_pwr2(const qd_real &a, double b) { - return qd_real(a[0] * b, a[1] * b, a[2] * b, a[3] * b); -} - -/********** Multiplications **********/ -inline qd_real operator*(const qd_real &a, double b) { - double p0, p1, p2, p3; - double q0, q1, q2; - double s0, s1, s2, s3, s4; - - p0 = qd::two_prod(a[0], b, q0); - p1 = qd::two_prod(a[1], b, q1); - p2 = qd::two_prod(a[2], b, q2); - p3 = a[3] * b; - - s0 = p0; - - s1 = qd::two_sum(q0, p1, s2); - - qd::three_sum(s2, q1, p2); - - qd::three_sum2(q1, q2, p3); - s3 = q1; - - s4 = q2 + p2; - - qd::renorm(s0, s1, s2, s3, s4); - return qd_real(s0, s1, s2, s3); - -} - -/* quad-double * double-double */ -/* a0 * b0 0 - a0 * b1 1 - a1 * b0 2 - a1 * b1 3 - a2 * b0 4 - a2 * b1 5 - a3 * b0 6 - a3 * b1 7 */ -inline qd_real operator*(const qd_real &a, const dd_real &b) { - double p0, p1, p2, p3, p4; - double q0, q1, q2, q3, q4; - double s0, s1, s2; - double t0, t1; - - p0 = qd::two_prod(a[0], b._hi(), q0); - p1 = qd::two_prod(a[0], b._lo(), q1); - p2 = qd::two_prod(a[1], b._hi(), q2); - p3 = qd::two_prod(a[1], b._lo(), q3); - p4 = qd::two_prod(a[2], b._hi(), q4); - - qd::three_sum(p1, p2, q0); - - /* Five-Three-Sum */ - qd::three_sum(p2, p3, p4); - q1 = qd::two_sum(q1, q2, q2); - s0 = qd::two_sum(p2, q1, t0); - s1 = qd::two_sum(p3, q2, t1); - s1 = qd::two_sum(s1, t0, t0); - s2 = t0 + t1 + p4; - p2 = s0; - - p3 = a[2] * b._hi() + a[3] * b._lo() + q3 + q4; - qd::three_sum2(p3, q0, s1); - p4 = q0 + s2; - - qd::renorm(p0, p1, p2, p3, p4); - return qd_real(p0, p1, p2, p3); -} - -/* quad-double * quad-double */ -/* a0 * b0 0 - a0 * b1 1 - a1 * b0 2 - a0 * b2 3 - a1 * b1 4 - a2 * b0 5 - a0 * b3 6 - a1 * b2 7 - a2 * b1 8 - a3 * b0 9 */ -inline qd_real qd_real::sloppy_mul(const qd_real &a, const qd_real &b) { - double p0, p1, p2, p3, p4, p5; - double q0, q1, q2, q3, q4, q5; - double t0, t1; - double s0, s1, s2; - - p0 = qd::two_prod(a[0], b[0], q0); - - p1 = qd::two_prod(a[0], b[1], q1); - p2 = qd::two_prod(a[1], b[0], q2); - - p3 = qd::two_prod(a[0], b[2], q3); - p4 = qd::two_prod(a[1], b[1], q4); - p5 = qd::two_prod(a[2], b[0], q5); - - /* Start Accumulation */ - qd::three_sum(p1, p2, q0); - - /* Six-Three Sum of p2, q1, q2, p3, p4, p5. */ - qd::three_sum(p2, q1, q2); - qd::three_sum(p3, p4, p5); - /* compute (s0, s1, s2) = (p2, q1, q2) + (p3, p4, p5). */ - s0 = qd::two_sum(p2, p3, t0); - s1 = qd::two_sum(q1, p4, t1); - s2 = q2 + p5; - s1 = qd::two_sum(s1, t0, t0); - s2 += (t0 + t1); - - /* O(eps^3) order terms */ - s1 += a[0]*b[3] + a[1]*b[2] + a[2]*b[1] + a[3]*b[0] + q0 + q3 + q4 + q5; - qd::renorm(p0, p1, s0, s1, s2); - return qd_real(p0, p1, s0, s1); -} - -inline qd_real qd_real::accurate_mul(const qd_real &a, const qd_real &b) { - double p0, p1, p2, p3, p4, p5; - double q0, q1, q2, q3, q4, q5; - double p6, p7, p8, p9; - double q6, q7, q8, q9; - double r0, r1; - double t0, t1; - double s0, s1, s2; - - p0 = qd::two_prod(a[0], b[0], q0); - - p1 = qd::two_prod(a[0], b[1], q1); - p2 = qd::two_prod(a[1], b[0], q2); - - p3 = qd::two_prod(a[0], b[2], q3); - p4 = qd::two_prod(a[1], b[1], q4); - p5 = qd::two_prod(a[2], b[0], q5); - - /* Start Accumulation */ - qd::three_sum(p1, p2, q0); - - /* Six-Three Sum of p2, q1, q2, p3, p4, p5. */ - qd::three_sum(p2, q1, q2); - qd::three_sum(p3, p4, p5); - /* compute (s0, s1, s2) = (p2, q1, q2) + (p3, p4, p5). */ - s0 = qd::two_sum(p2, p3, t0); - s1 = qd::two_sum(q1, p4, t1); - s2 = q2 + p5; - s1 = qd::two_sum(s1, t0, t0); - s2 += (t0 + t1); - - /* O(eps^3) order terms */ - p6 = qd::two_prod(a[0], b[3], q6); - p7 = qd::two_prod(a[1], b[2], q7); - p8 = qd::two_prod(a[2], b[1], q8); - p9 = qd::two_prod(a[3], b[0], q9); - - /* Nine-Two-Sum of q0, s1, q3, q4, q5, p6, p7, p8, p9. */ - q0 = qd::two_sum(q0, q3, q3); - q4 = qd::two_sum(q4, q5, q5); - p6 = qd::two_sum(p6, p7, p7); - p8 = qd::two_sum(p8, p9, p9); - /* Compute (t0, t1) = (q0, q3) + (q4, q5). */ - t0 = qd::two_sum(q0, q4, t1); - t1 += (q3 + q5); - /* Compute (r0, r1) = (p6, p7) + (p8, p9). */ - r0 = qd::two_sum(p6, p8, r1); - r1 += (p7 + p9); - /* Compute (q3, q4) = (t0, t1) + (r0, r1). */ - q3 = qd::two_sum(t0, r0, q4); - q4 += (t1 + r1); - /* Compute (t0, t1) = (q3, q4) + s1. */ - t0 = qd::two_sum(q3, s1, t1); - t1 += q4; - - /* O(eps^4) terms -- Nine-One-Sum */ - t1 += a[1] * b[3] + a[2] * b[2] + a[3] * b[1] + q6 + q7 + q8 + q9 + s2; - - qd::renorm(p0, p1, s0, t0, t1); - return qd_real(p0, p1, s0, t0); -} - -inline qd_real operator*(const qd_real &a, const qd_real &b) { -#ifdef QD_SLOPPY_MUL - return qd_real::sloppy_mul(a, b); -#else - return qd_real::accurate_mul(a, b); -#endif -} - -/* quad-double ^ 2 = (x0 + x1 + x2 + x3) ^ 2 - = x0 ^ 2 + 2 x0 * x1 + (2 x0 * x2 + x1 ^ 2) - + (2 x0 * x3 + 2 x1 * x2) */ -inline qd_real sqr(const qd_real &a) { - double p0, p1, p2, p3, p4, p5; - double q0, q1, q2, q3; - double s0, s1; - double t0, t1; - - p0 = qd::two_sqr(a[0], q0); - p1 = qd::two_prod(2.0 * a[0], a[1], q1); - p2 = qd::two_prod(2.0 * a[0], a[2], q2); - p3 = qd::two_sqr(a[1], q3); - - p1 = qd::two_sum(q0, p1, q0); - - q0 = qd::two_sum(q0, q1, q1); - p2 = qd::two_sum(p2, p3, p3); - - s0 = qd::two_sum(q0, p2, t0); - s1 = qd::two_sum(q1, p3, t1); - - s1 = qd::two_sum(s1, t0, t0); - t0 += t1; - - s1 = qd::quick_two_sum(s1, t0, t0); - p2 = qd::quick_two_sum(s0, s1, t1); - p3 = qd::quick_two_sum(t1, t0, q0); - - p4 = 2.0 * a[0] * a[3]; - p5 = 2.0 * a[1] * a[2]; - - p4 = qd::two_sum(p4, p5, p5); - q2 = qd::two_sum(q2, q3, q3); - - t0 = qd::two_sum(p4, q2, t1); - t1 = t1 + p5 + q3; - - p3 = qd::two_sum(p3, t0, p4); - p4 = p4 + q0 + t1; - - qd::renorm(p0, p1, p2, p3, p4); - return qd_real(p0, p1, p2, p3); - -} - -/********** Self-Multiplication **********/ -/* quad-double *= double */ -inline qd_real &qd_real::operator*=(double a) { - *this = (*this * a); - return *this; -} - -/* quad-double *= double-double */ -inline qd_real &qd_real::operator*=(const dd_real &a) { - *this = (*this * a); - return *this; -} - -/* quad-double *= quad-double */ -inline qd_real &qd_real::operator*=(const qd_real &a) { - *this = *this * a; - return *this; -} - -inline qd_real operator/ (const qd_real &a, const dd_real &b) { -#ifdef QD_SLOPPY_DIV - return qd_real::sloppy_div(a, b); -#else - return qd_real::accurate_div(a, b); -#endif -} - -inline qd_real operator/(const qd_real &a, const qd_real &b) { -#ifdef QD_SLOPPY_DIV - return qd_real::sloppy_div(a, b); -#else - return qd_real::accurate_div(a, b); -#endif -} - -/* double / quad-double */ -inline qd_real operator/(double a, const qd_real &b) { - return qd_real(a) / b; -} - -/* double-double / quad-double */ -inline qd_real operator/(const dd_real &a, const qd_real &b) { - return qd_real(a) / b; -} - -/********** Self-Divisions **********/ -/* quad-double /= double */ -inline qd_real &qd_real::operator/=(double a) { - *this = (*this / a); - return *this; -} - -/* quad-double /= double-double */ -inline qd_real &qd_real::operator/=(const dd_real &a) { - *this = (*this / a); - return *this; -} - -/* quad-double /= quad-double */ -inline qd_real &qd_real::operator/=(const qd_real &a) { - *this = (*this / a); - return *this; -} - - -/********** Exponentiation **********/ -inline qd_real qd_real::operator^(int n) const { - return pow(*this, n); -} - -/********** Miscellaneous **********/ -inline qd_real abs(const qd_real &a) { - return (a[0] < 0.0) ? -a : a; -} - -inline qd_real fabs(const qd_real &a) { - return abs(a); -} - -/* Quick version. May be off by one when qd is very close - to the middle of two integers. */ -inline qd_real quick_nint(const qd_real &a) { - qd_real r = qd_real(qd::nint(a[0]), qd::nint(a[1]), - qd::nint(a[2]), qd::nint(a[3])); - r.renorm(); - return r; -} - -/*********** Assignments ************/ -/* quad-double = double */ -inline qd_real &qd_real::operator=(double a) { - x[0] = a; - x[1] = x[2] = x[3] = 0.0; - return *this; -} - -/* quad-double = double-double */ -inline qd_real &qd_real::operator=(const dd_real &a) { - x[0] = a._hi(); - x[1] = a._lo(); - x[2] = x[3] = 0.0; - return *this; -} - -/********** Equality Comparison **********/ -inline bool operator==(const qd_real &a, double b) { - return (a[0] == b && a[1] == 0.0 && a[2] == 0.0 && a[3] == 0.0); -} - -inline bool operator==(double a, const qd_real &b) { - return (b == a); -} - -inline bool operator==(const qd_real &a, const dd_real &b) { - return (a[0] == b._hi() && a[1] == b._lo() && - a[2] == 0.0 && a[3] == 0.0); -} - -inline bool operator==(const dd_real &a, const qd_real &b) { - return (b == a); -} - -inline bool operator==(const qd_real &a, const qd_real &b) { - return (a[0] == b[0] && a[1] == b[1] && - a[2] == b[2] && a[3] == b[3]); -} - - -/********** Less-Than Comparison ***********/ -inline bool operator<(const qd_real &a, double b) { - return (a[0] < b || (a[0] == b && a[1] < 0.0)); -} - -inline bool operator<(double a, const qd_real &b) { - return (b > a); -} - -inline bool operator<(const qd_real &a, const dd_real &b) { - return (a[0] < b._hi() || - (a[0] == b._hi() && (a[1] < b._lo() || - (a[1] == b._lo() && a[2] < 0.0)))); -} - -inline bool operator<(const dd_real &a, const qd_real &b) { - return (b > a); -} - -inline bool operator<(const qd_real &a, const qd_real &b) { - return (a[0] < b[0] || - (a[0] == b[0] && (a[1] < b[1] || - (a[1] == b[1] && (a[2] < b[2] || - (a[2] == b[2] && a[3] < b[3])))))); -} - -/********** Greater-Than Comparison ***********/ -inline bool operator>(const qd_real &a, double b) { - return (a[0] > b || (a[0] == b && a[1] > 0.0)); -} - -inline bool operator>(double a, const qd_real &b) { - return (b < a); -} - -inline bool operator>(const qd_real &a, const dd_real &b) { - return (a[0] > b._hi() || - (a[0] == b._hi() && (a[1] > b._lo() || - (a[1] == b._lo() && a[2] > 0.0)))); -} - -inline bool operator>(const dd_real &a, const qd_real &b) { - return (b < a); -} - -inline bool operator>(const qd_real &a, const qd_real &b) { - return (a[0] > b[0] || - (a[0] == b[0] && (a[1] > b[1] || - (a[1] == b[1] && (a[2] > b[2] || - (a[2] == b[2] && a[3] > b[3])))))); -} - - -/********** Less-Than-Or-Equal-To Comparison **********/ -inline bool operator<=(const qd_real &a, double b) { - return (a[0] < b || (a[0] == b && a[1] <= 0.0)); -} - -inline bool operator<=(double a, const qd_real &b) { - return (b >= a); -} - -inline bool operator<=(const qd_real &a, const dd_real &b) { - return (a[0] < b._hi() || - (a[0] == b._hi() && (a[1] < b._lo() || - (a[1] == b._lo() && a[2] <= 0.0)))); -} - -inline bool operator<=(const dd_real &a, const qd_real &b) { - return (b >= a); -} - -inline bool operator<=(const qd_real &a, const qd_real &b) { - return (a[0] < b[0] || - (a[0] == b[0] && (a[1] < b[1] || - (a[1] == b[1] && (a[2] < b[2] || - (a[2] == b[2] && a[3] <= b[3])))))); -} - -/********** Greater-Than-Or-Equal-To Comparison **********/ -inline bool operator>=(const qd_real &a, double b) { - return (a[0] > b || (a[0] == b && a[1] >= 0.0)); -} - -inline bool operator>=(double a, const qd_real &b) { - return (b <= a); -} - -inline bool operator>=(const qd_real &a, const dd_real &b) { - return (a[0] > b._hi() || - (a[0] == b._hi() && (a[1] > b._lo() || - (a[1] == b._lo() && a[2] >= 0.0)))); -} - -inline bool operator>=(const dd_real &a, const qd_real &b) { - return (b <= a); -} - -inline bool operator>=(const qd_real &a, const qd_real &b) { - return (a[0] > b[0] || - (a[0] == b[0] && (a[1] > b[1] || - (a[1] == b[1] && (a[2] > b[2] || - (a[2] == b[2] && a[3] >= b[3])))))); -} - - - -/********** Not-Equal-To Comparison **********/ -inline bool operator!=(const qd_real &a, double b) { - return !(a == b); -} - -inline bool operator!=(double a, const qd_real &b) { - return !(a == b); -} - -inline bool operator!=(const qd_real &a, const dd_real &b) { - return !(a == b); -} - -inline bool operator!=(const dd_real &a, const qd_real &b) { - return !(a == b); -} - -inline bool operator!=(const qd_real &a, const qd_real &b) { - return !(a == b); -} - - - -inline qd_real aint(const qd_real &a) { - return (a[0] >= 0) ? floor(a) : ceil(a); -} - -inline bool qd_real::is_zero() const { - return (x[0] == 0.0); -} - -inline bool qd_real::is_one() const { - return (x[0] == 1.0 && x[1] == 0.0 && x[2] == 0.0 && x[3] == 0.0); -} - -inline bool qd_real::is_positive() const { - return (x[0] > 0.0); -} - -inline bool qd_real::is_negative() const { - return (x[0] < 0.0); -} - -inline qd_real::operator bool() const { - return (x[0] != 0.0); -} - -inline qd_real::operator double() const { - return to_double(*this); -} - -inline dd_real to_dd_real(const qd_real &a) { - return dd_real(a[0], a[1]); -} - -inline double to_double(const qd_real &a) { - return a[0]; -} - -inline int to_int(const qd_real &a) { - return static_cast(a[0]); -} - -inline qd_real inv(const qd_real &qd) { - return 1.0 / qd; -} - -inline qd_real max(const qd_real &a, const qd_real &b) { - return (a > b) ? a : b; -} - -inline qd_real max(const qd_real &a, const qd_real &b, - const qd_real &c) { - return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); -} - -inline qd_real min(const qd_real &a, const qd_real &b) { - return (a < b) ? a : b; -} - -inline qd_real min(const qd_real &a, const qd_real &b, - const qd_real &c) { - return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); -} - -/* Random number generator */ -inline qd_real qd_real::rand() { - return qdrand(); -} - -inline qd_real ldexp(const qd_real &a, int n) { - return qd_real(std::ldexp(a[0], n), std::ldexp(a[1], n), - std::ldexp(a[2], n), std::ldexp(a[3], n)); -} - -#endif /* _QD_QD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/qd_real.cc b/src/external/PackedCSparse/qd/qd_real.cc deleted file mode 100644 index 02cb7aa3..00000000 --- a/src/external/PackedCSparse/qd/qd_real.cc +++ /dev/null @@ -1,2624 +0,0 @@ -/* - * src/qd_real.cc - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2007 - * - * Contains implementation of non-inlined functions of quad-double - * package. Inlined functions are found in qd_inline.h (in include directory). - */ -#include -#include -#include -#include -#include -#include -#include - -#include "qd_config.h" -#include "qd_real.h" -#include "util.h" - -#include "bits.h" - -#ifndef QD_INLINE -#include "qd_inline.h" -#endif - -using std::cout; -using std::cerr; -using std::endl; -using std::istream; -using std::ostream; -using std::ios_base; -using std::string; -using std::setw; - -using namespace qd; - -void qd_real::error(const char *msg) { - //if (msg) { cerr << "ERROR " << msg << endl; } -} - -/********** Multiplications **********/ - -qd_real nint(const qd_real &a) { - double x0, x1, x2, x3; - - x0 = nint(a[0]); - x1 = x2 = x3 = 0.0; - - if (x0 == a[0]) { - /* First double is already an integer. */ - x1 = nint(a[1]); - - if (x1 == a[1]) { - /* Second double is already an integer. */ - x2 = nint(a[2]); - - if (x2 == a[2]) { - /* Third double is already an integer. */ - x3 = nint(a[3]); - } else { - if (std::abs(x2 - a[2]) == 0.5 && a[3] < 0.0) { - x2 -= 1.0; - } - } - - } else { - if (std::abs(x1 - a[1]) == 0.5 && a[2] < 0.0) { - x1 -= 1.0; - } - } - - } else { - /* First double is not an integer. */ - if (std::abs(x0 - a[0]) == 0.5 && a[1] < 0.0) { - x0 -= 1.0; - } - } - - renorm(x0, x1, x2, x3); - return qd_real(x0, x1, x2, x3); -} - -qd_real floor(const qd_real &a) { - double x0, x1, x2, x3; - x1 = x2 = x3 = 0.0; - x0 = std::floor(a[0]); - - if (x0 == a[0]) { - x1 = std::floor(a[1]); - - if (x1 == a[1]) { - x2 = std::floor(a[2]); - - if (x2 == a[2]) { - x3 = std::floor(a[3]); - } - } - - renorm(x0, x1, x2, x3); - return qd_real(x0, x1, x2, x3); - } - - return qd_real(x0, x1, x2, x3); -} - -qd_real ceil(const qd_real &a) { - double x0, x1, x2, x3; - x1 = x2 = x3 = 0.0; - x0 = std::ceil(a[0]); - - if (x0 == a[0]) { - x1 = std::ceil(a[1]); - - if (x1 == a[1]) { - x2 = std::ceil(a[2]); - - if (x2 == a[2]) { - x3 = std::ceil(a[3]); - } - } - - renorm(x0, x1, x2, x3); - return qd_real(x0, x1, x2, x3); - } - - return qd_real(x0, x1, x2, x3); -} - - - -/********** Divisions **********/ -/* quad-double / double */ -qd_real operator/(const qd_real &a, double b) { - /* Strategy: compute approximate quotient using high order - doubles, and then correct it 3 times using the remainder. - (Analogous to long division.) */ - double t0, t1; - double q0, q1, q2, q3; - qd_real r; - - q0 = a[0] / b; /* approximate quotient */ - - /* Compute the remainder a - q0 * b */ - t0 = two_prod(q0, b, t1); - r = a - dd_real(t0, t1); - - /* Compute the first correction */ - q1 = r[0] / b; - t0 = two_prod(q1, b, t1); - r -= dd_real(t0, t1); - - /* Second correction to the quotient. */ - q2 = r[0] / b; - t0 = two_prod(q2, b, t1); - r -= dd_real(t0, t1); - - /* Final correction to the quotient. */ - q3 = r[0] / b; - - renorm(q0, q1, q2, q3); - return qd_real(q0, q1, q2, q3); -} - -qd_real::qd_real(const char *s) { - if (qd_real::read(s, *this)) { - qd_real::error("(qd_real::qd_real): INPUT ERROR."); - *this = qd_real::_nan; - } -} - -qd_real &qd_real::operator=(const char *s) { - if (qd_real::read(s, *this)) { - qd_real::error("(qd_real::operator=): INPUT ERROR."); - *this = qd_real::_nan; - } - return *this; -} - -istream &operator>>(istream &s, qd_real &qd) { - char str[255]; - s >> str; - qd = qd_real(str); - return s; -} - -ostream &operator<<(ostream &os, const qd_real &qd) { - bool showpos = (os.flags() & ios_base::showpos) != 0; - bool uppercase = (os.flags() & ios_base::uppercase) != 0; - return os << qd.to_string((int)os.precision(), (int)os.width(), os.flags(), - showpos, uppercase, os.fill()); -} - -/* Read a quad-double from s. */ -int qd_real::read(const char *s, qd_real &qd) { - const char *p = s; - char ch; - int sign = 0; - int point = -1; /* location of decimal point */ - int nd = 0; /* number of digits read */ - int e = 0; /* exponent. */ - bool done = false; - qd_real r = 0.0; /* number being read */ - - /* Skip any leading spaces */ - while (*p == ' ') p++; - - while (!done && (ch = *p) != '\0') { - if (ch >= '0' && ch <= '9') { - /* It's a digit */ - int d = ch - '0'; - r *= 10.0; - r += static_cast(d); - nd++; - } else { - /* Non-digit */ - switch (ch) { - case '.': - if (point >= 0) - return -1; /* we've already encountered a decimal point. */ - point = nd; - break; - case '-': - case '+': - if (sign != 0 || nd > 0) - return -1; /* we've already encountered a sign, or if its - not at first position. */ - sign = (ch == '-') ? -1 : 1; - break; - case 'E': - case 'e': - int nread; - nread = std::sscanf(p+1, "%d", &e); - done = true; - if (nread != 1) - return -1; /* read of exponent failed. */ - break; - case ' ': - done = true; - break; - default: - return -1; - - } - } - - p++; - } - - - - /* Adjust exponent to account for decimal point */ - if (point >= 0) { - e -= (nd - point); - } - - /* Multiply the the exponent */ - if (e != 0) { - r *= (qd_real(10.0) ^ e); - } - - qd = (sign < 0) ? -r : r; - return 0; -} - -void qd_real::to_digits(char *s, int &expn, int precision) const { - int D = precision + 1; /* number of digits to compute */ - - qd_real r = abs(*this); - int e; /* exponent */ - int i, d; - - if (x[0] == 0.0) { - /* this == 0.0 */ - expn = 0; - for (i = 0; i < precision; i++) s[i] = '0'; - return; - } - - /* First determine the (approximate) exponent. */ - e = static_cast(std::floor(std::log10(std::abs(x[0])))); - - if (e < -300) { - r *= qd_real(10.0) ^ 300; - r /= qd_real(10.0) ^ (e + 300); - } else if (e > 300) { - r = ldexp(r, -53); - r /= qd_real(10.0) ^ e; - r = ldexp(r, 53); - } else { - r /= qd_real(10.0) ^ e; - } - - /* Fix exponent if we are off by one */ - if (r >= 10.0) { - r /= 10.0; - e++; - } else if (r < 1.0) { - r *= 10.0; - e--; - } - - if (r >= 10.0 || r < 1.0) { - qd_real::error("(qd_real::to_digits): can't compute exponent."); - return; - } - - /* Extract the digits */ - for (i = 0; i < D; i++) { - d = static_cast(r[0]); - r -= d; - r *= 10.0; - - s[i] = static_cast(d + '0'); - } - - /* Fix out of range digits. */ - for (i = D-1; i > 0; i--) { - if (s[i] < '0') { - s[i-1]--; - s[i] += 10; - } else if (s[i] > '9') { - s[i-1]++; - s[i] -= 10; - } - } - - if (s[0] <= '0') { - qd_real::error("(qd_real::to_digits): non-positive leading digit."); - return; - } - - /* Round, handle carry */ - if (s[D-1] >= '5') { - s[D-2]++; - - i = D-2; - while (i > 0 && s[i] > '9') { - s[i] -= 10; - s[--i]++; - } - } - - /* If first digit is 10, shift everything. */ - if (s[0] > '9') { - e++; - for (i = precision; i >= 2; i--) s[i] = s[i-1]; - s[0] = '1'; - s[1] = '0'; - } - - s[precision] = 0; - expn = e; -} - -/* Writes the quad-double number into the character array s of length len. - The integer d specifies how many significant digits to write. - The string s must be able to hold at least (d+8) characters. - showpos indicates whether to use the + sign, and uppercase indicates - whether the E or e is to be used for the exponent. */ -void qd_real::write(char *s, int len, int precision, - bool showpos, bool uppercase) const { - string str = to_string(precision, 0, ios_base::scientific, showpos, uppercase); - strncpy(s, str.c_str(), len-1); - s[len-1] = 0; -} - -void round_string_qd(char *s, int precision, int *offset){ - /* - Input string must be all digits or errors will occur. - */ - - int i; - int D = precision ; - - /* Round, handle carry */ - if (D>0 && s[D] >= '5') { - s[D-1]++; - - i = D-1; - while (i > 0 && s[i] > '9') { - s[i] -= 10; - s[--i]++; - } - } - - /* If first digit is 10, shift everything. */ - if (s[0] > '9') { - // e++; // don't modify exponent here - for (i = precision; i >= 1; i--) s[i+1] = s[i]; - s[0] = '1'; - s[1] = '0'; - - (*offset)++ ; // now offset needs to be increased by one - precision++ ; - } - - s[precision] = 0; // add terminator for array -} - - -string qd_real::to_string(int precision, int width, ios_base::fmtflags fmt, - bool showpos, bool uppercase, char fill) const { - string s; - bool fixed = (fmt & ios_base::fixed) != 0; - bool sgn = true; - int i, e = 0; - - if (isinf()) { - if (*this < 0.0) - s += '-'; - else if (showpos) - s += '+'; - else - sgn = false; - s += uppercase ? "INF" : "inf"; - } else if (isnan()) { - s = uppercase ? "NAN" : "nan"; - sgn = false; - } else { - if (*this < 0.0) - s += '-'; - else if (showpos) - s += '+'; - else - sgn = false; - - if (*this == 0.0) { - /* Zero case */ - s += '0'; - if (precision > 0) { - s += '.'; - s.append(precision, '0'); - } - } else { - /* Non-zero case */ - int off = (fixed ? (1 + to_int(floor(log10(abs(*this))))) : 1); - int d = precision + off; - - int d_with_extra = d; - if(fixed) - d_with_extra = std::max(120, d); // longer than the max accuracy for DD - - // highly special case - fixed mode, precision is zero, abs(*this) < 1.0 - // without this trap a number like 0.9 printed fixed with 0 precision prints as 0 - // should be rounded to 1. - if(fixed && (precision == 0) && (abs(*this) < 1.0)){ - if(abs(*this) >= 0.5) - s += '1'; - else - s += '0'; - - return s; - } - - // handle near zero to working precision (but not exactly zero) - if (fixed && d <= 0) { - s += '0'; - if (precision > 0) { - s += '.'; - s.append(precision, '0'); - } - } else { // default - - char *t ; // = new char[d+1]; - int j; - - if(fixed){ - t = new char[d_with_extra+1]; - to_digits(t, e, d_with_extra); - } - else{ - t = new char[d+1]; - to_digits(t, e, d); - } - - off = e + 1; - - if (fixed) { - // fix the string if it's been computed incorrectly - // round here in the decimal string if required - round_string_qd(t, d, &off); - - if (off > 0) { - for (i = 0; i < off; i++) s += t[i]; - if (precision > 0) { - s += '.'; - for (j = 0; j < precision; j++, i++) s += t[i]; - } - } else { - s += "0."; - if (off < 0) s.append(-off, '0'); - for (i = 0; i < d; i++) s += t[i]; - } - } else { - s += t[0]; - if (precision > 0) s += '.'; - - for (i = 1; i <= precision; i++) - s += t[i]; - - } - delete [] t; - } - } - - // trap for improper offset with large values - // without this trap, output of values of the for 10^j - 1 fail for j > 28 - // and are output with the point in the wrong place, leading to a dramatically off value - if(fixed && (precision > 0)){ - // make sure that the value isn't dramatically larger - double from_string = atof(s.c_str()); - - // if this ratio is large, then we've got problems - if( fabs( from_string / this->x[0] ) > 3.0 ){ - - // loop on the string, find the point, move it up one - // don't act on the first character - for(i=1; i < (int)s.length(); i++){ - if(s[i] == '.'){ - s[i] = s[i-1] ; - s[i-1] = '.' ; - break; - } - } - - from_string = atof(s.c_str()); - // if this ratio is large, then the string has not been fixed - if( fabs( from_string / this->x[0] ) > 3.0 ){ - dd_real::error("Re-rounding unsuccessful in large number fixed point trap.") ; - } - } - } - - if (!fixed) { - /* Fill in exponent part */ - s += uppercase ? 'E' : 'e'; - append_expn(s, e); - } - } - - /* Fill in the blanks */ - size_t len = s.length(); - if (len < width) { - int delta = width - len; - if (fmt & ios_base::internal) { - if (sgn) - s.insert(static_cast(1), delta, fill); - else - s.insert(static_cast(0), delta, fill); - } else if (fmt & ios_base::left) { - s.append(delta, fill); - } else { - s.insert(static_cast(0), delta, fill); - } - } - - return s; -} - -/* Computes qd^n, where n is an integer. */ -qd_real pow(const qd_real &a, int n) { - if (n == 0) - return 1.0; - - qd_real r = a; /* odd-case multiplier */ - qd_real s = 1.0; /* current answer */ - int N = std::abs(n); - - if (N > 1) { - - /* Use binary exponentiation. */ - while (N > 0) { - if (N % 2 == 1) { - /* If odd, multiply by r. Note eventually N = 1, so this - eventually executes. */ - s *= r; - } - N /= 2; - if (N > 0) - r = sqr(r); - } - - } else { - s = r; - } - - if (n < 0) - return (1.0 / s); - - return s; -} - -qd_real pow(const qd_real &a, const qd_real &b) { - return exp(b * log(a)); -} - -qd_real npwr(const qd_real &a, int n) { - return pow(a, n); -} - -/* Debugging routines */ -void qd_real::dump_bits(const string &name, std::ostream &os) const { - string::size_type len = name.length(); - if (len > 0) { - os << name << " = "; - len += 3; - } - os << "[ "; - len += 2; - for (int j = 0; j < 4; j++) { - if (j > 0) for (string::size_type i = 0; i < len; i++) os << ' '; - print_double_info(os, x[j]); - if (j < 3) - os << endl; - else - os << " ]" << endl; - } -} - -void qd_real::dump(const string &name, std::ostream &os) const { - std::ios_base::fmtflags old_flags = os.flags(); - std::streamsize old_prec = os.precision(19); - os << std::scientific; - - string::size_type len = name.length(); - if (len > 0) { - os << name << " = "; - len += 3; - } - os << "[ "; - len += 2; - os << setw(27) << x[0] << ", " << setw(26) << x[1] << "," << endl; - for (string::size_type i = 0; i < len; i++) os << ' '; - os << setw(27) << x[2] << ", " << setw(26) << x[3] << " ]" << endl; - - os.precision(old_prec); - os.flags(old_flags); -} - -/* Divisions */ -/* quad-double / double-double */ -qd_real qd_real::sloppy_div(const qd_real &a, const dd_real &b) { - double q0, q1, q2, q3; - qd_real r; - qd_real qd_b(b); - - q0 = a[0] / b._hi(); - r = a - q0 * qd_b; - - q1 = r[0] / b._hi(); - r -= (q1 * qd_b); - - q2 = r[0] / b._hi(); - r -= (q2 * qd_b); - - q3 = r[0] / b._hi(); - - ::renorm(q0, q1, q2, q3); - return qd_real(q0, q1, q2, q3); -} - -qd_real qd_real::accurate_div(const qd_real &a, const dd_real &b) { - double q0, q1, q2, q3, q4; - qd_real r; - qd_real qd_b(b); - - q0 = a[0] / b._hi(); - r = a - q0 * qd_b; - - q1 = r[0] / b._hi(); - r -= (q1 * qd_b); - - q2 = r[0] / b._hi(); - r -= (q2 * qd_b); - - q3 = r[0] / b._hi(); - r -= (q3 * qd_b); - - q4 = r[0] / b._hi(); - - ::renorm(q0, q1, q2, q3, q4); - return qd_real(q0, q1, q2, q3); -} - -/* quad-double / quad-double */ -qd_real qd_real::sloppy_div(const qd_real &a, const qd_real &b) { - double q0, q1, q2, q3; - - qd_real r; - - q0 = a[0] / b[0]; - r = a - (b * q0); - - q1 = r[0] / b[0]; - r -= (b * q1); - - q2 = r[0] / b[0]; - r -= (b * q2); - - q3 = r[0] / b[0]; - - ::renorm(q0, q1, q2, q3); - - return qd_real(q0, q1, q2, q3); -} - -qd_real qd_real::accurate_div(const qd_real &a, const qd_real &b) { - double q0, q1, q2, q3; - - qd_real r; - - q0 = a[0] / b[0]; - r = a - (b * q0); - - q1 = r[0] / b[0]; - r -= (b * q1); - - q2 = r[0] / b[0]; - r -= (b * q2); - - q3 = r[0] / b[0]; - - r -= (b * q3); - double q4 = r[0] / b[0]; - - ::renorm(q0, q1, q2, q3, q4); - - return qd_real(q0, q1, q2, q3); -} - -QD_API qd_real sqrt(const qd_real &a) { - /* Strategy: - - Perform the following Newton iteration: - - x' = x + (1 - a * x^2) * x / 2; - - which converges to 1/sqrt(a), starting with the - double precision approximation to 1/sqrt(a). - Since Newton's iteration more or less doubles the - number of correct digits, we only need to perform it - twice. - */ - - if (a.is_zero()) - return 0.0; - - if (a.is_negative()) { - qd_real::error("(qd_real::sqrt): Negative argument."); - return qd_real::_nan; - } - - qd_real r = (1.0 / std::sqrt(a[0])); - qd_real h = mul_pwr2(a, 0.5); - - r += ((0.5 - h * sqr(r)) * r); - r += ((0.5 - h * sqr(r)) * r); - r += ((0.5 - h * sqr(r)) * r); - - r *= a; - return r; -} - - -/* Computes the n-th root of a */ -qd_real nroot(const qd_real &a, int n) { - /* Strategy: Use Newton's iteration to solve - - 1/(x^n) - a = 0 - - Newton iteration becomes - - x' = x + x * (1 - a * x^n) / n - - Since Newton's iteration converges quadratically, - we only need to perform it twice. - - */ - if (n <= 0) { - qd_real::error("(qd_real::nroot): N must be positive."); - return qd_real::_nan; - } - - if (n % 2 == 0 && a.is_negative()) { - qd_real::error("(qd_real::nroot): Negative argument."); - return qd_real::_nan; - } - - if (n == 1) { - return a; - } - if (n == 2) { - return sqrt(a); - } - if (a.is_zero()) { - return qd_real(0.0); - } - - - /* Note a^{-1/n} = exp(-log(a)/n) */ - qd_real r = abs(a); - qd_real x = std::exp(-std::log(r.x[0]) / n); - - /* Perform Newton's iteration. */ - double dbl_n = static_cast(n); - x += x * (1.0 - r * npwr(x, n)) / dbl_n; - x += x * (1.0 - r * npwr(x, n)) / dbl_n; - x += x * (1.0 - r * npwr(x, n)) / dbl_n; - if (a[0] < 0.0){ - x = -x; - } - return 1.0 / x; -} - -static const int n_inv_fact = 15; -static const qd_real inv_fact[n_inv_fact] = { - qd_real( 1.66666666666666657e-01, 9.25185853854297066e-18, - 5.13581318503262866e-34, 2.85094902409834186e-50), - qd_real( 4.16666666666666644e-02, 2.31296463463574266e-18, - 1.28395329625815716e-34, 7.12737256024585466e-51), - qd_real( 8.33333333333333322e-03, 1.15648231731787138e-19, - 1.60494162032269652e-36, 2.22730392507682967e-53), - qd_real( 1.38888888888888894e-03, -5.30054395437357706e-20, - -1.73868675534958776e-36, -1.63335621172300840e-52), - qd_real( 1.98412698412698413e-04, 1.72095582934207053e-22, - 1.49269123913941271e-40, 1.29470326746002471e-58), - qd_real( 2.48015873015873016e-05, 2.15119478667758816e-23, - 1.86586404892426588e-41, 1.61837908432503088e-59), - qd_real( 2.75573192239858925e-06, -1.85839327404647208e-22, - 8.49175460488199287e-39, -5.72661640789429621e-55), - qd_real( 2.75573192239858883e-07, 2.37677146222502973e-23, - -3.26318890334088294e-40, 1.61435111860404415e-56), - qd_real( 2.50521083854417202e-08, -1.44881407093591197e-24, - 2.04267351467144546e-41, -8.49632672007163175e-58), - qd_real( 2.08767569878681002e-09, -1.20734505911325997e-25, - 1.70222792889287100e-42, 1.41609532150396700e-58), - qd_real( 1.60590438368216133e-10, 1.25852945887520981e-26, - -5.31334602762985031e-43, 3.54021472597605528e-59), - qd_real( 1.14707455977297245e-11, 2.06555127528307454e-28, - 6.88907923246664603e-45, 5.72920002655109095e-61), - qd_real( 7.64716373181981641e-13, 7.03872877733453001e-30, - -7.82753927716258345e-48, 1.92138649443790242e-64), - qd_real( 4.77947733238738525e-14, 4.39920548583408126e-31, - -4.89221204822661465e-49, 1.20086655902368901e-65), - qd_real( 2.81145725434552060e-15, 1.65088427308614326e-31, - -2.87777179307447918e-50, 4.27110689256293549e-67) -}; - -qd_real exp(const qd_real &a) { - /* Strategy: We first reduce the size of x by noting that - - exp(kr + m * log(2)) = 2^m * exp(r)^k - - where m and k are integers. By choosing m appropriately - we can make |kr| <= log(2) / 2 = 0.347. Then exp(r) is - evaluated using the familiar Taylor series. Reducing the - argument substantially speeds up the convergence. */ - - const double k = ldexp(1.0, 16); - const double inv_k = 1.0 / k; - - if (a[0] <= -709.0) - return 0.0; - - if (a[0] >= 709.0) - return qd_real::_inf; - - if (a.is_zero()) - return 1.0; - - if (a.is_one()) - return qd_real::_e; - - double m = std::floor(a.x[0] / qd_real::_log2.x[0] + 0.5); - qd_real r = mul_pwr2(a - qd_real::_log2 * m, inv_k); - qd_real s, p, t; - double thresh = inv_k * qd_real::_eps; - - p = sqr(r); - s = r + mul_pwr2(p, 0.5); - int i = 0; - do { - p *= r; - t = p * inv_fact[i++]; - s += t; - } while (std::abs(to_double(t)) > thresh && i < 9); - - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s = mul_pwr2(s, 2.0) + sqr(s); - s += 1.0; - return ldexp(s, static_cast(m)); -} - -/* Logarithm. Computes log(x) in quad-double precision. - This is a natural logarithm (i.e., base e). */ -qd_real log(const qd_real &a) { - /* Strategy. The Taylor series for log converges much more - slowly than that of exp, due to the lack of the factorial - term in the denominator. Hence this routine instead tries - to determine the root of the function - - f(x) = exp(x) - a - - using Newton iteration. The iteration is given by - - x' = x - f(x)/f'(x) - = x - (1 - a * exp(-x)) - = x + a * exp(-x) - 1. - - Two iteration is needed, since Newton's iteration - approximately doubles the number of digits per iteration. */ - - if (a.is_one()) { - return 0.0; - } - - if (a[0] <= 0.0) { - qd_real::error("(qd_real::log): Non-positive argument."); - return qd_real::_nan; - } - - if (a[0] == 0.0) { - return -qd_real::_inf; - } - - qd_real x = std::log(a[0]); /* Initial approximation */ - - x = x + a * exp(-x) - 1.0; - x = x + a * exp(-x) - 1.0; - x = x + a * exp(-x) - 1.0; - - return x; -} - -qd_real log10(const qd_real &a) { - return log(a) / qd_real::_log10; -} - -static const qd_real _pi1024 = qd_real( - 3.067961575771282340e-03, 1.195944139792337116e-19, - -2.924579892303066080e-36, 1.086381075061880158e-52); - -/* Table of sin(k * pi/1024) and cos(k * pi/1024). */ -static const qd_real sin_table [] = { - qd_real( 3.0679567629659761e-03, 1.2690279085455925e-19, - 5.2879464245328389e-36, -1.7820334081955298e-52), - qd_real( 6.1358846491544753e-03, 9.0545257482474933e-20, - 1.6260113133745320e-37, -9.7492001208767410e-55), - qd_real( 9.2037547820598194e-03, -1.2136591693535934e-19, - 5.5696903949425567e-36, 1.2505635791936951e-52), - qd_real( 1.2271538285719925e-02, 6.9197907640283170e-19, - -4.0203726713435555e-36, -2.0688703606952816e-52), - qd_real( 1.5339206284988102e-02, -8.4462578865401696e-19, - 4.6535897505058629e-35, -1.3923682978570467e-51), - qd_real( 1.8406729905804820e-02, 7.4195533812833160e-19, - 3.9068476486787607e-35, 3.6393321292898614e-52), - qd_real( 2.1474080275469508e-02, -4.5407960207688566e-19, - -2.2031770119723005e-35, 1.2709814654833741e-51), - qd_real( 2.4541228522912288e-02, -9.1868490125778782e-20, - 4.8706148704467061e-36, -2.8153947855469224e-52), - qd_real( 2.7608145778965743e-02, -1.5932358831389269e-18, - -7.0475416242776030e-35, -2.7518494176602744e-51), - qd_real( 3.0674803176636626e-02, -1.6936054844107918e-20, - -2.0039543064442544e-36, -1.6267505108658196e-52), - qd_real( 3.3741171851377587e-02, -2.0096074292368340e-18, - -1.3548237016537134e-34, 6.5554881875899973e-51), - qd_real( 3.6807222941358832e-02, 6.1060088803529842e-19, - -4.0448721259852727e-35, -2.1111056765671495e-51), - qd_real( 3.9872927587739811e-02, 4.6657453481183289e-19, - 3.4119333562288684e-35, 2.4007534726187511e-51), - qd_real( 4.2938256934940820e-02, 2.8351940588660907e-18, - 1.6991309601186475e-34, 6.8026536098672629e-51), - qd_real( 4.6003182130914630e-02, -1.1182813940157788e-18, - 7.5235020270378946e-35, 4.1187304955493722e-52), - qd_real( 4.9067674327418015e-02, -6.7961037205182801e-19, - -4.4318868124718325e-35, -9.9376628132525316e-52), - qd_real( 5.2131704680283324e-02, -2.4243695291953779e-18, - -1.3675405320092298e-34, -8.3938137621145070e-51), - qd_real( 5.5195244349689941e-02, -1.3340299860891103e-18, - -3.4359574125665608e-35, 1.1911462755409369e-51), - qd_real( 5.8258264500435759e-02, 2.3299905496077492e-19, - 1.9376108990628660e-36, -5.1273775710095301e-53), - qd_real( 6.1320736302208578e-02, -5.1181134064638108e-19, - -4.2726335866706313e-35, 2.6368495557440691e-51), - qd_real( 6.4382630929857465e-02, -4.2325997000052705e-18, - 3.3260117711855937e-35, 1.4736267706718352e-51), - qd_real( 6.7443919563664065e-02, -6.9221796556983636e-18, - 1.5909286358911040e-34, -7.8828946891835218e-51), - qd_real( 7.0504573389613870e-02, -6.8552791107342883e-18, - -1.9961177630841580e-34, 2.0127129580485300e-50), - qd_real( 7.3564563599667426e-02, -2.7784941506273593e-18, - -9.1240375489852821e-35, -1.9589752023546795e-51), - qd_real( 7.6623861392031492e-02, 2.3253700287958801e-19, - -1.3186083921213440e-36, -4.9927872608099673e-53), - qd_real( 7.9682437971430126e-02, -4.4867664311373041e-18, - 2.8540789143650264e-34, 2.8491348583262741e-51), - qd_real( 8.2740264549375692e-02, 1.4735983530877760e-18, - 3.7284093452233713e-35, 2.9024430036724088e-52), - qd_real( 8.5797312344439894e-02, -3.3881893830684029e-18, - -1.6135529531508258e-34, 7.7294651620588049e-51), - qd_real( 8.8853552582524600e-02, -3.7501775830290691e-18, - 3.7543606373911573e-34, 2.2233701854451859e-50), - qd_real( 9.1908956497132724e-02, 4.7631594854274564e-18, - 1.5722874642939344e-34, -4.8464145447831456e-51), - qd_real( 9.4963495329639006e-02, -6.5885886400417564e-18, - -2.1371116991641965e-34, 1.3819370559249300e-50), - qd_real( 9.8017140329560604e-02, -1.6345823622442560e-18, - -1.3209238810006454e-35, -3.5691060049117942e-52), - qd_real( 1.0106986275482782e-01, 3.3164325719308656e-18, - -1.2004224885132282e-34, 7.2028828495418631e-51), - qd_real( 1.0412163387205457e-01, 6.5760254085385100e-18, - 1.7066246171219214e-34, -4.9499340996893514e-51), - qd_real( 1.0717242495680884e-01, 6.4424044279026198e-18, - -8.3956976499698139e-35, -4.0667730213318321e-51), - qd_real( 1.1022220729388306e-01, -5.6789503537823233e-19, - 1.0380274792383233e-35, 1.5213997918456695e-52), - qd_real( 1.1327095217756435e-01, 2.7100481012132900e-18, - 1.5323292999491619e-35, 4.9564432810360879e-52), - qd_real( 1.1631863091190477e-01, 1.0294914877509705e-18, - -9.3975734948993038e-35, 1.3534827323719708e-52), - qd_real( 1.1936521481099137e-01, -3.9500089391898506e-18, - 3.5317349978227311e-34, 1.8856046807012275e-51), - qd_real( 1.2241067519921620e-01, 2.8354501489965335e-18, - 1.8151655751493305e-34, -2.8716592177915192e-51), - qd_real( 1.2545498341154623e-01, 4.8686751763148235e-18, - 5.9878105258097936e-35, -3.3534629098722107e-51), - qd_real( 1.2849811079379317e-01, 3.8198603954988802e-18, - -1.8627501455947798e-34, -2.4308161133527791e-51), - qd_real( 1.3154002870288312e-01, -5.0039708262213813e-18, - -1.2983004159245552e-34, -4.6872034915794122e-51), - qd_real( 1.3458070850712620e-01, -9.1670359171480699e-18, - 1.5916493007073973e-34, 4.0237002484366833e-51), - qd_real( 1.3762012158648604e-01, 6.6253255866774482e-18, - -2.3746583031401459e-34, -9.3703876173093250e-52), - qd_real( 1.4065823933284924e-01, -7.9193932965524741e-18, - 6.0972464202108397e-34, 2.4566623241035797e-50), - qd_real( 1.4369503315029444e-01, 1.1472723016618666e-17, - -5.1884954557576435e-35, -4.2220684832186607e-51), - qd_real( 1.4673047445536175e-01, 3.7269471470465677e-18, - 3.7352398151250827e-34, -4.0881822289508634e-51), - qd_real( 1.4976453467732151e-01, 8.0812114131285151e-18, - 1.2979142554917325e-34, 9.9380667487736254e-51), - qd_real( 1.5279718525844344e-01, -7.6313573938416838e-18, - 5.7714690450284125e-34, -3.7731132582986687e-50), - qd_real( 1.5582839765426523e-01, 3.0351307187678221e-18, - -1.0976942315176184e-34, 7.8734647685257867e-51), - qd_real( 1.5885814333386145e-01, -4.0163200573859079e-18, - -9.2840580257628812e-35, -2.8567420029274875e-51), - qd_real( 1.6188639378011183e-01, 1.1850519643573528e-17, - -5.0440990519162957e-34, 3.0510028707928009e-50), - qd_real( 1.6491312048996992e-01, -7.0405288319166738e-19, - 3.3211107491245527e-35, 8.6663299254686031e-52), - qd_real( 1.6793829497473117e-01, 5.4284533721558139e-18, - -3.3263339336181369e-34, -1.8536367335123848e-50), - qd_real( 1.7096188876030122e-01, 9.1919980181759094e-18, - -6.7688743940982606e-34, -1.0377711384318389e-50), - qd_real( 1.7398387338746382e-01, 5.8151994618107928e-18, - -1.6751014298301606e-34, -6.6982259797164963e-51), - qd_real( 1.7700422041214875e-01, 6.7329300635408167e-18, - 2.8042736644246623e-34, 3.6786888232793599e-51), - qd_real( 1.8002290140569951e-01, 7.9701826047392143e-18, - -7.0765920110524977e-34, 1.9622512608461784e-50), - qd_real( 1.8303988795514095e-01, 7.7349918688637383e-18, - -4.4803769968145083e-34, 1.1201148793328890e-50), - qd_real( 1.8605515166344666e-01, -1.2564893007679552e-17, - 7.5953844248530810e-34, -3.8471695132415039e-51), - qd_real( 1.8906866414980622e-01, -7.6208955803527778e-18, - -4.4792298656662981e-34, -4.4136824096645007e-50), - qd_real( 1.9208039704989244e-01, 4.3348343941174903e-18, - -2.3404121848139937e-34, 1.5789970962611856e-50), - qd_real( 1.9509032201612828e-01, -7.9910790684617313e-18, - 6.1846270024220713e-34, -3.5840270918032937e-50), - qd_real( 1.9809841071795359e-01, -1.8434411800689445e-18, - 1.4139031318237285e-34, 1.0542811125343809e-50), - qd_real( 2.0110463484209190e-01, 1.1010032669300739e-17, - -3.9123576757413791e-34, 2.4084852500063531e-51), - qd_real( 2.0410896609281687e-01, 6.0941297773957752e-18, - -2.8275409970449641e-34, 4.6101008563532989e-51), - qd_real( 2.0711137619221856e-01, -1.0613362528971356e-17, - 2.2456805112690884e-34, 1.3483736125280904e-50), - qd_real( 2.1011183688046961e-01, 1.1561548476512844e-17, - 6.0355905610401254e-34, 3.3329909618405675e-50), - qd_real( 2.1311031991609136e-01, 1.2031873821063860e-17, - -3.4142699719695635e-34, -1.2436262780241778e-50), - qd_real( 2.1610679707621952e-01, -1.0111196082609117e-17, - 7.2789545335189643e-34, -2.9347540365258610e-50), - qd_real( 2.1910124015686980e-01, -3.6513812299150776e-19, - -2.3359499418606442e-35, 3.1785298198458653e-52), - qd_real( 2.2209362097320354e-01, -3.0337210995812162e-18, - 6.6654668033632998e-35, 2.0110862322656942e-51), - qd_real( 2.2508391135979283e-01, 3.9507040822556510e-18, - 2.4287993958305375e-35, 5.6662797513020322e-52), - qd_real( 2.2807208317088573e-01, 8.2361837339258012e-18, - 6.9786781316397937e-34, -6.4122962482639504e-51), - qd_real( 2.3105810828067111e-01, 1.0129787149761869e-17, - -6.9359234615816044e-34, -2.8877355604883782e-50), - qd_real( 2.3404195858354343e-01, -6.9922402696101173e-18, - -5.7323031922750280e-34, 5.3092579966872727e-51), - qd_real( 2.3702360599436720e-01, 8.8544852285039918e-18, - 1.3588480826354134e-34, 1.0381022520213867e-50), - qd_real( 2.4000302244874150e-01, -1.2137758975632164e-17, - -2.6448807731703891e-34, -1.9929733800670473e-51), - qd_real( 2.4298017990326390e-01, -8.7514315297196632e-18, - -6.5723260373079431e-34, -1.0333158083172177e-50), - qd_real( 2.4595505033579462e-01, -1.1129044052741832e-17, - 4.3805998202883397e-34, 1.2219399554686291e-50), - qd_real( 2.4892760574572018e-01, -8.1783436100020990e-18, - 5.5666875261111840e-34, 3.8080473058748167e-50), - qd_real( 2.5189781815421697e-01, -1.7591436032517039e-17, - -1.0959681232525285e-33, 5.6209426020232456e-50), - qd_real( 2.5486565960451457e-01, -1.3602299806901461e-19, - -6.0073844642762535e-36, -3.0072751311893878e-52), - qd_real( 2.5783110216215899e-01, 1.8480038630879957e-17, - 3.3201664714047599e-34, -5.5547819290576764e-51), - qd_real( 2.6079411791527551e-01, 4.2721420983550075e-18, - 5.6782126934777920e-35, 3.1428338084365397e-51), - qd_real( 2.6375467897483140e-01, -1.8837947680038700e-17, - 1.3720129045754794e-33, -8.2763406665966033e-50), - qd_real( 2.6671275747489837e-01, 2.0941222578826688e-17, - -1.1303466524727989e-33, 1.9954224050508963e-50), - qd_real( 2.6966832557291509e-01, 1.5765657618133259e-17, - -6.9696142173370086e-34, -4.0455346879146776e-50), - qd_real( 2.7262135544994898e-01, 7.8697166076387850e-18, - 6.6179388602933372e-35, -2.7642903696386267e-51), - qd_real( 2.7557181931095814e-01, 1.9320328962556582e-17, - 1.3932094180100280e-33, 1.3617253920018116e-50), - qd_real( 2.7851968938505312e-01, -1.0030273719543544e-17, - 7.2592115325689254e-34, -1.0068516296655851e-50), - qd_real( 2.8146493792575800e-01, -1.2322299641274009e-17, - -1.0564788706386435e-34, 7.5137424251265885e-51), - qd_real( 2.8440753721127182e-01, 2.2209268510661475e-17, - -9.1823095629523708e-34, -5.2192875308892218e-50), - qd_real( 2.8734745954472951e-01, 1.5461117367645717e-17, - -6.3263973663444076e-34, -2.2982538416476214e-50), - qd_real( 2.9028467725446239e-01, -1.8927978707774251e-17, - 1.1522953157142315e-33, 7.4738655654716596e-50), - qd_real( 2.9321916269425863e-01, 2.2385430811901833e-17, - 1.3662484646539680e-33, -4.2451325253996938e-50), - qd_real( 2.9615088824362384e-01, -2.0220736360876938e-17, - -7.9252212533920413e-35, -2.8990577729572470e-51), - qd_real( 2.9907982630804048e-01, 1.6701181609219447e-18, - 8.6091151117316292e-35, 3.9931286230012102e-52), - qd_real( 3.0200594931922808e-01, -1.7167666235262474e-17, - 2.3336182149008069e-34, 8.3025334555220004e-51), - qd_real( 3.0492922973540243e-01, -2.2989033898191262e-17, - -1.4598901099661133e-34, 3.7760487693121827e-51), - qd_real( 3.0784964004153487e-01, 2.7074088527245185e-17, - 1.2568858206899284e-33, 7.2931815105901645e-50), - qd_real( 3.1076715274961147e-01, 2.0887076364048513e-17, - -3.0130590791065942e-34, 1.3876739009935179e-51), - qd_real( 3.1368174039889146e-01, 1.4560447299968912e-17, - 3.6564186898011595e-34, 1.1654264734999375e-50), - qd_real( 3.1659337555616585e-01, 2.1435292512726283e-17, - 1.2338169231377316e-33, 3.3963542100989293e-50), - qd_real( 3.1950203081601569e-01, -1.3981562491096626e-17, - 8.1730000697411350e-34, -7.7671096270210952e-50), - qd_real( 3.2240767880106985e-01, -4.0519039937959398e-18, - 3.7438302780296796e-34, 8.7936731046639195e-51), - qd_real( 3.2531029216226293e-01, 7.9171249463765892e-18, - -6.7576622068146391e-35, 2.3021655066929538e-51), - qd_real( 3.2820984357909255e-01, -2.6693140719641896e-17, - 7.8928851447534788e-34, 2.5525163821987809e-51), - qd_real( 3.3110630575987643e-01, -2.7469465474778694e-17, - -1.3401245916610206e-33, 6.5531762489976163e-50), - qd_real( 3.3399965144200938e-01, 2.2598986806288142e-17, - 7.8063057192586115e-34, 2.0427600895486683e-50), - qd_real( 3.3688985339222005e-01, -4.2000940033475092e-19, - -2.9178652969985438e-36, -1.1597376437036749e-52), - qd_real( 3.3977688440682685e-01, 6.6028679499418282e-18, - 1.2575009988669683e-34, 2.5569067699008304e-51), - qd_real( 3.4266071731199438e-01, 1.9261518449306319e-17, - -9.2754189135990867e-34, 8.5439996687390166e-50), - qd_real( 3.4554132496398904e-01, 2.7251143672916123e-17, - 7.0138163601941737e-34, -1.4176292197454015e-50), - qd_real( 3.4841868024943456e-01, 3.6974420514204918e-18, - 3.5532146878499996e-34, 1.9565462544501322e-50), - qd_real( 3.5129275608556715e-01, -2.2670712098795844e-17, - -1.6994216673139631e-34, -1.2271556077284517e-50), - qd_real( 3.5416352542049040e-01, -1.6951763305764860e-17, - 1.2772331777814617e-33, -3.3703785435843310e-50), - qd_real( 3.5703096123343003e-01, -4.8218191137919166e-19, - -4.1672436994492361e-35, -7.1531167149364352e-52), - qd_real( 3.5989503653498817e-01, -1.7601687123839282e-17, - 1.3375125473046791e-33, 7.9467815593584340e-50), - qd_real( 3.6275572436739723e-01, -9.1668352663749849e-18, - -7.4317843956936735e-34, -2.0199582511804564e-50), - qd_real( 3.6561299780477385e-01, 1.6217898770457546e-17, - 1.1286970151961055e-33, -7.1825287318139010e-50), - qd_real( 3.6846682995337232e-01, 1.0463640796159268e-17, - 2.0554984738517304e-35, 1.0441861305618769e-51), - qd_real( 3.7131719395183754e-01, 3.4749239648238266e-19, - -7.5151053042866671e-37, -2.8153468438650851e-53), - qd_real( 3.7416406297145799e-01, 8.0114103761962118e-18, - 5.3429599813406052e-34, 1.0351378796539210e-50), - qd_real( 3.7700741021641826e-01, -2.7255302041956930e-18, - 6.3646586445018137e-35, 8.3048657176503559e-52), - qd_real( 3.7984720892405116e-01, 9.9151305855172370e-18, - 4.8761409697224886e-34, 1.4025084000776705e-50), - qd_real( 3.8268343236508978e-01, -1.0050772696461588e-17, - -2.0605316302806695e-34, -1.2717724698085205e-50), - qd_real( 3.8551605384391885e-01, 1.5177665396472313e-17, - 1.4198230518016535e-33, 5.8955167159904235e-50), - qd_real( 3.8834504669882630e-01, -1.0053770598398717e-17, - 7.5942999255057131e-34, -3.1967974046654219e-50), - qd_real( 3.9117038430225387e-01, 1.7997787858243995e-17, - -1.0613482402609856e-33, -5.4582148817791032e-50), - qd_real( 3.9399204006104810e-01, 9.7649241641239336e-18, - -2.1233599441284617e-34, -5.5529836795340819e-51), - qd_real( 3.9680998741671031e-01, 2.0545063670840126e-17, - 6.1347058801922842e-34, 1.0733788150636430e-50), - qd_real( 3.9962419984564684e-01, -1.5065497476189372e-17, - -9.9653258881867298e-34, -5.7524323712725355e-50), - qd_real( 4.0243465085941843e-01, 1.0902619339328270e-17, - 7.3998528125989765e-34, 2.2745784806823499e-50), - qd_real( 4.0524131400498986e-01, 9.9111401942899884e-18, - -2.5169070895434648e-34, 9.2772984818436573e-53), - qd_real( 4.0804416286497869e-01, -7.0006015137351311e-18, - -1.4108207334268228e-34, 1.5175546997577136e-52), - qd_real( 4.1084317105790397e-01, -2.4219835190355499e-17, - -1.1418902925313314e-33, -2.0996843165093468e-50), - qd_real( 4.1363831223843456e-01, -1.0393984940597871e-17, - -1.1481681174503880e-34, -2.0281052851028680e-51), - qd_real( 4.1642956009763721e-01, -2.5475580413131732e-17, - -3.4482678506112824e-34, 7.1788619351865480e-51), - qd_real( 4.1921688836322396e-01, -4.2232463750110590e-18, - -3.6053023045255790e-34, -2.2209673210025631e-50), - qd_real( 4.2200027079979968e-01, 4.3543266994128527e-18, - 3.1734310272251190e-34, -1.3573247980738668e-50), - qd_real( 4.2477968120910881e-01, 2.7462312204277281e-17, - -4.6552847802111948e-34, 6.5961781099193122e-51), - qd_real( 4.2755509343028208e-01, 9.4111898162954726e-18, - -1.7446682426598801e-34, -2.2054492626480169e-51), - qd_real( 4.3032648134008261e-01, 2.2259686974092690e-17, - 8.5972591314085075e-34, -2.9420897889003020e-50), - qd_real( 4.3309381885315196e-01, 1.1224283329847517e-17, - 5.3223748041075651e-35, 5.3926192627014212e-51), - qd_real( 4.3585707992225547e-01, 1.6230515450644527e-17, - -6.4371449063579431e-35, -6.9102436481386757e-51), - qd_real( 4.3861623853852766e-01, -2.0883315831075090e-17, - -1.4259583540891877e-34, 6.3864763590657077e-52), - qd_real( 4.4137126873171667e-01, 2.2360783886964969e-17, - 1.1864769603515770e-34, -3.8087003266189232e-51), - qd_real( 4.4412214457042926e-01, -2.4218874422178315e-17, - 2.2205230838703907e-34, 9.2133035911356258e-51), - qd_real( 4.4686884016237421e-01, -1.9222136142309382e-17, - -4.4425678589732049e-35, -1.3673609292149535e-51), - qd_real( 4.4961132965460660e-01, 4.8831924232035243e-18, - 2.7151084498191381e-34, -1.5653993171613154e-50), - qd_real( 4.5234958723377089e-01, -1.4827977472196122e-17, - -7.6947501088972324e-34, 1.7656856882031319e-50), - qd_real( 4.5508358712634384e-01, -1.2379906758116472e-17, - 5.5289688955542643e-34, -8.5382312840209386e-51), - qd_real( 4.5781330359887723e-01, -8.4554254922295949e-18, - -6.3770394246764263e-34, 3.1778253575564249e-50), - qd_real( 4.6053871095824001e-01, 1.8488777492177872e-17, - -1.0527732154209725e-33, 3.3235593490947102e-50), - qd_real( 4.6325978355186020e-01, -7.3514924533231707e-18, - 6.7175396881707035e-34, 3.9594127612123379e-50), - qd_real( 4.6597649576796618e-01, -3.3023547778235135e-18, - 3.4904677050476886e-35, 3.4483855263874246e-51), - qd_real( 4.6868882203582796e-01, -2.2949251681845054e-17, - -1.1364757641823658e-33, 6.8840522501918612e-50), - qd_real( 4.7139673682599764e-01, 6.5166781360690130e-18, - 2.9457546966235984e-34, -6.2159717738836630e-51), - qd_real( 4.7410021465055002e-01, -8.1451601548978075e-18, - -3.4789448555614422e-34, -1.1681943974658508e-50), - qd_real( 4.7679923006332214e-01, -1.0293515338305794e-17, - -3.6582045008369952e-34, 1.7424131479176475e-50), - qd_real( 4.7949375766015301e-01, 1.8419999662684771e-17, - -1.3040838621273312e-33, 1.0977131822246471e-50), - qd_real( 4.8218377207912277e-01, -2.5861500925520442e-17, - -6.2913197606500007e-36, 4.0802359808684726e-52), - qd_real( 4.8486924800079112e-01, -1.8034004203262245e-17, - -3.5244276906958044e-34, -1.7138318654749246e-50), - qd_real( 4.8755016014843594e-01, 1.4231090931273653e-17, - -1.8277733073262697e-34, -1.5208291790429557e-51), - qd_real( 4.9022648328829116e-01, -5.1496145643440404e-18, - -3.6903027405284104e-34, 1.5172940095151304e-50), - qd_real( 4.9289819222978404e-01, -1.0257831676562186e-18, - 6.9520817760885069e-35, -2.4260961214090389e-51), - qd_real( 4.9556526182577254e-01, -9.4323241942365362e-18, - 3.1212918657699143e-35, 4.2009072375242736e-52), - qd_real( 4.9822766697278187e-01, -1.6126383830540798e-17, - -1.5092897319298871e-33, 1.1049298890895917e-50), - qd_real( 5.0088538261124083e-01, -3.9604015147074639e-17, - -2.2208395201898007e-33, 1.3648202735839417e-49), - qd_real( 5.0353838372571758e-01, -1.6731308204967497e-17, - -1.0140233644074786e-33, 4.0953071937671477e-50), - qd_real( 5.0618664534515534e-01, -4.8321592986493711e-17, - 9.2858107226642252e-34, 4.2699802401037005e-50), - qd_real( 5.0883014254310699e-01, 4.7836968268014130e-17, - -1.0727022928806035e-33, 2.7309374513672757e-50), - qd_real( 5.1146885043797041e-01, -1.3088001221007579e-17, - 4.0929033363366899e-34, -3.7952190153477926e-50), - qd_real( 5.1410274419322177e-01, -4.5712707523615624e-17, - 1.5488279442238283e-33, -2.5853959305521130e-50), - qd_real( 5.1673179901764987e-01, 8.3018617233836515e-18, - 5.8251027467695202e-34, -2.2812397190535076e-50), - qd_real( 5.1935599016558964e-01, -5.5331248144171145e-17, - -3.1628375609769026e-35, -2.4091972051188571e-51), - qd_real( 5.2197529293715439e-01, -4.6555795692088883e-17, - 4.6378980936850430e-34, -3.3470542934689532e-51), - qd_real( 5.2458968267846895e-01, -4.3068869040082345e-17, - -4.2013155291932055e-34, -1.5096069926700274e-50), - qd_real( 5.2719913478190139e-01, -4.2202983480560619e-17, - 8.5585916184867295e-34, 7.9974339336732307e-50), - qd_real( 5.2980362468629472e-01, -4.8067841706482342e-17, - 5.8309721046630296e-34, -8.9740761521756660e-51), - qd_real( 5.3240312787719801e-01, -4.1020306135800895e-17, - -1.9239996374230821e-33, -1.5326987913812184e-49), - qd_real( 5.3499761988709726e-01, -5.3683132708358134e-17, - -1.3900569918838112e-33, 2.7154084726474092e-50), - qd_real( 5.3758707629564551e-01, -2.2617365388403054e-17, - -5.9787279033447075e-34, 3.1204419729043625e-51), - qd_real( 5.4017147272989285e-01, 2.7072447965935839e-17, - 1.1698799709213829e-33, -5.9094668515881500e-50), - qd_real( 5.4275078486451589e-01, 1.7148261004757101e-17, - -1.3525905925200870e-33, 4.9724411290727323e-50), - qd_real( 5.4532498842204646e-01, -4.1517817538384258e-17, - -1.5318930219385941e-33, 6.3629921101413974e-50), - qd_real( 5.4789405917310019e-01, -2.4065878297113363e-17, - -3.5639213669362606e-36, -2.6013270854271645e-52), - qd_real( 5.5045797293660481e-01, -8.3319903015807663e-18, - -2.3058454035767633e-34, -2.1611290432369010e-50), - qd_real( 5.5301670558002758e-01, -4.7061536623798204e-17, - -1.0617111545918056e-33, -1.6196316144407379e-50), - qd_real( 5.5557023301960218e-01, 4.7094109405616768e-17, - -2.0640520383682921e-33, 1.2290163188567138e-49), - qd_real( 5.5811853122055610e-01, 1.3481176324765226e-17, - -5.5016743873011438e-34, -2.3484822739335416e-50), - qd_real( 5.6066157619733603e-01, -7.3956418153476152e-18, - 3.9680620611731193e-34, 3.1995952200836223e-50), - qd_real( 5.6319934401383409e-01, 2.3835775146854829e-17, - 1.3511793173769814e-34, 9.3201311581248143e-51), - qd_real( 5.6573181078361323e-01, -3.4096079596590466e-17, - -1.7073289744303546e-33, 8.9147089975404507e-50), - qd_real( 5.6825895267013160e-01, -5.0935673642769248e-17, - -1.6274356351028249e-33, 9.8183151561702966e-51), - qd_real( 5.7078074588696726e-01, 2.4568151455566208e-17, - -1.2844481247560350e-33, -1.8037634376936261e-50), - qd_real( 5.7329716669804220e-01, 8.5176611669306400e-18, - -6.4443208788026766e-34, 2.2546105543273003e-50), - qd_real( 5.7580819141784534e-01, -3.7909495458942734e-17, - -2.7433738046854309e-33, 1.1130841524216795e-49), - qd_real( 5.7831379641165559e-01, -2.6237691512372831e-17, - 1.3679051680738167e-33, -3.1409808935335900e-50), - qd_real( 5.8081395809576453e-01, 1.8585338586613408e-17, - 2.7673843114549181e-34, 1.9605349619836937e-50), - qd_real( 5.8330865293769829e-01, 3.4516601079044858e-18, - 1.8065977478946306e-34, -6.3953958038544646e-51), - qd_real( 5.8579785745643886e-01, -3.7485501964311294e-18, - 2.7965403775536614e-34, -7.1816936024157202e-51), - qd_real( 5.8828154822264533e-01, -2.9292166725006846e-17, - -2.3744954603693934e-33, -1.1571631191512480e-50), - qd_real( 5.9075970185887428e-01, -4.7013584170659542e-17, - 2.4808417611768356e-33, 1.2598907673643198e-50), - qd_real( 5.9323229503979980e-01, 1.2892320944189053e-17, - 5.3058364776359583e-34, 4.1141674699390052e-50), - qd_real( 5.9569930449243336e-01, -1.3438641936579467e-17, - -6.7877687907721049e-35, -5.6046937531684890e-51), - qd_real( 5.9816070699634227e-01, 3.8801885783000657e-17, - -1.2084165858094663e-33, -4.0456610843430061e-50), - qd_real( 6.0061647938386897e-01, -4.6398198229461932e-17, - -1.6673493003710801e-33, 5.1982824378491445e-50), - qd_real( 6.0306659854034816e-01, 3.7323357680559650e-17, - 2.7771920866974305e-33, -1.6194229649742458e-49), - qd_real( 6.0551104140432555e-01, -3.1202672493305677e-17, - 1.2761267338680916e-33, -4.0859368598379647e-50), - qd_real( 6.0794978496777363e-01, 3.5160832362096660e-17, - -2.5546242776778394e-34, -1.4085313551220694e-50), - qd_real( 6.1038280627630948e-01, -2.2563265648229169e-17, - 1.3185575011226730e-33, 8.2316691420063460e-50), - qd_real( 6.1281008242940971e-01, -4.2693476568409685e-18, - 2.5839965886650320e-34, 1.6884412005622537e-50), - qd_real( 6.1523159058062682e-01, 2.6231417767266950e-17, - -1.4095366621106716e-33, 7.2058690491304558e-50), - qd_real( 6.1764730793780398e-01, -4.7478594510902452e-17, - -7.2986558263123996e-34, -3.0152327517439154e-50), - qd_real( 6.2005721176328921e-01, -2.7983410837681118e-17, - 1.1649951056138923e-33, -5.4539089117135207e-50), - qd_real( 6.2246127937414997e-01, 5.2940728606573002e-18, - -4.8486411215945827e-35, 1.2696527641980109e-52), - qd_real( 6.2485948814238634e-01, 3.3671846037243900e-17, - -2.7846053391012096e-33, 5.6102718120012104e-50), - qd_real( 6.2725181549514408e-01, 3.0763585181253225e-17, - 2.7068930273498138e-34, -1.1172240309286484e-50), - qd_real( 6.2963823891492698e-01, 4.1115334049626806e-17, - -1.9167473580230747e-33, 1.1118424028161730e-49), - qd_real( 6.3201873593980906e-01, -4.0164942296463612e-17, - -7.2208643641736723e-34, 3.7828920470544344e-50), - qd_real( 6.3439328416364549e-01, 1.0420901929280035e-17, - 4.1174558929280492e-34, -1.4464152986630705e-51), - qd_real( 6.3676186123628420e-01, 3.1419048711901611e-17, - -2.2693738415126449e-33, -1.6023584204297388e-49), - qd_real( 6.3912444486377573e-01, 1.2416796312271043e-17, - -6.2095419626356605e-34, 2.7762065999506603e-50), - qd_real( 6.4148101280858316e-01, -9.9883430115943310e-18, - 4.1969230376730128e-34, 5.6980543799257597e-51), - qd_real( 6.4383154288979150e-01, -3.2084798795046886e-17, - -1.2595311907053305e-33, -4.0205885230841536e-50), - qd_real( 6.4617601298331639e-01, -2.9756137382280815e-17, - -1.0275370077518259e-33, 8.0852478665893014e-51), - qd_real( 6.4851440102211244e-01, 3.9870270313386831e-18, - 1.9408388509540788e-34, -5.1798420636193190e-51), - qd_real( 6.5084668499638088e-01, 3.9714670710500257e-17, - 2.9178546787002963e-34, 3.8140635508293278e-51), - qd_real( 6.5317284295377676e-01, 8.5695642060026238e-18, - -6.9165322305070633e-34, 2.3873751224185395e-50), - qd_real( 6.5549285299961535e-01, 3.5638734426385005e-17, - 1.2695365790889811e-33, 4.3984952865412050e-50), - qd_real( 6.5780669329707864e-01, 1.9580943058468545e-17, - -1.1944272256627192e-33, 2.8556402616436858e-50), - qd_real( 6.6011434206742048e-01, -1.3960054386823638e-19, - 6.1515777931494047e-36, 5.3510498875622660e-52), - qd_real( 6.6241577759017178e-01, -2.2615508885764591e-17, - 5.0177050318126862e-34, 2.9162532399530762e-50), - qd_real( 6.6471097820334490e-01, -3.6227793598034367e-17, - -9.0607934765540427e-34, 3.0917036342380213e-50), - qd_real( 6.6699992230363747e-01, 3.5284364997428166e-17, - -1.0382057232458238e-33, 7.3812756550167626e-50), - qd_real( 6.6928258834663612e-01, -5.4592652417447913e-17, - -2.5181014709695152e-33, -1.6867875999437174e-49), - qd_real( 6.7155895484701844e-01, -4.0489037749296692e-17, - 3.1995835625355681e-34, -1.4044414655670960e-50), - qd_real( 6.7382900037875604e-01, 2.3091901236161086e-17, - 5.7428037192881319e-34, 1.1240668354625977e-50), - qd_real( 6.7609270357531592e-01, 3.7256902248049466e-17, - 1.7059417895764375e-33, 9.7326347795300652e-50), - qd_real( 6.7835004312986147e-01, 1.8302093041863122e-17, - 9.5241675746813072e-34, 5.0328101116133503e-50), - qd_real( 6.8060099779545302e-01, 2.8473293354522047e-17, - 4.1331805977270903e-34, 4.2579030510748576e-50), - qd_real( 6.8284554638524808e-01, -1.2958058061524531e-17, - 1.8292386959330698e-34, 3.4536209116044487e-51), - qd_real( 6.8508366777270036e-01, 2.5948135194645137e-17, - -8.5030743129500702e-34, -6.9572086141009930e-50), - qd_real( 6.8731534089175916e-01, -5.5156158714917168e-17, - 1.1896489854266829e-33, -7.8505896218220662e-51), - qd_real( 6.8954054473706694e-01, -1.5889323294806790e-17, - 9.1242356240205712e-34, 3.8315454152267638e-50), - qd_real( 6.9175925836415775e-01, 2.7406078472410668e-17, - 1.3286508943202092e-33, 1.0651869129580079e-51), - qd_real( 6.9397146088965400e-01, 7.4345076956280137e-18, - 7.5061528388197460e-34, -1.5928000240686583e-50), - qd_real( 6.9617713149146299e-01, -4.1224081213582889e-17, - -3.1838716762083291e-35, -3.9625587412119131e-51), - qd_real( 6.9837624940897280e-01, 4.8988282435667768e-17, - 1.9134010413244152e-33, 2.6161153243793989e-50), - qd_real( 7.0056879394324834e-01, 3.1027960192992922e-17, - 9.5638250509179997e-34, 4.5896916138107048e-51), - qd_real( 7.0275474445722530e-01, 2.5278294383629822e-18, - -8.6985561210674942e-35, -5.6899862307812990e-51), - qd_real( 7.0493408037590488e-01, 2.7608725585748502e-17, - 2.9816599471629137e-34, 1.1533044185111206e-50), - qd_real( 7.0710678118654757e-01, -4.8336466567264567e-17, - 2.0693376543497068e-33, 2.4677734957341755e-50) -}; - -static const qd_real cos_table [] = { - qd_real( 9.9999529380957619e-01, -1.9668064285322189e-17, - -6.3053955095883481e-34, 5.3266110855726731e-52), - qd_real( 9.9998117528260111e-01, 3.3568103522895585e-17, - -1.4740132559368063e-35, 9.8603097594755596e-52), - qd_real( 9.9995764455196390e-01, -3.1527836866647287e-17, - 2.6363251186638437e-33, 1.0007504815488399e-49), - qd_real( 9.9992470183914450e-01, 3.7931082512668012e-17, - -8.5099918660501484e-35, -4.9956973223295153e-51), - qd_real( 9.9988234745421256e-01, -3.5477814872408538e-17, - 1.7102001035303974e-33, -1.0725388519026542e-49), - qd_real( 9.9983058179582340e-01, 1.8825140517551119e-17, - -5.1383513457616937e-34, -3.8378827995403787e-50), - qd_real( 9.9976940535121528e-01, 4.2681177032289012e-17, - 1.9062302359737099e-33, -6.0221153262881160e-50), - qd_real( 9.9969881869620425e-01, -2.9851486403799753e-17, - -1.9084787370733737e-33, 5.5980260344029202e-51), - qd_real( 9.9961882249517864e-01, -4.1181965521424734e-17, - 2.0915365593699916e-33, 8.1403390920903734e-50), - qd_real( 9.9952941750109314e-01, 2.0517917823755591e-17, - -4.7673802585706520e-34, -2.9443604198656772e-50), - qd_real( 9.9943060455546173e-01, 3.9644497752257798e-17, - -2.3757223716722428e-34, -1.2856759011361726e-51), - qd_real( 9.9932238458834954e-01, -4.2858538440845682e-17, - 3.3235101605146565e-34, -8.3554272377057543e-51), - qd_real( 9.9920475861836389e-01, 9.1796317110385693e-18, - 5.5416208185868570e-34, 8.0267046717615311e-52), - qd_real( 9.9907772775264536e-01, 2.1419007653587032e-17, - -7.9048203318529618e-34, -5.3166296181112712e-50), - qd_real( 9.9894129318685687e-01, -2.0610641910058638e-17, - -1.2546525485913485e-33, -7.5175888806157064e-50), - qd_real( 9.9879545620517241e-01, -1.2291693337075465e-17, - 2.4468446786491271e-34, 1.0723891085210268e-50), - qd_real( 9.9864021818026527e-01, -4.8690254312923302e-17, - -2.9470881967909147e-34, -1.3000650761346907e-50), - qd_real( 9.9847558057329477e-01, -2.2002931182778795e-17, - -1.2371509454944992e-33, -2.4911225131232065e-50), - qd_real( 9.9830154493389289e-01, -5.1869402702792278e-17, - 1.0480195493633452e-33, -2.8995649143155511e-50), - qd_real( 9.9811811290014918e-01, 2.7935487558113833e-17, - 2.4423341255830345e-33, -6.7646699175334417e-50), - qd_real( 9.9792528619859600e-01, 1.7143659778886362e-17, - 5.7885840902887460e-34, -9.2601432603894597e-51), - qd_real( 9.9772306664419164e-01, -2.6394475274898721e-17, - -1.6176223087661783e-34, -9.9924942889362281e-51), - qd_real( 9.9751145614030345e-01, 5.6007205919806937e-18, - -5.9477673514685690e-35, -1.4166807162743627e-54), - qd_real( 9.9729045667869021e-01, 9.1647695371101735e-18, - 6.7824134309739296e-34, -8.6191392795543357e-52), - qd_real( 9.9706007033948296e-01, 1.6734093546241963e-17, - -1.3169951440780028e-33, 1.0311048767952477e-50), - qd_real( 9.9682029929116567e-01, 4.7062820708615655e-17, - 2.8412041076474937e-33, -8.0006155670263622e-50), - qd_real( 9.9657114579055484e-01, 1.1707179088390986e-17, - -7.5934413263024663e-34, 2.8474848436926008e-50), - qd_real( 9.9631261218277800e-01, 1.1336497891624735e-17, - 3.4002458674414360e-34, 7.7419075921544901e-52), - qd_real( 9.9604470090125197e-01, 2.2870031707670695e-17, - -3.9184839405013148e-34, -3.7081260416246375e-50), - qd_real( 9.9576741446765982e-01, -2.3151908323094359e-17, - -1.6306512931944591e-34, -1.5925420783863192e-51), - qd_real( 9.9548075549192694e-01, 3.2084621412226554e-18, - -4.9501292146013023e-36, -2.7811428850878516e-52), - qd_real( 9.9518472667219693e-01, -4.2486913678304410e-17, - 1.3315510772504614e-33, 6.7927987417051888e-50), - qd_real( 9.9487933079480562e-01, 4.2130813284943662e-18, - -4.2062597488288452e-35, 2.5157064556087620e-51), - qd_real( 9.9456457073425542e-01, 3.6745069641528058e-17, - -3.0603304105471010e-33, 1.0397872280487526e-49), - qd_real( 9.9424044945318790e-01, 4.4129423472462673e-17, - -3.0107231708238066e-33, 7.4201582906861892e-50), - qd_real( 9.9390697000235606e-01, -1.8964849471123746e-17, - -1.5980853777937752e-35, -8.5374807150597082e-52), - qd_real( 9.9356413552059530e-01, 2.9752309927797428e-17, - -4.5066707331134233e-34, -3.3548191633805036e-50), - qd_real( 9.9321194923479450e-01, 3.3096906261272262e-17, - 1.5592811973249567e-33, 1.4373977733253592e-50), - qd_real( 9.9285041445986510e-01, -1.4094517733693302e-17, - -1.1954558131616916e-33, 1.8761873742867983e-50), - qd_real( 9.9247953459870997e-01, 3.1093055095428906e-17, - -1.8379594757818019e-33, -3.9885758559381314e-51), - qd_real( 9.9209931314219180e-01, -3.9431926149588778e-17, - -6.2758062911047230e-34, -1.2960929559212390e-50), - qd_real( 9.9170975366909953e-01, -2.3372891311883661e-18, - 2.7073298824968591e-35, -1.2569459441802872e-51), - qd_real( 9.9131085984611544e-01, -2.5192111583372105e-17, - -1.2852471567380887e-33, 5.2385212584310483e-50), - qd_real( 9.9090263542778001e-01, 1.5394565094566704e-17, - -1.0799984133184567e-33, 2.7451115960133595e-51), - qd_real( 9.9048508425645709e-01, -5.5411437553780867e-17, - -1.4614017210753585e-33, -3.8339374397387620e-50), - qd_real( 9.9005821026229712e-01, -1.7055485906233963e-17, - 1.3454939685758777e-33, 7.3117589137300036e-50), - qd_real( 9.8962201746320089e-01, -5.2398217968132530e-17, - 1.3463189211456219e-33, 5.8021640554894872e-50), - qd_real( 9.8917650996478101e-01, -4.0987309937047111e-17, - -4.4857560552048437e-34, -3.9414504502871125e-50), - qd_real( 9.8872169196032378e-01, -1.0976227206656125e-17, - 3.2311342577653764e-34, 9.6367946583575041e-51), - qd_real( 9.8825756773074946e-01, 2.7030607784372632e-17, - 7.7514866488601377e-35, 2.1019644956864938e-51), - qd_real( 9.8778414164457218e-01, -2.3600693397159021e-17, - -1.2323283769707861e-33, 3.0130900716803339e-50), - qd_real( 9.8730141815785843e-01, -5.2332261255715652e-17, - -2.7937644333152473e-33, 1.2074160567958408e-49), - qd_real( 9.8680940181418553e-01, -5.0287214351061075e-17, - -2.2681526238144461e-33, 4.4003694320169133e-50), - qd_real( 9.8630809724459867e-01, -2.1520877103013341e-17, - 1.1866528054187716e-33, -7.8532199199813836e-50), - qd_real( 9.8579750916756748e-01, -5.1439452979953012e-17, - 2.6276439309996725e-33, 7.5423552783286347e-50), - qd_real( 9.8527764238894122e-01, 2.3155637027900207e-17, - -7.5275971545764833e-34, 1.0582231660456094e-50), - qd_real( 9.8474850180190421e-01, 1.0548144061829957e-17, - 2.8786145266267306e-34, -3.6782210081466112e-51), - qd_real( 9.8421009238692903e-01, 4.7983922627050691e-17, - 2.2597419645070588e-34, 1.7573875814863400e-50), - qd_real( 9.8366241921173025e-01, 1.9864948201635255e-17, - -1.0743046281211033e-35, 1.7975662796558100e-52), - qd_real( 9.8310548743121629e-01, 4.2170007522888628e-17, - 8.2396265656440904e-34, -8.0803700139096561e-50), - qd_real( 9.8253930228744124e-01, 1.5149580813777224e-17, - -4.1802771422186237e-34, -2.2150174326226160e-50), - qd_real( 9.8196386910955524e-01, 2.1108443711513084e-17, - -1.5253013442896054e-33, -6.8388082079337969e-50), - qd_real( 9.8137919331375456e-01, 1.3428163260355633e-17, - -6.5294290469962986e-34, 2.7965412287456268e-51), - qd_real( 9.8078528040323043e-01, 1.8546939997825006e-17, - -1.0696564445530757e-33, 6.6668174475264961e-50), - qd_real( 9.8018213596811743e-01, -3.6801786963856159e-17, - 6.3245171387992842e-34, 1.8600176137175971e-50), - qd_real( 9.7956976568544052e-01, 1.5573991584990420e-17, - -1.3401066029782990e-33, -1.7263702199862149e-50), - qd_real( 9.7894817531906220e-01, -2.3817727961148053e-18, - -1.0694750370381661e-34, -8.2293047196087462e-51), - qd_real( 9.7831737071962765e-01, -2.1623082233344895e-17, - 1.0970403012028032e-33, 7.7091923099369339e-50), - qd_real( 9.7767735782450993e-01, 5.0514136167059628e-17, - -1.3254751701428788e-33, 7.0161254312124538e-50), - qd_real( 9.7702814265775439e-01, -4.3353875751555997e-17, - 5.4948839831535478e-34, -9.2755263105377306e-51), - qd_real( 9.7636973133002114e-01, 9.3093931526213780e-18, - -4.1184949155685665e-34, -3.1913926031393690e-50), - qd_real( 9.7570213003852857e-01, -2.5572556081259686e-17, - -9.3174244508942223e-34, -8.3675863211646863e-51), - qd_real( 9.7502534506699412e-01, 2.6642660651899135e-17, - 1.7819392739353853e-34, -3.3159625385648947e-51), - qd_real( 9.7433938278557586e-01, 2.3041221476151512e-18, - 1.0758686005031430e-34, 5.1074116432809478e-51), - qd_real( 9.7364424965081198e-01, -5.1729808691005871e-17, - -1.5508473005989887e-33, -1.6505125917675401e-49), - qd_real( 9.7293995220556018e-01, -3.1311211122281800e-17, - -2.6874087789006141e-33, -2.1652434818822145e-51), - qd_real( 9.7222649707893627e-01, 3.6461169785938221e-17, - 3.0309636883883133e-33, -1.2702716907967306e-51), - qd_real( 9.7150389098625178e-01, -7.9865421122289046e-18, - -4.3628417211263380e-34, 3.4307517798759352e-51), - qd_real( 9.7077214072895035e-01, -4.7992163325114922e-17, - 3.0347528910975783e-33, 8.5989199506479701e-50), - qd_real( 9.7003125319454397e-01, 1.8365300348428844e-17, - -1.4311097571944918e-33, 8.5846781998740697e-51), - qd_real( 9.6928123535654853e-01, -4.5663660261927896e-17, - 9.6147526917239387e-34, 8.1267605207871330e-51), - qd_real( 9.6852209427441727e-01, 4.9475074918244771e-17, - 2.8558738351911241e-33, 6.2948422316507461e-50), - qd_real( 9.6775383709347551e-01, -4.5512132825515820e-17, - -1.4127617988719093e-33, -8.4620609089704578e-50), - qd_real( 9.6697647104485207e-01, 3.8496228837337864e-17, - -5.3881631542745647e-34, -3.5221863171458959e-50), - qd_real( 9.6619000344541250e-01, 5.1298840401665493e-17, - 1.4564075904769808e-34, 1.0095973971377432e-50), - qd_real( 9.6539444169768940e-01, -2.3745389918392156e-17, - 5.9221515590053862e-34, -3.8811192556231094e-50), - qd_real( 9.6458979328981276e-01, -3.4189470735959786e-17, - 2.2982074155463522e-33, -4.5128791045607634e-50), - qd_real( 9.6377606579543984e-01, 2.6463950561220029e-17, - -2.9073234590199323e-36, -1.2938328629395601e-52), - qd_real( 9.6295326687368388e-01, 8.9341960404313634e-18, - -3.9071244661020126e-34, 1.6212091116847394e-50), - qd_real( 9.6212140426904158e-01, 1.5236770453846305e-17, - -1.3050173525597142e-33, 7.9016122394092666e-50), - qd_real( 9.6128048581132064e-01, 2.0933955216674039e-18, - 1.0768607469015692e-34, -5.9453639304361774e-51), - qd_real( 9.6043051941556579e-01, 2.4653904815317185e-17, - -1.3792169410906322e-33, -4.7726598378506903e-51), - qd_real( 9.5957151308198452e-01, 1.1000640085000957e-17, - -4.2036030828223975e-34, 4.0023704842606573e-51), - qd_real( 9.5870347489587160e-01, -4.3685014392372053e-17, - 2.2001800662729131e-33, -1.0553721324358075e-49), - qd_real( 9.5782641302753291e-01, -1.7696710075371263e-17, - 1.9164034110382190e-34, 8.1489235071754813e-51), - qd_real( 9.5694033573220882e-01, 4.0553869861875701e-17, - -1.7147013364302149e-33, 2.5736745295329455e-50), - qd_real( 9.5604525134999641e-01, 3.7705045279589067e-17, - 1.9678699997347571e-33, 8.5093177731230180e-50), - qd_real( 9.5514116830577067e-01, 5.0088652955014668e-17, - -2.6983181838059211e-33, 1.0102323575596493e-49), - qd_real( 9.5422809510910567e-01, -3.7545901690626874e-17, - 1.4951619241257764e-33, -8.2717333151394973e-50), - qd_real( 9.5330604035419386e-01, -2.5190738779919934e-17, - -1.4272239821134379e-33, -4.6717286809283155e-50), - qd_real( 9.5237501271976588e-01, -2.0269300462299272e-17, - -1.0635956887246246e-33, -3.5514537666487619e-50), - qd_real( 9.5143502096900834e-01, 3.1350584123266695e-17, - -2.4824833452737813e-33, 9.5450335525380613e-51), - qd_real( 9.5048607394948170e-01, 1.9410097562630436e-17, - -8.1559393949816789e-34, -1.0501209720164562e-50), - qd_real( 9.4952818059303667e-01, -7.5544151928043298e-18, - -5.1260245024046686e-34, 1.8093643389040406e-50), - qd_real( 9.4856134991573027e-01, 2.0668262262333232e-17, - -5.9440730243667306e-34, 1.4268853111554300e-50), - qd_real( 9.4758559101774109e-01, 4.3417993852125991e-17, - -2.7728667889840373e-34, 5.5709160196519968e-51), - qd_real( 9.4660091308328353e-01, 3.5056800210680730e-17, - 9.8578536940318117e-34, 6.6035911064585197e-50), - qd_real( 9.4560732538052128e-01, 4.6019102478523738e-17, - -6.2534384769452059e-34, 1.5758941215779961e-50), - qd_real( 9.4460483726148026e-01, 8.8100545476641165e-18, - 5.2291695602757842e-34, -3.3487256018407123e-50), - qd_real( 9.4359345816196039e-01, -2.4093127844404214e-17, - 1.0283279856803939e-34, -2.3398232614531355e-51), - qd_real( 9.4257319760144687e-01, 1.3235564806436886e-17, - -5.7048262885386911e-35, 3.9947050442753744e-51), - qd_real( 9.4154406518302081e-01, -2.7896379547698341e-17, - 1.6273236356733898e-33, -5.3075944708471203e-51), - qd_real( 9.4050607059326830e-01, 2.8610421567116268e-17, - 2.9261501147538827e-33, -2.6849867690896925e-50), - qd_real( 9.3945922360218992e-01, -7.0152867943098655e-18, - -5.6395693818011210e-34, 3.5568142678987651e-50), - qd_real( 9.3840353406310806e-01, 5.4242545044795490e-17, - -1.9039966607859759e-33, -1.5627792988341215e-49), - qd_real( 9.3733901191257496e-01, -3.6570926284362776e-17, - -1.1902940071273247e-33, -1.1215082331583223e-50), - qd_real( 9.3626566717027826e-01, -1.3013766145497654e-17, - 5.2229870061990595e-34, -3.3972777075634108e-51), - qd_real( 9.3518350993894761e-01, -3.2609395302485065e-17, - -8.1813015218875245e-34, 5.5642140024928139e-50), - qd_real( 9.3409255040425887e-01, 4.4662824360767511e-17, - -2.5903243047396916e-33, 8.1505209004343043e-50), - qd_real( 9.3299279883473885e-01, 4.2041415555384355e-17, - 9.0285896495521276e-34, 5.3019984977661259e-50), - qd_real( 9.3188426558166815e-01, -4.0785944377318095e-17, - 1.7631450298754169e-33, 2.5776403305507453e-50), - qd_real( 9.3076696107898371e-01, 1.9703775102838329e-17, - 6.5657908718278205e-34, -1.9480347966259524e-51), - qd_real( 9.2964089584318121e-01, 5.1282530016864107e-17, - 2.3719739891916261e-34, -1.7230065426917127e-50), - qd_real( 9.2850608047321559e-01, -2.3306639848485943e-17, - -7.7799084333208503e-34, -5.8597558009300305e-50), - qd_real( 9.2736252565040111e-01, -2.7677111692155437e-17, - 2.2110293450199576e-34, 2.0349190819680613e-50), - qd_real( 9.2621024213831138e-01, -3.7303754586099054e-17, - 2.0464457809993405e-33, 1.3831799631231817e-49), - qd_real( 9.2504924078267758e-01, 6.0529447412576159e-18, - -8.8256517760278541e-35, 1.8285462122388328e-51), - qd_real( 9.2387953251128674e-01, 1.7645047084336677e-17, - -5.0442537321586818e-34, -4.0478677716823890e-50), - qd_real( 9.2270112833387852e-01, 5.2963798918539814e-17, - -5.7135699628876685e-34, 3.0163671797219087e-50), - qd_real( 9.2151403934204190e-01, 4.1639843390684644e-17, - 1.1891485604702356e-33, 2.0862437594380324e-50), - qd_real( 9.2031827670911059e-01, -2.7806888779036837e-17, - 2.7011013677071274e-33, 1.1998578792455499e-49), - qd_real( 9.1911385169005777e-01, -2.6496484622344718e-17, - 6.5403604763461920e-34, -2.8997180201186078e-50), - qd_real( 9.1790077562139050e-01, -3.9074579680849515e-17, - 2.3004636541490264e-33, 3.9851762744443107e-50), - qd_real( 9.1667905992104270e-01, -4.1733978698287568e-17, - 1.2094444804381172e-33, 4.9356916826097816e-50), - qd_real( 9.1544871608826783e-01, -1.3591056692900894e-17, - 5.9923027475594735e-34, 2.1403295925962879e-50), - qd_real( 9.1420975570353069e-01, -3.6316182527814423e-17, - -1.9438819777122554e-33, 2.8340679287728316e-50), - qd_real( 9.1296219042839821e-01, -4.7932505228039469e-17, - -1.7753551889428638e-33, 4.0607782903868160e-51), - qd_real( 9.1170603200542988e-01, -2.6913273175034130e-17, - -5.1928101916162528e-35, 1.1338175936090630e-51), - qd_real( 9.1044129225806725e-01, -5.0433041673313820e-17, - 1.0938746257404305e-33, 9.5378272084170731e-51), - qd_real( 9.0916798309052238e-01, -3.6878564091359894e-18, - 2.9951330310507693e-34, -1.2225666136919926e-50), - qd_real( 9.0788611648766626e-01, -4.9459964301225840e-17, - -1.6599682707075313e-33, -5.1925202712634716e-50), - qd_real( 9.0659570451491533e-01, 3.0506718955442023e-17, - -1.4478836557141204e-33, 1.8906373784448725e-50), - qd_real( 9.0529675931811882e-01, -4.1153099826889901e-17, - 2.9859368705184223e-33, 5.1145293917439211e-50), - qd_real( 9.0398929312344334e-01, -6.6097544687484308e-18, - 1.2728013034680357e-34, -4.3026097234014823e-51), - qd_real( 9.0267331823725883e-01, -1.9250787033961483e-17, - 1.3242128993244527e-33, -5.2971030688703665e-50), - qd_real( 9.0134884704602203e-01, -1.3524789367698682e-17, - 6.3605353115880091e-34, 3.6227400654573828e-50), - qd_real( 9.0001589201616028e-01, -5.0639618050802273e-17, - 1.0783525384031576e-33, 2.8130016326515111e-50), - qd_real( 8.9867446569395382e-01, 2.6316906461033013e-17, - 3.7003137047796840e-35, -2.3447719900465938e-51), - qd_real( 8.9732458070541832e-01, -3.6396283314867290e-17, - -2.3611649895474815e-33, 1.1837247047900082e-49), - qd_real( 8.9596624975618511e-01, 4.9025099114811813e-17, - -1.9440489814795326e-33, -1.7070486667767033e-49), - qd_real( 8.9459948563138270e-01, -1.7516226396814919e-17, - -1.3200670047246923e-33, -1.5953009884324695e-50), - qd_real( 8.9322430119551532e-01, -4.1161239151908913e-18, - 2.5380253805715999e-34, 4.2849455510516192e-51), - qd_real( 8.9184070939234272e-01, 4.6690228137124547e-18, - 1.6150254286841982e-34, -3.9617448820725012e-51), - qd_real( 8.9044872324475788e-01, 1.1781931459051803e-17, - -1.3346142209571930e-34, -9.4982373530733431e-51), - qd_real( 8.8904835585466457e-01, -1.1164514966766675e-17, - -3.4797636107798736e-34, -1.5605079997040631e-50), - qd_real( 8.8763962040285393e-01, 1.2805091918587960e-17, - 3.9948742059584459e-35, 3.8940716325338136e-51), - qd_real( 8.8622253014888064e-01, -6.7307369600274315e-18, - 1.2385593432917413e-34, 2.0364014759133320e-51), - qd_real( 8.8479709843093779e-01, -9.4331469628972690e-18, - -5.7106541478701439e-34, 1.8260134111907397e-50), - qd_real( 8.8336333866573158e-01, 1.5822643380255127e-17, - -7.8921320007588250e-34, -1.4782321016179836e-50), - qd_real( 8.8192126434835505e-01, -1.9843248405890562e-17, - -7.0412114007673834e-34, -1.0636770169389104e-50), - qd_real( 8.8047088905216075e-01, 1.6311096602996350e-17, - -5.7541360594724172e-34, -4.0128611862170021e-50), - qd_real( 8.7901222642863353e-01, -4.7356837291118011e-17, - 1.4388771297975192e-33, -2.9085554304479134e-50), - qd_real( 8.7754529020726124e-01, 5.0113311846499550e-17, - 2.8382769008739543e-34, 1.5550640393164140e-50), - qd_real( 8.7607009419540660e-01, 5.8729024235147677e-18, - 2.7941144391738458e-34, -1.8536073846509828e-50), - qd_real( 8.7458665227817611e-01, -5.7216617730397065e-19, - -2.9705811503689596e-35, 8.7389593969796752e-52), - qd_real( 8.7309497841829009e-01, 7.8424672990129903e-18, - -4.8685015839797165e-34, -2.2815570587477527e-50), - qd_real( 8.7159508665595109e-01, -5.5272998038551050e-17, - -2.2104090204984907e-33, -9.7749763187643172e-50), - qd_real( 8.7008699110871146e-01, -4.1888510868549968e-17, - 7.0900185861878415e-34, 3.7600251115157260e-50), - qd_real( 8.6857070597134090e-01, 2.7192781689782903e-19, - -1.6710140396932428e-35, -1.2625514734637969e-51), - qd_real( 8.6704624551569265e-01, 3.0267859550930567e-18, - -1.1559438782171572e-34, -5.3580556397808012e-52), - qd_real( 8.6551362409056909e-01, -6.3723113549628899e-18, - 2.3725520321746832e-34, 1.5911880348395175e-50), - qd_real( 8.6397285612158670e-01, 4.1486355957361607e-17, - 2.2709976932210266e-33, -8.1228385659479984e-50), - qd_real( 8.6242395611104050e-01, 3.7008992527383130e-17, - 5.2128411542701573e-34, 2.6945600081026861e-50), - qd_real( 8.6086693863776731e-01, -3.0050048898573656e-17, - -8.8706183090892111e-34, 1.5005320558097301e-50), - qd_real( 8.5930181835700836e-01, 4.2435655816850687e-17, - 7.6181814059912025e-34, -3.9592127850658708e-50), - qd_real( 8.5772861000027212e-01, -4.8183447936336620e-17, - -1.1044130517687532e-33, -8.7400233444645562e-50), - qd_real( 8.5614732837519447e-01, 9.1806925616606261e-18, - 5.6328649785951470e-34, 2.3326646113217378e-51), - qd_real( 8.5455798836540053e-01, -1.2991124236396092e-17, - 1.2893407722948080e-34, -3.6506925747583053e-52), - qd_real( 8.5296060493036363e-01, 2.7152984251981370e-17, - 7.4336483283120719e-34, 4.2162417622350668e-50), - qd_real( 8.5135519310526520e-01, -5.3279874446016209e-17, - 2.2281156380919942e-33, -4.0281886404138477e-50), - qd_real( 8.4974176800085244e-01, 5.1812347659974015e-17, - 3.0810626087331275e-33, -2.5931308201994965e-50), - qd_real( 8.4812034480329723e-01, 1.8762563415239981e-17, - 1.4048773307919617e-33, -2.4915221509958691e-50), - qd_real( 8.4649093877405213e-01, -4.7969419958569345e-17, - -2.7518267097886703e-33, -7.3518959727313350e-50), - qd_real( 8.4485356524970712e-01, -4.3631360296879637e-17, - -2.0307726853367547e-33, 4.3097229819851761e-50), - qd_real( 8.4320823964184544e-01, 9.6536707005959077e-19, - 2.8995142431556364e-36, 9.6715076811480284e-53), - qd_real( 8.4155497743689844e-01, -3.4095465391321557e-17, - -8.4130208607579595e-34, -4.9447283960568686e-50), - qd_real( 8.3989379419599952e-01, -1.6673694881511411e-17, - -1.4759184141750289e-33, -7.5795098161914058e-50), - qd_real( 8.3822470555483808e-01, -3.5560085052855026e-17, - 1.1689791577022643e-33, -5.8627347359723411e-50), - qd_real( 8.3654772722351201e-01, -2.0899059027066533e-17, - -9.8104097821002585e-35, -3.1609177868229853e-51), - qd_real( 8.3486287498638001e-01, 4.6048430609159657e-17, - -5.1827423265239912e-34, -7.0505343435504109e-51), - qd_real( 8.3317016470191319e-01, 1.3275129507229764e-18, - 4.8589164115370863e-35, 4.5422281300506859e-51), - qd_real( 8.3146961230254524e-01, 1.4073856984728024e-18, - 4.6951315383980830e-35, 5.1431906049905658e-51), - qd_real( 8.2976123379452305e-01, -2.9349109376485597e-18, - 1.1496917934149818e-34, 3.5186665544980233e-51), - qd_real( 8.2804504525775580e-01, -4.4196593225871532e-17, - 2.7967864855211251e-33, 1.0030777287393502e-49), - qd_real( 8.2632106284566353e-01, -5.3957485453612902e-17, - 6.8976896130138550e-34, 3.8106164274199196e-50), - qd_real( 8.2458930278502529e-01, -2.6512360488868275e-17, - 1.6916964350914386e-34, 6.7693974813562649e-51), - qd_real( 8.2284978137582632e-01, 1.5193019034505495e-17, - 9.6890547246521685e-34, 5.6994562923653264e-50), - qd_real( 8.2110251499110465e-01, 3.0715131609697682e-17, - -1.7037168325855879e-33, -1.1149862443283853e-49), - qd_real( 8.1934752007679701e-01, -4.8200736995191133e-17, - -1.5574489646672781e-35, -9.5647853614522216e-53), - qd_real( 8.1758481315158371e-01, -1.4883149812426772e-17, - -7.8273262771298917e-34, 4.1332149161031594e-50), - qd_real( 8.1581441080673378e-01, 8.2652693782130871e-18, - -2.3028778135179471e-34, 1.5102071387249843e-50), - qd_real( 8.1403632970594841e-01, -5.2127351877042624e-17, - -1.9047670611316360e-33, -1.6937269585941507e-49), - qd_real( 8.1225058658520388e-01, 3.1054545609214803e-17, - 2.2649541922707251e-34, -7.4221684154649405e-51), - qd_real( 8.1045719825259477e-01, 2.3520367349840499e-17, - -7.7530070904846341e-34, -7.2792616357197140e-50), - qd_real( 8.0865618158817498e-01, 9.3251597879721674e-18, - -7.1823301933068394e-34, 2.3925440846132106e-50), - qd_real( 8.0684755354379922e-01, 4.9220603766095546e-17, - 2.9796016899903487e-33, 1.5220754223615788e-49), - qd_real( 8.0503133114296355e-01, 5.1368289568212149e-17, - 6.3082807402256524e-34, 7.3277646085129827e-51), - qd_real( 8.0320753148064494e-01, -3.3060609804814910e-17, - -1.2242726252420433e-33, 2.8413673268630117e-50), - qd_real( 8.0137617172314024e-01, -2.0958013413495834e-17, - -4.3798162198006931e-34, 2.0235690497752515e-50), - qd_real( 7.9953726910790501e-01, 2.0356723822005431e-17, - -9.7448513696896360e-34, 5.3608109599696008e-52), - qd_real( 7.9769084094339116e-01, -4.6730759884788944e-17, - 2.3075897077191757e-33, 3.1605567774640253e-51), - qd_real( 7.9583690460888357e-01, -3.0062724851910721e-17, - -2.2496210832042235e-33, -6.5881774117183040e-50), - qd_real( 7.9397547755433717e-01, -7.4194631759921416e-18, - 2.4124341304631069e-34, -4.9956808616244972e-51), - qd_real( 7.9210657730021239e-01, -3.7087850202326467e-17, - -1.4874457267228264e-33, 2.9323097289153505e-50), - qd_real( 7.9023022143731003e-01, 2.3056905954954492e-17, - 1.4481080533260193e-33, -7.6725237057203488e-50), - qd_real( 7.8834642762660623e-01, 3.4396993154059708e-17, - 1.7710623746737170e-33, 1.7084159098417402e-49), - qd_real( 7.8645521359908577e-01, -9.7841429939305265e-18, - 3.3906063272445472e-34, 5.7269505320382577e-51), - qd_real( 7.8455659715557524e-01, -8.5627965423173476e-18, - -2.1106834459001849e-34, -1.6890322182469603e-50), - qd_real( 7.8265059616657573e-01, 9.0745866975808825e-18, - 6.7623847404278666e-34, -1.7173237731987271e-50), - qd_real( 7.8073722857209449e-01, -9.9198782066678806e-18, - -2.1265794012162715e-36, 3.0772165598957647e-54), - qd_real( 7.7881651238147598e-01, -2.4891385579973807e-17, - 6.7665497024807980e-35, -6.5218594281701332e-52), - qd_real( 7.7688846567323244e-01, 7.7418602570672864e-18, - -5.9986517872157897e-34, 3.0566548232958972e-50), - qd_real( 7.7495310659487393e-01, -5.2209083189826433e-17, - -9.6653593393686612e-34, 3.7027750076562569e-50), - qd_real( 7.7301045336273699e-01, -3.2565907033649772e-17, - 1.3860807251523929e-33, -3.9971329917586022e-50), - qd_real( 7.7106052426181382e-01, -4.4558442347769265e-17, - -2.9863565614083783e-33, -6.8795262083596236e-50), - qd_real( 7.6910333764557959e-01, 5.1546455184564817e-17, - 2.6142829553524292e-33, -1.6199023632773298e-49), - qd_real( 7.6713891193582040e-01, -1.8885903683750782e-17, - -1.3659359331495433e-33, -2.2538834962921934e-50), - qd_real( 7.6516726562245896e-01, -3.2707225612534598e-17, - 1.1177117747079528e-33, -3.7005182280175715e-50), - qd_real( 7.6318841726338127e-01, 2.6314748416750748e-18, - 1.4048039063095910e-34, 8.9601886626630321e-52), - qd_real( 7.6120238548426178e-01, 3.5315510881690551e-17, - 1.2833566381864357e-33, 8.6221435180890613e-50), - qd_real( 7.5920918897838807e-01, -3.8558842175523123e-17, - 2.9720241208332759e-34, -1.2521388928220163e-50), - qd_real( 7.5720884650648457e-01, -1.9909098777335502e-17, - 3.9409283266158482e-34, 2.0744254207802976e-50), - qd_real( 7.5520137689653655e-01, -1.9402238001823017e-17, - -3.7756206444727573e-34, -2.1212242308178287e-50), - qd_real( 7.5318679904361252e-01, -3.7937789838736540e-17, - -6.7009539920231559e-34, -6.7128562115050214e-51), - qd_real( 7.5116513190968637e-01, 4.3499761158645868e-17, - 2.5227718971102212e-33, -6.5969709212757102e-50), - qd_real( 7.4913639452345937e-01, -4.4729078447011889e-17, - -2.4206025249983768e-33, 1.1336681351116422e-49), - qd_real( 7.4710060598018013e-01, 1.1874824875965430e-17, - 2.1992523849833518e-34, 1.1025018564644483e-50), - qd_real( 7.4505778544146595e-01, 1.5078686911877863e-17, - 8.0898987212942471e-34, 8.2677958765323532e-50), - qd_real( 7.4300795213512172e-01, -2.5144629669719265e-17, - 7.1128989512526157e-34, 3.0181629077821220e-50), - qd_real( 7.4095112535495911e-01, -1.4708616952297345e-17, - -4.9550433827142032e-34, 3.1434132533735671e-50), - qd_real( 7.3888732446061511e-01, 3.4324874808225091e-17, - -1.3706639444717610e-33, -3.3520827530718938e-51), - qd_real( 7.3681656887736990e-01, -2.8932468101656295e-17, - -3.4649887126202378e-34, -1.8484474476291476e-50), - qd_real( 7.3473887809596350e-01, -3.4507595976263941e-17, - -2.3718000676666409e-33, -3.9696090387165402e-50), - qd_real( 7.3265427167241282e-01, 1.8918673481573520e-17, - -1.5123719544119886e-33, -9.7922152011625728e-51), - qd_real( 7.3056276922782759e-01, -2.9689959904476928e-17, - -1.1276871244239744e-33, -3.0531520961539007e-50), - qd_real( 7.2846439044822520e-01, 1.1924642323370718e-19, - 5.9001892316611011e-36, 1.2178089069502704e-52), - qd_real( 7.2635915508434601e-01, -3.1917502443460542e-17, - 7.7047912412039396e-34, 4.1455880160182123e-50), - qd_real( 7.2424708295146689e-01, 2.9198471334403004e-17, - 2.3027324968739464e-33, -1.2928820533892183e-51), - qd_real( 7.2212819392921535e-01, -2.3871262053452047e-17, - 1.0636125432862273e-33, -4.4598638837802517e-50), - qd_real( 7.2000250796138165e-01, -2.5689658854462333e-17, - -9.1492566948567925e-34, 4.4403780801267786e-50), - qd_real( 7.1787004505573171e-01, 2.7006476062511453e-17, - -2.2854956580215348e-34, 9.1726903890287867e-51), - qd_real( 7.1573082528381871e-01, -5.1581018476410262e-17, - -1.3736271349300259e-34, -1.2734611344111297e-50), - qd_real( 7.1358486878079364e-01, -4.2342504403133584e-17, - -4.2690366101617268e-34, -2.6352370883066522e-50), - qd_real( 7.1143219574521643e-01, 7.9643298613856813e-18, - 2.9488239510721469e-34, 1.6985236437666356e-50), - qd_real( 7.0927282643886569e-01, -3.7597359110245730e-17, - 1.0613125954645119e-34, 8.9465480185486032e-51), - qd_real( 7.0710678118654757e-01, -4.8336466567264567e-17, - 2.0693376543497068e-33, 2.4677734957341755e-50) -}; - -/* Computes sin(a) and cos(a) using Taylor series. - Assumes |a| <= pi/2048. */ -static void sincos_taylor(const qd_real &a, - qd_real &sin_a, qd_real &cos_a) { - const double thresh = 0.5 * qd_real::_eps * std::abs(to_double(a)); - qd_real p, s, t, x; - - if (a.is_zero()) { - sin_a = 0.0; - cos_a = 1.0; - return; - } - - x = -sqr(a); - s = a; - p = a; - int i = 0; - do { - p *= x; - t = p * inv_fact[i]; - s += t; - i += 2; - } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); - - sin_a = s; - cos_a = sqrt(1.0 - sqr(s)); -} - -static qd_real sin_taylor(const qd_real &a) { - const double thresh = 0.5 * qd_real::_eps * std::abs(to_double(a)); - qd_real p, s, t, x; - - if (a.is_zero()) { - return 0.0; - } - - x = -sqr(a); - s = a; - p = a; - int i = 0; - do { - p *= x; - t = p * inv_fact[i]; - s += t; - i += 2; - } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); - - return s; -} - -static qd_real cos_taylor(const qd_real &a) { - const double thresh = 0.5 * qd_real::_eps; - qd_real p, s, t, x; - - if (a.is_zero()) { - return 1.0; - } - - x = -sqr(a); - s = 1.0 + mul_pwr2(x, 0.5); - p = x; - int i = 1; - do { - p *= x; - t = p * inv_fact[i]; - s += t; - i += 2; - } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); - - return s; -} - -qd_real sin(const qd_real &a) { - - /* Strategy. To compute sin(x), we choose integers a, b so that - - x = s + a * (pi/2) + b * (pi/1024) - - and |s| <= pi/2048. Using a precomputed table of - sin(k pi / 1024) and cos(k pi / 1024), we can compute - sin(x) from sin(s) and cos(s). This greatly increases the - convergence of the sine Taylor series. */ - - if (a.is_zero()) { - return 0.0; - } - - // approximately reduce modulo 2*pi - qd_real z = nint(a / qd_real::_2pi); - qd_real r = a - qd_real::_2pi * z; - - // approximately reduce modulo pi/2 and then modulo pi/1024 - double q = std::floor(r.x[0] / qd_real::_pi2[0] + 0.5); - qd_real t = r - qd_real::_pi2 * q; - int j = static_cast(q); - q = std::floor(t.x[0] / _pi1024[0] + 0.5); - t -= _pi1024 * q; - int k = static_cast(q); - int abs_k = std::abs(k); - - if (j < -2 || j > 2) { - qd_real::error("(qd_real::sin): Cannot reduce modulo pi/2."); - return qd_real::_nan; - } - - if (abs_k > 256) { - qd_real::error("(qd_real::sin): Cannot reduce modulo pi/1024."); - return qd_real::_nan; - } - - if (k == 0) { - switch (j) { - case 0: - return sin_taylor(t); - case 1: - return cos_taylor(t); - case -1: - return -cos_taylor(t); - default: - return -sin_taylor(t); - } - } - - qd_real sin_t, cos_t; - qd_real u = cos_table[abs_k-1]; - qd_real v = sin_table[abs_k-1]; - sincos_taylor(t, sin_t, cos_t); - - if (j == 0) { - if (k > 0) { - r = u * sin_t + v * cos_t; - } else { - r = u * sin_t - v * cos_t; - } - } else if (j == 1) { - if (k > 0) { - r = u * cos_t - v * sin_t; - } else { - r = u * cos_t + v * sin_t; - } - } else if (j == -1) { - if (k > 0) { - r = v * sin_t - u * cos_t; - } else { - r = - u * cos_t - v * sin_t; - } - } else { - if (k > 0) { - r = - u * sin_t - v * cos_t; - } else { - r = v * cos_t - u * sin_t; - } - } - - return r; -} - -qd_real cos(const qd_real &a) { - - if (a.is_zero()) { - return 1.0; - } - - // approximately reduce modulo 2*pi - qd_real z = nint(a / qd_real::_2pi); - qd_real r = a - qd_real::_2pi * z; - - // approximately reduce modulo pi/2 and then modulo pi/1024 - double q = std::floor(r.x[0] / qd_real::_pi2.x[0] + 0.5); - qd_real t = r - qd_real::_pi2 * q; - int j = static_cast(q); - q = std::floor(t.x[0] / _pi1024.x[0] + 0.5); - t -= _pi1024 * q; - int k = static_cast(q); - int abs_k = std::abs(k); - - if (j < -2 || j > 2) { - qd_real::error("(qd_real::cos): Cannot reduce modulo pi/2."); - return qd_real::_nan; - } - - if (abs_k > 256) { - qd_real::error("(qd_real::cos): Cannot reduce modulo pi/1024."); - return qd_real::_nan; - } - - if (k == 0) { - switch (j) { - case 0: - return cos_taylor(t); - case 1: - return -sin_taylor(t); - case -1: - return sin_taylor(t); - default: - return -cos_taylor(t); - } - } - - qd_real sin_t, cos_t; - sincos_taylor(t, sin_t, cos_t); - - qd_real u = cos_table[abs_k-1]; - qd_real v = sin_table[abs_k-1]; - - if (j == 0) { - if (k > 0) { - r = u * cos_t - v * sin_t; - } else { - r = u * cos_t + v * sin_t; - } - } else if (j == 1) { - if (k > 0) { - r = - u * sin_t - v * cos_t; - } else { - r = v * cos_t - u * sin_t; - } - } else if (j == -1) { - if (k > 0) { - r = u * sin_t + v * cos_t; - } else { - r = u * sin_t - v * cos_t; - } - } else { - if (k > 0) { - r = v * sin_t - u * cos_t; - } else { - r = - u * cos_t - v * sin_t; - } - } - - return r; -} - -void sincos(const qd_real &a, qd_real &sin_a, qd_real &cos_a) { - - if (a.is_zero()) { - sin_a = 0.0; - cos_a = 1.0; - return; - } - - // approximately reduce by 2*pi - qd_real z = nint(a / qd_real::_2pi); - qd_real t = a - qd_real::_2pi * z; - - // approximately reduce by pi/2 and then by pi/1024. - double q = std::floor(t.x[0] / qd_real::_pi2.x[0] + 0.5); - t -= qd_real::_pi2 * q; - int j = static_cast(q); - q = std::floor(t.x[0] / _pi1024.x[0] + 0.5); - t -= _pi1024 * q; - int k = static_cast(q); - int abs_k = std::abs(k); - - if (j < -2 || j > 2) { - qd_real::error("(qd_real::sincos): Cannot reduce modulo pi/2."); - cos_a = sin_a = qd_real::_nan; - return; - } - - if (abs_k > 256) { - qd_real::error("(qd_real::sincos): Cannot reduce modulo pi/1024."); - cos_a = sin_a = qd_real::_nan; - return; - } - - qd_real sin_t, cos_t; - sincos_taylor(t, sin_t, cos_t); - - if (k == 0) { - if (j == 0) { - sin_a = sin_t; - cos_a = cos_t; - } else if (j == 1) { - sin_a = cos_t; - cos_a = -sin_t; - } else if (j == -1) { - sin_a = -cos_t; - cos_a = sin_t; - } else { - sin_a = -sin_t; - cos_a = -cos_t; - } - return; - } - - qd_real u = cos_table[abs_k-1]; - qd_real v = sin_table[abs_k-1]; - - if (j == 0) { - if (k > 0) { - sin_a = u * sin_t + v * cos_t; - cos_a = u * cos_t - v * sin_t; - } else { - sin_a = u * sin_t - v * cos_t; - cos_a = u * cos_t + v * sin_t; - } - } else if (j == 1) { - if (k > 0) { - cos_a = - u * sin_t - v * cos_t; - sin_a = u * cos_t - v * sin_t; - } else { - cos_a = v * cos_t - u * sin_t; - sin_a = u * cos_t + v * sin_t; - } - } else if (j == -1) { - if (k > 0) { - cos_a = u * sin_t + v * cos_t; - sin_a = v * sin_t - u * cos_t; - } else { - cos_a = u * sin_t - v * cos_t; - sin_a = - u * cos_t - v * sin_t; - } - } else { - if (k > 0) { - sin_a = - u * sin_t - v * cos_t; - cos_a = v * sin_t - u * cos_t; - } else { - sin_a = v * cos_t - u * sin_t; - cos_a = - u * cos_t - v * sin_t; - } - } -} - -qd_real atan(const qd_real &a) { - return atan2(a, qd_real(1.0)); -} - -qd_real atan2(const qd_real &y, const qd_real &x) { - /* Strategy: Instead of using Taylor series to compute - arctan, we instead use Newton's iteration to solve - the equation - - sin(z) = y/r or cos(z) = x/r - - where r = sqrt(x^2 + y^2). - The iteration is given by - - z' = z + (y - sin(z)) / cos(z) (for equation 1) - z' = z - (x - cos(z)) / sin(z) (for equation 2) - - Here, x and y are normalized so that x^2 + y^2 = 1. - If |x| > |y|, then first iteration is used since the - denominator is larger. Otherwise, the second is used. - */ - - if (x.is_zero()) { - - if (y.is_zero()) { - /* Both x and y is zero. */ - qd_real::error("(qd_real::atan2): Both arguments zero."); - return qd_real::_nan; - } - - return (y.is_positive()) ? qd_real::_pi2 : -qd_real::_pi2; - } else if (y.is_zero()) { - return (x.is_positive()) ? qd_real(0.0) : qd_real::_pi; - } - - if (x == y) { - return (y.is_positive()) ? qd_real::_pi4 : -qd_real::_3pi4; - } - - if (x == -y) { - return (y.is_positive()) ? qd_real::_3pi4 : -qd_real::_pi4; - } - - qd_real r = sqrt(sqr(x) + sqr(y)); - qd_real xx = x / r; - qd_real yy = y / r; - - /* Compute double precision approximation to atan. */ - qd_real z = std::atan2(to_double(y), to_double(x)); - qd_real sin_z, cos_z; - - if (std::abs(xx.x[0]) > std::abs(yy.x[0])) { - /* Use Newton iteration 1. z' = z + (y - sin(z)) / cos(z) */ - sincos(z, sin_z, cos_z); - z += (yy - sin_z) / cos_z; - sincos(z, sin_z, cos_z); - z += (yy - sin_z) / cos_z; - sincos(z, sin_z, cos_z); - z += (yy - sin_z) / cos_z; - } else { - /* Use Newton iteration 2. z' = z - (x - cos(z)) / sin(z) */ - sincos(z, sin_z, cos_z); - z -= (xx - cos_z) / sin_z; - sincos(z, sin_z, cos_z); - z -= (xx - cos_z) / sin_z; - sincos(z, sin_z, cos_z); - z -= (xx - cos_z) / sin_z; - } - - return z; -} - - -qd_real drem(const qd_real &a, const qd_real &b) { - qd_real n = nint(a/b); - return (a - n * b); -} - -qd_real divrem(const qd_real &a, const qd_real &b, qd_real &r) { - qd_real n = nint(a/b); - r = a - n * b; - return n; -} - -qd_real tan(const qd_real &a) { - qd_real s, c; - sincos(a, s, c); - return s/c; -} - -qd_real asin(const qd_real &a) { - qd_real abs_a = abs(a); - - if (abs_a > 1.0) { - qd_real::error("(qd_real::asin): Argument out of domain."); - return qd_real::_nan; - } - - if (abs_a.is_one()) { - return (a.is_positive()) ? qd_real::_pi2 : -qd_real::_pi2; - } - - return atan2(a, sqrt(1.0 - sqr(a))); -} - -qd_real acos(const qd_real &a) { - qd_real abs_a = abs(a); - - if (abs_a > 1.0) { - qd_real::error("(qd_real::acos): Argument out of domain."); - return qd_real::_nan; - } - - if (abs_a.is_one()) { - return (a.is_positive()) ? qd_real(0.0) : qd_real::_pi; - } - - return atan2(sqrt(1.0 - sqr(a)), a); -} - -qd_real sinh(const qd_real &a) { - if (a.is_zero()) { - return 0.0; - } - - if (abs(a) > 0.05) { - qd_real ea = exp(a); - return mul_pwr2(ea - inv(ea), 0.5); - } - - /* Since a is small, using the above formula gives - a lot of cancellation. So use Taylor series. */ - qd_real s = a; - qd_real t = a; - qd_real r = sqr(t); - double m = 1.0; - double thresh = std::abs(to_double(a) * qd_real::_eps); - - do { - m += 2.0; - t *= r; - t /= (m-1) * m; - - s += t; - } while (abs(t) > thresh); - - return s; -} - -qd_real cosh(const qd_real &a) { - if (a.is_zero()) { - return 1.0; - } - - qd_real ea = exp(a); - return mul_pwr2(ea + inv(ea), 0.5); -} - -qd_real tanh(const qd_real &a) { - if (a.is_zero()) { - return 0.0; - } - - if (std::abs(to_double(a)) > 0.05) { - qd_real ea = exp(a); - qd_real inv_ea = inv(ea); - return (ea - inv_ea) / (ea + inv_ea); - } else { - qd_real s, c; - s = sinh(a); - c = sqrt(1.0 + sqr(s)); - return s / c; - } -} - -void sincosh(const qd_real &a, qd_real &s, qd_real &c) { - if (std::abs(to_double(a)) <= 0.05) { - s = sinh(a); - c = sqrt(1.0 + sqr(s)); - } else { - qd_real ea = exp(a); - qd_real inv_ea = inv(ea); - s = mul_pwr2(ea - inv_ea, 0.5); - c = mul_pwr2(ea + inv_ea, 0.5); - } -} - -qd_real asinh(const qd_real &a) { - return log(a + sqrt(sqr(a) + 1.0)); -} - -qd_real acosh(const qd_real &a) { - if (a < 1.0) { - qd_real::error("(qd_real::acosh): Argument out of domain."); - return qd_real::_nan; - } - - return log(a + sqrt(sqr(a) - 1.0)); -} - -qd_real atanh(const qd_real &a) { - if (abs(a) >= 1.0) { - qd_real::error("(qd_real::atanh): Argument out of domain."); - return qd_real::_nan; - } - - return mul_pwr2(log((1.0 + a) / (1.0 - a)), 0.5); -} - -QD_API qd_real fmod(const qd_real &a, const qd_real &b) { - qd_real n = aint(a / b); - return (a - b * n); -} - -QD_API qd_real qdrand() { - static const double m_const = 4.6566128730773926e-10; /* = 2^{-31} */ - double m = m_const; - qd_real r = 0.0; - double d; - - /* Strategy: Generate 31 bits at a time, using lrand48 - random number generator. Shift the bits, and repeat - 7 times. */ - - for (int i = 0; i < 7; i++, m *= m_const) { - d = std::rand() * m; - r += d; - } - - return r; -} - - -/* polyeval(c, n, x) - Evaluates the given n-th degree polynomial at x. - The polynomial is given by the array of (n+1) coefficients. */ -qd_real polyeval(const qd_real *c, int n, const qd_real &x) { - /* Just use Horner's method of polynomial evaluation. */ - qd_real r = c[n]; - - for (int i = n-1; i >= 0; i--) { - r *= x; - r += c[i]; - } - - return r; -} - -/* polyroot(c, n, x0) - Given an n-th degree polynomial, finds a root close to - the given guess x0. Note that this uses simple Newton - iteration scheme, and does not work for multiple roots. */ -QD_API qd_real polyroot(const qd_real *c, int n, - const qd_real &x0, int max_iter, double thresh) { - qd_real x = x0; - qd_real f; - qd_real *d = new qd_real[n]; - bool conv = false; - int i; - double max_c = std::abs(to_double(c[0])); - double v; - - if (thresh == 0.0) thresh = qd_real::_eps; - - /* Compute the coefficients of the derivatives. */ - for (i = 1; i <= n; i++) { - v = std::abs(to_double(c[i])); - if (v > max_c) max_c = v; - d[i-1] = c[i] * static_cast(i); - } - thresh *= max_c; - - /* Newton iteration. */ - for (i = 0; i < max_iter; i++) { - f = polyeval(c, n, x); - - if (abs(f) < thresh) { - conv = true; - break; - } - x -= (f / polyeval(d, n-1, x)); - } - delete [] d; - - if (!conv) { - qd_real::error("(qd_real::polyroot): Failed to converge."); - return qd_real::_nan; - } - - return x; -} - -qd_real qd_real::debug_rand() { - if (std::rand() % 2 == 0) - return qdrand(); - - int expn = 0; - qd_real a = 0.0; - double d; - for (int i = 0; i < 4; i++) { - d = std::ldexp(std::rand() / static_cast(RAND_MAX), -expn); - a += d; - expn = expn + 54 + std::rand() % 200; - } - return a; -} - diff --git a/src/external/PackedCSparse/qd/qd_real.h b/src/external/PackedCSparse/qd/qd_real.h deleted file mode 100644 index 1ecfc732..00000000 --- a/src/external/PackedCSparse/qd/qd_real.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - * include/qd_real.h - * - * This work was supported by the Director, Office of Science, Division - * of Mathematical, Information, and Computational Sciences of the - * U.S. Department of Energy under contract number DE-AC03-76SF00098. - * - * Copyright (c) 2000-2007 - * - * Quad-double precision (>= 212-bit significand) floating point arithmetic - * package, written in ANSI C++, taking full advantage of operator overloading. - * Uses similar techniques as that of David Bailey's double-double package - * and that of Jonathan Shewchuk's adaptive precision floating point - * arithmetic package. See - * - * http://www.nersc.gov/~dhbailey/mpdist/mpdist.html - * http://www.cs.cmu.edu/~quake/robust.html - * - * for more details. - * - * Yozo Hida - */ -#ifndef _QD_QD_REAL_H -#define _QD_QD_REAL_H - -#include -#include -#include -#include "qd_config.h" -#include "dd_real.h" - -struct QD_API qd_real { - double x[4]; /* The Components. */ - - /* Eliminates any zeros in the middle component(s). */ - void zero_elim(); - void zero_elim(double &e); - - void renorm(); - void renorm(double &e); - - void quick_accum(double d, double &e); - void quick_prod_accum(double a, double b, double &e); - - qd_real(double x0, double x1, double x2, double x3); - explicit qd_real(const double *xx); - - static const qd_real _2pi; - static const qd_real _pi; - static const qd_real _3pi4; - static const qd_real _pi2; - static const qd_real _pi4; - static const qd_real _e; - static const qd_real _log2; - static const qd_real _log10; - static const qd_real _nan; - static const qd_real _inf; - - static const double _eps; - static const double _min_normalized; - static const qd_real _max; - static const qd_real _safe_max; - static const int _ndigits; - - qd_real(); - qd_real(const char *s); - qd_real(const dd_real &dd); - qd_real(double d); - qd_real(int i); - - double operator[](int i) const; - double &operator[](int i); - - static void error(const char *msg); - - bool isnan() const; - bool isfinite() const { return QD_ISFINITE(x[0]); } - bool isinf() const { return QD_ISINF(x[0]); } - - static qd_real ieee_add(const qd_real &a, const qd_real &b); - static qd_real sloppy_add(const qd_real &a, const qd_real &b); - - qd_real &operator+=(double a); - qd_real &operator+=(const dd_real &a); - qd_real &operator+=(const qd_real &a); - - qd_real &operator-=(double a); - qd_real &operator-=(const dd_real &a); - qd_real &operator-=(const qd_real &a); - - static qd_real sloppy_mul(const qd_real &a, const qd_real &b); - static qd_real accurate_mul(const qd_real &a, const qd_real &b); - - qd_real &operator*=(double a); - qd_real &operator*=(const dd_real &a); - qd_real &operator*=(const qd_real &a); - - static qd_real sloppy_div(const qd_real &a, const dd_real &b); - static qd_real accurate_div(const qd_real &a, const dd_real &b); - static qd_real sloppy_div(const qd_real &a, const qd_real &b); - static qd_real accurate_div(const qd_real &a, const qd_real &b); - - qd_real &operator/=(double a); - qd_real &operator/=(const dd_real &a); - qd_real &operator/=(const qd_real &a); - - qd_real operator^(int n) const; - - qd_real operator-() const; - - qd_real &operator=(double a); - qd_real &operator=(const dd_real &a); - qd_real &operator=(const char *s); - - bool is_zero() const; - bool is_one() const; - bool is_positive() const; - bool is_negative() const; - - explicit operator bool() const; // new - explicit operator double() const; // new - - static qd_real rand(void); - - void to_digits(char *s, int &expn, int precision = _ndigits) const; - void write(char *s, int len, int precision = _ndigits, - bool showpos = false, bool uppercase = false) const; - std::string to_string(int precision = _ndigits, int width = 0, - std::ios_base::fmtflags fmt = static_cast(0), - bool showpos = false, bool uppercase = false, char fill = ' ') const; - static int read(const char *s, qd_real &a); - - /* Debugging methods */ - void dump(const std::string &name, std::ostream &os = std::cerr) const; - void dump_bits(const std::string &name, - std::ostream &os = std::cerr) const; - - static qd_real debug_rand(); - -}; - -namespace std { - template <> - class numeric_limits : public numeric_limits { - public: - inline static double epsilon() { return qd_real::_eps; } - inline static double min() { return qd_real::_min_normalized; } - inline static qd_real max() { return qd_real::_max; } - inline static qd_real safe_max() { return qd_real::_safe_max; } - static const int digits = 209; - static const int digits10 = 62; - }; -} - -QD_API qd_real polyeval(const qd_real *c, int n, const qd_real &x); -QD_API qd_real polyroot(const qd_real *c, int n, - const qd_real &x0, int max_iter = 64, double thresh = 0.0); - -QD_API qd_real qdrand(void); -QD_API qd_real sqrt(const qd_real &a); - -QD_API inline bool isnan(const qd_real &a) { return a.isnan(); } -QD_API inline bool isfinite(const qd_real &a) { return a.isfinite(); } -QD_API inline bool isinf(const qd_real &a) { return a.isinf(); } - -/* Computes qd * d where d is known to be a power of 2. - This can be done component wise. */ -QD_API qd_real mul_pwr2(const qd_real &qd, double d); - -QD_API qd_real operator+(const qd_real &a, const qd_real &b); -QD_API qd_real operator+(const dd_real &a, const qd_real &b); -QD_API qd_real operator+(const qd_real &a, const dd_real &b); -QD_API qd_real operator+(const qd_real &a, double b); -QD_API qd_real operator+(double a, const qd_real &b); - -QD_API qd_real operator-(const qd_real &a, const qd_real &b); -QD_API qd_real operator-(const dd_real &a, const qd_real &b); -QD_API qd_real operator-(const qd_real &a, const dd_real &b); -QD_API qd_real operator-(const qd_real &a, double b); -QD_API qd_real operator-(double a, const qd_real &b); - -QD_API qd_real operator*(const qd_real &a, const qd_real &b); -QD_API qd_real operator*(const dd_real &a, const qd_real &b); -QD_API qd_real operator*(const qd_real &a, const dd_real &b); -QD_API qd_real operator*(const qd_real &a, double b); -QD_API qd_real operator*(double a, const qd_real &b); - -QD_API qd_real operator/(const qd_real &a, const qd_real &b); -QD_API qd_real operator/(const dd_real &a, const qd_real &b); -QD_API qd_real operator/(const qd_real &a, const dd_real &b); -QD_API qd_real operator/(const qd_real &a, double b); -QD_API qd_real operator/(double a, const qd_real &b); - -QD_API qd_real sqr(const qd_real &a); -QD_API qd_real sqrt(const qd_real &a); -QD_API qd_real pow(const qd_real &a, int n); -QD_API qd_real pow(const qd_real &a, const qd_real &b); -QD_API qd_real npwr(const qd_real &a, int n); - -QD_API qd_real nroot(const qd_real &a, int n); - -QD_API qd_real rem(const qd_real &a, const qd_real &b); -QD_API qd_real drem(const qd_real &a, const qd_real &b); -QD_API qd_real divrem(const qd_real &a, const qd_real &b, qd_real &r); - -dd_real to_dd_real(const qd_real &a); -double to_double(const qd_real &a); -int to_int(const qd_real &a); - -QD_API bool operator==(const qd_real &a, const qd_real &b); -QD_API bool operator==(const qd_real &a, const dd_real &b); -QD_API bool operator==(const dd_real &a, const qd_real &b); -QD_API bool operator==(double a, const qd_real &b); -QD_API bool operator==(const qd_real &a, double b); - -QD_API bool operator<(const qd_real &a, const qd_real &b); -QD_API bool operator<(const qd_real &a, const dd_real &b); -QD_API bool operator<(const dd_real &a, const qd_real &b); -QD_API bool operator<(double a, const qd_real &b); -QD_API bool operator<(const qd_real &a, double b); - -QD_API bool operator>(const qd_real &a, const qd_real &b); -QD_API bool operator>(const qd_real &a, const dd_real &b); -QD_API bool operator>(const dd_real &a, const qd_real &b); -QD_API bool operator>(double a, const qd_real &b); -QD_API bool operator>(const qd_real &a, double b); - -QD_API bool operator<=(const qd_real &a, const qd_real &b); -QD_API bool operator<=(const qd_real &a, const dd_real &b); -QD_API bool operator<=(const dd_real &a, const qd_real &b); -QD_API bool operator<=(double a, const qd_real &b); -QD_API bool operator<=(const qd_real &a, double b); - -QD_API bool operator>=(const qd_real &a, const qd_real &b); -QD_API bool operator>=(const qd_real &a, const dd_real &b); -QD_API bool operator>=(const dd_real &a, const qd_real &b); -QD_API bool operator>=(double a, const qd_real &b); -QD_API bool operator>=(const qd_real &a, double b); - -QD_API bool operator!=(const qd_real &a, const qd_real &b); -QD_API bool operator!=(const qd_real &a, const dd_real &b); -QD_API bool operator!=(const dd_real &a, const qd_real &b); -QD_API bool operator!=(double a, const qd_real &b); -QD_API bool operator!=(const qd_real &a, double b); - -QD_API qd_real fabs(const qd_real &a); -QD_API qd_real abs(const qd_real &a); /* same as fabs */ - -QD_API qd_real ldexp(const qd_real &a, int n); - -QD_API qd_real nint(const qd_real &a); -QD_API qd_real quick_nint(const qd_real &a); -QD_API qd_real floor(const qd_real &a); -QD_API qd_real ceil(const qd_real &a); -QD_API qd_real aint(const qd_real &a); - -QD_API qd_real sin(const qd_real &a); -QD_API qd_real cos(const qd_real &a); -QD_API qd_real tan(const qd_real &a); -QD_API void sincos(const qd_real &a, qd_real &s, qd_real &c); - -QD_API qd_real asin(const qd_real &a); -QD_API qd_real acos(const qd_real &a); -QD_API qd_real atan(const qd_real &a); -QD_API qd_real atan2(const qd_real &y, const qd_real &x); - -QD_API qd_real exp(const qd_real &a); -QD_API qd_real log(const qd_real &a); -QD_API qd_real log10(const qd_real &a); - -QD_API qd_real sinh(const qd_real &a); -QD_API qd_real cosh(const qd_real &a); -QD_API qd_real tanh(const qd_real &a); -QD_API void sincosh(const qd_real &a, qd_real &sin_qd, qd_real &cos_qd); - -QD_API qd_real asinh(const qd_real &a); -QD_API qd_real acosh(const qd_real &a); -QD_API qd_real atanh(const qd_real &a); - -QD_API qd_real qdrand(void); - -QD_API qd_real max(const qd_real &a, const qd_real &b); -QD_API qd_real max(const qd_real &a, const qd_real &b, const qd_real &c); -QD_API qd_real min(const qd_real &a, const qd_real &b); -QD_API qd_real min(const qd_real &a, const qd_real &b, const qd_real &c); - -QD_API qd_real fmod(const qd_real &a, const qd_real &b); - -QD_API std::ostream &operator<<(std::ostream &s, const qd_real &a); -QD_API std::istream &operator>>(std::istream &s, qd_real &a); -#ifdef QD_INLINE -#include "qd_inline.h" -#endif - -#endif /* _QD_QD_REAL_H */ - diff --git a/src/external/PackedCSparse/qd/util.cc b/src/external/PackedCSparse/qd/util.cc deleted file mode 100644 index ab962081..00000000 --- a/src/external/PackedCSparse/qd/util.cc +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include "util.h" - -void append_expn(std::string &str, int expn) { - int k; - - str += (expn < 0 ? '-' : '+'); - expn = std::abs(expn); - - if (expn >= 100) { - k = (expn / 100); - str += '0' + k; - expn -= 100*k; - } - - k = (expn / 10); - str += '0' + k; - expn -= 10*k; - - str += '0' + expn; -} - diff --git a/src/external/PackedCSparse/qd/util.h b/src/external/PackedCSparse/qd/util.h deleted file mode 100644 index 7de35836..00000000 --- a/src/external/PackedCSparse/qd/util.h +++ /dev/null @@ -1,4 +0,0 @@ -#include - -void append_expn(std::string &str, int expn); - diff --git a/src/external/PackedCSparse/transpose.h b/src/external/PackedCSparse/transpose.h deleted file mode 100644 index 6e1502e3..00000000 --- a/src/external/PackedCSparse/transpose.h +++ /dev/null @@ -1,90 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis -// Copyright (c) 2022 Ioannis Iakovidis - -// This file is converted from PolytopeSamplerMatlab -//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis -#pragma once -#include "SparseMatrix.h" - -// Problem: -// Compute M = A' - -// Algorithm: -// We precompute the mapping from entries of A to entries of At - -namespace PackedCSparse { - template - struct TransposeOutput : SparseMatrix - { - UniquePtr forward; - - template - void initialize(const SparseMatrix& A) - { - pcs_assert(A.initialized(), "transpose: bad inputs."); - SparseMatrix::initialize(A.n, A.m, A.nnz()); - - Ti Am = A.m, An = A.n, * Ap = A.p.get(), * Ai = A.i.get(); - Ti Bm = this->m, Bn = this->n, * Bp = this->p.get(), * Bi = this->i.get(); - Ti nz = A.nnz(); - - // compute row counts of A - Ti* count = new Ti[Bn + 1](); - - for (Ti p = 0; p < nz; p++) - count[Ai[p]]++; - - // compute this->p - Bp[0] = 0; - for (Ti i = 0; i < Bn; i++) - { - Bp[i + 1] = Bp[i] + count[i]; - count[i] = Bp[i]; // Now, cnt[i] stores the index of the first element in the i-th row - } - - // compute i and forward - if (!std::is_same::value) - forward.reset(new Ti[nz]); - for (Ti j = 0; j < An; j++) - { - for (Ti p = Ap[j]; p < Ap[j + 1]; p++) - { - Ti q = count[Ai[p]]; - Bi[q] = j; - if (!std::is_same::value) - forward[p] = q; - count[Ai[p]]++; - } - } - - delete[] count; - } - }; - - template - void transpose(TransposeOutput& o, const SparseMatrix& A) - { - if (!o.initialized()) - o.initialize(A); - - Tx* Ax = A.x.get(); Tx2 *Bx = o.x.get(); - Ti nz = o.nnz(), *forward = o.forward.get(); - - if (!std::is_same::value) - { - for (Ti s = 0; s < nz; s++) - Bx[forward[s]] = Tx2(Ax[s]); - } - } - - template - TransposeOutput transpose(const SparseMatrix& A) - { - TransposeOutput o; - transpose(o, A); - return o; - } -} diff --git a/src/external/Padua/padua.cpp b/src/external/Padua/padua.cpp deleted file mode 100644 index 3319b637..00000000 --- a/src/external/Padua/padua.cpp +++ /dev/null @@ -1,1868 +0,0 @@ -// Credit to https://people.sc.fsu.edu/~jburkardt/cpp_src/padua/padua.cpp - -# include -# include -# include -# include -# include -# include -# include -# include - - - - -#include "padua.h" - - - using std::string; - using std::cerr; - using std::ofstream; - using std::ostringstream; - using std::setprecision; - using std::cout; - using std::setw; - - -namespace padua { -//****************************************************************************80 - - void filename_inc(string *filename) - -//****************************************************************************80 -// -// Purpose: -// -// FILENAME_INC increments a partially numeric file name. -// -// Discussion: -// -// It is assumed that the digits in the name, whether scattered or -// connected, represent a number that is to be increased by 1 on -// each call. If this number is all 9's on input, the output number -// is all 0's. Non-numeric letters of the name are unaffected. -// -// If the name is empty, then the routine stops. -// -// If the name contains no digits, the empty string is returned. -// -// Example: -// -// Input Output -// ----- ------ -// "a7to11.txt" "a7to12.txt" (typical case. Last digit incremented) -// "a7to99.txt" "a8to00.txt" (last digit incremented, with carry.) -// "a9to99.txt" "a0to00.txt" (wrap around) -// "cat.txt" " " (no digits to increment) -// " " STOP! (error) -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 22 November 2011 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input/output, string *FILENAME, the filename to be incremented. -// - { - char c; - int change; - int i; - int lens; - - lens = (*filename).length(); - - if (lens <= 0) { - cerr << "\n"; - cerr << "FILENAME_INC - Fatal error!\n"; - cerr << " The input string is empty.\n"; - exit(1); - } - - change = 0; - - for (i = lens - 1; 0 <= i; i--) { - c = (*filename)[i]; - - if ('0' <= c && c <= '9') { - change = change + 1; - - if (c == '9') { - c = '0'; - (*filename)[i] = c; - } else { - c = c + 1; - (*filename)[i] = c; - return; - } - } - } -// -// No digits were found. Return blank. -// - if (change == 0) { - for (i = lens - 1; 0 <= i; i--) { - (*filename)[i] = ' '; - } - } - - return; - } -//****************************************************************************80 - - string i4_to_string(int i4) - -//****************************************************************************80 -// -// Purpose: -// -// I4_TO_STRING converts an I4 to a C++ string. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 16 January 2013 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input, int I4, an integer. -// -// Input, string FORMAT, the format string. -// -// Output, string I4_TO_STRING, the string. -// - { - ostringstream fred; - string value; - - fred << i4; - - value = fred.str(); - - return value; - } -//****************************************************************************80 - - int padua_order(int l) - -//****************************************************************************80 -// -// Purpose: -// -// PADUA_ORDER returns the size of the Padua set of given level. -// -// Discussion: -// -// The Padua sets are indexed by a level that starts at 0. -// This function returns the number of points in each level. -// -// Example: -// -// Level Size -// ----- ---- -// 0 1 -// 1 3 -// 2 6 -// 3 10 -// 4 15 -// 5 21 -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 07 April 2014 -// -// Author: -// -// John Burkardt -// -// Reference: -// -// Marco Caliari, Stefano de Marchi, Marco Vianello, -// Bivariate interpolation on the square at new nodal sets, -// Applied Mathematics and Computation, -// Volume 165, Number 2, 2005, pages 261-274. -// -// Parameters: -// -// Input, int L, the level of the set. -// 0 <= L -// -// Output, int PADUA_ORDER, the order (number of points) in the set. -// - { - int i; - int n; - - n = 0; - for (i = 0; i <= l; i++) { - n = n + (l / 2) + 1; - if ((l % 2) == 1 && (i % 2) == 1) { - n = n + 1; - } - } - - return n; - } -//****************************************************************************80 - - void padua_plot(int l, string filename) - -//****************************************************************************80 -// -// Purpose: -// -// PADUA_PLOT plots the Padua points of given level. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 09 June 2014 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input, int L, the level of the set. -// 0 <= L -// -// Input, string FILENAME, a common filename prefix for -// the files to be created. -// - { - string command_filename; - ofstream command_unit; - string data_filename; - ofstream data_unit; - int j; - int n; - string plot_filename; - double *xy; - - n = padua_order(l); - - xy = padua_points(l); -// -// Create graphics data file. -// - data_filename = filename + "_data.txt"; - data_unit.open(data_filename.c_str()); - for (j = 0; j < n; j++) { - data_unit << " " << setprecision(16) << xy[0 + j * 2] - << " " << setprecision(16) << xy[1 + j * 2] << "\n"; - } - data_unit.close(); - cout << "\n"; - cout << " Created data file '" << data_filename << "'.\n"; -// -// Create graphics command file. -// - command_filename = filename + "_commands.txt"; - command_unit.open(command_filename.c_str()); - command_unit << "# " << command_filename << "\n"; - command_unit << "#\n"; - command_unit << "# Usage:\n"; - command_unit << "# gnuplot < " << command_filename << "\n"; - command_unit << "#\n"; - command_unit << "set term png\n"; - plot_filename = filename + ".png"; - command_unit << "set output '" << plot_filename << "'\n"; - command_unit << "set xlabel '<--- X --->'\n"; - command_unit << "set ylabel '<--- Y --->'\n"; - command_unit << "set title 'Padua Points, Level " << l << "\n"; - command_unit << "set grid\n"; - command_unit << "set key off\n"; - command_unit << "set size ratio -1\n"; - command_unit << "set style data lines\n"; - command_unit << "set timestamp\n"; - command_unit << "plot [-1:+1] [-1:+1] '" << data_filename - << "' using 1:2 with points lt 3 pt 3\n"; - command_unit.close(); - - cout << " Created command file '" << command_filename << "'.\n"; -// -// Free memory. -// - delete[] xy; - - return; - } -//****************************************************************************80 - - double *padua_points(int l) - -//****************************************************************************80 -// -// Purpose: -// -// PADUA_POINTS returns the Padua points of level L. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 09 June 2014 -// -// Author: -// -// John Burkardt -// -// Reference: -// -// Marco Caliari, Stefano de Marchi, Marco Vianello, -// Bivariate interpolation on the square at new nodal sets, -// Applied Mathematics and Computation, -// Volume 165, Number 2, 2005, pages 261-274. -// -// Parameters: -// -// Input, int L, the level of the set. -// 0 <= L -// -// Output, double PADUA_POINTS[2*((L+1)*(L+2))/2)], the Padua points. -// - { - double angle1; - double angle2; - int i; - int j; - int j_hi; - int k; - int n; - const double r8_pi = 3.141592653589793; - double *xy; - - n = ((l + 1) * (l + 2)) / 2; - xy = new double[2 * n]; - - if (l == 0) { - xy[0 + 0 * 2] = 0.0; - xy[1 + 0 * 2] = 0.0; - return xy; - } - - k = 0; - - for (i = 0; i <= l; i++) { - j_hi = (l / 2) + 1; - if ((l % 2) == 1 && (i % 2) == 1) { - j_hi = j_hi + 1; - } - - for (j = 1; j <= j_hi; j++) { - if (i * 2 == l) { - xy[0 + k * 2] = 0.0; - } else { - angle1 = (double) (i) * r8_pi / (double) (l); - xy[0 + k * 2] = cos(angle1); - } - - if ((i % 2) == 0) { - if (2 * (2 * j - 1) == l + 1) { - xy[1 + k * 2] = 0.0; - } else { - angle2 = (double) (2 * j - 1) * r8_pi / (double) (l + 1); - xy[1 + k * 2] = cos(angle2); - } - } else { - if (2 * (2 * j - 2) == l + 1) { - xy[1 + k * 2] = 0.0; - } else { - angle2 = (double) (2 * j - 2) * r8_pi / (double) (l + 1); - xy[1 + k * 2] = cos(angle2); - } - } - k = k + 1; - } - } - - return xy; - } -//****************************************************************************80 - - double *padua_points_set(int l) - -//****************************************************************************80 -// -// Purpose: -// -// PADUA_POINTS_SET sets the Padua points. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 30 August 2016 -// -// Author: -// -// John Burkardt -// -// Reference: -// -// Marco Caliari, Stefano de Marchi, Marco Vianello, -// Bivariate interpolation on the square at new nodal sets, -// Applied Mathematics and Computation, -// Volume 165, Number 2, 2005, pages 261-274. -// -// Parameters: -// -// Input, int L, the level. -// 0 <= L <= 10. -// -// Output, double PADUA_POINTS_SET[2*N], the Padua points. -// - { - int j1; - int j1_hi; - int j2; - int n; - double t; - double *xy; - double xy00[2 * 1] = { - 0.000000000000000, 0.000000000000000}; - double xy01[2 * 3] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, 1.000000000000000, - 1.000000000000000, 0.000000000000000}; - double xy02[2 * 6] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, 0.5000000000000001, - 0.000000000000000, -0.4999999999999998, - 0.000000000000000, 1.000000000000000, - 1.000000000000000, -1.000000000000000, - 1.000000000000000, 0.5000000000000001}; - double xy03[2 * 10] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, 0.000000000000000, - -1.000000000000000, 1.000000000000000, - -0.4999999999999998, -0.7071067811865475, - -0.4999999999999998, 0.7071067811865476, - 0.5000000000000001, -1.000000000000000, - 0.5000000000000001, 0.000000000000000, - 0.5000000000000001, 1.000000000000000, - 1.000000000000000, -0.7071067811865475, - 1.000000000000000, 0.7071067811865476}; - double xy04[2 * 15] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, -0.3090169943749473, - -1.000000000000000, 0.8090169943749475, - -0.7071067811865475, -0.8090169943749473, - -0.7071067811865475, 0.3090169943749475, - -0.7071067811865475, 1.000000000000000, - 0.000000000000000, -1.000000000000000, - 0.000000000000000, -0.3090169943749473, - 0.000000000000000, 0.8090169943749475, - 0.7071067811865476, -0.8090169943749473, - 0.7071067811865476, 0.3090169943749475, - 0.7071067811865476, 1.000000000000000, - 1.000000000000000, -1.000000000000000, - 1.000000000000000, -0.3090169943749473, - 1.000000000000000, 0.8090169943749475}; - double xy05[2 * 21] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, -0.4999999999999998, - -1.000000000000000, 0.5000000000000001, - -1.000000000000000, 1.000000000000000, - -0.8090169943749473, -0.8660254037844387, - -0.8090169943749473, 0.000000000000000, - -0.8090169943749473, 0.8660254037844387, - -0.3090169943749473, -1.000000000000000, - -0.3090169943749473, -0.4999999999999998, - -0.3090169943749473, 0.5000000000000001, - -0.3090169943749473, 1.000000000000000, - 0.3090169943749475, -0.8660254037844387, - 0.3090169943749475, 0.000000000000000, - 0.3090169943749475, 0.8660254037844387, - 0.8090169943749475, -1.000000000000000, - 0.8090169943749475, -0.4999999999999998, - 0.8090169943749475, 0.5000000000000001, - 0.8090169943749475, 1.000000000000000, - 1.000000000000000, -0.8660254037844387, - 1.000000000000000, 0.000000000000000, - 1.000000000000000, 0.8660254037844387}; - double xy06[2 * 28] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, -0.6234898018587335, - -1.000000000000000, 0.2225209339563144, - -1.000000000000000, 0.9009688679024191, - -0.8660254037844387, -0.9009688679024190, - -0.8660254037844387, -0.2225209339563143, - -0.8660254037844387, 0.6234898018587336, - -0.8660254037844387, 1.000000000000000, - -0.4999999999999998, -1.000000000000000, - -0.4999999999999998, -0.6234898018587335, - -0.4999999999999998, 0.2225209339563144, - -0.4999999999999998, 0.9009688679024191, - 0.000000000000000, -0.9009688679024190, - 0.000000000000000, -0.2225209339563143, - 0.000000000000000, 0.6234898018587336, - 0.000000000000000, 1.000000000000000, - 0.5000000000000001, -1.000000000000000, - 0.5000000000000001, -0.6234898018587335, - 0.5000000000000001, 0.2225209339563144, - 0.5000000000000001, 0.9009688679024191, - 0.8660254037844387, -0.9009688679024190, - 0.8660254037844387, -0.2225209339563143, - 0.8660254037844387, 0.6234898018587336, - 0.8660254037844387, 1.000000000000000, - 1.000000000000000, -1.000000000000000, - 1.000000000000000, -0.6234898018587335, - 1.000000000000000, 0.2225209339563144, - 1.000000000000000, 0.9009688679024191}; - double xy07[2 * 36] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, -0.7071067811865475, - -1.000000000000000, 0.000000000000000, - -1.000000000000000, 0.7071067811865476, - -1.000000000000000, 1.000000000000000, - -0.9009688679024190, -0.9238795325112867, - -0.9009688679024190, -0.3826834323650897, - -0.9009688679024190, 0.3826834323650898, - -0.9009688679024190, 0.9238795325112867, - -0.6234898018587335, -1.000000000000000, - -0.6234898018587335, -0.7071067811865475, - -0.6234898018587335, 0.000000000000000, - -0.6234898018587335, 0.7071067811865476, - -0.6234898018587335, 1.000000000000000, - -0.2225209339563143, -0.9238795325112867, - -0.2225209339563143, -0.3826834323650897, - -0.2225209339563143, 0.3826834323650898, - -0.2225209339563143, 0.9238795325112867, - 0.2225209339563144, -1.000000000000000, - 0.2225209339563144, -0.7071067811865475, - 0.2225209339563144, 0.000000000000000, - 0.2225209339563144, 0.7071067811865476, - 0.2225209339563144, 1.000000000000000, - 0.6234898018587336, -0.9238795325112867, - 0.6234898018587336, -0.3826834323650897, - 0.6234898018587336, 0.3826834323650898, - 0.6234898018587336, 0.9238795325112867, - 0.9009688679024191, -1.000000000000000, - 0.9009688679024191, -0.7071067811865475, - 0.9009688679024191, 0.000000000000000, - 0.9009688679024191, 0.7071067811865476, - 0.9009688679024191, 1.000000000000000, - 1.000000000000000, -0.9238795325112867, - 1.000000000000000, -0.3826834323650897, - 1.000000000000000, 0.3826834323650898, - 1.000000000000000, 0.9238795325112867}; - double xy08[2 * 45] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, -0.7660444431189779, - -1.000000000000000, -0.1736481776669303, - -1.000000000000000, 0.5000000000000001, - -1.000000000000000, 0.9396926207859084, - -0.9238795325112867, -0.9396926207859083, - -0.9238795325112867, -0.4999999999999998, - -0.9238795325112867, 0.1736481776669304, - -0.9238795325112867, 0.7660444431189780, - -0.9238795325112867, 1.000000000000000, - -0.7071067811865475, -1.000000000000000, - -0.7071067811865475, -0.7660444431189779, - -0.7071067811865475, -0.1736481776669303, - -0.7071067811865475, 0.5000000000000001, - -0.7071067811865475, 0.9396926207859084, - -0.3826834323650897, -0.9396926207859083, - -0.3826834323650897, -0.4999999999999998, - -0.3826834323650897, 0.1736481776669304, - -0.3826834323650897, 0.7660444431189780, - -0.3826834323650897, 1.000000000000000, - 0.000000000000000, -1.000000000000000, - 0.000000000000000, -0.7660444431189779, - 0.000000000000000, -0.1736481776669303, - 0.000000000000000, 0.5000000000000001, - 0.000000000000000, 0.9396926207859084, - 0.3826834323650898, -0.9396926207859083, - 0.3826834323650898, -0.4999999999999998, - 0.3826834323650898, 0.1736481776669304, - 0.3826834323650898, 0.7660444431189780, - 0.3826834323650898, 1.000000000000000, - 0.7071067811865476, -1.000000000000000, - 0.7071067811865476, -0.7660444431189779, - 0.7071067811865476, -0.1736481776669303, - 0.7071067811865476, 0.5000000000000001, - 0.7071067811865476, 0.9396926207859084, - 0.9238795325112867, -0.9396926207859083, - 0.9238795325112867, -0.4999999999999998, - 0.9238795325112867, 0.1736481776669304, - 0.9238795325112867, 0.7660444431189780, - 0.9238795325112867, 1.000000000000000, - 1.000000000000000, -1.000000000000000, - 1.000000000000000, -0.7660444431189779, - 1.000000000000000, -0.1736481776669303, - 1.000000000000000, 0.5000000000000001, - 1.000000000000000, 0.9396926207859084}; - double xy09[2 * 55] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, -0.8090169943749473, - -1.000000000000000, -0.3090169943749473, - -1.000000000000000, 0.3090169943749475, - -1.000000000000000, 0.8090169943749475, - -1.000000000000000, 1.000000000000000, - -0.9396926207859083, -0.9510565162951535, - -0.9396926207859083, -0.5877852522924730, - -0.9396926207859083, 0.000000000000000, - -0.9396926207859083, 0.5877852522924731, - -0.9396926207859083, 0.9510565162951535, - -0.7660444431189779, -1.000000000000000, - -0.7660444431189779, -0.8090169943749473, - -0.7660444431189779, -0.3090169943749473, - -0.7660444431189779, 0.3090169943749475, - -0.7660444431189779, 0.8090169943749475, - -0.7660444431189779, 1.000000000000000, - -0.4999999999999998, -0.9510565162951535, - -0.4999999999999998, -0.5877852522924730, - -0.4999999999999998, 0.000000000000000, - -0.4999999999999998, 0.5877852522924731, - -0.4999999999999998, 0.9510565162951535, - -0.1736481776669303, -1.000000000000000, - -0.1736481776669303, -0.8090169943749473, - -0.1736481776669303, -0.3090169943749473, - -0.1736481776669303, 0.3090169943749475, - -0.1736481776669303, 0.8090169943749475, - -0.1736481776669303, 1.000000000000000, - 0.1736481776669304, -0.9510565162951535, - 0.1736481776669304, -0.5877852522924730, - 0.1736481776669304, 0.000000000000000, - 0.1736481776669304, 0.5877852522924731, - 0.1736481776669304, 0.9510565162951535, - 0.5000000000000001, -1.000000000000000, - 0.5000000000000001, -0.8090169943749473, - 0.5000000000000001, -0.3090169943749473, - 0.5000000000000001, 0.3090169943749475, - 0.5000000000000001, 0.8090169943749475, - 0.5000000000000001, 1.000000000000000, - 0.7660444431189780, -0.9510565162951535, - 0.7660444431189780, -0.5877852522924730, - 0.7660444431189780, 0.000000000000000, - 0.7660444431189780, 0.5877852522924731, - 0.7660444431189780, 0.9510565162951535, - 0.9396926207859084, -1.000000000000000, - 0.9396926207859084, -0.8090169943749473, - 0.9396926207859084, -0.3090169943749473, - 0.9396926207859084, 0.3090169943749475, - 0.9396926207859084, 0.8090169943749475, - 0.9396926207859084, 1.000000000000000, - 1.000000000000000, -0.9510565162951535, - 1.000000000000000, -0.5877852522924730, - 1.000000000000000, 0.000000000000000, - 1.000000000000000, 0.5877852522924731, - 1.000000000000000, 0.9510565162951535}; - double xy10[2 * 66] = { - -1.000000000000000, -1.000000000000000, - -1.000000000000000, -0.8412535328311811, - -1.000000000000000, -0.4154150130018863, - -1.000000000000000, 0.1423148382732851, - -1.000000000000000, 0.6548607339452851, - -1.000000000000000, 0.9594929736144974, - -0.9510565162951535, -0.9594929736144974, - -0.9510565162951535, -0.6548607339452850, - -0.9510565162951535, -0.1423148382732850, - -0.9510565162951535, 0.4154150130018864, - -0.9510565162951535, 0.8412535328311812, - -0.9510565162951535, 1.000000000000000, - -0.8090169943749473, -1.000000000000000, - -0.8090169943749473, -0.8412535328311811, - -0.8090169943749473, -0.4154150130018863, - -0.8090169943749473, 0.1423148382732851, - -0.8090169943749473, 0.6548607339452851, - -0.8090169943749473, 0.9594929736144974, - -0.5877852522924730, -0.9594929736144974, - -0.5877852522924730, -0.6548607339452850, - -0.5877852522924730, -0.1423148382732850, - -0.5877852522924730, 0.4154150130018864, - -0.5877852522924730, 0.8412535328311812, - -0.5877852522924730, 1.000000000000000, - -0.3090169943749473, -1.000000000000000, - -0.3090169943749473, -0.8412535328311811, - -0.3090169943749473, -0.4154150130018863, - -0.3090169943749473, 0.1423148382732851, - -0.3090169943749473, 0.6548607339452851, - -0.3090169943749473, 0.9594929736144974, - 0.000000000000000, -0.9594929736144974, - 0.000000000000000, -0.6548607339452850, - 0.000000000000000, -0.1423148382732850, - 0.000000000000000, 0.4154150130018864, - 0.000000000000000, 0.8412535328311812, - 0.000000000000000, 1.000000000000000, - 0.3090169943749475, -1.000000000000000, - 0.3090169943749475, -0.8412535328311811, - 0.3090169943749475, -0.4154150130018863, - 0.3090169943749475, 0.1423148382732851, - 0.3090169943749475, 0.6548607339452851, - 0.3090169943749475, 0.9594929736144974, - 0.5877852522924731, -0.9594929736144974, - 0.5877852522924731, -0.6548607339452850, - 0.5877852522924731, -0.1423148382732850, - 0.5877852522924731, 0.4154150130018864, - 0.5877852522924731, 0.8412535328311812, - 0.5877852522924731, 1.000000000000000, - 0.8090169943749475, -1.000000000000000, - 0.8090169943749475, -0.8412535328311811, - 0.8090169943749475, -0.4154150130018863, - 0.8090169943749475, 0.1423148382732851, - 0.8090169943749475, 0.6548607339452851, - 0.8090169943749475, 0.9594929736144974, - 0.9510565162951535, -0.9594929736144974, - 0.9510565162951535, -0.6548607339452850, - 0.9510565162951535, -0.1423148382732850, - 0.9510565162951535, 0.4154150130018864, - 0.9510565162951535, 0.8412535328311812, - 0.9510565162951535, 1.000000000000000, - 1.000000000000000, -1.000000000000000, - 1.000000000000000, -0.8412535328311811, - 1.000000000000000, -0.4154150130018863, - 1.000000000000000, 0.1423148382732851, - 1.000000000000000, 0.6548607339452851, - 1.000000000000000, 0.9594929736144974}; - - n = ((l + 1) * (l + 2)) / 2; - - if (l == 0) { - xy = r8vec_copy_new(2 * n, xy00); - } else if (l == 1) { - xy = r8vec_copy_new(2 * n, xy01); - } else if (l == 2) { - xy = r8vec_copy_new(2 * n, xy02); - } else if (l == 3) { - xy = r8vec_copy_new(2 * n, xy03); - } else if (l == 4) { - xy = r8vec_copy_new(2 * n, xy04); - } else if (l == 5) { - xy = r8vec_copy_new(2 * n, xy05); - } else if (l == 6) { - xy = r8vec_copy_new(2 * n, xy06); - } else if (l == 7) { - xy = r8vec_copy_new(2 * n, xy07); - } else if (l == 8) { - xy = r8vec_copy_new(2 * n, xy08); - } else if (l == 9) { - xy = r8vec_copy_new(2 * n, xy09); - } else if (l == 10) { - xy = r8vec_copy_new(2 * n, xy10); - } else { - cerr << "\n"; - cerr << "PADUA_POINTS_SET - Fatal error!\n"; - cerr << " Illegal value of L = " << l << "\n"; - cerr << " Legal values are 1 through 10.\n"; - exit(1); - } -// -// Reverse data to match published information. -// - j1_hi = (n - 1) / 2; - - for (j1 = 0; j1 < j1_hi; j1++) { - j2 = n - 1 - j1; - t = xy[0 + 2 * j1]; - xy[0 + 2 * j1] = xy[0 + 2 * j2]; - xy[0 + 2 * j2] = t; - t = xy[1 + 2 * j1]; - xy[1 + 2 * j1] = xy[1 + 2 * j2]; - xy[1 + 2 * j2] = t; - } - - return xy; - } -//****************************************************************************80 - - double *padua_weights_set(int l) - -//****************************************************************************80 -// -// Purpose: -// -// PADUA_WEIGHTS_SET sets quadrature weights for the Padua points. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 11 June 2014 -// -// Author: -// -// John Burkardt -// -// Reference: -// -// Marco Caliari, Stefano de Marchi, Marco Vianello, -// Bivariate interpolation on the square at new nodal sets, -// Applied Mathematics and Computation, -// Volume 165, Number 2, 2005, pages 261-274. -// -// Parameters: -// -// Input, int L, the level. -// 0 <= L <= 10. -// -// Output, double PADUA_WEIGHTS_SET[N], the quadrature weights. -// - { - int n; - double *w; - - n = ((l + 1) * (l + 2)) / 2; - w = new double[n]; - - if (l == 0) { - w[0] = 4.000000000000000E+00; - } else if (l == 1) { - w[0] = 1.000000000000000E+00; - w[1] = 1.000000000000000E+00; - w[2] = 2.000000000000000E+00; - } else if (l == 2) { - w[0] = 0.0E+00; - w[1] = 0.6666666666666663E+00; - w[2] = 2.222222222222222E+00; - w[3] = 0.4444444444444444E+00; - w[4] = 0.0E+00; - w[5] = 0.6666666666666664E+00; - } else if (l == 3) { - w[0] = -0.5555555555555480E-01; - w[1] = 0.3333333333333331E+00; - w[2] = -0.5555555555555580E-01; - w[3] = 0.8888888888888886E+00; - w[4] = 0.8888888888888893E+00; - w[5] = 0.2222222222222224E+00; - w[6] = 1.333333333333333E+00; - w[7] = 0.2222222222222220E+00; - w[8] = 0.1111111111111109E+00; - w[9] = 0.1111111111111112E+00; - } else if (l == 4) { - w[0] = -0.8888888888888932E-02; - w[1] = 0.8104919101110961E-01; - w[2] = 0.6117303121111219E-01; - w[3] = 0.3874097078666789E+00; - w[4] = 0.6259236254666545E+00; - w[5] = 0.5333333333333362E-01; - w[6] = 0.7111111111111067E-01; - w[7] = 0.9830822022444241E+00; - w[8] = 0.5458066866444642E+00; - w[9] = 0.3874097078666780E+00; - w[10] = 0.6259236254666568E+00; - w[11] = 0.5333333333333383E-01; - w[12] = -0.8888888888888703E-02; - w[13] = 0.8104919101110968E-01; - w[14] = 0.6117303121111135E-01; - } else if (l == 5) { - w[0] = -0.1037037037037093E-01; - w[1] = 0.5037037037036911E-01; - w[2] = 0.5037037037037081E-01; - w[3] = -0.1037037037036947E-01; - w[4] = 0.1876963678740801E+00; - w[5] = 0.3460933466518654E+00; - w[6] = 0.1876963678740763E+00; - w[7] = 0.4514390511851724E-01; - w[8] = 0.5541130536814713E+00; - w[9] = 0.5541130536814728E+00; - w[10] = 0.4514390511851834E-01; - w[11] = 0.2804517802740705E+00; - w[12] = 0.6376103570518378E+00; - w[13] = 0.2804517802740683E+00; - w[14] = 0.3189313191851883E-01; - w[15] = 0.3288499092814910E+00; - w[16] = 0.3288499092814925E+00; - w[17] = 0.3189313191851956E-01; - w[18] = 0.2074074074074123E-01; - w[19] = 0.3851851851851849E-01; - w[20] = 0.2074074074074051E-01; - } else if (l == 6) { - w[0] = -0.3023431594858565E-02; - w[1] = 0.1957267632451884E-01; - w[2] = 0.2633929313290840E-01; - w[3] = 0.1425431928029237E-01; - w[4] = 0.1006383046329639E+00; - w[5] = 0.2208900184526934E+00; - w[6] = 0.1743144584714012E+00; - w[7] = 0.1209372637943976E-01; - w[8] = 0.1934996220710680E-01; - w[9] = 0.3245064820875231E+00; - w[10] = 0.4027058473592984E+00; - w[11] = 0.1677234226317961E+00; - w[12] = 0.1953319357827178E+00; - w[13] = 0.4489633053035124E+00; - w[14] = 0.3721824611057551E+00; - w[15] = 0.2479213907785274E-01; - w[16] = 0.1934996220710561E-01; - w[17] = 0.3245064820875153E+00; - w[18] = 0.4027058473592959E+00; - w[19] = 0.1677234226317944E+00; - w[20] = 0.1006383046329745E+00; - w[21] = 0.2208900184526933E+00; - w[22] = 0.1743144584714027E+00; - w[23] = 0.1209372637944051E-01; - w[24] = -0.3023431594861990E-02; - w[25] = 0.1957267632451757E-01; - w[26] = 0.2633929313290797E-01; - w[27] = 0.1425431928029198E-01; - } else if (l == 7) { - w[0] = -0.3287981859413765E-02; - w[1] = 0.1337868480725671E-01; - w[2] = 0.2063492063491996E-01; - w[3] = 0.1337868480725546E-01; - w[4] = -0.3287981859408898E-02; - w[5] = 0.5949324721885513E-01; - w[6] = 0.1306477599993571E+00; - w[7] = 0.1306477599993581E+00; - w[8] = 0.5949324721885061E-01; - w[9] = 0.1263869091685831E-01; - w[10] = 0.1979944935601103E+00; - w[11] = 0.2832184784823740E+00; - w[12] = 0.1979944935601143E+00; - w[13] = 0.1263869091685747E-01; - w[14] = 0.1221817987389771E+00; - w[15] = 0.3150266070593529E+00; - w[16] = 0.3150266070593440E+00; - w[17] = 0.1221817987389802E+00; - w[18] = 0.1771365352315134E-01; - w[19] = 0.2490926964598258E+00; - w[20] = 0.3408041116306980E+00; - w[21] = 0.2490926964598291E+00; - w[22] = 0.1771365352314976E-01; - w[23] = 0.9646986307476696E-01; - w[24] = 0.2557725606433917E+00; - w[25] = 0.2557725606433927E+00; - w[26] = 0.9646986307476431E-01; - w[27] = 0.8649923133686802E-02; - w[28] = 0.1062007918394705E+00; - w[29] = 0.1505805844901012E+00; - w[30] = 0.1062007918394705E+00; - w[31] = 0.8649923133690016E-02; - w[32] = 0.6355881462931014E-02; - w[33] = 0.1405228180237514E-01; - w[34] = 0.1405228180237651E-01; - w[35] = 0.6355881462928496E-02; - } else if (l == 8) { - w[0] = -0.1269841269835311E-02; - w[1] = 0.6706089639041270E-02; - w[2] = 0.1111455441352989E-01; - w[3] = 0.1026455026455282E-01; - w[4] = 0.4930678698742625E-02; - w[5] = 0.3633146869162523E-01; - w[6] = 0.8838322767333079E-01; - w[7] = 0.9965911758463214E-01; - w[8] = 0.6400185533755555E-01; - w[9] = 0.4061629144893127E-02; - w[10] = 0.6772486772485166E-02; - w[11] = 0.1258344472781388E+00; - w[12] = 0.1927501398511116E+00; - w[13] = 0.1699470899470907E+00; - w[14] = 0.6342599488133535E-01; - w[15] = 0.8376332474107638E-01; - w[16] = 0.2170841444607031E+00; - w[17] = 0.2477307250801775E+00; - w[18] = 0.1648098048612226E+00; - w[19] = 0.1004771829779292E-01; - w[20] = 0.1015873015872910E-01; - w[21] = 0.1784328991205164E+00; - w[22] = 0.2729409493576765E+00; - w[23] = 0.2364021164021134E+00; - w[24] = 0.8936689226256009E-01; - w[25] = 0.8376332474107701E-01; - w[26] = 0.2170841444607054E+00; - w[27] = 0.2477307250801761E+00; - w[28] = 0.1648098048612200E+00; - w[29] = 0.1004771829779330E-01; - w[30] = 0.6772486772485237E-02; - w[31] = 0.1258344472781358E+00; - w[32] = 0.1927501398511135E+00; - w[33] = 0.1699470899470926E+00; - w[34] = 0.6342599488133838E-01; - w[35] = 0.3633146869162453E-01; - w[36] = 0.8838322767332588E-01; - w[37] = 0.9965911758463601E-01; - w[38] = 0.6400185533755502E-01; - w[39] = 0.4061629144888279E-02; - w[40] = -0.1269841269836355E-02; - w[41] = 0.6706089639046927E-02; - w[42] = 0.1111455441352761E-01; - w[43] = 0.1026455026454956E-01; - w[44] = 0.4930678698747173E-02; - } else if (l == 9) { - w[0] = -0.1368606701945113E-02; - w[1] = 0.4837977417140975E-02; - w[2] = 0.8876308297144902E-02; - w[3] = 0.8876308297143068E-02; - w[4] = 0.4837977417150492E-02; - w[5] = -0.1368606701935084E-02; - w[6] = 0.2425285860992349E-01; - w[7] = 0.5727330842923516E-01; - w[8] = 0.7008257906578071E-01; - w[9] = 0.5727330842922034E-01; - w[10] = 0.2425285860989794E-01; - w[11] = 0.4659404339099723E-02; - w[12] = 0.8354521980498550E-01; - w[13] = 0.1370796991940044E+00; - w[14] = 0.1370796991940248E+00; - w[15] = 0.8354521980500107E-01; - w[16] = 0.4659404339109654E-02; - w[17] = 0.5564545640233619E-01; - w[18] = 0.1524391996823315E+00; - w[19] = 0.1877107583774149E+00; - w[20] = 0.1524391996823176E+00; - w[21] = 0.5564545640232402E-01; - w[22] = 0.8186176158691754E-02; - w[23] = 0.1295355639606716E+00; - w[24] = 0.2061407656847711E+00; - w[25] = 0.2061407656847630E+00; - w[26] = 0.1295355639606894E+00; - w[27] = 0.8186176158692687E-02; - w[28] = 0.6234969028097752E-01; - w[29] = 0.1730419031522391E+00; - w[30] = 0.2169418247419051E+00; - w[31] = 0.1730419031522361E+00; - w[32] = 0.6234969028097048E-01; - w[33] = 0.7506172839505762E-02; - w[34] = 0.1142161960569350E+00; - w[35] = 0.1802176663769002E+00; - w[36] = 0.1802176663769038E+00; - w[37] = 0.1142161960569279E+00; - w[38] = 0.7506172839512260E-02; - w[39] = 0.4031900987631698E-01; - w[40] = 0.1142976211857364E+00; - w[41] = 0.1413353845521477E+00; - w[42] = 0.1142976211857414E+00; - w[43] = 0.4031900987631700E-01; - w[44] = 0.3239075586856897E-02; - w[45] = 0.4317587564913915E-01; - w[46] = 0.7015250533601934E-01; - w[47] = 0.7015250533601930E-01; - w[48] = 0.4317587564913908E-01; - w[49] = 0.3239075586852207E-02; - w[50] = 0.2550690557469151E-02; - w[51] = 0.6084230077461027E-02; - w[52] = 0.7421516754852508E-02; - w[53] = 0.6084230077458821E-02; - w[54] = 0.2550690557473353E-02; - } else if (l == 10) { - w[0] = -0.6240762604463766E-03; - w[1] = 0.2843227149025789E-02; - w[2] = 0.5250031948150784E-02; - w[3] = 0.5891746241568810E-02; - w[4] = 0.4705736485964679E-02; - w[5] = 0.2135354637732944E-02; - w[6] = 0.1610939653924566E-01; - w[7] = 0.4099595211758227E-01; - w[8] = 0.5326500934654063E-01; - w[9] = 0.4863338516658277E-01; - w[10] = 0.2843474741781434E-01; - w[11] = 0.1719619179693151E-02; - w[12] = 0.2883769745121509E-02; - w[13] = 0.5724711668876453E-01; - w[14] = 0.9659872841640438E-01; - w[15] = 0.1053210323353631E+00; - w[16] = 0.8066212502628711E-01; - w[17] = 0.2855765663647366E-01; - w[18] = 0.3981286043310814E-01; - w[19] = 0.1090390674981577E+00; - w[20] = 0.1430169021081585E+00; - w[21] = 0.1313686303763064E+00; - w[22] = 0.7932850918298831E-01; - w[23] = 0.4610696968783255E-02; - w[24] = 0.5086495679684716E-02; - w[25] = 0.9311356395361167E-01; - w[26] = 0.1562320334111262E+00; - w[27] = 0.1696057154254139E+00; - w[28] = 0.1283581371975154E+00; - w[29] = 0.4603059518094556E-01; - w[30] = 0.4894888812994630E-01; - w[31] = 0.1347281473526573E+00; - w[32] = 0.1764193542601264E+00; - w[33] = 0.1635037456303485E+00; - w[34] = 0.9822749154565460E-01; - w[35] = 0.5704840613923174E-02; - w[36] = 0.5086495679679268E-02; - w[37] = 0.9311356395362781E-01; - w[38] = 0.1562320334111511E+00; - w[39] = 0.1696057154253968E+00; - w[40] = 0.1283581371975113E+00; - w[41] = 0.4603059518094044E-01; - w[42] = 0.3981286043311782E-01; - w[43] = 0.1090390674981293E+00; - w[44] = 0.1430169021081508E+00; - w[45] = 0.1313686303763217E+00; - w[46] = 0.7932850918299997E-01; - w[47] = 0.4610696968790496E-02; - w[48] = 0.2883769745110260E-02; - w[49] = 0.5724711668875122E-01; - w[50] = 0.9659872841642343E-01; - w[51] = 0.1053210323353932E+00; - w[52] = 0.8066212502626474E-01; - w[53] = 0.2855765663644533E-01; - w[54] = 0.1610939653928420E-01; - w[55] = 0.4099595211758404E-01; - w[56] = 0.5326500934649123E-01; - w[57] = 0.4863338516656233E-01; - w[58] = 0.2843474741784810E-01; - w[59] = 0.1719619179720036E-02; - w[60] = -0.6240762604606350E-03; - w[61] = 0.2843227149011163E-02; - w[62] = 0.5250031948172295E-02; - w[63] = 0.5891746241587802E-02; - w[64] = 0.4705736485965663E-02; - w[65] = 0.2135354637703863E-02; - } else { - cerr << "\n"; - cerr << "PADUA_WEIGHTS_SET - Fatal error\n"; - cerr << " Illegal value of L = " << l << "\n"; - cerr << " Legal values are 0 through 10.\n"; - exit(1); - } -// -// Reverse order to match published data. -// - r8vec_reverse(n, w); - - return w; - } -//****************************************************************************80 - - double *padua_weights(int l) - -//****************************************************************************80 -// -// Purpose: -// -// PADUA_WEIGHTS returns quadrature weights do Padua points. -// -// Discussion: -// -// The order of the weights corresponds to the ordering used -// by the companion function padua_points(). -// -// Caliari, de Marchi and Vianello supplied a MATLAB code pdwtsMM -// which carries out this same computation in a way that makes -// more efficient use of MATLAB's vector and matrix capabilities. -// This version of the computation was painfully rewritten to display -// the individual scalar computations, so that it could be translated -// into other languages. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 11 June 2014 -// -// Author: -// -// John Burkardt -// -// Reference: -// -// Marco Caliari, Stefano de Marchi, Marco Vianello, -// Bivariate interpolation on the square at new nodal sets, -// Applied Mathematics and Computation, -// Volume 165, Number 2, 2005, pages 261-274. -// -// Parameters: -// -// Input, int L, the level of the set. -// 0 <= L -// -// Output, double PADUA_WEIGHTS[(L+1)*(L+2)/2], the quadrature weights. -// - { - double angle; - int i; - int i2; - int j; - int j2; - int lp1h; - int lp2h; - int lp3h; - double mi; - double mj; - double *mom; - int n; - const double r8_pi = 3.141592653589793; - double *te1; - double *te2; - double *tmteo; - double *tmtoe; - double *to1; - double *to2; - double *w; - double *w1; - double *w2; - - n = ((l + 1) * (l + 2)) / 2; - w = new double[n]; - - if (l == 0) { - w[0] = 4.0; - return w; - } -// -// Relatives of L/2: -// - lp1h = (l + 1) / 2; - lp2h = (l + 2) / 2; - lp3h = (l + 3) / 2; -// -// TE1, TE2, TO1, TO2: -// Even and odd Chebyshev polynomials on subgrids 1 and 2. -// - te1 = new double[lp2h * lp2h]; - - for (j = 0; j < lp2h; j++) { - for (i = 0; i < lp2h; i++) { - angle = r8_pi * (double) (2 * i * 2 * j) / (double) (l); - te1[i + j * lp2h] = cos(angle); - } - } - - for (j = 0; j < lp2h; j++) { - for (i = 1; i < lp2h; i++) { - te1[i + j * lp2h] = te1[i + j * lp2h] * sqrt(2.0); - } - } - - to1 = new double[lp2h * lp1h]; - - for (j = 0; j < lp1h; j++) { - for (i = 0; i < lp2h; i++) { - angle = r8_pi * (double) (2 * i * (2 * j + 1)) - / (double) (l); - to1[i + j * lp2h] = cos(angle); - } - } - - for (j = 0; j < lp1h; j++) { - for (i = 1; i < lp2h; i++) { - to1[i + j * lp2h] = to1[i + j * lp2h] * sqrt(2.0); - } - } - - te2 = new double[lp2h * lp3h]; - - for (j = 0; j < lp3h; j++) { - for (i = 0; i < lp2h; i++) { - angle = r8_pi * (double) (2 * i * 2 * j) / (double) (l + 1); - te2[i + j * lp2h] = cos(angle); - } - } - - for (j = 0; j < lp3h; j++) { - for (i = 1; i < lp2h; i++) { - te2[i + j * lp2h] = te2[i + j * lp2h] * sqrt(2.0); - } - } - - to2 = new double[lp2h * lp2h]; - - for (j = 0; j < lp2h; j++) { - for (i = 0; i < lp2h; i++) { - angle = r8_pi * (double) (2 * i * (2 * j + 1)) - / (double) (l + 1); - to2[i + j * lp2h] = cos(angle); - } - } - - for (j = 0; j < lp2h; j++) { - for (i = 1; i < lp2h; i++) { - to2[i + j * lp2h] = to2[i + j * lp2h] * sqrt(2.0); - } - } -// -// MOM: Moments matrix do even * even pairs. -// - mom = new double[lp2h * lp2h]; - - for (j = 0; j < lp2h; j++) { - mj = 2.0 * sqrt(2.0) / (double) (1 - pow(2 * j, 2)); - for (i = 0; i < lp2h - j; i++) { - mi = 2.0 * sqrt(2.0) / (double) (1 - pow(2 * i, 2)); - mom[i + j * lp2h] = mi * mj; - } - } - - i = 0; - for (j = 0; j < lp2h; j++) { - mom[i + j * lp2h] = mom[i + j * lp2h] / sqrt(2.0); - } - - j = 0; - for (i = 0; i < lp2h; i++) { - mom[i + j * lp2h] = mom[i + j * lp2h] / sqrt(2.0); - } - if ((l % 2) == 0) { - i = lp2h - 1; - j = 0; - mom[i + j * lp2h] = mom[i + j * lp2h] / 2.0; - } -// -// TMTOE and TMTEO: matrix products. -// - tmtoe = new double[lp2h * lp2h]; - - for (j = 0; j < lp2h; j++) { - for (i = 0; i < lp2h; i++) { - tmtoe[i + j * lp2h] = 0.0; - } - } - - for (j2 = 0; j2 < lp2h; j2++) { - for (i2 = 0; i2 < lp2h - j2; i2++) { - for (j = 0; j < lp2h; j++) { - for (i = 0; i < lp2h; i++) { - tmtoe[i + j * lp2h] = tmtoe[i + j * lp2h] - + to2[i2 + i * lp2h] * mom[j2 + i2 * lp2h] * te1[j2 + j * lp2h]; - } - } - } - } - - tmteo = new double[lp3h * lp1h]; - - for (j = 0; j < lp1h; j++) { - for (i = 0; i < lp3h; i++) { - tmteo[i + j * lp3h] = 0.0; - } - } - - for (j2 = 0; j2 < lp2h; j2++) { - for (i2 = 0; i2 < lp2h - j2; i2++) { - for (j = 0; j < lp1h; j++) { - for (i = 0; i < lp3h; i++) { - tmteo[i + j * lp3h] = tmteo[i + j * lp3h] - + te2[i2 + i * lp2h] * mom[j2 + i2 * lp2h] * to1[j2 + j * lp2h]; - } - } - } - } -// -// W1 and W2: Interpolation weight matrices. -// - w1 = new double[lp2h * lp2h]; - - for (j = 0; j < lp2h; j++) { - for (i = 0; i < lp2h; i++) { - w1[i + j * lp2h] = 2.0 / (double) (l * (l + 1)); - } - } - - j = 0; - for (i = 0; i < lp2h; i++) { - w1[i + j * lp2h] = w1[i + j * lp2h] / 2.0; - } - - if ((l % 2) == 0) { - j = lp2h - 1; - for (i = 0; i < lp2h; i++) { - w1[i + j * lp2h] = w1[i + j * lp2h] / 2.0; - } - - i = lp2h - 1; - for (j = 0; j < lp2h; j++) { - w1[i + j * lp2h] = w1[i + j * lp2h] / 2.0; - } - } - - w2 = new double[lp3h * lp1h]; - - for (j = 0; j < lp1h; j++) { - for (i = 0; i < lp3h; i++) { - w2[i + j * lp3h] = 2.0 / (double) (l * (l + 1)); - } - } - - i = 0; - for (j = 0; j < lp1h; j++) { - w2[i + j * lp3h] = w2[i + j * lp3h] / 2.0; - } - - if ((l % 2) == 1) { - i = lp3h - 1; - for (j = 0; j < lp1h; j++) { - w2[i + j * lp3h] = w2[i + j * lp3h] / 2.0; - } - j = lp1h - 1; - for (i = 0; i < lp3h; i++) { - w2[i + j * lp3h] = w2[i + j * lp3h] / 2.0; - } - } -// -// Cubature weights as matrices on the subgrids. -// - for (j = 0; j < lp2h; j++) { - for (i = 0; i < lp2h; i++) { - w1[i + j * lp2h] = w1[i + j * lp2h] * tmtoe[i + j * lp2h]; - } - } - - for (j = 0; j < lp1h; j++) { - for (i = 0; i < lp3h; i++) { - w2[i + j * lp3h] = w2[i + j * lp3h] * tmteo[i + j * lp3h]; - } - } -// -// Pack weight matrices W1 and W2 into the vector W. -// - if ((l % 2) == 0) { - for (j = 0; j < lp2h; j++) { - for (i = 0; i < lp2h; i++) { - w[i + 2 * j * lp2h] = w1[i + j * lp2h]; - } - } - - for (j = 0; j < lp1h; j++) { - for (i = 0; i < lp3h; i++) { - w[i + (2 * j + 1) * lp2h] = w2[i + j * lp3h]; - } - } - } else { - for (j = 0; j < lp1h; j++) { - for (i = 0; i < lp2h; i++) { - w[i + j * (l + 2)] = w1[i + j * lp2h]; - } - } - - for (j = 0; j < lp1h; j++) { - for (i = 0; i < lp3h; i++) { - w[i + lp2h + j * (l + 2)] = w2[i + j * lp3h]; - } - } - - } -// -// Free memory. -// - delete[] te1; - delete[] te2; - delete[] tmteo; - delete[] tmtoe; - delete[] to1; - delete[] to2; - delete[] w1; - delete[] w2; - - return w; - } -//****************************************************************************80 - - double r8_max(double x, double y) - -//****************************************************************************80 -// -// Purpose: -// -// R8_MAX returns the maximum of two R8's. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 18 August 2004 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input, double X, Y, the quantities to compare. -// -// Output, double R8_MAX, the maximum of X and Y. -// - { - double value; - - if (y < x) { - value = x; - } else { - value = y; - } - return value; - } -//****************************************************************************80 - - void r8mat_transpose_print(int m, int n, double a[], string title) - -//****************************************************************************80 -// -// Purpose: -// -// R8MAT_TRANSPOSE_PRINT prints an R8MAT, transposed. -// -// Discussion: -// -// An R8MAT is a doubly dimensioned array of R8 values, stored as a vector -// in column-major order. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 10 September 2009 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input, int M, N, the number of rows and columns. -// -// Input, double A[M*N], an M by N matrix to be printed. -// -// Input, string TITLE, a title. -// - { - r8mat_transpose_print_some(m, n, a, 1, 1, m, n, title); - - return; - } -//****************************************************************************80 - - void r8mat_transpose_print_some(int m, int n, double a[], int ilo, int jlo, - int ihi, int jhi, string title) - -//****************************************************************************80 -// -// Purpose: -// -// R8MAT_TRANSPOSE_PRINT_SOME prints some of an R8MAT, transposed. -// -// Discussion: -// -// An R8MAT is a doubly dimensioned array of R8 values, stored as a vector -// in column-major order. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 07 April 2014 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input, int M, N, the number of rows and columns. -// -// Input, double A[M*N], an M by N matrix to be printed. -// -// Input, int ILO, JLO, the first row and column to print. -// -// Input, int IHI, JHI, the last row and column to print. -// -// Input, string TITLE, a title. -// - { -# define INCX 5 - - int i; - int i2; - int i2hi; - int i2lo; - int i2lo_hi; - int i2lo_lo; - int inc; - int j; - int j2hi; - int j2lo; - - cout << "\n"; - cout << title << "\n"; - - if (m <= 0 || n <= 0) { - cout << "\n"; - cout << " (None)\n"; - return; - } - - if (ilo < 1) { - i2lo_lo = 1; - } else { - i2lo_lo = ilo; - } - - if (ihi < m) { - i2lo_hi = m; - } else { - i2lo_hi = ihi; - } - - for (i2lo = i2lo_lo; i2lo <= i2lo_hi; i2lo = i2lo + INCX) { - i2hi = i2lo + INCX - 1; - - if (m < i2hi) { - i2hi = m; - } - if (ihi < i2hi) { - i2hi = ihi; - } - - inc = i2hi + 1 - i2lo; - - cout << "\n"; - cout << " Row: "; - for (i = i2lo; i <= i2hi; i++) { - cout << setw(7) << i - 1 << " "; - } - cout << "\n"; - cout << " Col\n"; - cout << "\n"; - - if (jlo < 1) { - j2lo = 1; - } else { - j2lo = jlo; - } - if (n < jhi) { - j2hi = n; - } else { - j2hi = jhi; - } - - for (j = j2lo; j <= j2hi; j++) { - cout << setw(5) << j - 1 << ":"; - for (i2 = 1; i2 <= inc; i2++) { - i = i2lo - 1 + i2; - cout << setw(14) << a[(i - 1) + (j - 1) * m]; - } - cout << "\n"; - } - } - - return; -# undef INCX - } -//****************************************************************************80 - - double *r8vec_copy_new(int n, double a1[]) - -//****************************************************************************80 -// -// Purpose: -// -// R8VEC_COPY_NEW copies an R8VEC to a new R8VEC. -// -// Discussion: -// -// An R8VEC is a vector of R8's. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 03 July 2008 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input, int N, the number of entries in the vectors. -// -// Input, double A1[N], the vector to be copied. -// -// Output, double R8VEC_COPY_NEW[N], the copy of A1. -// - { - double *a2; - int i; - - a2 = new double[n]; - - for (i = 0; i < n; i++) { - a2[i] = a1[i]; - } - return a2; - } -//****************************************************************************80 - - void r8vec_print(int n, double a[], string title) - -//****************************************************************************80 -// -// Purpose: -// -// R8VEC_PRINT prints an R8VEC. -// -// Discussion: -// -// An R8VEC is a vector of R8's. -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 16 August 2004 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input, int N, the number of components of the vector. -// -// Input, double A[N], the vector to be printed. -// -// Input, string TITLE, a title. -// - { - int i; - - cout << "\n"; - cout << title << "\n"; - cout << "\n"; - for (i = 0; i < n; i++) { - cout << " " << setw(8) << i - << ": " << setw(14) << a[i] << "\n"; - } - - return; - } -//****************************************************************************80 - - void r8vec_reverse(int n, double a[]) - -//****************************************************************************80 -// -// Purpose: -// -// R8VEC_REVERSE reverses the elements of an R8VEC. -// -// Discussion: -// -// An R8VEC is a vector of R8's. -// -// Example: -// -// Input: -// -// N = 5, A = ( 11.0, 12.0, 13.0, 14.0, 15.0 ). -// -// Output: -// -// A = ( 15.0, 14.0, 13.0, 12.0, 11.0 ). -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 18 September 2005 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// Input, int N, the number of entries in the array. -// -// Input/output, double A[N], the array to be reversed. -// - { - int i; - int i_hi; - double temp; - - i_hi = n / 2; - - for (i = 1; i <= i_hi; i++) { - temp = a[i - 1]; - a[i - 1] = a[n - i]; - a[n - i] = temp; - } - - return; - } -//****************************************************************************80 - - void timestamp() - -//****************************************************************************80 -// -// Purpose: -// -// TIMESTAMP prints the current YMDHMS date as a time stamp. -// -// Example: -// -// 31 May 2001 09:45:54 AM -// -// Licensing: -// -// This code is distributed under the GNU LGPL license. -// -// Modified: -// -// 08 July 2009 -// -// Author: -// -// John Burkardt -// -// Parameters: -// -// None -// - { -# define TIME_SIZE 40 - - static char time_buffer[TIME_SIZE]; - const struct std::tm *tm_ptr; - std::time_t now; - - now = std::time(NULL); - tm_ptr = std::localtime(&now); - - std::strftime(time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm_ptr); - - std::cout << time_buffer << "\n"; - - return; -# undef TIME_SIZE - } -} \ No newline at end of file diff --git a/src/external/Padua/padua.h b/src/external/Padua/padua.h deleted file mode 100644 index 46374e7b..00000000 --- a/src/external/Padua/padua.h +++ /dev/null @@ -1,36 +0,0 @@ -#include - -using std::string; - -namespace padua { - void filename_inc(string *filename); - - string i4_to_string(int i4); - - int padua_order(int l); - - void padua_plot(int l, string filename); - - double *padua_points(int l); - - double *padua_points_set(int l); - - double *padua_weights(int l); - - double *padua_weights_set(int l); - - double r8_max(double x, double y); - - void r8mat_transpose_print(int m, int n, double a[], string title); - - void r8mat_transpose_print_some(int m, int n, double a[], int ilo, int jlo, - int ihi, int jhi, string title); - - double *r8vec_copy_new(int n, double a1[]); - - void r8vec_print(int n, double a[], string title); - - void r8vec_reverse(int n, double a[]); - - void timestamp(); -} \ No newline at end of file diff --git a/src/external/Spectra/include/Spectra/GenEigsBase.h b/src/external/Spectra/include/Spectra/GenEigsBase.h deleted file mode 100644 index 19b12c15..00000000 --- a/src/external/Spectra/include/Spectra/GenEigsBase.h +++ /dev/null @@ -1,479 +0,0 @@ -// Copyright (C) 2018-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef GEN_EIGS_BASE_H -#define GEN_EIGS_BASE_H - -#include -#include // std::vector -#include // std::abs, std::pow, std::sqrt -#include // std::min, std::copy -#include // std::complex, std::conj, std::norm, std::abs -#include // std::invalid_argument - -#include "Util/TypeTraits.h" -#include "Util/SelectionRule.h" -#include "Util/CompInfo.h" -#include "Util/SimpleRandom.h" -#include "MatOp/internal/ArnoldiOp.h" -#include "LinAlg/UpperHessenbergQR.h" -#include "LinAlg/DoubleShiftQR.h" -#include "LinAlg/UpperHessenbergEigen.h" -#include "LinAlg/Arnoldi.h" - -namespace Spectra { - - -/// -/// \ingroup EigenSolver -/// -/// This is the base class for general eigen solvers, mainly for internal use. -/// It is kept here to provide the documentation for member functions of concrete eigen solvers -/// such as GenEigsSolver and GenEigsRealShiftSolver. -/// -template < typename Scalar, - int SelectionRule, - typename OpType, - typename BOpType > -class GenEigsBase -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Array Array; - typedef Eigen::Array BoolArray; - typedef Eigen::Map MapMat; - typedef Eigen::Map MapVec; - typedef Eigen::Map MapConstVec; - - typedef std::complex Complex; - typedef Eigen::Matrix ComplexMatrix; - typedef Eigen::Matrix ComplexVector; - - typedef ArnoldiOp ArnoldiOpType; - typedef Arnoldi ArnoldiFac; - -protected: - OpType* m_op; // object to conduct matrix operation, - // e.g. matrix-vector product - const Index m_n; // dimension of matrix A - const Index m_nev; // number of eigenvalues requested - const Index m_ncv; // dimension of Krylov subspace in the Arnoldi method - Index m_nmatop; // number of matrix operations called - Index m_niter; // number of restarting iterations - - ArnoldiFac m_fac; // Arnoldi factorization - - ComplexVector m_ritz_val; // Ritz values - ComplexMatrix m_ritz_vec; // Ritz vectors - ComplexVector m_ritz_est; // last row of m_ritz_vec - -private: - BoolArray m_ritz_conv; // indicator of the convergence of Ritz values - int m_info; // status of the computation - - const Scalar m_near_0; // a very small value, but 1.0 / m_near_0 does not overflow - // ~= 1e-307 for the "double" type - const Scalar m_eps; // the machine precision, ~= 1e-16 for the "double" type - const Scalar m_eps23; // m_eps^(2/3), used to test the convergence - - // Real Ritz values calculated from UpperHessenbergEigen have exact zero imaginary part - // Complex Ritz values have exact conjugate pairs - // So we use exact tests here - static bool is_complex(const Complex& v) { return v.imag() != Scalar(0); } - static bool is_conj(const Complex& v1, const Complex& v2) { return v1 == Eigen::numext::conj(v2); } - - // Implicitly restarted Arnoldi factorization - void restart(Index k) - { - using std::norm; - - if(k >= m_ncv) - return; - - DoubleShiftQR decomp_ds(m_ncv); - UpperHessenbergQR decomp_hb(m_ncv); - Matrix Q = Matrix::Identity(m_ncv, m_ncv); - - for(Index i = k; i < m_ncv; i++) - { - if(is_complex(m_ritz_val[i]) && is_conj(m_ritz_val[i], m_ritz_val[i + 1])) - { - // H - mu * I = Q1 * R1 - // H <- R1 * Q1 + mu * I = Q1' * H * Q1 - // H - conj(mu) * I = Q2 * R2 - // H <- R2 * Q2 + conj(mu) * I = Q2' * H * Q2 - // - // (H - mu * I) * (H - conj(mu) * I) = Q1 * Q2 * R2 * R1 = Q * R - const Scalar s = Scalar(2) * m_ritz_val[i].real(); - const Scalar t = norm(m_ritz_val[i]); - - decomp_ds.compute(m_fac.matrix_H(), s, t); - - // Q -> Q * Qi - decomp_ds.apply_YQ(Q); - // H -> Q'HQ - // Matrix Q = Matrix::Identity(m_ncv, m_ncv); - // decomp_ds.apply_YQ(Q); - // m_fac_H = Q.transpose() * m_fac_H * Q; - m_fac.compress_H(decomp_ds); - - i++; - } else { - // QR decomposition of H - mu * I, mu is real - decomp_hb.compute(m_fac.matrix_H(), m_ritz_val[i].real()); - - // Q -> Q * Qi - decomp_hb.apply_YQ(Q); - // H -> Q'HQ = RQ + mu * I - m_fac.compress_H(decomp_hb); - } - } - - m_fac.compress_V(Q); - m_fac.factorize_from(k, m_ncv, m_nmatop); - - retrieve_ritzpair(); - } - - // Calculates the number of converged Ritz values - Index num_converged(Scalar tol) - { - // thresh = tol * max(m_eps23, abs(theta)), theta for Ritz value - Array thresh = tol * m_ritz_val.head(m_nev).array().abs().max(m_eps23); - Array resid = m_ritz_est.head(m_nev).array().abs() * m_fac.f_norm(); - // Converged "wanted" Ritz values - m_ritz_conv = (resid < thresh); - - return m_ritz_conv.cast().sum(); - } - - // Returns the adjusted nev for restarting - Index nev_adjusted(Index nconv) - { - using std::abs; - - Index nev_new = m_nev; - for(Index i = m_nev; i < m_ncv; i++) - if(abs(m_ritz_est[i]) < m_near_0) nev_new++; - - // Adjust nev_new, according to dnaup2.f line 660~674 in ARPACK - nev_new += std::min(nconv, (m_ncv - nev_new) / 2); - if(nev_new == 1 && m_ncv >= 6) - nev_new = m_ncv / 2; - else if(nev_new == 1 && m_ncv > 3) - nev_new = 2; - - if(nev_new > m_ncv - 2) - nev_new = m_ncv - 2; - - // Increase nev by one if ritz_val[nev - 1] and - // ritz_val[nev] are conjugate pairs - if(is_complex(m_ritz_val[nev_new - 1]) && - is_conj(m_ritz_val[nev_new - 1], m_ritz_val[nev_new])) - { - nev_new++; - } - - return nev_new; - } - - // Retrieves and sorts Ritz values and Ritz vectors - void retrieve_ritzpair() - { - UpperHessenbergEigen decomp(m_fac.matrix_H()); - const ComplexVector& evals = decomp.eigenvalues(); - ComplexMatrix evecs = decomp.eigenvectors(); - - SortEigenvalue sorting(evals.data(), evals.size()); - std::vector ind = sorting.index(); - - // Copy the Ritz values and vectors to m_ritz_val and m_ritz_vec, respectively - for(Index i = 0; i < m_ncv; i++) - { - m_ritz_val[i] = evals[ind[i]]; - m_ritz_est[i] = evecs(m_ncv - 1, ind[i]); - } - for(Index i = 0; i < m_nev; i++) - { - m_ritz_vec.col(i).noalias() = evecs.col(ind[i]); - } - } - -protected: - // Sorts the first nev Ritz pairs in the specified order - // This is used to return the final results - virtual void sort_ritzpair(int sort_rule) - { - // First make sure that we have a valid index vector - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - std::vector ind = sorting.index(); - - switch(sort_rule) - { - case LARGEST_MAGN: - break; - case LARGEST_REAL: - { - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - ind = sorting.index(); - } - break; - case LARGEST_IMAG: - { - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - ind = sorting.index(); - } - break; - case SMALLEST_MAGN: - { - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - ind = sorting.index(); - } - break; - case SMALLEST_REAL: - { - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - ind = sorting.index(); - } - break; - case SMALLEST_IMAG: - { - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - ind = sorting.index(); - } - break; - default: - throw std::invalid_argument("unsupported sorting rule"); - } - - ComplexVector new_ritz_val(m_ncv); - ComplexMatrix new_ritz_vec(m_ncv, m_nev); - BoolArray new_ritz_conv(m_nev); - - for(Index i = 0; i < m_nev; i++) - { - new_ritz_val[i] = m_ritz_val[ind[i]]; - new_ritz_vec.col(i).noalias() = m_ritz_vec.col(ind[i]); - new_ritz_conv[i] = m_ritz_conv[ind[i]]; - } - - m_ritz_val.swap(new_ritz_val); - m_ritz_vec.swap(new_ritz_vec); - m_ritz_conv.swap(new_ritz_conv); - } - -public: - /// \cond - - GenEigsBase(OpType* op, BOpType* Bop, Index nev, Index ncv) : - m_op(op), - m_n(m_op->rows()), - m_nev(nev), - m_ncv(ncv > m_n ? m_n : ncv), - m_nmatop(0), - m_niter(0), - m_fac(ArnoldiOpType(op, Bop), m_ncv), - m_info(NOT_COMPUTED), - m_near_0(TypeTraits::min() * Scalar(10)), - m_eps(Eigen::NumTraits::epsilon()), - m_eps23(Eigen::numext::pow(m_eps, Scalar(2.0) / 3)) - { - if(nev < 1 || nev > m_n - 2) - throw std::invalid_argument("nev must satisfy 1 <= nev <= n - 2, n is the size of matrix"); - - if(ncv < nev + 2 || ncv > m_n) - throw std::invalid_argument("ncv must satisfy nev + 2 <= ncv <= n, n is the size of matrix"); - } - - /// - /// Virtual destructor - /// - virtual ~GenEigsBase() {} - - /// \endcond - - /// - /// Initializes the solver by providing an initial residual vector. - /// - /// \param init_resid Pointer to the initial residual vector. - /// - /// **Spectra** (and also **ARPACK**) uses an iterative algorithm - /// to find eigenvalues. This function allows the user to provide the initial - /// residual vector. - /// - void init(const Scalar* init_resid) - { - // Reset all matrices/vectors to zero - m_ritz_val.resize(m_ncv); - m_ritz_vec.resize(m_ncv, m_nev); - m_ritz_est.resize(m_ncv); - m_ritz_conv.resize(m_nev); - - m_ritz_val.setZero(); - m_ritz_vec.setZero(); - m_ritz_est.setZero(); - m_ritz_conv.setZero(); - - m_nmatop = 0; - m_niter = 0; - - // Initialize the Arnoldi factorization - MapConstVec v0(init_resid, m_n); - m_fac.init(v0, m_nmatop); - } - - /// - /// Initializes the solver by providing a random initial residual vector. - /// - /// This overloaded function generates a random initial residual vector - /// (with a fixed random seed) for the algorithm. Elements in the vector - /// follow independent Uniform(-0.5, 0.5) distribution. - /// - void init() - { - SimpleRandom rng(0); - Vector init_resid = rng.random_vec(m_n); - init(init_resid.data()); - } - - /// - /// Conducts the major computation procedure. - /// - /// \param maxit Maximum number of iterations allowed in the algorithm. - /// \param tol Precision parameter for the calculated eigenvalues. - /// \param sort_rule Rule to sort the eigenvalues and eigenvectors. - /// Supported values are - /// `Spectra::LARGEST_MAGN`, `Spectra::LARGEST_REAL`, - /// `Spectra::LARGEST_IMAG`, `Spectra::SMALLEST_MAGN`, - /// `Spectra::SMALLEST_REAL` and `Spectra::SMALLEST_IMAG`, - /// for example `LARGEST_MAGN` indicates that eigenvalues - /// with largest magnitude come first. - /// Note that this argument is only used to - /// **sort** the final result, and the **selection** rule - /// (e.g. selecting the largest or smallest eigenvalues in the - /// full spectrum) is specified by the template parameter - /// `SelectionRule` of GenEigsSolver. - /// - /// \return Number of converged eigenvalues. - /// - Index compute(Index maxit = 1000, Scalar tol = 1e-10, int sort_rule = LARGEST_MAGN) - { - // The m-step Arnoldi factorization - m_fac.factorize_from(1, m_ncv, m_nmatop); - retrieve_ritzpair(); - // Restarting - Index i, nconv = 0, nev_adj; - for(i = 0; i < maxit; i++) - { - nconv = num_converged(tol); - if(nconv >= m_nev) - break; - - nev_adj = nev_adjusted(nconv); - restart(nev_adj); - } - // Sorting results - sort_ritzpair(sort_rule); - - m_niter += i + 1; - m_info = (nconv >= m_nev) ? SUCCESSFUL : NOT_CONVERGING; - - return std::min(m_nev, nconv); - } - - /// - /// Returns the status of the computation. - /// The full list of enumeration values can be found in \ref Enumerations. - /// - int info() const { return m_info; } - - /// - /// Returns the number of iterations used in the computation. - /// - Index num_iterations() const { return m_niter; } - - /// - /// Returns the number of matrix operations used in the computation. - /// - Index num_operations() const { return m_nmatop; } - - /// - /// Returns the converged eigenvalues. - /// - /// \return A complex-valued vector containing the eigenvalues. - /// Returned vector type will be `Eigen::Vector, ...>`, depending on - /// the template parameter `Scalar` defined. - /// - ComplexVector eigenvalues() const - { - const Index nconv = m_ritz_conv.cast().sum(); - ComplexVector res(nconv); - - if(!nconv) - return res; - - Index j = 0; - for(Index i = 0; i < m_nev; i++) - { - if(m_ritz_conv[i]) - { - res[j] = m_ritz_val[i]; - j++; - } - } - - return res; - } - - /// - /// Returns the eigenvectors associated with the converged eigenvalues. - /// - /// \param nvec The number of eigenvectors to return. - /// - /// \return A complex-valued matrix containing the eigenvectors. - /// Returned matrix type will be `Eigen::Matrix, ...>`, - /// depending on the template parameter `Scalar` defined. - /// - ComplexMatrix eigenvectors(Index nvec) const - { - const Index nconv = m_ritz_conv.cast().sum(); - nvec = std::min(nvec, nconv); - ComplexMatrix res(m_n, nvec); - - if(!nvec) - return res; - - ComplexMatrix ritz_vec_conv(m_ncv, nvec); - Index j = 0; - for(Index i = 0; i < m_nev && j < nvec; i++) - { - if(m_ritz_conv[i]) - { - ritz_vec_conv.col(j).noalias() = m_ritz_vec.col(i); - j++; - } - } - - res.noalias() = m_fac.matrix_V() * ritz_vec_conv; - - return res; - } - - /// - /// Returns all converged eigenvectors. - /// - ComplexMatrix eigenvectors() const - { - return eigenvectors(m_nev); - } -}; - - -} // namespace Spectra - -#endif // GEN_EIGS_BASE_H diff --git a/src/external/Spectra/include/Spectra/GenEigsComplexShiftSolver.h b/src/external/Spectra/include/Spectra/GenEigsComplexShiftSolver.h deleted file mode 100644 index 2c1aee7f..00000000 --- a/src/external/Spectra/include/Spectra/GenEigsComplexShiftSolver.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef GEN_EIGS_COMPLEX_SHIFT_SOLVER_H -#define GEN_EIGS_COMPLEX_SHIFT_SOLVER_H - -#include - -#include "GenEigsBase.h" -#include "Util/SelectionRule.h" -#include "MatOp/DenseGenComplexShiftSolve.h" - -namespace Spectra { - - -/// -/// \ingroup EigenSolver -/// -/// This class implements the eigen solver for general real matrices with -/// a complex shift value in the **shift-and-invert mode**. The background -/// knowledge of the shift-and-invert mode can be found in the documentation -/// of the SymEigsShiftSolver class. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// \tparam SelectionRule An enumeration value indicating the selection rule of -/// the shifted-and-inverted eigenvalues. -/// The full list of enumeration values can be found in -/// \ref Enumerations. -/// \tparam OpType The name of the matrix operation class. Users could either -/// use the DenseGenComplexShiftSolve wrapper class, or define their -/// own that implements all the public member functions as in -/// DenseGenComplexShiftSolve. -/// -template > -class GenEigsComplexShiftSolver: public GenEigsBase -{ -private: - typedef Eigen::Index Index; - typedef std::complex Complex; - typedef Eigen::Matrix Vector; - typedef Eigen::Matrix ComplexVector; - - const Scalar m_sigmar; - const Scalar m_sigmai; - - // First transform back the Ritz values, and then sort - void sort_ritzpair(int sort_rule) - { - using std::abs; - using std::sqrt; - using std::norm; - - // The eigenvalues we get from the iteration is - // nu = 0.5 * (1 / (lambda - sigma) + 1 / (lambda - conj(sigma))) - // So the eigenvalues of the original problem is - // 1 \pm sqrt(1 - 4 * nu^2 * sigmai^2) - // lambda = sigmar + ----------------------------------- - // 2 * nu - // We need to pick the correct root - // Let (lambdaj, vj) be the j-th eigen pair, then A * vj = lambdaj * vj - // and inv(A - r * I) * vj = 1 / (lambdaj - r) * vj - // where r is any shift value. - // We can use this identity to determine lambdaj - // - // op(v) computes Re(inv(A - r * I) * v) for any real v - // If r is real, then op(v) is also real. Let a = Re(vj), b = Im(vj), - // then op(vj) = op(a) + op(b) * i - // By comparing op(vj) and [1 / (lambdaj - r) * vj], we can determine - // which one is the correct root - - // Select a random shift value - SimpleRandom rng(0); - const Scalar shiftr = rng.random() * m_sigmar + rng.random(); - const Complex shift = Complex(shiftr, Scalar(0)); - this->m_op->set_shift(shiftr, Scalar(0)); - - // Calculate inv(A - r * I) * vj - Vector v_real(this->m_n), v_imag(this->m_n), OPv_real(this->m_n), OPv_imag(this->m_n); - const Scalar eps = Eigen::NumTraits::epsilon(); - for(Index i = 0; i < this->m_nev; i++) - { - v_real.noalias() = this->m_fac.matrix_V() * this->m_ritz_vec.col(i).real(); - v_imag.noalias() = this->m_fac.matrix_V() * this->m_ritz_vec.col(i).imag(); - this->m_op->perform_op(v_real.data(), OPv_real.data()); - this->m_op->perform_op(v_imag.data(), OPv_imag.data()); - - // Two roots computed from the quadratic equation - const Complex nu = this->m_ritz_val[i]; - const Complex root_part1 = m_sigmar + Scalar(0.5) / nu; - const Complex root_part2 = Scalar(0.5) * sqrt(Scalar(1) - Scalar(4) * m_sigmai * m_sigmai * (nu * nu)) / nu; - const Complex root1 = root_part1 + root_part2; - const Complex root2 = root_part1 - root_part2; - - // Test roots - Scalar err1 = Scalar(0), err2 = Scalar(0); - for(int k = 0; k < this->m_n; k++) - { - const Complex rhs1 = Complex(v_real[k], v_imag[k]) / (root1 - shift); - const Complex rhs2 = Complex(v_real[k], v_imag[k]) / (root2 - shift); - const Complex OPv = Complex(OPv_real[k], OPv_imag[k]); - err1 += norm(OPv - rhs1); - err2 += norm(OPv - rhs2); - } - - const Complex lambdaj = (err1 < err2) ? root1 : root2; - this->m_ritz_val[i] = lambdaj; - - if(abs(Eigen::numext::imag(lambdaj)) > eps) - { - this->m_ritz_val[i + 1] = Eigen::numext::conj(lambdaj); - i++; - } else { - this->m_ritz_val[i] = Complex(Eigen::numext::real(lambdaj), Scalar(0)); - } - } - - GenEigsBase::sort_ritzpair(sort_rule); - } -public: - /// - /// Constructor to create a eigen solver object using the shift-and-invert mode. - /// - /// \param op Pointer to the matrix operation object. This class should implement - /// the complex shift-solve operation of \f$A\f$: calculating - /// \f$\mathrm{Re}\{(A-\sigma I)^{-1}v\}\f$ for any vector \f$v\f$. Users could either - /// create the object from the DenseGenComplexShiftSolve wrapper class, or - /// define their own that implements all the public member functions - /// as in DenseGenComplexShiftSolve. - /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-2\f$, - /// where \f$n\f$ is the size of matrix. - /// \param ncv Parameter that controls the convergence speed of the algorithm. - /// Typically a larger `ncv` means faster convergence, but it may - /// also result in greater memory use and more matrix operations - /// in each iteration. This parameter must satisfy \f$nev+2 \le ncv \le n\f$, - /// and is advised to take \f$ncv \ge 2\cdot nev + 1\f$. - /// \param sigmar The real part of the shift. - /// \param sigmai The imaginary part of the shift. - /// - GenEigsComplexShiftSolver(OpType* op, Index nev, Index ncv, const Scalar& sigmar, const Scalar& sigmai) : - GenEigsBase(op, NULL, nev, ncv), - m_sigmar(sigmar), m_sigmai(sigmai) - { - this->m_op->set_shift(m_sigmar, m_sigmai); - } -}; - - -} // namespace Spectra - -#endif // GEN_EIGS_COMPLEX_SHIFT_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/GenEigsRealShiftSolver.h b/src/external/Spectra/include/Spectra/GenEigsRealShiftSolver.h deleted file mode 100644 index a7e3da8e..00000000 --- a/src/external/Spectra/include/Spectra/GenEigsRealShiftSolver.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef GEN_EIGS_REAL_SHIFT_SOLVER_H -#define GEN_EIGS_REAL_SHIFT_SOLVER_H - -#include - -#include "GenEigsBase.h" -#include "Util/SelectionRule.h" -#include "MatOp/DenseGenRealShiftSolve.h" - -namespace Spectra { - - -/// -/// \ingroup EigenSolver -/// -/// This class implements the eigen solver for general real matrices with -/// a real shift value in the **shift-and-invert mode**. The background -/// knowledge of the shift-and-invert mode can be found in the documentation -/// of the SymEigsShiftSolver class. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// \tparam SelectionRule An enumeration value indicating the selection rule of -/// the shifted-and-inverted eigenvalues. -/// The full list of enumeration values can be found in -/// \ref Enumerations. -/// \tparam OpType The name of the matrix operation class. Users could either -/// use the wrapper classes such as DenseGenRealShiftSolve and -/// SparseGenRealShiftSolve, or define their -/// own that implements all the public member functions as in -/// DenseGenRealShiftSolve. -/// -template > -class GenEigsRealShiftSolver: public GenEigsBase -{ -private: - typedef Eigen::Index Index; - typedef std::complex Complex; - typedef Eigen::Array ComplexArray; - - const Scalar m_sigma; - - // First transform back the Ritz values, and then sort - void sort_ritzpair(int sort_rule) - { - // The eigenvalues we get from the iteration is nu = 1 / (lambda - sigma) - // So the eigenvalues of the original problem is lambda = 1 / nu + sigma - ComplexArray ritz_val_org = Scalar(1.0) / this->m_ritz_val.head(this->m_nev).array() + m_sigma; - this->m_ritz_val.head(this->m_nev) = ritz_val_org; - GenEigsBase::sort_ritzpair(sort_rule); - } -public: - /// - /// Constructor to create a eigen solver object using the shift-and-invert mode. - /// - /// \param op Pointer to the matrix operation object. This class should implement - /// the shift-solve operation of \f$A\f$: calculating - /// \f$(A-\sigma I)^{-1}v\f$ for any vector \f$v\f$. Users could either - /// create the object from the wrapper class such as DenseGenRealShiftSolve, or - /// define their own that implements all the public member functions - /// as in DenseGenRealShiftSolve. - /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-2\f$, - /// where \f$n\f$ is the size of matrix. - /// \param ncv Parameter that controls the convergence speed of the algorithm. - /// Typically a larger `ncv` means faster convergence, but it may - /// also result in greater memory use and more matrix operations - /// in each iteration. This parameter must satisfy \f$nev+2 \le ncv \le n\f$, - /// and is advised to take \f$ncv \ge 2\cdot nev + 1\f$. - /// \param sigma The real-valued shift. - /// - GenEigsRealShiftSolver(OpType* op, Index nev, Index ncv, Scalar sigma) : - GenEigsBase(op, NULL, nev, ncv), - m_sigma(sigma) - { - this->m_op->set_shift(m_sigma); - } -}; - - -} // namespace Spectra - -#endif // GEN_EIGS_REAL_SHIFT_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/GenEigsSolver.h b/src/external/Spectra/include/Spectra/GenEigsSolver.h deleted file mode 100644 index a6960acf..00000000 --- a/src/external/Spectra/include/Spectra/GenEigsSolver.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef GEN_EIGS_SOLVER_H -#define GEN_EIGS_SOLVER_H - -#include - -#include "GenEigsBase.h" -#include "Util/SelectionRule.h" -#include "MatOp/DenseGenMatProd.h" - -namespace Spectra { - - -/// -/// \ingroup EigenSolver -/// -/// This class implements the eigen solver for general real matrices, i.e., -/// to solve \f$Ax=\lambda x\f$ for a possibly non-symmetric \f$A\f$ matrix. -/// -/// Most of the background information documented in the SymEigsSolver class -/// also applies to the GenEigsSolver class here, except that the eigenvalues -/// and eigenvectors of a general matrix can now be complex-valued. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// \tparam SelectionRule An enumeration value indicating the selection rule of -/// the requested eigenvalues, for example `LARGEST_MAGN` -/// to retrieve eigenvalues with the largest magnitude. -/// The full list of enumeration values can be found in -/// \ref Enumerations. -/// \tparam OpType The name of the matrix operation class. Users could either -/// use the wrapper classes such as DenseGenMatProd and -/// SparseGenMatProd, or define their -/// own that implements all the public member functions as in -/// DenseGenMatProd. -/// -/// An example that illustrates the usage of GenEigsSolver is give below: -/// -/// \code{.cpp} -/// #include -/// #include -/// // is implicitly included -/// #include -/// -/// using namespace Spectra; -/// -/// int main() -/// { -/// // We are going to calculate the eigenvalues of M -/// Eigen::MatrixXd M = Eigen::MatrixXd::Random(10, 10); -/// -/// // Construct matrix operation object using the wrapper class -/// DenseGenMatProd op(M); -/// -/// // Construct eigen solver object, requesting the largest -/// // (in magnitude, or norm) three eigenvalues -/// GenEigsSolver< double, LARGEST_MAGN, DenseGenMatProd > eigs(&op, 3, 6); -/// -/// // Initialize and compute -/// eigs.init(); -/// int nconv = eigs.compute(); -/// -/// // Retrieve results -/// Eigen::VectorXcd evalues; -/// if(eigs.info() == SUCCESSFUL) -/// evalues = eigs.eigenvalues(); -/// -/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; -/// -/// return 0; -/// } -/// \endcode -/// -/// And also an example for sparse matrices: -/// -/// \code{.cpp} -/// #include -/// #include -/// #include -/// #include -/// #include -/// -/// using namespace Spectra; -/// -/// int main() -/// { -/// // A band matrix with 1 on the main diagonal, 2 on the below-main subdiagonal, -/// // and 3 on the above-main subdiagonal -/// const int n = 10; -/// Eigen::SparseMatrix M(n, n); -/// M.reserve(Eigen::VectorXi::Constant(n, 3)); -/// for(int i = 0; i < n; i++) -/// { -/// M.insert(i, i) = 1.0; -/// if(i > 0) -/// M.insert(i - 1, i) = 3.0; -/// if(i < n - 1) -/// M.insert(i + 1, i) = 2.0; -/// } -/// -/// // Construct matrix operation object using the wrapper class SparseGenMatProd -/// SparseGenMatProd op(M); -/// -/// // Construct eigen solver object, requesting the largest three eigenvalues -/// GenEigsSolver< double, LARGEST_MAGN, SparseGenMatProd > eigs(&op, 3, 6); -/// -/// // Initialize and compute -/// eigs.init(); -/// int nconv = eigs.compute(); -/// -/// // Retrieve results -/// Eigen::VectorXcd evalues; -/// if(eigs.info() == SUCCESSFUL) -/// evalues = eigs.eigenvalues(); -/// -/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; -/// -/// return 0; -/// } -/// \endcode -template < typename Scalar = double, - int SelectionRule = LARGEST_MAGN, - typename OpType = DenseGenMatProd > -class GenEigsSolver: public GenEigsBase -{ -private: - typedef Eigen::Index Index; - -public: - /// - /// Constructor to create a solver object. - /// - /// \param op Pointer to the matrix operation object, which should implement - /// the matrix-vector multiplication operation of \f$A\f$: - /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either - /// create the object from the wrapper class such as DenseGenMatProd, or - /// define their own that implements all the public member functions - /// as in DenseGenMatProd. - /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-2\f$, - /// where \f$n\f$ is the size of matrix. - /// \param ncv Parameter that controls the convergence speed of the algorithm. - /// Typically a larger `ncv` means faster convergence, but it may - /// also result in greater memory use and more matrix operations - /// in each iteration. This parameter must satisfy \f$nev+2 \le ncv \le n\f$, - /// and is advised to take \f$ncv \ge 2\cdot nev + 1\f$. - /// - GenEigsSolver(OpType* op, Index nev, Index ncv) : - GenEigsBase(op, NULL, nev, ncv) - {} -}; - - -} // namespace Spectra - -#endif // GEN_EIGS_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/Arnoldi.h b/src/external/Spectra/include/Spectra/LinAlg/Arnoldi.h deleted file mode 100644 index b9fa75b5..00000000 --- a/src/external/Spectra/include/Spectra/LinAlg/Arnoldi.h +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright (C) 2018-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef ARNOLDI_H -#define ARNOLDI_H - -#include -#include // std::sqrt -#include // std::invalid_argument -#include // std::stringstream - -#include "../MatOp/internal/ArnoldiOp.h" -#include "../Util/TypeTraits.h" -#include "../Util/SimpleRandom.h" -#include "UpperHessenbergQR.h" -#include "DoubleShiftQR.h" - -namespace Spectra { - - -// Arnoldi factorization A * V = V * H + f * e' -// A: n x n -// V: n x k -// H: k x k -// f: n x 1 -// e: [0, ..., 0, 1] -// V and H are allocated of dimension m, so the maximum value of k is m -template -class Arnoldi -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapMat; - typedef Eigen::Map MapVec; - typedef Eigen::Map MapConstMat; - typedef Eigen::Map MapConstVec; - -protected: - ArnoldiOpType m_op; // Operators for the Arnoldi factorization - - const Index m_n; // dimension of A - const Index m_m; // maximum dimension of subspace V - Index m_k; // current dimension of subspace V - - Matrix m_fac_V; // V matrix in the Arnoldi factorization - Matrix m_fac_H; // H matrix in the Arnoldi factorization - Vector m_fac_f; // residual in the Arnoldi factorization - Scalar m_beta; // ||f||, B-norm of f - - const Scalar m_near_0; // a very small value, but 1.0 / m_near_0 does not overflow - // ~= 1e-307 for the "double" type - const Scalar m_eps; // the machine precision, ~= 1e-16 for the "double" type - - // Given orthonormal basis functions V, find a nonzero vector f such that V'Bf = 0 - // Assume that f has been properly allocated - void expand_basis(MapConstMat& V, const Index seed, Vector& f, Scalar& fnorm) - { - using std::sqrt; - - const Scalar thresh = m_eps * sqrt(Scalar(m_n)); - Vector Vf(V.cols()); - for(Index iter = 0; iter < 5; iter++) - { - // Randomly generate a new vector and orthogonalize it against V - SimpleRandom rng(seed + 123 * iter); - f.noalias() = rng.random_vec(m_n); - // f <- f - V * V'Bf, so that f is orthogonal to V in B-norm - m_op.trans_product(V, f, Vf); - f.noalias() -= V * Vf; - // fnorm <- ||f|| - fnorm = m_op.norm(f); - - // If fnorm is too close to zero, we try a new random vector, - // otherwise return the result - if(fnorm >= thresh) - return; - } - } - -public: - Arnoldi(const ArnoldiOpType& op, Index m) : - m_op(op), m_n(op.rows()), m_m(m), m_k(0), - m_near_0(TypeTraits::min() * Scalar(10)), - m_eps(Eigen::NumTraits::epsilon()) - {} - - virtual ~Arnoldi() {} - - // Const-reference to internal structures - const Matrix& matrix_V() const { return m_fac_V; } - const Matrix& matrix_H() const { return m_fac_H; } - const Vector& vector_f() const { return m_fac_f; } - Scalar f_norm() const { return m_beta; } - Index subspace_dim() const { return m_k; } - - // Initialize with an operator and an initial vector - void init(MapConstVec& v0, Index& op_counter) - { - m_fac_V.resize(m_n, m_m); - m_fac_H.resize(m_m, m_m); - m_fac_f.resize(m_n); - m_fac_H.setZero(); - - // Verify the initial vector - const Scalar v0norm = m_op.norm(v0); - if(v0norm < m_near_0) - throw std::invalid_argument("initial residual vector cannot be zero"); - - // Points to the first column of V - MapVec v(m_fac_V.data(), m_n); - - // Normalize - v.noalias() = v0 / v0norm; - - // Compute H and f - Vector w(m_n); - m_op.perform_op(v.data(), w.data()); - op_counter++; - - m_fac_H(0, 0) = m_op.inner_product(v, w); - m_fac_f.noalias() = w - v * m_fac_H(0, 0); - - // In some cases f is zero in exact arithmetics, but due to rounding errors - // it may contain tiny fluctuations. When this happens, we force f to be zero - if(m_fac_f.cwiseAbs().maxCoeff() < m_eps) - { - m_fac_f.setZero(); - m_beta = Scalar(0); - } else { - m_beta = m_op.norm(m_fac_f); - } - - // Indicate that this is a step-1 factorization - m_k = 1; - } - - // Arnoldi factorization starting from step-k - virtual void factorize_from(Index from_k, Index to_m, Index& op_counter) - { - using std::sqrt; - - if(to_m <= from_k) return; - - if(from_k > m_k) - { - std::stringstream msg; - msg << "Arnoldi: from_k (= " << from_k << - ") is larger than the current subspace dimension (= " << - m_k << ")"; - throw std::invalid_argument(msg.str()); - } - - const Scalar beta_thresh = m_eps * sqrt(Scalar(m_n)); - - // Pre-allocate vectors - Vector Vf(to_m); - Vector w(m_n); - - // Keep the upperleft k x k submatrix of H and set other elements to 0 - m_fac_H.rightCols(m_m - from_k).setZero(); - m_fac_H.block(from_k, 0, m_m - from_k, from_k).setZero(); - - for(Index i = from_k; i <= to_m - 1; i++) - { - bool restart = false; - // If beta = 0, then the next V is not full rank - // We need to generate a new residual vector that is orthogonal - // to the current V, which we call a restart - if(m_beta < m_near_0) - { - MapConstMat V(m_fac_V.data(), m_n, i); // The first i columns - expand_basis(V, 2 * i, m_fac_f, m_beta); - restart = true; - } - - // v <- f / ||f|| - m_fac_V.col(i).noalias() = m_fac_f / m_beta; // The (i+1)-th column - - // Note that H[i+1, i] equals to the unrestarted beta - m_fac_H(i, i - 1) = restart ? Scalar(0) : m_beta; - - // w <- A * v, v = m_fac_V.col(i) - m_op.perform_op(&m_fac_V(0, i), w.data()); - op_counter++; - - const Index i1 = i + 1; - // First i+1 columns of V - MapConstMat Vs(m_fac_V.data(), m_n, i1); - // h = m_fac_H(0:i, i) - MapVec h(&m_fac_H(0, i), i1); - // h <- V'Bw - m_op.trans_product(Vs, w, h); - - // f <- w - V * h - m_fac_f.noalias() = w - Vs * h; - m_beta = m_op.norm(m_fac_f); - - if(m_beta > Scalar(0.717) * m_op.norm(h)) - continue; - - // f/||f|| is going to be the next column of V, so we need to test - // whether V'B(f/||f||) ~= 0 - m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); - Scalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); - // If not, iteratively correct the residual - int count = 0; - while(count < 5 && ortho_err > m_eps * m_beta) - { - // There is an edge case: when beta=||f|| is close to zero, f mostly consists - // of noises of rounding errors, so the test [ortho_err < eps * beta] is very - // likely to fail. In particular, if beta=0, then the test is ensured to fail. - // Hence when this happens, we force f to be zero, and then restart in the - // next iteration. - if(m_beta < beta_thresh) - { - m_fac_f.setZero(); - m_beta = Scalar(0); - break; - } - - // f <- f - V * Vf - m_fac_f.noalias() -= Vs * Vf.head(i1); - // h <- h + Vf - h.noalias() += Vf.head(i1); - // beta <- ||f|| - m_beta = m_op.norm(m_fac_f); - - m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); - ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); - count++; - } - } - - // Indicate that this is a step-m factorization - m_k = to_m; - } - - // Apply H -> Q'HQ, where Q is from a double shift QR decomposition - void compress_H(const DoubleShiftQR& decomp) - { - decomp.matrix_QtHQ(m_fac_H); - m_k -= 2; - } - - // Apply H -> Q'HQ, where Q is from an upper Hessenberg QR decomposition - void compress_H(const UpperHessenbergQR& decomp) - { - decomp.matrix_QtHQ(m_fac_H); - m_k--; - } - - // Apply V -> VQ and compute the new f. - // Should be called after compress_H(), since m_k is updated there. - // Only need to update the first k+1 columns of V - // The first (m - k + i) elements of the i-th column of Q are non-zero, - // and the rest are zero - void compress_V(const Matrix& Q) - { - Matrix Vs(m_n, m_k + 1); - for(Index i = 0; i < m_k; i++) - { - const Index nnz = m_m - m_k + i + 1; - MapConstVec q(&Q(0, i), nnz); - Vs.col(i).noalias() = m_fac_V.leftCols(nnz) * q; - } - Vs.col(m_k).noalias() = m_fac_V * Q.col(m_k); - m_fac_V.leftCols(m_k + 1).noalias() = Vs; - - Vector fk = m_fac_f * Q(m_m - 1, m_k - 1) + m_fac_V.col(m_k) * m_fac_H(m_k, m_k - 1); - m_fac_f.swap(fk); - m_beta = m_op.norm(m_fac_f); - } -}; - - -} // namespace Spectra - -#endif // ARNOLDI_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/BKLDLT.h b/src/external/Spectra/include/Spectra/LinAlg/BKLDLT.h deleted file mode 100644 index 5509749b..00000000 --- a/src/external/Spectra/include/Spectra/LinAlg/BKLDLT.h +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright (C) 2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef BK_LDLT_H -#define BK_LDLT_H - -#include -#include -#include - -#include "../Util/CompInfo.h" - -namespace Spectra { - - -// Bunch-Kaufman LDLT decomposition -// References: -// 1. Bunch, J. R., & Kaufman, L. (1977). Some stable methods for calculating inertia and solving symmetric linear systems. -// Mathematics of computation, 31(137), 163-179. -// 2. Golub, G. H., & Van Loan, C. F. (2012). Matrix computations (Vol. 3). JHU press. Section 4.4. -// 3. Bunch-Parlett diagonal pivoting -// 4. Ashcraft, C., Grimes, R. G., & Lewis, J. G. (1998). Accurate symmetric indefinite linear equation solvers. -// SIAM Journal on Matrix Analysis and Applications, 20(2), 513-561. -template -class BKLDLT -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapVec; - typedef Eigen::Map MapConstVec; - - typedef Eigen::Matrix IntVector; - typedef Eigen::Ref GenericVector; - typedef Eigen::Ref GenericMatrix; - typedef const Eigen::Ref ConstGenericMatrix; - typedef const Eigen::Ref ConstGenericVector; - - Index m_n; - Vector m_data; // storage for a lower-triangular matrix - std::vector m_colptr; // pointers to columns - IntVector m_perm; // [-2, -1, 3, 1, 4, 5]: 0 <-> 2, 1 <-> 1, 2 <-> 3, 3 <-> 1, 4 <-> 4, 5 <-> 5 - std::vector< std::pair > m_permc; // compressed version of m_perm: [(0, 2), (2, 3), (3, 1)] - - bool m_computed; - int m_info; - - // Access to elements - // Pointer to the k-th column - Scalar* col_pointer(Index k) { return m_colptr[k]; } - // A[i, j] -> m_colptr[j][i - j], i >= j - Scalar& coeff(Index i, Index j) { return m_colptr[j][i - j]; } - const Scalar& coeff(Index i, Index j) const { return m_colptr[j][i - j]; } - // A[i, i] -> m_colptr[i][0] - Scalar& diag_coeff(Index i) { return m_colptr[i][0]; } - const Scalar& diag_coeff(Index i) const { return m_colptr[i][0]; } - - // Compute column pointers - void compute_pointer() - { - m_colptr.clear(); - m_colptr.reserve(m_n); - Scalar* head = m_data.data(); - - for(Index i = 0; i < m_n; i++) - { - m_colptr.push_back(head); - head += (m_n - i); - } - } - - // Copy mat - shift * I to m_data - void copy_data(ConstGenericMatrix& mat, int uplo, const Scalar& shift) - { - if(uplo == Eigen::Lower) - { - for(Index j = 0; j < m_n; j++) - { - const Scalar* begin = &mat.coeffRef(j, j); - const Index len = m_n - j; - std::copy(begin, begin + len, col_pointer(j)); - diag_coeff(j) -= shift; - } - } else { - Scalar* dest = m_data.data(); - for(Index i = 0; i < m_n; i++) - { - for(Index j = i; j < m_n; j++, dest++) - { - *dest = mat.coeff(i, j); - } - diag_coeff(i) -= shift; - } - } - } - - // Compute compressed permutations - void compress_permutation() - { - for(Index i = 0; i < m_n; i++) - { - // Recover the permutation action - const Index perm = (m_perm[i] >= 0) ? (m_perm[i]) : (-m_perm[i] - 1); - if(perm != i) - m_permc.push_back(std::make_pair(i, perm)); - } - } - - // Working on the A[k:end, k:end] submatrix - // Exchange k <-> r - // Assume r >= k - void pivoting_1x1(Index k, Index r) - { - // No permutation - if(k == r) - { - m_perm[k] = r; - return; - } - - // A[k, k] <-> A[r, r] - std::swap(diag_coeff(k), diag_coeff(r)); - - // A[(r+1):end, k] <-> A[(r+1):end, r] - std::swap_ranges(&coeff(r + 1, k), col_pointer(k + 1), &coeff(r + 1, r)); - - // A[(k+1):(r-1), k] <-> A[r, (k+1):(r-1)] - Scalar* src = &coeff(k + 1, k); - for(Index j = k + 1; j < r; j++, src++) - { - std::swap(*src, coeff(r, j)); - } - - m_perm[k] = r; - } - - // Working on the A[k:end, k:end] submatrix - // Exchange [k+1, k] <-> [r, p] - // Assume p >= k, r >= k+1 - void pivoting_2x2(Index k, Index r, Index p) - { - pivoting_1x1(k, p); - pivoting_1x1(k + 1, r); - - // A[k+1, k] <-> A[r, k] - std::swap(coeff(k + 1, k), coeff(r, k)); - - // Use negative signs to indicate a 2x2 block - // Also minus one to distinguish a negative zero from a positive zero - m_perm[k] = -m_perm[k] - 1; - m_perm[k + 1] = -m_perm[k + 1] - 1; - } - - // A[r1, c1:c2] <-> A[r2, c1:c2] - // Assume r2 >= r1 > c2 >= c1 - void interchange_rows(Index r1, Index r2, Index c1, Index c2) - { - if(r1 == r2) - return; - - for(Index j = c1; j <= c2; j++) - { - std::swap(coeff(r1, j), coeff(r2, j)); - } - } - - // lambda = |A[r, k]| = max{|A[k+1, k]|, ..., |A[end, k]|} - // Largest (in magnitude) off-diagonal element in the first column of the current reduced matrix - // r is the row index - // Assume k < end - Scalar find_lambda(Index k, Index& r) - { - using std::abs; - - const Scalar* head = col_pointer(k); // => A[k, k] - const Scalar* end = col_pointer(k + 1); - // Start with r=k+1, lambda=A[k+1, k] - r = k + 1; - Scalar lambda = abs(head[1]); - // Scan remaining elements - for(const Scalar* ptr = head + 2; ptr < end; ptr++) - { - const Scalar abs_elem = abs(*ptr); - if(lambda < abs_elem) - { - lambda = abs_elem; - r = k + (ptr - head); - } - } - - return lambda; - } - - // sigma = |A[p, r]| = max {|A[k, r]|, ..., |A[end, r]|} \ {A[r, r]} - // Largest (in magnitude) off-diagonal element in the r-th column of the current reduced matrix - // p is the row index - // Assume k < r < end - Scalar find_sigma(Index k, Index r, Index& p) - { - using std::abs; - - // First search A[r+1, r], ..., A[end, r], which has the same task as find_lambda() - // If r == end, we skip this search - Scalar sigma = Scalar(-1); - if(r < m_n - 1) - sigma = find_lambda(r, p); - - // Then search A[k, r], ..., A[r-1, r], which maps to A[r, k], ..., A[r, r-1] - for(Index j = k; j < r; j++) - { - const Scalar abs_elem = abs(coeff(r, j)); - if(sigma < abs_elem) - { - sigma = abs_elem; - p = j; - } - } - - return sigma; - } - - // Generate permutations and apply to A - // Return true if the resulting pivoting is 1x1, and false if 2x2 - bool permutate_mat(Index k, const Scalar& alpha) - { - using std::abs; - - Index r = k, p = k; - const Scalar lambda = find_lambda(k, r); - - // If lambda=0, no need to interchange - if(lambda > Scalar(0)) - { - const Scalar abs_akk = abs(diag_coeff(k)); - // If |A[k, k]| >= alpha * lambda, no need to interchange - if(abs_akk < alpha * lambda) - { - const Scalar sigma = find_sigma(k, r, p); - - // If sigma * |A[k, k]| >= alpha * lambda^2, no need to interchange - if(sigma * abs_akk < alpha * lambda * lambda) - { - if(abs_akk >= alpha * sigma) - { - // Permutation on A - pivoting_1x1(k, r); - - // Permutation on L - interchange_rows(k, r, 0, k - 1); - return true; - } else { - // There are two versions of permutation here - // 1. A[k+1, k] <-> A[r, k] - // 2. A[k+1, k] <-> A[r, p], where p >= k and r >= k+1 - // - // Version 1 and 2 are used by Ref[1] and Ref[2], respectively - - // Version 1 implementation - p = k; - - // Version 2 implementation - // [r, p] and [p, r] are symmetric, but we need to make sure - // p >= k and r >= k+1, so it is safe to always make r > p - // One exception is when min{r,p} == k+1, in which case we make - // r = k+1, so that only one permutation needs to be performed - /* const Index rp_min = std::min(r, p); - const Index rp_max = std::max(r, p); - if(rp_min == k + 1) - { - r = rp_min; p = rp_max; - } else { - r = rp_max; p = rp_min; - } */ - - // Right now we use Version 1 since it reduces the overhead of interchange - - // Permutation on A - pivoting_2x2(k, r, p); - // Permutation on L - interchange_rows(k, p, 0, k - 1); - interchange_rows(k + 1, r, 0, k - 1); - return false; - } - } - } - } - - return true; - } - - // E = [e11, e12] - // [e21, e22] - // Overwrite E with inv(E) - void inverse_inplace_2x2(Scalar& e11, Scalar& e21, Scalar& e22) const - { - // inv(E) = [d11, d12], d11 = e22/delta, d21 = -e21/delta, d22 = e11/delta - // [d21, d22] - const Scalar delta = e11 * e22 - e21 * e21; - std::swap(e11, e22); - e11 /= delta; - e22 /= delta; - e21 = -e21 / delta; - } - - // Return value is the status, SUCCESSFUL/NUMERICAL_ISSUE - int gaussian_elimination_1x1(Index k) - { - // D = 1 / A[k, k] - const Scalar akk = diag_coeff(k); - // Return NUMERICAL_ISSUE if not invertible - if(akk == Scalar(0)) - return NUMERICAL_ISSUE; - - diag_coeff(k) = Scalar(1) / akk; - - // B -= l * l' / A[k, k], B := A[(k+1):end, (k+1):end], l := L[(k+1):end, k] - Scalar* lptr = col_pointer(k) + 1; - const Index ldim = m_n - k - 1; - MapVec l(lptr, ldim); - for(Index j = 0; j < ldim; j++) - { - MapVec(col_pointer(j + k + 1), ldim - j).noalias() -= (lptr[j] / akk) * l.tail(ldim - j); - } - - // l /= A[k, k] - l /= akk; - - return SUCCESSFUL; - } - - // Return value is the status, SUCCESSFUL/NUMERICAL_ISSUE - int gaussian_elimination_2x2(Index k) - { - // D = inv(E) - Scalar& e11 = diag_coeff(k); - Scalar& e21 = coeff(k + 1, k); - Scalar& e22 = diag_coeff(k + 1); - // Return NUMERICAL_ISSUE if not invertible - if(e11 * e22 - e21 * e21 == Scalar(0)) - return NUMERICAL_ISSUE; - - inverse_inplace_2x2(e11, e21, e22); - - // X = l * inv(E), l := L[(k+2):end, k:(k+1)] - Scalar* l1ptr = &coeff(k + 2, k); - Scalar* l2ptr = &coeff(k + 2, k + 1); - const Index ldim = m_n - k - 2; - MapVec l1(l1ptr, ldim), l2(l2ptr, ldim); - - Eigen::Matrix X(ldim, 2); - X.col(0).noalias() = l1 * e11 + l2 * e21; - X.col(1).noalias() = l1 * e21 + l2 * e22; - - // B -= l * inv(E) * l' = X * l', B = A[(k+2):end, (k+2):end] - for(Index j = 0; j < ldim; j++) - { - MapVec(col_pointer(j + k + 2), ldim - j).noalias() -= (X.col(0).tail(ldim - j) * l1ptr[j] + X.col(1).tail(ldim - j) * l2ptr[j]); - } - - // l = X - l1.noalias() = X.col(0); - l2.noalias() = X.col(1); - - return SUCCESSFUL; - } - -public: - BKLDLT() : - m_n(0), m_computed(false), m_info(NOT_COMPUTED) - {} - - // Factorize mat - shift * I - BKLDLT(ConstGenericMatrix& mat, int uplo = Eigen::Lower, const Scalar& shift = Scalar(0)) : - m_n(mat.rows()), m_computed(false), m_info(NOT_COMPUTED) - { - compute(mat, uplo, shift); - } - - void compute(ConstGenericMatrix& mat, int uplo = Eigen::Lower, const Scalar& shift = Scalar(0)) - { - using std::abs; - - m_n = mat.rows(); - if(m_n != mat.cols()) - throw std::invalid_argument("BKLDLT: matrix must be square"); - - m_perm.setLinSpaced(m_n, 0, m_n - 1); - m_permc.clear(); - - // Copy data - m_data.resize((m_n * (m_n + 1)) / 2); - compute_pointer(); - copy_data(mat, uplo, shift); - - const Scalar alpha = (1.0 + std::sqrt(17.0)) / 8.0; - Index k = 0; - for(k = 0; k < m_n - 1; k++) - { - // 1. Interchange rows and columns of A, and save the result to m_perm - bool is_1x1 = permutate_mat(k, alpha); - - // 2. Gaussian elimination - if(is_1x1) - { - m_info = gaussian_elimination_1x1(k); - } else { - m_info = gaussian_elimination_2x2(k); - k++; - } - - // 3. Check status - if(m_info != SUCCESSFUL) - break; - } - // Invert the last 1x1 block if it exists - if(k == m_n - 1) - { - const Scalar akk = diag_coeff(k); - if(akk == Scalar(0)) - m_info = NUMERICAL_ISSUE; - - diag_coeff(k) = Scalar(1) / diag_coeff(k); - } - - compress_permutation(); - - m_computed = true; - } - - // Solve Ax=b - void solve_inplace(GenericVector b) const - { - if(!m_computed) - throw std::logic_error("BKLDLT: need to call compute() first"); - - // PAP' = LDL' - // 1. b -> Pb - Scalar* x = b.data(); - MapVec res(x, m_n); - Index npermc = m_permc.size(); - for(Index i = 0; i < npermc; i++) - { - std::swap(x[m_permc[i].first], x[m_permc[i].second]); - } - - // 2. Lz = Pb - // If m_perm[end] < 0, then end with m_n - 3, otherwise end with m_n - 2 - const Index end = (m_perm[m_n - 1] < 0) ? (m_n - 3) : (m_n - 2); - for(Index i = 0; i <= end; i++) - { - const Index b1size = m_n - i - 1; - const Index b2size = b1size - 1; - if(m_perm[i] >= 0) - { - MapConstVec l(&coeff(i + 1, i), b1size); - res.segment(i + 1, b1size).noalias() -= l * x[i]; - } else { - MapConstVec l1(&coeff(i + 2, i), b2size); - MapConstVec l2(&coeff(i + 2, i + 1), b2size); - res.segment(i + 2, b2size).noalias() -= (l1 * x[i] + l2 * x[i + 1]); - i++; - } - } - - // 3. Dw = z - for(Index i = 0; i < m_n; i++) - { - const Scalar e11 = diag_coeff(i); - if(m_perm[i] >= 0) - { - x[i] *= e11; - } else { - const Scalar e21 = coeff(i + 1, i), e22 = diag_coeff(i + 1); - const Scalar wi = x[i] * e11 + x[i + 1] * e21; - x[i + 1] = x[i] * e21 + x[i + 1] * e22; - x[i] = wi; - i++; - } - } - - // 4. L'y = w - // If m_perm[end] < 0, then start with m_n - 3, otherwise start with m_n - 2 - Index i = (m_perm[m_n - 1] < 0) ? (m_n - 3) : (m_n - 2); - for(; i >= 0; i--) - { - const Index ldim = m_n - i - 1; - MapConstVec l(&coeff(i + 1, i), ldim); - x[i] -= res.segment(i + 1, ldim).dot(l); - - if(m_perm[i] < 0) - { - MapConstVec l2(&coeff(i + 1, i - 1), ldim); - x[i - 1] -= res.segment(i + 1, ldim).dot(l2); - i--; - } - } - - // 5. x = P'y - for(Index i = npermc - 1; i >= 0; i--) - { - std::swap(x[m_permc[i].first], x[m_permc[i].second]); - } - } - - Vector solve(ConstGenericVector& b) const - { - Vector res = b; - solve_inplace(res); - return res; - } - - int info() const { return m_info; } -}; - - -} // namespace Spectra - -#endif // BK_LDLT_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/DoubleShiftQR.h b/src/external/Spectra/include/Spectra/LinAlg/DoubleShiftQR.h deleted file mode 100644 index 2191909a..00000000 --- a/src/external/Spectra/include/Spectra/LinAlg/DoubleShiftQR.h +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef DOUBLE_SHIFT_QR_H -#define DOUBLE_SHIFT_QR_H - -#include -#include // std::vector -#include // std::min, std::fill, std::copy -#include // std::abs, std::sqrt, std::pow -#include // std::invalid_argument, std::logic_error - -#include "../Util/TypeTraits.h" - -namespace Spectra { - - -template -class DoubleShiftQR -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Matrix3X; - typedef Eigen::Matrix Vector; - typedef Eigen::Array IntArray; - - typedef Eigen::Ref GenericMatrix; - typedef const Eigen::Ref ConstGenericMatrix; - - Index m_n; // Dimension of the matrix - Matrix m_mat_H; // A copy of the matrix to be factorized - Scalar m_shift_s; // Shift constant - Scalar m_shift_t; // Shift constant - Matrix3X m_ref_u; // Householder reflectors - IntArray m_ref_nr; // How many rows does each reflector affects - // 3 - A general reflector - // 2 - A Givens rotation - // 1 - An identity transformation - const Scalar m_near_0; // a very small value, but 1.0 / m_safe_min does not overflow - // ~= 1e-307 for the "double" type - const Scalar m_eps; // the machine precision, - // e.g. ~= 1e-16 for the "double" type - const Scalar m_eps_rel; - const Scalar m_eps_abs; - bool m_computed; // Whether matrix has been factorized - - void compute_reflector(const Scalar& x1, const Scalar& x2, const Scalar& x3, Index ind) - { - using std::abs; - - Scalar* u = &m_ref_u.coeffRef(0, ind); - unsigned char* nr = m_ref_nr.data(); - // In general case the reflector affects 3 rows - nr[ind] = 3; - Scalar x2x3 = Scalar(0); - // If x3 is zero, decrease nr by 1 - if(abs(x3) < m_near_0) - { - // If x2 is also zero, nr will be 1, and we can exit this function - if(abs(x2) < m_near_0) - { - nr[ind] = 1; - return; - } else { - nr[ind] = 2; - } - x2x3 = abs(x2); - } else { - x2x3 = Eigen::numext::hypot(x2, x3); - } - - // x1' = x1 - rho * ||x|| - // rho = -sign(x1), if x1 == 0, we choose rho = 1 - Scalar x1_new = x1 - ((x1 <= 0) - (x1 > 0)) * Eigen::numext::hypot(x1, x2x3); - Scalar x_norm = Eigen::numext::hypot(x1_new, x2x3); - // Double check the norm of new x - if(x_norm < m_near_0) - { - nr[ind] = 1; - return; - } - u[0] = x1_new / x_norm; - u[1] = x2 / x_norm; - u[2] = x3 / x_norm; - } - - void compute_reflector(const Scalar* x, Index ind) - { - compute_reflector(x[0], x[1], x[2], ind); - } - - // Update the block X = H(il:iu, il:iu) - void update_block(Index il, Index iu) - { - // Block size - const Index bsize = iu - il + 1; - - // If block size == 1, there is no need to apply reflectors - if(bsize == 1) - { - m_ref_nr.coeffRef(il) = 1; - return; - } - - const Scalar x00 = m_mat_H.coeff(il, il), - x01 = m_mat_H.coeff(il, il + 1), - x10 = m_mat_H.coeff(il + 1, il), - x11 = m_mat_H.coeff(il + 1, il + 1); - // m00 = x00 * (x00 - s) + x01 * x10 + t - const Scalar m00 = x00 * (x00 - m_shift_s) + x01 * x10 + m_shift_t; - // m10 = x10 * (x00 + x11 - s) - const Scalar m10 = x10 * (x00 + x11 - m_shift_s); - - // For block size == 2, do a Givens rotation on M = X * X - s * X + t * I - if(bsize == 2) - { - // This causes nr=2 - compute_reflector(m00, m10, 0, il); - // Apply the reflector to X - apply_PX(m_mat_H.block(il, il, 2, m_n - il), m_n, il); - apply_XP(m_mat_H.block(0, il, il + 2, 2), m_n, il); - - m_ref_nr.coeffRef(il + 1) = 1; - return; - } - - // For block size >=3, use the regular strategy - // m20 = x21 * x10 - const Scalar m20 = m_mat_H.coeff(il + 2, il + 1) * m_mat_H.coeff(il + 1, il); - compute_reflector(m00, m10, m20, il); - - // Apply the first reflector - apply_PX(m_mat_H.block(il, il, 3, m_n - il), m_n, il); - apply_XP(m_mat_H.block(0, il, il + std::min(bsize, Index(4)), 3), m_n, il); - - // Calculate the following reflectors - // If entering this loop, block size is at least 4. - for(Index i = 1; i < bsize - 2; i++) - { - compute_reflector(&m_mat_H.coeffRef(il + i, il + i - 1), il + i); - // Apply the reflector to X - apply_PX(m_mat_H.block(il + i, il + i - 1, 3, m_n - il - i + 1), m_n, il + i); - apply_XP(m_mat_H.block(0, il + i, il + std::min(bsize, Index(i + 4)), 3), m_n, il + i); - } - - // The last reflector - // This causes nr=2 - compute_reflector(m_mat_H.coeff(iu - 1, iu - 2), m_mat_H.coeff(iu, iu - 2), 0, iu - 1); - // Apply the reflector to X - apply_PX(m_mat_H.block(iu - 1, iu - 2, 2, m_n - iu + 2), m_n, iu - 1); - apply_XP(m_mat_H.block(0, iu - 1, il + bsize, 2), m_n, iu - 1); - - m_ref_nr.coeffRef(iu) = 1; - } - - // P = I - 2 * u * u' = P' - // PX = X - 2 * u * (u'X) - void apply_PX(GenericMatrix X, Index stride, Index u_ind) const - { - const Index nr = m_ref_nr.coeff(u_ind); - if(nr == 1) - return; - - const Scalar u0 = m_ref_u.coeff(0, u_ind), - u1 = m_ref_u.coeff(1, u_ind); - const Scalar u0_2 = Scalar(2) * u0, - u1_2 = Scalar(2) * u1; - - const Index nrow = X.rows(); - const Index ncol = X.cols(); - - Scalar* xptr = X.data(); - if(nr == 2 || nrow == 2) - { - for(Index i = 0; i < ncol; i++, xptr += stride) - { - const Scalar tmp = u0_2 * xptr[0] + u1_2 * xptr[1]; - xptr[0] -= tmp * u0; - xptr[1] -= tmp * u1; - } - } else { - const Scalar u2 = m_ref_u.coeff(2, u_ind); - const Scalar u2_2 = Scalar(2) * u2; - for(Index i = 0; i < ncol; i++, xptr += stride) - { - const Scalar tmp = u0_2 * xptr[0] + u1_2 * xptr[1] + u2_2 * xptr[2]; - xptr[0] -= tmp * u0; - xptr[1] -= tmp * u1; - xptr[2] -= tmp * u2; - } - } - } - - // x is a pointer to a vector - // Px = x - 2 * dot(x, u) * u - void apply_PX(Scalar* x, Index u_ind) const - { - const Index nr = m_ref_nr.coeff(u_ind); - if(nr == 1) - return; - - const Scalar u0 = m_ref_u.coeff(0, u_ind), - u1 = m_ref_u.coeff(1, u_ind), - u2 = m_ref_u.coeff(2, u_ind); - - // When the reflector only contains two elements, u2 has been set to zero - const bool nr_is_2 = (nr == 2); - const Scalar dot2 = Scalar(2) * (x[0] * u0 + x[1] * u1 + (nr_is_2 ? 0 : (x[2] * u2))); - x[0] -= dot2 * u0; - x[1] -= dot2 * u1; - if(!nr_is_2) - x[2] -= dot2 * u2; - } - - // XP = X - 2 * (X * u) * u' - void apply_XP(GenericMatrix X, Index stride, Index u_ind) const - { - const Index nr = m_ref_nr.coeff(u_ind); - if(nr == 1) - return; - - const Scalar u0 = m_ref_u.coeff(0, u_ind), - u1 = m_ref_u.coeff(1, u_ind); - const Scalar u0_2 = Scalar(2) * u0, - u1_2 = Scalar(2) * u1; - - const int nrow = X.rows(); - const int ncol = X.cols(); - Scalar *X0 = X.data(), *X1 = X0 + stride; // X0 => X.col(0), X1 => X.col(1) - - if(nr == 2 || ncol == 2) - { - // tmp = 2 * u0 * X0 + 2 * u1 * X1 - // X0 => X0 - u0 * tmp - // X1 => X1 - u1 * tmp - for(Index i = 0; i < nrow; i++) - { - const Scalar tmp = u0_2 * X0[i] + u1_2 * X1[i]; - X0[i] -= tmp * u0; - X1[i] -= tmp * u1; - } - } else { - Scalar* X2 = X1 + stride; // X2 => X.col(2) - const Scalar u2 = m_ref_u.coeff(2, u_ind); - const Scalar u2_2 = Scalar(2) * u2; - for(Index i = 0; i < nrow; i++) - { - const Scalar tmp = u0_2 * X0[i] + u1_2 * X1[i] + u2_2 * X2[i]; - X0[i] -= tmp * u0; - X1[i] -= tmp * u1; - X2[i] -= tmp * u2; - } - } - } - -public: - DoubleShiftQR(Index size) : - m_n(size), - m_near_0(TypeTraits::min() * Scalar(10)), - m_eps(Eigen::NumTraits::epsilon()), - m_eps_rel(m_eps), - m_eps_abs(m_near_0 * (m_n / m_eps)), - m_computed(false) - {} - - DoubleShiftQR(ConstGenericMatrix& mat, const Scalar& s, const Scalar& t) : - m_n(mat.rows()), - m_mat_H(m_n, m_n), - m_shift_s(s), - m_shift_t(t), - m_ref_u(3, m_n), - m_ref_nr(m_n), - m_near_0(TypeTraits::min() * Scalar(10)), - m_eps(Eigen::NumTraits::epsilon()), - m_eps_rel(m_eps), - m_eps_abs(m_near_0 * (m_n / m_eps)), - m_computed(false) - { - compute(mat, s, t); - } - - void compute(ConstGenericMatrix& mat, const Scalar& s, const Scalar& t) - { - using std::abs; - - m_n = mat.rows(); - if(m_n != mat.cols()) - throw std::invalid_argument("DoubleShiftQR: matrix must be square"); - - m_mat_H.resize(m_n, m_n); - m_shift_s = s; - m_shift_t = t; - m_ref_u.resize(3, m_n); - m_ref_nr.resize(m_n); - - // Make a copy of mat - std::copy(mat.data(), mat.data() + mat.size(), m_mat_H.data()); - - // Obtain the indices of zero elements in the subdiagonal, - // so that H can be divided into several blocks - std::vector zero_ind; - zero_ind.reserve(m_n - 1); - zero_ind.push_back(0); - Scalar* Hii = m_mat_H.data(); - for(Index i = 0; i < m_n - 2; i++, Hii += (m_n + 1)) - { - // Hii[1] => m_mat_H(i + 1, i) - const Scalar h = abs(Hii[1]); - if(h <= 0 || h <= m_eps_rel * (abs(Hii[0]) + abs(Hii[m_n + 1]))) - { - Hii[1] = 0; - zero_ind.push_back(i + 1); - } - // Make sure m_mat_H is upper Hessenberg - // Zero the elements below m_mat_H(i + 1, i) - std::fill(Hii + 2, Hii + m_n - i, Scalar(0)); - } - zero_ind.push_back(m_n); - - for(std::vector::size_type i = 0; i < zero_ind.size() - 1; i++) - { - const Index start = zero_ind[i]; - const Index end = zero_ind[i + 1] - 1; - // Compute refelctors and update each block - update_block(start, end); - } - - m_computed = true; - } - - void matrix_QtHQ(Matrix& dest) const - { - if(!m_computed) - throw std::logic_error("DoubleShiftQR: need to call compute() first"); - - dest.noalias() = m_mat_H; - } - - // Q = P0 * P1 * ... - // Q'y = P_{n-2} * ... * P1 * P0 * y - void apply_QtY(Vector& y) const - { - if(!m_computed) - throw std::logic_error("DoubleShiftQR: need to call compute() first"); - - Scalar* y_ptr = y.data(); - const Index n1 = m_n - 1; - for(Index i = 0; i < n1; i++, y_ptr++) - { - apply_PX(y_ptr, i); - } - } - - // Q = P0 * P1 * ... - // YQ = Y * P0 * P1 * ... - void apply_YQ(GenericMatrix Y) const - { - if(!m_computed) - throw std::logic_error("DoubleShiftQR: need to call compute() first"); - - const Index nrow = Y.rows(); - const Index n2 = m_n - 2; - for(Index i = 0; i < n2; i++) - { - apply_XP(Y.block(0, i, nrow, 3), nrow, i); - } - apply_XP(Y.block(0, n2, nrow, 2), nrow, n2); - } -}; - - -} // namespace Spectra - -#endif // DOUBLE_SHIFT_QR_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/Lanczos.h b/src/external/Spectra/include/Spectra/LinAlg/Lanczos.h deleted file mode 100644 index 2301dd30..00000000 --- a/src/external/Spectra/include/Spectra/LinAlg/Lanczos.h +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (C) 2018-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef LANCZOS_H -#define LANCZOS_H - -#include -#include // std::sqrt -#include // std::invalid_argument -#include // std::stringstream - -#include "Arnoldi.h" - -namespace Spectra { - - -// Lanczos factorization A * V = V * H + f * e' -// A: n x n -// V: n x k -// H: k x k -// f: n x 1 -// e: [0, ..., 0, 1] -// V and H are allocated of dimension m, so the maximum value of k is m -template -class Lanczos: public Arnoldi -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapMat; - typedef Eigen::Map MapVec; - typedef Eigen::Map MapConstMat; - typedef Eigen::Map MapConstVec; - - using Arnoldi::m_op; - using Arnoldi::m_n; - using Arnoldi::m_m; - using Arnoldi::m_k; - using Arnoldi::m_fac_V; - using Arnoldi::m_fac_H; - using Arnoldi::m_fac_f; - using Arnoldi::m_beta; - using Arnoldi::m_near_0; - using Arnoldi::m_eps; - -public: - Lanczos(const ArnoldiOpType& op, Index m) : - Arnoldi(op, m) - {} - - // Lanczos factorization starting from step-k - void factorize_from(Index from_k, Index to_m, Index& op_counter) - { - using std::sqrt; - - if(to_m <= from_k) return; - - if(from_k > m_k) - { - std::stringstream msg; - msg << "Lanczos: from_k (= " << from_k << - ") is larger than the current subspace dimension (= " << - m_k << ")"; - throw std::invalid_argument(msg.str()); - } - - const Scalar beta_thresh = m_eps * sqrt(Scalar(m_n)); - - // Pre-allocate vectors - Vector Vf(to_m); - Vector w(m_n); - - // Keep the upperleft k x k submatrix of H and set other elements to 0 - m_fac_H.rightCols(m_m - from_k).setZero(); - m_fac_H.block(from_k, 0, m_m - from_k, from_k).setZero(); - - for(Index i = from_k; i <= to_m - 1; i++) - { - bool restart = false; - // If beta = 0, then the next V is not full rank - // We need to generate a new residual vector that is orthogonal - // to the current V, which we call a restart - if(m_beta < m_near_0) - { - MapConstMat V(m_fac_V.data(), m_n, i); // The first i columns - this->expand_basis(V, 2 * i, m_fac_f, m_beta); - restart = true; - } - - // v <- f / ||f|| - MapVec v(&m_fac_V(0, i), m_n); // The (i+1)-th column - v.noalias() = m_fac_f / m_beta; - - // Note that H[i+1, i] equals to the unrestarted beta - m_fac_H(i, i - 1) = restart ? Scalar(0) : m_beta; - - // w <- A * v - m_op.perform_op(v.data(), w.data()); - op_counter++; - - // H[i+1, i+1] = = v'Bw - m_fac_H(i - 1, i) = m_fac_H(i, i - 1); // Due to symmetry - m_fac_H(i, i) = m_op.inner_product(v, w); - - // f <- w - V * V'Bw = w - H[i+1, i] * V{i} - H[i+1, i+1] * V{i+1} - // If restarting, we know that H[i+1, i] = 0 - if(restart) - m_fac_f.noalias() = w - m_fac_H(i, i) * v; - else - m_fac_f.noalias() = w - m_fac_H(i, i - 1) * m_fac_V.col(i - 1) - m_fac_H(i, i) * v; - - m_beta = m_op.norm(m_fac_f); - - // f/||f|| is going to be the next column of V, so we need to test - // whether V'B(f/||f||) ~= 0 - const Index i1 = i + 1; - MapMat Vs(m_fac_V.data(), m_n, i1); // The first (i+1) columns - m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); - Scalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); - // If not, iteratively correct the residual - int count = 0; - while(count < 5 && ortho_err > m_eps * m_beta) - { - // There is an edge case: when beta=||f|| is close to zero, f mostly consists - // of noises of rounding errors, so the test [ortho_err < eps * beta] is very - // likely to fail. In particular, if beta=0, then the test is ensured to fail. - // Hence when this happens, we force f to be zero, and then restart in the - // next iteration. - if(m_beta < beta_thresh) - { - m_fac_f.setZero(); - m_beta = Scalar(0); - break; - } - - // f <- f - V * Vf - m_fac_f.noalias() -= Vs * Vf.head(i1); - // h <- h + Vf - m_fac_H(i - 1, i) += Vf[i - 1]; - m_fac_H(i, i - 1) = m_fac_H(i - 1, i); - m_fac_H(i, i) += Vf[i]; - // beta <- ||f|| - m_beta = m_op.norm(m_fac_f); - - m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); - ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); - count++; - } - } - - // Indicate that this is a step-m factorization - m_k = to_m; - } - - // Apply H -> Q'HQ, where Q is from a tridiagonal QR decomposition - void compress_H(const TridiagQR& decomp) - { - decomp.matrix_QtHQ(m_fac_H); - m_k--; - } -}; - - -} // namespace Spectra - -#endif // LANCZOS_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/TridiagEigen.h b/src/external/Spectra/include/Spectra/LinAlg/TridiagEigen.h deleted file mode 100644 index b79fe8d1..00000000 --- a/src/external/Spectra/include/Spectra/LinAlg/TridiagEigen.h +++ /dev/null @@ -1,219 +0,0 @@ -// The code was adapted from Eigen/src/Eigenvaleus/SelfAdjointEigenSolver.h -// -// Copyright (C) 2008-2010 Gael Guennebaud -// Copyright (C) 2010 Jitse Niesen -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef TRIDIAG_EIGEN_H -#define TRIDIAG_EIGEN_H - -#include -#include -#include - -#include "../Util/TypeTraits.h" - -namespace Spectra { - - -template -class TridiagEigen -{ -private: - typedef Eigen::Index Index; - // For convenience in adapting the tridiagonal_qr_step() function - typedef Scalar RealScalar; - - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - - typedef Eigen::Ref GenericMatrix; - typedef const Eigen::Ref ConstGenericMatrix; - - Index m_n; - Vector m_main_diag; // Main diagonal elements of the matrix - Vector m_sub_diag; // Sub-diagonal elements of the matrix - Matrix m_evecs; // To store eigenvectors - - bool m_computed; - const Scalar m_near_0; // a very small value, ~= 1e-307 for the "double" type - - // Adapted from Eigen/src/Eigenvaleus/SelfAdjointEigenSolver.h - static void tridiagonal_qr_step(RealScalar* diag, - RealScalar* subdiag, Index start, - Index end, Scalar* matrixQ, - Index n) - { - using std::abs; - - RealScalar td = (diag[end-1] - diag[end]) * RealScalar(0.5); - RealScalar e = subdiag[end-1]; - // Note that thanks to scaling, e^2 or td^2 cannot overflow, however they can still - // underflow thus leading to inf/NaN values when using the following commented code: - // RealScalar e2 = numext::abs2(subdiag[end-1]); - // RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * sqrt(td*td + e2)); - // This explain the following, somewhat more complicated, version: - RealScalar mu = diag[end]; - if(td == Scalar(0)) - mu -= abs(e); - else - { - RealScalar e2 = Eigen::numext::abs2(subdiag[end-1]); - RealScalar h = Eigen::numext::hypot(td, e); - if(e2==RealScalar(0)) mu -= (e / (td + (td>RealScalar(0) ? RealScalar(1) : RealScalar(-1)))) * (e / h); - else mu -= e2 / (td + (td>RealScalar(0) ? h : -h)); - } - - RealScalar x = diag[start] - mu; - RealScalar z = subdiag[start]; - Eigen::Map q(matrixQ, n, n); - for(Index k = start; k < end; ++k) - { - Eigen::JacobiRotation rot; - rot.makeGivens(x, z); - - const RealScalar s = rot.s(); - const RealScalar c = rot.c(); - - // do T = G' T G - RealScalar sdk = s * diag[k] + c * subdiag[k]; - RealScalar dkp1 = s * subdiag[k] + c * diag[k + 1]; - - diag[k] = c * (c * diag[k] - s * subdiag[k]) - s * (c * subdiag[k] - s * diag[k + 1]); - diag[k + 1] = s * sdk + c * dkp1; - subdiag[k] = c * sdk - s * dkp1; - - if(k > start) - subdiag[k - 1] = c * subdiag[k - 1] - s * z; - - x = subdiag[k]; - - if(k < end - 1) - { - z = -s * subdiag[k+1]; - subdiag[k + 1] = c * subdiag[k + 1]; - } - - // apply the givens rotation to the unit matrix Q = Q * G - if(matrixQ) - q.applyOnTheRight(k, k + 1, rot); - } - } - -public: - TridiagEigen() : - m_n(0), m_computed(false), - m_near_0(TypeTraits::min() * Scalar(10)) - {} - - TridiagEigen(ConstGenericMatrix& mat) : - m_n(mat.rows()), m_computed(false), - m_near_0(TypeTraits::min() * Scalar(10)) - { - compute(mat); - } - - void compute(ConstGenericMatrix& mat) - { - using std::abs; - - m_n = mat.rows(); - if(m_n != mat.cols()) - throw std::invalid_argument("TridiagEigen: matrix must be square"); - - m_main_diag.resize(m_n); - m_sub_diag.resize(m_n - 1); - m_evecs.resize(m_n, m_n); - m_evecs.setIdentity(); - - // Scale matrix to improve stability - const Scalar scale = std::max(mat.diagonal().cwiseAbs().maxCoeff(), - mat.diagonal(-1).cwiseAbs().maxCoeff()); - // If scale=0, mat is a zero matrix, so we can early stop - if(scale < m_near_0) - { - // m_main_diag contains eigenvalues - m_main_diag.setZero(); - // m_evecs has been set identity - // m_evecs.setIdentity(); - m_computed = true; - return; - } - m_main_diag.noalias() = mat.diagonal() / scale; - m_sub_diag.noalias() = mat.diagonal(-1) / scale; - - Scalar* diag = m_main_diag.data(); - Scalar* subdiag = m_sub_diag.data(); - - Index end = m_n - 1; - Index start = 0; - Index iter = 0; // total number of iterations - int info = 0; // 0 for success, 1 for failure - - const Scalar considerAsZero = TypeTraits::min(); - const Scalar precision = Scalar(2) * Eigen::NumTraits::epsilon(); - - while(end > 0) - { - for(Index i = start; i < end; i++) - if(abs(subdiag[i]) <= considerAsZero || - abs(subdiag[i]) <= (abs(diag[i]) + abs(diag[i + 1])) * precision) - subdiag[i] = 0; - - // find the largest unreduced block - while(end > 0 && subdiag[end - 1] == Scalar(0)) - end--; - - if(end <= 0) - break; - - // if we spent too many iterations, we give up - iter++; - if(iter > 30 * m_n) - { - info = 1; - break; - } - - start = end - 1; - while(start > 0 && subdiag[start - 1] != Scalar(0)) - start--; - - tridiagonal_qr_step(diag, subdiag, start, end, m_evecs.data(), m_n); - } - - if(info > 0) - throw std::runtime_error("TridiagEigen: eigen decomposition failed"); - - // Scale eigenvalues back - m_main_diag *= scale; - - m_computed = true; - } - - const Vector& eigenvalues() const - { - if(!m_computed) - throw std::logic_error("TridiagEigen: need to call compute() first"); - - // After calling compute(), main_diag will contain the eigenvalues. - return m_main_diag; - } - - const Matrix& eigenvectors() const - { - if(!m_computed) - throw std::logic_error("TridiagEigen: need to call compute() first"); - - return m_evecs; - } -}; - - -} // namespace Spectra - -#endif // TRIDIAG_EIGEN_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergEigen.h b/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergEigen.h deleted file mode 100644 index 4e099f56..00000000 --- a/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergEigen.h +++ /dev/null @@ -1,317 +0,0 @@ -// The code was adapted from Eigen/src/Eigenvaleus/EigenSolver.h -// -// Copyright (C) 2008 Gael Guennebaud -// Copyright (C) 2010,2012 Jitse Niesen -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef UPPER_HESSENBERG_EIGEN_H -#define UPPER_HESSENBERG_EIGEN_H - -#include -#include -#include - -namespace Spectra { - - -template -class UpperHessenbergEigen -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - - typedef Eigen::Ref GenericMatrix; - typedef const Eigen::Ref ConstGenericMatrix; - - typedef std::complex Complex; - typedef Eigen::Matrix ComplexMatrix; - typedef Eigen::Matrix ComplexVector; - - Index m_n; // Size of the matrix - Eigen::RealSchur m_realSchur; // Schur decomposition solver - Matrix m_matT; // Schur T matrix - Matrix m_eivec; // Storing eigenvectors - ComplexVector m_eivalues; // Eigenvalues - - bool m_computed; - - void doComputeEigenvectors() - { - using std::abs; - - const Index size = m_eivec.cols(); - const Scalar eps = Eigen::NumTraits::epsilon(); - - // inefficient! this is already computed in RealSchur - Scalar norm(0); - for(Index j = 0; j < size; ++j) - { - norm += m_matT.row(j).segment((std::max)(j-1, Index(0)), size-(std::max)(j-1, Index(0))).cwiseAbs().sum(); - } - - // Backsubstitute to find vectors of upper triangular form - if(norm == Scalar(0)) - return; - - for(Index n = size - 1; n >= 0; n--) - { - Scalar p = m_eivalues.coeff(n).real(); - Scalar q = m_eivalues.coeff(n).imag(); - - // Scalar vector - if(q == Scalar(0)) - { - Scalar lastr(0), lastw(0); - Index l = n; - - m_matT.coeffRef(n,n) = Scalar(1); - for(Index i = n-1; i >= 0; i--) - { - Scalar w = m_matT.coeff(i,i) - p; - Scalar r = m_matT.row(i).segment(l,n-l+1).dot(m_matT.col(n).segment(l, n-l+1)); - - if(m_eivalues.coeff(i).imag() < Scalar(0)) - { - lastw = w; - lastr = r; - } else { - l = i; - if(m_eivalues.coeff(i).imag() == Scalar(0)) - { - if (w != Scalar(0)) - m_matT.coeffRef(i,n) = -r / w; - else - m_matT.coeffRef(i,n) = -r / (eps * norm); - } - else // Solve real equations - { - Scalar x = m_matT.coeff(i,i+1); - Scalar y = m_matT.coeff(i+1,i); - Scalar denom = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag(); - Scalar t = (x * lastr - lastw * r) / denom; - m_matT.coeffRef(i,n) = t; - if(abs(x) > abs(lastw)) - m_matT.coeffRef(i+1,n) = (-r - w * t) / x; - else - m_matT.coeffRef(i+1,n) = (-lastr - y * t) / lastw; - } - - // Overflow control - Scalar t = abs(m_matT.coeff(i,n)); - if((eps * t) * t > Scalar(1)) - m_matT.col(n).tail(size-i) /= t; - } - } - } else if(q < Scalar(0) && n > 0) { // Complex vector - Scalar lastra(0), lastsa(0), lastw(0); - Index l = n-1; - - // Last vector component imaginary so matrix is triangular - if(abs(m_matT.coeff(n,n-1)) > abs(m_matT.coeff(n-1,n))) - { - m_matT.coeffRef(n-1,n-1) = q / m_matT.coeff(n,n-1); - m_matT.coeffRef(n-1,n) = -(m_matT.coeff(n,n) - p) / m_matT.coeff(n,n-1); - } - else - { - Complex cc = Complex(Scalar(0),-m_matT.coeff(n-1,n)) / Complex(m_matT.coeff(n-1,n-1)-p,q); - m_matT.coeffRef(n-1,n-1) = Eigen::numext::real(cc); - m_matT.coeffRef(n-1,n) = Eigen::numext::imag(cc); - } - m_matT.coeffRef(n,n-1) = Scalar(0); - m_matT.coeffRef(n,n) = Scalar(1); - for(Index i = n-2; i >= 0; i--) - { - Scalar ra = m_matT.row(i).segment(l, n-l+1).dot(m_matT.col(n-1).segment(l, n-l+1)); - Scalar sa = m_matT.row(i).segment(l, n-l+1).dot(m_matT.col(n).segment(l, n-l+1)); - Scalar w = m_matT.coeff(i,i) - p; - - if(m_eivalues.coeff(i).imag() < Scalar(0)) - { - lastw = w; - lastra = ra; - lastsa = sa; - } - else - { - l = i; - if(m_eivalues.coeff(i).imag() == Scalar(0)) - { - Complex cc = Complex(-ra,-sa) / Complex(w,q); - m_matT.coeffRef(i,n-1) = Eigen::numext::real(cc); - m_matT.coeffRef(i,n) = Eigen::numext::imag(cc); - } - else - { - // Solve complex equations - Scalar x = m_matT.coeff(i,i+1); - Scalar y = m_matT.coeff(i+1,i); - Scalar vr = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag() - q * q; - Scalar vi = (m_eivalues.coeff(i).real() - p) * Scalar(2) * q; - if((vr == Scalar(0)) && (vi == Scalar(0))) - vr = eps * norm * (abs(w) + abs(q) + abs(x) + abs(y) + abs(lastw)); - - Complex cc = Complex(x*lastra-lastw*ra+q*sa,x*lastsa-lastw*sa-q*ra) / Complex(vr,vi); - m_matT.coeffRef(i,n-1) = Eigen::numext::real(cc); - m_matT.coeffRef(i,n) = Eigen::numext::imag(cc); - if(abs(x) > (abs(lastw) + abs(q))) - { - m_matT.coeffRef(i+1,n-1) = (-ra - w * m_matT.coeff(i,n-1) + q * m_matT.coeff(i,n)) / x; - m_matT.coeffRef(i+1,n) = (-sa - w * m_matT.coeff(i,n) - q * m_matT.coeff(i,n-1)) / x; - } - else - { - cc = Complex(-lastra-y*m_matT.coeff(i,n-1),-lastsa-y*m_matT.coeff(i,n)) / Complex(lastw,q); - m_matT.coeffRef(i+1,n-1) = Eigen::numext::real(cc); - m_matT.coeffRef(i+1,n) = Eigen::numext::imag(cc); - } - } - - // Overflow control - Scalar t = std::max(abs(m_matT.coeff(i,n-1)), abs(m_matT.coeff(i,n))); - if((eps * t) * t > Scalar(1)) - m_matT.block(i, n-1, size-i, 2) /= t; - - } - } - - // We handled a pair of complex conjugate eigenvalues, so need to skip them both - n--; - } - } - - // Back transformation to get eigenvectors of original matrix - Vector m_tmp(size); - for(Index j = size-1; j >= 0; j--) - { - m_tmp.noalias() = m_eivec.leftCols(j+1) * m_matT.col(j).segment(0, j+1); - m_eivec.col(j) = m_tmp; - } - } - -public: - - UpperHessenbergEigen() : - m_n(0), m_computed(false) - {} - - UpperHessenbergEigen(ConstGenericMatrix& mat) : - m_n(mat.rows()), m_computed(false) - { - compute(mat); - } - - void compute(ConstGenericMatrix& mat) - { - using std::abs; - using std::sqrt; - - if(mat.rows() != mat.cols()) - throw std::invalid_argument("UpperHessenbergEigen: matrix must be square"); - - m_n = mat.rows(); - // Scale matrix prior to the Schur decomposition - const Scalar scale = mat.cwiseAbs().maxCoeff(); - - // Reduce to real Schur form - Matrix Q = Matrix::Identity(m_n, m_n); - m_realSchur.computeFromHessenberg(mat / scale, Q, true); - if(m_realSchur.info() != Eigen::Success) - throw std::runtime_error("UpperHessenbergEigen: eigen decomposition failed"); - - m_matT = m_realSchur.matrixT(); - m_eivec = m_realSchur.matrixU(); - - // Compute eigenvalues from matT - m_eivalues.resize(m_n); - Index i = 0; - while(i < m_n) - { - // Real eigenvalue - if(i == m_n - 1 || m_matT.coeff(i+1, i) == Scalar(0)) - { - m_eivalues.coeffRef(i) = m_matT.coeff(i, i); - ++i; - } - else // Complex eigenvalues - { - Scalar p = Scalar(0.5) * (m_matT.coeff(i, i) - m_matT.coeff(i+1, i+1)); - Scalar z; - // Compute z = sqrt(abs(p * p + m_matT.coeff(i+1, i) * m_matT.coeff(i, i+1))); - // without overflow - { - Scalar t0 = m_matT.coeff(i+1, i); - Scalar t1 = m_matT.coeff(i, i+1); - Scalar maxval = std::max(abs(p), std::max(abs(t0), abs(t1))); - t0 /= maxval; - t1 /= maxval; - Scalar p0 = p / maxval; - z = maxval * sqrt(abs(p0 * p0 + t0 * t1)); - } - m_eivalues.coeffRef(i) = Complex(m_matT.coeff(i+1, i+1) + p, z); - m_eivalues.coeffRef(i+1) = Complex(m_matT.coeff(i+1, i+1) + p, -z); - i += 2; - } - } - - // Compute eigenvectors - doComputeEigenvectors(); - - // Scale eigenvalues back - m_eivalues *= scale; - - m_computed = true; - } - - const ComplexVector& eigenvalues() const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergEigen: need to call compute() first"); - - return m_eivalues; - } - - ComplexMatrix eigenvectors() - { - using std::abs; - - if(!m_computed) - throw std::logic_error("UpperHessenbergEigen: need to call compute() first"); - - Index n = m_eivec.cols(); - ComplexMatrix matV(n, n); - for(Index j = 0; j < n; ++j) - { - // imaginary part of real eigenvalue is already set to exact zero - if(Eigen::numext::imag(m_eivalues.coeff(j)) == Scalar(0) || j + 1 == n) - { - // we have a real eigen value - matV.col(j) = m_eivec.col(j).template cast(); - matV.col(j).normalize(); - } else { - // we have a pair of complex eigen values - for(Index i = 0; i < n; ++i) - { - matV.coeffRef(i,j) = Complex(m_eivec.coeff(i,j), m_eivec.coeff(i,j+1)); - matV.coeffRef(i,j+1) = Complex(m_eivec.coeff(i,j), -m_eivec.coeff(i,j+1)); - } - matV.col(j).normalize(); - matV.col(j+1).normalize(); - ++j; - } - } - - return matV; - } -}; - - -} // namespace Spectra - -#endif // UPPER_HESSENBERG_EIGEN_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergQR.h b/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergQR.h deleted file mode 100644 index a66d9598..00000000 --- a/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergQR.h +++ /dev/null @@ -1,670 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef UPPER_HESSENBERG_QR_H -#define UPPER_HESSENBERG_QR_H - -#include -#include // std::sqrt -#include // std::fill, std::copy -#include // std::logic_error - -namespace Spectra { - - -/// -/// \defgroup Internals Internal Classes -/// -/// Classes for internal use. May be useful to developers. -/// - -/// -/// \ingroup Internals -/// @{ -/// - -/// -/// \defgroup LinearAlgebra Linear Algebra -/// -/// A number of classes for linear algebra operations. -/// - -/// -/// \ingroup LinearAlgebra -/// -/// Perform the QR decomposition of an upper Hessenberg matrix. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// -template -class UpperHessenbergQR -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Matrix RowVector; - typedef Eigen::Array Array; - - typedef Eigen::Ref GenericMatrix; - typedef const Eigen::Ref ConstGenericMatrix; - - Matrix m_mat_T; - -protected: - Index m_n; - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - // Q = G1 * G2 * ... * G_{n-1} - Scalar m_shift; - Array m_rot_cos; - Array m_rot_sin; - bool m_computed; - - // Given x and y, compute 1) r = sqrt(x^2 + y^2), 2) c = x / r, 3) s = -y / r - // If both x and y are zero, set c = 1 and s = 0 - // We must implement it in a numerically stable way - static void compute_rotation(const Scalar& x, const Scalar& y, Scalar& r, Scalar& c, Scalar& s) - { - using std::sqrt; - - const Scalar xsign = (x > Scalar(0)) - (x < Scalar(0)); - const Scalar ysign = (y > Scalar(0)) - (y < Scalar(0)); - const Scalar xabs = x * xsign; - const Scalar yabs = y * ysign; - if(xabs > yabs) - { - // In this case xabs != 0 - const Scalar ratio = yabs / xabs; // so that 0 <= ratio < 1 - const Scalar common = sqrt(Scalar(1) + ratio * ratio); - c = xsign / common; - r = xabs * common; - s = -y / r; - } else { - if(yabs == Scalar(0)) - { - r = Scalar(0); c = Scalar(1); s = Scalar(0); - return; - } - const Scalar ratio = xabs / yabs; // so that 0 <= ratio <= 1 - const Scalar common = sqrt(Scalar(1) + ratio * ratio); - s = -ysign / common; - r = yabs * common; - c = x / r; - } - } - -public: - /// - /// Constructor to preallocate memory. Computation can - /// be performed later by calling the compute() method. - /// - UpperHessenbergQR(Index size) : - m_n(size), - m_rot_cos(m_n - 1), - m_rot_sin(m_n - 1), - m_computed(false) - {} - - /// - /// Constructor to create an object that performs and stores the - /// QR decomposition of an upper Hessenberg matrix `mat`, with an - /// optional shift: \f$H-sI=QR\f$. Here \f$H\f$ stands for the matrix - /// `mat`, and \f$s\f$ is the shift. - /// - /// \param mat Matrix type can be `Eigen::Matrix` (e.g. - /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// Only the upper triangular and the lower subdiagonal parts of - /// the matrix are used. - /// - UpperHessenbergQR(ConstGenericMatrix& mat, const Scalar& shift = Scalar(0)) : - m_n(mat.rows()), - m_shift(shift), - m_rot_cos(m_n - 1), - m_rot_sin(m_n - 1), - m_computed(false) - { - compute(mat, shift); - } - - /// - /// Virtual destructor. - /// - virtual ~UpperHessenbergQR() {}; - - /// - /// Conduct the QR factorization of an upper Hessenberg matrix with - /// an optional shift. - /// - /// \param mat Matrix type can be `Eigen::Matrix` (e.g. - /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// Only the upper triangular and the lower subdiagonal parts of - /// the matrix are used. - /// - virtual void compute(ConstGenericMatrix& mat, const Scalar& shift = Scalar(0)) - { - m_n = mat.rows(); - if(m_n != mat.cols()) - throw std::invalid_argument("UpperHessenbergQR: matrix must be square"); - - m_shift = shift; - m_mat_T.resize(m_n, m_n); - m_rot_cos.resize(m_n - 1); - m_rot_sin.resize(m_n - 1); - - // Make a copy of mat - s * I - std::copy(mat.data(), mat.data() + mat.size(), m_mat_T.data()); - m_mat_T.diagonal().array() -= m_shift; - - Scalar xi, xj, r, c, s; - Scalar *Tii, *ptr; - const Index n1 = m_n - 1; - for(Index i = 0; i < n1; i++) - { - Tii = &m_mat_T.coeffRef(i, i); - - // Make sure mat_T is upper Hessenberg - // Zero the elements below mat_T(i + 1, i) - std::fill(Tii + 2, Tii + m_n - i, Scalar(0)); - - xi = Tii[0]; // mat_T(i, i) - xj = Tii[1]; // mat_T(i + 1, i) - compute_rotation(xi, xj, r, c, s); - m_rot_cos[i] = c; - m_rot_sin[i] = s; - - // For a complete QR decomposition, - // we first obtain the rotation matrix - // G = [ cos sin] - // [-sin cos] - // and then do T[i:(i + 1), i:(n - 1)] = G' * T[i:(i + 1), i:(n - 1)] - - // Gt << c, -s, s, c; - // m_mat_T.block(i, i, 2, m_n - i) = Gt * m_mat_T.block(i, i, 2, m_n - i); - Tii[0] = r; // m_mat_T(i, i) => r - Tii[1] = 0; // m_mat_T(i + 1, i) => 0 - ptr = Tii + m_n; // m_mat_T(i, k), k = i+1, i+2, ..., n-1 - for(Index j = i + 1; j < m_n; j++, ptr += m_n) - { - Scalar tmp = ptr[0]; - ptr[0] = c * tmp - s * ptr[1]; - ptr[1] = s * tmp + c * ptr[1]; - } - - // If we do not need to calculate the R matrix, then - // only the cos and sin sequences are required. - // In such case we only update T[i + 1, (i + 1):(n - 1)] - // m_mat_T.block(i + 1, i + 1, 1, m_n - i - 1) *= c; - // m_mat_T.block(i + 1, i + 1, 1, m_n - i - 1) += s * mat_T.block(i, i + 1, 1, m_n - i - 1); - } - - m_computed = true; - } - - /// - /// Return the \f$R\f$ matrix in the QR decomposition, which is an - /// upper triangular matrix. - /// - /// \return Returned matrix type will be `Eigen::Matrix`, depending on - /// the template parameter `Scalar` defined. - /// - virtual Matrix matrix_R() const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergQR: need to call compute() first"); - - return m_mat_T; - } - - /// - /// Overwrite `dest` with \f$Q'HQ = RQ + sI\f$, where \f$H\f$ is the input matrix `mat`, - /// and \f$s\f$ is the shift. The result is an upper Hessenberg matrix. - /// - /// \param mat The matrix to be overwritten, whose type should be `Eigen::Matrix`, - /// depending on the template parameter `Scalar` defined. - /// - virtual void matrix_QtHQ(Matrix& dest) const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergQR: need to call compute() first"); - - // Make a copy of the R matrix - dest.resize(m_n, m_n); - std::copy(m_mat_T.data(), m_mat_T.data() + m_mat_T.size(), dest.data()); - - // Compute the RQ matrix - const Index n1 = m_n - 1; - for(Index i = 0; i < n1; i++) - { - const Scalar c = m_rot_cos.coeff(i); - const Scalar s = m_rot_sin.coeff(i); - // RQ[, i:(i + 1)] = RQ[, i:(i + 1)] * Gi - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - Scalar *Yi, *Yi1; - Yi = &dest.coeffRef(0, i); - Yi1 = Yi + m_n; // RQ(0, i + 1) - const Index i2 = i + 2; - for(Index j = 0; j < i2; j++) - { - const Scalar tmp = Yi[j]; - Yi[j] = c * tmp - s * Yi1[j]; - Yi1[j] = s * tmp + c * Yi1[j]; - } - - /* Vector dest = RQ.block(0, i, i + 2, 1); - dest.block(0, i, i + 2, 1) = c * Yi - s * dest.block(0, i + 1, i + 2, 1); - dest.block(0, i + 1, i + 2, 1) = s * Yi + c * dest.block(0, i + 1, i + 2, 1); */ - } - - // Add the shift to the diagonal - dest.diagonal().array() += m_shift; - } - - /// - /// Apply the \f$Q\f$ matrix to a vector \f$y\f$. - /// - /// \param Y A vector that will be overwritten by the matrix product \f$Qy\f$. - /// - /// Vector type can be `Eigen::Vector`, depending on - /// the template parameter `Scalar` defined. - /// - // Y -> QY = G1 * G2 * ... * Y - void apply_QY(Vector& Y) const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergQR: need to call compute() first"); - - for(Index i = m_n - 2; i >= 0; i--) - { - const Scalar c = m_rot_cos.coeff(i); - const Scalar s = m_rot_sin.coeff(i); - // Y[i:(i + 1)] = Gi * Y[i:(i + 1)] - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - const Scalar tmp = Y[i]; - Y[i] = c * tmp + s * Y[i + 1]; - Y[i + 1] = -s * tmp + c * Y[i + 1]; - } - } - - /// - /// Apply the \f$Q\f$ matrix to a vector \f$y\f$. - /// - /// \param Y A vector that will be overwritten by the matrix product \f$Q'y\f$. - /// - /// Vector type can be `Eigen::Vector`, depending on - /// the template parameter `Scalar` defined. - /// - // Y -> Q'Y = G_{n-1}' * ... * G2' * G1' * Y - void apply_QtY(Vector& Y) const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergQR: need to call compute() first"); - - const Index n1 = m_n - 1; - for(Index i = 0; i < n1; i++) - { - const Scalar c = m_rot_cos.coeff(i); - const Scalar s = m_rot_sin.coeff(i); - // Y[i:(i + 1)] = Gi' * Y[i:(i + 1)] - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - const Scalar tmp = Y[i]; - Y[i] = c * tmp - s * Y[i + 1]; - Y[i + 1] = s * tmp + c * Y[i + 1]; - } - } - - /// - /// Apply the \f$Q\f$ matrix to another matrix \f$Y\f$. - /// - /// \param Y A matrix that will be overwritten by the matrix product \f$QY\f$. - /// - /// Matrix type can be `Eigen::Matrix` (e.g. - /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - // Y -> QY = G1 * G2 * ... * Y - void apply_QY(GenericMatrix Y) const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergQR: need to call compute() first"); - - RowVector Yi(Y.cols()), Yi1(Y.cols()); - for(Index i = m_n - 2; i >= 0; i--) - { - const Scalar c = m_rot_cos.coeff(i); - const Scalar s = m_rot_sin.coeff(i); - // Y[i:(i + 1), ] = Gi * Y[i:(i + 1), ] - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - Yi.noalias() = Y.row(i); - Yi1.noalias() = Y.row(i + 1); - Y.row(i) = c * Yi + s * Yi1; - Y.row(i + 1) = -s * Yi + c * Yi1; - } - } - - /// - /// Apply the \f$Q\f$ matrix to another matrix \f$Y\f$. - /// - /// \param Y A matrix that will be overwritten by the matrix product \f$Q'Y\f$. - /// - /// Matrix type can be `Eigen::Matrix` (e.g. - /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - // Y -> Q'Y = G_{n-1}' * ... * G2' * G1' * Y - void apply_QtY(GenericMatrix Y) const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergQR: need to call compute() first"); - - RowVector Yi(Y.cols()), Yi1(Y.cols()); - const Index n1 = m_n - 1; - for(Index i = 0; i < n1; i++) - { - const Scalar c = m_rot_cos.coeff(i); - const Scalar s = m_rot_sin.coeff(i); - // Y[i:(i + 1), ] = Gi' * Y[i:(i + 1), ] - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - Yi.noalias() = Y.row(i); - Yi1.noalias() = Y.row(i + 1); - Y.row(i) = c * Yi - s * Yi1; - Y.row(i + 1) = s * Yi + c * Yi1; - } - } - - /// - /// Apply the \f$Q\f$ matrix to another matrix \f$Y\f$. - /// - /// \param Y A matrix that will be overwritten by the matrix product \f$YQ\f$. - /// - /// Matrix type can be `Eigen::Matrix` (e.g. - /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - // Y -> YQ = Y * G1 * G2 * ... - void apply_YQ(GenericMatrix Y) const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergQR: need to call compute() first"); - - /*Vector Yi(Y.rows()); - for(Index i = 0; i < m_n - 1; i++) - { - const Scalar c = m_rot_cos.coeff(i); - const Scalar s = m_rot_sin.coeff(i); - // Y[, i:(i + 1)] = Y[, i:(i + 1)] * Gi - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - Yi.noalias() = Y.col(i); - Y.col(i) = c * Yi - s * Y.col(i + 1); - Y.col(i + 1) = s * Yi + c * Y.col(i + 1); - }*/ - Scalar *Y_col_i, *Y_col_i1; - const Index n1 = m_n - 1; - const Index nrow = Y.rows(); - for(Index i = 0; i < n1; i++) - { - const Scalar c = m_rot_cos.coeff(i); - const Scalar s = m_rot_sin.coeff(i); - - Y_col_i = &Y.coeffRef(0, i); - Y_col_i1 = &Y.coeffRef(0, i + 1); - for(Index j = 0; j < nrow; j++) - { - Scalar tmp = Y_col_i[j]; - Y_col_i[j] = c * tmp - s * Y_col_i1[j]; - Y_col_i1[j] = s * tmp + c * Y_col_i1[j]; - } - } - } - - /// - /// Apply the \f$Q\f$ matrix to another matrix \f$Y\f$. - /// - /// \param Y A matrix that will be overwritten by the matrix product \f$YQ'\f$. - /// - /// Matrix type can be `Eigen::Matrix` (e.g. - /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - // Y -> YQ' = Y * G_{n-1}' * ... * G2' * G1' - void apply_YQt(GenericMatrix Y) const - { - if(!m_computed) - throw std::logic_error("UpperHessenbergQR: need to call compute() first"); - - Vector Yi(Y.rows()); - for(Index i = m_n - 2; i >= 0; i--) - { - const Scalar c = m_rot_cos.coeff(i); - const Scalar s = m_rot_sin.coeff(i); - // Y[, i:(i + 1)] = Y[, i:(i + 1)] * Gi' - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - Yi.noalias() = Y.col(i); - Y.col(i) = c * Yi + s * Y.col(i + 1); - Y.col(i + 1) = -s * Yi + c * Y.col(i + 1); - } - } -}; - - - -/// -/// \ingroup LinearAlgebra -/// -/// Perform the QR decomposition of a tridiagonal matrix, a special -/// case of upper Hessenberg matrices. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// -template -class TridiagQR: public UpperHessenbergQR -{ -private: - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef const Eigen::Ref ConstGenericMatrix; - - typedef typename Matrix::Index Index; - - Vector m_T_diag; // diagonal elements of T - Vector m_T_lsub; // lower subdiagonal of T - Vector m_T_usub; // upper subdiagonal of T - Vector m_T_usub2; // 2nd upper subdiagonal of T - -public: - /// - /// Constructor to preallocate memory. Computation can - /// be performed later by calling the compute() method. - /// - TridiagQR(Index size) : - UpperHessenbergQR(size) - {} - - /// - /// Constructor to create an object that performs and stores the - /// QR decomposition of an upper Hessenberg matrix `mat`, with an - /// optional shift: \f$H-sI=QR\f$. Here \f$H\f$ stands for the matrix - /// `mat`, and \f$s\f$ is the shift. - /// - /// \param mat Matrix type can be `Eigen::Matrix` (e.g. - /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// Only the major- and sub- diagonal parts of - /// the matrix are used. - /// - TridiagQR(ConstGenericMatrix& mat, const Scalar& shift = Scalar(0)) : - UpperHessenbergQR(mat.rows()) - { - this->compute(mat, shift); - } - - /// - /// Conduct the QR factorization of a tridiagonal matrix with an - /// optional shift. - /// - /// \param mat Matrix type can be `Eigen::Matrix` (e.g. - /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// Only the major- and sub- diagonal parts of - /// the matrix are used. - /// - void compute(ConstGenericMatrix& mat, const Scalar& shift = Scalar(0)) - { - this->m_n = mat.rows(); - if(this->m_n != mat.cols()) - throw std::invalid_argument("TridiagQR: matrix must be square"); - - this->m_shift = shift; - m_T_diag.resize(this->m_n); - m_T_lsub.resize(this->m_n - 1); - m_T_usub.resize(this->m_n - 1); - m_T_usub2.resize(this->m_n - 2); - this->m_rot_cos.resize(this->m_n - 1); - this->m_rot_sin.resize(this->m_n - 1); - - m_T_diag.array() = mat.diagonal().array() - this->m_shift; - m_T_lsub.noalias() = mat.diagonal(-1); - m_T_usub.noalias() = m_T_lsub; - - // A number of pointers to avoid repeated address calculation - Scalar *c = this->m_rot_cos.data(), // pointer to the cosine vector - *s = this->m_rot_sin.data(), // pointer to the sine vector - r; - const Index n1 = this->m_n - 1; - for(Index i = 0; i < n1; i++) - { - // diag[i] == T[i, i] - // lsub[i] == T[i + 1, i] - // r = sqrt(T[i, i]^2 + T[i + 1, i]^2) - // c = T[i, i] / r, s = -T[i + 1, i] / r - this->compute_rotation(m_T_diag.coeff(i), m_T_lsub.coeff(i), r, *c, *s); - - // For a complete QR decomposition, - // we first obtain the rotation matrix - // G = [ cos sin] - // [-sin cos] - // and then do T[i:(i + 1), i:(i + 2)] = G' * T[i:(i + 1), i:(i + 2)] - - // Update T[i, i] and T[i + 1, i] - // The updated value of T[i, i] is known to be r - // The updated value of T[i + 1, i] is known to be 0 - m_T_diag.coeffRef(i) = r; - m_T_lsub.coeffRef(i) = Scalar(0); - // Update T[i, i + 1] and T[i + 1, i + 1] - // usub[i] == T[i, i + 1] - // diag[i + 1] == T[i + 1, i + 1] - const Scalar tmp = m_T_usub.coeff(i); - m_T_usub.coeffRef(i) = (*c) * tmp - (*s) * m_T_diag.coeff(i + 1); - m_T_diag.coeffRef(i + 1) = (*s) * tmp + (*c) * m_T_diag.coeff(i + 1); - // Update T[i, i + 2] and T[i + 1, i + 2] - // usub2[i] == T[i, i + 2] - // usub[i + 1] == T[i + 1, i + 2] - if(i < n1 - 1) - { - m_T_usub2.coeffRef(i) = -(*s) * m_T_usub.coeff(i + 1); - m_T_usub.coeffRef(i + 1) *= (*c); - } - - c++; - s++; - - // If we do not need to calculate the R matrix, then - // only the cos and sin sequences are required. - // In such case we only update T[i + 1, (i + 1):(i + 2)] - // T[i + 1, i + 1] = c * T[i + 1, i + 1] + s * T[i, i + 1]; - // T[i + 1, i + 2] *= c; - } - - this->m_computed = true; - } - - /// - /// Return the \f$R\f$ matrix in the QR decomposition, which is an - /// upper triangular matrix. - /// - /// \return Returned matrix type will be `Eigen::Matrix`, depending on - /// the template parameter `Scalar` defined. - /// - Matrix matrix_R() const - { - if(!this->m_computed) - throw std::logic_error("TridiagQR: need to call compute() first"); - - Matrix R = Matrix::Zero(this->m_n, this->m_n); - R.diagonal().noalias() = m_T_diag; - R.diagonal(1).noalias() = m_T_usub; - R.diagonal(2).noalias() = m_T_usub2; - - return R; - } - - /// - /// Overwrite `dest` with \f$Q'HQ = RQ + sI\f$, where \f$H\f$ is the input matrix `mat`, - /// and \f$s\f$ is the shift. The result is a tridiagonal matrix. - /// - /// \param mat The matrix to be overwritten, whose type should be `Eigen::Matrix`, - /// depending on the template parameter `Scalar` defined. - /// - void matrix_QtHQ(Matrix& dest) const - { - if(!this->m_computed) - throw std::logic_error("TridiagQR: need to call compute() first"); - - // Make a copy of the R matrix - dest.resize(this->m_n, this->m_n); - dest.setZero(); - dest.diagonal().noalias() = m_T_diag; - // The upper diagonal refers to m_T_usub - // The 2nd upper subdiagonal will be zero in RQ - - // Compute the RQ matrix - // [m11 m12] points to RQ[i:(i+1), i:(i+1)] - // [0 m22] - // - // Gi = [ cos[i] sin[i]] - // [-sin[i] cos[i]] - const Index n1 = this->m_n - 1; - for(Index i = 0; i < n1; i++) - { - const Scalar c = this->m_rot_cos.coeff(i); - const Scalar s = this->m_rot_sin.coeff(i); - const Scalar m11 = dest.coeff(i, i), - m12 = m_T_usub.coeff(i), - m22 = m_T_diag.coeff(i + 1); - - // Update the diagonal and the lower subdiagonal of dest - dest.coeffRef(i , i ) = c * m11 - s * m12; - dest.coeffRef(i + 1, i ) = - s * m22; - dest.coeffRef(i + 1, i + 1) = c * m22; - } - - // Copy the lower subdiagonal to upper subdiagonal - dest.diagonal(1).noalias() = dest.diagonal(-1); - - // Add the shift to the diagonal - dest.diagonal().array() += this->m_shift; - } -}; - -/// -/// @} -/// - - -} // namespace Spectra - -#endif // UPPER_HESSENBERG_QR_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseCholesky.h b/src/external/Spectra/include/Spectra/MatOp/DenseCholesky.h deleted file mode 100644 index 354c9508..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/DenseCholesky.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef DENSE_CHOLESKY_H -#define DENSE_CHOLESKY_H - -#include -#include -#include -#include "../Util/CompInfo.h" - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the operations related to Cholesky decomposition on a -/// positive definite matrix, \f$B=LL'\f$, where \f$L\f$ is a lower triangular -/// matrix. It is mainly used in the SymGEigsSolver generalized eigen solver -/// in the Cholesky decomposition mode. -/// -template -class DenseCholesky -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstMat; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef const Eigen::Ref ConstGenericMatrix; - - const Index m_n; - Eigen::LLT m_decomp; - int m_info; // status of the decomposition - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** matrix object, whose type can be - /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and - /// `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - DenseCholesky(ConstGenericMatrix& mat) : - m_n(mat.rows()), m_info(NOT_COMPUTED) - { - if(mat.rows() != mat.cols()) - throw std::invalid_argument("DenseCholesky: matrix must be square"); - - m_decomp.compute(mat); - m_info = (m_decomp.info() == Eigen::Success) ? - SUCCESSFUL : - NUMERICAL_ISSUE; - } - - /// - /// Returns the number of rows of the underlying matrix. - /// - Index rows() const { return m_n; } - /// - /// Returns the number of columns of the underlying matrix. - /// - Index cols() const { return m_n; } - - /// - /// Returns the status of the computation. - /// The full list of enumeration values can be found in \ref Enumerations. - /// - int info() const { return m_info; } - - /// - /// Performs the lower triangular solving operation \f$y=L^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(L) * x_in - void lower_triangular_solve(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_decomp.matrixL().solve(x); - } - - /// - /// Performs the upper triangular solving operation \f$y=(L')^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(L') * x_in - void upper_triangular_solve(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_decomp.matrixU().solve(x); - } -}; - - -} // namespace Spectra - -#endif // DENSE_CHOLESKY_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h deleted file mode 100644 index 7f189067..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef DENSE_GEN_COMPLEX_SHIFT_SOLVE_H -#define DENSE_GEN_COMPLEX_SHIFT_SOLVE_H - -#include -#include -#include - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the complex shift-solve operation on a general real matrix \f$A\f$, -/// i.e., calculating \f$y=\mathrm{Re}\{(A-\sigma I)^{-1}x\}\f$ for any complex-valued -/// \f$\sigma\f$ and real-valued vector \f$x\f$. It is mainly used in the -/// GenEigsComplexShiftSolver eigen solver. -/// -template -class DenseGenComplexShiftSolve -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef const Eigen::Ref ConstGenericMatrix; - - typedef std::complex Complex; - typedef Eigen::Matrix ComplexMatrix; - typedef Eigen::Matrix ComplexVector; - - typedef Eigen::PartialPivLU ComplexSolver; - - ConstGenericMatrix m_mat; - const Index m_n; - ComplexSolver m_solver; - ComplexVector m_x_cache; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** matrix object, whose type can be - /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and - /// `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - DenseGenComplexShiftSolve(ConstGenericMatrix& mat) : - m_mat(mat), m_n(mat.rows()) - { - if(mat.rows() != mat.cols()) - throw std::invalid_argument("DenseGenComplexShiftSolve: matrix must be square"); - } - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_n; } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_n; } - - /// - /// Set the complex shift \f$\sigma\f$. - /// - /// \param sigmar Real part of \f$\sigma\f$. - /// \param sigmai Imaginary part of \f$\sigma\f$. - /// - void set_shift(Scalar sigmar, Scalar sigmai) - { - m_solver.compute(m_mat.template cast() - Complex(sigmar, sigmai) * ComplexMatrix::Identity(m_n, m_n)); - m_x_cache.resize(m_n); - m_x_cache.setZero(); - } - - /// - /// Perform the complex shift-solve operation - /// \f$y=\mathrm{Re}\{(A-\sigma I)^{-1}x\}\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = Re( inv(A - sigma * I) * x_in ) - void perform_op(const Scalar* x_in, Scalar* y_out) - { - m_x_cache.real() = MapConstVec(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_solver.solve(m_x_cache).real(); - } -}; - - -} // namespace Spectra - -#endif // DENSE_GEN_COMPLEX_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseGenMatProd.h b/src/external/Spectra/include/Spectra/MatOp/DenseGenMatProd.h deleted file mode 100644 index 6933dac0..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/DenseGenMatProd.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef DENSE_GEN_MAT_PROD_H -#define DENSE_GEN_MAT_PROD_H - -#include - -namespace Spectra { - - -/// -/// \defgroup MatOp Matrix Operations -/// -/// Define matrix operations on existing matrix objects -/// - -/// -/// \ingroup MatOp -/// -/// This class defines the matrix-vector multiplication operation on a -/// general real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector -/// \f$x\f$. It is mainly used in the GenEigsSolver and -/// SymEigsSolver eigen solvers. -/// -template -class DenseGenMatProd -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef const Eigen::Ref ConstGenericMatrix; - - ConstGenericMatrix m_mat; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** matrix object, whose type can be - /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and - /// `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - DenseGenMatProd(ConstGenericMatrix& mat) : - m_mat(mat) - {} - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_mat.rows(); } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_mat.cols(); } - - /// - /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = A * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_mat.cols()); - MapVec y(y_out, m_mat.rows()); - y.noalias() = m_mat * x; - } -}; - - -} // namespace Spectra - -#endif // DENSE_GEN_MAT_PROD_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h deleted file mode 100644 index d7ba8f2a..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef DENSE_GEN_REAL_SHIFT_SOLVE_H -#define DENSE_GEN_REAL_SHIFT_SOLVE_H - -#include -#include -#include - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the shift-solve operation on a general real matrix \f$A\f$, -/// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and -/// vector \f$x\f$. It is mainly used in the GenEigsRealShiftSolver eigen solver. -/// -template -class DenseGenRealShiftSolve -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef const Eigen::Ref ConstGenericMatrix; - - ConstGenericMatrix m_mat; - const Index m_n; - Eigen::PartialPivLU m_solver; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** matrix object, whose type can be - /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and - /// `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - DenseGenRealShiftSolve(ConstGenericMatrix& mat) : - m_mat(mat), m_n(mat.rows()) - { - if(mat.rows() != mat.cols()) - throw std::invalid_argument("DenseGenRealShiftSolve: matrix must be square"); - } - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_n; } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_n; } - - /// - /// Set the real shift \f$\sigma\f$. - /// - void set_shift(Scalar sigma) - { - m_solver.compute(m_mat - sigma * Matrix::Identity(m_n, m_n)); - } - - /// - /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(A - sigma * I) * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_solver.solve(x); - } -}; - - -} // namespace Spectra - -#endif // DENSE_GEN_REAL_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseSymMatProd.h b/src/external/Spectra/include/Spectra/MatOp/DenseSymMatProd.h deleted file mode 100644 index 23ca36e4..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/DenseSymMatProd.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef DENSE_SYM_MAT_PROD_H -#define DENSE_SYM_MAT_PROD_H - -#include - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the matrix-vector multiplication operation on a -/// symmetric real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector -/// \f$x\f$. It is mainly used in the SymEigsSolver eigen solver. -/// -template -class DenseSymMatProd -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef const Eigen::Ref ConstGenericMatrix; - - ConstGenericMatrix m_mat; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** matrix object, whose type can be - /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and - /// `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - DenseSymMatProd(ConstGenericMatrix& mat) : - m_mat(mat) - {} - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_mat.rows(); } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_mat.cols(); } - - /// - /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = A * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_mat.cols()); - MapVec y(y_out, m_mat.rows()); - y.noalias() = m_mat.template selfadjointView() * x; - } -}; - - -} // namespace Spectra - -#endif // DENSE_SYM_MAT_PROD_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseSymShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/DenseSymShiftSolve.h deleted file mode 100644 index 2e2c67f6..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/DenseSymShiftSolve.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef DENSE_SYM_SHIFT_SOLVE_H -#define DENSE_SYM_SHIFT_SOLVE_H - -#include -#include - -#include "../LinAlg/BKLDLT.h" -#include "../Util/CompInfo.h" - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the shift-solve operation on a real symmetric matrix \f$A\f$, -/// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and -/// vector \f$x\f$. It is mainly used in the SymEigsShiftSolver eigen solver. -/// -template -class DenseSymShiftSolve -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef const Eigen::Ref ConstGenericMatrix; - - ConstGenericMatrix m_mat; - const int m_n; - BKLDLT m_solver; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** matrix object, whose type can be - /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and - /// `Eigen::MatrixXf`), or its mapped version - /// (e.g. `Eigen::Map`). - /// - DenseSymShiftSolve(ConstGenericMatrix& mat) : - m_mat(mat), m_n(mat.rows()) - { - if(mat.rows() != mat.cols()) - throw std::invalid_argument("DenseSymShiftSolve: matrix must be square"); - } - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_n; } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_n; } - - /// - /// Set the real shift \f$\sigma\f$. - /// - void set_shift(Scalar sigma) - { - m_solver.compute(m_mat, Uplo, sigma); - if(m_solver.info() != SUCCESSFUL) - throw std::invalid_argument("DenseSymShiftSolve: factorization failed with the given shift"); - } - - /// - /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(A - sigma * I) * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_solver.solve(x); - } -}; - - -} // namespace Spectra - -#endif // DENSE_SYM_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseCholesky.h b/src/external/Spectra/include/Spectra/MatOp/SparseCholesky.h deleted file mode 100644 index 0788596d..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/SparseCholesky.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SPARSE_CHOLESKY_H -#define SPARSE_CHOLESKY_H - -#include -#include -#include -#include -#include "../Util/CompInfo.h" - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the operations related to Cholesky decomposition on a -/// sparse positive definite matrix, \f$B=LL'\f$, where \f$L\f$ is a lower triangular -/// matrix. It is mainly used in the SymGEigsSolver generalized eigen solver -/// in the Cholesky decomposition mode. -/// -template -class SparseCholesky -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef Eigen::SparseMatrix SparseMatrix; - typedef const Eigen::Ref ConstGenericSparseMatrix; - - const Index m_n; - Eigen::SimplicialLLT m_decomp; - int m_info; // status of the decomposition - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** sparse matrix object, whose type can be - /// `Eigen::SparseMatrix` or its mapped version - /// `Eigen::Map >`. - /// - SparseCholesky(ConstGenericSparseMatrix& mat) : - m_n(mat.rows()) - { - if(mat.rows() != mat.cols()) - throw std::invalid_argument("SparseCholesky: matrix must be square"); - - m_decomp.compute(mat); - m_info = (m_decomp.info() == Eigen::Success) ? - SUCCESSFUL : - NUMERICAL_ISSUE; - } - - /// - /// Returns the number of rows of the underlying matrix. - /// - Index rows() const { return m_n; } - /// - /// Returns the number of columns of the underlying matrix. - /// - Index cols() const { return m_n; } - - /// - /// Returns the status of the computation. - /// The full list of enumeration values can be found in \ref Enumerations. - /// - int info() const { return m_info; } - - /// - /// Performs the lower triangular solving operation \f$y=L^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(L) * x_in - void lower_triangular_solve(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_decomp.permutationP() * x; - m_decomp.matrixL().solveInPlace(y); - } - - /// - /// Performs the upper triangular solving operation \f$y=(L')^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(L') * x_in - void upper_triangular_solve(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_decomp.matrixU().solve(x); - y = m_decomp.permutationPinv() * y; - } -}; - - -} // namespace Spectra - -#endif // SPARSE_CHOLESKY_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseGenMatProd.h b/src/external/Spectra/include/Spectra/MatOp/SparseGenMatProd.h deleted file mode 100644 index 90881395..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/SparseGenMatProd.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SPARSE_GEN_MAT_PROD_H -#define SPARSE_GEN_MAT_PROD_H - -#include -#include - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the matrix-vector multiplication operation on a -/// sparse real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector -/// \f$x\f$. It is mainly used in the GenEigsSolver and SymEigsSolver -/// eigen solvers. -/// -template -class SparseGenMatProd -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef Eigen::SparseMatrix SparseMatrix; - typedef const Eigen::Ref ConstGenericSparseMatrix; - - ConstGenericSparseMatrix m_mat; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** sparse matrix object, whose type can be - /// `Eigen::SparseMatrix` or its mapped version - /// `Eigen::Map >`. - /// - SparseGenMatProd(ConstGenericSparseMatrix& mat) : - m_mat(mat) - {} - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_mat.rows(); } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_mat.cols(); } - - /// - /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = A * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_mat.cols()); - MapVec y(y_out, m_mat.rows()); - y.noalias() = m_mat * x; - } -}; - - -} // namespace Spectra - -#endif // SPARSE_GEN_MAT_PROD_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h deleted file mode 100644 index df4ec6cf..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SPARSE_GEN_REAL_SHIFT_SOLVE_H -#define SPARSE_GEN_REAL_SHIFT_SOLVE_H - -#include -#include -#include -#include - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the shift-solve operation on a sparse real matrix \f$A\f$, -/// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and -/// vector \f$x\f$. It is mainly used in the GenEigsRealShiftSolver eigen solver. -/// -template -class SparseGenRealShiftSolve -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef Eigen::SparseMatrix SparseMatrix; - typedef const Eigen::Ref ConstGenericSparseMatrix; - - ConstGenericSparseMatrix m_mat; - const int m_n; - Eigen::SparseLU m_solver; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** sparse matrix object, whose type can be - /// `Eigen::SparseMatrix` or its mapped version - /// `Eigen::Map >`. - /// - SparseGenRealShiftSolve(ConstGenericSparseMatrix& mat) : - m_mat(mat), m_n(mat.rows()) - { - if(mat.rows() != mat.cols()) - throw std::invalid_argument("SparseGenRealShiftSolve: matrix must be square"); - } - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_n; } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_n; } - - /// - /// Set the real shift \f$\sigma\f$. - /// - void set_shift(Scalar sigma) - { - SparseMatrix I(m_n, m_n); - I.setIdentity(); - - m_solver.compute(m_mat - sigma * I); - if(m_solver.info() != Eigen::Success) - throw std::invalid_argument("SparseGenRealShiftSolve: factorization failed with the given shift"); - } - - /// - /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(A - sigma * I) * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_solver.solve(x); - } -}; - - -} // namespace Spectra - -#endif // SPARSE_GEN_REAL_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseRegularInverse.h b/src/external/Spectra/include/Spectra/MatOp/SparseRegularInverse.h deleted file mode 100644 index ec6614a5..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/SparseRegularInverse.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 2017-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SPARSE_REGULAR_INVERSE_H -#define SPARSE_REGULAR_INVERSE_H - -#include -#include -#include -#include - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines matrix operations required by the generalized eigen solver -/// in the regular inverse mode. For a sparse and positive definite matrix \f$B\f$, -/// it implements the matrix-vector product \f$y=Bx\f$ and the linear equation -/// solving operation \f$y=B^{-1}x\f$. -/// -/// This class is intended to be used with the SymGEigsSolver generalized eigen solver -/// in the regular inverse mode. -/// -template -class SparseRegularInverse -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef Eigen::SparseMatrix SparseMatrix; - typedef const Eigen::Ref ConstGenericSparseMatrix; - - ConstGenericSparseMatrix m_mat; - const int m_n; - Eigen::ConjugateGradient m_cg; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** sparse matrix object, whose type can be - /// `Eigen::SparseMatrix` or its mapped version - /// `Eigen::Map >`. - /// - SparseRegularInverse(ConstGenericSparseMatrix& mat) : - m_mat(mat), m_n(mat.rows()) - { - if(mat.rows() != mat.cols()) - throw std::invalid_argument("SparseRegularInverse: matrix must be square"); - - m_cg.compute(mat); - } - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_n; } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_n; } - - /// - /// Perform the solving operation \f$y=B^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(B) * x_in - void solve(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_cg.solve(x); - } - - /// - /// Perform the matrix-vector multiplication operation \f$y=Bx\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = B * x_in - void mat_prod(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_mat.template selfadjointView() * x; - } -}; - - -} // namespace Spectra - -#endif // SPARSE_REGULAR_INVERSE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseSymMatProd.h b/src/external/Spectra/include/Spectra/MatOp/SparseSymMatProd.h deleted file mode 100644 index ef8f96ee..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/SparseSymMatProd.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SPARSE_SYM_MAT_PROD_H -#define SPARSE_SYM_MAT_PROD_H - -#include -#include - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the matrix-vector multiplication operation on a -/// sparse real symmetric matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector -/// \f$x\f$. It is mainly used in the SymEigsSolver eigen solver. -/// -template -class SparseSymMatProd -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef Eigen::SparseMatrix SparseMatrix; - typedef const Eigen::Ref ConstGenericSparseMatrix; - - ConstGenericSparseMatrix m_mat; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** sparse matrix object, whose type can be - /// `Eigen::SparseMatrix` or its mapped version - /// `Eigen::Map >`. - /// - SparseSymMatProd(ConstGenericSparseMatrix& mat) : - m_mat(mat) - {} - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_mat.rows(); } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_mat.cols(); } - - /// - /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = A * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_mat.cols()); - MapVec y(y_out, m_mat.rows()); - y.noalias() = m_mat.template selfadjointView() * x; - } -}; - - -} // namespace Spectra - -#endif // SPARSE_SYM_MAT_PROD_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseSymShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/SparseSymShiftSolve.h deleted file mode 100644 index 1821cc32..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/SparseSymShiftSolve.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SPARSE_SYM_SHIFT_SOLVE_H -#define SPARSE_SYM_SHIFT_SOLVE_H - -#include -#include -#include -#include - -namespace Spectra { - - -/// -/// \ingroup MatOp -/// -/// This class defines the shift-solve operation on a sparse real symmetric matrix \f$A\f$, -/// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and -/// vector \f$x\f$. It is mainly used in the SymEigsShiftSolver eigen solver. -/// -template -class SparseSymShiftSolve -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef Eigen::SparseMatrix SparseMatrix; - typedef const Eigen::Ref ConstGenericSparseMatrix; - - ConstGenericSparseMatrix m_mat; - const int m_n; - Eigen::SparseLU m_solver; - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param mat An **Eigen** sparse matrix object, whose type can be - /// `Eigen::SparseMatrix` or its mapped version - /// `Eigen::Map >`. - /// - SparseSymShiftSolve(ConstGenericSparseMatrix& mat) : - m_mat(mat), m_n(mat.rows()) - { - if(mat.rows() != mat.cols()) - throw std::invalid_argument("SparseSymShiftSolve: matrix must be square"); - } - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_n; } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_n; } - - /// - /// Set the real shift \f$\sigma\f$. - /// - void set_shift(Scalar sigma) - { - SparseMatrix mat = m_mat.template selfadjointView(); - SparseMatrix identity(m_n, m_n); - identity.setIdentity(); - mat = mat - sigma * identity; - m_solver.isSymmetric(true); - m_solver.compute(mat); - if(m_solver.info() != Eigen::Success) - throw std::invalid_argument("SparseSymShiftSolve: factorization failed with the given shift"); - } - - /// - /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(A - sigma * I) * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) const - { - MapConstVec x(x_in, m_n); - MapVec y(y_out, m_n); - y.noalias() = m_solver.solve(x); - } -}; - - -} // namespace Spectra - -#endif // SPARSE_SYM_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/internal/ArnoldiOp.h b/src/external/Spectra/include/Spectra/MatOp/internal/ArnoldiOp.h deleted file mode 100644 index b79704b5..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/internal/ArnoldiOp.h +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (C) 2018-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef ARNOLDI_OP_H -#define ARNOLDI_OP_H - -#include -#include // std::sqrt - -namespace Spectra { - - -/// -/// \ingroup Internals -/// @{ -/// - -/// -/// \defgroup Operators Operators -/// -/// Different types of operators. -/// - -/// -/// \ingroup Operators -/// -/// Operators used in the Arnoldi factorization. -/// -template -class ArnoldiOp -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - - OpType& m_op; - BOpType& m_Bop; - Vector m_cache; - -public: - ArnoldiOp(OpType* op, BOpType* Bop) : - m_op(*op), m_Bop(*Bop), m_cache(op->rows()) - {} - - inline Index rows() const { return m_op.rows(); } - - // In generalized eigenvalue problem Ax=lambda*Bx, define the inner product to be = x'By. - // For regular eigenvalue problems, it is the usual inner product = x'y - - // Compute = x'By - // x and y are two vectors - template - Scalar inner_product(const Arg1& x, const Arg2& y) - { - m_Bop.mat_prod(y.data(), m_cache.data()); - return x.dot(m_cache); - } - - // Compute res = = X'By - // X is a matrix, y is a vector, res is a vector - template - void trans_product(const Arg1& x, const Arg2& y, Eigen::Ref res) - { - m_Bop.mat_prod(y.data(), m_cache.data()); - res.noalias() = x.transpose() * m_cache; - } - - // B-norm of a vector, ||x||_B = sqrt(x'Bx) - template - Scalar norm(const Arg& x) - { - using std::sqrt; - return sqrt(inner_product(x, x)); - } - - // The "A" operator to generate the Krylov subspace - inline void perform_op(const Scalar* x_in, Scalar* y_out) - { - m_op.perform_op(x_in, y_out); - } -}; - - - -/// -/// \ingroup Operators -/// -/// Placeholder for the B-operator when \f$B = I\f$. -/// -class IdentityBOp {}; - - - -/// -/// \ingroup Operators -/// -/// Partial specialization for the case \f$B = I\f$. -/// -template -class ArnoldiOp -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - - OpType& m_op; - -public: - ArnoldiOp(OpType* op, IdentityBOp* /*Bop*/) : - m_op(*op) - {} - - inline Index rows() const { return m_op.rows(); } - - // Compute = x'y - // x and y are two vectors - template - Scalar inner_product(const Arg1& x, const Arg2& y) const - { - return x.dot(y); - } - - // Compute res = = X'y - // X is a matrix, y is a vector, res is a vector - template - void trans_product(const Arg1& x, const Arg2& y, Eigen::Ref res) const - { - res.noalias() = x.transpose() * y; - } - - // B-norm of a vector. For regular eigenvalue problems it is simply the L2 norm - template - Scalar norm(const Arg& x) - { - return x.norm(); - } - - // The "A" operator to generate the Krylov subspace - inline void perform_op(Scalar* x_in, Scalar* y_out) - { - m_op.perform_op(x_in, y_out); - } -}; - -/// -/// @} -/// - - -} // namespace Spectra - -#endif // ARNOLDI_OP_H diff --git a/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h b/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h deleted file mode 100644 index d7acdb2a..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SYM_GEIGS_CHOLESKY_OP_H -#define SYM_GEIGS_CHOLESKY_OP_H - -#include -#include "../DenseSymMatProd.h" -#include "../DenseCholesky.h" - -namespace Spectra { - - -/// -/// \ingroup Operators -/// -/// This class defines the matrix operation for generalized eigen solver in the -/// Cholesky decomposition mode. It calculates \f$y=L^{-1}A(L')^{-1}x\f$ for any -/// vector \f$x\f$, where \f$L\f$ is the Cholesky decomposition of \f$B\f$. -/// This class is intended for internal use. -/// -template < typename Scalar = double, - typename OpType = DenseSymMatProd, - typename BOpType = DenseCholesky > -class SymGEigsCholeskyOp -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - - OpType& m_op; - BOpType& m_Bop; - Vector m_cache; // temporary working space - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param op Pointer to the \f$A\f$ matrix operation object. - /// \param Bop Pointer to the \f$B\f$ matrix operation object. - /// - SymGEigsCholeskyOp(OpType& op, BOpType& Bop) : - m_op(op), m_Bop(Bop), m_cache(op.rows()) - {} - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_Bop.rows(); } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_Bop.rows(); } - - /// - /// Perform the matrix operation \f$y=L^{-1}A(L')^{-1}x\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(L) * A * inv(L') * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) - { - m_Bop.upper_triangular_solve(x_in, y_out); - m_op.perform_op(y_out, m_cache.data()); - m_Bop.lower_triangular_solve(m_cache.data(), y_out); - } -}; - - -} // namespace Spectra - -#endif // SYM_GEIGS_CHOLESKY_OP_H diff --git a/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h b/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h deleted file mode 100644 index ac00dcb2..00000000 --- a/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2017-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SYM_GEIGS_REG_INV_OP_H -#define SYM_GEIGS_REG_INV_OP_H - -#include -#include "../SparseSymMatProd.h" -#include "../SparseRegularInverse.h" - -namespace Spectra { - - -/// -/// \ingroup Operators -/// -/// This class defines the matrix operation for generalized eigen solver in the -/// regular inverse mode. This class is intended for internal use. -/// -template < typename Scalar = double, - typename OpType = SparseSymMatProd, - typename BOpType = SparseRegularInverse > -class SymGEigsRegInvOp -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - - OpType& m_op; - BOpType& m_Bop; - Vector m_cache; // temporary working space - -public: - /// - /// Constructor to create the matrix operation object. - /// - /// \param op Pointer to the \f$A\f$ matrix operation object. - /// \param Bop Pointer to the \f$B\f$ matrix operation object. - /// - SymGEigsRegInvOp(OpType& op, BOpType& Bop) : - m_op(op), m_Bop(Bop), m_cache(op.rows()) - {} - - /// - /// Return the number of rows of the underlying matrix. - /// - Index rows() const { return m_Bop.rows(); } - /// - /// Return the number of columns of the underlying matrix. - /// - Index cols() const { return m_Bop.rows(); } - - /// - /// Perform the matrix operation \f$y=B^{-1}Ax\f$. - /// - /// \param x_in Pointer to the \f$x\f$ vector. - /// \param y_out Pointer to the \f$y\f$ vector. - /// - // y_out = inv(B) * A * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) - { - m_op.perform_op(x_in, m_cache.data()); - m_Bop.solve(m_cache.data(), y_out); - } -}; - - -} // namespace Spectra - -#endif // SYM_GEIGS_REG_INV_OP_H diff --git a/src/external/Spectra/include/Spectra/SymEigsBase.h b/src/external/Spectra/include/Spectra/SymEigsBase.h deleted file mode 100644 index 24d5b505..00000000 --- a/src/external/Spectra/include/Spectra/SymEigsBase.h +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright (C) 2018-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SYM_EIGS_BASE_H -#define SYM_EIGS_BASE_H - -#include -#include // std::vector -#include // std::abs, std::pow, std::sqrt -#include // std::min, std::copy -#include // std::invalid_argument - -#include "Util/TypeTraits.h" -#include "Util/SelectionRule.h" -#include "Util/CompInfo.h" -#include "Util/SimpleRandom.h" -#include "MatOp/internal/ArnoldiOp.h" -#include "LinAlg/UpperHessenbergQR.h" -#include "LinAlg/TridiagEigen.h" -#include "LinAlg/Lanczos.h" - -namespace Spectra { - - -/// -/// \defgroup EigenSolver Eigen Solvers -/// -/// Eigen solvers for different types of problems. -/// - -/// -/// \ingroup EigenSolver -/// -/// This is the base class for symmetric eigen solvers, mainly for internal use. -/// It is kept here to provide the documentation for member functions of concrete eigen solvers -/// such as SymEigsSolver and SymEigsShiftSolver. -/// -template < typename Scalar, - int SelectionRule, - typename OpType, - typename BOpType > -class SymEigsBase -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef Eigen::Array Array; - typedef Eigen::Array BoolArray; - typedef Eigen::Map MapMat; - typedef Eigen::Map MapVec; - typedef Eigen::Map MapConstVec; - - typedef ArnoldiOp ArnoldiOpType; - typedef Lanczos LanczosFac; - -protected: - OpType* m_op; // object to conduct matrix operation, - // e.g. matrix-vector product - const Index m_n; // dimension of matrix A - const Index m_nev; // number of eigenvalues requested - const Index m_ncv; // dimension of Krylov subspace in the Lanczos method - Index m_nmatop; // number of matrix operations called - Index m_niter; // number of restarting iterations - - LanczosFac m_fac; // Lanczos factorization - Vector m_ritz_val; // Ritz values - -private: - Matrix m_ritz_vec; // Ritz vectors - Vector m_ritz_est; // last row of m_ritz_vec, also called the Ritz estimates - BoolArray m_ritz_conv; // indicator of the convergence of Ritz values - int m_info; // status of the computation - - const Scalar m_near_0; // a very small value, but 1.0 / m_near_0 does not overflow - // ~= 1e-307 for the "double" type - const Scalar m_eps; // the machine precision, ~= 1e-16 for the "double" type - const Scalar m_eps23; // m_eps^(2/3), used to test the convergence - - // Implicitly restarted Lanczos factorization - void restart(Index k) - { - if(k >= m_ncv) - return; - - TridiagQR decomp(m_ncv); - Matrix Q = Matrix::Identity(m_ncv, m_ncv); - - for(Index i = k; i < m_ncv; i++) - { - // QR decomposition of H-mu*I, mu is the shift - decomp.compute(m_fac.matrix_H(), m_ritz_val[i]); - - // Q -> Q * Qi - decomp.apply_YQ(Q); - // H -> Q'HQ - // Since QR = H - mu * I, we have H = QR + mu * I - // and therefore Q'HQ = RQ + mu * I - m_fac.compress_H(decomp); - } - - m_fac.compress_V(Q); - m_fac.factorize_from(k, m_ncv, m_nmatop); - - retrieve_ritzpair(); - } - - // Calculates the number of converged Ritz values - Index num_converged(Scalar tol) - { - // thresh = tol * max(m_eps23, abs(theta)), theta for Ritz value - Array thresh = tol * m_ritz_val.head(m_nev).array().abs().max(m_eps23); - Array resid = m_ritz_est.head(m_nev).array().abs() * m_fac.f_norm(); - // Converged "wanted" Ritz values - m_ritz_conv = (resid < thresh); - - return m_ritz_conv.cast().sum(); - } - - // Returns the adjusted nev for restarting - Index nev_adjusted(Index nconv) - { - using std::abs; - - Index nev_new = m_nev; - for(Index i = m_nev; i < m_ncv; i++) - if(abs(m_ritz_est[i]) < m_near_0) nev_new++; - - // Adjust nev_new, according to dsaup2.f line 677~684 in ARPACK - nev_new += std::min(nconv, (m_ncv - nev_new) / 2); - if(nev_new == 1 && m_ncv >= 6) - nev_new = m_ncv / 2; - else if(nev_new == 1 && m_ncv > 2) - nev_new = 2; - - if(nev_new > m_ncv - 1) - nev_new = m_ncv - 1; - - return nev_new; - } - - // Retrieves and sorts Ritz values and Ritz vectors - void retrieve_ritzpair() - { - TridiagEigen decomp(m_fac.matrix_H()); - const Vector& evals = decomp.eigenvalues(); - const Matrix& evecs = decomp.eigenvectors(); - - SortEigenvalue sorting(evals.data(), evals.size()); - std::vector ind = sorting.index(); - - // For BOTH_ENDS, the eigenvalues are sorted according - // to the LARGEST_ALGE rule, so we need to move those smallest - // values to the left - // The order would be - // Largest => Smallest => 2nd largest => 2nd smallest => ... - // We keep this order since the first k values will always be - // the wanted collection, no matter k is nev_updated (used in restart()) - // or is nev (used in sort_ritzpair()) - if(SelectionRule == BOTH_ENDS) - { - std::vector ind_copy(ind); - for(Index i = 0; i < m_ncv; i++) - { - // If i is even, pick values from the left (large values) - // If i is odd, pick values from the right (small values) - if(i % 2 == 0) - ind[i] = ind_copy[i / 2]; - else - ind[i] = ind_copy[m_ncv - 1 - i / 2]; - } - } - - // Copy the Ritz values and vectors to m_ritz_val and m_ritz_vec, respectively - for(Index i = 0; i < m_ncv; i++) - { - m_ritz_val[i] = evals[ind[i]]; - m_ritz_est[i] = evecs(m_ncv - 1, ind[i]); - } - for(Index i = 0; i < m_nev; i++) - { - m_ritz_vec.col(i).noalias() = evecs.col(ind[i]); - } - } - -protected: - // Sorts the first nev Ritz pairs in the specified order - // This is used to return the final results - virtual void sort_ritzpair(int sort_rule) - { - // First make sure that we have a valid index vector - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - std::vector ind = sorting.index(); - - switch(sort_rule) - { - case LARGEST_ALGE: - break; - case LARGEST_MAGN: - { - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - ind = sorting.index(); - } - break; - case SMALLEST_ALGE: - { - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - ind = sorting.index(); - } - break; - case SMALLEST_MAGN: - { - SortEigenvalue sorting(m_ritz_val.data(), m_nev); - ind = sorting.index(); - } - break; - default: - throw std::invalid_argument("unsupported sorting rule"); - } - - Vector new_ritz_val(m_ncv); - Matrix new_ritz_vec(m_ncv, m_nev); - BoolArray new_ritz_conv(m_nev); - - for(Index i = 0; i < m_nev; i++) - { - new_ritz_val[i] = m_ritz_val[ind[i]]; - new_ritz_vec.col(i).noalias() = m_ritz_vec.col(ind[i]); - new_ritz_conv[i] = m_ritz_conv[ind[i]]; - } - - m_ritz_val.swap(new_ritz_val); - m_ritz_vec.swap(new_ritz_vec); - m_ritz_conv.swap(new_ritz_conv); - } - -public: - /// \cond - - SymEigsBase(OpType* op, BOpType* Bop, Index nev, Index ncv) : - m_op(op), - m_n(m_op->rows()), - m_nev(nev), - m_ncv(ncv > m_n ? m_n : ncv), - m_nmatop(0), - m_niter(0), - m_fac(ArnoldiOpType(op, Bop), m_ncv), - m_info(NOT_COMPUTED), - m_near_0(TypeTraits::min() * Scalar(10)), - m_eps(Eigen::NumTraits::epsilon()), - m_eps23(Eigen::numext::pow(m_eps, Scalar(2.0) / 3)) - { - if(nev < 1 || nev > m_n - 1) - throw std::invalid_argument("nev must satisfy 1 <= nev <= n - 1, n is the size of matrix"); - - if(ncv <= nev || ncv > m_n) - throw std::invalid_argument("ncv must satisfy nev < ncv <= n, n is the size of matrix"); - } - - /// - /// Virtual destructor - /// - virtual ~SymEigsBase() {} - - /// \endcond - - /// - /// Initializes the solver by providing an initial residual vector. - /// - /// \param init_resid Pointer to the initial residual vector. - /// - /// **Spectra** (and also **ARPACK**) uses an iterative algorithm - /// to find eigenvalues. This function allows the user to provide the initial - /// residual vector. - /// - void init(const Scalar* init_resid) - { - // Reset all matrices/vectors to zero - m_ritz_val.resize(m_ncv); - m_ritz_vec.resize(m_ncv, m_nev); - m_ritz_est.resize(m_ncv); - m_ritz_conv.resize(m_nev); - - m_ritz_val.setZero(); - m_ritz_vec.setZero(); - m_ritz_est.setZero(); - m_ritz_conv.setZero(); - - m_nmatop = 0; - m_niter = 0; - - // Initialize the Lanczos factorization - MapConstVec v0(init_resid, m_n); - m_fac.init(v0, m_nmatop); - } - - /// - /// Initializes the solver by providing a random initial residual vector. - /// - /// This overloaded function generates a random initial residual vector - /// (with a fixed random seed) for the algorithm. Elements in the vector - /// follow independent Uniform(-0.5, 0.5) distribution. - /// - void init() - { - SimpleRandom rng(0); - Vector init_resid = rng.random_vec(m_n); - init(init_resid.data()); - } - - /// - /// Conducts the major computation procedure. - /// - /// \param maxit Maximum number of iterations allowed in the algorithm. - /// \param tol Precision parameter for the calculated eigenvalues. - /// \param sort_rule Rule to sort the eigenvalues and eigenvectors. - /// Supported values are - /// `Spectra::LARGEST_ALGE`, `Spectra::LARGEST_MAGN`, - /// `Spectra::SMALLEST_ALGE` and `Spectra::SMALLEST_MAGN`, - /// for example `LARGEST_ALGE` indicates that largest eigenvalues - /// come first. Note that this argument is only used to - /// **sort** the final result, and the **selection** rule - /// (e.g. selecting the largest or smallest eigenvalues in the - /// full spectrum) is specified by the template parameter - /// `SelectionRule` of SymEigsSolver. - /// - /// \return Number of converged eigenvalues. - /// - Index compute(Index maxit = 1000, Scalar tol = 1e-10, int sort_rule = LARGEST_ALGE) - { - // The m-step Lanczos factorization - m_fac.factorize_from(1, m_ncv, m_nmatop); - retrieve_ritzpair(); - // Restarting - Index i, nconv = 0, nev_adj; - for(i = 0; i < maxit; i++) - { - nconv = num_converged(tol); - if(nconv >= m_nev) - break; - - nev_adj = nev_adjusted(nconv); - restart(nev_adj); - } - // Sorting results - sort_ritzpair(sort_rule); - - m_niter += i + 1; - m_info = (nconv >= m_nev) ? SUCCESSFUL : NOT_CONVERGING; - - return std::min(m_nev, nconv); - } - - /// - /// Returns the status of the computation. - /// The full list of enumeration values can be found in \ref Enumerations. - /// - int info() const { return m_info; } - - /// - /// Returns the number of iterations used in the computation. - /// - Index num_iterations() const { return m_niter; } - - /// - /// Returns the number of matrix operations used in the computation. - /// - Index num_operations() const { return m_nmatop; } - - /// - /// Returns the converged eigenvalues. - /// - /// \return A vector containing the eigenvalues. - /// Returned vector type will be `Eigen::Vector`, depending on - /// the template parameter `Scalar` defined. - /// - Vector eigenvalues() const - { - const Index nconv = m_ritz_conv.cast().sum(); - Vector res(nconv); - - if(!nconv) - return res; - - Index j = 0; - for(Index i = 0; i < m_nev; i++) - { - if(m_ritz_conv[i]) - { - res[j] = m_ritz_val[i]; - j++; - } - } - - return res; - } - - /// - /// Returns the eigenvectors associated with the converged eigenvalues. - /// - /// \param nvec The number of eigenvectors to return. - /// - /// \return A matrix containing the eigenvectors. - /// Returned matrix type will be `Eigen::Matrix`, - /// depending on the template parameter `Scalar` defined. - /// - virtual Matrix eigenvectors(Index nvec) const - { - const Index nconv = m_ritz_conv.cast().sum(); - nvec = std::min(nvec, nconv); - Matrix res(m_n, nvec); - - if(!nvec) - return res; - - Matrix ritz_vec_conv(m_ncv, nvec); - Index j = 0; - for(Index i = 0; i < m_nev && j < nvec; i++) - { - if(m_ritz_conv[i]) - { - ritz_vec_conv.col(j).noalias() = m_ritz_vec.col(i); - j++; - } - } - - res.noalias() = m_fac.matrix_V() * ritz_vec_conv; - - return res; - } - - /// - /// Returns all converged eigenvectors. - /// - virtual Matrix eigenvectors() const - { - return eigenvectors(m_nev); - } -}; - - -} // namespace Spectra - -#endif // SYM_EIGS_BASE_H diff --git a/src/external/Spectra/include/Spectra/SymEigsShiftSolver.h b/src/external/Spectra/include/Spectra/SymEigsShiftSolver.h deleted file mode 100644 index 56bd44ec..00000000 --- a/src/external/Spectra/include/Spectra/SymEigsShiftSolver.h +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SYM_EIGS_SHIFT_SOLVER_H -#define SYM_EIGS_SHIFT_SOLVER_H - -#include - -#include "SymEigsBase.h" -#include "Util/SelectionRule.h" -#include "MatOp/DenseSymShiftSolve.h" - -namespace Spectra { - - -/// -/// \ingroup EigenSolver -/// -/// This class implements the eigen solver for real symmetric matrices using -/// the **shift-and-invert mode**. The background information of the symmetric -/// eigen solver is documented in the SymEigsSolver class. Here we focus on -/// explaining the shift-and-invert mode. -/// -/// The shift-and-invert mode is based on the following fact: -/// If \f$\lambda\f$ and \f$x\f$ are a pair of eigenvalue and eigenvector of -/// matrix \f$A\f$, such that \f$Ax=\lambda x\f$, then for any \f$\sigma\f$, -/// we have -/// \f[(A-\sigma I)^{-1}x=\nu x\f] -/// where -/// \f[\nu=\frac{1}{\lambda-\sigma}\f] -/// which indicates that \f$(\nu, x)\f$ is an eigenpair of the matrix -/// \f$(A-\sigma I)^{-1}\f$. -/// -/// Therefore, if we pass the matrix operation \f$(A-\sigma I)^{-1}y\f$ -/// (rather than \f$Ay\f$) to the eigen solver, then we would get the desired -/// values of \f$\nu\f$, and \f$\lambda\f$ can also be easily obtained by noting -/// that \f$\lambda=\sigma+\nu^{-1}\f$. -/// -/// The reason why we need this type of manipulation is that -/// the algorithm of **Spectra** (and also **ARPACK**) -/// is good at finding eigenvalues with large magnitude, but may fail in looking -/// for eigenvalues that are close to zero. However, if we really need them, we -/// can set \f$\sigma=0\f$, find the largest eigenvalues of \f$A^{-1}\f$, and then -/// transform back to \f$\lambda\f$, since in this case largest values of \f$\nu\f$ -/// implies smallest values of \f$\lambda\f$. -/// -/// To summarize, in the shift-and-invert mode, the selection rule will apply to -/// \f$\nu=1/(\lambda-\sigma)\f$ rather than \f$\lambda\f$. So a selection rule -/// of `LARGEST_MAGN` combined with shift \f$\sigma\f$ will find eigenvalues of -/// \f$A\f$ that are closest to \f$\sigma\f$. But note that the eigenvalues() -/// method will always return the eigenvalues in the original problem (i.e., -/// returning \f$\lambda\f$ rather than \f$\nu\f$), and eigenvectors are the -/// same for both the original problem and the shifted-and-inverted problem. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// \tparam SelectionRule An enumeration value indicating the selection rule of -/// the shifted-and-inverted eigenvalues. -/// The full list of enumeration values can be found in -/// \ref Enumerations. -/// \tparam OpType The name of the matrix operation class. Users could either -/// use the wrapper classes such as DenseSymShiftSolve and -/// SparseSymShiftSolve, or define their -/// own that implements all the public member functions as in -/// DenseSymShiftSolve. -/// -/// Below is an example that illustrates the use of the shift-and-invert mode: -/// -/// \code{.cpp} -/// #include -/// #include -/// // is implicitly included -/// #include -/// -/// using namespace Spectra; -/// -/// int main() -/// { -/// // A size-10 diagonal matrix with elements 1, 2, ..., 10 -/// Eigen::MatrixXd M = Eigen::MatrixXd::Zero(10, 10); -/// for(int i = 0; i < M.rows(); i++) -/// M(i, i) = i + 1; -/// -/// // Construct matrix operation object using the wrapper class -/// DenseSymShiftSolve op(M); -/// -/// // Construct eigen solver object with shift 0 -/// // This will find eigenvalues that are closest to 0 -/// SymEigsShiftSolver< double, LARGEST_MAGN, -/// DenseSymShiftSolve > eigs(&op, 3, 6, 0.0); -/// -/// eigs.init(); -/// eigs.compute(); -/// if(eigs.info() == SUCCESSFUL) -/// { -/// Eigen::VectorXd evalues = eigs.eigenvalues(); -/// // Will get (3.0, 2.0, 1.0) -/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; -/// } -/// -/// return 0; -/// } -/// \endcode -/// -/// Also an example for user-supplied matrix shift-solve operation class: -/// -/// \code{.cpp} -/// #include -/// #include -/// #include -/// -/// using namespace Spectra; -/// -/// // M = diag(1, 2, ..., 10) -/// class MyDiagonalTenShiftSolve -/// { -/// private: -/// double sigma_; -/// public: -/// int rows() { return 10; } -/// int cols() { return 10; } -/// void set_shift(double sigma) { sigma_ = sigma; } -/// // y_out = inv(A - sigma * I) * x_in -/// // inv(A - sigma * I) = diag(1/(1-sigma), 1/(2-sigma), ...) -/// void perform_op(double *x_in, double *y_out) -/// { -/// for(int i = 0; i < rows(); i++) -/// { -/// y_out[i] = x_in[i] / (i + 1 - sigma_); -/// } -/// } -/// }; -/// -/// int main() -/// { -/// MyDiagonalTenShiftSolve op; -/// // Find three eigenvalues that are closest to 3.14 -/// SymEigsShiftSolver eigs(&op, 3, 6, 3.14); -/// eigs.init(); -/// eigs.compute(); -/// if(eigs.info() == SUCCESSFUL) -/// { -/// Eigen::VectorXd evalues = eigs.eigenvalues(); -/// // Will get (4.0, 3.0, 2.0) -/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; -/// } -/// -/// return 0; -/// } -/// \endcode -/// -template > -class SymEigsShiftSolver: public SymEigsBase -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Array Array; - - const Scalar m_sigma; - - // First transform back the Ritz values, and then sort - void sort_ritzpair(int sort_rule) - { - Array m_ritz_val_org = Scalar(1.0) / this->m_ritz_val.head(this->m_nev).array() + m_sigma; - this->m_ritz_val.head(this->m_nev) = m_ritz_val_org; - SymEigsBase::sort_ritzpair(sort_rule); - } - -public: - /// - /// Constructor to create a eigen solver object using the shift-and-invert mode. - /// - /// \param op Pointer to the matrix operation object, which should implement - /// the shift-solve operation of \f$A\f$: calculating - /// \f$(A-\sigma I)^{-1}v\f$ for any vector \f$v\f$. Users could either - /// create the object from the wrapper class such as DenseSymShiftSolve, or - /// define their own that implements all the public member functions - /// as in DenseSymShiftSolve. - /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, - /// where \f$n\f$ is the size of matrix. - /// \param ncv Parameter that controls the convergence speed of the algorithm. - /// Typically a larger `ncv_` means faster convergence, but it may - /// also result in greater memory use and more matrix operations - /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, - /// and is advised to take \f$ncv \ge 2\cdot nev\f$. - /// \param sigma The value of the shift. - /// - SymEigsShiftSolver(OpType* op, Index nev, Index ncv, Scalar sigma) : - SymEigsBase(op, NULL, nev, ncv), - m_sigma(sigma) - { - this->m_op->set_shift(m_sigma); - } -}; - - -} // namespace Spectra - -#endif // SYM_EIGS_SHIFT_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/SymEigsSolver.h b/src/external/Spectra/include/Spectra/SymEigsSolver.h deleted file mode 100644 index 8ba3e509..00000000 --- a/src/external/Spectra/include/Spectra/SymEigsSolver.h +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SYM_EIGS_SOLVER_H -#define SYM_EIGS_SOLVER_H - -#include - -#include "SymEigsBase.h" -#include "Util/SelectionRule.h" -#include "MatOp/DenseSymMatProd.h" - -namespace Spectra { - - -/// -/// \ingroup EigenSolver -/// -/// This class implements the eigen solver for real symmetric matrices, i.e., -/// to solve \f$Ax=\lambda x\f$ where \f$A\f$ is symmetric. -/// -/// **Spectra** is designed to calculate a specified number (\f$k\f$) -/// of eigenvalues of a large square matrix (\f$A\f$). Usually \f$k\f$ is much -/// less than the size of the matrix (\f$n\f$), so that only a few eigenvalues -/// and eigenvectors are computed. -/// -/// Rather than providing the whole \f$A\f$ matrix, the algorithm only requires -/// the matrix-vector multiplication operation of \f$A\f$. Therefore, users of -/// this solver need to supply a class that computes the result of \f$Av\f$ -/// for any given vector \f$v\f$. The name of this class should be given to -/// the template parameter `OpType`, and instance of this class passed to -/// the constructor of SymEigsSolver. -/// -/// If the matrix \f$A\f$ is already stored as a matrix object in **Eigen**, -/// for example `Eigen::MatrixXd`, then there is an easy way to construct such -/// matrix operation class, by using the built-in wrapper class DenseSymMatProd -/// which wraps an existing matrix object in **Eigen**. This is also the -/// default template parameter for SymEigsSolver. For sparse matrices, the -/// wrapper class SparseSymMatProd can be used similarly. -/// -/// If the users need to define their own matrix-vector multiplication operation -/// class, it should implement all the public member functions as in DenseSymMatProd. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// \tparam SelectionRule An enumeration value indicating the selection rule of -/// the requested eigenvalues, for example `LARGEST_MAGN` -/// to retrieve eigenvalues with the largest magnitude. -/// The full list of enumeration values can be found in -/// \ref Enumerations. -/// \tparam OpType The name of the matrix operation class. Users could either -/// use the wrapper classes such as DenseSymMatProd and -/// SparseSymMatProd, or define their -/// own that implements all the public member functions as in -/// DenseSymMatProd. -/// -/// Below is an example that demonstrates the usage of this class. -/// -/// \code{.cpp} -/// #include -/// #include -/// // is implicitly included -/// #include -/// -/// using namespace Spectra; -/// -/// int main() -/// { -/// // We are going to calculate the eigenvalues of M -/// Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10); -/// Eigen::MatrixXd M = A + A.transpose(); -/// -/// // Construct matrix operation object using the wrapper class DenseSymMatProd -/// DenseSymMatProd op(M); -/// -/// // Construct eigen solver object, requesting the largest three eigenvalues -/// SymEigsSolver< double, LARGEST_ALGE, DenseSymMatProd > eigs(&op, 3, 6); -/// -/// // Initialize and compute -/// eigs.init(); -/// int nconv = eigs.compute(); -/// -/// // Retrieve results -/// Eigen::VectorXd evalues; -/// if(eigs.info() == SUCCESSFUL) -/// evalues = eigs.eigenvalues(); -/// -/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; -/// -/// return 0; -/// } -/// \endcode -/// -/// And here is an example for user-supplied matrix operation class. -/// -/// \code{.cpp} -/// #include -/// #include -/// #include -/// -/// using namespace Spectra; -/// -/// // M = diag(1, 2, ..., 10) -/// class MyDiagonalTen -/// { -/// public: -/// int rows() { return 10; } -/// int cols() { return 10; } -/// // y_out = M * x_in -/// void perform_op(double *x_in, double *y_out) -/// { -/// for(int i = 0; i < rows(); i++) -/// { -/// y_out[i] = x_in[i] * (i + 1); -/// } -/// } -/// }; -/// -/// int main() -/// { -/// MyDiagonalTen op; -/// SymEigsSolver eigs(&op, 3, 6); -/// eigs.init(); -/// eigs.compute(); -/// if(eigs.info() == SUCCESSFUL) -/// { -/// Eigen::VectorXd evalues = eigs.eigenvalues(); -/// // Will get (10, 9, 8) -/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; -/// } -/// -/// return 0; -/// } -/// \endcode -/// -template < typename Scalar = double, - int SelectionRule = LARGEST_MAGN, - typename OpType = DenseSymMatProd > -class SymEigsSolver: public SymEigsBase -{ -private: - typedef Eigen::Index Index; - -public: - /// - /// Constructor to create a solver object. - /// - /// \param op Pointer to the matrix operation object, which should implement - /// the matrix-vector multiplication operation of \f$A\f$: - /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either - /// create the object from the wrapper class such as DenseSymMatProd, or - /// define their own that implements all the public member functions - /// as in DenseSymMatProd. - /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, - /// where \f$n\f$ is the size of matrix. - /// \param ncv Parameter that controls the convergence speed of the algorithm. - /// Typically a larger `ncv` means faster convergence, but it may - /// also result in greater memory use and more matrix operations - /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, - /// and is advised to take \f$ncv \ge 2\cdot nev\f$. - /// - SymEigsSolver(OpType* op, Index nev, Index ncv) : - SymEigsBase(op, NULL, nev, ncv) - {} - -}; - - -} // namespace Spectra - -#endif // SYM_EIGS_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/SymGEigsSolver.h b/src/external/Spectra/include/Spectra/SymGEigsSolver.h deleted file mode 100644 index 8e774284..00000000 --- a/src/external/Spectra/include/Spectra/SymGEigsSolver.h +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SYM_GEIGS_SOLVER_H -#define SYM_GEIGS_SOLVER_H - -#include "SymEigsBase.h" -#include "Util/GEigsMode.h" -#include "MatOp/internal/SymGEigsCholeskyOp.h" -#include "MatOp/internal/SymGEigsRegInvOp.h" - -namespace Spectra { - - -/// -/// \defgroup GEigenSolver Generalized Eigen Solvers -/// -/// Generalized eigen solvers for different types of problems. -/// - -/// -/// \ingroup GEigenSolver -/// -/// This class implements the generalized eigen solver for real symmetric -/// matrices, i.e., to solve \f$Ax=\lambda Bx\f$ where \f$A\f$ is symmetric and -/// \f$B\f$ is positive definite. -/// -/// There are two modes of this solver, specified by the template parameter -/// GEigsMode. See the pages for the specialized classes for details. -/// - The Cholesky mode assumes that \f$B\f$ can be factorized using Cholesky -/// decomposition, which is the preferred mode when the decomposition is -/// available. (This can be easily done in Eigen using the dense or sparse -/// Cholesky solver.) -/// See \ref SymGEigsSolver "SymGEigsSolver (Cholesky mode)" for more details. -/// - The regular inverse mode requires the matrix-vector product \f$Bv\f$ and the -/// linear equation solving operation \f$B^{-1}v\f$. This mode should only be -/// used when the Cholesky decomposition of \f$B\f$ is hard to implement, or -/// when computing \f$B^{-1}v\f$ is much faster than the Cholesky decomposition. -/// See \ref SymGEigsSolver "SymGEigsSolver (Regular inverse mode)" for more details. - -// Empty class template -template < typename Scalar, - int SelectionRule, - typename OpType, - typename BOpType, - int GEigsMode > -class SymGEigsSolver -{}; - - - -/// -/// \ingroup GEigenSolver -/// -/// This class implements the generalized eigen solver for real symmetric -/// matrices using Cholesky decomposition, i.e., to solve \f$Ax=\lambda Bx\f$ -/// where \f$A\f$ is symmetric and \f$B\f$ is positive definite with the Cholesky -/// decomposition \f$B=LL'\f$. -/// -/// This solver requires two matrix operation objects: one for \f$A\f$ that implements -/// the matrix multiplication \f$Av\f$, and one for \f$B\f$ that implements the lower -/// and upper triangular solving \f$L^{-1}v\f$ and \f$(L')^{-1}v\f$. -/// -/// If \f$A\f$ and \f$B\f$ are stored as Eigen matrices, then the first operation -/// can be created using the DenseSymMatProd or SparseSymMatProd classes, and -/// the second operation can be created using the DenseCholesky or SparseCholesky -/// classes. If the users need to define their own operation classes, then they -/// should implement all the public member functions as in those built-in classes. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// \tparam SelectionRule An enumeration value indicating the selection rule of -/// the requested eigenvalues, for example `LARGEST_MAGN` -/// to retrieve eigenvalues with the largest magnitude. -/// The full list of enumeration values can be found in -/// \ref Enumerations. -/// \tparam OpType The name of the matrix operation class for \f$A\f$. Users could either -/// use the wrapper classes such as DenseSymMatProd and -/// SparseSymMatProd, or define their -/// own that implements all the public member functions as in -/// DenseSymMatProd. -/// \tparam BOpType The name of the matrix operation class for \f$B\f$. Users could either -/// use the wrapper classes such as DenseCholesky and -/// SparseCholesky, or define their -/// own that implements all the public member functions as in -/// DenseCholesky. -/// \tparam GEigsMode Mode of the generalized eigen solver. In this solver -/// it is Spectra::GEIGS_CHOLESKY. -/// -/// Below is an example that demonstrates the usage of this class. -/// -/// \code{.cpp} -/// #include -/// #include -/// #include -/// #include -/// #include -/// #include -/// #include -/// -/// using namespace Spectra; -/// -/// int main() -/// { -/// // We are going to solve the generalized eigenvalue problem A * x = lambda * B * x -/// const int n = 100; -/// -/// // Define the A matrix -/// Eigen::MatrixXd M = Eigen::MatrixXd::Random(n, n); -/// Eigen::MatrixXd A = M + M.transpose(); -/// -/// // Define the B matrix, a band matrix with 2 on the diagonal and 1 on the subdiagonals -/// Eigen::SparseMatrix B(n, n); -/// B.reserve(Eigen::VectorXi::Constant(n, 3)); -/// for(int i = 0; i < n; i++) -/// { -/// B.insert(i, i) = 2.0; -/// if(i > 0) -/// B.insert(i - 1, i) = 1.0; -/// if(i < n - 1) -/// B.insert(i + 1, i) = 1.0; -/// } -/// -/// // Construct matrix operation object using the wrapper classes -/// DenseSymMatProd op(A); -/// SparseCholesky Bop(B); -/// -/// // Construct generalized eigen solver object, requesting the largest three generalized eigenvalues -/// SymGEigsSolver, SparseCholesky, GEIGS_CHOLESKY> -/// geigs(&op, &Bop, 3, 6); -/// -/// // Initialize and compute -/// geigs.init(); -/// int nconv = geigs.compute(); -/// -/// // Retrieve results -/// Eigen::VectorXd evalues; -/// Eigen::MatrixXd evecs; -/// if(geigs.info() == SUCCESSFUL) -/// { -/// evalues = geigs.eigenvalues(); -/// evecs = geigs.eigenvectors(); -/// } -/// -/// std::cout << "Generalized eigenvalues found:\n" << evalues << std::endl; -/// std::cout << "Generalized eigenvectors found:\n" << evecs.topRows(10) << std::endl; -/// -/// // Verify results using the generalized eigen solver in Eigen -/// Eigen::MatrixXd Bdense = B; -/// Eigen::GeneralizedSelfAdjointEigenSolver es(A, Bdense); -/// -/// std::cout << "Generalized eigenvalues:\n" << es.eigenvalues().tail(3) << std::endl; -/// std::cout << "Generalized eigenvectors:\n" << es.eigenvectors().rightCols(3).topRows(10) << std::endl; -/// -/// return 0; -/// } -/// \endcode - -// Partial specialization for GEigsMode = GEIGS_CHOLESKY -template < typename Scalar, - int SelectionRule, - typename OpType, - typename BOpType > -class SymGEigsSolver: - public SymEigsBase, IdentityBOp> -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - - BOpType* m_Bop; - -public: - /// - /// Constructor to create a solver object. - /// - /// \param op Pointer to the \f$A\f$ matrix operation object. It - /// should implement the matrix-vector multiplication operation of \f$A\f$: - /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either - /// create the object from the wrapper classes such as DenseSymMatProd, or - /// define their own that implements all the public member functions - /// as in DenseSymMatProd. - /// \param Bop Pointer to the \f$B\f$ matrix operation object. It - /// represents a Cholesky decomposition of \f$B\f$, and should - /// implement the lower and upper triangular solving operations: - /// calculating \f$L^{-1}v\f$ and \f$(L')^{-1}v\f$ for any vector - /// \f$v\f$, where \f$LL'=B\f$. Users could either - /// create the object from the wrapper classes such as DenseCholesky, or - /// define their own that implements all the public member functions - /// as in DenseCholesky. - /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, - /// where \f$n\f$ is the size of matrix. - /// \param ncv Parameter that controls the convergence speed of the algorithm. - /// Typically a larger `ncv` means faster convergence, but it may - /// also result in greater memory use and more matrix operations - /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, - /// and is advised to take \f$ncv \ge 2\cdot nev\f$. - /// - SymGEigsSolver(OpType* op, BOpType* Bop, Index nev, Index ncv) : - SymEigsBase, IdentityBOp>( - new SymGEigsCholeskyOp(*op, *Bop), NULL, nev, ncv - ), - m_Bop(Bop) - {} - - /// \cond - - ~SymGEigsSolver() - { - // m_op contains the constructed SymGEigsCholeskyOp object - delete this->m_op; - } - - Matrix eigenvectors(Index nvec) const - { - Matrix res = SymEigsBase, IdentityBOp>::eigenvectors(nvec); - Vector tmp(res.rows()); - const Index nconv = res.cols(); - for(Index i = 0; i < nconv; i++) - { - m_Bop->upper_triangular_solve(&res(0, i), tmp.data()); - res.col(i).noalias() = tmp; - } - - return res; - } - - Matrix eigenvectors() const - { - return SymGEigsSolver::eigenvectors(this->m_nev); - } - - /// \endcond -}; - - - -/// -/// \ingroup GEigenSolver -/// -/// This class implements the generalized eigen solver for real symmetric -/// matrices in the regular inverse mode, i.e., to solve \f$Ax=\lambda Bx\f$ -/// where \f$A\f$ is symmetric, and \f$B\f$ is positive definite with the operations -/// defined below. -/// -/// This solver requires two matrix operation objects: one for \f$A\f$ that implements -/// the matrix multiplication \f$Av\f$, and one for \f$B\f$ that implements the -/// matrix-vector product \f$Bv\f$ and the linear equation solving operation \f$B^{-1}v\f$. -/// -/// If \f$A\f$ and \f$B\f$ are stored as Eigen matrices, then the first operation -/// can be created using the DenseSymMatProd or SparseSymMatProd classes, and -/// the second operation can be created using the SparseRegularInverse class. There is no -/// wrapper class for a dense \f$B\f$ matrix since in this case the Cholesky mode -/// is always preferred. If the users need to define their own operation classes, then they -/// should implement all the public member functions as in those built-in classes. -/// -/// \tparam Scalar The element type of the matrix. -/// Currently supported types are `float`, `double` and `long double`. -/// \tparam SelectionRule An enumeration value indicating the selection rule of -/// the requested eigenvalues, for example `LARGEST_MAGN` -/// to retrieve eigenvalues with the largest magnitude. -/// The full list of enumeration values can be found in -/// \ref Enumerations. -/// \tparam OpType The name of the matrix operation class for \f$A\f$. Users could either -/// use the wrapper classes such as DenseSymMatProd and -/// SparseSymMatProd, or define their -/// own that implements all the public member functions as in -/// DenseSymMatProd. -/// \tparam BOpType The name of the matrix operation class for \f$B\f$. Users could either -/// use the wrapper class SparseRegularInverse, or define their -/// own that implements all the public member functions as in -/// SparseRegularInverse. -/// \tparam GEigsMode Mode of the generalized eigen solver. In this solver -/// it is Spectra::GEIGS_REGULAR_INVERSE. -/// - -// Partial specialization for GEigsMode = GEIGS_REGULAR_INVERSE -template < typename Scalar, - int SelectionRule, - typename OpType, - typename BOpType > -class SymGEigsSolver: - public SymEigsBase, BOpType> -{ -private: - typedef Eigen::Index Index; - -public: - /// - /// Constructor to create a solver object. - /// - /// \param op Pointer to the \f$A\f$ matrix operation object. It - /// should implement the matrix-vector multiplication operation of \f$A\f$: - /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either - /// create the object from the wrapper classes such as DenseSymMatProd, or - /// define their own that implements all the public member functions - /// as in DenseSymMatProd. - /// \param Bop Pointer to the \f$B\f$ matrix operation object. It should - /// implement the multiplication operation \f$Bv\f$ and the linear equation - /// solving operation \f$B^{-1}v\f$ for any vector \f$v\f$. Users could either - /// create the object from the wrapper class SparseRegularInverse, or - /// define their own that implements all the public member functions - /// as in SparseRegularInverse. - /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, - /// where \f$n\f$ is the size of matrix. - /// \param ncv Parameter that controls the convergence speed of the algorithm. - /// Typically a larger `ncv` means faster convergence, but it may - /// also result in greater memory use and more matrix operations - /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, - /// and is advised to take \f$ncv \ge 2\cdot nev\f$. - /// - SymGEigsSolver(OpType* op, BOpType* Bop, Index nev, Index ncv) : - SymEigsBase, BOpType>( - new SymGEigsRegInvOp(*op, *Bop), Bop, nev, ncv - ) - {} - - /// \cond - ~SymGEigsSolver() - { - // m_op contains the constructed SymGEigsRegInvOp object - delete this->m_op; - } - /// \endcond -}; - - -} // namespace Spectra - -#endif // SYM_GEIGS_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/Util/CompInfo.h b/src/external/Spectra/include/Spectra/Util/CompInfo.h deleted file mode 100644 index b8e639d6..00000000 --- a/src/external/Spectra/include/Spectra/Util/CompInfo.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef COMP_INFO_H -#define COMP_INFO_H - -namespace Spectra { - - -/// -/// \ingroup Enumerations -/// -/// The enumeration to report the status of computation. -/// -enum COMPUTATION_INFO -{ - SUCCESSFUL = 0, ///< Computation was successful. - - NOT_COMPUTED, ///< Used in eigen solvers, indicating that computation - ///< has not been conducted. Users should call - ///< the `compute()` member function of solvers. - - NOT_CONVERGING, ///< Used in eigen solvers, indicating that some eigenvalues - ///< did not converge. The `compute()` - ///< function returns the number of converged eigenvalues. - - NUMERICAL_ISSUE ///< Used in Cholesky decomposition, indicating that the - ///< matrix is not positive definite. -}; - - -} // namespace Spectra - -#endif // COMP_INFO_H diff --git a/src/external/Spectra/include/Spectra/Util/GEigsMode.h b/src/external/Spectra/include/Spectra/Util/GEigsMode.h deleted file mode 100644 index d03f269d..00000000 --- a/src/external/Spectra/include/Spectra/Util/GEigsMode.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef GEIGS_MODE_H -#define GEIGS_MODE_H - -namespace Spectra { - - -/// -/// \ingroup Enumerations -/// -/// The enumeration to specify the mode of generalized eigenvalue solver. -/// -enum GEIGS_MODE -{ - GEIGS_CHOLESKY = 0, ///< Using Cholesky decomposition to solve generalized eigenvalues. - - GEIGS_REGULAR_INVERSE, ///< Regular inverse mode for generalized eigenvalue solver. - - GEIGS_SHIFT_INVERT, ///< Shift-and-invert mode for generalized eigenvalue solver. - - GEIGS_BUCKLING, ///< Buckling mode for generalized eigenvalue solver. - - GEIGS_CAYLEY ///< Cayley transformation mode for generalized eigenvalue solver. -}; - - -} // namespace Spectra - -#endif // GEIGS_MODE_H diff --git a/src/external/Spectra/include/Spectra/Util/SelectionRule.h b/src/external/Spectra/include/Spectra/Util/SelectionRule.h deleted file mode 100644 index 19f71dcf..00000000 --- a/src/external/Spectra/include/Spectra/Util/SelectionRule.h +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SELECTION_RULE_H -#define SELECTION_RULE_H - -#include // std::vector -#include // std::abs -#include // std::sort -#include // std::complex -#include // std::pair -#include // std::invalid_argument - -namespace Spectra { - - -/// -/// \defgroup Enumerations -/// -/// Enumeration types for the selection rule of eigenvalues. -/// - -/// -/// \ingroup Enumerations -/// -/// The enumeration of selection rules of desired eigenvalues. -/// -enum SELECT_EIGENVALUE -{ - LARGEST_MAGN = 0, ///< Select eigenvalues with largest magnitude. Magnitude - ///< means the absolute value for real numbers and norm for - ///< complex numbers. Applies to both symmetric and general - ///< eigen solvers. - - LARGEST_REAL, ///< Select eigenvalues with largest real part. Only for general eigen solvers. - - LARGEST_IMAG, ///< Select eigenvalues with largest imaginary part (in magnitude). Only for general eigen solvers. - - LARGEST_ALGE, ///< Select eigenvalues with largest algebraic value, considering - ///< any negative sign. Only for symmetric eigen solvers. - - SMALLEST_MAGN, ///< Select eigenvalues with smallest magnitude. Applies to both symmetric and general - ///< eigen solvers. - - SMALLEST_REAL, ///< Select eigenvalues with smallest real part. Only for general eigen solvers. - - SMALLEST_IMAG, ///< Select eigenvalues with smallest imaginary part (in magnitude). Only for general eigen solvers. - - SMALLEST_ALGE, ///< Select eigenvalues with smallest algebraic value. Only for symmetric eigen solvers. - - BOTH_ENDS ///< Select eigenvalues half from each end of the spectrum. When - ///< `nev` is odd, compute more from the high end. Only for symmetric eigen solvers. -}; - -/// -/// \ingroup Enumerations -/// -/// The enumeration of selection rules of desired eigenvalues. Alias for `SELECT_EIGENVALUE`. -/// -enum SELECT_EIGENVALUE_ALIAS -{ - WHICH_LM = 0, ///< Alias for `LARGEST_MAGN` - WHICH_LR, ///< Alias for `LARGEST_REAL` - WHICH_LI, ///< Alias for `LARGEST_IMAG` - WHICH_LA, ///< Alias for `LARGEST_ALGE` - WHICH_SM, ///< Alias for `SMALLEST_MAGN` - WHICH_SR, ///< Alias for `SMALLEST_REAL` - WHICH_SI, ///< Alias for `SMALLEST_IMAG` - WHICH_SA, ///< Alias for `SMALLEST_ALGE` - WHICH_BE ///< Alias for `BOTH_ENDS` -}; - -/// \cond - -// Get the element type of a "scalar" -// ElemType => double -// ElemType< std::complex > => double -template -class ElemType -{ -public: - typedef T type; -}; - -template -class ElemType< std::complex > -{ -public: - typedef T type; -}; - -// When comparing eigenvalues, we first calculate the "target" -// to sort. For example, if we want to choose the eigenvalues with -// largest magnitude, the target will be -abs(x). -// The minus sign is due to the fact that std::sort() sorts in ascending order. - -// Default target: throw an exception -template -class SortingTarget -{ -public: - static typename ElemType::type get(const Scalar& val) - { - using std::abs; - throw std::invalid_argument("incompatible selection rule"); - return -abs(val); - } -}; - -// Specialization for LARGEST_MAGN -// This covers [float, double, complex] x [LARGEST_MAGN] -template -class SortingTarget -{ -public: - static typename ElemType::type get(const Scalar& val) - { - using std::abs; - return -abs(val); - } -}; - -// Specialization for LARGEST_REAL -// This covers [complex] x [LARGEST_REAL] -template -class SortingTarget, LARGEST_REAL> -{ -public: - static RealType get(const std::complex& val) - { - return -val.real(); - } -}; - -// Specialization for LARGEST_IMAG -// This covers [complex] x [LARGEST_IMAG] -template -class SortingTarget, LARGEST_IMAG> -{ -public: - static RealType get(const std::complex& val) - { - using std::abs; - return -abs(val.imag()); - } -}; - -// Specialization for LARGEST_ALGE -// This covers [float, double] x [LARGEST_ALGE] -template -class SortingTarget -{ -public: - static Scalar get(const Scalar& val) - { - return -val; - } -}; - -// Here BOTH_ENDS is the same as LARGEST_ALGE, but -// we need some additional steps, which are done in -// SymEigsSolver.h => retrieve_ritzpair(). -// There we move the smallest values to the proper locations. -template -class SortingTarget -{ -public: - static Scalar get(const Scalar& val) - { - return -val; - } -}; - -// Specialization for SMALLEST_MAGN -// This covers [float, double, complex] x [SMALLEST_MAGN] -template -class SortingTarget -{ -public: - static typename ElemType::type get(const Scalar& val) - { - using std::abs; - return abs(val); - } -}; - -// Specialization for SMALLEST_REAL -// This covers [complex] x [SMALLEST_REAL] -template -class SortingTarget, SMALLEST_REAL> -{ -public: - static RealType get(const std::complex& val) - { - return val.real(); - } -}; - -// Specialization for SMALLEST_IMAG -// This covers [complex] x [SMALLEST_IMAG] -template -class SortingTarget, SMALLEST_IMAG> -{ -public: - static RealType get(const std::complex& val) - { - using std::abs; - return abs(val.imag()); - } -}; - -// Specialization for SMALLEST_ALGE -// This covers [float, double] x [SMALLEST_ALGE] -template -class SortingTarget -{ -public: - static Scalar get(const Scalar& val) - { - return val; - } -}; - -// Sort eigenvalues and return the order index -template -class PairComparator -{ -public: - bool operator() (const PairType& v1, const PairType& v2) - { - return v1.first < v2.first; - } -}; - -template -class SortEigenvalue -{ -private: - typedef typename ElemType::type TargetType; // Type of the sorting target, will be - // a floating number type, e.g. "double" - typedef std::pair PairType; // Type of the sorting pair, including - // the sorting target and the index - - std::vector pair_sort; - -public: - SortEigenvalue(const T* start, int size) : - pair_sort(size) - { - for(int i = 0; i < size; i++) - { - pair_sort[i].first = SortingTarget::get(start[i]); - pair_sort[i].second = i; - } - PairComparator comp; - std::sort(pair_sort.begin(), pair_sort.end(), comp); - } - - std::vector index() - { - std::vector ind(pair_sort.size()); - for(unsigned int i = 0; i < ind.size(); i++) - ind[i] = pair_sort[i].second; - - return ind; - } -}; - -/// \endcond - - -} // namespace Spectra - -#endif // SELECTION_RULE_H diff --git a/src/external/Spectra/include/Spectra/Util/SimpleRandom.h b/src/external/Spectra/include/Spectra/Util/SimpleRandom.h deleted file mode 100644 index 7b1e6162..00000000 --- a/src/external/Spectra/include/Spectra/Util/SimpleRandom.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2016-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef SIMPLE_RANDOM_H -#define SIMPLE_RANDOM_H - -#include - -/// \cond - -namespace Spectra { - - -// We need a simple pseudo random number generator here: -// 1. It is used to generate initial and restarted residual vector. -// 2. It is not necessary to be so "random" and advanced. All we hope -// is that the residual vector is not in the space spanned by the -// current Krylov space. This should be met almost surely. -// 3. We don't want to call RNG in C++, since we actually want the -// algorithm to be deterministic. Also, calling RNG in C/C++ is not -// allowed in R packages submitted to CRAN. -// 4. The method should be as simple as possible, so an LCG is enough. -// 5. Based on public domain code by Ray Gardner -// http://stjarnhimlen.se/snippets/rg_rand.c - - -template -class SimpleRandom -{ -private: - typedef Eigen::Index Index; - typedef Eigen::Matrix Vector; - - const unsigned int m_a; // multiplier - const unsigned long m_max; // 2^31 - 1 - long m_rand; - - inline long next_long_rand(long seed) - { - unsigned long lo, hi; - - lo = m_a * (long)(seed & 0xFFFF); - hi = m_a * (long)((unsigned long)seed >> 16); - lo += (hi & 0x7FFF) << 16; - if(lo > m_max) - { - lo &= m_max; - ++lo; - } - lo += hi >> 15; - if(lo > m_max) - { - lo &= m_max; - ++lo; - } - return (long)lo; - } -public: - SimpleRandom(unsigned long init_seed) : - m_a(16807), - m_max(2147483647L), - m_rand(init_seed ? (init_seed & m_max) : 1) - {} - - Scalar random() - { - m_rand = next_long_rand(m_rand); - return Scalar(m_rand) / Scalar(m_max) - Scalar(0.5); - } - - // Vector of random numbers of type Scalar - // Ranging from -0.5 to 0.5 - Vector random_vec(const Index len) - { - Vector res(len); - for(Index i = 0; i < len; i++) - { - m_rand = next_long_rand(m_rand); - res[i] = Scalar(m_rand) / Scalar(m_max) - Scalar(0.5); - } - return res; - } -}; - - -} // namespace Spectra - -/// \endcond - -#endif // SIMPLE_RANDOM_H diff --git a/src/external/Spectra/include/Spectra/Util/TypeTraits.h b/src/external/Spectra/include/Spectra/Util/TypeTraits.h deleted file mode 100644 index a4cc05b2..00000000 --- a/src/external/Spectra/include/Spectra/Util/TypeTraits.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (C) 2018-2019 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef TYPE_TRAITS_H -#define TYPE_TRAITS_H - -#include -#include - -/// \cond - -namespace Spectra { - - -// For a real value type "Scalar", we want to know its smallest -// positive value, i.e., std::numeric_limits::min(). -// However, we must take non-standard value types into account, -// so we rely on Eigen::NumTraits. -// -// Eigen::NumTraits has defined epsilon() and lowest(), but -// lowest() means negative highest(), which is a very small -// negative value. -// -// Therefore, we manually define this limit, and use eplison()^3 -// to mimic it for non-standard types. - -// Generic definition -template -struct TypeTraits -{ - static inline Scalar min() - { - return Eigen::numext::pow(Eigen::NumTraits::epsilon(), Scalar(3)); - } -}; - -// Full specialization -template <> -struct TypeTraits -{ - static inline float min() - { - return std::numeric_limits::min(); - } -}; - -template <> -struct TypeTraits -{ - static inline double min() - { - return std::numeric_limits::min(); - } -}; - -template <> -struct TypeTraits -{ - static inline long double min() - { - return std::numeric_limits::min(); - } -}; - - -} // namespace Spectra - -/// \endcond - -#endif // TYPE_TRAITS_H diff --git a/src/external/Spectra/include/Spectra/contrib/LOBPCGSolver.h b/src/external/Spectra/include/Spectra/contrib/LOBPCGSolver.h deleted file mode 100644 index 5ca001f6..00000000 --- a/src/external/Spectra/include/Spectra/contrib/LOBPCGSolver.h +++ /dev/null @@ -1,501 +0,0 @@ -// Written by Anna Araslanova -// Modified by Yixuan Qiu -// License: MIT - -#ifndef LOBPCG_SOLVER -#define LOBPCG_SOLVER - -#include -#include - -#include -#include -#include -#include -#include - -#include "../SymGEigsSolver.h" - - -namespace Spectra { - - /// - /// \ingroup EigenSolver - /// - - /// *** METHOD - /// The class represent the LOBPCG algorithm, which was invented by Andrew Knyazev - /// Theoretical background of the procedure can be found in the articles below - /// - Knyazev, A.V., 2001. Toward the optimal preconditioned eigensolver : Locally optimal block preconditioned conjugate gradient method.SIAM journal on scientific computing, 23(2), pp.517 - 541. - /// - Knyazev, A.V., Argentati, M.E., Lashuk, I. and Ovtchinnikov, E.E., 2007. Block locally optimal preconditioned eigenvalue xolvers(BLOPEX) in HYPRE and PETSc.SIAM Journal on Scientific Computing, 29(5), pp.2224 - 2239. - /// - /// *** CONDITIONS OF USE - /// Locally Optimal Block Preconditioned Conjugate Gradient(LOBPCG) is a method for finding the M smallest eigenvalues - /// and eigenvectors of a large symmetric positive definite generalized eigenvalue problem - /// \f$Ax=\lambda Bx,\f$ - /// where \f$A_{NxN}\f$ is a symmetric matrix, \f$B\f$ is symmetric and positive - definite. \f$A and B\f$ are also assumed large and sparse - /// \f$\textit{M}\f$ should be \f$\<< textit{N}\f$ (at least \f$\textit{5M} < \textit{N} \f$) - /// - /// *** ARGUMENTS - /// Eigen::SparseMatrix A; // N*N - Ax = lambda*Bx, lrage and sparse - /// Eigen::SparseMatrix X; // N*M - initial approximations to eigenvectors (random in general case) - /// Spectra::LOBPCGSolver solver(A, X); - /// *Eigen::SparseMatrix B; // N*N - Ax = lambda*Bx, sparse, positive definite - /// solver.setConstraints(B); - /// *Eigen::SparseMatrix Y; // N*K - constraints, already found eigenvectors - /// solver.setB(B); - /// *Eigen::SparseMatrix T; // N*N - preconditioner ~ A^-1 - /// solver.setPreconditioner(T); - /// - /// *** OUTCOMES - /// solver.solve(); // compute eigenpairs // void - /// solver.info(); // state of converjance // int - /// solver.residuals(); // get residuals to evaluate biases // Eigen::Matrix - /// solver.eigenvalues(); // get eigenvalues // Eigen::Matrix - /// solver.eigenvectors(); // get eigenvectors // Eigen::Matrix - /// - /// *** EXAMPLE - /// \code{.cpp} - /// #include - /// - /// // random A - /// Matrix a; - /// a = (Matrix::Random(10, 10).array() > 0.6).cast() * Matrix::Random(10, 10).array() * 5; - /// a = Matrix((a).triangularView()) + Matrix((a).triangularView()).transpose(); - /// for (int i = 0; i < 10; i++) - /// a(i, i) = i + 0.5; - /// std::cout << a << "\n"; - /// Eigen::SparseMatrix A(a.sparseView()); - /// // random X - /// Eigen::Matrix x; - /// x = Matrix::Random(10, 2).array(); - /// Eigen::SparseMatrix X(x.sparseView()); - /// // solve Ax = lambda*x - /// Spectra::LOBPCGSolver solver(A, X); - /// solver.compute(10, 1e-4); // 10 iterations, L2_tolerance = 1e-4*N - /// std::cout << "info\n" << solver.info() << std::endl; - /// std::cout << "eigenvalues\n" << solver.eigenvalues() << std::endl; - /// std::cout << "eigenvectors\n" << solver.eigenvectors() << std::endl; - /// std::cout << "residuals\n" << solver.residuals() << std::endl; - /// \endcode - /// - - template < typename Scalar = long double> - class LOBPCGSolver { - private: - - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - - typedef std::complex Complex; - typedef Eigen::Matrix ComplexMatrix; - typedef Eigen::Matrix ComplexVector; - - typedef Eigen::SparseMatrix SparseMatrix; - typedef Eigen::SparseMatrix SparseComplexMatrix; - - const int m_n; // dimension of matrix A - const int m_nev; // number of eigenvalues requested - SparseMatrix A, X; - SparseMatrix m_Y, m_B, m_preconditioner; - bool flag_with_constraints, flag_with_B, flag_with_preconditioner; - - public: - SparseMatrix m_residuals; - Matrix m_evectors; - Vector m_evalues; - int m_info; - - private: - - // B-orthonormalize matrix M - int orthogonalizeInPlace(SparseMatrix &M, SparseMatrix &B, \ - SparseMatrix &true_BM, bool has_true_BM = false) { - - SparseMatrix BM; - - if (has_true_BM == false) { - if (flag_with_B) { BM = B * M; } - else { BM = M; } - } - else { - BM = true_BM; - } - - Eigen::SimplicialLDLT chol_MBM(M.transpose() * BM); - - if (chol_MBM.info() != SUCCESSFUL) { - // LDLT decomposition fail - m_info = chol_MBM.info(); - return chol_MBM.info(); - } - - SparseComplexMatrix Upper_MBM = chol_MBM.matrixU().template cast(); - ComplexVector D_MBM_vec = chol_MBM.vectorD().template cast(); - - D_MBM_vec = D_MBM_vec.cwiseSqrt(); - - for (int i = 0; i < D_MBM_vec.rows(); i++) { - D_MBM_vec(i) = Complex(1.0, 0.0) / D_MBM_vec(i); - } - - SparseComplexMatrix D_MBM_mat(D_MBM_vec.asDiagonal()); - - SparseComplexMatrix U_inv(Upper_MBM.rows(), Upper_MBM.cols()); - U_inv.setIdentity(); - Upper_MBM.template triangularView().solveInPlace(U_inv); - - SparseComplexMatrix right_product = U_inv * D_MBM_mat; - M = M*right_product.real(); - if (flag_with_B) { true_BM = B * M; } - else { true_BM = M; } - - return SUCCESSFUL; - } - - void applyConstraintsInPlace(SparseMatrix &X, SparseMatrix&Y, \ - SparseMatrix&B) { - SparseMatrix BY; - if (flag_with_B) { BY = B * Y; } - else { BY = Y; } - - SparseMatrix YBY = Y.transpose() * BY; - SparseMatrix BYX = BY.transpose() * X; - - SparseMatrix YBY_XYX = (Matrix(YBY).bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(Matrix(BYX))).sparseView(); - X = X - Y * YBY_XYX; - } - - /* - return - 'AB - CD' - */ - Matrix stack_4_matricies(Matrix A, Matrix B, \ - Matrix C, Matrix D) { - Matrix result(A.rows() + C.rows(), A.cols() + B.cols()); - result.topLeftCorner(A.rows(), A.cols()) = A; - result.topRightCorner(B.rows(), B.cols()) = B; - result.bottomLeftCorner(C.rows(), C.cols()) = C; - result.bottomRightCorner(D.rows(), D.cols()) = D; - return result; - } - - Matrix stack_9_matricies(Matrix A, Matrix B, Matrix C, \ - Matrix D, Matrix E, Matrix F, \ - Matrix G, Matrix H, Matrix I) { - - Matrix result(A.rows() + D.rows() + G.rows(), A.cols() + B.cols() + C.cols()); - result.block(0, 0, A.rows(), A.cols()) = A; - result.block(0, A.cols(), B.rows(), B.cols()) = B; - result.block(0, A.cols() + B.cols(), C.rows(), C.cols()) = C; - result.block(A.rows(), 0, D.rows(), D.cols()) = D; - result.block(A.rows(), A.cols(), E.rows(), E.cols()) = E; - result.block(A.rows(), A.cols() + B.cols(), F.rows(), F.cols()) = F; - result.block(A.rows() + D.rows(), 0, G.rows(), G.cols()) = G; - result.block(A.rows() + D.rows(), A.cols(), H.rows(), H.cols()) = H; - result.block(A.rows() + D.rows(), A.cols() + B.cols(), I.rows(), I.cols()) = I; - - return result; - } - - void sort_epairs(Vector &evalues, Matrix &evectors, int SelectionRule) { - - std::function cmp; - if (SelectionRule == SMALLEST_ALGE) - cmp = std::less{}; - else - cmp = std::greater{}; - - std::map epairs(cmp); - for (int i = 0; i < m_evectors.cols(); ++i) - epairs.insert(std::make_pair(evalues(i), evectors.col(i))); - - int i = 0; - for (auto& epair : epairs) { - evectors.col(i) = epair.second; - evalues(i) = epair.first; - i++; - } - } - - void removeColumns(SparseMatrix& matrix, std::vector& colToRemove) - { - // remove columns through matrix multiplication - SparseMatrix new_matrix(matrix.cols(), matrix.cols() - int(colToRemove.size())); - int iCol = 0; - std::vector> tripletList; - tripletList.reserve(matrix.cols() - int(colToRemove.size())); - - for (int iRow = 0; iRow < matrix.cols(); iRow++) { - if (std::find(colToRemove.begin(), colToRemove.end(), iRow) == colToRemove.end()) { - tripletList.push_back(Eigen::Triplet(iRow, iCol, 1)); - iCol++; - } - } - - new_matrix.setFromTriplets(tripletList.begin(), tripletList.end()); - matrix = matrix * new_matrix; - } - - int checkConvergence_getBlocksize(SparseMatrix & m_residuals, Scalar tolerance_L2, std::vector & columnsToDelete) { - // square roots from sum of squares by column - int BlockSize = m_nev; - Scalar sum, buffer; - - for (int iCol = 0; iCol < m_nev; iCol++) { - sum = 0; - for (int iRow = 0; iRow < m_n; iRow++) { - buffer = m_residuals.coeff(iRow, iCol); - sum += buffer * buffer; - } - - if (sqrt(sum) < tolerance_L2) { - BlockSize--; - columnsToDelete.push_back(iCol); - } - } - return BlockSize; - } - - - public: - - LOBPCGSolver(const SparseMatrix& A, const SparseMatrix X) : - m_n(A.rows()), - m_nev(X.cols()), - m_info(NOT_COMPUTED), - flag_with_constraints(false), - flag_with_B(false), - flag_with_preconditioner(false), - A(A), - X(X) - { - if (A.rows() != X.rows() || A.rows() != A.cols()) - throw std::invalid_argument("Wrong size"); - - //if (m_n < 5* m_nev) - // throw std::invalid_argument("The problem size is small compared to the block size. Use standard eigensolver"); - } - - void setConstraints(const SparseMatrix& Y) { - m_Y = Y; - flag_with_constraints = true; - } - - void setB(const SparseMatrix& B) { - if (B.rows() != A.rows() || B.cols() != A.cols()) - throw std::invalid_argument("Wrong size"); - m_B = B; - flag_with_B = true; - } - - void setPreconditioner(const SparseMatrix& preconditioner) { - m_preconditioner = preconditioner; - flag_with_preconditioner = true; - } - - void compute(int maxit = 10, Scalar tol_div_n = 1e-7) { - - Scalar tolerance_L2 = tol_div_n * m_n; - int BlockSize; - int max_iter = std::min(m_n, maxit); - - SparseMatrix directions, AX, AR, BX, AD, ADD, DD, BDD, BD, XAD, RAD, DAD, XBD, RBD, BR, sparse_eVecX, sparse_eVecR, sparse_eVecD, inverse_matrix; - Matrix XAR, RAR, XBR, gramA, gramB, eVecX, eVecR, eVecD; - std::vector columnsToDelete; - - if (flag_with_constraints) { - // Apply the constraints Y to X - applyConstraintsInPlace(X, m_Y, m_B); - } - - // Make initial vectors orthonormal - // implicit BX declaration - if (orthogonalizeInPlace(X, m_B, BX) != SUCCESSFUL) { - max_iter = 0; - } - - AX = A * X; - // Solve the following NxN eigenvalue problem for all N eigenvalues and -vectors: - // first approximation via a dense problem - Eigen::EigenSolver eigs(Matrix(X.transpose() * AX)); - - if (eigs.info() != SUCCESSFUL) { - m_info = eigs.info(); - max_iter = 0; - } - else { - m_evalues = eigs.eigenvalues().real(); - m_evectors = eigs.eigenvectors().real(); - sort_epairs(m_evalues, m_evectors, SMALLEST_ALGE); - sparse_eVecX = m_evectors.sparseView(); - - X = X * sparse_eVecX; - AX = AX * sparse_eVecX; - BX = BX * sparse_eVecX; - } - - - for (int iter_num = 0; iter_num < max_iter; iter_num++) { - m_residuals.resize(m_n, m_nev); - for (int i = 0; i < m_nev; i++) { - m_residuals.col(i) = AX.col(i) - m_evalues(i) * BX.col(i); - } - BlockSize = checkConvergence_getBlocksize(m_residuals, tolerance_L2, columnsToDelete); - - if (BlockSize == 0) { - m_info = SUCCESSFUL; - break; - } - - // substitution of the original active mask - if (columnsToDelete.size() > 0) { - removeColumns(m_residuals, columnsToDelete); - if (iter_num > 0) { - removeColumns(directions, columnsToDelete); - removeColumns(AD, columnsToDelete); - removeColumns(BD, columnsToDelete); - } - columnsToDelete.clear(); // for next iteration - } - - if (flag_with_preconditioner) { - // Apply the preconditioner to the residuals - m_residuals = m_preconditioner * m_residuals; - } - - if (flag_with_constraints) { - // Apply the constraints Y to residuals - applyConstraintsInPlace(m_residuals, m_Y, m_B); - } - - if (orthogonalizeInPlace(m_residuals, m_B, BR) != SUCCESSFUL) { - break; - } - AR = A * m_residuals; - - // Orthonormalize conjugate directions - if (iter_num > 0) { - if (orthogonalizeInPlace(directions, m_B, BD, true) != SUCCESSFUL) { - break; - } - AD = A * directions; - } - - // Perform the Rayleigh Ritz Procedure - XAR = Matrix(X.transpose() * AR); - RAR = Matrix(m_residuals.transpose() * AR); - XBR = Matrix(X.transpose() * BR); - - if (iter_num > 0) { - - XAD = X.transpose() * AD; - RAD = m_residuals.transpose() * AD; - DAD = directions.transpose() * AD; - XBD = X.transpose() * BD; - RBD = m_residuals.transpose() * BD; - - gramA = stack_9_matricies(m_evalues.asDiagonal(), XAR, XAD, XAR.transpose(), RAR, RAD, XAD.transpose(), RAD.transpose(), DAD.transpose()); - gramB = stack_9_matricies(Matrix::Identity(m_nev, m_nev), XBR, XBD, XBR.transpose(), Matrix::Identity(BlockSize, BlockSize), RBD, XBD.transpose(), RBD.transpose(), Matrix::Identity(BlockSize, BlockSize)); - - } - else { - gramA = stack_4_matricies(m_evalues.asDiagonal(), XAR, XAR.transpose(), RAR); - gramB = stack_4_matricies(Matrix::Identity(m_nev, m_nev), XBR, XBR.transpose(), Matrix::Identity(BlockSize, BlockSize)); - } - - //calculate the lowest/largest m eigenpairs; Solve the generalized eigenvalue problem. - DenseSymMatProd Aop(gramA); - DenseCholesky Bop(gramB); - - SymGEigsSolver, \ - DenseCholesky, GEIGS_CHOLESKY> geigs(&Aop, &Bop, m_nev, std::min(10, int(gramA.rows()) - 1)); - - geigs.init(); - int nconv = geigs.compute(); - - //Mat evecs; - if (geigs.info() == SUCCESSFUL) { - m_evalues = geigs.eigenvalues(); - m_evectors = geigs.eigenvectors(); - sort_epairs(m_evalues, m_evectors, SMALLEST_ALGE); - } - else { - // Problem With General EgenVec - m_info = geigs.info(); - break; - } - - // Compute Ritz vectors - if (iter_num > 0) { - eVecX = m_evectors.block(0, 0, m_nev, m_nev); - eVecR = m_evectors.block(m_nev, 0, BlockSize, m_nev); - eVecD = m_evectors.block(m_nev + BlockSize, 0, BlockSize, m_nev); - - sparse_eVecX = eVecX.sparseView(); - sparse_eVecR = eVecR.sparseView(); - sparse_eVecD = eVecD.sparseView(); - - DD = m_residuals * sparse_eVecR; // new conjugate directions - ADD = AR * sparse_eVecR; - BDD = BR * sparse_eVecR; - - DD = DD + directions * sparse_eVecD; - ADD = ADD + AD * sparse_eVecD; - BDD = BDD + BD * sparse_eVecD; - } - else { - eVecX = m_evectors.block(0, 0, m_nev, m_nev); - eVecR = m_evectors.block(m_nev, 0, BlockSize, m_nev); - - sparse_eVecX = eVecX.sparseView(); - sparse_eVecR = eVecR.sparseView(); - - DD = m_residuals * sparse_eVecR; - ADD = AR * sparse_eVecR; - BDD = BR * sparse_eVecR; - } - - X = X * sparse_eVecX + DD; - AX = AX * sparse_eVecX + ADD; - BX = BX * sparse_eVecX + BDD; - - directions = DD; - AD = ADD; - BD = BDD; - - } // iteration loop - - // calculate last residuals - m_residuals.resize(m_n, m_nev); - for (int i = 0; i < m_nev; i++) { - m_residuals.col(i) = AX.col(i) - m_evalues(i) * BX.col(i); - } - BlockSize = checkConvergence_getBlocksize(m_residuals, tolerance_L2, columnsToDelete); - - if (BlockSize == 0) { - m_info = SUCCESSFUL; - } - } // compute - - Vector eigenvalues() { - return m_evalues; - } - - Matrix eigenvectors() { - return m_evectors; - } - - Matrix residuals() { - return Matrix(m_residuals); - } - - int info() { return m_info; } - - }; - - -} // namespace Spectra - -#endif // LOBPCG_SOLVER diff --git a/src/external/Spectra/include/Spectra/contrib/PartialSVDSolver.h b/src/external/Spectra/include/Spectra/contrib/PartialSVDSolver.h deleted file mode 100644 index dad5b400..00000000 --- a/src/external/Spectra/include/Spectra/contrib/PartialSVDSolver.h +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (C) 2018 Yixuan Qiu -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#ifndef PARTIAL_SVD_SOLVER_H -#define PARTIAL_SVD_SOLVER_H - -#include -#include "../SymEigsSolver.h" - - -namespace Spectra { - - -// Abstract class for matrix operation -template -class SVDMatOp -{ -public: - virtual int rows() const = 0; - virtual int cols() const = 0; - - // y_out = A' * A * x_in or y_out = A * A' * x_in - virtual void perform_op(const Scalar* x_in, Scalar* y_out) = 0; - - virtual ~SVDMatOp() {} -}; - -// Operation of a tall matrix in SVD -// We compute the eigenvalues of A' * A -// MatrixType is either Eigen::Matrix or Eigen::SparseMatrix -template -class SVDTallMatOp: public SVDMatOp -{ -private: - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef const Eigen::Ref ConstGenericMatrix; - - ConstGenericMatrix m_mat; - const int m_dim; - Vector m_cache; - -public: - // Constructor - SVDTallMatOp(ConstGenericMatrix& mat) : - m_mat(mat), - m_dim(std::min(mat.rows(), mat.cols())), - m_cache(mat.rows()) - {} - - // These are the rows and columns of A' * A - int rows() const { return m_dim; } - int cols() const { return m_dim; } - - // y_out = A' * A * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) - { - MapConstVec x(x_in, m_mat.cols()); - MapVec y(y_out, m_mat.cols()); - m_cache.noalias() = m_mat * x; - y.noalias() = m_mat.transpose() * m_cache; - } -}; - -// Operation of a wide matrix in SVD -// We compute the eigenvalues of A * A' -// MatrixType is either Eigen::Matrix or Eigen::SparseMatrix -template -class SVDWideMatOp: public SVDMatOp -{ -private: - typedef Eigen::Matrix Vector; - typedef Eigen::Map MapConstVec; - typedef Eigen::Map MapVec; - typedef const Eigen::Ref ConstGenericMatrix; - - ConstGenericMatrix m_mat; - const int m_dim; - Vector m_cache; - -public: - // Constructor - SVDWideMatOp(ConstGenericMatrix& mat) : - m_mat(mat), - m_dim(std::min(mat.rows(), mat.cols())), - m_cache(mat.cols()) - {} - - // These are the rows and columns of A * A' - int rows() const { return m_dim; } - int cols() const { return m_dim; } - - // y_out = A * A' * x_in - void perform_op(const Scalar* x_in, Scalar* y_out) - { - MapConstVec x(x_in, m_mat.rows()); - MapVec y(y_out, m_mat.rows()); - m_cache.noalias() = m_mat.transpose() * x; - y.noalias() = m_mat * m_cache; - } -}; - -// Partial SVD solver -// MatrixType is either Eigen::Matrix or Eigen::SparseMatrix -template < typename Scalar = double, - typename MatrixType = Eigen::Matrix > -class PartialSVDSolver -{ -private: - typedef Eigen::Matrix Matrix; - typedef Eigen::Matrix Vector; - typedef const Eigen::Ref ConstGenericMatrix; - - ConstGenericMatrix m_mat; - const int m_m; - const int m_n; - SVDMatOp* m_op; - SymEigsSolver< Scalar, LARGEST_ALGE, SVDMatOp >* m_eigs; - int m_nconv; - Matrix m_evecs; - -public: - // Constructor - PartialSVDSolver(ConstGenericMatrix& mat, int ncomp, int ncv) : - m_mat(mat), m_m(mat.rows()), m_n(mat.cols()), m_evecs(0, 0) - { - // Determine the matrix type, tall or wide - if(m_m > m_n) - { - m_op = new SVDTallMatOp(mat); - } else { - m_op = new SVDWideMatOp(mat); - } - - // Solver object - m_eigs = new SymEigsSolver< Scalar, LARGEST_ALGE, SVDMatOp >(m_op, ncomp, ncv); - } - - // Destructor - virtual ~PartialSVDSolver() - { - delete m_eigs; - delete m_op; - } - - // Computation - int compute(int maxit = 1000, Scalar tol = 1e-10) - { - m_eigs->init(); - m_nconv = m_eigs->compute(maxit, tol); - - return m_nconv; - } - - // The converged singular values - Vector singular_values() const - { - Vector svals = m_eigs->eigenvalues().cwiseSqrt(); - - return svals; - } - - // The converged left singular vectors - Matrix matrix_U(int nu) - { - if(m_evecs.cols() < 1) - { - m_evecs = m_eigs->eigenvectors(); - } - nu = std::min(nu, m_nconv); - if(m_m <= m_n) - { - return m_evecs.leftCols(nu); - } - - return m_mat * (m_evecs.leftCols(nu).array().rowwise() / m_eigs->eigenvalues().head(nu).transpose().array().sqrt()).matrix(); - } - - // The converged right singular vectors - Matrix matrix_V(int nv) - { - if(m_evecs.cols() < 1) - { - m_evecs = m_eigs->eigenvectors(); - } - nv = std::min(nv, m_nconv); - if(m_m > m_n) - { - return m_evecs.leftCols(nv); - } - - return m_mat.transpose() * (m_evecs.leftCols(nv).array().rowwise() / m_eigs->eigenvalues().head(nv).transpose().array().sqrt()).matrix(); - } -}; - - -} // namespace Spectra - -#endif // PARTIAL_SVD_SOLVER_H diff --git a/src/external/arpack++/include/README b/src/external/arpack++/include/README deleted file mode 100644 index dc4a33dc..00000000 --- a/src/external/arpack++/include/README +++ /dev/null @@ -1,232 +0,0 @@ -This is the ARPACK++ include directory. - -1) Files included in this directory: - - a) Files that contain ARPACK++ classes definitions: - - i) Base classes: - - file class - ---------- ---------------- - arrseig.h ARrcStdEig - arrgeig.h ARrcGenEig - arseig.h ARStdEig - argeig.h ARGenEig - armat.h ARMatrix - - - ii) Classes that require matrix-vector product functions: - - file class - ---------- ---------------- - arssym.h ARSymStdEig - arsnsym.h ARNonSymStdEig - arscomp.h ARCompStdEig - argsym.h ARSymGenEig - argnsym.h ARNonSymGenEig - argcomp.g ARCompGenEig - - - iii) Classes that require matrices in CSC format (SuperLU version): - - file class - ---------- ---------------- - arlssym.h ARluSymStdEig - arlsnsym.h ARluNonSymStdEig - arlscomp.h ARluCompStdEig - arlgsym.h ARluSymGenEig - arlgnsym.h ARluNonSymGenEig - arlgcomp.h ARluCompGenEig - - - iv) Classes that require matrices in CSC format (UMFPACK version): - - file class - ---------- ---------------- - arussym.h ARluSymStdEig - arusnsym.h ARluNonSymStdEig - aruscomp.h ARluCompStdEig - arugsym.h ARluSymGenEig - arugnsym.h ARluNonSymGenEig - arugcomp.h ARluCompGenEig - - - v) Classes that require matrices in band format: - - file class - ---------- ---------------- - arbssym.h ARluSymStdEig - arbsnsym.h ARluNonSymStdEig - arbscomp.h ARluCompStdEig - arbgsym.h ARluSymGenEig - arbgnsym.h ARluNonSymGenEig - arbgcomp.h ARluCompGenEig - - - vi) Reverse communication classes: - - file class - ---------- ---------------- - arrssym.h ARrcSymStdEig - arrsnsym.h ARrcNonSymStdEig - arrscomp.h ARrcCompStdEig - arrgsym.h ARrcSymGenEig - arrgnsym.h ARrcNonSymGenEig - arrgcomp.h ARrcCompGenEig - - - vii) Matrix classes: - - file class - ---------- ---------------- - arlsmat.h ARluSymMatrix - arlspen.h ARluSymPencil - arlnsmat.h ARluNonSymMatrix - arlnspen.h ARluNonSymPencil - arusmat.h ARumSymMatrix - aruspen.h ARumSymPencil - arunsmat.h ARumNonSymMatrix - arunspen.h ARumNonSymPencil - arbsmat.h ARbdSymMatrix - arbspen.h ARbdSymPencil - arbnsmat.h ARbdNonSymMatrix - arbnspen.h ARbdNonSymPencil - arhbmat.h ARhbMatrix - - - b) Package interface files: - - i) ARPACK FORTRAN interface: - - file Contents - ---------- ----------------------------------------------- - saupp.h Interface with dsaupd and ssaupd subroutines. - seupp.h Interface with dseupd and sseupd subroutines. - naupp.h Interface with dnaupd and snaupd subroutines. - neupp.h Interface with dneupd and sneupd subroutines. - caupp.h Interface with znaupd and cnaupd subroutines. - ceupp.h Interface with zneupd and cneupd subroutines. - debug.h Interface with ARPACK debugging variables. - arpackf.h Fortran to C function prototypes convertion. - - - ii) LAPACK and BLAS1 interface: - - file Contents - ---------- ----------------------------------------------- - lapackc.h Various LAPACK function declarations. - lapackf.h Fortran to C function prototypes convertion. - blas1c.h Various BLAS1 function declarations. - blas1f.h Fortran to C function prototypes convertion. - - - iii) SuperLU interface: - - file Contents - ---------- ----------------------------------------------- - superluc.h Various SuperLU function declarations. - arlspdef.h Altered version of ssp_defs.h, dsp_defs.h, - csp_defs.h and zsp_defs.h header files. - arlsupm.h Unaltered copy of supermatrix.h header file. - arlnames.h Unaltered copy of Cnames.h header file. - arlutil.h Unaltered copy of util.h, superlu_enum_consts.h. - arlcomp.h Unaltered copy of dcomplex.h and scomplex.h. - - - iv) UMFPACK interface: - - file Contents - ---------- ----------------------------------------------- - umfpackc.h Various UMFPACK function declarations. - umfpackf.h Fortran to C function prototypes convertion. - - - c) Other auxiliary files: - - file Contents - ---------- ----------------------------------------------- - arch.h Machine dependent functions and variable types. - arcomp.h "arcomplex" complex type definition. - arerror.h "ArpackError" class definition. - - - -2) Compiler-dependent instructions. - - Some compiler-dependent functions and data types used by arpack++ are - grouped in the file arch.h. This file should be changed to reflect the - characteristics of your system. Another file, arcomp.h, contains the - definition of a class template called arcomplex, created to emulate - the g++ complex class when another compiler is being used. This file - must also be changed if g++ (or CC) is not being used. - - a) Changing ARPACK++ parameters and definitions included in arch.h: - - All ARPACK++ parameters that are not intended to be changed frequently - were included in the arch.h file. Are defined in this file - - i) Some machine and problem-dependent umfpack parameters. - - If the umfpack is to be used, the user can modify some of its - parameters to correctly reflect the environment and the class - of problems being solved. The constants included in arch.h - correspond to a subset of the parameters generated by the um21i - umfpack function. Other relevant parameters can also be passed - to the ARumNonSymMatrix class constructor. - - ii) Some fortran to c conversion functions. - - Because fortran and c++ functions tend to have different - representations in different platforms, a function that - converts a fortran function name to the c++ format is - defined in arch.h. This function can be altered by the user - if the environment being used was not included in arch.h. - - iii) Some fortran to c type conversion rules. - - arch.h also includes the definition of some rules required - to convert INTEGER and LOGICAL FORTRAN types to c++. - - iv) The c++ bool type. - - If the c++ compiler being used does not include a bool type, - this type can also be defined in arch.h. - - - b) Redefining arcomplex class in arcomp.h: - - ARPACK++ uses a self-defined complex class called arcomplex. - Actually, arcomplex is a class template used to represent - both single and double precision complex numbers. It was created - in an effort to permit ARPACK++ to deal with different compilers, - since c++ does not define a unique complex type. - arcomplex is intended to emulate the gnu g++ complex class when - other compilers are being used (when g++ is used, ARPACK++ simply - declares arcomplex to be the standard complex type). arcomp.h - includes a complex class definition for the CC compiler only. At - the present time, no other compiler was used to generate ARPACK++ - programs, so further work must be done to permit the use of the - library with other compilers. - To define a new complex type, the user must create a class - template (called arcomplex) that contains at least three members: - - i) A default constructor; - ii) A copy constructor; and - iii) A constructor that takes two real numbers as parameters (one - is the real and other the imaginary part of the complex number). - - Naturally, all usual mathematical operations on complex numbers, - such as addition, multiplication, multiplication by a real number, - etc, should also be defined. But because most compilers include a - complex data type, the simplest way of defining arcomplex is to use - only the three constructors mentioned above to establish a relation - between the actual complex class and the gnu g++ standard. - - -5) ARPACK (fortran) authors: - - Danny Sorensen (sorensen@caam.rice.edu) - Richard Lehoucq (lehoucq@mcs.anl.gov) - Chao Yang (chao@caam.rice.edu) - Kristi Maschhoff (kristyn@caam.rice.edu) - diff --git a/src/external/arpack++/include/arbgcomp.h b/src/external/arpack++/include/arbgcomp.h deleted file mode 100644 index 93f68228..00000000 --- a/src/external/arpack++/include/arbgcomp.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBGComp.h. - Arpack++ class ARluCompGenEig definition - (band matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARBGCOMP_H -#define ARBGCOMP_H - -#include -#include -#include "arch.h" -#include "arbnsmat.h" -#include "arbnspen.h" -#include "arrseig.h" -#include "argcomp.h" - - -template -class ARluCompGenEig: - public virtual - ARCompGenEig, ARFLOAT >, - ARbdNonSymPencil, ARFLOAT > > { - - private: - - // a) Data structure used to store matrices. - - ARbdNonSymPencil, ARFLOAT > Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluCompGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(arcomplex sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(arcomplex sigmap); - - // c.2) Constructors and destructor. - - ARluCompGenEig() { } - // Short constructor. - - ARluCompGenEig(int nevp, ARbdNonSymMatrix, ARFLOAT>& A, - ARbdNonSymMatrix, ARFLOAT>& B, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluCompGenEig(int nevp, ARbdNonSymMatrix, ARFLOAT>& A, - ARbdNonSymMatrix, ARFLOAT>& B, - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluCompGenEig(const ARluCompGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluCompGenEig() { } - - // d) Operators. - - ARluCompGenEig& operator=(const ARluCompGenEig& other); - // Assignment operator. - -}; // class ARluCompGenEig. - - -// ------------------------------------------------------------------------ // -// ARluCompGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluCompGenEig:: -Copy(const ARluCompGenEig& other) -{ - - ARCompGenEig, ARFLOAT >, - ARbdNonSymPencil, ARFLOAT> >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - -} // Copy. - - -template -inline void ARluCompGenEig:: -ChangeShift(arcomplex sigmaRp) -{ - - this->objOP->FactorAsB(sigmaRp); - ARrcStdEig >::ChangeShift(sigmaRp); - -} // ChangeShift. - - -template -inline void ARluCompGenEig::SetRegularMode() -{ - - ARStdEig, - ARbdNonSymPencil, ARFLOAT> >:: - SetRegularMode(&Pencil, - &ARbdNonSymPencil, ARFLOAT>::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluCompGenEig:: -SetShiftInvertMode(arcomplex sigmap) -{ - - ARCompGenEig, ARFLOAT>, - ARbdNonSymPencil, ARFLOAT> >:: - SetShiftInvertMode(sigmap, &Pencil, - &ARbdNonSymPencil,ARFLOAT>::MultInvAsBv); - -} // SetShiftInvertMode. - - -template -inline ARluCompGenEig:: -ARluCompGenEig(int nevp, ARbdNonSymMatrix, ARFLOAT>& A, - ARbdNonSymMatrix, ARFLOAT>& B, const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, - arcomplex* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARbdNonSymPencil, ARFLOAT>::MultInvBAv, - &Pencil, - &ARbdNonSymPencil, ARFLOAT>::MultBv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluCompGenEig:: -ARluCompGenEig(int nevp, ARbdNonSymMatrix, ARFLOAT>& A, - ARbdNonSymMatrix, ARFLOAT>& B, - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARbdNonSymPencil, ARFLOAT>::MultInvAsBv, - &Pencil, - &ARbdNonSymPencil,ARFLOAT>::MultBv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - SetShiftInvertMode(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluCompGenEig& ARluCompGenEig:: -operator=(const ARluCompGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBGCOMP_H diff --git a/src/external/arpack++/include/arbgnsym.h b/src/external/arpack++/include/arbgnsym.h deleted file mode 100644 index 19a14130..00000000 --- a/src/external/arpack++/include/arbgnsym.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBGNSym.h. - Arpack++ class ARluNonSymGenEig definition - (band matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARBGNSYM_H -#define ARBGNSYM_H - -#include -#include -#include "arch.h" -#include "arbnsmat.h" -#include "arbnspen.h" -#include "argnsym.h" - - -template -class ARluNonSymGenEig: - public virtual ARNonSymGenEig, - ARbdNonSymPencil > { - - private: - - // a) Data structure used to store matrices. - - ARbdNonSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluNonSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp = 0.0); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetComplexShiftMode(char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp); - - // c.2) Constructors and destructor. - - ARluNonSymGenEig() { } - // Short constructor. - - ARluNonSymGenEig(int nevp, ARbdNonSymMatrix& A, - ARbdNonSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluNonSymGenEig(int nevp, ARbdNonSymMatrix& A, - ARbdNonSymMatrix& B, ARFLOAT sigma, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (real shift and invert mode). - - ARluNonSymGenEig(int nevp, ARbdNonSymMatrix& A, - ARbdNonSymMatrix& B, char partp, - ARFLOAT sigmaRp, ARFLOAT sigmaIp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (complex shift and invert mode). - - ARluNonSymGenEig(const ARluNonSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluNonSymGenEig& operator=(const ARluNonSymGenEig& other); - // Assignment operator. - -}; // class ARluNonSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluNonSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymGenEig:: -Copy(const ARluNonSymGenEig& other) -{ - - ARNonSymGenEig, - ARbdNonSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - -} // Copy. - - -template -inline void ARluNonSymGenEig:: -ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - if (sigmaIp == 0.0) { - this->objOP->FactorAsB(sigmaRp); - } - else { - this->objOP->FactorAsB(sigmaRp, sigmaIp, this->part); - } - ARrcNonSymGenEig::ChangeShift(sigmaRp, sigmaIp); - -} // ChangeShift. - - -template -inline void ARluNonSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARbdNonSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluNonSymGenEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARNonSymGenEig, - ARbdNonSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, - &ARbdNonSymPencil::MultInvAsBv); - -} // SetShiftInvertMode. - - -template -inline void ARluNonSymGenEig:: -SetComplexShiftMode(char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - ARNonSymGenEig, - ARbdNonSymPencil >:: - SetComplexShiftMode(partp, sigmaRp, sigmaIp, &Pencil, - &ARbdNonSymPencil::MultInvAsBv, - &Pencil, &ARbdNonSymPencil::MultAv); - -} // SetComplexShiftMode. - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARbdNonSymMatrix& A, - ARbdNonSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARbdNonSymPencil::MultInvBAv, &Pencil, - &ARbdNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARbdNonSymMatrix& A, - ARbdNonSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARbdNonSymPencil::MultInvAsBv, &Pencil, - &ARbdNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - SetShiftInvertMode(sigmap); - -} // Long constructor (real shift and invert mode). - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARbdNonSymMatrix& A, - ARbdNonSymMatrix& B, char partp, - ARFLOAT sigmaRp, ARFLOAT sigmaIp, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARbdNonSymPencil::MultInvAsBv, &Pencil, - &ARbdNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - SetComplexShiftMode(partp, sigmaRp, sigmaIp); - -} // Long constructor (complex shift and invert mode). - - -template -ARluNonSymGenEig& ARluNonSymGenEig:: -operator=(const ARluNonSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBGNSYM_H diff --git a/src/external/arpack++/include/arbgsym.h b/src/external/arpack++/include/arbgsym.h deleted file mode 100644 index 82926bca..00000000 --- a/src/external/arpack++/include/arbgsym.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBGSym.h. - Arpack++ class ARluSymGenEig definition - (band matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARBGSYM_H -#define ARBGSYM_H - -#include -#include -#include "arch.h" -#include "arbsmat.h" -#include "arbspen.h" -#include "argsym.h" - - -template -class ARluSymGenEig: - public virtual ARSymGenEig, - ARbdSymPencil > { - - private: - - // a) Data structure used to store matrices. - - ARbdSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmap); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetBucklingMode(ARFLOAT sigmap); - - virtual void SetCayleyMode(ARFLOAT sigmap); - - // c.2) Constructors and destructor. - - ARluSymGenEig() { } - // Short constructor. - - ARluSymGenEig(int nevp, ARbdSymMatrix& A, - ARbdSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymGenEig(char InvertModep, int nevp, ARbdSymMatrix& A, - ARbdSymMatrix& B, ARFLOAT sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert, buckling and Cayley modes). - - ARluSymGenEig(const ARluSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluSymGenEig& operator=(const ARluSymGenEig& other); - // Assignment operator. - -}; // class ARluSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymGenEig:: -Copy(const ARluSymGenEig& other) -{ - - ARSymGenEig, - ARbdSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - -} // Copy. - - -template -inline void ARluSymGenEig::ChangeShift(ARFLOAT sigmap) -{ - - this->objOP->FactorAsB(sigmap); - ARrcSymGenEig::ChangeShift(sigmap); - -} // ChangeShift. - - -template -inline void ARluSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARbdSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluSymGenEig:: -SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARbdSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, &ARbdSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARbdSymPencil::MultBv); - -} // SetShiftInvertMode. - - -template -inline void ARluSymGenEig:: -SetBucklingMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARbdSymPencil >:: - SetBucklingMode(sigmap, &Pencil, &ARbdSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARbdSymPencil::MultAv); - -} // SetBucklingMode. - - -template -inline void ARluSymGenEig:: -SetCayleyMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARbdSymPencil >:: - SetCayleyMode(sigmap, &Pencil, &ARbdSymPencil::MultInvAsBv, - &Pencil, &ARbdSymPencil::MultAv); - this->ChangeMultBx(&Pencil, &ARbdSymPencil::MultBv); - -} // SetCayleyMode. - - -template -inline ARluSymGenEig:: -ARluSymGenEig(int nevp, ARbdSymMatrix& A, - ARbdSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->InvertMode = 'S'; - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARbdSymPencil::MultInvBAv, &Pencil, - &ARbdSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymGenEig:: -ARluSymGenEig(char InvertModep, int nevp, ARbdSymMatrix& A, - ARbdSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARbdSymPencil::MultInvAsBv, &Pencil, - &ARbdSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - this->InvertMode = this->CheckInvertMode(InvertModep); - switch (this->InvertMode) { - case 'B': - this->ChangeMultBx(&Pencil, &ARbdSymPencil::MultAv); - case 'S': - ChangeShift(sigmap); - break; - case 'C': - SetCayleyMode(sigmap); - } - -} // Long constructor (shift and invert, buckling and Cayley modes). - - -template -ARluSymGenEig& ARluSymGenEig:: -operator=(const ARluSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBGSYM_H diff --git a/src/external/arpack++/include/arbnsmat.h b/src/external/arpack++/include/arbnsmat.h deleted file mode 100644 index f56069f7..00000000 --- a/src/external/arpack++/include/arbnsmat.h +++ /dev/null @@ -1,431 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBNSMat.h. - Arpack++ class ARbdNonSymMatrix definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "arbnspen.h" - -#ifndef ARBNSMAT_H -#define ARBNSMAT_H - -#include -#include "arch.h" -#include "armat.h" -#include "arerror.h" -#include "blas1c.h" -#include "lapackc.h" - -template class ARbdNonSymPencil; - -template -class ARbdNonSymMatrix: public ARMatrix { - - friend class ARbdNonSymPencil; - friend class ARbdNonSymPencil; - - protected: - - bool factored; - int ndiagL; - int ndiagU; - int lda; - int info; - int* ipiv; - ARTYPE* A; - ARTYPE* Ainv; - - void ClearMem(); - - virtual void Copy(const ARbdNonSymMatrix& other); - - void ExpandA(); - - void SubtractAsI(ARTYPE sigma); - - void CreateStructure(); - - void ThrowError(); - - public: - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultMtv(ARTYPE* v, ARTYPE* w); - - void MultMtMv(ARTYPE* v, ARTYPE* w); - - void MultMMtv(ARTYPE* v, ARTYPE* w); - - void Mult0MMt0v(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, int ndiagLp, int ndiagUp, ARTYPE* Ap); - - ARbdNonSymMatrix(): ARMatrix() { factored = false; } - // Short constructor that does nothing. - - ARbdNonSymMatrix(int np, int ndiagLp, int ndiagUp, ARTYPE* Ap); - // Long constructor. - - ARbdNonSymMatrix(const ARbdNonSymMatrix& other) { Copy(other); } - // Copy constructor. - - virtual ~ARbdNonSymMatrix() { ClearMem(); } - // Destructor. - - ARbdNonSymMatrix& operator=(const ARbdNonSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARbdNonSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARbdNonSymMatrix::ClearMem() -{ - - if (factored) { - delete[] Ainv; - delete[] ipiv; - Ainv = NULL; - ipiv = NULL; - } - -} // ClearMem. - - -template -inline void ARbdNonSymMatrix:: -Copy(const ARbdNonSymMatrix& other) -{ - - // Copying very fundamental variables and user-defined parameters. - - this->m = other.m; - this->n = other.n; - this->defined = other.defined; - factored = other.factored; - ndiagL = other.ndiagL; - ndiagU = other.ndiagU; - lda = other.lda; - info = other.info; - A = other.A; - - // Returning from here if "other" was not factored. - - if (!factored) return; - - // Copying vectors. - - Ainv = new ARTYPE[ this->n*lda]; - ipiv = new int[ this->n]; - - copy( this->n*lda, other.Ainv, 1, Ainv, 1); - for (int i=0; i< this->n; i++) ipiv[i] = other.ipiv[i]; - -} // Copy. - - -template -void ARbdNonSymMatrix::ExpandA() -{ - - int i, inca; - - // Copying A to Ainv. - - inca = ndiagL+ndiagU+1; - for (i = 0; i < inca; i++) { - copy( this->n, &A[i], inca, &Ainv[ndiagL+i], lda); - } - -} // ExpandA. - - -template -void ARbdNonSymMatrix::SubtractAsI(ARTYPE sigma) -{ - - // Copying A to Ainv. - - ExpandA(); - - // Subtracting sigma from diagonal elements. - - for (int i=(ndiagL+ndiagU); i<(lda* this->n); i+=lda) Ainv[i] -= sigma; - -} // SubtractAsI. - - -template -inline void ARbdNonSymMatrix::CreateStructure() -{ - - ClearMem(); - Ainv = new ARTYPE[lda* this->n]; - ipiv = new int[ this->n]; - -} // CreateStructure. - - -template -inline void ARbdNonSymMatrix::ThrowError() -{ - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARbdNonSymMatrix::FactorA"); - } - else if (info) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARbdNonSymMatrix::FactorA"); - } - -} // ThrowError. - - -template -void ARbdNonSymMatrix::FactorA() -{ - - // Quitting the function if A was not defined. - - if (! this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARbdNonSymMatrix::FactorA"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Copying A to Ainv; - - ExpandA(); - - // Decomposing A. - - gbtrf( this->n, this->n, ndiagL, ndiagU, Ainv, lda, ipiv, info); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorA. - - -template -void ARbdNonSymMatrix::FactorAsI(ARTYPE sigma) -{ - - // Quitting the function if A was not defined. - - if (! this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARbdNonSymMatrix::FactorAsI"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Subtracting sigma*I from A. - - SubtractAsI(sigma); - - // Decomposing AsI. - - gbtrf( this->n, this->n, ndiagL, ndiagU, Ainv, lda, ipiv, info); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorAsI. - - -template -void ARbdNonSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE one; - ARTYPE zero; - - one = (ARTYPE)0 + 1.0; - zero = (ARTYPE)0; - - // Quitting the function if A was not defined. - - if (! this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARbdNonSymMatrix::MultMv"); - } - - // Determining w = M.v. - - gbmv("N", this->m, this->n, ndiagL, ndiagU, one, A, - ndiagL+ndiagU+1, v, 1, zero, w, 1); - -} // MultMv. - - -template -void ARbdNonSymMatrix::MultMtv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE one; - ARTYPE zero; - - one = (ARTYPE)0 + 1.0; - zero = (ARTYPE)0; - - // Quitting the function if A was not defined. - - if (! this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARbdNonSymMatrix::MultMtv"); - } - - // Determining w = M'.v. - - gbmv("T", this->m, this->n, ndiagL, ndiagU, one, A, - ndiagL+ndiagU+1, v, 1, zero, w, 1); - -} // MultMtv. - - -template -void ARbdNonSymMatrix::MultMtMv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE* t = new ARTYPE[ this->m]; - - MultMv(v,t); - MultMtv(t,w); - - delete[] t; - -} // MultMtMv. - - -template -void ARbdNonSymMatrix::MultMMtv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE* t = new ARTYPE[ this->n]; - - MultMtv(v,t); - MultMv(t,w); - - delete[] t; - -} // MultMMtv. - - -template -void ARbdNonSymMatrix::Mult0MMt0v(ARTYPE* v, ARTYPE* w) -{ - - MultMv(&v[ this->m],w); - MultMtv(v,&w[ this->m]); - -} // Mult0MMt0v. - - -template -void ARbdNonSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARbdNonSymMatrix::MultInvv"); - } - - // Overwritting w with v. - - copy( this->n, v, 1, w, 1); - - // Solving A.w = v (or AsI.w = v). - - gbtrs("N", this->n, ndiagL, ndiagU, 1, Ainv, lda, ipiv, w, this->m, info); - - // Handling errors. - - ThrowError(); - -} // MultInvv. - - -template -inline void ARbdNonSymMatrix:: -DefineMatrix(int np, int ndiagLp, int ndiagUp, ARTYPE* Ap) -{ - - // Defining member variables. - - this->m = np; - this->n = np; - ndiagL = ndiagLp; - ndiagU = ndiagUp; - lda = 2*ndiagL+ndiagU+1; - A = Ap; - this->defined = true; - Ainv = NULL; - ipiv = NULL; - info = 0; - -} // DefineMatrix. - - -template -inline ARbdNonSymMatrix:: -ARbdNonSymMatrix(int np, int ndiagLp, - int ndiagUp, ARTYPE* Ap) : ARMatrix(np) -{ - - factored = false; - DefineMatrix(np, ndiagLp, ndiagUp, Ap); - -} // Long constructor. - - -template -ARbdNonSymMatrix& ARbdNonSymMatrix:: -operator=(const ARbdNonSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBNSMAT_H diff --git a/src/external/arpack++/include/arbnspen.h b/src/external/arpack++/include/arbnspen.h deleted file mode 100644 index 2284ffa3..00000000 --- a/src/external/arpack++/include/arbnspen.h +++ /dev/null @@ -1,470 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBNSPen.h. - Arpack++ class ARbdNonSymPencil definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARBNSPEN_H -#define ARBNSPEN_H - -#include "arch.h" -#include "arerror.h" -#include "blas1c.h" -#include "lapackc.h" -#include "arbnsmat.h" - - -template -class ARbdNonSymPencil -{ - - protected: - - char part; - ARbdNonSymMatrix* A; - ARbdNonSymMatrix* B; - ARbdNonSymMatrix AsB; -#ifdef ARCOMP_H - ARbdNonSymMatrix, ARFLOAT> AsBc; -#endif - - int max(int a, int b) { return (a>b)?a:b; } - - int min(int a, int b) { return (a dy[], int incy); - - void ComplexAxpy(int n, arcomplex da, ARTYPE dx[], - int incx, arcomplex dy[], int incy); - - virtual void Copy(const ARbdNonSymPencil& other); - - void SubtractAsB(ARTYPE sigma); - -#ifdef ARCOMP_H - void SubtractAsB(ARFLOAT sigmaR, ARFLOAT sigmaI); -#endif - - public: - -#ifdef ARCOMP_H - bool IsFactored() { return (AsB.IsFactored()||AsBc.IsFactored()); } -#else - bool IsFactored() { return AsB.IsFactored(); } -#endif - - void FactorAsB(ARTYPE sigma); - -#ifdef ARCOMP_H - void FactorAsB(ARFLOAT sigmaR, ARFLOAT sigmaI, char partp = 'R'); -#endif - - void MultAv(ARTYPE* v, ARTYPE* w) { A->MultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - -#ifdef ARCOMP_H - void MultInvAsBv(arcomplex* v, arcomplex* w); -#endif - - void MultInvAsBv(ARFLOAT* v, ARFLOAT* w); - - void DefineMatrices(ARbdNonSymMatrix& Ap, - ARbdNonSymMatrix& Bp); - - ARbdNonSymPencil() { part = 'N'; } - // Short constructor that does nothing. - - ARbdNonSymPencil(ARbdNonSymMatrix& Ap, - ARbdNonSymMatrix& Bp); - // Long constructor. - - ARbdNonSymPencil(const ARbdNonSymPencil& other) { Copy(other); } - // Copy constructor. - - virtual ~ARbdNonSymPencil() { } - // Destructor. - - ARbdNonSymPencil& operator=(const ARbdNonSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARbdNonSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARbdNonSymPencil:: -Copy(const ARbdNonSymPencil& other) -{ - - part = other.part; - A = other.A; - B = other.B; - AsB = other.AsB; -#ifdef ARCOMP_H - AsBc = other.AsBc; -#endif - -} // Copy. - - -template -void ARbdNonSymPencil:: -ComplexCopy(int n, ARFLOAT dx[], int incx, arcomplex dy[], int incy) -{ - - for (int ix=0, iy=0; ix<(n*incx); ix+=incx, iy+=incy) { - dy[iy] = arcomplex(dx[ix], 0.0); - } - -} // ComplexCopy. - - -template -void ARbdNonSymPencil:: -ComplexAxpy(int n, arcomplex da, ARTYPE dx[], int incx, - arcomplex dy[], int incy) -{ - - for (int ix=0, iy=0; ix<(n*incx); ix+=incx, iy+=incy) { - dy[iy] += da*dx[ix]; - } - -} // ComplexAxpy. - - -template -void ARbdNonSymPencil::SubtractAsB(ARTYPE sigma) -{ - - int i, inca, incb, minL, minU, begB, begAsB; - ARTYPE negsig; - - inca = A->ndiagL+A->ndiagU+1; - incb = B->ndiagL+B->ndiagU+1; - negsig = -sigma; - - // Expanding A. - - begAsB = AsB.ndiagL+AsB.ndiagU-A->ndiagU; - for (i = 0; i < inca; i++) { - copy(AsB.n, &A->A[i], inca, &AsB.Ainv[begAsB+i], AsB.lda); - } - - // Transferring part of B (*(-sigma)) if AsB.ndiagU > A->ndiagU. - - if (A->ndiagU < AsB.ndiagU) { - for (i = 0; i < AsB.ndiagU-A->ndiagU; i++) { - copy(AsB.n, &B->A[i], incb, &AsB.Ainv[AsB.ndiagL+i], AsB.lda); - scal(AsB.n, negsig, &AsB.Ainv[AsB.ndiagL+i], AsB.lda); - } - } - - // Subtracting sigma*B from A. - - minL = min(A->ndiagL, B->ndiagL); - minU = min(A->ndiagU, B->ndiagU); - begB = B->ndiagU-minU; - begAsB = AsB.ndiagL+AsB.ndiagU-minU; - - for (i = 0; i < minL+minU+1; i++) { - axpy(AsB.n, -sigma, &B->A[begB+i], incb, &AsB.Ainv[begAsB+i], AsB.lda); - } - - // Transferring part of B (*(-sigma)) if AsB.ndiagL > A->ndiagL. - - if (A->ndiagL < AsB.ndiagL) { - begB = B->ndiagU+1+minL; - begAsB = AsB.ndiagL+AsB.ndiagU+1+minL; - for (i = 0; i < AsB.ndiagL-A->ndiagL; i++) { - copy(AsB.n, &B->A[begB+i], incb, &AsB.Ainv[begAsB+i], AsB.lda); - scal(AsB.n, negsig, &AsB.Ainv[begAsB+i], AsB.lda); - } - } - -} // SubtractAsB (ARTYPE shift). - - -#ifdef ARCOMP_H -template -void ARbdNonSymPencil:: -SubtractAsB(ARFLOAT sigmaR, ARFLOAT sigmaI) -{ - - int i, inca, incb, minL, minU, begB, begAsB; - arcomplex sigma; - - inca = A->ndiagL+A->ndiagU+1; - incb = B->ndiagL+B->ndiagU+1; - sigma = arcomplex(sigmaR, sigmaI); - - // Expanding A. - - begAsB = AsBc.ndiagL+AsBc.ndiagU-A->ndiagU; - for (i = 0; i < inca; i++) { - ComplexCopy(AsBc.n,(ARFLOAT*)(&A->A[i]),inca,&AsBc.Ainv[begAsB+i],AsBc.lda); - } - - // Transferring part of B (*(-sigma)) if AsBc.ndiagU > A->ndiagU. - - if (A->ndiagU < AsBc.ndiagU) { - for (i = 0; i < AsBc.ndiagU-A->ndiagU; i++) { - ComplexCopy(AsBc.n, (ARFLOAT*)(&B->A[i]), incb, - &AsBc.Ainv[AsBc.ndiagL+i], AsBc.lda); - scal(AsBc.n, -sigma, &AsBc.Ainv[AsBc.ndiagL+i], AsBc.lda); - } - } - - // Subtracting sigma*B from A. - - minL = min(A->ndiagL, B->ndiagL); - minU = min(A->ndiagU, B->ndiagU); - begB = B->ndiagU-minU; - begAsB = AsBc.ndiagL+AsBc.ndiagU-minU; - - for (i = 0; i < minL+minU+1; i++) { - ComplexAxpy(AsBc.n, -sigma, &B->A[begB+i], incb, - &AsBc.Ainv[begAsB+i], AsBc.lda); - } - - // Transferring part of B (*(-sigma)) if AsBc.ndiagL > A->ndiagL. - - if (A->ndiagL < AsBc.ndiagL) { - begB = B->ndiagU+1+minL; - begAsB = AsBc.ndiagL+AsBc.ndiagU+1+minL; - for (i = 0; i < AsBc.ndiagL-A->ndiagL; i++) { - ComplexCopy(AsBc.n, (ARFLOAT*)(&B->A[begB+i]), incb, - &AsBc.Ainv[begAsB+i], AsBc.lda); - scal(AsBc.n, -sigma, &AsBc.Ainv[begAsB+i], AsBc.lda); - } - } - -} // SubtractAsB (arcomplex shift). -#endif // ARCOMP_H - - -template -void ARbdNonSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARbdNonSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARbdNonSymPencil::FactorAsB"); - } - - // Copying A to AsB if sigma = 0. - - if (sigma == (ARTYPE)0) { - - AsB = *A; - if (!AsB.IsFactored()) AsB.FactorA(); - return; - - } - - // Defining matrix AsB. - - if (!AsB.IsDefined()) { - AsB.DefineMatrix(A->ncols(), max(A->ndiagL, B->ndiagL), - max(A->ndiagU, B->ndiagU), A->A); - } - - // Reserving memory for some vectors used in matrix decomposition. - - AsB.CreateStructure(); - - // Subtracting sigma*B from A and storing the result on AsB. - - SubtractAsB(sigma); - - // Decomposing AsB. - - gbtrf(AsB.n, AsB.n, AsB.ndiagL, AsB.ndiagU, - AsB.Ainv, AsB.lda, AsB.ipiv, AsB.info); - - // Handling errors. - - AsB.ThrowError(); - - AsB.factored = true; - -} // FactorAsB (ARTYPE shift). - - -#ifdef ARCOMP_H -template -void ARbdNonSymPencil:: -FactorAsB(ARFLOAT sigmaR, ARFLOAT sigmaI, char partp) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARbdNonSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARbdNonSymPencil::FactorAsB"); - } - - // Defining matrix AsBc. - - if (!AsBc.IsDefined()) { - part = partp; - AsBc.DefineMatrix(A->ncols(), max(A->ndiagL,B->ndiagL), - max(A->ndiagU,B->ndiagU), 0); - } - - // Reserving memory for some vectors used in matrix decomposition. - - AsBc.CreateStructure(); - - // Subtracting sigma*B from A and storing the result on AsBc. - - SubtractAsB(sigmaR, sigmaI); - - // Decomposing AsBc. - - gbtrf(AsBc.n, AsBc.n, AsBc.ndiagL, AsBc.ndiagU, - AsBc.Ainv, AsBc.lda, AsBc.ipiv, AsBc.info); - - // Handling errors. - - AsBc.ThrowError(); - - AsBc.factored = true; - -} // FactorAsB (arcomplex shift). -#endif // ARCOMP_H. - - -template -void ARbdNonSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - B->MultInvv(w, w); - -} // MultInvBAv. - - -#ifdef ARCOMP_H -template -void ARbdNonSymPencil:: -MultInvAsBv(arcomplex* v, arcomplex* w) -{ - - AsB.MultInvv((ARTYPE*)v, (ARTYPE*)w); - -} // MultInvAsBv (arcomplex). -#endif // ARCOMP_H. - - -template -void ARbdNonSymPencil::MultInvAsBv(ARFLOAT* v, ARFLOAT* w) -{ - - if (part == 'N') { // shift is real. - - AsB.MultInvv((ARTYPE*)v, (ARTYPE*)w); - - } - else { // shift is complex. - -#ifdef ARCOMP_H - - int i; - arcomplex *tv, *tw; - - tv = new arcomplex[AsBc.ncols()]; - tw = new arcomplex[AsBc.ncols()]; - - for (i=0; i!=AsBc.ncols(); i++) tv[i] = arcomplex(v[i], 0.0); - - AsBc.MultInvv(tv, tw); - - if (part=='I') { - for (i=0; i!=AsBc.ncols(); i++) w[i] = imag(tw[i]); - } - else { - for (i=0; i!=AsBc.ncols(); i++) w[i] = real(tw[i]); - } - - delete[] tv; - delete[] tw; - -#endif // ARCOMP_H. - - } - -} // MultInvAsBv (ARFLOAT). - - -template -inline void ARbdNonSymPencil:: -DefineMatrices(ARbdNonSymMatrix& Ap, - ARbdNonSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - -} // DefineMatrices. - - -template -inline ARbdNonSymPencil:: -ARbdNonSymPencil(ARbdNonSymMatrix& Ap, - ARbdNonSymMatrix& Bp) -{ - - DefineMatrices(Ap, Bp); - -} // Long constructor. - - -template -ARbdNonSymPencil& ARbdNonSymPencil:: -operator=(const ARbdNonSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBNSPEN_H diff --git a/src/external/arpack++/include/arbscomp.h b/src/external/arpack++/include/arbscomp.h deleted file mode 100644 index 334cd3a6..00000000 --- a/src/external/arpack++/include/arbscomp.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBSComp.h. - Arpack++ class ARluCompStdEig definition - (band matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARBSCOMP_H -#define ARBSCOMP_H - -#include -#include -#include "arch.h" -#include "arscomp.h" -#include "arbnsmat.h" -#include "arrseig.h" - - -template -class ARluCompStdEig: - public virtual ARCompStdEig, ARFLOAT> > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(arcomplex sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(arcomplex sigmap); - - // a.2) Constructors and destructor. - - ARluCompStdEig() { } - // Short constructor. - - ARluCompStdEig(int nevp, ARbdNonSymMatrix, ARFLOAT>& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluCompStdEig(int nevp, ARbdNonSymMatrix, ARFLOAT>& A, - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluCompStdEig(const ARluCompStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluCompStdEig() { } - // Destructor. - - - // b) Operators. - - ARluCompStdEig& operator=(const ARluCompStdEig& other); - // Assignment operator. - -}; // class ARluCompStdEig. - - -// ------------------------------------------------------------------------ // -// ARluCompStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluCompStdEig:: -ChangeShift(arcomplex sigmaRp) -{ - - this->objOP->FactorAsI(sigmaRp); - ARrcStdEig >::ChangeShift(sigmaRp); - -} // ChangeShift. - - -template -inline void ARluCompStdEig::SetRegularMode() -{ - - ARStdEig, - ARbdNonSymMatrix,ARFLOAT> >:: - SetRegularMode(this->objOP,&ARbdNonSymMatrix,ARFLOAT>::MultMv); - -} // SetRegularMode. - - -template -inline void ARluCompStdEig:: -SetShiftInvertMode(arcomplex sigmap) -{ - - ARStdEig, - ARbdNonSymMatrix,ARFLOAT> >:: - SetShiftInvertMode(sigmap, this->objOP, - &ARbdNonSymMatrix,ARFLOAT>::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluCompStdEig:: -ARluCompStdEig(int nevp, ARbdNonSymMatrix, ARFLOAT>& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, arcomplex* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, - &ARbdNonSymMatrix, ARFLOAT>::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluCompStdEig:: -ARluCompStdEig(int nevp, ARbdNonSymMatrix, ARFLOAT>& A, - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, - &ARbdNonSymMatrix, ARFLOAT>::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluCompStdEig& ARluCompStdEig:: -operator=(const ARluCompStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBSCOMP_H diff --git a/src/external/arpack++/include/arbsmat.h b/src/external/arpack++/include/arbsmat.h deleted file mode 100644 index e38c2ccf..00000000 --- a/src/external/arpack++/include/arbsmat.h +++ /dev/null @@ -1,378 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBSMat.h. - Arpack++ class ARbdSymMatrix definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "arbspen.h" - -#ifndef ARBSMAT_H -#define ARBSMAT_H - -#include -#include "arch.h" -#include "armat.h" -#include "arerror.h" -#include "blas1c.h" -#include "lapackc.h" - -template class ARbdSymPencil; - -template -class ARbdSymMatrix: public ARMatrix { - - friend class ARbdSymPencil; - - protected: - - bool factored; - char uplo; - int nsdiag; - int lda; - int info; - int* ipiv; - ARTYPE* A; - ARTYPE* Ainv; - - void ClearMem(); - - virtual void Copy(const ARbdSymMatrix& other); - - void ExpandA(); - - void SubtractAsI(ARTYPE sigma); - - void CreateStructure(); - - void ThrowError(); - - public: - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, int nsdiagp, ARTYPE* Ap, char uplop = 'L'); - - ARbdSymMatrix(): ARMatrix() { factored = false; } - // Short constructor that does nothing. - - ARbdSymMatrix(int np, int nsdiagp, ARTYPE* Ap, char uplop = 'L'); - // Long constructor. - - ARbdSymMatrix(const ARbdSymMatrix& other) { Copy(other); } - // Copy constructor. - - virtual ~ARbdSymMatrix() { ClearMem(); } - // Destructor. - - ARbdSymMatrix& operator=(const ARbdSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARbdSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARbdSymMatrix::ClearMem() -{ - - if (factored) { - delete[] Ainv; - delete[] ipiv; - Ainv = NULL; - ipiv = NULL; - } - -} // ClearMem. - - -template -inline void ARbdSymMatrix:: -Copy(const ARbdSymMatrix& other) -{ - - // Copying very fundamental variables and user-defined parameters. - - this->m = other.m; - this->n = other.n; - this->defined = other.defined; - factored = other.factored; - uplo = other.uplo; - nsdiag = other.nsdiag; - lda = other.lda; - info = other.info; - A = other.A; - - // Returning from here if "other" was not factored. - - if (!factored) return; - - // Copying vectors. - - Ainv = new ARTYPE[this->n*lda]; - ipiv = new int[this->n]; - - copy(this->n*lda, other.Ainv, 1, Ainv, 1); - for (int i=0; in; i++) ipiv[i] = other.ipiv[i]; - -} // Copy. - - -template -void ARbdSymMatrix::ExpandA() -{ - - int i; - - if (uplo == 'U') { - - // Copying the main diagonal of A to Ainv. - - copy(this->n, &A[nsdiag], nsdiag+1, &Ainv[2*nsdiag], lda); - - // Copying the superdiagonals of A to Ainv. - - for (i = 0; i < nsdiag; i++) { - copy(this->n, &A[i], nsdiag+1, &Ainv[nsdiag+i], lda); - copy(this->n-nsdiag+i, &A[i+(nsdiag-i)*(nsdiag+1)], nsdiag+1, - &Ainv[3*nsdiag-i], lda); - } - - } - else { - - // Copying the main diagonal of A to Ainv. - - copy(this->n, &A[0], nsdiag+1, &Ainv[2*nsdiag], lda); - - // Copying the subdiagonals of A to Ainv. - - for (i = 1; i <= nsdiag; i++) { - copy(this->n, &A[i], nsdiag+1, &Ainv[2*nsdiag+i], lda); - copy(this->n-i, &A[i], nsdiag+1, &Ainv[2*nsdiag-i+i*lda], lda); - } - - } - -} // ExpandA. - - -template -void ARbdSymMatrix::SubtractAsI(ARTYPE sigma) -{ - - // Copying A to Ainv. - - ExpandA(); - - // Subtracting sigma from diagonal elements. - - for (int i=(2*nsdiag); i<(lda*this->n); i+=lda) Ainv[i] -= sigma; - -} // SubtractAsI. - - -template -inline void ARbdSymMatrix::CreateStructure() -{ - - ClearMem(); - Ainv = new ARTYPE[lda*this->n]; - ipiv = new int[this->n]; - -} // CreateStructure. - - -template -inline void ARbdSymMatrix::ThrowError() -{ - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARbdSymMatrix::FactorA"); - } - else if (info) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARbdSymMatrix::FactorA"); - } - -} // ThrowError. - - -template -void ARbdSymMatrix::FactorA() -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARbdSymMatrix::FactorA"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Copying A to Ainv; - - ExpandA(); - - // Decomposing A. - - gbtrf(this->n, this->n, nsdiag, nsdiag, Ainv, lda, ipiv, info); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorA. - - -template -void ARbdSymMatrix::FactorAsI(ARTYPE sigma) -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARbdSymMatrix::FactorAsI"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Subtracting sigma*I from A. - - SubtractAsI(sigma); - - // Decomposing AsI. - - gbtrf(this->n, this->n, nsdiag, nsdiag, Ainv, lda, ipiv, info); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorAsI. - - -template -void ARbdSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE one = (ARTYPE)0 + 1.0; - ARTYPE zero = (ARTYPE)0; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARbdSymMatrix::MultMv"); - } - - // Determining w = M.v. - - sbmv(&uplo, this->n, nsdiag, one, A, nsdiag+1, v, 1, zero, w, 1); - -} // MultMv. - - -template -void ARbdSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARbdSymMatrix::MultInvv"); - } - - // Overwritting w with v. - - copy(this->n, v, 1, w, 1); - - // Solving A.w = v (or AsI.w = v). - - gbtrs("N", this->n, nsdiag, nsdiag, 1, Ainv, lda, ipiv, w, this->m, info); - - // Handling errors. - - ThrowError(); - -} // MultInvv. - - -template -inline void ARbdSymMatrix:: -DefineMatrix(int np, int nsdiagp, ARTYPE* Ap, char uplop) -{ - - // Defining member variables. - - this->m = np; - this->n = np; - nsdiag = nsdiagp; - lda = 3*nsdiag+1; - uplo = uplop; - A = Ap; - this->defined = true; - Ainv = NULL; - ipiv = NULL; - info = 0; - -} // DefineMatrix. - - -template -inline ARbdSymMatrix:: -ARbdSymMatrix(int np, int nsdiagp, - ARTYPE* Ap, char uplop) : ARMatrix(np) -{ - - factored = false; - DefineMatrix(np, nsdiagp, Ap, uplop); - -} // Long constructor. - - -template -ARbdSymMatrix& ARbdSymMatrix:: -operator=(const ARbdSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBSMAT_H diff --git a/src/external/arpack++/include/arbsnsym.h b/src/external/arpack++/include/arbsnsym.h deleted file mode 100644 index 808a4246..00000000 --- a/src/external/arpack++/include/arbsnsym.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBSNSym.h. - Arpack++ class ARluNonSymStdEig definition - (band matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARBSNSYM_H -#define ARBSNSYM_H - -#include -#include -#include "arch.h" -#include "arsnsym.h" -#include "arbnsmat.h" - - -template -class ARluNonSymStdEig: - public virtual ARNonSymStdEig > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // a.2) Constructors and destructor. - - ARluNonSymStdEig() { } - // Short constructor. - - ARluNonSymStdEig(int nevp, ARbdNonSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluNonSymStdEig(int nevp, ARbdNonSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluNonSymStdEig(const ARluNonSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymStdEig() { } - // Destructor. - - // b) Operators. - - ARluNonSymStdEig& operator=(const ARluNonSymStdEig& other); - // Assignment operator. - -}; // class ARluNonSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluNonSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymStdEig:: -ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI( this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluNonSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode( this->objOP, &ARbdNonSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluNonSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, - &ARbdNonSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluNonSymStdEig:: -ARluNonSymStdEig(int nevp, ARbdNonSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, - &ARbdNonSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluNonSymStdEig:: -ARluNonSymStdEig(int nevp, ARbdNonSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, - &ARbdNonSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluNonSymStdEig& ARluNonSymStdEig:: -operator=(const ARluNonSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBSNSYM_H diff --git a/src/external/arpack++/include/arbspen.h b/src/external/arpack++/include/arbspen.h deleted file mode 100644 index 0dbd73bb..00000000 --- a/src/external/arpack++/include/arbspen.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBSPen.h. - Arpack++ class ARbdSymPencil definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARBSPEN_H -#define ARBSPEN_H - -#include "arch.h" -#include "arerror.h" -#include "blas1c.h" -#include "lapackc.h" -#include "arbsmat.h" - - -template -class ARbdSymPencil -{ - - protected: - - ARbdSymMatrix* A; - ARbdSymMatrix* B; - ARbdSymMatrix AsB; - - int max(int a, int b) { return (a>b)?a:b; } - - int min(int a, int b) { return (aMultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - - void MultInvAsBv(ARTYPE* v, ARTYPE* w) { AsB.MultInvv(v,w); } - - void DefineMatrices(ARbdSymMatrix& Ap, ARbdSymMatrix& Bp); - - ARbdSymPencil() { AsB.factored = false; } - // Short constructor that does nothing. - - ARbdSymPencil(ARbdSymMatrix& Ap, ARbdSymMatrix& Bp); - // Long constructor. - - ARbdSymPencil(const ARbdSymPencil& other) { Copy(other); } - // Copy constructor. - - virtual ~ARbdSymPencil() { } - // Destructor. - - ARbdSymPencil& operator=(const ARbdSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARbdSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARbdSymPencil::Copy(const ARbdSymPencil& other) -{ - - A = other.A; - B = other.B; - AsB = other.AsB; - -} // Copy. - - -template -void ARbdSymPencil::SubtractAsB(ARTYPE sigma) -{ - - int i, n, minD, ndA, ndB, ndAsB, lda; - ARTYPE negsig; - - negsig = -sigma; - n = AsB.n; - ndA = A->nsdiag; - ndB = B->nsdiag; - ndAsB = AsB.nsdiag; - lda = AsB.lda; - - // Expanding A. - - if (A->uplo == 'U') { - - // Copying the main diagonal of A. - - copy(n, &A->A[ndA], ndA+1, &AsB.Ainv[2*ndAsB], lda); - - // Copying the superdiagonals of A. - - for (i = 0; i < ndA; i++) { - copy(n, &A->A[i], ndA+1, &AsB.Ainv[2*ndAsB-ndA+i], lda); - copy(n-ndA+i, &A->A[i+(ndA-i)*(ndA+1)], ndA+1, - &AsB.Ainv[2*ndAsB+ndA-i], lda); - } - - } - else { - - // Copying the main diagonal of A to Ainv. - - copy(n, &A->A[0], ndA+1, &AsB.Ainv[2*ndAsB], lda); - - // Copying the subdiagonals of A to Ainv. - - for (i = 1; i <= ndA; i++) { - copy(n, &A->A[i], ndA+1, &AsB.Ainv[2*ndAsB+i], lda); - copy(n-i, &A->A[i], ndA+1, &AsB.Ainv[2*ndAsB-i+i*lda], lda); - } - - } - - // Transferring part of B (*(-sigma)) if AsB.nsdiag > A->nsdiag. - - if (A->nsdiag < AsB.nsdiag) { - - if (B->uplo == 'U') { - - for (i = 0; i < ndAsB-ndA; i++) { - copy(n, &B->A[i], ndB+1, &AsB.Ainv[ndAsB+i], lda); - scal(n, negsig, &AsB.Ainv[ndAsB+i], lda); - copy(n-ndAsB+i, &AsB.Ainv[ndAsB+i+(ndAsB-i)*lda], lda, - &AsB.Ainv[lda-i-1], lda); - } - - } - else { - - for (i = ndA+1; i <= ndAsB; i++) { - copy(n, &B->A[i], ndB+1, &AsB.Ainv[2*ndAsB+i], lda); - scal(n, negsig, &AsB.Ainv[2*ndAsB+i], lda); - copy(n-i, &AsB.Ainv[2*ndAsB+i], lda, - &AsB.Ainv[2*ndAsB-i+i*lda], lda); - } - - } - - } - - // Subtracting sigma*B from A. - - minD = min(ndA, ndB); - - if (B->uplo == 'U') { - - // Subtracting the main diagonal of B. - - axpy(n, negsig, &B->A[ndB], ndB+1, &AsB.Ainv[2*ndAsB], lda); - - // Subtracting the superdiagonals. - - for (i = 0; i < minD; i++) { - axpy(n, negsig, &B->A[ndB-minD+i], ndB+1, - &AsB.Ainv[2*ndAsB-minD+i], lda); - copy(n-minD+i, &AsB.Ainv[2*ndAsB-minD+i+(minD-i)*lda], lda, - &AsB.Ainv[2*ndAsB+minD-i], lda); - } - - } - else { - - // Subtracting the main diagonal of B. - - axpy(n, negsig, &B->A[0], ndB+1, &AsB.Ainv[2*ndAsB], lda); - - // Subtracting the subdiagonals. - - for (i = 1; i <= minD; i++) { - axpy(n, negsig, &B->A[i], ndB+1, &AsB.Ainv[2*ndAsB+i], lda); - copy(n-i, &AsB.Ainv[2*ndAsB+i], lda, - &AsB.Ainv[2*ndAsB-i+i*lda], lda); - } - - } - -} // SubtractAsB (ARTYPE shift). - - -template -void ARbdSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARbdSymPencil::FactorAsB"); - } - - // Copying A to AsB if sigma = 0. - - if (sigma == (ARTYPE)0) { - - AsB = *A; - if (!AsB.IsFactored()) AsB.FactorA(); - return; - - } - - // Defining matrix AsB. - - if (!AsB.IsDefined()) { - AsB.DefineMatrix(A->ncols(), max(A->nsdiag, B->nsdiag), A->A); - } - - // Reserving memory for some vectors used in matrix decomposition. - - AsB.CreateStructure(); - - // Subtracting sigma*B from A and storing the result on AsB. - - SubtractAsB(sigma); - - // Decomposing AsB. - - gbtrf(AsB.n, AsB.n, AsB.nsdiag, AsB.nsdiag, - AsB.Ainv, AsB.lda, AsB.ipiv, AsB.info); - - // Handling errors. - - AsB.ThrowError(); - - AsB.factored = true; - -} // FactorAsB (ARTYPE shift). - - -template -void ARbdSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - copy(A->ncols(), w, 1, v, 1); - B->MultInvv(w, w); - -} // MultInvBAv. - - -template -inline void ARbdSymPencil:: -DefineMatrices(ARbdSymMatrix& Ap, ARbdSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - -} // DefineMatrices. - - -template -inline ARbdSymPencil:: -ARbdSymPencil(ARbdSymMatrix& Ap, ARbdSymMatrix& Bp) -{ - - AsB.factored = false; - DefineMatrices(Ap, Bp); - -} // Long constructor. - - -template -ARbdSymPencil& ARbdSymPencil:: -operator=(const ARbdSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBSPEN_H diff --git a/src/external/arpack++/include/arbssym.h b/src/external/arpack++/include/arbssym.h deleted file mode 100644 index 94d70f68..00000000 --- a/src/external/arpack++/include/arbssym.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARBSSym.h. - Arpack++ class ARluSymStdEig definition - (band matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARBSSYM_H -#define ARBSSYM_H - -#include -#include -#include "arch.h" -#include "arssym.h" -#include "arbsmat.h" - - -template -class ARluSymStdEig: - public virtual ARSymStdEig > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // a.2) Constructors and destructor. - - ARluSymStdEig() { } - // Short constructor. - - ARluSymStdEig(int nevp, ARbdSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymStdEig(int nevp, ARbdSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluSymStdEig(const ARluSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymStdEig() { } - // Destructor. - - // b) Operators. - - ARluSymStdEig& operator=(const ARluSymStdEig& other); - // Assignment operator. - -}; // class ARluSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymStdEig:: -ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI(this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(this->objOP, &ARbdSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, &ARbdSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARbdSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, &ARbdSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARbdSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, &ARbdSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluSymStdEig& ARluSymStdEig:: -operator=(const ARluSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARBSSYM_H diff --git a/src/external/arpack++/include/arcgsym.h b/src/external/arpack++/include/arcgsym.h deleted file mode 100644 index 45c14f83..00000000 --- a/src/external/arpack++/include/arcgsym.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARCGSym.h. - Arpack++ class ARluSymGenEig definition - (CHOLMOD version). - - Author of this class: - Martin Reuter - Date 11/05/2012 - - Arpack++ Author: - Francisco Gomes - - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Kristi Maschhoff - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARCGSYM_H -#define ARCGSYM_H - -#include -#include -#include "arch.h" -#include "arcsmat.h" -#include "arcspen.h" -#include "argsym.h" - - -template -class ARluSymGenEig: - public virtual ARSymGenEig, - ARchSymPencil > { - - private: - - // a) Data structure used to store matrices. - - ARchSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmap); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetBucklingMode(ARFLOAT sigmap); - - virtual void SetCayleyMode(ARFLOAT sigmap); - - // c.2) Constructors and destructor. - - ARluSymGenEig() { } - // Short constructor. - - ARluSymGenEig(int nevp, ARchSymMatrix& A, - ARchSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymGenEig(char InvertModep, int nevp, ARchSymMatrix& A, - ARchSymMatrix& B, ARFLOAT sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert, buckling and Cayley modes). - - ARluSymGenEig(const ARluSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluSymGenEig& operator=(const ARluSymGenEig& other); - // Assignment operator. - -}; // class ARluSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymGenEig:: -Copy(const ARluSymGenEig& other) -{ - - ARSymGenEig, - ARchSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - -} // Copy. - - -template -inline void ARluSymGenEig::ChangeShift(ARFLOAT sigmap) -{ - - this->objOP->FactorAsB(sigmap); - ARrcSymGenEig::ChangeShift(sigmap); - -} // ChangeShift. - - -template -inline void ARluSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARchSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluSymGenEig:: -SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARchSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, &ARchSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARchSymPencil::MultBv); - -} // SetShiftInvertMode. - - -template -inline void ARluSymGenEig:: -SetBucklingMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARchSymPencil >:: - SetBucklingMode(sigmap, &Pencil, &ARchSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARchSymPencil::MultAv); - -} // SetBucklingMode. - - -template -inline void ARluSymGenEig:: -SetCayleyMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARchSymPencil >:: - SetCayleyMode(sigmap, &Pencil, &ARchSymPencil::MultInvAsBv, - &Pencil, &ARchSymPencil::MultAv); - this->ChangeMultBx(&Pencil, &ARchSymPencil::MultBv); - -} // SetCayleyMode. - - -template -inline ARluSymGenEig:: -ARluSymGenEig(int nevp, ARchSymMatrix& A, - ARchSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->InvertMode = 'S'; - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARchSymPencil::MultInvBAv, &Pencil, - &ARchSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymGenEig:: -ARluSymGenEig(char InvertModep, int nevp, ARchSymMatrix& A, - ARchSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARchSymPencil::MultInvAsBv, &Pencil, - &ARchSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - this->InvertMode = this->CheckInvertMode(InvertModep); - switch (this->InvertMode) { - case 'B': // Buckling mode. - this->ChangeMultBx(&Pencil, &ARchSymPencil::MultAv); - case 'S': // Shift and invert mode. - ChangeShift(sigmap); - break; - case 'C': // Cayley mode. - SetCayleyMode(sigmap); - } - -} // Long constructor (shift and invert, buckling and Cayley modes). - - -template -ARluSymGenEig& ARluSymGenEig:: -operator=(const ARluSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUGSYM_H diff --git a/src/external/arpack++/include/arch.h b/src/external/arpack++/include/arch.h deleted file mode 100644 index 13515730..00000000 --- a/src/external/arpack++/include/arch.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE arch.h - Modified version of arch.h (from LAPACK++ 1.1). - Machine dependent functions and variable types. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#ifndef ARCH_H -#define ARCH_H - -// ARPACK++ arcomplex type definition. -// If you are not using g++ (or CC) and also are not intending -// use complex variables, comment out the following line. - -#include "arcomp.h" - -// STL vector class. -// If the Standard Template Library is not available at your system -// and you do not want to install it, comment out the following line. - -#include - -// If your STL vector class defines a variable other than -// __SGI_STL_VECTOR_H, please change this variable name -// in the ifdef command below. - -#ifdef __SGI_STL_VECTOR_H - #define STL_VECTOR_H -#endif - -// UMFPACK parameters. -// These parameters are used by UMFPACK library functions. Normally -// they are not modified by the user. To use the default value, set -// the parameter to zero. For a complete description of all UMFPACK -// parameters, see the library documentation. - -#define UICNTL7 0 // icntl(7). Block size for the blas (machine-dependent). -#define UICNTL5 0 // icntl(5). Number of columns to examine during pivot search. -#define UCNTL2 0 // cntl(2). Amalgamation parameter. -#define UKEEP7 0 // keep(7). Absolute number of elements a column must have - // to be considered "dense". -#define UKEEP8 0 // keep(8). Relative number of elements a column must have - // to be considered "dense". Dense columns have more - // than max{0,UMFABDEN,UMFREDEN*sqrt(n)} elements. - -// Line length used when reading a dense matrix from a file. - -#define LINELEN 256 - -// Linkage names between C, C++, and Fortran (platform dependent) - -#if defined(RIOS) && !defined(CLAPACK) -#define F77NAME(x) x -#else -// #include -// #define F77NAME(x) name2(x,_) -#define F77NAME(x) x ## _ -#endif - -#if defined(SGI) && !defined(SGI_DEC) -#define SGI_DEC - -extern "C" { - void mkidxname() {} - void mkdatname() {} -} -#endif - - -// Type conversion. - -typedef int ARint; -typedef int ARlogical; - -#ifdef __SUNPRO_CC - - typedef int bool; - int true = 1; - int false = 0; - -#endif - - -#endif // ARCH_H diff --git a/src/external/arpack++/include/arcomp.h b/src/external/arpack++/include/arcomp.h deleted file mode 100644 index 6a391d02..00000000 --- a/src/external/arpack++/include/arcomp.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE arcomp.h - arcomplex complex type definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARCOMP_H -#define ARCOMP_H - -#include - -#ifdef __GNUG__ - -#define arcomplex std::complex - -#endif - -#if defined(__SUNPRO_CC) || defined(__sgi) - - template - class arcomplex: public complex - { - public: - - arcomplex(ARFLOAT x, ARFLOAT y): complex(x,y) { } - arcomplex(): complex() { } - arcomplex(complex x): complex(x) { } - - }; - -#endif - -#endif // ARCOMP_H - - - diff --git a/src/external/arpack++/include/arcsmat.h b/src/external/arpack++/include/arcsmat.h deleted file mode 100644 index a97e9fad..00000000 --- a/src/external/arpack++/include/arcsmat.h +++ /dev/null @@ -1,473 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARCSMat.h. - Arpack++ class ARchSymMatrix definition. - (CHOLMOD wrapper) - - Author of this class: - Martin Reuter - Date 11/05/2012 - - Arpack++ Author: - Francisco Gomes - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "arcspen.h" - -#ifndef ARCSMAT_H -#define ARCSMAT_H - -#include -#include -#include "arch.h" -#include "armat.h" -#include "arhbmat.h" -#include "arerror.h" -#include "cholmodc.h" -//#include "blas1c.h" -//#include "superluc.h" -//#include "arlspdef.h" -//#include "arlutil.h" -#include - -template class ARchSymPencil; - -template -class ARchSymMatrix: public ARMatrix { - - friend class ARchSymPencil; - - protected: - - bool factored; - char uplo; - int nnz; - int* irow; - int* pcol; - double threshold; - ARTYPE* a; - ARhbMatrix mat; - cholmod_common c ; - cholmod_sparse *A ; - cholmod_factor *L ; - - bool DataOK(); - - virtual void Copy(const ARchSymMatrix& other); - - void ClearMem(); - - public: - - int nzeros() { return nnz; } - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, char uplop = 'L', double thresholdp = 0.1, - bool check = true); - - ARchSymMatrix(): ARMatrix() { factored = false; cholmod_start (&c) ;} - // Short constructor that does nothing. - - ARchSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, char uplop = 'L', double thresholdp = 0.1, - bool check = true); - // Long constructor. - - ARchSymMatrix(const std::string& name, double thresholdp = 0.1, - bool check = true); - // Long constructor (Harwell-Boeing file). - - ARchSymMatrix(const ARchSymMatrix& other) { cholmod_start (&c) ; Copy(other); } - // Copy constructor. - - virtual ~ARchSymMatrix() { ClearMem(); cholmod_finish (&c) ;} - // Destructor. - - ARchSymMatrix& operator=(const ARchSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARchSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -bool ARchSymMatrix::DataOK() -{ - - int i, j, k; - - // Checking if pcol is in ascending order. - - i = 0; - while ((i!=this->n)&&(pcol[i]<=pcol[i+1])) i++; - if (i!=this->n) return false; - - // Checking if irow components are in order and within bounds. - - for (i=0; i!=this->n; i++) { - j = pcol[i]; - k = pcol[i+1]-1; - if (j<=k) { - if (uplo == 'U') { - if ((irow[j]<0)||(irow[k]>i)) return false; - } - else { // uplo == 'L'. - if ((irow[j]=this->n)) return false; - } - while ((j!=k)&&(irow[j] -void ARchSymMatrix::ClearMem() -{ - - if (factored) { - cholmod_free_factor (&L, &c) ; - } - if (this->defined) { - //cholmod_free_sparse (&A, &c); - //delete[] permc; - //delete[] permr; - //permc = NULL; - //permr = NULL; - - free(A); // don't delete data in A as it came from external - A = NULL; - } - -} // ClearMem. - - - -template -inline void ARchSymMatrix::Copy(const ARchSymMatrix& other) -{ - - // Copying very fundamental variables. - ClearMem(); - - this->defined = other.defined; - // Returning from here if "other" was not initialized. - if (!this->defined) return; - - this->n = other.n; - factored = other.factored; - uplo = other.uplo; - nnz = other.nnz; - irow = other.irow; - pcol = other.pcol; - threshold = other.threshold; - a = other.a; - //c = other.c; - - A = cholmod_copy_sparse(other.A,&c); - - if (L) cholmod_free_factor(&L,&c); - if (factored) - L = cholmod_copy_factor(other.L,&c); - -} // Copy. - - - -template -void ARchSymMatrix::FactorA() -{ - int info; - - //std::cout << "ARchSymMatrix::FactorA" << std::endl; - - // Quitting the function if A was not defined. - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARchSymMatrix::FactorA"); - } - - // Deleting previous versions of L. - if (factored) { - cholmod_free_factor (&L, &c) ; - } - - L = cholmod_analyze (A, &c) ; - info = cholmod_factorize (A, L, &c) ; - - - factored = (info != 0); - - if (c.status != CHOLMOD_OK) - { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARchSymMatrix::FactorA"); - - factored = false; - } - -// -// // Handling errors. -// -// if (info < 0) { // Illegal argument. -// throw ArpackError(ArpackError::PARAMETER_ERROR, -// "ARchSymMatrix::FactorA"); -// } -// else if (info > this->n) { // Memory is not sufficient. -// throw ArpackError(ArpackError::MEMORY_OVERFLOW, -// "ARchSymMatrix::FactorA"); -// } -// else if (info > 0) { // Matrix is singular. -// throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, -// "ARchSymMatrix::FactorA"); -// } - -} // FactorA. - - -template -void ARchSymMatrix::FactorAsI(ARTYPE sigma) -{ - - //std::cout <<"ARchSymMatrix::FactorAsI " << std::endl; - - // Quitting the function if A was not defined. - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARchSymMatrix::FactorAsI"); - } - - - // Deleting previous versions of L. - if (factored) { - cholmod_free_factor (&L, &c) ; - } - -// FILE *fp ; -// fp = fopen ("A.mat", "w" ) ; -// cholmod_write_sparse(fp,A,NULL,NULL,&c); - - // Factorizing A-sigma*I - double sigma2[2]; - sigma2[0] = -sigma; - sigma2[1] = 0.0; - L = cholmod_analyze (A, &c) ; - int info = cholmod_factorize_p (A,sigma2,NULL,0,L,&c) ; - - factored = (info != 0); - - if (c.status != CHOLMOD_OK) - { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARchSymMatrix::FactorAsI"); - - factored = false; - } - - -} // FactorAsI. - - -template -void ARchSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ - //std::cout << "ARchSymMatrix::MultMv " << std::endl; - - int i, j, k; - ARTYPE t; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARchSymMatrix::MultMv"); - } - - // Determining w = M.v. - - for (i=0; i!=this->m; i++) w[i]=(ARTYPE)0; - - if (uplo == 'U') { - - for (i=0; i!=this->n; i++) { - t = v[i]; - k = pcol[i+1]; - if ((k!=pcol[i])&&(irow[k-1]==i)) { - w[i] += t*a[k-1]; - k--; - } - for (j=pcol[i]; jn; i++) { - t = v[i]; - k = pcol[i]; - if ((k!=pcol[i+1])&&(irow[k]==i)) { - w[i] += t*a[k]; - k++; - } - for (j=k; j -void ARchSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ - //std::cout << "ARchSymMatrix::MultInvv " << std::endl; - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARchSymMatrix::MultInvv"); - } - - // Solving A.w = v (or AsI.w = v). - - //std::cout<< " b = [ " << v[0]; - //for(int i=1;in;i++) - // std::cout << " , " << v[i]; - //std::cout<< " ]" <n,1,v,&c); - - cholmod_dense *x = cholmod_solve (CHOLMOD_A, L, b, &c) ; - - Get_Cholmod_Dense_Data(x, this->n, w); - - //std::cout<< " x = [ " << w[0]; - //for(int i=1;in;i++) - // std::cout << " , " << w[i]; - //std::cout<< " ]" < -inline void ARchSymMatrix:: -DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, int* pcolp, - char uplop, double thresholdp, bool check) -{ - - this->m = np; - this->n = np; - nnz = nnzp; - a = ap; - irow = irowp; - pcol = pcolp; - pcol[this->n] = nnz; - uplo = uplop; - threshold = thresholdp; - - // Checking data. - if ((check)&&(!DataOK())) { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARchSymMatrix::DefineMatrix"); - } - - // Creating SuperMatrix A. - A = Create_Cholmod_Sparse_Matrix(this->n, this->n, nnz, a, irow, pcol, uplo, &c); - - this->defined = true; - -} // DefineMatrix. - - -template -inline ARchSymMatrix:: -ARchSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, char uplop, double thresholdp, - bool check) : ARMatrix(np) -{ - cholmod_start (&c) ; - - factored = false; - DefineMatrix(np, nnzp, ap, irowp, pcolp, uplop, thresholdp, check); - -} // Long constructor. - - -template -ARchSymMatrix:: -ARchSymMatrix(const std::string& file, double thresholdp, bool check) -{ - cholmod_start (&c) ; - - factored = false; - - try { - mat.Define(file); - } - catch (ArpackError) { // Returning from here if an error has occurred. - throw ArpackError(ArpackError::CANNOT_READ_FILE, "ARchSymMatrix"); - } - - if ((mat.NCols() == mat.NRows()) && (mat.IsSymmetric())) { - - DefineMatrix(mat.NCols(), mat.NonZeros(), (ARTYPE*)mat.Entries(), - mat.RowInd(), mat.ColPtr(), 'L', thresholdp, check); - } - else { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARchSymMatrix::ARchSymMatrix"); - } -} // Long constructor (Harwell-Boeing file). - - -template -ARchSymMatrix& ARchSymMatrix:: -operator=(const ARchSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARCSMAT_H diff --git a/src/external/arpack++/include/arcspen.h b/src/external/arpack++/include/arcspen.h deleted file mode 100644 index 424535fa..00000000 --- a/src/external/arpack++/include/arcspen.h +++ /dev/null @@ -1,434 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARCSPen.h. - Arpack++ class ARchSymMPencil definition. - (CHOLMOD wrapper) - - Author of this class: - Martin Reuter - Date 11/05/2012 - - Arpack++ Author: - Francisco Gomes - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARCSPEN_H -#define ARCSPEN_H - -//#include "arch.h" -//#include "arerror.h" -#include "blas1c.h" -//#include "lapackc.h" -#include "arcsmat.h" - - -template -class ARchSymPencil -{ - - protected: - - ARchSymMatrix* A; - ARchSymMatrix* B; - cholmod_factor *LAsB ; - bool factoredAsB; - cholmod_common c ; - - virtual void Copy(const ARchSymPencil& other); - -// void SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], -// int yind[], int ny, ARTYPE z[], int zind[], int& nz); - -// void ExpandAsB(); - -// void SubtractAsB(ARTYPE sigma); - - public: - - bool IsFactored() { return factoredAsB; } - - void FactorAsB(ARTYPE sigma); - - void MultAv(ARTYPE* v, ARTYPE* w) { A->MultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - - void MultInvAsBv(ARTYPE* v, ARTYPE* w); - - void DefineMatrices(ARchSymMatrix& Ap, ARchSymMatrix& Bp); - - ARchSymPencil() { factoredAsB = false; A=NULL; B=NULL; LAsB=NULL; cholmod_start (&c) ; } - // Short constructor that does nothing. - - ARchSymPencil(ARchSymMatrix& Ap, ARchSymMatrix& Bp); - // Long constructor. - - ARchSymPencil(const ARchSymPencil& other) { cholmod_start (&c) ; Copy(other); } - // Copy constructor. - - virtual ~ARchSymPencil() { if (LAsB) cholmod_free_factor(&LAsB,&c); cholmod_finish (&c) ;} - // Destructor. - - ARchSymPencil& operator=(const ARchSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARchSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARchSymPencil::Copy(const ARchSymPencil& other) -{ - if (LAsB) cholmod_free_factor(&LAsB,&c); - A = other.A; - B = other.B; - factoredAsB = other.factoredAsB; - if (factoredAsB) - LAsB = cholmod_copy_factor(other.LAsB,&c); - -} // Copy. - -/* -template -void ARchSymPencil:: -SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], - int yind[], int ny, ARTYPE z[], int zind[], int& nz) -// A strongly sequential (and inefficient) sparse saxpy algorithm. -{ - - int ix, iy; - - nz = 0; - if ((nx == 0) || (a == (ARTYPE)0)) { - copy(ny,y,1,z,1); - for (iy=0; iy!=ny; iy++) zind[iy] = yind[iy]; - nz = ny; - return; - } - if (ny == 0) { - copy(nx,x,1,z,1); - scal(nx,a,z,1); - for (ix=0; ix!=nx; ix++) zind[ix] = xind[ix]; - nz = nx; - return; - } - ix = 0; - iy = 0; - while (true) { - if (xind[ix] == yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]+y[iy++]; - if ((ix == nx)||(iy == ny)) break; - } - else if (xind[ix] < yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]; - if (ix == nx) break; - } - else { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - if (iy == ny) break; - } - } - while (iy < ny) { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - } - while (ix < nx) { - zind[nz] = xind[ix]; - z[nz++] = x[ix++]; - } - -} // SparseSaxpy. - - -template -void ARchSymPencil::ExpandAsB() -{ - - int i, j, k, n; - int *pcol, *irow, *index, *pos; - ARTYPE *value; - - // Initializing variables. - - n = AsB.n; - index = AsB.index; - value = AsB.value; - irow = &index[n+1]; - pcol = new int[AsB.n+1]; - pos = new int[AsB.n+1]; - for (i=0; i<=n; i++) pcol[i] = index[i]; - for (i=0; i<=n; i++) pos[i] = 0; - - // Counting the elements in each column of AsB. - - if (AsB.uplo == 'U') { - - for (i=0; i!=n; i++) { - k = pcol[i+1]; - if ((k!=pcol[i])&&(irow[k-1]==i)) k--; - for (j=pcol[i]; j0; i--) index[i] += pos[i-1]; - - // Expanding A. - - if (AsB.uplo == 'U') { - - for (i=n-1; i>=0; i--) { - pos[i] = index[i]+pcol[i+1]-pcol[i]; - k = pos[i]-1; - for (j=pcol[i+1]-1; j>=pcol[i]; j--) { - value[k] = value[j]; - irow[k--] = irow[j]; - } - } - for (i=1; iindex[i])&&(irow[k-1]==i)) k--; - for (j=index[i]; j=0; i--) { - k = index[i+1]-1; - for (j=pcol[i+1]-1; j>=pcol[i]; j--) { - value[k] = value[j]; - irow[k--] = irow[j]; - } - pos[i] = index[i]; - } - for (i=0; i<(n-1); i++) { - k = index[i+1]-pcol[i+1]+pcol[i]; - if ((k -void ARchSymPencil::SubtractAsB(ARTYPE sigma) -{ - - int i, acol, bcol, asbcol, scol; - - // Quitting function if A->uplo is not equal to B->uplo. - - if ((A->uplo != B->uplo)&&(sigma != (ARTYPE)0)) { - throw ArpackError(ArpackError::DIFFERENT_TRIANGLES, - "ARchSymPencil::SubtractAsB"); - } - AsB.uplo = A->uplo; - - // Subtracting sigma*B from A. - - AsB.index[0] = 0; - asbcol = 0; - - for (i=0; i!=AsB.n; i++) { - bcol = B->pcol[i]; - acol = A->pcol[i]; - SparseSaxpy(-sigma, &B->a[bcol], &B->irow[bcol], B->pcol[i+1]-bcol, - &A->a[acol], &A->irow[acol], A->pcol[i+1]-acol, - &AsB.value[asbcol], &AsB.index[asbcol+AsB.n+1], scol); - asbcol += scol; - AsB.index[i+1] = asbcol; - } - - // Expanding AsB. - - ExpandAsB(); - - // Adding one to all elements of vector index - // because the decomposition function was written in FORTRAN. - - for (i=0; i<=AsB.n+AsB.nnz; i++) AsB.index[i]++; - -} // SubtractAsB. - -*/ - -template -void ARchSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARchSymPencil::FactorAsB"); - } - - - if (LAsB) cholmod_free_factor(&LAsB,&c); - - cholmod_sparse* AsB; - if (sigma != 0.0) - { - std::cout << " Subtracting sigma B (sigma="<A,B->A,alpha,beta,1,0,&c); - } - else - AsB = A->A; - -//FILE *fp; -//fp=fopen("AsB.asc", "w"); -//cholmod_write_sparse(fp,AsB,NULL,NULL,&c); -//FILE *fpa; -//fpa=fopen("As.asc", "w"); -//cholmod_write_sparse(fpa,B->A,NULL,NULL,&c); -//FILE *fpb; -//fpb=fopen("Bs.asc", "w"); -//cholmod_write_sparse(fpb,A->A,NULL,NULL,&c); - - LAsB = cholmod_analyze (AsB, &c) ; - int info = cholmod_factorize (AsB, LAsB, &c) ; - - factoredAsB = (info != 0); - if (c.status != CHOLMOD_OK) - { - //std::cout << " sigma : " << sigma << std::endl; - - Write_Cholmod_Sparse_Matrix("AsB-error.asc",AsB,&c); - - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARchSymPencil::FactorAsB"); - - factoredAsB = false; - } - - if (sigma != 0.0) - cholmod_free_sparse(&AsB,&c); - - -} // FactorAsB (ARTYPE shift). - - -template -void ARchSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - ::copy(A->ncols(), w, 1, v, 1); - B->MultInvv(w, w); - -} // MultInvBAv. - -template -void ARchSymPencil::MultInvAsBv(ARTYPE* v, ARTYPE* w) -{ - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARchSymPencil::MultInvAsBv"); - } - - // Solving A.w = v (or AsI.w = v). - - //create b from v (data is not copied!!) - cholmod_dense * b = Create_Cholmod_Dense_Matrix(A->n,1,v,&c); - - cholmod_dense *x = cholmod_solve (CHOLMOD_A, LAsB, b, &c) ; - - Get_Cholmod_Dense_Data(x, A->n, w); - - free(b); - cholmod_free_dense(&x,&c); - - -} // MultInvAsBv - -template -inline void ARchSymPencil:: -DefineMatrices(ARchSymMatrix& Ap, ARchSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - - if (A->n != B->n) { - throw ArpackError(ArpackError::INCOMPATIBLE_SIZES, - "ARchSymMatrix::DefineMatrices"); - } - -} // DefineMatrices. - - -template -inline ARchSymPencil:: -ARchSymPencil(ARchSymMatrix& Ap, ARchSymMatrix& Bp) -{ - cholmod_start (&c) ; - LAsB=NULL; - DefineMatrices(Ap, Bp); - -} // Long constructor. - - -template -ARchSymPencil& ARchSymPencil:: -operator=(const ARchSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUSPEN_H diff --git a/src/external/arpack++/include/arcssym.h b/src/external/arpack++/include/arcssym.h deleted file mode 100644 index 26094e93..00000000 --- a/src/external/arpack++/include/arcssym.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUSSym.h. - Arpack++ class ARluSymStdEig definition - (CHOLMOD version). - - Author of this class: - Martin Reuter - Date 11/05/2012 - - Arpack++ Author: - Francisco Gomes - - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARCSSYM_H -#define ARCSSYM_H - -#include -#include -#include "arch.h" -#include "arssym.h" -#include "arcsmat.h" - - -template -class ARluSymStdEig: - public virtual ARSymStdEig > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // a.2) Constructors and destructor. - - ARluSymStdEig() { } - // Short constructor. - - ARluSymStdEig(int nevp, ARchSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymStdEig(int nevp, ARchSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluSymStdEig(const ARluSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymStdEig() { } - // Destructor. - - // b) Operators. - - ARluSymStdEig& operator=(const ARluSymStdEig& other); - // Assignment operator. - -}; // class ARluSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymStdEig::ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI(this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(this->objOP, &ARchSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, &ARchSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARchSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, &ARchSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARchSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, &ARchSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - this->ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluSymStdEig& ARluSymStdEig:: -operator=(const ARluSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUSSYM_H diff --git a/src/external/arpack++/include/ardfmat.h b/src/external/arpack++/include/ardfmat.h deleted file mode 100644 index cacf6b4c..00000000 --- a/src/external/arpack++/include/ardfmat.h +++ /dev/null @@ -1,453 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDFMat.h - Matrix template that generates a dense matrix from a file. - - ARPACK authors: - Richard Lehoucq - Kristyn Maschhoff - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#ifndef ARDFMAT_H -#define ARDFMAT_H - -#include -#include -#include -#include -#include -#include -#include "arch.h" -#include "arerror.h" - - -template -class ARdfMatrix { - - private: - - // const int linelength = 256; - - std::string datafile; // Filename. - std::ifstream file; // File handler. - int m; // Number of rows. - int n; // Number of columns. - int blksize; // Size of each matrix block that is read at once. - int block; // Index of the matrix block that is to be read. - int nblocks; // Number of blocks the matrix contain. - int first; // First row/column stored in val. - int strows; // Number of rows actually stored in val. - int stcols; // Number of columns actually stored in val. - int headsize; // Number of lines in the heading part of the file - // (including the line that contains the matrix size). - bool roword; // A variable that indicates if the data will be read - // using a row-major or a column-major ordering. - ARTYPE* val; // Numerical values of matrix entries. - - void ConvertDouble(char* num); - - void SetComplexPointers(char* num, char* &realp, char* &imagp); - - bool ReadEntry(std::ifstream& file, double& val); - - bool ReadEntry(std::ifstream& file, float& val); - - bool ReadEntry(std::ifstream& file, arcomplex& val); - - bool ReadEntry(std::ifstream& file, arcomplex& val); - - public: - - bool IsDefined() const { return (m!=0); } - - bool IsOutOfCore() const { - return ((m!=0) && ((roword && (blksize -inline void ARdfMatrix::ConvertDouble(char* num) -{ - - char* pd; - - pd = strchr((char*)num,'D'); - if (pd) *pd = 'E'; - pd = strchr((char*)num,'d'); - if (pd) *pd = 'E'; - -} // ConvertDouble. - - -template -inline void ARdfMatrix:: -SetComplexPointers(char* num, char* &realp, char* &imagp) -{ - - realp = num; - while (*realp == ' ') realp++; - imagp = realp; - while (*imagp != ' ') imagp++; - -} // SetComplexPointers. - - -template -inline bool ARdfMatrix::ReadEntry(std::ifstream& file, double& val) -{ - - char num[LINELEN]; - char c; - - if (file.get((char*)num,LINELEN,'\n')) { - file.get(c); - ConvertDouble((char*)num); - val = atof((char*)num); - return true; - } - else { - return false; - } - -} // ReadEntry (double). - - -template -inline bool ARdfMatrix::ReadEntry(std::ifstream& file, float& val) -{ - - double dval; - bool ret; - - ret = ReadEntry(file, dval); - val = (float)dval; - return ret; - -} // ReadEntry (float). - - -template -inline bool ARdfMatrix:: -ReadEntry(std::ifstream& file, arcomplex& val) -{ - - char num[LINELEN]; - char c; - char *realp, *imagp; - - if (file.get((char*)num,LINELEN,'\n')) { - file.get(c); - SetComplexPointers((char*)num, realp, imagp); - ConvertDouble((char*)realp); - ConvertDouble((char*)imagp); - val = arcomplex(atof((char*)realp), atof((char*)imagp)); - return true; - } - else { - return false; - } - -} // ReadEntry (arcomplex). - - -template -inline bool ARdfMatrix:: -ReadEntry(std::ifstream& file, arcomplex& val) -{ - - char num[LINELEN]; - char c; - char *realp, *imagp; - - if (file.get((char*)num,LINELEN,'\n')) { - file.get(c); - SetComplexPointers((char*)num, realp, imagp); - ConvertDouble((char*)realp); - ConvertDouble((char*)imagp); - val = arcomplex(atof((char*)realp), atof((char*)imagp)); - return true; - } - else { - return false; - } - -} // ReadEntry (arcomplex). - - -template -void ARdfMatrix::Rewind() -{ - - char data[LINELEN]; - char c; - - file.seekg(0); - block = 0; - first = 0; - strows = 0; - stcols = 0; - - // Skipping the header. - - for (int i=0; i -void ARdfMatrix::ReadBlock() -{ - - int i, j, last; - ARTYPE value; - - // Repositioning the file pointer if block == 0. - - if (block == 0) Rewind(); - - // Reading a block. - - first = (block++)*blksize; // First row/column to be read. - last = first+blksize; // First row/column of the next block. - - if (roword) { - - // Adjusting last if we are going to read the last block. - - if (last > m) { - last = m; - block = 0; - } - last -= first; - strows = last; - stcols = n; - - // Reading matrix data. - - for (i=0; i n) { - last = n; - block = 0; - } - last -= first; - strows = m; - stcols = last; - - // Reading matrix data. - - j = 0; - while ((j < m*last) && (ReadEntry(file, value))) { - val[j++] = value; - } - - // Exiting if the file is corrupted. - - if (j < m*last) { - throw ArpackError(ArpackError::UNEXPECTED_EOF, "ARdfMatrix"); - } - - } - -} // ReadBlock. - - -template -void ARdfMatrix::Define(const std::string& filename, int blksizep) -{ - - // Declaring variables. - - char c; - char data[LINELEN]; - - // Opening the file. - - datafile = filename; - file.open(datafile.c_str()); - - if (!file) { - throw ArpackError(ArpackError::CANNOT_OPEN_FILE, "ARdfMatrix"); - } - - // Setting initial values. - - blksize = blksizep; - block = 0; - headsize = 0; - first = 0; - strows = 0; - stcols = 0; - - // Reading the file heading. - - do { - file.get((char*)data,LINELEN,'\n'); - file.get(c); - headsize++; - } - while (data[0] == '%'); - - // Reading m and n or returning if a problem was detected. - - if (sscanf(data, "%d %d", &m, &n) != 2) { - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARdfMatrix"); - } - if ((m<1) || (n<1)) { - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARdfMatrix"); - } - - // Defining roword. - - roword = ((blksize != 0) && (m > n)); - - // (Re)Dimensioning val. - - if (val != NULL) delete[] val; - - if (blksize == 0) { - - // Redefining blksize and reading the entire matrix. - - blksize = n; - nblocks = 1; - val = new ARTYPE[m*blksize]; - ReadBlock(); - - } - else if (roword) { - - // m >> n, so we will read only blksize rows (but not now). - - if (blksize > m) blksize = m; - nblocks = (m+blksize-1)/blksize; - val = new ARTYPE[blksize*n]; - if (blksize == m) ReadBlock(); - - } - else { - - // n >> m, so we will read only blksize columns (but not now). - - if (blksize > n) blksize = n; - nblocks = (n+blksize-1)/blksize; - val = new ARTYPE[m*blksize]; - if (blksize == n) ReadBlock(); - - } - -} // Define. - - -template -ARdfMatrix::ARdfMatrix() -{ - - m = 0; - n = 0; - block = 0; - blksize = 0; - headsize = 0; - first = 0; - strows = 0; - stcols = 0; - roword = false; - val = NULL; - -} // Short constructor. - - -template -ARdfMatrix::ARdfMatrix(const std::string& filename, int blksizep) -{ - - val = NULL; - Define(filename, blksizep); - -} // Long constructor. - - -template -ARdfMatrix::~ARdfMatrix() -{ - - if (val != NULL) delete[] val; - -} // Destructor. - - -#endif // ARDFMAT_H - diff --git a/src/external/arpack++/include/ardgcomp.h b/src/external/arpack++/include/ardgcomp.h deleted file mode 100644 index 41240dce..00000000 --- a/src/external/arpack++/include/ardgcomp.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDGComp.h. - Arpack++ class ARluCompGenEig definition - (dense matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARDGCOMP_H -#define ARDGCOMP_H - -#include -#include -#include "arch.h" -#include "ardnsmat.h" -#include "ardnspen.h" -#include "arrseig.h" -#include "argcomp.h" - - -template -class ARluCompGenEig: - public virtual - ARCompGenEig, ARFLOAT >, - ARdsNonSymPencil, ARFLOAT > > { - - private: - - // a) Data structure used to store matrices. - - ARdsNonSymPencil, ARFLOAT > Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluCompGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(arcomplex sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(arcomplex sigmap); - - // c.2) Constructors and destructor. - - ARluCompGenEig() { } - // Short constructor. - - ARluCompGenEig(int nevp, ARdsNonSymMatrix, ARFLOAT>& A, - ARdsNonSymMatrix, ARFLOAT>& B, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluCompGenEig(int nevp, ARdsNonSymMatrix, ARFLOAT>& A, - ARdsNonSymMatrix, ARFLOAT>& B, - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluCompGenEig(const ARluCompGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluCompGenEig() { } - - // d) Operators. - - ARluCompGenEig& operator=(const ARluCompGenEig& other); - // Assignment operator. - -}; // class ARluCompGenEig. - - -// ------------------------------------------------------------------------ // -// ARluCompGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluCompGenEig:: -Copy(const ARluCompGenEig& other) -{ - - ARCompGenEig, ARFLOAT >, - ARdsNonSymPencil, ARFLOAT> >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - -} // Copy. - - -template -inline void ARluCompGenEig:: -ChangeShift(arcomplex sigmaRp) -{ - - this->objOP->FactorAsB(sigmaRp); - ARrcStdEig >::ChangeShift(sigmaRp); - -} // ChangeShift. - - -template -inline void ARluCompGenEig::SetRegularMode() -{ - - ARStdEig, - ARdsNonSymPencil, ARFLOAT> >:: - SetRegularMode(&Pencil, - &ARdsNonSymPencil, ARFLOAT>::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluCompGenEig:: -SetShiftInvertMode(arcomplex sigmap) -{ - - ARCompGenEig, ARFLOAT>, - ARdsNonSymPencil, ARFLOAT> >:: - SetShiftInvertMode(sigmap, &Pencil, - &ARdsNonSymPencil,ARFLOAT>::MultInvAsBv); - -} // SetShiftInvertMode. - - -template -inline ARluCompGenEig:: -ARluCompGenEig(int nevp, ARdsNonSymMatrix, ARFLOAT>& A, - ARdsNonSymMatrix, ARFLOAT>& B, const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, - arcomplex* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARdsNonSymPencil, ARFLOAT>::MultInvBAv, - &Pencil, - &ARdsNonSymPencil, ARFLOAT>::MultBv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluCompGenEig:: -ARluCompGenEig(int nevp, ARdsNonSymMatrix, ARFLOAT>& A, - ARdsNonSymMatrix, ARFLOAT>& B, - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARdsNonSymPencil, ARFLOAT>::MultInvAsBv, - &Pencil, - &ARdsNonSymPencil, ARFLOAT>::MultBv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - SetShiftInvertMode(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluCompGenEig& ARluCompGenEig:: -operator=(const ARluCompGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDGCOMP_H diff --git a/src/external/arpack++/include/ardgnsym.h b/src/external/arpack++/include/ardgnsym.h deleted file mode 100644 index ec9c60bd..00000000 --- a/src/external/arpack++/include/ardgnsym.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDGNSym.h. - Arpack++ class ARluNonSymGenEig definition - (dense matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARDGNSYM_H -#define ARDGNSYM_H - -#include -#include -#include "arch.h" -#include "ardnsmat.h" -#include "ardnspen.h" -#include "argnsym.h" - - -template -class ARluNonSymGenEig: - public virtual ARNonSymGenEig, - ARdsNonSymPencil > { - - private: - - // a) Data structure used to store matrices. - - ARdsNonSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluNonSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp = 0.0); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetComplexShiftMode(char partp, ARFLOAT sigmaRp,ARFLOAT sigmaIp); - - // c.2) Constructors and destructor. - - ARluNonSymGenEig() { } - // Short constructor. - - ARluNonSymGenEig(int nevp, ARdsNonSymMatrix& A, - ARdsNonSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluNonSymGenEig(int nevp, ARdsNonSymMatrix& A, - ARdsNonSymMatrix& B, ARFLOAT sigma, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (real shift and invert mode). - - ARluNonSymGenEig(int nevp, ARdsNonSymMatrix& A, - ARdsNonSymMatrix& B, char partp, - ARFLOAT sigmaRp, ARFLOAT sigmaIp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (complex shift and invert mode). - - ARluNonSymGenEig(const ARluNonSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluNonSymGenEig& operator=(const ARluNonSymGenEig& other); - // Assignment operator. - -}; // class ARluNonSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluNonSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymGenEig:: -Copy(const ARluNonSymGenEig& other) -{ - - ARNonSymGenEig, - ARdsNonSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - -} // Copy. - - -template -inline void ARluNonSymGenEig:: -ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - if (sigmaIp == 0.0) { - this->objOP->FactorAsB(sigmaRp); - } - else { - this->objOP->FactorAsB(sigmaRp, sigmaIp, this->part); - } - ARrcNonSymGenEig::ChangeShift(sigmaRp, sigmaIp); - -} // ChangeShift. - - -template -inline void ARluNonSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARdsNonSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluNonSymGenEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARNonSymGenEig, - ARdsNonSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, - &ARdsNonSymPencil::MultInvAsBv); - -} // SetShiftInvertMode. - - -template -inline void ARluNonSymGenEig:: -SetComplexShiftMode(char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - ARNonSymGenEig, - ARdsNonSymPencil >:: - SetComplexShiftMode(partp, sigmaRp, sigmaIp, &Pencil, - &ARdsNonSymPencil::MultInvAsBv, - &Pencil, &ARdsNonSymPencil::MultAv); - -} // SetComplexShiftMode. - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARdsNonSymMatrix& A, - ARdsNonSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARdsNonSymPencil::MultInvBAv, &Pencil, - &ARdsNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARdsNonSymMatrix& A, - ARdsNonSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARdsNonSymPencil::MultInvAsBv, &Pencil, - &ARdsNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - SetShiftInvertMode(sigmap); - -} // Long constructor (real shift and invert mode). - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARdsNonSymMatrix& A, - ARdsNonSymMatrix& B, - char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp, const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, ARFLOAT* residp, - bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARdsNonSymPencil::MultInvAsBv, &Pencil, - &ARdsNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - SetComplexShiftMode(partp, sigmaRp, sigmaIp); - -} // Long constructor (complex shift and invert mode). - - -template -ARluNonSymGenEig& ARluNonSymGenEig:: -operator=(const ARluNonSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDGNSYM_H diff --git a/src/external/arpack++/include/ardgsym.h b/src/external/arpack++/include/ardgsym.h deleted file mode 100644 index 31600802..00000000 --- a/src/external/arpack++/include/ardgsym.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDGSym.h. - Arpack++ class ARluSymGenEig definition - (dense matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARDGSYM_H -#define ARDGSYM_H - -#include -#include -#include "arch.h" -#include "ardsmat.h" -#include "ardspen.h" -#include "argsym.h" - - -template -class ARluSymGenEig: - public virtual ARSymGenEig, - ARdsSymPencil > { - - private: - - // a) Data structure used to store matrices. - - ARdsSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmap); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetBucklingMode(ARFLOAT sigmap); - - virtual void SetCayleyMode(ARFLOAT sigmap); - - // c.2) Constructors and destructor. - - ARluSymGenEig() { } - // Short constructor. - - ARluSymGenEig(int nevp, ARdsSymMatrix& A, - ARdsSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymGenEig(char InvertModep, int nevp, ARdsSymMatrix& A, - ARdsSymMatrix& B, ARFLOAT sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert, buckling and Cayley modes). - - ARluSymGenEig(const ARluSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluSymGenEig& operator=(const ARluSymGenEig& other); - // Assignment operator. - -}; // class ARluSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymGenEig:: -Copy(const ARluSymGenEig& other) -{ - - ARSymGenEig, - ARdsSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - -} // Copy. - - -template -inline void ARluSymGenEig::ChangeShift(ARFLOAT sigmap) -{ - - this->objOP->FactorAsB(sigmap); - ARrcSymGenEig::ChangeShift(sigmap); - -} // ChangeShift. - - -template -inline void ARluSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARdsSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluSymGenEig:: -SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARdsSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, &ARdsSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARdsSymPencil::MultBv); - -} // SetShiftInvertMode. - - -template -inline void ARluSymGenEig:: -SetBucklingMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARdsSymPencil >:: - SetBucklingMode(sigmap, &Pencil, &ARdsSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARdsSymPencil::MultAv); - -} // SetBucklingMode. - - -template -inline void ARluSymGenEig:: -SetCayleyMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARdsSymPencil >:: - SetCayleyMode(sigmap, &Pencil, &ARdsSymPencil::MultInvAsBv, - &Pencil, &ARdsSymPencil::MultAv); - this->ChangeMultBx(&Pencil, &ARdsSymPencil::MultBv); - -} // SetCayleyMode. - - -template -inline ARluSymGenEig:: -ARluSymGenEig(int nevp, ARdsSymMatrix& A, - ARdsSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->InvertMode = 'S'; - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARdsSymPencil::MultInvBAv, &Pencil, - &ARdsSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymGenEig:: -ARluSymGenEig(char InvertModep, int nevp, ARdsSymMatrix& A, - ARdsSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARdsSymPencil::MultInvAsBv, &Pencil, - &ARdsSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - this->InvertMode = this->CheckInvertMode(InvertModep); - switch (this->InvertMode) { - case 'B': - this->ChangeMultBx(&Pencil, &ARdsSymPencil::MultAv); - case 'S': - ChangeShift(sigmap); - break; - case 'C': - SetCayleyMode(sigmap); - } - -} // Long constructor (shift and invert, buckling and Cayley modes). - - -template -ARluSymGenEig& ARluSymGenEig:: -operator=(const ARluSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDGSYM_H diff --git a/src/external/arpack++/include/ardnsmat.h b/src/external/arpack++/include/ardnsmat.h deleted file mode 100644 index a2436b93..00000000 --- a/src/external/arpack++/include/ardnsmat.h +++ /dev/null @@ -1,622 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDNSMat.h. - Arpack++ class ARdsNonSymMatrix definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "ardnspen.h" - -#ifndef ARDNSMAT_H -#define ARDNSMAT_H - -#include -#include -#include "arch.h" -#include "armat.h" -#include "arerror.h" -#include "blas1c.h" -#include "lapackc.h" -#include "ardfmat.h" - -template class ARdsNonSymPencil; - -template -class ARdsNonSymMatrix: public ARMatrix { - - friend class ARdsNonSymPencil; - friend class ARdsNonSymPencil; - - protected: - - bool factored; - int info; - int* ipiv; - ARTYPE* A; - ARTYPE* Ainv; - ARdfMatrix mat; - - void ClearMem(); - - virtual void Copy(const ARdsNonSymMatrix& other); - - void CreateStructure(); - - void ThrowError(); - - public: - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultMtv(ARTYPE* v, ARTYPE* w); - - void MultMtMv(ARTYPE* v, ARTYPE* w); - - void MultMMtv(ARTYPE* v, ARTYPE* w); - - void Mult0MMt0v(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, ARTYPE* Ap); - - void DefineMatrix(int mp, int np, ARTYPE* Ap); - - ARdsNonSymMatrix(): ARMatrix() { factored = false; } - // Short constructor that does nothing. - - ARdsNonSymMatrix(int np, ARTYPE* Ap); - // Long constructor (square matrix). - - ARdsNonSymMatrix(int mp, int np, ARTYPE* Ap); - // Long constructor (rectangular matrix). - - ARdsNonSymMatrix(const std::string& file, int blksizep = 0); - // Long constructor (Matrix stored in a file). - - ARdsNonSymMatrix(const ARdsNonSymMatrix& other) { Copy(other); } - // Copy constructor. - - virtual ~ARdsNonSymMatrix() { ClearMem(); } - // Destructor. - - ARdsNonSymMatrix& operator=(const ARdsNonSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARdsNonSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARdsNonSymMatrix::ClearMem() -{ - - if (factored) { - delete[] Ainv; - delete[] ipiv; - Ainv = NULL; - ipiv = NULL; - } - -} // ClearMem. - - -template -inline void ARdsNonSymMatrix:: -Copy(const ARdsNonSymMatrix& other) -{ - - // Copying very fundamental variables and user-defined parameters. - - this->m = other.m; - this->n = other.n; - this->defined = other.defined; - factored = other.factored; - info = other.info; - A = other.A; - - // Copying mat. - - if (other.mat.IsDefined()) { - mat.Define(other.mat.Filename(),other.mat.BlockSize()); - } - - // Returning from here if "other" was not factored. - - if (!factored) return; - - // Copying vectors. - - Ainv = new ARTYPE[this->m*this->n]; - ipiv = new int[this->n]; - - copy(this->m*this->n, other.Ainv, 1, Ainv, 1); - for (int i=0; in; i++) ipiv[i] = other.ipiv[i]; - -} // Copy. - - -template -inline void ARdsNonSymMatrix::CreateStructure() -{ - - ClearMem(); - Ainv = new ARTYPE[this->m*this->n]; - ipiv = new int[this->n]; - -} // CreateStructure. - - -template -inline void ARdsNonSymMatrix::ThrowError() -{ - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARdsNonSymMatrix::FactorA"); - } - else if (info) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARdsNonSymMatrix::FactorA"); - } - -} // ThrowError. - - -template -void ARdsNonSymMatrix::FactorA() -{ - - // Quitting the function if A was not defined or is rectangular. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARdsNonSymMatrix::FactorA"); - } - - if (this->m!=this->n) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARdsNonSymMatrix::FactorA"); - } - - if (mat.IsOutOfCore()) { - throw ArpackError(ArpackError::INSUFICIENT_MEMORY, - "ARdsNonSymMatrix::FactorA"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Copying A to Ainv; - - ::copy(this->m*this->n, A, 1, Ainv, 1); - - // Decomposing A. - - getrf(this->m, this->n, Ainv, this->m, ipiv, info); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorA. - - -template -void ARdsNonSymMatrix::FactorAsI(ARTYPE sigma) -{ - - // Quitting the function if A was not defined or is rectangular. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARdsNonSymMatrix::FactorAsI"); - } - - if (this->m!=this->n) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARdsNonSymMatrix::FactorAsI"); - } - - if (mat.IsOutOfCore()) { - throw ArpackError(ArpackError::INSUFICIENT_MEMORY, - "ARdsNonSymMatrix::FactorAsI"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Subtracting sigma*I from A. - - ::copy(this->m*this->n,A,1,Ainv,1); - for (int i=0; i<(this->m*this->n); i+=this->m+1) Ainv[i]-=sigma; - - // Decomposing AsI. - - getrf(this->m, this->n, Ainv, this->m, ipiv, info); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorAsI. - - -template -void ARdsNonSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ - - int i; - ARTYPE* t; - ARTYPE one; - ARTYPE zero; - - one = (ARTYPE)0 + 1.0; - zero = (ARTYPE)0; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARdsNonSymMatrix::MultMv"); - } - - // Determining w = M.v. - - if (mat.IsOutOfCore()) { - - if (this->m>this->n) { - - // Matrix is "tall". - - mat.Rewind(); - for (i=0; in, one, mat.Entries(), - mat.RowsInMemory(), v, 1, zero, &w[mat.FirstIndex()], 1); - } - - } - else { - - // Matrix is "fat". - - mat.Rewind(); - t = new ARTYPE[mat.ColsInMemory()]; - for (i=0; im; i++) w[i] = zero; - for (i=0; im, mat.ColsInMemory(), one, mat.Entries(), - this->m, &v[mat.FirstIndex()], 1, zero, t, 1); - axpy(this->m, one, t, 1, w, 1); - } - delete[] t; - - } - - } - else { - - gemv("N", this->m, this->n, one, A, this->m, v, 1, zero, w, 1); - - } - -} // MultMv. - - -template -void ARdsNonSymMatrix::MultMtv(ARTYPE* v, ARTYPE* w) -{ - - int i; - ARTYPE* t; - ARTYPE one; - ARTYPE zero; - - one = (ARTYPE)0 + 1.0; - zero = (ARTYPE)0; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARdsNonSymMatrix::MultMtv"); - } - - // Determining w = M'.v. - - if (mat.IsOutOfCore()) { - - if (this->m<=this->n) { - - // Matrix is "fat". - - mat.Rewind(); - for (i=0; im, mat.ColsInMemory(), one, mat.Entries(), - this->m, v, 1, zero, &w[mat.FirstIndex()], 1); - } - - } - else { - - // Matrix is "tall". - - mat.Rewind(); - t = new ARTYPE[mat.ColsInMemory()]; - for (i=0; im; i++) w[i] = zero; - for (i=0; in, one, mat.Entries(), - mat.RowsInMemory(), &v[mat.FirstIndex()], 1, zero, t, 1); - axpy(mat.RowsInMemory(), one, t, 1, w, 1); - } - delete[] t; - - } - - } - else { - - gemv("T", this->m, this->n, one, A, this->m, v, 1, zero, w, 1); - - } - - -} // MultMtv. - - -template -void ARdsNonSymMatrix::MultMtMv(ARTYPE* v, ARTYPE* w) -{ - - int i; - ARTYPE *t, *s; - ARTYPE one; - ARTYPE zero; - - one = (ARTYPE)0 + 1.0; - zero = (ARTYPE)0; - - if (mat.IsOutOfCore() && (this->m>this->n)) { - - // Special code for "tall" matrices. - - t = new ARTYPE[mat.BlockSize()]; - s = new ARTYPE[this->n]; - - mat.Rewind(); - for (i=0; in; i++) w[i] = zero; - for (i=0; in, one, mat.Entries(), - mat.RowsInMemory(), v, 1, zero, t, 1); - gemv("T", mat.RowsInMemory(), this->n, one, mat.Entries(), - mat.RowsInMemory(), t, 1, zero, s, 1); - axpy(this->n, one, s, 1, w, 1); - - } - - delete[] t; - delete[] s; - - } - else { - - t = new ARTYPE[this->m]; - - MultMv(v,t); - MultMtv(t,w); - - delete[] t; - - } - - -} // MultMtMv. - - -template -void ARdsNonSymMatrix::MultMMtv(ARTYPE* v, ARTYPE* w) -{ - - int i; - ARTYPE *t, *s; - ARTYPE one; - ARTYPE zero; - - one = (ARTYPE)0 + 1.0; - zero = (ARTYPE)0; - - if (mat.IsOutOfCore() && (this->m<=this->n)) { - - // Special code for "fat" matrices. - - t = new ARTYPE[mat.BlockSize()]; - s = new ARTYPE[this->m]; - - mat.Rewind(); - for (i=0; im; i++) w[i] = zero; - for (i=0; im, mat.ColsInMemory(), one, mat.Entries(), - this->m, v, 1, zero, t, 1); - gemv("N", this->m, mat.ColsInMemory(), one, mat.Entries(), - this->m, t, 1, zero, s, 1); - axpy(this->m, one, s, 1, w, 1); - - } - - delete[] t; - delete[] s; - - } - else { - - t = new ARTYPE[this->n]; - - MultMtv(v,t); - MultMv(t,w); - - delete[] t; - - } - -} // MultMMtv. - - -template -void ARdsNonSymMatrix::Mult0MMt0v(ARTYPE* v, ARTYPE* w) -{ - - MultMv(&v[this->m],w); - MultMtv(v,&w[this->m]); - -} // Mult0MMt0v. - - -template -void ARdsNonSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARdsNonSymMatrix::MultInvv"); - } - - // Overwritting w with v. - - copy(this->n, v, 1, w, 1); - - // Solving A.w = v (or AsI.w = v). - - getrs("N", this->n, 1, Ainv, this->m, ipiv, w, this->m, info); - - // Handling errors. - - ThrowError(); - -} // MultInvv. - - -template -inline void ARdsNonSymMatrix:: -DefineMatrix(int np, ARTYPE* Ap) -{ - - // Defining member variables. - - this->n = np; - this->m = np; - A = Ap; - this->defined = true; - Ainv = NULL; - ipiv = NULL; - info = 0; - -} // DefineMatrix (square). - - -template -inline void ARdsNonSymMatrix:: -DefineMatrix(int mp, int np, ARTYPE* Ap) -{ - - // Defining member variables. - - this->m = mp; - this->n = np; - A = Ap; - this->defined = true; - Ainv = NULL; - ipiv = NULL; - info = 0; - -} // DefineMatrix (rectangular). - - -template -inline ARdsNonSymMatrix:: -ARdsNonSymMatrix(int np, ARTYPE* Ap) : ARMatrix(np) -{ - - factored = false; - DefineMatrix(np, Ap); - -} // Long constructor (square matrix). - - -template -inline ARdsNonSymMatrix:: -ARdsNonSymMatrix(int mp, int np, ARTYPE* Ap) : ARMatrix(mp, np) -{ - - factored = false; - DefineMatrix(mp, np, Ap); - -} // Long constructor (rectangular matrix). - - -template -ARdsNonSymMatrix::ARdsNonSymMatrix(const std::string& file, int blksizep) -{ - - factored = false; - - try { - mat.Define(file, blksizep); - } - catch (ArpackError) { // Returning from here if an error has occurred. - throw ArpackError(ArpackError::CANNOT_READ_FILE, "ARdsNonSymMatrix"); - } - - if (mat.NCols() == mat.NRows()) { - DefineMatrix(mat.NCols(), (ARTYPE*)mat.Entries()); - } - else { - DefineMatrix(mat.NRows(), mat.NCols(), (ARTYPE*)mat.Entries()); - } - -} // Long constructor (Matrix stored in a file). - - -template -ARdsNonSymMatrix& ARdsNonSymMatrix:: -operator=(const ARdsNonSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDNSMAT_H diff --git a/src/external/arpack++/include/ardnspen.h b/src/external/arpack++/include/ardnspen.h deleted file mode 100644 index 04d2bd41..00000000 --- a/src/external/arpack++/include/ardnspen.h +++ /dev/null @@ -1,324 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDNSPen.h. - Arpack++ class ARdsNonSymPencil definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARDNSPEN_H -#define ARDNSPEN_H - -#include "arch.h" -#include "arerror.h" -#include "blas1c.h" -#include "lapackc.h" -#include "ardnsmat.h" - - -template -class ARdsNonSymPencil -{ - - protected: - - char part; - ARdsNonSymMatrix* A; - ARdsNonSymMatrix* B; - ARdsNonSymMatrix AsB; -#ifdef ARCOMP_H - ARdsNonSymMatrix, ARFLOAT> AsBc; -#endif - - virtual void Copy(const ARdsNonSymPencil& other); - - public: - -#ifdef ARCOMP_H - bool IsFactored() { return (AsB.IsFactored()||AsBc.IsFactored()); } -#else - bool IsFactored() { return AsB.IsFactored(); } -#endif - - void FactorAsB(ARTYPE sigma); - -#ifdef ARCOMP_H - void FactorAsB(ARFLOAT sigmaR, ARFLOAT sigmaI, char partp = 'R'); -#endif - - void MultAv(ARTYPE* v, ARTYPE* w) { A->MultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - -#ifdef ARCOMP_H - void MultInvAsBv(arcomplex* v, arcomplex* w); -#endif - - void MultInvAsBv(ARFLOAT* v, ARFLOAT* w); - - void DefineMatrices(ARdsNonSymMatrix& Ap, - ARdsNonSymMatrix& Bp); - - ARdsNonSymPencil() { part = 'N'; } - // Short constructor that does nothing. - - ARdsNonSymPencil(ARdsNonSymMatrix& Ap, - ARdsNonSymMatrix& Bp); - // Long constructor. - - ARdsNonSymPencil(const ARdsNonSymPencil& other) { Copy(other); } - // Copy constructor. - - virtual ~ARdsNonSymPencil() { } - // Destructor. - - ARdsNonSymPencil& operator=(const ARdsNonSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARdsNonSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARdsNonSymPencil:: -Copy(const ARdsNonSymPencil& other) -{ - - part = other.part; - A = other.A; - B = other.B; - AsB = other.AsB; -#ifdef ARCOMP_H - AsBc = other.AsBc; -#endif - -} // Copy. - - -template -void ARdsNonSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARdsNonSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARdsNonSymPencil::FactorAsB"); - } - - // Copying A to AsB if sigma = 0. - - if (sigma == (ARTYPE)0) { - - AsB = *A; - if (!AsB.IsFactored()) AsB.FactorA(); - return; - - } - - // Defining matrix AsB. - - if (!AsB.IsDefined()) { - AsB.DefineMatrix(A->ncols(), A->A); - } - - // Reserving memory for some vectors used in matrix decomposition. - - AsB.CreateStructure(); - - // Subtracting sigma*B from A and storing the result on AsB. - - ::copy(A->m*A->n, A->A, 1, AsB.Ainv, 1); - axpy(A->m*A->n, -sigma, B->A, 1, AsB.Ainv, 1); - - // Decomposing AsB. - - getrf(AsB.m, AsB.n, AsB.Ainv, AsB.m, AsB.ipiv, AsB.info); - - // Handling errors. - - AsB.ThrowError(); - - AsB.factored = true; - -} // FactorAsB (ARTYPE shift). - - -#ifdef ARCOMP_H -template -void ARdsNonSymPencil:: -FactorAsB(ARFLOAT sigmaR, ARFLOAT sigmaI, char partp) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARdsNonSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARdsNonSymPencil::FactorAsB"); - } - - // Defining matrix AsB. - - if (!AsBc.IsDefined()) { - part = partp; - AsBc.DefineMatrix(A->ncols(), 0); - } - - // Reserving memory for some vectors used in matrix decomposition. - - AsBc.CreateStructure(); - - // Subtracting sigma*B from A and storing the result on AsBc. - - arcomplex sigma(sigmaR, sigmaI); - for (int i=0; i<(A->m*A->n); i++) AsBc.Ainv[i] = A->A[i]-sigma*B->A[i]; - - // Decomposing AsBc. - - getrf(AsBc.m, AsBc.n, AsBc.Ainv, AsBc.m, AsBc.ipiv, AsBc.info); - - // Handling errors. - - AsBc.ThrowError(); - - AsBc.factored = true; - -} // FactorAsB (arcomplex shift). -#endif // ARCOMP_H. - - -template -void ARdsNonSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - B->MultInvv(w, w); - -} // MultInvBAv. - - -#ifdef ARCOMP_H - -template -void ARdsNonSymPencil:: -MultInvAsBv(arcomplex* v, arcomplex* w) -{ - - AsB.MultInvv((ARTYPE*)v,(ARTYPE*)w); - -} // MultInvAsBv (arcomplex). - -#endif // ARCOMP_H. - - -template -void ARdsNonSymPencil::MultInvAsBv(ARFLOAT* v, ARFLOAT* w) -{ - - if (part == 'N') { // shift is real. - - AsB.MultInvv((ARTYPE*)v,(ARTYPE*)w); - - } - else { // shift is complex. - -#ifdef ARCOMP_H - - int i; - arcomplex *tv, *tw; - - tv = new arcomplex[AsBc.ncols()]; - tw = new arcomplex[AsBc.ncols()]; - - for (i=0; i!=AsBc.ncols(); i++) tv[i] = arcomplex(v[i], 0.0); - - AsBc.MultInvv(tv, tw); - - if (part=='I') { - for (i=0; i!=AsBc.ncols(); i++) w[i] = imag(tw[i]); - } - else { - for (i=0; i!=AsBc.ncols(); i++) w[i] = real(tw[i]); - } - - delete[] tv; - delete[] tw; - -#endif // ARCOMP_H. - - } - -} // MultInvAsBv (ARFLOAT). - - -template -inline void ARdsNonSymPencil:: -DefineMatrices(ARdsNonSymMatrix& Ap, - ARdsNonSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - - if ((A->n != B->n)||(A->m != B->m)) { - throw ArpackError(ArpackError::INCOMPATIBLE_SIZES, - "ARdsNonSymMatrix::DefineMatrices"); - } - -} // DefineMatrices. - - -template -inline ARdsNonSymPencil:: -ARdsNonSymPencil(ARdsNonSymMatrix& Ap, - ARdsNonSymMatrix& Bp) -{ - - DefineMatrices(Ap, Bp); - -} // Long constructor. - - -template -ARdsNonSymPencil& ARdsNonSymPencil:: -operator=(const ARdsNonSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDNSPEN_H diff --git a/src/external/arpack++/include/ardscomp.h b/src/external/arpack++/include/ardscomp.h deleted file mode 100644 index 094f5840..00000000 --- a/src/external/arpack++/include/ardscomp.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDSComp.h. - Arpack++ class ARluCompStdEig definition - (dense matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARDSCOMP_H -#define ARDSCOMP_H - -#include -#include -#include "arch.h" -#include "arscomp.h" -#include "ardnsmat.h" -#include "arrseig.h" - - -template -class ARluCompStdEig: - public virtual ARCompStdEig, ARFLOAT> > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(arcomplex sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(arcomplex sigmap); - - // a.2) Constructors and destructor. - - ARluCompStdEig() { } - // Short constructor. - - ARluCompStdEig(int nevp, ARdsNonSymMatrix, ARFLOAT>& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluCompStdEig(int nevp, ARdsNonSymMatrix, ARFLOAT>& A, - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluCompStdEig(const ARluCompStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluCompStdEig() { } - // Destructor. - - - // b) Operators. - - ARluCompStdEig& operator=(const ARluCompStdEig& other); - // Assignment operator. - -}; // class ARluCompStdEig. - - -// ------------------------------------------------------------------------ // -// ARluCompStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluCompStdEig:: -ChangeShift(arcomplex sigmaRp) -{ - - this->objOP->FactorAsI(sigmaRp); - ARrcStdEig >::ChangeShift(sigmaRp); - -} // ChangeShift. - - -template -inline void ARluCompStdEig::SetRegularMode() -{ - - ARStdEig, - ARdsNonSymMatrix, ARFLOAT> >:: - SetRegularMode(this->objOP, - &ARdsNonSymMatrix, ARFLOAT>::MultMv); - -} // SetRegularMode. - - -template -inline void ARluCompStdEig:: -SetShiftInvertMode(arcomplex sigmap) -{ - - ARStdEig, - ARdsNonSymMatrix, ARFLOAT> >:: - SetShiftInvertMode(sigmap, this->objOP, - &ARdsNonSymMatrix,ARFLOAT>::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluCompStdEig:: -ARluCompStdEig(int nevp, ARdsNonSymMatrix, ARFLOAT>& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, arcomplex* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, - &ARdsNonSymMatrix, ARFLOAT>::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluCompStdEig:: -ARluCompStdEig(int nevp, ARdsNonSymMatrix, ARFLOAT>& A, - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, - &ARdsNonSymMatrix, ARFLOAT>::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluCompStdEig& ARluCompStdEig:: -operator=(const ARluCompStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDSCOMP_H diff --git a/src/external/arpack++/include/ardsmat.h b/src/external/arpack++/include/ardsmat.h deleted file mode 100644 index 1b16ae23..00000000 --- a/src/external/arpack++/include/ardsmat.h +++ /dev/null @@ -1,357 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDSMat.h. - Arpack++ class ARdsSymMatrix definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "ardspen.h" - -#ifndef ARDSMAT_H -#define ARDSMAT_H - -#include - -#include "arch.h" -#include "armat.h" -#include "arerror.h" -#include "blas1c.h" -#include "lapackc.h" - -template class ARdsSymPencil; - -template -class ARdsSymMatrix: public ARMatrix { - - friend class ARdsSymPencil; - - protected: - - bool factored; - char uplo; - int info; - int* ipiv; - ARTYPE* A; - ARTYPE* Ainv; - - void ClearMem(); - - virtual void Copy(const ARdsSymMatrix& other); - - void SubtractAsI(ARTYPE sigma); - - void CreateStructure(); - - void ThrowError(); - - public: - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, ARTYPE* Ap, char uplop = 'L'); - - ARdsSymMatrix(): ARMatrix() { factored = false; } - // Short constructor that does nothing. - - ARdsSymMatrix(int np, ARTYPE* Ap, char uplop = 'L'); - // Long constructor. - - ARdsSymMatrix(const ARdsSymMatrix& other) { Copy(other); } - // Copy constructor. - - virtual ~ARdsSymMatrix() { ClearMem(); } - // Destructor. - - ARdsSymMatrix& operator=(const ARdsSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARdsSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARdsSymMatrix::ClearMem() -{ - - if (factored) { - delete[] Ainv; - delete[] ipiv; - Ainv = NULL; - ipiv = NULL; - } - -} // ClearMem. - - -template -inline void ARdsSymMatrix:: -Copy(const ARdsSymMatrix& other) -{ - - // Copying very fundamental variables and user-defined parameters. - - this->m = other.m; - this->n = other.n; - this->defined = other.defined; - factored = other.factored; - uplo = other.uplo; - info = other.info; - A = other.A; - - // Returning from here if "other" was not factored. - - if (!factored) return; - - // Copying vectors. - - Ainv = new ARTYPE[(this->n*this->n+this->n)/2]; - ipiv = new int[this->n]; - - copy((this->n*this->n+this->n)/2, other.Ainv, 1, Ainv, 1); - for (int i=0; in; i++) ipiv[i] = other.ipiv[i]; - -} // Copy. - - -template -void ARdsSymMatrix::SubtractAsI(ARTYPE sigma) -{ - - int i,j; - - // Copying A to Ainv. - - ::copy((this->n*this->n+this->n)/2 ,A, 1, Ainv, 1); - - // Subtracting sigma from diagonal elements. - - if (uplo=='L') { - for (i=0, j=0; in; j+=(this->n-(i++))) Ainv[j] -= sigma; - } - else { - for (i=0, j=0; in; j+=(++i)) Ainv[j] -= sigma; - } - -} // SubtractAsI. - - -template -inline void ARdsSymMatrix::CreateStructure() -{ - - ClearMem(); - Ainv = new ARTYPE[(this->n*this->n+this->n)/2]; - ipiv = new int[this->n]; - -} // CreateStructure. - - -template -inline void ARdsSymMatrix::ThrowError() -{ - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARdsSymMatrix::FactorA"); - } - else if (info) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARdsSymMatrix::FactorA"); - } - -} // ThrowError. - - -template -void ARdsSymMatrix::FactorA() -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARdsSymMatrix::FactorA"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Copying A to Ainv; - - ::copy((this->n*this->n+this->n)/2 ,A, 1, Ainv, 1); - - // Decomposing A. - - sptrf(&uplo, this->n, Ainv, ipiv, info); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorA. - - -template -void ARdsSymMatrix::FactorAsI(ARTYPE sigma) -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARdsSymMatrix::FactorAsI"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Subtracting sigma*I from A. - - SubtractAsI(sigma); - - // Decomposing AsI. - - sptrf(&uplo, this->n, Ainv, ipiv, info); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorAsI. - - -template -void ARdsSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ - - int i, j; - - ARTYPE zero = (ARTYPE)0; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARdsSymMatrix::MultMv"); - } - - // Determining w = M.v (unfortunately, the BLAS does not - // have a routine that works with packed matrices). - - for (i=0; in; i++) w[i] = zero; - - if (uplo=='L') { - - for (i=0, j=0; in; j+=(this->n-(i++))) { - w[i] += dot(this->n-i, &A[j], 1, &v[i], 1); - axpy(this->n-i-1, v[i], &A[j+1], 1, &w[i+1], 1); - } - - } - else { // uplo = 'U' - - for (i=0, j=0; in; j+=(++i)) { - w[i] += dot(i+1, &A[j], 1, v, 1); - axpy(i, v[i], &A[j], 1, w, 1); - } - - } - -} // MultMv. - - -template -void ARdsSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARdsSymMatrix::MultInvv"); - } - - // Overwritting w with v. - - copy(this->n, v, 1, w, 1); - - // Solving A.w = v (or AsI.w = v). - - sptrs(&uplo, this->n, 1, Ainv, ipiv, w, this->n, info); - - // Handling errors. - - ThrowError(); - -} // MultInvv. - - -template -inline void ARdsSymMatrix:: -DefineMatrix(int np, ARTYPE* Ap, char uplop) -{ - - // Defining member variables. - - this->m = np; - this->n = np; - uplo = uplop; - A = Ap; - this->defined = true; - Ainv = NULL; - ipiv = NULL; - info = 0; - -} // DefineMatrix. - - -template -inline ARdsSymMatrix:: -ARdsSymMatrix(int np, ARTYPE* Ap, char uplop) : ARMatrix(np) -{ - - factored = false; - DefineMatrix(np, Ap, uplop); - -} // Long constructor. - - -template -ARdsSymMatrix& ARdsSymMatrix:: -operator=(const ARdsSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDSMAT_H diff --git a/src/external/arpack++/include/ardsnsym.h b/src/external/arpack++/include/ardsnsym.h deleted file mode 100644 index fc2afe26..00000000 --- a/src/external/arpack++/include/ardsnsym.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDSNSym.h. - Arpack++ class ARluNonSymStdEig definition - (dense matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARDSNSYM_H -#define ARDSNSYM_H - -#include -#include -#include "arch.h" -#include "arsnsym.h" -#include "ardnsmat.h" - - -template -class ARluNonSymStdEig: - public virtual ARNonSymStdEig > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // a.2) Constructors and destructor. - - ARluNonSymStdEig() { } - // Short constructor. - - ARluNonSymStdEig(int nevp, ARdsNonSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluNonSymStdEig(int nevp, ARdsNonSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluNonSymStdEig(const ARluNonSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymStdEig() { } - // Destructor. - - // b) Operators. - - ARluNonSymStdEig& operator=(const ARluNonSymStdEig& other); - // Assignment operator. - -}; // class ARluNonSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluNonSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymStdEig:: -ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI(this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluNonSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(this->objOP, &ARdsNonSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluNonSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, - &ARdsNonSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluNonSymStdEig:: -ARluNonSymStdEig(int nevp, ARdsNonSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, - &ARdsNonSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluNonSymStdEig:: -ARluNonSymStdEig(int nevp, ARdsNonSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, - &ARdsNonSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluNonSymStdEig& ARluNonSymStdEig:: -operator=(const ARluNonSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDSNSYM_H diff --git a/src/external/arpack++/include/ardspen.h b/src/external/arpack++/include/ardspen.h deleted file mode 100644 index 4e16b82b..00000000 --- a/src/external/arpack++/include/ardspen.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDSPen.h. - Arpack++ class ARdsSymPencil definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARDSPEN_H -#define ARDSPEN_H - -#include "arch.h" -#include "arerror.h" -#include "blas1c.h" -#include "lapackc.h" -#include "ardsmat.h" - - -template -class ARdsSymPencil -{ - - protected: - - ARdsSymMatrix* A; - ARdsSymMatrix* B; - ARdsSymMatrix AsB; - - virtual void Copy(const ARdsSymPencil& other); - - void SubtractAsB(ARTYPE sigma); - - public: - - bool IsFactored() { return AsB.IsFactored(); } - - void FactorAsB(ARTYPE sigma); - - void MultAv(ARTYPE* v, ARTYPE* w) { A->MultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - - void MultInvAsBv(ARTYPE* v, ARTYPE* w) { AsB.MultInvv(v,w); } - - void DefineMatrices(ARdsSymMatrix& Ap, ARdsSymMatrix& Bp); - - ARdsSymPencil() { AsB.factored = false; } - // Short constructor that does nothing. - - ARdsSymPencil(ARdsSymMatrix& Ap, ARdsSymMatrix& Bp); - // Long constructor. - - ARdsSymPencil(const ARdsSymPencil& other) { Copy(other); } - // Copy constructor. - - virtual ~ARdsSymPencil() { } - // Destructor. - - ARdsSymPencil& operator=(const ARdsSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARdsSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARdsSymPencil::Copy(const ARdsSymPencil& other) -{ - - A = other.A; - B = other.B; - AsB = other.AsB; - -} // Copy. - - -template -void ARdsSymPencil::SubtractAsB(ARTYPE sigma) -{ - - int sizeA, i, j, k, l; - - // Copying A into AsB. - - sizeA = (A->ncols()*A->ncols()+A->ncols())/2; - ::copy(sizeA, A->A, 1, AsB.Ainv, 1); - - // Returning if sigma == 0. - - if (sigma == (ARTYPE)0) return; - - // Subtracting sigma*B. - - if (A->uplo == B->uplo) { - - axpy(sizeA, -sigma, B->A, 1, AsB.Ainv, 1); - - } - else if (A->uplo == 'L') { // B->uplo == 'U' - - j = 0; - for (i=0; in; i++) { - for (l=i+1, k=(l*l+l)/2-1; l<=A->n; k+=(l++)) { - AsB.Ainv[j++]-=sigma*B->A[k]; - } - } - - } - else { // A->uplo == 'U' && B->uplo == 'L' - - j = 0; - for (i=0; in; i++) { - for (l=i+1, k=(l*l+l)/2-1; l<=A->n; k+=(l++)) { - AsB.Ainv[k]-=sigma*B->A[j++]; - } - } - - } - -} // SubtractAsB (ARTYPE shift). - - -template -void ARdsSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARdsSymPencil::FactorAsB"); - } - - // Copying A to AsB if sigma = 0. - - if (sigma == (ARTYPE)0) { - - AsB = *A; - if (!AsB.IsFactored()) AsB.FactorA(); - return; - - } - - // Defining matrix AsB. - - if (!AsB.IsDefined()) { - AsB.DefineMatrix(A->ncols(), A->A, A->uplo); - } - - // Reserving memory for some vectors used in matrix decomposition. - - AsB.CreateStructure(); - - // Subtracting sigma*B from A and storing the result on AsB. - - SubtractAsB(sigma); - - // Decomposing AsB. - - sptrf(&AsB.uplo, AsB.n, AsB.Ainv, AsB.ipiv, AsB.info); - - // Handling errors. - - AsB.ThrowError(); - - AsB.factored = true; - -} // FactorAsB (ARTYPE shift). - - -template -void ARdsSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - copy(A->ncols(), w, 1, v, 1); - B->MultInvv(w, w); - -} // MultInvBAv. - - -template -inline void ARdsSymPencil:: -DefineMatrices(ARdsSymMatrix& Ap, ARdsSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - - if ((A->n != B->n)||(A->m != B->m)) { - throw ArpackError(ArpackError::INCOMPATIBLE_SIZES, - "ARdsNonSymMatrix::DefineMatrices"); - } - -} // DefineMatrices. - - -template -inline ARdsSymPencil:: -ARdsSymPencil(ARdsSymMatrix& Ap, ARdsSymMatrix& Bp) -{ - - AsB.factored = false; - DefineMatrices(Ap, Bp); - -} // Long constructor. - - -template -ARdsSymPencil& ARdsSymPencil:: -operator=(const ARdsSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDSPEN_H diff --git a/src/external/arpack++/include/ardssym.h b/src/external/arpack++/include/ardssym.h deleted file mode 100644 index faee7855..00000000 --- a/src/external/arpack++/include/ardssym.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARDSSym.h. - Arpack++ class ARluSymStdEig definition - (dense matrix version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARDSSYM_H -#define ARDSSYM_H - -#include -#include -#include "arch.h" -#include "arssym.h" -#include "ardsmat.h" - - -template -class ARluSymStdEig: - public virtual ARSymStdEig > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // a.2) Constructors and destructor. - - ARluSymStdEig() { } - // Short constructor. - - ARluSymStdEig(int nevp, ARdsSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymStdEig(int nevp, ARdsSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluSymStdEig(const ARluSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymStdEig() { } - // Destructor. - - // b) Operators. - - ARluSymStdEig& operator=(const ARluSymStdEig& other); - // Assignment operator. - -}; // class ARluSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymStdEig:: -ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI(this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(this->objOP, &ARdsSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, &ARdsSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARdsSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, &ARdsSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARdsSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, &ARdsSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluSymStdEig& ARluSymStdEig:: -operator=(const ARluSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARDSSYM_H diff --git a/src/external/arpack++/include/arerror.h b/src/external/arpack++/include/arerror.h deleted file mode 100644 index 6f96b479..00000000 --- a/src/external/arpack++/include/arerror.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARError.h. - Definition of ArpackError, a class that handles errors - occurred during Arpack execution. - - There are three ways of handling an error: - a) Declaring a variable of type ArpackError and calling - function Set with the correct ErrorCode (see codes below). - b) Calling the constructor ArpackError(ErrorCode) to define - a variable. - c) Calling ArpackError::Set(ErrorCode) directly. - - If an error occurs, a brief description of the error is - displayed on the "cerr" stream, unless the variable - ARPACK_SILENT_MODE is defined. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARERROR_H -#define ARERROR_H - -#include -#include -#include - -//#include "arch.h" - -template< typename T > -struct ArpackError_static -{ - public: - - enum ErrorCode { // Listing all kinds of errors. - - // Innocuous error type. - - NO_ERRORS = 0, - - // Errors in parameter definitions. - - PARAMETER_ERROR = -101, - N_SMALLER_THAN_2 = -102, - NEV_OUT_OF_BOUNDS = -103, - WHICH_UNDEFINED = -104, - PART_UNDEFINED = -105, - INVMODE_UNDEFINED = -106, - RANGE_ERROR = -107, - - // Errors in Aupp and Eupp functions. - - LAPACK_ERROR = -201, - START_RESID_ZERO = -202, - NOT_ACCURATE_EIG = -203, - REORDERING_ERROR = -204, - ARNOLDI_NOT_BUILD = -205, - AUPP_ERROR = -291, - EUPP_ERROR = -292, - - // Errors in main functions. - - CANNOT_PREPARE = -301, - CANNOT_FIND_BASIS = -302, - CANNOT_FIND_VALUES = -303, - CANNOT_FIND_VECTORS = -304, - CANNOT_FIND_SCHUR = -305, - SCHUR_UNDEFINED = -306, - - // Errors due to incorrect function calling sequence. - - CANNOT_GET_VECTOR = -401, - CANNOT_GET_PROD = -402, - CANNOT_PUT_VECTOR = -403, - PREPARE_NOT_OK = -404, - BASIS_NOT_OK = -405, - VALUES_NOT_OK = -406, - VECTORS_NOT_OK = -407, - SCHUR_NOT_OK = -408, - RESID_NOT_OK = -409, - - // Errors in classes that perform LU decompositions. - - MATRIX_IS_SINGULAR = -501, - DATA_UNDEFINED = -502, - INSUFICIENT_MEMORY = -503, - NOT_SQUARE_MATRIX = -504, - NOT_FACTORED_MATRIX = -505, - INCOMPATIBLE_SIZES = -506, - DIFFERENT_TRIANGLES = -507, - INCONSISTENT_DATA = -508, - CANNOT_READ_FILE = -509, - - // Errors in matrix files. - - CANNOT_OPEN_FILE = -551, - WRONG_MATRIX_TYPE = -552, - WRONG_DATA_TYPE = -553, - RHS_IGNORED = -554, - UNEXPECTED_EOF = -555, - - // Other severe errors. - - NOT_IMPLEMENTED = -901, - MEMORY_OVERFLOW = -902, - GENERIC_SEVERE = -999, - - // Warnings. - - NCV_OUT_OF_BOUNDS = 101, - MAXIT_NON_POSITIVE = 102, - MAX_ITERATIONS = 201, - NO_SHIFTS_APPLIED = 202, - CHANGING_AUTOSHIFT = 301, - DISCARDING_FACTORS = 401, - GENERIC_WARNING = 999 - - }; - - protected: - - static ErrorCode code; - -}; -// trick to initialize static member code, which is allowed in template - -template< typename T > -enum ArpackError_static::ErrorCode ArpackError_static::code = NO_ERRORS; -// "code" initialization. - -class ArpackError: public ArpackError_static { - - private: - - static void Print(const std::string& where, const std::string& message); - // Writes error messages on cerr stream. - - public: - - static void Set(ErrorCode error, const std::string& where="AREigenProblem"); - // Set error code and write error messages. - - static int Status() { return (int) code; } - // Returns current value of error code. - - ArpackError(ErrorCode error, const std::string& where="AREigenProblem") { - Set(error,where); - } - // Constructor that set error code. - - ArpackError() { code = NO_ERRORS; }; - // Constructor that does nothing. - -}; - -inline void ArpackError::Print(const std::string& where, const std::string& message) -{ - -#ifndef ARPACK_SILENT_MODE - std::cerr << "Arpack error in " << where << "." << std::endl; - std::cerr << "-> " << message << "." << std::endl; -#endif - -} // Print - -inline void ArpackError::Set(ErrorCode error, const std::string& where) -{ - - code = error; - switch (code) { - case NO_ERRORS : - return; - case NOT_IMPLEMENTED : - Print(where, "This function was not implemented yet"); - return; - case MEMORY_OVERFLOW : - Print(where, "Memory overflow"); - return; - case GENERIC_SEVERE : - Print(where, "Severe error"); - return; - case PARAMETER_ERROR : - Print(where, "Some parameters were not correctly defined"); - return; - case N_SMALLER_THAN_2 : - Print(where, "'n' must be greater than one"); - return; - case NEV_OUT_OF_BOUNDS : - Print(where, "'nev' is out of bounds"); - return; - case WHICH_UNDEFINED : - Print(where, "'which' was not correctly defined"); - return; - case PART_UNDEFINED : - Print(where, "'part' must be one of 'R' or 'I'"); - return; - case INVMODE_UNDEFINED : - Print(where, "'InvertMode' must be one of 'S' or 'B'"); - return; - case RANGE_ERROR : - Print(where, "Range error"); - return; - case LAPACK_ERROR : - Print(where, "Could not perform LAPACK eigenvalue calculation"); - return; - case START_RESID_ZERO : - Print(where, "Starting vector is zero"); - return; - case NOT_ACCURATE_EIG : - Print(where, "Could not find any eigenvalue to sufficient accuracy"); - return; - case REORDERING_ERROR : - Print(where, "Reordering of Schur form was not possible"); - return; - case ARNOLDI_NOT_BUILD : - Print(where, "Could not build an Arnoldi factorization"); - return; - case AUPP_ERROR : - Print(where, "Error in ARPACK Aupd fortran code"); - return; - case EUPP_ERROR : - Print(where, "Error in ARPACK Eupd fortran code"); - return; - case CANNOT_PREPARE : - Print(where, "Could not correctly define internal variables"); - return; - case CANNOT_FIND_BASIS : - Print(where, "Could not find an Arnoldi basis"); - return; - case CANNOT_FIND_VALUES : - Print(where, "Could not find any eigenvalue"); - return; - case CANNOT_FIND_VECTORS: - Print(where, "Could not find any eigenvector"); - return; - case CANNOT_FIND_SCHUR : - Print(where, "Could not find any Schur vector"); - return; - case SCHUR_UNDEFINED : - Print(where, "FindEigenvectors must be used instead of FindSchurVectors"); - return; - case CANNOT_GET_VECTOR : - Print(where, "Vector is not already available"); - return; - case CANNOT_GET_PROD : - Print(where, "Matrix-vector product is not already available"); - return; - case CANNOT_PUT_VECTOR : - Print(where, "Could not store vector"); - return; - case PREPARE_NOT_OK : - Print(where, "DefineParameters must be called prior to this function"); - return; - case BASIS_NOT_OK : - Print(where, "An Arnoldi basis is not available"); - return; - case VALUES_NOT_OK : - Print(where, "Eigenvalues are not available"); - return; - case VECTORS_NOT_OK : - Print(where, "Eigenvectors are not available"); - return; - case SCHUR_NOT_OK : - Print(where, "Schur vectors are not available"); - return; - case RESID_NOT_OK : - Print(where, "Residual vector is not available"); - return; - case MATRIX_IS_SINGULAR : - Print(where, "Matrix is singular and could not be factored"); - return; - case DATA_UNDEFINED : - Print(where, "Matrix data was not defined"); - return; - case INSUFICIENT_MEMORY : - Print(where, "fill-in factor must be increased"); - return; - case NOT_SQUARE_MATRIX : - Print(where, "Matrix must be square to be factored"); - return; - case NOT_FACTORED_MATRIX: - Print(where, "Matrix must be factored before solving a system"); - return; - case INCOMPATIBLE_SIZES : - Print(where, "Matrix dimensions must agree"); - return; - case DIFFERENT_TRIANGLES: - Print(where, "A.uplo and B.uplo must be equal"); - return; - case INCONSISTENT_DATA : - Print(where, "Matrix data contain inconsistencies"); - return; - case CANNOT_READ_FILE : - Print(where, "Data file could not be read"); - return; - case CANNOT_OPEN_FILE : - Print(where, "Invalid path or filename"); - return; - case WRONG_MATRIX_TYPE : - Print(where, "Wrong matrix type"); - return; - case WRONG_DATA_TYPE : - Print(where, "Wrong data type"); - return; - case RHS_IGNORED : - Print(where, "RHS vector will be ignored"); - return; - case UNEXPECTED_EOF : - Print(where, "Unexpected end of file"); - return; - case NCV_OUT_OF_BOUNDS : - Print(where, "'ncv' is out of bounds"); - return; - case MAXIT_NON_POSITIVE : - Print(where, "'maxit' must be greater than zero"); - return; - case MAX_ITERATIONS : - Print(where, "Maximum number of iterations taken"); - return; - case NO_SHIFTS_APPLIED : - Print(where, "No shifts could be applied during a cycle of IRAM iteration"); - return; - case CHANGING_AUTOSHIFT : - Print(where, "Turning to automatic selection of implicit shifts"); - return; - case DISCARDING_FACTORS : - Print(where, "Factors L and U were not copied. Matrix must be factored"); - return; - case GENERIC_WARNING : - default: ; - Print(where, "There is something wrong"); - return; - } - -} // Set. - -//ArpackError::ErrorCode ArpackError::code = NO_ERRORS; -// "code" initialization. - -#endif // ARERROR_H diff --git a/src/external/arpack++/include/argcomp.h b/src/external/arpack++/include/argcomp.h deleted file mode 100644 index bce04ea9..00000000 --- a/src/external/arpack++/include/argcomp.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARGComp.h. - Arpack++ class ARCompGenEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARGCOMP_H -#define ARGCOMP_H - -#include -#include -#include "arch.h" -#include "arscomp.h" -#include "argeig.h" - -template -class ARCompGenEig: - virtual public ARGenEig, ARFOP, ARFB>, - virtual public ARCompStdEig { - - public: - - // a) Constructors and destructor. - - ARCompGenEig() { } - // Short constructor (Does nothing but calling base classes constructors). - - ARCompGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(arcomplex[],arcomplex[]), - ARFB* objBp, - void (ARFB::* MultBxp)(arcomplex[],arcomplex[]), - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARCompGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(arcomplex[],arcomplex[]), - ARFB* objBp, - void (ARFB::* MultBxp)(arcomplex[],arcomplex[]), - arcomplex sigmap, - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, arcomplex* residp = NULL, - bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARCompGenEig(const ARCompGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARCompGenEig() { } - // Destructor. - - // b) Operators. - - ARCompGenEig& operator=(const ARCompGenEig& other); - // Assignment operator. - -}; // class ARCompGenEig. - - -// ------------------------------------------------------------------------ // -// ARCompGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline ARCompGenEig:: -ARCompGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(arcomplex[],arcomplex[]), - ARFB* objBp, - void (ARFB::* MultBxp)(arcomplex[], arcomplex[]), - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, arcomplex* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(np, nevp, objOPp, MultOPxp, objBp, MultBxp, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARCompGenEig:: -ARCompGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(arcomplex[],arcomplex[]), - ARFB* objBp, - void (ARFB::* MultBxp)(arcomplex[], arcomplex[]), - arcomplex sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, arcomplex* residp, bool ishiftp) - -{ - - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, objOPp, MultOPxp, objBp, MultBxp, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARCompGenEig& ARCompGenEig:: -operator=(const ARCompGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARGCOMP_H diff --git a/src/external/arpack++/include/argeig.h b/src/external/arpack++/include/argeig.h deleted file mode 100644 index 74ccc07f..00000000 --- a/src/external/arpack++/include/argeig.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARGEig.h. - Arpack++ class ARGenEig definition. - Derived from ARStdEig, this class is the - base class for all generalized eigenvalue problems definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARGEIG_H -#define ARGEIG_H - -#include -#include -#include "arch.h" -#include "arerror.h" -#include "arrgeig.h" -#include "arseig.h" - -// ARGenEig class definition. - -template -class ARGenEig: - virtual public ARrcGenEig, - virtual public ARStdEig { - - public: - - // a) Notation. - - typedef void (ARFB::* TypeBx)(ARTYPE[], ARTYPE[]); - typedef void (ARFOP::* TypeOPx)(ARTYPE[], ARTYPE[]); - - - protected: - - // b) Protected variables: - - ARFB *objB; // Object that has MultBx as a member function. - TypeBx MultBx; // Function that evaluates the product B*x. - - // c) Protected functions: - - virtual void Copy(const ARGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // d) Public functions: - - // d.1) Function that stores user defined parameters. - - virtual void DefineParameters(int np, int nevp, ARFOP* objOPp, - TypeOPx MultOPxp, ARFB* objBp, - TypeBx MultBxp, const std::string& whichp="LM", - int ncvp=0, ARFLOAT tolp=0.0, - int maxitp=0, ARTYPE* residp=NULL, - bool ishiftp=true); - // Set values of problem parameters (also called by constructors). - - - // d.2) Function that allow changes in problem parameters. - - void ChangeMultBx(ARFB* objBp, TypeBx MultBxp); - // Changes the matrix-vector function that performs B*x. - - - // d.3) Functions that perform all calculations in one step. - - virtual int FindArnoldiBasis(); - // Determines the Arnoldi basis related to the given problem. - - - // d.4) Constructors and destructor. - - ARGenEig() { } - // Constructor that does nothing but calling base classes constructors. - - ARGenEig(const ARGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARGenEig() { } - // Destructor (presently meaningless). - - // e) Operators. - - ARGenEig& operator=(const ARGenEig& other); - // Assignment operator. - -}; // class ARGenEig. - - -// ------------------------------------------------------------------------ // -// ARGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARGenEig:: -Copy(const ARGenEig& other) -{ - - ARStdEig::Copy(other); - objB = other.objB; - MultBx = other.MultBx; - -} // Copy. - - -template -void ARGenEig:: -DefineParameters(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARTYPE[], ARTYPE[]), ARFB* objBp, - void (ARFB::* MultBxp)(ARTYPE[], ARTYPE[]), const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, ARTYPE* residp, - bool ishiftp) - -{ - - // Setting parameters of generalized problems. - - objB = objBp; - MultBx = MultBxp; - - // Setting common eigen-problem parameters. - - ARStdEig:: - DefineParameters(np, nevp, objOPp, MultOPxp, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // DefineParameters. - - -template -inline void ARGenEig:: -ChangeMultBx(ARFB* objBp, void (ARFB::* MultBxp)(ARTYPE[], ARTYPE[])) -{ - - objB = objBp; - MultBx = MultBxp; - this->Restart(); - -} // ChangeMultBx. - - -template -int ARGenEig::FindArnoldiBasis() -{ - - if (!this->BasisOK) this->Restart(); - - // Changing to auto shift mode. - - if (!this->AutoShift) { - ArpackError::Set(ArpackError::CHANGING_AUTOSHIFT, "FindArnoldiBasis"); - this->AutoShift=true; - } - - // ARPACK main loop. - - while (!this->BasisOK) { - - // Calling Aupp. - - try { this->TakeStep(); } - catch (ArpackError) { - ArpackError(ArpackError::CANNOT_FIND_BASIS, "FindArnoldiBasis"); - return 0; - } - - switch (this->ido) { - case -1: - - // Performing y <- OP*B*x for the first time when mode != 2. - - if (this->mode != 2) { - this->ipntr[3] = this->ipntr[2]+this->n; // not a clever idea, but... - (this->objB->*MultBx)(&this->workd[this->ipntr[1]],&this->workd[this->ipntr[3]]); - } - - case 1: - - // Performing y <- OP*w. - - if (this->mode == 2) { // w = x if mode = 2. - (this->objOP->*(this->MultOPx))(&this->workd[this->ipntr[1]],&this->workd[this->ipntr[2]]); - } - else { // w = B*x otherwise. - (this->objOP->*(this->MultOPx))(&this->workd[this->ipntr[3]],&this->workd[this->ipntr[2]]); - } - break; - - case 2: - - // Performing y <- B*x. - - (this->objB->*MultBx)(&this->workd[this->ipntr[1]],&this->workd[this->ipntr[2]]); - - } - } - return this->nconv; - -} // FindArnoldiBasis. - - -template -ARGenEig& ARGenEig:: -operator=(const ARGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARGEIG_H - diff --git a/src/external/arpack++/include/argnsym.h b/src/external/arpack++/include/argnsym.h deleted file mode 100644 index 9590cb7b..00000000 --- a/src/external/arpack++/include/argnsym.h +++ /dev/null @@ -1,362 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARGNSym.h. - Arpack++ class ARNonSymGenEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARGNSYM_H -#define ARGNSYM_H - -#include -#include -#include "arch.h" -#include "blas1c.h" -#include "lapackc.h" -#include "arsnsym.h" -#include "argeig.h" -#include "arrgnsym.h" - -template -class ARNonSymGenEig: - virtual public ARGenEig, - virtual public ARNonSymStdEig, - virtual public ARrcNonSymGenEig { - - public: - - // a) Notation. - - typedef void (ARFB::* TypeBx)(ARFLOAT[], ARFLOAT[]); - - - protected: - - // b) Protected variables: - - ARFB *objA; // Object that has MultAx as a member function. - TypeBx MultAx; // Function that evaluates the product A*x. - - - // c) Protected functions: - - void RecoverEigenvalues(); - // Uses Rayleigh quotient to recover eigenvalues of the original - // problem when shift is complex. - - virtual void Copy(const ARNonSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // d) Public functions: - - // d.1) Functions that allow changes in problem parameters. - - virtual void SetShiftInvertMode(ARFLOAT sigmaRp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[],ARFLOAT[])); - // Turns the problem to real shift-and-invert mode with sigmaRp as shift. - - virtual void SetComplexShiftMode(char partp, ARFLOAT sigmaRp, - ARFLOAT sigmaIp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[],ARFLOAT[]), - ARFB* objAp, - void (ARFB::* MultAxp)(ARFLOAT[],ARFLOAT[])); - // Turns the problem to complex shift-and-invert mode with shift - // defined by sigmaRp and sigmaIp. MultAx is used to obtain eigenvalues. - - - // d.2) Functions that perform all calculations in one step. - - virtual int FindEigenvalues(); - // Determines nev approximated eigenvalues of the given eigen-problem. - - virtual int FindEigenvectors(bool schurp = false); - // Determines nev approximated eigenvectors of the given eigen-problem - // Optionally also determines nev Schur vectors that span the desired - // invariant subspace. - - virtual int FindSchurVectors(); - // Determines nev Schur vectors that span the desired invariant subspace. - // Redefined in ARSymEig. - - - // d.3) Constructors and destructor. - - ARNonSymGenEig() { this->part = 'R'; } - // Short constructor (Does nothing but calling base classes constructors). - - ARNonSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARNonSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigmap, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, ARFLOAT* residp = NULL, - bool ishiftp = true); - // Long constructor (real shift and invert mode). - - ARNonSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), ARFB* objAp, - void (ARFB::* MultAxp)(ARFLOAT[], ARFLOAT[]), ARFB* objBp, - void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), char partp, - ARFLOAT sigmaRp, ARFLOAT sigmaIp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (complex shift and invert mode). - - ARNonSymGenEig(const ARNonSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARNonSymGenEig() { } - // Destructor. - - // e) Operators. - - ARNonSymGenEig& operator=(const ARNonSymGenEig& other); - // Assignment operator. - -}; // class ARNonSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARNonSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARNonSymGenEig:: -Copy(const ARNonSymGenEig& other) -{ - - ARGenEig::Copy(other); - objA = other.objA; - MultAx = other.MultAx; - this->part = other.part; - -} // Copy. - - -template -void ARNonSymGenEig::RecoverEigenvalues() -{ - - int j, ColJ, ColJp1; - ARFLOAT numr, numi, denr, deni; - ARFLOAT* Ax; - - Ax = new ARFLOAT[this->n]; - - for (j=0; jnconv; j++) { - - ColJ = j*this->n; - ColJp1 = ColJ+this->n; - - if (this->EigValI[j] == (ARFLOAT)0.0) { - - // Eigenvalue is real. Computing EigVal = x'(Ax)/x'(Mx). - - (this->objB->*MultAx)(&this->EigVec[ColJ], Ax); - numr = dot(this->n, &this->EigVec[ColJ], 1, Ax, 1); - (this->objB->*(this->MultBx))(&this->EigVec[ColJ], Ax); - denr = dot(this->n, &this->EigVec[ColJ], 1, Ax, 1); - this->EigValR[j] = numr / denr; - - } - else { - - // Eigenvalue is complex. - - // Computing x'(Ax). - - (this->objB->*MultAx)(&this->EigVec[ColJ], Ax); - numr = dot(this->n, &this->EigVec[ColJ], 1, Ax, 1); - numi = dot(this->n, &this->EigVec[ColJp1], 1, Ax, 1); - (this->objB->*MultAx)(&this->EigVec[ColJp1], Ax); - numr = numr + dot(this->n, &this->EigVec[ColJp1], 1, Ax, 1); - numi = -numi + dot(this->n, &this->EigVec[ColJ], 1, Ax, 1); - - // Computing x'(Mx). - - (this->objB->*(this->MultBx))(&this->EigVec[ColJ], Ax); - denr = dot(this->n, &this->EigVec[ColJ], 1, Ax, 1); - deni = dot(this->n, &this->EigVec[ColJp1], 1, Ax, 1); - (this->objB->*(this->MultBx))(&this->EigVec[ColJp1], Ax); - denr = denr + dot(this->n, &this->EigVec[ColJp1], 1, Ax, 1); - deni = -deni + dot(this->n, &this->EigVec[ColJ], 1, Ax, 1); - - // Computing the first eigenvalue of the conjugate pair. - - this->EigValR[j] = (numr*denr+numi*deni) / lapy2(denr, deni); - this->EigValI[j] = (numi*denr-numr*deni) / lapy2(denr, deni); - - // Getting the second eigenvalue of the conjugate pair by taking - // the conjugate of the first. - - this->EigValR[j+1] = this->EigValR[j]; - this->EigValI[j+1] = -this->EigValI[j]; - j++; - - } - - } - - delete[] Ax; - -} // RecoverEigenvalues. - - -template -inline void ARNonSymGenEig:: -SetShiftInvertMode(ARFLOAT sigmaRp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[])) -{ - - this->part = 'R'; - this->objOP = objOPp; - this->MultOPx = MultOPxp; - this->ChangeShift(sigmaRp); - -} // SetShiftInvertMode. - - -template -inline void ARNonSymGenEig:: -SetComplexShiftMode(char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp, - ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objAp, void (ARFB::* MultAxp)(ARFLOAT[], ARFLOAT[])) -{ - - this->objOP = objOPp; - this->MultOPx = MultOPxp; - objA = objAp; - MultAx = MultAxp; - this->part = this->CheckPart(partp); - this->ChangeShift(sigmaRp, sigmaIp); - -} // SetComplexShiftMode. - - -template -inline int ARNonSymGenEig::FindEigenvalues() -{ - - this->nconv = ARStdEig::FindEigenvalues(); - if (this->sigmaI != 0.0) RecoverEigenvalues(); - return this->nconv; - -} // FindEigenvalues. - - -template -inline int ARNonSymGenEig::FindEigenvectors(bool schurp) -{ - - this->nconv = ARStdEig::FindEigenvectors(schurp); - if (this->sigmaI != 0.0) RecoverEigenvalues(); - return this->nconv; - -} // FindEigenvectors. - - -template -int ARNonSymGenEig::FindSchurVectors() -{ - - this->nconv = ARStdEig::FindSchurVectors(); - if (this->sigmaI != 0.0) RecoverEigenvalues(); - return this->nconv; - -} // FindSchurVectors. - - -template -inline ARNonSymGenEig:: -ARNonSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - const std::string& whichp, int ncvp, ARFLOAT tolp, int maxitp, - ARFLOAT* residp, bool ishiftp) - -{ - - this->part = 'R'; // Considering mode = 3 in ChangeShift. - this->NoShift(); - this->DefineParameters(np, nevp, objOPp, MultOPxp, objBp, MultBxp, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARNonSymGenEig:: -ARNonSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - SetShiftInvertMode(sigmap, objOPp, MultOPxp); - this->DefineParameters(np, nevp, objOPp, MultOPxp, objBp, MultBxp, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - - -} // Long constructor (real shift and invert mode). - - -template -inline ARNonSymGenEig:: -ARNonSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objAp, void (ARFB::* MultAxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp, - const std::string& whichp, int ncvp, ARFLOAT tolp, int maxitp, - ARFLOAT* residp, bool ishiftp) - -{ - - SetComplexShiftMode(partp, sigmaRp, sigmaIp, objOPp, - MultOPxp, objAp, MultAxp); - this->DefineParameters(np, nevp, objOPp, MultOPxp, objBp, MultBxp, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARNonSymGenEig& ARNonSymGenEig:: -operator=(const ARNonSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARGNSYM_H - diff --git a/src/external/arpack++/include/argsym.h b/src/external/arpack++/include/argsym.h deleted file mode 100644 index 8191ba0a..00000000 --- a/src/external/arpack++/include/argsym.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARGSym.h. - Arpack++ class ARSymGenEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARGSYM_H -#define ARGSYM_H - -#include -#include -#include "arch.h" -#include "arssym.h" -#include "arrgsym.h" -#include "argeig.h" - -template -class ARSymGenEig: - virtual public ARGenEig, - virtual public ARSymStdEig, - virtual public ARrcSymGenEig { - - public: - - // a) Notation. - - typedef void (ARFB::* TypeBx)(ARFLOAT[], ARFLOAT[]); - - - protected: - - // b) Protected variables: - - ARFB *objA; // Object that has MultAx as a member function. - TypeBx MultAx; // Function that evaluates the product A*x. - - // c) Protected functions: - - virtual void Copy(const ARSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // d) Public functions: - - // d.1) Functions that allow changes in problem parameters. - - void SetShiftInvertMode(ARFLOAT sigmap, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[])); - // Turns problem to shift and invert mode with shift defined by sigmap. - - void SetBucklingMode(ARFLOAT sigmap, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[])); - // Turns problem to buckling mode with shift defined by sigmap. - - void SetCayleyMode(ARFLOAT sigmap, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objAp, void (ARFB::* MultAxp)(ARFLOAT[], ARFLOAT[])); - // Turns problem to Cayley mode with shift defined by sigmap. - - - // d.2) Functions that perform all calculations in one step. - - int FindArnoldiBasis(); - // Determines the Arnoldi basis related to the given problem. - - - // d.3) Constructors and destructor. - - ARSymGenEig() { this->InvertMode = 'S'; } - // Short constructor that does almost nothing. - - ARSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), ARFB* objBp, - void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARSymGenEig(char invertmodep, int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigmap, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, ARFLOAT* residp = NULL, - bool ishiftp = true); - // Long constructor (shift-and-invert and buckling mode). - - ARSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), ARFB* objAp, - void (ARFB::* MultAxp)(ARFLOAT[], ARFLOAT[]), ARFB* objBp, - void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), ARFLOAT sigmap, - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (cayley mode). - - ARSymGenEig(const ARSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARSymGenEig() { } - // Destructor. - - // e) Operators. - - ARSymGenEig& operator=(const ARSymGenEig& other); - // Assignment operator. - -}; // class ARSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARSymGenEig:: -Copy(const ARSymGenEig& other) -{ - - ARGenEig::Copy(other); - objA = other.objA; - MultAx = other.MultAx; - this->InvertMode = other.InvertMode; - -} // Copy. - - -template -void ARSymGenEig:: -SetShiftInvertMode(ARFLOAT sigmap, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[])) -{ - - this->InvertMode = 'S'; - this->objOP = objOPp; - this->MultOPx = MultOPxp; - this->ChangeShift(sigmap); - -} // SetShiftInvertMode. - - -template -void ARSymGenEig:: -SetBucklingMode(ARFLOAT sigmap, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[])) - -{ - - this->InvertMode = 'B'; - this->objOP = objOPp; - this->MultOPx = MultOPxp; - this->ChangeShift(sigmap); - -} // SetBucklingMode. - - -template -void ARSymGenEig:: -SetCayleyMode(ARFLOAT sigmap, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), ARFB* objAp, - void (ARFB::* MultAxp)(ARFLOAT[], ARFLOAT[])) - -{ - - this->InvertMode = 'C'; - this->objOP = objOPp; - this->MultOPx = MultOPxp; - objA = objAp; - MultAx = MultAxp; - this->ChangeShift(sigmap); - -} // SetCayleyMode. - - -template -int ARSymGenEig::FindArnoldiBasis() -{ - - ARFLOAT* temp; - - if (this->mode != 5) { // Using base function if not in Cayley mode. - return ARGenEig::FindArnoldiBasis(); - } - else { - - temp = new ARFLOAT[this->n+1]; - - if (!this->BasisOK) this->Restart(); - - // Changing to auto shift mode. - - if (!this->AutoShift) { - ArpackError::Set(ArpackError::CHANGING_AUTOSHIFT, "FindArnoldiBasis"); - this->AutoShift=true; - } - - // ARPACK main loop. - - while (!this->BasisOK) { - - // Calling Aupp. - - try { this->TakeStep(); } - catch (ArpackError) { - ArpackError(ArpackError::CANNOT_FIND_BASIS, "FindArnoldiBasis"); - delete[] temp; - return 0; - } - - switch (this->ido) { - case -1: - - // Performing y <- B*x for the first time. - - this->ipntr[3] = this->ipntr[2]+this->n; // not a clever idea, but... - (this->objB->*(this->MultBx))(&this->workd[this->ipntr[1]],&this->workd[this->ipntr[3]]); - - case 1: - - // Performing y <- OP*(A+sigma*B)*x, B*x is already available. - - (this->objB->*MultAx)(&this->workd[this->ipntr[1]], temp); - axpy(this->n, this->sigmaR, &this->workd[this->ipntr[3]], 1, temp, 1); - (this->objOP->*(this->MultOPx))(temp, &this->workd[this->ipntr[2]]); - break; - - case 2: - - // Performing y <- B*x. - - (this->objB->*(this->MultBx))(&this->workd[this->ipntr[1]],&this->workd[this->ipntr[2]]); - - } - } - - delete[] temp; - - return this->nconv; - } - -} // FindArnoldiBasis. - - -template -inline ARSymGenEig:: -ARSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - const std::string& whichp, int ncvp, ARFLOAT tolp, int maxitp, - ARFLOAT* residp, bool ishiftp) - -{ - - this->InvertMode = 'S'; - this->NoShift(); - this->DefineParameters(np, nevp, objOPp, MultOPxp, objBp, MultBxp, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARSymGenEig:: -ARSymGenEig(char InvertModep, int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->InvertMode = this->CheckInvertMode(InvertModep); // InvertMode = 'S' or 'B'. - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, objOPp, MultOPxp, objBp, MultBxp, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift-and-invert and buckling mode). - - -template -inline ARSymGenEig:: -ARSymGenEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objAp, void (ARFB::* MultAxp)(ARFLOAT[], ARFLOAT[]), - ARFB* objBp, void (ARFB::* MultBxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - SetCayleyMode(sigmap, objOPp, this->MultOPx, objAp, MultAxp); - this->DefineParameters(np, nevp, objOPp, MultOPxp, objBp, MultBxp, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (cayley mode). - - -template -ARSymGenEig& ARSymGenEig:: -operator=(const ARSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARGSYM_H - diff --git a/src/external/arpack++/include/arhbmat.h b/src/external/arpack++/include/arhbmat.h deleted file mode 100644 index 97c4db40..00000000 --- a/src/external/arpack++/include/arhbmat.h +++ /dev/null @@ -1,407 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARHBMat.h - Matrix template that generates a matrix in CSC format - from a Harwell-Boing matrix file. - - ARPACK authors: - Richard Lehoucq - Kristyn Maschhoff - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#ifndef ARHBMAT_H -#define ARHBMAT_H - -#include -#include -#include -#include -#include -#include "arch.h" -#include "arerror.h" - - -template -class ARhbMatrix { - - private: - - std::string datafile; // Filename. - std::string title; // Title. - std::string name; // Name. - std::string type; // Matrix type. - int m; // Number of rows. - int n; // Number of columns. - int nnz; // Number of nonzero variables. - ARINT* irow; // Row indices. - ARINT* pcol; // Column pointers. - ARTYPE* val; // Numerical values of matrix entries. - - void ConvertDouble(char* num); - - bool ReadEntry(std::ifstream& file, int nval, int fval, int& j, double& val); - - bool ReadEntry(std::ifstream& file, int nval, int fval, int& j, float& val); - - bool ReadEntry(std::ifstream& file, int nval, int fval, - int& j, arcomplex& val); - - bool ReadEntry(std::ifstream& file, int nval, int fval, - int& j, arcomplex& val); - - void ReadFormat(std::ifstream& file, int& n, int& fmt); - - public: - - bool IsDefined() { return (m!=0); } - - bool IsReal() { return (type.size() > 0 && type[0]=='R'); } - - bool IsComplex() { return (type.size() > 0 && type[0]=='C'); } - - bool IsSymmetric() { return (type.size() > 1 && type[1]=='S'); } - - bool IsUnsymmetric() { return (type.size() > 1 && type[1]=='U'); } - - bool IsHermitian() { return (type.size() > 1 && type[1]=='H'); } - - bool IsSkewSymmetric() { return (type.size() > 1 && type[1]=='Z'); } - - const std::string& Filename() { return datafile; } - - const std::string& Title() { return title; } - - const std::string& Name() { return name; } - - const std::string& Type() { return type; } - - int NRows() { return m; } - - int NCols() { return n; } - - int NonZeros() { return nnz; } - - ARINT* RowInd() { return irow; } - - ARINT* ColPtr() { return pcol; } - - ARTYPE* Entries() { return val; } - - void Define(const std::string& filename); - // Function that reads the matrix file. - - ARhbMatrix(); - // Short constructor. - - ARhbMatrix(const std::string& filename) { Define(filename); } - // Long constructor. - - ~ARhbMatrix(); - // Destructor. - -}; // Class ARhbMatrix. - - -// ------------------------------------------------------------------------ // -// ARhbMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARhbMatrix::ConvertDouble(char* num) -{ - - char* pd; - - pd = strchr((char*)num,'D'); - if (pd) *pd = 'E'; - pd = strchr((char*)num,'d'); - if (pd) *pd = 'E'; - - -} // ConvertDouble. - - -template -inline bool ARhbMatrix:: -ReadEntry(std::ifstream& file, int nval, int fval, int& j, double& val) -{ - - char num[81]; - char c; - - if (file.get((char*)num,fval,'\n')) { - ConvertDouble((char*)num); - val = atof((char*)num); - if (!((++j)%nval)) do file.get(c); while (c!='\n'); - return true; - } - else { - return false; - } - -} // ReadEntry (double). - - -template -inline bool ARhbMatrix:: -ReadEntry(std::ifstream& file, int nval, int fval, int& j, float& val) -{ - - double dval; - bool ret; - - ret = ReadEntry(file, nval, fval, j, dval); - val = (float)dval; - return ret; - -} // ReadEntry (float). - - -template -inline bool ARhbMatrix:: -ReadEntry(std::ifstream& file, int nval, int fval, - int& j, arcomplex& val) -{ - - char num[81], img[81]; - char c; - - if (file.get((char*)num,fval,'\n')) { - ConvertDouble((char*)num); - if (!((++j)%nval)) do file.get(c); while (c!='\n'); - if (file.get((char*)img,fval,'\n')) { - ConvertDouble((char*)img); - if (!((++j)%nval)) do file.get(c); while (c!='\n'); - val = arcomplex(atof((char*)num), atof((char*)img)); - return true; - } - else { - return false; - } - } - else { - return false; - } - -} // ReadEntry (arcomplex). - - -template -inline bool ARhbMatrix:: -ReadEntry(std::ifstream& file, int nval, int fval, - int& j, arcomplex& val) -{ - - // I hope one day c++ will have a standard complex - // class, so functions like this can be suppressed. - - char num[81], img[81]; - char c; - - if (file.get((char*)num,fval,'\n')) { - ConvertDouble((char*)num); - if (!((++j)%nval)) do file.get(c); while (c!='\n'); - if (file.get((char*)img,fval,'\n')) { - ConvertDouble((char*)img); - if (!((++j)%nval)) do file.get(c); while (c!='\n'); - val = arcomplex(atof((char*)num), atof((char*)img)); - return true; - } - else { - return false; - } - } - else { - return false; - } - -} // ReadEntry (arcomplex). - - -template -void ARhbMatrix::ReadFormat(std::ifstream& file, int& n, int& fmt) -{ - - char c; - - do file.get(c); while ((c != '(') && (c!='\n')); - file >> n; - file.get(c); - while ((c!='I') && (c!='i') && (c!='E') && (c!='e') && - (c!='D') && (c!='d') && (c!='\n')) { - do file.get(c); while ((c != ',') && (c!='\n')); - file >> n; - file.get(c); - } - if ((c==')')||(c=='\n')) { // Reading error! - fmt = 0; - } - else { - file >> fmt; - } - -} // ReadFormat. - - -template -void ARhbMatrix::Define(const std::string& filename) -{ - - // Declaring variables. - - int i, j; - int lintot, linptr, linind, linval, linrhs; - int npcol, fpcol, nirow, firow, nval, fval; - char c; - char num[81]; - char titlechar[73]; - char namechar[9]; - char typechar[4]; - ARTYPE value; - - // Opening file. - - datafile = filename; - std::ifstream file(datafile.c_str()); - - if (!file) { - throw ArpackError(ArpackError::CANNOT_OPEN_FILE, "ARhbMatrix"); - } - - // Reading the first line. - - file.get((char*)titlechar,73,'\n'); - title = std::string(titlechar); - file.get((char*)namechar,9,'\n'); - name = std::string(namechar); - do file.get(c); while (c!='\n'); - - // Reading the second line. - - file >> lintot >> linptr >> linind >> linval >> linrhs; - do file.get(c); while (c!='\n'); - - if ((linptr < 1) || (linind < 1)) { - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARhbMatrix"); - } - - // Reading the third line. - - file.get((char*)typechar,4,'\n'); - type = std::string(typechar); - file >> m >> n >> nnz; - do file.get(c); while (c!='\n'); - - if ( (type.size()<3) || ((type[0] != 'R') && (type[0] != 'C')) || (type[2] != 'A')) { - throw ArpackError(ArpackError::WRONG_MATRIX_TYPE, "ARhbMatrix"); - } - else if ((m < 1) || (n < 1) || (nnz < 1)) { - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARhbMatrix"); - } - - // Reading the fourth line. - - ReadFormat(file, npcol, fpcol); - ReadFormat(file, nirow, firow); - ReadFormat(file, nval, fval); - do file.get(c); while (c!='\n'); - if ((fpcol<1) || (firow<1) || (fval<1)) { - throw ArpackError(ArpackError::WRONG_DATA_TYPE, "ARhbMatrix"); - } - - // Skipping the fifth line. - - if (linrhs) { - do file.get(c); while (c!='\n'); - ArpackError(ArpackError::RHS_IGNORED, "ARhbMatrix"); - } - - // Reading column pointers. - - pcol = new ARINT[n+1]; - fpcol++; - i = 0; - while ((i <= n) && (file.get((char*)num,fpcol,'\n'))) { - pcol[i++] = atoi((char*)num)-1; - if (!(i%npcol)) do file.get(c); while (c!='\n'); - } - if (i%npcol) do file.get(c); while (c!='\n'); - - if (i <= n) { - throw ArpackError(ArpackError::UNEXPECTED_EOF, "ARhbMatrix"); - } - - // Reading row indices. - - irow = new ARINT[nnz]; - firow++; - i = 0; - while ((i < nnz) && (file.get((char*)num,firow,'\n'))) { - irow[i++] = atoi((char*)num)-1; - if (!(i%nirow)) do file.get(c); while (c!='\n'); - } - if (i%nirow) do file.get(c); while (c!='\n'); - - if (i < nnz) { - throw ArpackError(ArpackError::UNEXPECTED_EOF, "ARhbMatrix"); - } - - // Reading matrix elements. - - fval++; - val = new ARTYPE[nnz]; - i = 0; - j = 0; - while ((i < nnz) && (ReadEntry(file, nval, fval, j, value))) { - val[i++] = value; - } - if (j%nval) do file.get(c); while (c!='\n'); - - if (i < nnz) { - throw ArpackError(ArpackError::UNEXPECTED_EOF, "ARhbMatrix"); - } - - // Closing file and reporting success. - - file.close(); - -} // Define. - - -template -ARhbMatrix::ARhbMatrix() -{ - - m = n = nnz = 0; - title[0]= '\0'; - name[0] = '\0'; - type[0] = '\0'; - pcol = NULL; - irow = NULL; - val = NULL; - -} // Short constructor. - - -template -ARhbMatrix::~ARhbMatrix() -{ - - if (irow != NULL) delete[] irow; - if (pcol != NULL) delete[] pcol; - if (val != NULL) delete[] val; - -} // Destructor. - - -#endif // ARHBMAT_H - diff --git a/src/external/arpack++/include/arlcomp.h b/src/external/arpack++/include/arlcomp.h deleted file mode 100644 index 8b9b5fbc..00000000 --- a/src/external/arpack++/include/arlcomp.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLComp.h. - ALTERED copy of dcomplex.h and scomplex.h (from SuperLU package). - Structure complex was renamed to lscomplex. - Structure doublecomplex was renamed to ldcomplex. -*/ - -/* - * -- SuperLU routine (version 2.0) -- - * Univ. of California Berkeley, Xerox Palo Alto Research Center, - * and Lawrence Berkeley National Lab. - * November 15, 1997 - * - * - */ -#ifndef __SUPERLU_DCOMPLEX /* allow multiple inclusions */ -#define __SUPERLU_DCOMPLEX - -/* - * This header file is to be included in source files z*.c - */ -#ifndef DCOMPLEX_INCLUDE -#define DCOMPLEX_INCLUDE - -typedef struct { double r, i; } ldcomplex; - - -/* Macro definitions */ - -/*! \brief Complex Addition c = a + b */ -#define z_add(c, a, b) { (c)->r = (a)->r + (b)->r; \ - (c)->i = (a)->i + (b)->i; } - -/*! \brief Complex Subtraction c = a - b */ -#define z_sub(c, a, b) { (c)->r = (a)->r - (b)->r; \ - (c)->i = (a)->i - (b)->i; } - -/*! \brief Complex-Double Multiplication */ -#define zd_mult(c, a, b) { (c)->r = (a)->r * (b); \ - (c)->i = (a)->i * (b); } - -/*! \brief Complex-Complex Multiplication */ -#define zz_mult(c, a, b) { \ - double cr, ci; \ - cr = (a)->r * (b)->r - (a)->i * (b)->i; \ - ci = (a)->i * (b)->r + (a)->r * (b)->i; \ - (c)->r = cr; \ - (c)->i = ci; \ - } - -#define zz_conj(a, b) { \ - (a)->r = (b)->r; \ - (a)->i = -((b)->i); \ - } - -/*! \brief Complex equality testing */ -#define z_eq(a, b) ( (a)->r == (b)->r && (a)->i == (b)->i ) - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Prototypes for functions in dcomplex.c */ -void z_div(ldcomplex *, ldcomplex *, ldcomplex *); -double z_abs(ldcomplex *); /* exact */ -double z_abs1(ldcomplex *); /* approximate */ -void z_exp(ldcomplex *, ldcomplex *); -void d_cnjg(ldcomplex *r, ldcomplex *z); -double d_imag(ldcomplex *); -ldcomplex z_sgn(ldcomplex *); -ldcomplex z_sqrt(ldcomplex *); - - -#ifdef __cplusplus - } -#endif - -#endif - -#endif /* __SUPERLU_DCOMPLEX */ - - -#ifndef __SUPERLU_SCOMPLEX /* allow multiple inclusions */ -#define __SUPERLU_SCOMPLEX - -/* - * This header file is to be included in source files c*.c - */ -#ifndef SCOMPLEX_INCLUDE -#define SCOMPLEX_INCLUDE - -typedef struct { float r, i; } lscomplex; - - -/* Macro definitions */ - -/*! \brief Complex Addition c = a + b */ -#define c_add(c, a, b) { (c)->r = (a)->r + (b)->r; \ - (c)->i = (a)->i + (b)->i; } - -/*! \brief Complex Subtraction c = a - b */ -#define c_sub(c, a, b) { (c)->r = (a)->r - (b)->r; \ - (c)->i = (a)->i - (b)->i; } - -/*! \brief Complex-Double Multiplication */ -#define cs_mult(c, a, b) { (c)->r = (a)->r * (b); \ - (c)->i = (a)->i * (b); } - -/*! \brief Complex-Complex Multiplication */ -#define cc_mult(c, a, b) { \ - float cr, ci; \ - cr = (a)->r * (b)->r - (a)->i * (b)->i; \ - ci = (a)->i * (b)->r + (a)->r * (b)->i; \ - (c)->r = cr; \ - (c)->i = ci; \ - } - -#define cc_conj(a, b) { \ - (a)->r = (b)->r; \ - (a)->i = -((b)->i); \ - } - -/*! \brief Complex equality testing */ -#define c_eq(a, b) ( (a)->r == (b)->r && (a)->i == (b)->i ) - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Prototypes for functions in scomplex.c */ -void c_div(lscomplex *, lscomplex *, lscomplex *); -double c_abs(lscomplex *); /* exact */ -double c_abs1(lscomplex *); /* approximate */ -void c_exp(lscomplex *, lscomplex *); -void r_cnjg(lscomplex *, lscomplex *); -double r_imag(lscomplex *); -lscomplex c_sgn(lscomplex *); -lscomplex c_sqrt(lscomplex *); - - -#ifdef __cplusplus - } -#endif - -#endif - -#endif /* __SUPERLU_SCOMPLEX */ - diff --git a/src/external/arpack++/include/arlgcomp.h b/src/external/arpack++/include/arlgcomp.h deleted file mode 100644 index b4692164..00000000 --- a/src/external/arpack++/include/arlgcomp.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLGComp.h. - Arpack++ class ARluCompGenEig definition - (superlu version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARLGCOMP_H -#define ARLGCOMP_H - -#include -#include -#include "arch.h" -#include "arlnsmat.h" -#include "arlnspen.h" -#include "arrseig.h" -#include "argcomp.h" - - -template -class ARluCompGenEig: - public virtual - ARCompGenEig, ARFLOAT >, - ARluNonSymPencil, ARFLOAT > > { - - private: - - // a) Data structure used to store matrices. - - ARluNonSymPencil, ARFLOAT > Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluCompGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(arcomplex sigmap); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(arcomplex sigmap); - - // c.2) Constructors and destructor. - - ARluCompGenEig() { } - // Short constructor. - - ARluCompGenEig(int nevp, ARluNonSymMatrix, ARFLOAT>& A, - ARluNonSymMatrix, ARFLOAT>& B, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluCompGenEig(int nevp, ARluNonSymMatrix, ARFLOAT>& A, - ARluNonSymMatrix, ARFLOAT>& B, - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluCompGenEig(const ARluCompGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluCompGenEig() { } - - // d) Operators. - - ARluCompGenEig& operator=(const ARluCompGenEig& other); - // Assignment operator. - -}; // class ARluCompGenEig. - - -// ------------------------------------------------------------------------ // -// ARluCompGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluCompGenEig:: -Copy(const ARluCompGenEig& other) -{ - - ARCompGenEig, ARFLOAT >, - ARluNonSymPencil, ARFLOAT> >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - if (this->mode > 2) this->objOP->FactorAsB(this->sigmaR); - -} // Copy. - - -template -inline void ARluCompGenEig::ChangeShift(arcomplex sigmap) -{ - - this->objOP->FactorAsB(sigmap); - ARrcStdEig >::ChangeShift(sigmap); - -} // ChangeShift. - - -template -inline void ARluCompGenEig::SetRegularMode() -{ - - ARStdEig, - ARluNonSymPencil, ARFLOAT> >:: - SetRegularMode(&Pencil, - &ARluNonSymPencil, ARFLOAT>::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluCompGenEig:: -SetShiftInvertMode(arcomplex sigmap) -{ - - ARCompGenEig, ARFLOAT>, - ARluNonSymPencil, ARFLOAT> >:: - SetShiftInvertMode(sigmap, &Pencil, - &ARluNonSymPencil,ARFLOAT>::MultInvAsBv); - -} // SetShiftInvertMode. - - -template -inline ARluCompGenEig:: -ARluCompGenEig(int nevp, ARluNonSymMatrix, ARFLOAT>& A, - ARluNonSymMatrix, ARFLOAT>& B, const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, - arcomplex* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARluNonSymPencil, ARFLOAT>::MultInvBAv, - &Pencil, - &ARluNonSymPencil, ARFLOAT>::MultBv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluCompGenEig:: -ARluCompGenEig(int nevp, ARluNonSymMatrix, ARFLOAT>& A, - ARluNonSymMatrix, ARFLOAT>& B, - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARluNonSymPencil, ARFLOAT>::MultInvAsBv, - &Pencil, - &ARluNonSymPencil, ARFLOAT>::MultBv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - SetShiftInvertMode(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluCompGenEig& ARluCompGenEig:: -operator=(const ARluCompGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLGCOMP_H diff --git a/src/external/arpack++/include/arlgnsym.h b/src/external/arpack++/include/arlgnsym.h deleted file mode 100644 index ea9b53e2..00000000 --- a/src/external/arpack++/include/arlgnsym.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLGNSym.h. - Arpack++ class ARluNonSymGenEig definition - (SuperLU version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARLGNSYM_H -#define ARLGNSYM_H - -#include -#include -#include "arch.h" -#include "arlnsmat.h" -#include "arlnspen.h" -#include "argnsym.h" - - -template -class ARluNonSymGenEig: - public virtual ARNonSymGenEig, - ARluNonSymPencil > { - - protected: - - // a) Data structure used to store matrices. - - ARluNonSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluNonSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp = 0.0); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetComplexShiftMode(char partp,ARFLOAT sigmaRp,ARFLOAT sigmaIp); - - // c.2) Constructors and destructor. - - ARluNonSymGenEig() { } - // Short constructor. - - ARluNonSymGenEig(int nevp, ARluNonSymMatrix& A, - ARluNonSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluNonSymGenEig(int nevp, ARluNonSymMatrix& A, - ARluNonSymMatrix& B, ARFLOAT sigma, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (real shift and invert mode). - - ARluNonSymGenEig(int nevp, ARluNonSymMatrix& A, - ARluNonSymMatrix& B, char partp, - ARFLOAT sigmaRp, ARFLOAT sigmaIp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (complex shift and invert mode). - - ARluNonSymGenEig(const ARluNonSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluNonSymGenEig& operator=(const ARluNonSymGenEig& other); - // Assignment operator. - -}; // class ARluNonSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluNonSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymGenEig:: -Copy(const ARluNonSymGenEig& other) -{ - - ARNonSymGenEig, - ARluNonSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - if (this->mode > 2) { - if (this->sigmaI == 0.0) { - this->objOP->FactorAsB(this->sigmaR); - } - else { - this->objOP->FactorAsB(this->sigmaR, this->sigmaI, this->part); - } - } - -} // Copy. - - -template -inline void ARluNonSymGenEig:: -ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - if (sigmaIp == 0.0) { - this->objOP->FactorAsB(sigmaRp); - } - else { - this->objOP->FactorAsB(sigmaRp, sigmaIp, this->part); - } - ARrcNonSymGenEig::ChangeShift(sigmaRp, sigmaIp); - -} // ChangeShift. - - -template -inline void ARluNonSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARluNonSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluNonSymGenEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARNonSymGenEig, - ARluNonSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, - &ARluNonSymPencil::MultInvAsBv); - -} // SetShiftInvertMode. - - -template -inline void ARluNonSymGenEig:: -SetComplexShiftMode(char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - ARNonSymGenEig, - ARluNonSymPencil >:: - SetComplexShiftMode(partp, sigmaRp, sigmaIp, &Pencil, - &ARluNonSymPencil::MultInvAsBv, - &Pencil, &ARluNonSymPencil::MultAv); - -} // SetComplexShiftMode. - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARluNonSymMatrix& A, - ARluNonSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARluNonSymPencil::MultInvBAv, &Pencil, - &ARluNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARluNonSymMatrix& A, - ARluNonSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARluNonSymPencil::MultInvAsBv, &Pencil, - &ARluNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - SetShiftInvertMode(sigmap); - -} // Long constructor (real shift and invert mode). - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARluNonSymMatrix& A, - ARluNonSymMatrix& B, - char partp, ARFLOAT sigmaRp, - ARFLOAT sigmaIp, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARluNonSymPencil::MultInvAsBv, &Pencil, - &ARluNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - SetComplexShiftMode(partp, sigmaRp, sigmaIp); - -} // Long constructor (complex shift and invert mode). - - -template -ARluNonSymGenEig& ARluNonSymGenEig:: -operator=(const ARluNonSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLGNSYM_H diff --git a/src/external/arpack++/include/arlgsym.h b/src/external/arpack++/include/arlgsym.h deleted file mode 100644 index 00f4a73c..00000000 --- a/src/external/arpack++/include/arlgsym.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLGSym.h. - Arpack++ class ARluSymGenEig definition - (SuperLU version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Kristi Maschhoff - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARLGSYM_H -#define ARLGSYM_H - -#include -#include -#include "arch.h" -#include "arlsmat.h" -#include "arlspen.h" -#include "argsym.h" - - -template -class ARluSymGenEig: - public virtual ARSymGenEig, - ARluSymPencil > { - - private: - - // a) Data structure used to store matrices. - - ARluSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmap); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetBucklingMode(ARFLOAT sigmap); - - virtual void SetCayleyMode(ARFLOAT sigmap); - - // c.2) Constructors and destructor. - - ARluSymGenEig() { } - // Short constructor. - - ARluSymGenEig(int nevp, ARluSymMatrix& A, - ARluSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymGenEig(char InvertModep, int nevp, ARluSymMatrix& A, - ARluSymMatrix& B, ARFLOAT sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert, buckling and Cayley modes). - - ARluSymGenEig(const ARluSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluSymGenEig& operator=(const ARluSymGenEig& other); - // Assignment operator. - -}; // class ARluSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymGenEig:: -Copy(const ARluSymGenEig& other) -{ - - ARSymGenEig, - ARluSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - if (this->mode > 2) this->objOP->FactorAsB(this->sigmaR); - -} // Copy. - - -template -inline void ARluSymGenEig::ChangeShift(ARFLOAT sigmap) -{ - - this->objOP->FactorAsB(sigmap); - ARrcSymGenEig::ChangeShift(sigmap); - -} // ChangeShift. - - -template -inline void ARluSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARluSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluSymGenEig:: -SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARluSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, &ARluSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARluSymPencil::MultBv); - -} // SetShiftInvertMode. - - -template -inline void ARluSymGenEig:: -SetBucklingMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARluSymPencil >:: - SetBucklingMode(sigmap, &Pencil, &ARluSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARluSymPencil::MultAv); - -} // SetBucklingMode. - - -template -inline void ARluSymGenEig:: -SetCayleyMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARluSymPencil >:: - SetCayleyMode(sigmap, &Pencil, &ARluSymPencil::MultInvAsBv, - &Pencil, &ARluSymPencil::MultAv); - this->ChangeMultBx(&Pencil, &ARluSymPencil::MultBv); - -} // SetCayleyMode. - - -template -inline ARluSymGenEig:: -ARluSymGenEig(int nevp, ARluSymMatrix& A, - ARluSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->InvertMode = 'S'; - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARluSymPencil::MultInvBAv, &Pencil, - &ARluSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymGenEig:: -ARluSymGenEig(char InvertModep, int nevp, ARluSymMatrix& A, - ARluSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARluSymPencil::MultInvAsBv, &Pencil, - &ARluSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - this->InvertMode = this->CheckInvertMode(InvertModep); - switch (this->InvertMode) { - case 'B': // Buckling mode. - this->ChangeMultBx(&Pencil, &ARluSymPencil::MultAv); - case 'S': // Shift and invert mode. - ChangeShift(sigmap); - break; - case 'C': // Cayley mode. - SetCayleyMode(sigmap); - } - -} // Long constructor (shift and invert, buckling and Cayley modes). - - -template -ARluSymGenEig& ARluSymGenEig:: -operator=(const ARluSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLGSYM_H diff --git a/src/external/arpack++/include/arlnames.h b/src/external/arpack++/include/arlnames.h deleted file mode 100644 index 1664f173..00000000 --- a/src/external/arpack++/include/arlnames.h +++ /dev/null @@ -1,464 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLNames.h. - Unaltered copy of Cnames.h (from SuperLU package). -*/ - -/* - * -- SuperLU routine (version 2.0) -- - * Univ. of California Berkeley, Xerox Palo Alto Research Center, - * and Lawrence Berkeley National Lab. - * November 1, 1997 - * - */ -#ifndef __SUPERLU_CNAMES /* allow multiple inclusions */ -#define __SUPERLU_CNAMES - -/* - * These macros define how C routines will be called. ADD_ assumes that - * they will be called by fortran, which expects C routines to have an - * underscore postfixed to the name (Suns, and the Intel expect this). - * NOCHANGE indicates that fortran will be calling, and that it expects - * the name called by fortran to be identical to that compiled by the C - * (RS6K's do this). UPCASE says it expects C routines called by fortran - * to be in all upcase (CRAY wants this). - */ - -#define ADD_ 0 -#define ADD__ 1 -#define NOCHANGE 2 -#define UPCASE 3 -#define OLD_CRAY 4 -#define C_CALL 5 - -#ifdef UpCase -#define F77_CALL_C UPCASE -#endif - -#ifdef NoChange -#define F77_CALL_C NOCHANGE -#endif - -#ifdef Add_ -#define F77_CALL_C ADD_ -#endif - -#ifdef Add__ -#define F77_CALL_C ADD__ -#endif - -#ifdef _CRAY -#define F77_CALL_C OLD_CRAY -#endif - -/* Default */ -#ifndef F77_CALL_C -#define F77_CALL_C ADD_ -#endif - - -#if (F77_CALL_C == ADD_) -/* - * These defines set up the naming scheme required to have a fortran 77 - * routine call a C routine - * No redefinition necessary to have following Fortran to C interface: - * FORTRAN CALL C DECLARATION - * call dgemm(...) void dgemm_(...) - * - * This is the default. - */ - -#endif - -#if (F77_CALL_C == ADD__) -/* - * These defines set up the naming scheme required to have a fortran 77 - * routine call a C routine - * for following Fortran to C interface: - * FORTRAN CALL C DECLARATION - * call dgemm(...) void dgemm__(...) - */ -/* BLAS */ -#define sswap_ sswap__ -#define saxpy_ saxpy__ -#define sasum_ sasum__ -#define isamax_ isamax__ -#define scopy_ scopy__ -#define sscal_ sscal__ -#define sger_ sger__ -#define snrm2_ snrm2__ -#define ssymv_ ssymv__ -#define sdot_ sdot__ -#define saxpy_ saxpy__ -#define ssyr2_ ssyr2__ -#define srot_ srot__ -#define sgemv_ sgemv__ -#define strsv_ strsv__ -#define sgemm_ sgemm__ -#define strsm_ strsm__ - -#define dswap_ dswap__ -#define daxpy_ daxpy__ -#define dasum_ dasum__ -#define idamax_ idamax__ -#define dcopy_ dcopy__ -#define dscal_ dscal__ -#define dger_ dger__ -#define dnrm2_ dnrm2__ -#define dsymv_ dsymv__ -#define ddot_ ddot__ -#define dsyr2_ dsyr2__ -#define drot_ drot__ -#define dgemv_ dgemv__ -#define dtrsv_ dtrsv__ -#define dgemm_ dgemm__ -#define dtrsm_ dtrsm__ - -#define cswap_ cswap__ -#define caxpy_ caxpy__ -#define scasum_ scasum__ -#define icamax_ icamax__ -#define ccopy_ ccopy__ -#define cscal_ cscal__ -#define scnrm2_ scnrm2__ -#define caxpy_ caxpy__ -#define cgemv_ cgemv__ -#define ctrsv_ ctrsv__ -#define cgemm_ cgemm__ -#define ctrsm_ ctrsm__ -#define cgerc_ cgerc__ -#define chemv_ chemv__ -#define cher2_ cher2__ - -#define zswap_ zswap__ -#define zaxpy_ zaxpy__ -#define dzasum_ dzasum__ -#define izamax_ izamax__ -#define zcopy_ zcopy__ -#define zscal_ zscal__ -#define dznrm2_ dznrm2__ -#define zaxpy_ zaxpy__ -#define zgemv_ zgemv__ -#define ztrsv_ ztrsv__ -#define zgemm_ zgemm__ -#define ztrsm_ ztrsm__ -#define zgerc_ zgerc__ -#define zhemv_ zhemv__ -#define zher2_ zher2__ - -/* LAPACK */ -#define dlamch_ dlamch__ -#define slamch_ slamch__ -#define xerbla_ xerbla__ -#define lsame_ lsame__ -#define dlacon_ dlacon__ -#define slacon_ slacon__ -#define icmax1_ icmax1__ -#define scsum1_ scsum1__ -#define clacon_ clacon__ -#define dzsum1_ dzsum1__ -#define izmax1_ izmax1__ -#define zlacon_ zlacon__ - -/* Fortran interface */ -#define c_bridge_dgssv_ c_bridge_dgssv__ -#define c_fortran_sgssv_ c_fortran_sgssv__ -#define c_fortran_dgssv_ c_fortran_dgssv__ -#define c_fortran_cgssv_ c_fortran_cgssv__ -#define c_fortran_zgssv_ c_fortran_zgssv__ -#endif - -#if (F77_CALL_C == UPCASE) -/* - * These defines set up the naming scheme required to have a fortran 77 - * routine call a C routine - * following Fortran to C interface: - * FORTRAN CALL C DECLARATION - * call dgemm(...) void DGEMM(...) - */ -/* BLAS */ -#define sswap_ SSWAP -#define saxpy_ SAXPY -#define sasum_ SASUM -#define isamax_ ISAMAX -#define scopy_ SCOPY -#define sscal_ SSCAL -#define sger_ SGER -#define snrm2_ SNRM2 -#define ssymv_ SSYMV -#define sdot_ SDOT -#define saxpy_ SAXPY -#define ssyr2_ SSYR2 -#define srot_ SROT -#define sgemv_ SGEMV -#define strsv_ STRSV -#define sgemm_ SGEMM -#define strsm_ STRSM - -#define dswap_ DSWAP -#define daxpy_ DAXPY -#define dasum_ DASUM -#define idamax_ IDAMAX -#define dcopy_ DCOPY -#define dscal_ DSCAL -#define dger_ DGER -#define dnrm2_ DNRM2 -#define dsymv_ DSYMV -#define ddot_ DDOT -#define dsyr2_ DSYR2 -#define drot_ DROT -#define dgemv_ DGEMV -#define dtrsv_ DTRSV -#define dgemm_ DGEMM -#define dtrsm_ DTRSM - -#define cswap_ CSWAP -#define caxpy_ CAXPY -#define scasum_ SCASUM -#define icamax_ ICAMAX -#define ccopy_ CCOPY -#define cscal_ CSCAL -#define scnrm2_ SCNRM2 -#define cgemv_ CGEMV -#define ctrsv_ CTRSV -#define cgemm_ CGEMM -#define ctrsm_ CTRSM -#define cgerc_ CGERC -#define chemv_ CHEMV -#define cher2_ CHER2 - -#define zswap_ ZSWAP -#define zaxpy_ ZAXPY -#define dzasum_ DZASUM -#define izamax_ IZAMAX -#define zcopy_ ZCOPY -#define zscal_ ZSCAL -#define dznrm2_ DZNRM2 -#define zgemv_ ZGEMV -#define ztrsv_ ZTRSV -#define zgemm_ ZGEMM -#define ztrsm_ ZTRSM -#define zgerc_ ZGERC -#define zhemv_ ZHEMV -#define zher2_ ZHER2 - -/* LAPACK */ -#define dlamch_ DLAMCH -#define slamch_ SLAMCH -#define xerbla_ XERBLA -#define lsame_ LSAME -#define dlacon_ DLACON -#define slacon_ SLACON -#define icmax1_ ICMAX1 -#define scsum1_ SCSUM1 -#define clacon_ CLACON -#define dzsum1_ DZSUM1 -#define izmax1_ IZMAX1 -#define zlacon_ ZLACON - -/* Fortran interface */ -#define c_bridge_dgssv_ C_BRIDGE_DGSSV -#define c_fortran_sgssv_ C_FORTRAN_SGSSV -#define c_fortran_dgssv_ C_FORTRAN_DGSSV -#define c_fortran_cgssv_ C_FORTRAN_CGSSV -#define c_fortran_zgssv_ C_FORTRAN_ZGSSV -#endif - - -#if (F77_CALL_C == OLD_CRAY) -/* - * These defines set up the naming scheme required to have a fortran 77 - * routine call a C routine - * following Fortran to C interface: - * FORTRAN CALL C DECLARATION - * call dgemm(...) void SGEMM(...) - */ -/* BLAS */ -#define sswap_ SSWAP -#define saxpy_ SAXPY -#define sasum_ SASUM -#define isamax_ ISAMAX -#define scopy_ SCOPY -#define sscal_ SSCAL -#define sger_ SGER -#define snrm2_ SNRM2 -#define ssymv_ SSYMV -#define sdot_ SDOT -#define ssyr2_ SSYR2 -#define srot_ SROT -#define sgemv_ SGEMV -#define strsv_ STRSV -#define sgemm_ SGEMM -#define strsm_ STRSM - -#define dswap_ SSWAP -#define daxpy_ SAXPY -#define dasum_ SASUM -#define idamax_ ISAMAX -#define dcopy_ SCOPY -#define dscal_ SSCAL -#define dger_ SGER -#define dnrm2_ SNRM2 -#define dsymv_ SSYMV -#define ddot_ SDOT -#define dsyr2_ SSYR2 -#define drot_ SROT -#define dgemv_ SGEMV -#define dtrsv_ STRSV -#define dgemm_ SGEMM -#define dtrsm_ STRSM - -#define cswap_ CSWAP -#define caxpy_ CAXPY -#define scasum_ SCASUM -#define icamax_ ICAMAX -#define ccopy_ CCOPY -#define cscal_ CSCAL -#define scnrm2_ SCNRM2 -#define caxpy_ CAXPY -#define cgemv_ CGEMV -#define ctrsv_ CTRSV -#define cgemm_ CGEMM -#define ctrsm_ CTRSM -#define cgerc_ CGERC -#define chemv_ CHEMV -#define cher2_ CHER2 - -#define zswap_ ZSWAP -#define zaxpy_ ZAXPY -#define dzasum_ DZASUM -#define izamax_ IZAMAX -#define zcopy_ ZCOPY -#define zscal_ ZSCAL -#define dznrm2_ DZNRM2 -#define zgemv_ ZGEMV -#define ztrsv_ ZTRSV -#define zgemm_ ZGEMM -#define ztrsm_ ZTRSM -#define zgerc_ ZGERC -#define zhemv_ ZHEMV -#define zher2_ ZHER2 - -/* LAPACK */ -#define dlamch_ DLAMCH -#define slamch_ SLAMCH -#define xerbla_ XERBLA -#define lsame_ LSAME -#define dlacon_ DLACON -#define slacon_ SLACON -#define icmax1_ ICMAX1 -#define scsum1_ SCSUM1 -#define clacon_ CLACON -#define dzsum1_ DZSUM1 -#define izmax1_ IZMAX1 -#define zlacon_ ZLACON - -/* Fortran interface */ -#define c_bridge_dgssv_ C_BRIDGE_DGSSV -#define c_fortran_sgssv_ C_FORTRAN_SGSSV -#define c_fortran_dgssv_ C_FORTRAN_DGSSV -#define c_fortran_cgssv_ C_FORTRAN_CGSSV -#define c_fortran_zgssv_ C_FORTRAN_ZGSSV -#endif - - -#if (F77_CALL_C == NOCHANGE) -/* - * These defines set up the naming scheme required to have a fortran 77 - * routine call a C routine - * for following Fortran to C interface: - * FORTRAN CALL C DECLARATION - * call dgemm(...) void dgemm(...) - */ -/* BLAS */ -#define sswap_ sswap -#define saxpy_ saxpy -#define sasum_ sasum -#define isamax_ isamax -#define scopy_ scopy -#define sscal_ sscal -#define sger_ sger -#define snrm2_ snrm2 -#define ssymv_ ssymv -#define sdot_ sdot -#define saxpy_ saxpy -#define ssyr2_ ssyr2 -#define srot_ srot -#define sgemv_ sgemv -#define strsv_ strsv -#define sgemm_ sgemm -#define strsm_ strsm - -#define dswap_ dswap -#define daxpy_ daxpy -#define dasum_ dasum -#define idamax_ idamax -#define dcopy_ dcopy -#define dscal_ dscal -#define dger_ dger -#define dnrm2_ dnrm2 -#define dsymv_ dsymv -#define ddot_ ddot -#define dsyr2_ dsyr2 -#define drot_ drot -#define dgemv_ dgemv -#define dtrsv_ dtrsv -#define dgemm_ dgemm -#define dtrsm_ dtrsm - -#define cswap_ cswap -#define caxpy_ caxpy -#define scasum_ scasum -#define icamax_ icamax -#define ccopy_ ccopy -#define cscal_ cscal -#define scnrm2_ scnrm2 -#define cgemv_ cgemv -#define ctrsv_ ctrsv -#define cgemm_ cgemm -#define ctrsm_ ctrsm -#define cgerc_ cgerc -#define chemv_ chemv -#define cher2_ cher2 - -#define zswap_ zswap -#define zaxpy_ zaxpy -#define dzasum_ dzasum -#define izamax_ izamax -#define zcopy_ zcopy -#define zscal_ zscal -#define dznrm2_ dznrm2 -#define zgemv_ zgemv -#define ztrsv_ ztrsv -#define zgemm_ zgemm -#define ztrsm_ ztrsm -#define zgerc_ zgerc -#define zhemv_ zhemv -#define zher2_ zher2 - -/* LAPACK */ -#define dlamch_ dlamch -#define slamch_ slamch -#define xerbla_ xerbla -#define lsame_ lsame -#define dlacon_ dlacon -#define slacon_ slacon -#define icmax1_ icmax1 -#define scsum1_ scsum1 -#define clacon_ clacon -#define dzsum1_ dzsum1 -#define izmax1_ izmax1 -#define zlacon_ zlacon - -/* Fortran interface */ -#define c_bridge_dgssv_ c_bridge_dgssv -#define c_fortran_sgssv_ c_fortran_sgssv -#define c_fortran_dgssv_ c_fortran_dgssv -#define c_fortran_cgssv_ c_fortran_cgssv -#define c_fortran_zgssv_ c_fortran_zgssv -#endif - - -#endif /* __SUPERLU_CNAMES */ diff --git a/src/external/arpack++/include/arlnsmat.h b/src/external/arpack++/include/arlnsmat.h deleted file mode 100644 index 07a547f9..00000000 --- a/src/external/arpack++/include/arlnsmat.h +++ /dev/null @@ -1,753 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLNSMat.h. - Arpack++ class ARluNonSymMatrix definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "arlnspen.h" - -#ifndef ARLNSMAT_H -#define ARLNSMAT_H - -#include -#include -#include "arch.h" -#include "armat.h" -#include "arhbmat.h" -#include "arerror.h" -#include "blas1c.h" -#include "superluc.h" -#include "arlspdef.h" -#include "arlutil.h" - -template class ARluNonSymPencil; - -template -class ARluNonSymMatrix: public ARMatrix { - - friend class ARluNonSymPencil; - friend class ARluNonSymPencil; - - protected: - - bool factored; - int order; - int nnz; - int* irow; - int* pcol; - int* permc; - int* permr; - double threshold; - ARTYPE* a; - SuperMatrix A; - SuperMatrix L; - SuperMatrix U; - ARhbMatrix mat; - SuperLUStat_t stat; - - bool DataOK(); - - virtual void Copy(const ARluNonSymMatrix& other); - - void ClearMem(); - - void SubtractAsI(ARTYPE sigma, NCformat& A, NCformat& AsI); - - public: - - int nzeros() { return nnz; } - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultMtv(ARTYPE* v, ARTYPE* w); - - void MultMtMv(ARTYPE* v, ARTYPE* w); - - void MultMMtv(ARTYPE* v, ARTYPE* w); - - void Mult0MMt0v(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, double thresholdp = 0.1, - int orderp = 1, bool check = true); // Square matrix. - - void DefineMatrix(int mp, int np, int nnzp, ARTYPE* ap, - int* irowp, int* pcolp); // Rectangular matrix. - - ARluNonSymMatrix(); - // Short constructor that does nothing. - - ARluNonSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, int* pcolp, - double thresholdp = 0.1, int orderp = 1, bool check = true); - // Long constructor (square matrix). - - ARluNonSymMatrix(int mp, int np, int nnzp, ARTYPE* ap, int* irowp,int* pcolp); - // Long constructor (rectangular matrix). - - ARluNonSymMatrix(const std::string& name, double thresholdp = 0.1, - int orderp = 1, bool check = true); - // Long constructor (Harwell-Boeing file). - - ARluNonSymMatrix(const ARluNonSymMatrix& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymMatrix() { ClearMem(); } - // Destructor. - - ARluNonSymMatrix& operator=(const ARluNonSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARluNonSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -bool ARluNonSymMatrix::DataOK() -{ - - int i, j, k; - - // Checking if pcol is in ascending order. - - i = 0; - while ((i!=this->n)&&(pcol[i]<=pcol[i+1])) i++; - if (i!=this->n) return false; - - // Checking if irow components are in order and within bounds. - - for (i=0; i!=this->n; i++) { - j = pcol[i]; - k = pcol[i+1]-1; - if (j<=k) { - if ((irow[j]<0)||(irow[k]>=this->n)) return false; - while ((j!=k)&&(irow[j] -inline void ARluNonSymMatrix:: -Copy(const ARluNonSymMatrix& other) -{ - - // Copying very fundamental variables. - - this->defined = other.defined; - factored = other.factored; - - // Returning from here if "other" was not initialized. - - if (!this->defined) return; - - // Copying user-defined parameters. - - if (other.n == other.m) { - DefineMatrix(other.n, other.nnz, other.a, other.irow, - other.pcol, other.threshold, other.order); - } - else { - DefineMatrix(other.m, other.n, other.nnz, - other.a, other.irow, other.pcol); - } - - // Throwing the original factorization away (this procedure - // is really awkward, but it is necessary because there - // is no copy function for matrices L and U in the SuperLU - // library and it is not a good idea to do this kind of deep - // copy here). - - if (factored) { - ArpackError(ArpackError::LAPACK_ERROR, "ARluNonSymMatrix"); - factored = false; - } - -} // Copy. - - -template -void ARluNonSymMatrix::ClearMem() -{ - - if (factored) { - Destroy_SuperNode_Matrix(&L); - Destroy_CompCol_Matrix(&U); - StatFree(&stat); - } - if (this->defined) { - Destroy_SuperMatrix_Store(&A); // delete A.Store; - delete[] permc; - delete[] permr; - permc = NULL; - permr = NULL; - A.Store = NULL; - } - -} // ClearMem. - - -template -void ARluNonSymMatrix:: -SubtractAsI(ARTYPE sigma, NCformat& A, NCformat& AsI) -{ - - // Defining local variables. - - int i, j, k, end; - ARTYPE* anzval; - ARTYPE* inzval; - - // Telling the compiler that nzval must be viewed as a vector of ARTYPE. - - anzval = (ARTYPE*)A.nzval; - inzval = (ARTYPE*)AsI.nzval; - - // Subtracting sigma from diagonal elements. - - k = 0; - AsI.colptr[0] = 0; - - for (i=0; i!=this->n; i++) { - - j = A.colptr[i]; - end = A.colptr[i+1]; - - // Copying superdiagonal elements of column i. - - while ((A.rowind[j] < i)&&(j < end)) { - inzval[k] = anzval[j]; - AsI.rowind[k++] = A.rowind[j++]; - } - - // Verifying if A(i,i) exists. - - if ((A.rowind[j] == i)&&(j < end)) { // A(i,i) exists, subtracting sigma. - inzval[k] = anzval[j++] - sigma; - } - else { // A(i,i) does not exist. - inzval[k] = -sigma; - } - AsI.rowind[k++] = i; - - // Copying subdiagonal elements of column i. - - while (j < end ) { - inzval[k] = anzval[j]; - AsI.rowind[k++] = A.rowind[j++]; - } - - AsI.colptr[i+1] = k; - - } - - AsI.nnz = AsI.colptr[this->n]; - -} // SubtractAsI. - - -template -void ARluNonSymMatrix::FactorA() -{ - - // Defining local variables. - - int info; - int* etree; - SuperMatrix AC; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARluNonSymMatrix::FactorA"); - } - - // Quitting the function if A is not square. - - if (this->m != this->n) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARluNonSymMatrix::FactorA"); - } - - // Deleting previous versions of L and U. - - if (factored) { - Destroy_SuperNode_Matrix(&L); - Destroy_CompCol_Matrix(&U); - StatFree(&stat); - } - - // Setting default values for gstrf parameters. - - int panel_size = sp_ienv(1); - int relax = sp_ienv(2); - superlu_options_t options; - - /* Set the default input options: - options.Fact = DOFACT; - options.Equil = YES; - options.ColPerm = COLAMD; - options.DiagPivotThresh = 1.0; - options.Trans = NOTRANS; - options.IterRefine = NOREFINE; - options.SymmetricMode = NO; - options.PivotGrowth = NO; - options.ConditionNumber = NO; - options.PrintStat = YES; - */ - set_default_options(&options); - options.DiagPivotThresh = threshold; - - // Reserving memory for etree (used in matrix decomposition). - - etree = new int[this->n]; - - // Defining LUStat. - - //StatInit(panel_size, relax); - StatInit(&stat); - - // Defining the column permutation of matrix A - // (using minimum degree ordering on A'*A). - - get_perm_c(order, &A, permc); - - // Permuting columns of A and - // creating the elimination tree of A'*A. - -// sp_preorder("N", &A, permc, etree, &AC); - sp_preorder(&options, &A, permc, etree, &AC); - - // Decomposing A. - -// gstrf("N",&AC, threshold, drop_tol, relax, panel_size, etree, -// NULL, 0, permr, permc, &L, &U, &info); - gstrf(&options,&AC, relax, panel_size, etree, - NULL, 0, permc, permr, &L, &U, &stat, &info); - - // Deleting AC and etree. - - Destroy_CompCol_Permuted(&AC); - delete[] etree; - - factored = (info == 0); - - // Handling errors. - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARluNonSymMatrix::FactorA"); - } - else if (info > this->n) { // Memory is not sufficient. - throw ArpackError(ArpackError::MEMORY_OVERFLOW, - "ARluNonSymMatrix::FactorA"); - } - else if (info > 0) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARluNonSymMatrix::FactorA"); - } - -} // FactorA. - - -template -void ARluNonSymMatrix::FactorAsI(ARTYPE sigma) -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARluNonSymMatrix::FactorAsI"); - } - - // Quitting the function if A is not square. - - if (this->m != this->n) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARluNonSymMatrix::FactorAsI"); - } - - // Defining local variables. - - int info; - int* etree; - int* irowi; - int* pcoli; - ARTYPE* asi; - SuperMatrix AsI; - SuperMatrix AC; - NCformat* Astore; - NCformat* AsIstore; - - // Deleting previous versions of L and U. - - if (factored) { - Destroy_SuperNode_Matrix(&L); - Destroy_CompCol_Matrix(&U); - StatFree(&stat); - } - - // Setting default values for gstrf parameters. - - int panel_size = sp_ienv(1); - int relax = sp_ienv(2); - superlu_options_t options; - - /* Set the default input options: - options.Fact = DOFACT; - options.Equil = YES; - options.ColPerm = COLAMD; - options.DiagPivotThresh = 1.0; - options.Trans = NOTRANS; - options.IterRefine = NOREFINE; - options.SymmetricMode = NO; - options.PivotGrowth = NO; - options.ConditionNumber = NO; - options.PrintStat = YES; - */ - set_default_options(&options); - options.DiagPivotThresh = threshold; - - // Creating a temporary matrix AsI. - - irowi = (int*)SUPERLU_MALLOC(sizeof(int) * (nnz+this->n)); - pcoli = (int*)SUPERLU_MALLOC(sizeof(int) * (this->n+1)); - asi = (ARTYPE*)SUPERLU_MALLOC(sizeof(ARTYPE) * (nnz+this->n)); - Create_CompCol_Matrix(&AsI, this->n, this->n, nnz, asi, irowi, pcoli, SLU_NC, SLU_GE); - - // Subtracting sigma*I from A and storing the result on AsI. - - Astore = (NCformat*)A.Store; - AsIstore = (NCformat*)AsI.Store; - SubtractAsI(sigma, *Astore, *AsIstore); - - // Reserving memory for etree (used in matrix decomposition). - - etree = new int[this->n]; - - // Defining LUStat. - - //StatInit(panel_size, relax); - StatInit(&stat); - - // Defining the column permutation of matrix AsI - // (using minimum degree ordering on AsI'*AsI). - - get_perm_c(order, &AsI, permc); - - // Permuting columns of AsI and - // creating the elimination tree of AsI'*AsI. - - //sp_preorder("N", &AsI, permc, etree, &AC); - sp_preorder(&options, &AsI, permc, etree, &AC); - - // Decomposing AsI. - -// gstrf("N",&AC, threshold, drop_tol, relax, panel_size, etree, -// NULL, 0, permr, permc, &L, &U, &info); - gstrf(&options,&AC, relax, panel_size, etree, - NULL, 0, permc, permr, &L, &U, &stat, &info); - - // Deleting AC, AsI and etree. - - Destroy_CompCol_Permuted(&AC); - Destroy_CompCol_Matrix(&AsI); - delete[] etree; - - factored = (info == 0); - - // Handling errors. - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARluNonSymMatrix::FactorAsI"); - } - else if (info > this->n) { // Memory is not sufficient. - throw ArpackError(ArpackError::MEMORY_OVERFLOW, - "ARluNonSymMatrix::FactorAsI"); - } - else if (info > 0) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARluNonSymMatrix::FactorAsI"); - } - -} // FactorAsI. - - -template -void ARluNonSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ - - int i,j; - ARTYPE t; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARluNonSymMatrix::MultMv"); - } - - // Determining w = M.v. - - for (i=0; i!=this->m; i++) w[i]=(ARTYPE)0; - - for (i=0; i!=this->n; i++) { - t = v[i]; - for (j=pcol[i]; j!=pcol[i+1]; j++) { - w[irow[j]] += t*a[j]; - } - } - -} // MultMv. - - -template -void ARluNonSymMatrix::MultMtv(ARTYPE* v, ARTYPE* w) -{ - - int i,j; - ARTYPE t; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARluNonSymMatrix::MultMtv"); - } - - // Determining w = M'.v. - - for (i=0; i!=this->n; i++) { - t = (ARTYPE)0; - for (j=pcol[i]; j!=pcol[i+1]; j++) { - t += v[irow[j]]*a[j]; - } - w[i] = t; - } - -} // MultMtv. - - -template -void ARluNonSymMatrix::MultMtMv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE* t = new ARTYPE[this->m]; - - MultMv(v,t); - MultMtv(t,w); - - delete[] t; - -} // MultMtMv. - - -template -void ARluNonSymMatrix::MultMMtv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE* t = new ARTYPE[this->n]; - - MultMtv(v,t); - MultMv(t,w); - - delete[] t; - -} // MultMMtv. - - -template -void ARluNonSymMatrix::Mult0MMt0v(ARTYPE* v, ARTYPE* w) -{ - - MultMv(&v[this->m],w); - MultMtv(v,&w[this->m]); - -} // Mult0MMt0v. - - -template -void ARluNonSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARluNonSymMatrix::MultInvv"); - } - - // Solving A.w = v (or AsI.w = v). - - int info; - SuperMatrix B; - - if (&v != &w) copy(this->n, v, 1, w, 1); - Create_Dense_Matrix(&B, this->n, 1, w, this->n, SLU_DN, SLU_GE); -// gstrs("N", &L, &U, permr, permc, &B, &info); - StatInit(&stat); - trans_t trans = NOTRANS; - gstrs(trans, &L, &U, permc, permr, &B, &stat, &info); - Destroy_SuperMatrix_Store(&B); // delete B.Store; - -} // MultInvv. - - -template -inline void ARluNonSymMatrix:: -DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, int* pcolp, - double thresholdp, int orderp, bool check) -{ - - this->m = np; - this->n = np; - nnz = nnzp; - a = ap; - irow = irowp; - pcol = pcolp; - pcol[this->n] = nnz; - threshold = thresholdp; - order = orderp; - - // Checking data. - - if ((check)&&(!DataOK())) { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARluSymMatrix::DefineMatrix"); - } - - // Creating SuperMatrix A. - - Create_CompCol_Matrix(&A, this->n, this->n, nnz, a, irow, pcol, SLU_NC, SLU_GE); - - // Reserving memory for vectors used in matrix decomposition. - - permc = new int[this->n]; - permr = new int[this->n]; - - this->defined = true; - -} // DefineMatrix (square). - - -template -inline void ARluNonSymMatrix:: -DefineMatrix(int mp, int np, int nnzp, ARTYPE* ap, int* irowp, int* pcolp) -{ - - this->m = mp; - this->n = np; - nnz = nnzp; - a = ap; - irow = irowp; - pcol = pcolp; - pcol[this->n] = nnz; - this->defined = true; - permc = NULL; - permr = NULL; - -} // DefineMatrix (rectangular). - - -template -inline ARluNonSymMatrix::ARluNonSymMatrix(): ARMatrix() -{ - - factored = false; - permc = NULL; - permr = NULL; - -} // Short constructor. - - -template -inline ARluNonSymMatrix:: -ARluNonSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, double thresholdp, - int orderp, bool check) : ARMatrix(np) -{ - - factored = false; - DefineMatrix(np, nnzp, ap, irowp, pcolp, thresholdp, orderp, check); - -} // Long constructor (square matrix). - - -template -inline ARluNonSymMatrix:: -ARluNonSymMatrix(int mp, int np, int nnzp, ARTYPE* ap, - int* irowp, int* pcolp) : ARMatrix(mp, np) -{ - - factored = false; - DefineMatrix(mp, np, nnzp, ap, irowp, pcolp); - -} // Long constructor (retangular matrix). - - -template -ARluNonSymMatrix:: -ARluNonSymMatrix(const std::string& file, double thresholdp, int orderp, bool check) -{ - - factored = false; - - try { - mat.Define(file); - } - catch (ArpackError) { // Returning from here if an error has occurred. - throw ArpackError(ArpackError::CANNOT_READ_FILE, "ARluNonSymMatrix"); - } - - if (mat.NCols()==mat.NRows()) { - DefineMatrix(mat.NCols(), mat.NonZeros(), (ARTYPE*)mat.Entries(), - mat.RowInd(), mat.ColPtr(), thresholdp, orderp, check); - } - else { - DefineMatrix(mat.NRows(), mat.NCols(), mat.NonZeros(), - (ARTYPE*)mat.Entries(), mat.RowInd(), mat.ColPtr()); - } - -} // Long constructor (Harwell-Boeing file). - - -template -ARluNonSymMatrix& ARluNonSymMatrix:: -operator=(const ARluNonSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLNSMAT_H diff --git a/src/external/arpack++/include/arlnspen.h b/src/external/arpack++/include/arlnspen.h deleted file mode 100644 index b479ac65..00000000 --- a/src/external/arpack++/include/arlnspen.h +++ /dev/null @@ -1,774 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLNSPen.h. - Arpack++ class ARluNonSymPencil definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARLNSPEN_H -#define ARLNSPEN_H - -#include - -#include "arch.h" -#include "arerror.h" -#include "blas1c.h" -#include "superluc.h" -#include "arlspdef.h" -#include "arlutil.h" -#include "arlnsmat.h" - - -template -class ARluNonSymPencil -{ - - protected: - - bool factored; - int* permc; - int* permr; - char part; - ARluNonSymMatrix* A; - ARluNonSymMatrix* B; - SuperMatrix L; - SuperMatrix U; - SuperLUStat_t stat; - - virtual void Copy(const ARluNonSymPencil& other); - - void ClearMem(); - - void SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], - int yind[], int ny, ARTYPE z[], int zind[], int& nz); - -#ifdef ARCOMP_H - void SparseSaxpy(arcomplex a, ARFLOAT x[], int xind[], int nx, - ARFLOAT y[], int yind[], int ny, arcomplex z[], - int zind[], int& nz); -#endif - - void SubtractAsB(int n, ARTYPE sigma, NCformat& A, - NCformat& B, NCformat& AsB); - -#ifdef ARCOMP_H - void SubtractAsB(int n, ARFLOAT sigmaR, ARFLOAT sigmaI, - NCformat& A, NCformat& B, NCformat& AsB); -#endif - - public: - - bool IsFactored() { return factored; } - - void FactorAsB(ARTYPE sigma); - -#ifdef ARCOMP_H - void FactorAsB(ARFLOAT sigmaR, ARFLOAT sigmaI, char partp = 'R'); -#endif - - void MultAv(ARTYPE* v, ARTYPE* w) { A->MultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - -#ifdef ARCOMP_H - void MultInvAsBv(arcomplex* v, arcomplex* w); -#endif - - void MultInvAsBv(ARFLOAT* v, ARFLOAT* w); - - void DefineMatrices(ARluNonSymMatrix& Ap, - ARluNonSymMatrix& Bp); - - ARluNonSymPencil(); - // Short constructor that does nothing. - - ARluNonSymPencil(ARluNonSymMatrix& Ap, - ARluNonSymMatrix& Bp); - // Long constructor. - - ARluNonSymPencil(const ARluNonSymPencil& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymPencil() { ClearMem(); } - // Destructor. - - ARluNonSymPencil& operator=(const ARluNonSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARluNonSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymPencil:: -Copy(const ARluNonSymPencil& other) -{ - - factored = other.factored; - part = other.part; - A = other.A; - B = other.B; - - // Throwing the original factorization away (this procedure - // is really awkward, but it is necessary because there - // is no copy function for matrices L and U in the SuperLU - // library and it is not a good idea to do this kind of deep - // copy here). - - if (factored) { - ArpackError(ArpackError::DISCARDING_FACTORS, "ARluNonSymPencil"); - factored = false; - } - -} // Copy. - - -template -void ARluNonSymPencil::ClearMem() -{ - - if (factored) { - Destroy_SuperNode_Matrix(&L); - Destroy_CompCol_Matrix(&U); - StatFree(&stat); - delete[] permc; - delete[] permr; - permc = NULL; - permr = NULL; - } - -} // ClearMem. - - -template -void ARluNonSymPencil:: -SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], - int yind[], int ny, ARTYPE z[], int zind[], int& nz) -// A strongly sequential (and inefficient) sparse saxpy algorithm. -{ - - int ix, iy; - - nz = 0; - if ((nx == 0) || (a == (ARTYPE)0)) { - copy(ny,y,1,z,1); - for (iy=0; iy!=ny; iy++) zind[iy] = yind[iy]; - nz = ny; - return; - } - if (ny == 0) { - copy(nx,x,1,z,1); - scal(nx,a,z,1); - for (ix=0; ix!=nx; ix++) zind[ix] = xind[ix]; - nz = nx; - return; - } - ix = 0; - iy = 0; - while (true) { - if (xind[ix] == yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]+y[iy++]; - if ((ix == nx)||(iy == ny)) break; - } - else if (xind[ix] < yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]; - if (ix == nx) break; - } - else { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - if (iy == ny) break; - } - } - while (iy < ny) { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - } - while (ix < nx) { - zind[nz] = xind[ix]; - z[nz++] = x[ix++]; - } - -} // SparseSaxpy (ARTYPE). - - -#ifdef ARCOMP_H -template -void ARluNonSymPencil:: -SparseSaxpy(arcomplex a, ARFLOAT x[], int xind[], int nx, ARFLOAT y[], - int yind[], int ny, arcomplex z[], int zind[], int& nz) -// A strongly sequential (and inefficient) sparse saxpy algorithm. -{ - - int ix, iy; - - nz = 0; - if ((nx == 0) || (a == arcomplex(0.0,0.0))) { - for (iy=0; iy!=ny; iy++) { - z[iy] = arcomplex(y[iy],0.0); - zind[iy] = yind[iy]; - } - nz = ny; - return; - } - if (ny == 0) { - for (ix=0; ix!=ny; ix++) { - z[ix] = a*arcomplex(x[ix],0.0); - zind[ix] = xind[ix]; - } - nz = nx; - return; - } - ix = 0; - iy = 0; - while (true) { - if (xind[ix] == yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]+y[iy++]; - if ((ix == nx)||(iy == ny)) break; - } - else if (xind[ix] < yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]; - if (ix == nx) break; - } - else { - zind[nz] = yind[iy]; - z[nz++] = arcomplex(y[iy++],0.0); - if (iy == ny) break; - } - } - while (iy < ny) { - zind[nz] = yind[iy]; - z[nz++] = arcomplex(y[iy++],0.0); - } - while (ix < nx) { - zind[nz] = xind[ix]; - z[nz++] = arcomplex(x[ix++],0.0); - } - -} // SparseSaxpy (arcomplex). -#endif // ARCOMP_H. - - -template -void ARluNonSymPencil:: -SubtractAsB(int n, ARTYPE sigma, NCformat& A, NCformat& B, NCformat& AsB) -{ - - int i, acol, bcol, asbcol, scol; - ARTYPE* anzval; - ARTYPE* bnzval; - ARTYPE* asbnzval; - - // Telling the compiler that nzval must ve viewed as a vector of ARTYPE. - - anzval = (ARTYPE*)A.nzval; - bnzval = (ARTYPE*)B.nzval; - asbnzval = (ARTYPE*)AsB.nzval; - - // Subtracting sigma*B from A. - - AsB.colptr[0] = 0; - asbcol = 0; - - for (i=0; i!=n; i++) { - bcol = B.colptr[i]; - acol = A.colptr[i]; - SparseSaxpy(-sigma, &bnzval[bcol], &B.rowind[bcol], B.colptr[i+1]-bcol, - &anzval[acol], &A.rowind[acol], A.colptr[i+1]-acol, - &asbnzval[asbcol], &AsB.rowind[asbcol], scol); - asbcol += scol; - AsB.colptr[i+1] = asbcol; - } - - AsB.nnz = AsB.colptr[n]; - -} // SubtractAsB (ARTYPE shift). - - -#ifdef ARCOMP_H -template -void ARluNonSymPencil:: -SubtractAsB(int n, ARFLOAT sigmaR, ARFLOAT sigmaI, - NCformat& A, NCformat& B, NCformat& AsB) -{ - - int i, acol, bcol, asbcol, scol; - ARTYPE* anzval; - ARTYPE* bnzval; - arcomplex* asbnzval; - arcomplex sigma; - - // Telling the compiler that nzval must ve viewed as a vector of ARTYPE. - - anzval = (ARTYPE*)A.nzval; - bnzval = (ARTYPE*)B.nzval; - asbnzval = (arcomplex*)AsB.nzval; - - // Subtracting sigma*B from A. - - sigma = arcomplex(sigmaR, sigmaI); - AsB.colptr[0] = 0; - asbcol = 0; - - for (i=0; i!=n; i++) { - bcol = B.colptr[i]; - acol = A.colptr[i]; - SparseSaxpy(-sigma, &bnzval[bcol], &B.rowind[bcol], B.colptr[i+1]-bcol, - &anzval[acol], &A.rowind[acol], A.colptr[i+1]-acol, - &asbnzval[asbcol], &AsB.rowind[asbcol], scol); - asbcol += scol; - AsB.colptr[i+1] = asbcol; - } - - AsB.nnz = AsB.colptr[n]; - -} // SubtractAsB (arcomplex shift). -#endif // ARCOMP_H. - - -template -void ARluNonSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARluNonSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARluNonSymPencil::FactorAsB"); - } - - // Defining local variables. - - int nnzi, info; - int* etree; - int* irowi; - int* pcoli; - ARTYPE* asb; - SuperMatrix AsB; - SuperMatrix AC; - NCformat* Astore; - NCformat* Bstore; - NCformat* AsBstore; - - // Deleting old versions of L, U, perm_r and perm_c. - - ClearMem(); - - // Setting default values for gstrf parameters. - - int panel_size = sp_ienv(1); - int relax = sp_ienv(2); - superlu_options_t options; - /* Set the default input options: - options.Fact = DOFACT; - options.Equil = YES; - options.ColPerm = COLAMD; - options.DiagPivotThresh = 1.0; - options.Trans = NOTRANS; - options.IterRefine = NOREFINE; - options.SymmetricMode = NO; - options.PivotGrowth = NO; - options.ConditionNumber = NO; - options.PrintStat = YES; - */ - set_default_options(&options); - options.DiagPivotThresh = A->threshold; - - // Defining A and B format. - - Astore = (NCformat*)A->A.Store; - Bstore = (NCformat*)B->A.Store; - - // Creating a temporary matrix AsB. - - nnzi = Astore->nnz+Bstore->nnz; - irowi = new int[nnzi]; - pcoli = new int[A->ncols()+1]; - asb = new ARTYPE[nnzi]; - Create_CompCol_Matrix(&AsB, A->nrows(), A->ncols(), nnzi, asb, - irowi, pcoli, SLU_NC, SLU_GE); - - // Subtracting sigma*B from A and storing the result on AsB. - - AsBstore = (NCformat*)AsB.Store; - SubtractAsB(A->ncols(), sigma, *Astore, *Bstore, *AsBstore); - - // Reserving memory for some vectors used in matrix decomposition. - - etree = new int[A->ncols()]; - if (permc == NULL) permc = new int[A->ncols()]; - if (permr == NULL) permr = new int[A->ncols()]; - - // Defining LUStat. - -// StatInit(panel_size, relax); - SuperLUStat_t stat; - StatInit(&stat); - - // Defining the column permutation of matrix AsB - // (using minimum degree ordering on AsB'*AsB). - - get_perm_c(A->order, &AsB, permc); - - // Permuting columns of AsB and - // creating the elimination tree of AsB'*AsB. - -// sp_preorder("N", &AsB, permc, etree, &AC); - sp_preorder(&options, &AsB, permc, etree, &AC); - - // Decomposing AsB. - -// gstrf("N",&AC, A->threshold, drop_tol, relax, panel_size, etree, -// NULL, 0, permr, permc, &L, &U, &info); - gstrf(&options, &AC, relax, panel_size, etree, - NULL, 0, permc, permr, &L, &U, &stat, &info); - - // Deleting AC, AsB and etree. - - Destroy_CompCol_Permuted(&AC); - Destroy_CompCol_Matrix(&AsB); - delete[] etree; - - factored = (info == 0); - - // Handling errors. - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARluNonSymPencil::FactorAsB"); - } - else if (info > A->ncols()) { // Memory is not sufficient. - throw ArpackError(ArpackError::MEMORY_OVERFLOW, - "ARluNonSymPencil::FactorAsB"); - } - else if (info > 0) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARluNonSymPencil::FactorAsB"); - } - -} // FactorAsB (ARTYPE shift). - - -#ifdef ARCOMP_H -template -void ARluNonSymPencil:: -FactorAsB(ARFLOAT sigmaR, ARFLOAT sigmaI, char partp) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARluNonSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARluNonSymPencil::FactorAsB"); - } - - // Defining local variables. - - int nnzi, info; - int* etree; - int* irowi; - int* pcoli; - arcomplex* asb; - SuperMatrix AsB; - SuperMatrix AC; - NCformat* Astore; - NCformat* Bstore; - NCformat* AsBstore; - - // Deleting old versions of L, U, perm_r and perm_c. - - ClearMem(); - - // Setting default values for gstrf parameters. - - int panel_size = sp_ienv(1); - int relax = sp_ienv(2); - superlu_options_t options; - /* Set the default input options: - options.Fact = DOFACT; - options.Equil = YES; - options.ColPerm = COLAMD; - options.DiagPivotThresh = 1.0; - options.Trans = NOTRANS; - options.IterRefine = NOREFINE; - options.SymmetricMode = NO; - options.PivotGrowth = NO; - options.ConditionNumber = NO; - options.PrintStat = YES; - */ - set_default_options(&options); - options.DiagPivotThresh = A->threshold; - - - // Defining A and B format. - - Astore = (NCformat*)A->A.Store; - Bstore = (NCformat*)B->A.Store; - - // Creating a temporary matrix AsB. - - part = partp; - nnzi = Astore->nnz+Bstore->nnz; - irowi = new int[nnzi]; - pcoli = new int[A->ncols()+1]; - asb = new arcomplex[nnzi]; - Create_CompCol_Matrix(&AsB, A->nrows(), A->ncols(), nnzi, asb, - irowi, pcoli, SLU_NC, SLU_GE); - - // Subtracting sigma*B from A and storing the result on AsB. - - AsBstore = (NCformat*)AsB.Store; - SubtractAsB(A->ncols(), sigmaR, sigmaI, *Astore, *Bstore, *AsBstore); - - // Reserving memory for some vectors used in matrix decomposition. - - etree = new int[A->ncols()]; - if (permc == NULL) permc = new int[A->ncols()]; - if (permr == NULL) permr = new int[A->ncols()]; - - // Defining LUStat. - -// StatInit(panel_size, relax); - SuperLUStat_t stat; - StatInit(&stat); - - // Defining the column permutation of matrix AsB - // (using minimum degree ordering on AsB'*AsB). - - get_perm_c(A->order, &AsB, permc); - - // Permuting columns of AsB and - // creating the elimination tree of AsB'*AsB. - - //sp_preorder("N", &AsB, permc, etree, &AC); - sp_preorder(&options, &AsB, permc, etree, &AC); - - // Decomposing AsB. - -// gstrf("N",&AC, A->threshold, drop_tol, relax, panel_size, etree, NULL, -// 0, permr, permc, &L, &U, &info); - gstrf(&options, &AC, relax, panel_size, etree, - NULL, 0, permc, permr, &L, &U, &stat, &info); - - // Deleting AC, AsB and etree. - - Destroy_CompCol_Permuted(&AC); - Destroy_CompCol_Matrix(&AsB); - delete[] etree; - - factored = (info == 0); - - // Handling errors. - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARluNonSymPencil::FactorAsB"); - } - else if (info > A->ncols()) { // Memory is not sufficient. - throw ArpackError(ArpackError::MEMORY_OVERFLOW, - "ARluNonSymPencil::FactorAsB"); - } - else if (info > 0) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARluNonSymPencil::FactorAsB"); - } - -} // FactorAsB (arcomplex shift). -#endif // ARCOMP_H. - - -template -void ARluNonSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - B->MultInvv(w, w); - -} // MultInvBAv. - - -#ifdef ARCOMP_H - -template -void ARluNonSymPencil:: -MultInvAsBv(arcomplex* v, arcomplex* w) -{ - - // Quitting the function if AsB was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARluNonSymPencil::MultInvAsBv"); - } - - // Solving AsB.w = v. - - int info; - SuperMatrix RHS; - - copy(A->nrows(), v, 1, w, 1); - Create_Dense_Matrix(&RHS, A->nrows(), 1, w, A->nrows(), SLU_DN, SLU_GE); -// gstrs("N", &L, &U, permr, permc, &RHS, &info); - trans_t trans = NOTRANS; - StatInit(&stat); - - gstrs(trans, &L, &U, permc, permr, &RHS, &stat, &info); - - Destroy_SuperMatrix_Store(&RHS); // delete RHS.Store; - -} // MultInvAsBv (arcomplex). - -#endif - - -template -void ARluNonSymPencil::MultInvAsBv(ARFLOAT* v, ARFLOAT* w) -{ - - // Quitting the function if AsB was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARluNonSymPencil::MultInvAsBv"); - } - - // Solving AsB.w = v. - - int info; - SuperMatrix RHS; - - if (part == 'N') { // shift is real. - - copy(A->nrows(), v, 1, w, 1); - Create_Dense_Matrix(&RHS, A->nrows(), 1, w, A->nrows(), SLU_DN, SLU_GE); - //gstrs("N", &L, &U, permr, permc, &RHS, &info); - trans_t trans = NOTRANS; - StatInit(&stat); - gstrs(trans, &L, &U, permc, permr, &RHS, &stat, &info); - - } - else { // shift is complex. - -#ifdef ARCOMP_H - - int i; - arcomplex *tv = new arcomplex[A->ncols()]; - - for (i=0; i!=A->ncols(); i++) tv[i] = arcomplex(v[i],0.0); - Create_Dense_Matrix(&RHS, A->ncols(), 1, tv, A->ncols(), SLU_DN, SLU_GE); - //gstrs("N", &L, &U, permr, permc, &RHS, &info); - trans_t trans = NOTRANS; - StatInit(&stat); - gstrs(trans, &L, &U, permc, permr, &RHS, &stat, &info); - - - if (part=='I') { - for (i=0; i!=A->ncols(); i++) w[i] = imag(tv[i]); - } - else { - for (i=0; i!=A->ncols(); i++) w[i] = real(tv[i]); - } - - delete[] tv; - -#endif - - } - - Destroy_SuperMatrix_Store(&RHS); // delete RHS.Store; - -} // MultInvAsBv (ARFLOAT). - - -template -inline void ARluNonSymPencil:: -DefineMatrices(ARluNonSymMatrix& Ap, - ARluNonSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - permc = NULL; - permr = NULL; - - if ((A->n != B->n)||(A->m != B->m)) { - throw ArpackError(ArpackError::INCOMPATIBLE_SIZES, - "ARluNonSymMatrix::DefineMatrices"); - } - -} // DefineMatrices. - - -template -inline ARluNonSymPencil::ARluNonSymPencil() -{ - - factored = false; - part = 'N'; - permr = NULL; - permc = NULL; - -} // Short constructor. - - -template -inline ARluNonSymPencil:: -ARluNonSymPencil(ARluNonSymMatrix& Ap, - ARluNonSymMatrix& Bp) -{ - - factored = false; - DefineMatrices(Ap, Bp); - -} // Long constructor. - - -template -ARluNonSymPencil& ARluNonSymPencil:: -operator=(const ARluNonSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLNSPEN_H diff --git a/src/external/arpack++/include/arlscomp.h b/src/external/arpack++/include/arlscomp.h deleted file mode 100644 index ed3b42dc..00000000 --- a/src/external/arpack++/include/arlscomp.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLSComp.h. - Arpack++ class ARluCompStdEig definition - (superlu version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARLSCOMP_H -#define ARLSCOMP_H - -#include -#include -#include "arch.h" -#include "arscomp.h" -#include "arlnsmat.h" -#include "arrseig.h" - - -template -class ARluCompStdEig: - public virtual ARCompStdEig, ARFLOAT> > { - - protected: - - // a) Protected function: - - virtual void Copy(const ARluCompStdEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // b) Public functions: - - // b.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(arcomplex sigmap); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(arcomplex sigmap); - - // b.2) Constructors and destructor. - - ARluCompStdEig() { } - // Short constructor. - - ARluCompStdEig(int nevp, ARluNonSymMatrix, ARFLOAT>& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluCompStdEig(int nevp, ARluNonSymMatrix, ARFLOAT>& A, - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluCompStdEig(const ARluCompStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluCompStdEig() { } - // Destructor. - - - // c) Operators. - - ARluCompStdEig& operator=(const ARluCompStdEig& other); - // Assignment operator. - -}; // class ARluCompStdEig. - - -// ------------------------------------------------------------------------ // -// ARluCompStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluCompStdEig:: -Copy(const ARluCompStdEig& other) -{ - - ARStdEig, - ARluNonSymMatrix, ARFLOAT> >:: - Copy(other); - if (this->mode > 2) this->objOP->FactorAsI(this->sigmaR); - -} // Copy. - - -template -inline void ARluCompStdEig::ChangeShift(arcomplex sigmap) -{ - - this->objOP->FactorAsI(sigmap); - ARrcStdEig >::ChangeShift(sigmap); - -} // ChangeShift. - - -template -inline void ARluCompStdEig::SetRegularMode() -{ - - ARStdEig, - ARluNonSymMatrix, ARFLOAT> >:: - SetRegularMode(this->objOP, - &ARluNonSymMatrix, ARFLOAT>::MultMv); - -} // SetRegularMode. - - -template -inline void ARluCompStdEig:: -SetShiftInvertMode(arcomplex sigmap) -{ - - ARStdEig, - ARluNonSymMatrix, ARFLOAT> >:: - SetShiftInvertMode(sigmap, this->objOP, - &ARluNonSymMatrix,ARFLOAT>::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluCompStdEig:: -ARluCompStdEig(int nevp, ARluNonSymMatrix, ARFLOAT>& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, arcomplex* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, - &ARluNonSymMatrix, ARFLOAT>::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluCompStdEig:: -ARluCompStdEig(int nevp, ARluNonSymMatrix, ARFLOAT>& A, - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, - &ARluNonSymMatrix, ARFLOAT>::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluCompStdEig& ARluCompStdEig:: -operator=(const ARluCompStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLSCOMP_H diff --git a/src/external/arpack++/include/arlsmat.h b/src/external/arpack++/include/arlsmat.h deleted file mode 100644 index 3c0ce079..00000000 --- a/src/external/arpack++/include/arlsmat.h +++ /dev/null @@ -1,765 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLSMat.h. - Arpack++ class ARluSymMatrix definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "arlspen.h" - -#ifndef ARLSMAT_H -#define ARLSMAT_H - -#include -#include -#include "arch.h" -#include "armat.h" -#include "arhbmat.h" -#include "arerror.h" -#include "blas1c.h" -#include "superluc.h" -#include "arlspdef.h" -#include "arlutil.h" - -template class ARluSymPencil; - -template -class ARluSymMatrix: public ARMatrix { - - friend class ARluSymPencil; - - protected: - - bool factored; - char uplo; - int order; - int nnz; - int* irow; - int* pcol; - int* permc; - int* permr; - double threshold; - ARTYPE* a; - SuperMatrix A; - SuperMatrix L; - SuperMatrix U; - ARhbMatrix mat; - SuperLUStat_t stat; - - bool DataOK(); - - virtual void Copy(const ARluSymMatrix& other); - - void ClearMem(); - - void ExpandA(NCformat& A, NCformat& Aexp, ARTYPE sigma = (ARTYPE)0); - - public: - - int nzeros() { return nnz; } - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, int* pcolp, - char uplop = 'L', double thresholdp = 0.1, - int orderp = 2, bool check = true); - - ARluSymMatrix(); - // Short constructor that does nothing. - - ARluSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, int* pcolp, - char uplop = 'L', double thresholdp = 0.1, - int orderp = 2, bool check = true); - // Long constructor. - - ARluSymMatrix(const std::string& name, double thresholdp = 0.1, - int orderp = 2, bool check = true); - // Long constructor (Harwell-Boeing file). - - ARluSymMatrix(const ARluSymMatrix& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymMatrix() { ClearMem(); } - // Destructor. - - ARluSymMatrix& operator=(const ARluSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARluSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -bool ARluSymMatrix::DataOK() -{ - - int i, j, k; - - // Checking if pcol is in ascending order. - - i = 0; - while ((i!=this->n)&&(pcol[i]<=pcol[i+1])) i++; - if (i!=this->n) return false; - - // Checking if irow components are in order and within bounds. - - for (i=0; i!=this->n; i++) { - j = pcol[i]; - k = pcol[i+1]-1; - if (j<=k) { - if (uplo == 'U') { - if ((irow[j]<0)||(irow[k]>i)) return false; - } - else { // uplo == 'L'. - if ((irow[j]=this->n)) return false; - } - while ((j!=k)&&(irow[j] -inline void ARluSymMatrix::Copy(const ARluSymMatrix& other) -{ - - // Copying very fundamental variables. - - this->defined = other.defined; - factored = other.factored; - - // Returning from here if "other" was not initialized. - - if (!this->defined) return; - - // Copying user-defined parameters. - - DefineMatrix(other.n, other.nnz, other.a, other.irow, other.pcol, - other.uplo, other.threshold, other.order); - - // Throwing the original factorization away (this procedure - // is really awkward, but it is necessary because there - // is no copy function for matrices L and U in the SuperLU - // library and it is not a good idea to do this kind of deep - // copy here). - - if (factored) { - ArpackError(ArpackError::LAPACK_ERROR, "ARluSymMatrix"); - factored = false; - } - -} // Copy. - - -template -void ARluSymMatrix::ClearMem() -{ - - if (factored) { - Destroy_SuperNode_Matrix(&L); - Destroy_CompCol_Matrix(&U); - StatFree(&stat); - } - if (this->defined) { - Destroy_SuperMatrix_Store(&A); // delete A.Store; - delete[] permc; - delete[] permr; - permc = NULL; - permr = NULL; - A.Store = NULL; - } - -} // ClearMem. - - -template -void ARluSymMatrix:: -ExpandA(NCformat& A, NCformat& Aexp, ARTYPE sigma) -{ - - // Defining local variables. - - bool subtract; - int i, j, k; - int *colA, *colE; - int *indA, *indE; - ARTYPE *valA, *valE; - - // Checking if sigma is zero. - - subtract = (sigma != (ARTYPE)0); - - // Simplifying the notation. - - valA = (ARTYPE*)A.nzval; - valE = (ARTYPE*)Aexp.nzval; - indA = (int*)A.rowind; - indE = (int*)Aexp.rowind; - colA = (int*)A.colptr; - colE = (int*)Aexp.colptr; - - // Filling colE with zeros. - - for (i=0; i<=this->n; i++) colE[i] = 0; - - // Counting the elements in each column of A. - - if (uplo == 'U') { - - for (i=0; i!=this->n; i++) { - k = colA[i+1]; - if ((k!=colA[i])&&(indA[k-1]==i)) { - k--; - } - else { - if (subtract) colE[i]++; - } - for (j=colA[i]; jn; i++) { - k = colA[i]; - if ((k!=colA[i+1])&&(indA[k]==i)) { - k++; - } - else { - if (subtract) colE[i]++; - } - for (j=k; jn; i++) colE[i+1]+=colE[i]; - - // Adding colA to colE. - - for (i=this->n; i>0; i--) colE[i] = colE[i-1]+colA[i]; - colE[0] = colA[0]; - - // Expanding A. - - if (uplo == 'U') { - - for (i=0; in; i++) { - for (j=colA[i]; j<(colA[i+1]-1); j++) { - indE[colE[i]] = indA[j]; - indE[colE[indA[j]]] = i; - valE[colE[i]++] = valA[j]; - valE[colE[indA[j]]++] = valA[j]; - } - if ((colA[i]!=colA[i+1])&&(indA[j]==i)) { - indE[colE[i]] = i; - if (subtract) { - valE[colE[i]++] = valA[j]-sigma; - } - else { - valE[colE[i]++] = valA[j]; - } - } - else { - if (subtract) { - indE[colE[i]] = i; - valE[colE[i]++] = -sigma; - } - } - } - - } - else { // uplo == 'L' - - for (i=0; in; i++) { - k=colA[i]; - if ((k!=colA[i+1])&&(indA[k]==i)) { - indE[colE[i]] = i; - if (subtract) { - valE[colE[i]++] = valA[k]-sigma; - } - else { - valE[colE[i]++] = valA[k]; - } - k++; - } - else { - if (subtract) { - indE[colE[i]] = i; - valE[colE[i]++] = -sigma; - } - } - for (j=k; jn; i>0; i--) { - colE[i] = colE[i-1]; - } - colE[0] = 0; - - Aexp.nnz = colE[this->n]; - -} // ExpandA. - - -template -void ARluSymMatrix::FactorA() -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARluSymMatrix::FactorA"); - } - - // Defining local variables. - - int info; - int* etree; - int* irowi; - int* pcoli; - ARTYPE* aexp; - SuperMatrix Aexp; - SuperMatrix AC; - NCformat* Astore; - NCformat* Aexpstore; - - // Deleting previous versions of L and U. - - if (factored) { - Destroy_SuperNode_Matrix(&L); - Destroy_CompCol_Matrix(&U); - StatFree(&stat); - } - - // Setting default values for gstrf parameters. - - int panel_size = sp_ienv(1); - int relax = sp_ienv(2); - superlu_options_t options; - - /* Set the default input options: - options.Fact = DOFACT; - options.Equil = YES; - options.ColPerm = COLAMD; - options.DiagPivotThresh = 1.0; - options.Trans = NOTRANS; - options.IterRefine = NOREFINE; - options.SymmetricMode = NO; - options.PivotGrowth = NO; - options.ConditionNumber = NO; - options.PrintStat = YES; - */ - set_default_options(&options); - - /* Now we modify the default options to use the symmetric mode. */ - options.SymmetricMode = YES; - options.ColPerm = MMD_AT_PLUS_A; - // options.DiagPivotThresh = 0.001; - options.DiagPivotThresh = threshold; - - // Creating a temporary matrix Aexp. - - irowi = (int*)SUPERLU_MALLOC(sizeof(int) * (nnz*2)); - pcoli = (int*)SUPERLU_MALLOC(sizeof(int) * (this->n+1)); - aexp = (ARTYPE*)SUPERLU_MALLOC(sizeof(ARTYPE) * (nnz*2)); - Create_CompCol_Matrix(&Aexp, this->n, this->n, nnz, aexp, irowi, pcoli, SLU_NC, SLU_GE); - - // Expanding A. - - Astore = (NCformat*)A.Store; - Aexpstore = (NCformat*)Aexp.Store; - ExpandA(*Astore, *Aexpstore); - - // Reserving memory for etree (used in matrix decomposition). - - etree = new int[this->n]; - - // Defining LUStat. - - //StatInit(panel_size, relax); - StatInit(&stat); - - // Defining the column permutation of matrix A - // (using minimum degree ordering). - - get_perm_c(order, &Aexp, permc); - - // Permuting columns of A and creating the elimination tree. - - //sp_preorder("N", &Aexp, permc, etree, &AC); - sp_preorder(&options, &Aexp, permc, etree, &AC); - - // Decomposing A. - -// gstrf("N",&AC, threshold, drop_tol, relax, panel_size, etree, -// NULL, 0, permr, permc, &L, &U, &info); - gstrf(&options,&AC, relax, panel_size, etree, - NULL, 0, permc, permr, &L, &U, &stat, &info); - - // Deleting AC, Aexp and etree. - - Destroy_CompCol_Permuted(&AC); - Destroy_CompCol_Matrix(&Aexp); - delete[] etree; - - factored = (info == 0); - - // Handling errors. - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARluSymMatrix::FactorA"); - } - else if (info > this->n) { // Memory is not sufficient. - throw ArpackError(ArpackError::MEMORY_OVERFLOW, - "ARluSymMatrix::FactorA"); - } - else if (info > 0) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARluSymMatrix::FactorA"); - } - -} // FactorA. - - -template -void ARluSymMatrix::FactorAsI(ARTYPE sigma) -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARluSymMatrix::FactorAsI"); - } - - // Defining local variables. - - int info; - int* etree; - int* irowi; - int* pcoli; - ARTYPE* asi; - SuperMatrix AsI; - SuperMatrix AC; - NCformat* Astore; - NCformat* AsIstore; - - // Deleting previous versions of L and U. - - if (factored) { - Destroy_SuperNode_Matrix(&L); - Destroy_CompCol_Matrix(&U); - StatFree(&stat); - } - - // Setting default values for gstrf parameters. - - int panel_size = sp_ienv(1); - int relax = sp_ienv(2); - superlu_options_t options; - - /* Set the default input options: - options.Fact = DOFACT; - options.Equil = YES; - options.ColPerm = COLAMD; - options.DiagPivotThresh = 1.0; - options.Trans = NOTRANS; - options.IterRefine = NOREFINE; - options.SymmetricMode = NO; - options.PivotGrowth = NO; - options.ConditionNumber = NO; - options.PrintStat = YES; - */ - set_default_options(&options); - - /* Now we modify the default options to use the symmetric mode. */ - options.SymmetricMode = YES; - options.ColPerm = MMD_AT_PLUS_A; - // options.DiagPivotThresh = 0.001; - options.DiagPivotThresh = threshold; - - // Creating a temporary matrix AsI. - - irowi = (int*)SUPERLU_MALLOC(sizeof(int) * (nnz*2+this->n)); - pcoli = (int*)SUPERLU_MALLOC(sizeof(int) * (this->n+1)); - asi = (ARTYPE*)SUPERLU_MALLOC(sizeof(ARTYPE) * (nnz*2+this->n)); - Create_CompCol_Matrix(&AsI, this->n, this->n, nnz, asi, irowi, pcoli, SLU_NC, SLU_GE); - - // Subtracting sigma*I from A and storing the result on AsI. - - Astore = (NCformat*)A.Store; - AsIstore = (NCformat*)AsI.Store; - ExpandA(*Astore, *AsIstore, sigma); - - // Reserving memory for etree (used in matrix decomposition). - - etree = new int[this->n]; - - // Defining LUStat. - - //StatInit(panel_size, relax); - StatInit(&stat); - - // Defining the column permutation of matrix AsI - // (using minimum degree ordering). - - get_perm_c(order, &AsI, permc); - - // Permuting columns of AsI and creating the elimination tree. - - sp_preorder(&options, &AsI, permc, etree, &AC); - - // Decomposing AsI. - -// gstrf("N",&AC, threshold, drop_tol, relax, panel_size, etree, -// NULL, 0, permr, permc, &L, &U, &info); - gstrf(&options,&AC, relax, panel_size, etree, - NULL, 0, permc, permr, &L, &U, &stat, &info); - - // Deleting AC, AsI and etree. - - Destroy_CompCol_Permuted(&AC); - Destroy_CompCol_Matrix(&AsI); - delete[] etree; - - factored = (info == 0); - - // Handling errors. - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARluSymMatrix::FactorAsI"); - } - else if (info > this->n) { // Memory is not sufficient. - throw ArpackError(ArpackError::MEMORY_OVERFLOW, - "ARluSymMatrix::FactorAsI"); - } - else if (info > 0) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARluSymMatrix::FactorAsI"); - } - -} // FactorAsI. - - -template -void ARluSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ - - int i, j, k; - ARTYPE t; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARluSymMatrix::MultMv"); - } - - // Determining w = M.v. - - for (i=0; i!=this->m; i++) w[i]=(ARTYPE)0; - - if (uplo == 'U') { - - for (i=0; i!=this->n; i++) { - t = v[i]; - k = pcol[i+1]; - if ((k!=pcol[i])&&(irow[k-1]==i)) { - w[i] += t*a[k-1]; - k--; - } - for (j=pcol[i]; jn; i++) { - t = v[i]; - k = pcol[i]; - if ((k!=pcol[i+1])&&(irow[k]==i)) { - w[i] += t*a[k]; - k++; - } - for (j=k; j -void ARluSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARluSymMatrix::MultInvv"); - } - - // Solving A.w = v (or AsI.w = v). - - int info; - SuperMatrix B; - - if (&v != &w) copy(this->n, v, 1, w, 1); - Create_Dense_Matrix(&B, this->n, 1, w, this->n, SLU_DN, SLU_GE); -// gstrs("N", &L, &U, permr, permc, &B, &info); - StatInit(&stat); - trans_t trans = NOTRANS; - gstrs(trans, &L, &U, permc, permr, &B, &stat, &info); - Destroy_SuperMatrix_Store(&B); // delete B.Store; - -} // MultInvv. - - -template -inline void ARluSymMatrix:: -DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, int* pcolp, - char uplop, double thresholdp, int orderp, bool check) -{ - - this->m = np; - this->n = np; - nnz = nnzp; - a = ap; - irow = irowp; - pcol = pcolp; - pcol[this->n] = nnz; - uplo = uplop; - threshold = thresholdp; - order = orderp; - - // Checking data. - - if ((check)&&(!DataOK())) { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARluSymMatrix::DefineMatrix"); - } - - // Creating SuperMatrix A. - - Create_CompCol_Matrix(&A, this->n, this->n, nnz, a, irow, pcol, SLU_NC, SLU_GE); - - // Reserving memory for vectors used in matrix decomposition. - - permc = new int[this->n]; - permr = new int[this->n]; - - this->defined = true; - -} // DefineMatrix. - - -template -inline ARluSymMatrix::ARluSymMatrix(): ARMatrix() -{ - - factored = false; - permc = NULL; - permr = NULL; - -} // Short constructor. - - -template -inline ARluSymMatrix:: -ARluSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, char uplop, double thresholdp, - int orderp, bool check) : ARMatrix(np) -{ - - factored = false; - DefineMatrix(np, nnzp, ap, irowp, pcolp, uplop, thresholdp, orderp, check); - -} // Long constructor. - - -template -ARluSymMatrix:: -ARluSymMatrix(const std::string& file, double thresholdp, int orderp, bool check) -{ - - factored = false; - - try { - mat.Define(file); - } - catch (ArpackError) { // Returning from here if an error has occurred. - throw ArpackError(ArpackError::CANNOT_READ_FILE, "ARluSymMatrix"); - } - - if ((mat.NCols() == mat.NRows()) && (mat.IsSymmetric())) { - - DefineMatrix(mat.NCols(), mat.NonZeros(), (ARTYPE*)mat.Entries(), - mat.RowInd(), mat.ColPtr(), 'L', thresholdp, orderp, check); - } - else { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARluSymMatrix::ARluSymMatrix"); - } - -} // Long constructor (Harwell-Boeing file). - - -template -ARluSymMatrix& ARluSymMatrix:: -operator=(const ARluSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLSMAT_H diff --git a/src/external/arpack++/include/arlsnsym.h b/src/external/arpack++/include/arlsnsym.h deleted file mode 100644 index e6369ada..00000000 --- a/src/external/arpack++/include/arlsnsym.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLSNSym.h. - Arpack++ class ARluNonSymStdEig definition - (SuperLU version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARLSNSYM_H -#define ARLSNSYM_H - -#include -#include -#include "arch.h" -#include "arsnsym.h" -#include "arlnsmat.h" - -template -class ARluNonSymStdEig: - public virtual ARNonSymStdEig > { - - protected: - - // a) Protected function: - - virtual void Copy(const ARluNonSymStdEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // b) Public functions: - - // b.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // b.2) Constructors and destructor. - - ARluNonSymStdEig() { } - // Short constructor. - - ARluNonSymStdEig(int nevp, ARluNonSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluNonSymStdEig(int nevp, ARluNonSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluNonSymStdEig(const ARluNonSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymStdEig() { } - // Destructor. - - // c) Operators. - - ARluNonSymStdEig& operator=(const ARluNonSymStdEig& other); - // Assignment operator. - -}; // class ARluNonSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluNonSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymStdEig:: -Copy(const ARluNonSymStdEig& other) -{ - - ARStdEig >:: Copy(other); - if (this->mode > 2) this->objOP->FactorAsI(this->sigmaR); - -} // Copy. - - -template -inline void ARluNonSymStdEig::ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI(this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluNonSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(this->objOP, &ARluNonSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluNonSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, - &ARluNonSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluNonSymStdEig:: -ARluNonSymStdEig(int nevp, ARluNonSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, - &ARluNonSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluNonSymStdEig:: -ARluNonSymStdEig(int nevp, ARluNonSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, - &ARluNonSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluNonSymStdEig& ARluNonSymStdEig:: -operator=(const ARluNonSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLSNSYM_H diff --git a/src/external/arpack++/include/arlspdef.h b/src/external/arpack++/include/arlspdef.h deleted file mode 100644 index 3dec1cc7..00000000 --- a/src/external/arpack++/include/arlspdef.h +++ /dev/null @@ -1,610 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLSpDef.h. - ALTERED version of slu_sdefs.h slu_ddefs.h slu_cdefs.h slu_zdefs.h - (from SuperLU 3.0 package). -*/ - - -#ifndef __SUPERLU_SP_DEFS /* allow multiple inclusions */ -#define __SUPERLU_SP_DEFS - -/* - * File name: sp_defs.h - * Purpose: Sparse matrix types and function prototypes - * History: - */ -#include "arlnames.h" -#include "arlsupm.h" -#include "arlcomp.h" -#include "arlutil.h" -#ifdef _CRAY -#include -#include -#endif - -/* Define my integer type int_t */ -typedef int int_t; /* default */ - -// /* No of marker arrays used in the symbolic factorization, -// each of size n */ -// #define NO_MARKER 3 -// #define NUM_TEMPV(m,w,t,b) ( MAX(m, (t + b)*w) ) -// -// typedef enum {LUSUP, UCOL, LSUB, USUB} MemType; -// typedef enum {HEAD, TAIL} stack_end_t; -// typedef enum {SYSTEM, USER} LU_space_t; - -/* - * Global data structures used in LU factorization - - * - * nsuper: #supernodes = nsuper + 1, numbered [0, nsuper]. - * (xsup,supno): supno[i] is the supernode no to which i belongs; - * xsup(s) points to the beginning of the s-th supernode. - * e.g. supno 0 1 2 2 3 3 3 4 4 4 4 4 (n=12) - * xsup 0 1 2 4 7 12 - * Note: dfs will be performed on supernode rep. relative to the new - * row pivoting ordering - * - * (xlsub,lsub): lsub[*] contains the compressed subscript of - * rectangular supernodes; xlsub[j] points to the starting - * location of the j-th column in lsub[*]. Note that xlsub - * is indexed by column. - * Storage: original row subscripts - * - * During the course of sparse LU factorization, we also use - * (xlsub,lsub) for the purpose of symmetric pruning. For each - * supernode {s,s+1,...,t=s+r} with first column s and last - * column t, the subscript set - * lsub[j], j=xlsub[s], .., xlsub[s+1]-1 - * is the structure of column s (i.e. structure of this supernode). - * It is used for the storage of numerical values. - * Furthermore, - * lsub[j], j=xlsub[t], .., xlsub[t+1]-1 - * is the structure of the last column t of this supernode. - * It is for the purpose of symmetric pruning. Therefore, the - * structural subscripts can be rearranged without making physical - * interchanges among the numerical values. - * - * However, if the supernode has only one column, then we - * only keep one set of subscripts. For any subscript interchange - * performed, similar interchange must be done on the numerical - * values. - * - * The last column structures (for pruning) will be removed - * after the numercial LU factorization phase. - * - * (xlusup,lusup): lusup[*] contains the numerical values of the - * rectangular supernodes; xlusup[j] points to the starting - * location of the j-th column in storage vector lusup[*] - * Note: xlusup is indexed by column. - * Each rectangular supernode is stored by column-major - * scheme, consistent with Fortran 2-dim array storage. - * - * (xusub,ucol,usub): ucol[*] stores the numerical values of - * U-columns outside the rectangular supernodes. The row - * subscript of nonzero ucol[k] is stored in usub[k]. - * xusub[i] points to the starting location of column i in ucol. - * Storage: new row subscripts; that is subscripts of PA. - */ - -typedef struct { - int *xsup; /* supernode and column mapping */ - int *supno; - int *lsub; /* compressed L subscripts */ - int *xlsub; - float *lusup; /* L supernodes */ - int *xlusup; - float *ucol; /* U columns */ - int *usub; - int *xusub; - int nzlmax; /* current max size of lsub */ - int nzumax; /* " " " ucol */ - int nzlumax; /* " " " lusup */ - int n; /* number of columns in the matrix */ - LU_space_t MemModel; /* 0 - system malloc'd; 1 - user provided */ - int num_expansions; - ExpHeader *expanders; /* Array of pointers to 4 types of memory */ - LU_stack_t stack; /* use user supplied memory */ -} sGlobalLU_t; - -typedef struct { - int *xsup; /* supernode and column mapping */ - int *supno; - int *lsub; /* compressed L subscripts */ - int *xlsub; - double *lusup; /* L supernodes */ - int *xlusup; - double *ucol; /* U columns */ - int *usub; - int *xusub; - int nzlmax; /* current max size of lsub */ - int nzumax; /* " " " ucol */ - int nzlumax; /* " " " lusup */ - int n; /* number of columns in the matrix */ - LU_space_t MemModel; /* 0 - system malloc'd; 1 - user provided */ - int num_expansions; - ExpHeader *expanders; /* Array of pointers to 4 types of memory */ - LU_stack_t stack; /* use user supplied memory */ -} dGlobalLU_t; - -typedef struct { - int *xsup; /* supernode and column mapping */ - int *supno; - int *lsub; /* compressed L subscripts */ - int *xlsub; - lscomplex *lusup; /* L supernodes */ - int *xlusup; - lscomplex *ucol; /* U columns */ - int *usub; - int *xusub; - int nzlmax; /* current max size of lsub */ - int nzumax; /* " " " ucol */ - int nzlumax; /* " " " lusup */ - int n; /* number of columns in the matrix */ - LU_space_t MemModel; /* 0 - system malloc'd; 1 - user provided */ - int num_expansions; - ExpHeader *expanders; /* Array of pointers to 4 types of memory */ - LU_stack_t stack; /* use user supplied memory */ -} cGlobalLU_t; - -typedef struct { - int *xsup; /* supernode and column mapping */ - int *supno; - int *lsub; /* compressed L subscripts */ - int *xlsub; - ldcomplex *lusup; /* L supernodes */ - int *xlusup; - ldcomplex *ucol; /* U columns */ - int *usub; - int *xusub; - int nzlmax; /* current max size of lsub */ - int nzumax; /* " " " ucol */ - int nzlumax; /* " " " lusup */ - int n; /* number of columns in the matrix */ - LU_space_t MemModel; /* 0 - system malloc'd; 1 - user provided */ - int num_expansions; - ExpHeader *expanders; /* Array of pointers to 4 types of memory */ - LU_stack_t stack; /* use user supplied memory */ -} zGlobalLU_t; - -// typedef struct { -// int panel_size; -// int relax; -// float diag_pivot_thresh; -// float drop_tol; -// } sfactor_param_t; -// -// typedef struct { -// int panel_size; -// int relax; -// double diag_pivot_thresh; -// double drop_tol; -// } dfactor_param_t; -// -//typedef struct { -// float for_lu; -// float total_needed; -// int expansions; -//} mem_usage_t; - -#ifdef __cplusplus -extern "C" { -#endif - -/* Driver routines */ -extern void -sgssv(superlu_options_t *, SuperMatrix *, int *, int *, SuperMatrix *, - SuperMatrix *, SuperMatrix *, SuperLUStat_t *, int *); -extern void -dgssv(superlu_options_t *, SuperMatrix *, int *, int *, SuperMatrix *, - SuperMatrix *, SuperMatrix *, SuperLUStat_t *, int *); -extern void -cgssv(superlu_options_t *, SuperMatrix *, int *, int *, SuperMatrix *, - SuperMatrix *, SuperMatrix *, SuperLUStat_t *, int *); -extern void -zgssv(superlu_options_t *, SuperMatrix *, int *, int *, SuperMatrix *, - SuperMatrix *, SuperMatrix *, SuperLUStat_t *, int *); -extern void -sgssvx(superlu_options_t *, SuperMatrix *, int *, int *, int *, - char *, float *, float *, SuperMatrix *, SuperMatrix *, - void *, int, SuperMatrix *, SuperMatrix *, - float *, float *, float *, float *, - sGlobalLU_t *, mem_usage_t *, SuperLUStat_t *, int *); -extern void -dgssvx(superlu_options_t *, SuperMatrix *, int *, int *, int *, - char *, double *, double *, SuperMatrix *, SuperMatrix *, - void *, int, SuperMatrix *, SuperMatrix *, - double *, double *, double *, double *, - dGlobalLU_t *, mem_usage_t *, SuperLUStat_t *, int *); -extern void -cgssvx(superlu_options_t *, SuperMatrix *, int *, int *, int *, - char *, float *, float *, SuperMatrix *, SuperMatrix *, - void *, int, SuperMatrix *, SuperMatrix *, - float *, float *, float *, float *, - cGlobalLU_t *, mem_usage_t *, SuperLUStat_t *, int *); -extern void -zgssvx(superlu_options_t *, SuperMatrix *, int *, int *, int *, - char *, double *, double *, SuperMatrix *, SuperMatrix *, - void *, int, SuperMatrix *, SuperMatrix *, - double *, double *, double *, double *, - zGlobalLU_t *, mem_usage_t *, SuperLUStat_t *, int *); - -/* Supernodal LU factor related */ -extern void -sCreate_CompCol_Matrix(SuperMatrix *, int, int, int, float *, - int *, int *, Stype_t, Dtype_t, Mtype_t); -extern void -dCreate_CompCol_Matrix(SuperMatrix *, int, int, int, double *, - int *, int *, Stype_t, Dtype_t, Mtype_t); -extern void -cCreate_CompCol_Matrix(SuperMatrix *, int, int, int, lscomplex *, - int *, int *, Stype_t, Dtype_t, Mtype_t); -extern void -zCreate_CompCol_Matrix(SuperMatrix *, int, int, int, ldcomplex *, - int *, int *, Stype_t, Dtype_t, Mtype_t); -extern void -sCreate_CompRow_Matrix(SuperMatrix *, int, int, int, float *, - int *, int *, Stype_t, Dtype_t, Mtype_t); -extern void -dCreate_CompRow_Matrix(SuperMatrix *, int, int, int, double *, - int *, int *, Stype_t, Dtype_t, Mtype_t); -extern void -cCreate_CompRow_Matrix(SuperMatrix *, int, int, int, lscomplex *, - int *, int *, Stype_t, Dtype_t, Mtype_t); -extern void -zCreate_CompRow_Matrix(SuperMatrix *, int, int, int, ldcomplex *, - int *, int *, Stype_t, Dtype_t, Mtype_t); -extern void -sCopy_CompCol_Matrix(SuperMatrix *, SuperMatrix *); -extern void -dCopy_CompCol_Matrix(SuperMatrix *, SuperMatrix *); -extern void -cCopy_CompCol_Matrix(SuperMatrix *, SuperMatrix *); -extern void -zCopy_CompCol_Matrix(SuperMatrix *, SuperMatrix *); -extern void -sCreate_Dense_Matrix(SuperMatrix *, int, int, float *, int, - Stype_t, Dtype_t, Mtype_t); -extern void -dCreate_Dense_Matrix(SuperMatrix *, int, int, double *, int, - Stype_t, Dtype_t, Mtype_t); -extern void -cCreate_Dense_Matrix(SuperMatrix *, int, int, lscomplex *, int, - Stype_t, Dtype_t, Mtype_t); -extern void -zCreate_Dense_Matrix(SuperMatrix *, int, int, ldcomplex *, int, - Stype_t, Dtype_t, Mtype_t); -extern void -sCreate_SuperNode_Matrix(SuperMatrix *, int, int, int, float *, - int *, int *, int *, int *, int *, - Stype_t, Dtype_t, Mtype_t); -extern void -dCreate_SuperNode_Matrix(SuperMatrix *, int, int, int, double *, - int *, int *, int *, int *, int *, - Stype_t, Dtype_t, Mtype_t); -extern void -cCreate_SuperNode_Matrix(SuperMatrix *, int, int, int, lscomplex *, - int *, int *, int *, int *, int *, - Stype_t, Dtype_t, Mtype_t); -extern void -zCreate_SuperNode_Matrix(SuperMatrix *, int, int, int, ldcomplex *, - int *, int *, int *, int *, int *, - Stype_t, Dtype_t, Mtype_t); -extern void -sCopy_Dense_Matrix(int, int, float *, int, float *, int); -extern void -dCopy_Dense_Matrix(int, int, double *, int, double *, int); -extern void -cCopy_Dense_Matrix(int, int, lscomplex *, int, lscomplex *, int); -extern void -zCopy_Dense_Matrix(int, int, ldcomplex *, int, ldcomplex *, int); - -// extern void Destroy_SuperMatrix_Store(SuperMatrix *); -// extern void Destroy_CompCol_Matrix(SuperMatrix *); -// extern void Destroy_SuperNode_Matrix(SuperMatrix *); -// extern void Destroy_CompCol_Permuted(SuperMatrix *); -// extern void Destroy_Dense_Matrix(SuperMatrix *); -// extern void get_perm_c(int, SuperMatrix *, int *); -// extern void sp_preorder (char*, SuperMatrix*, int*, int*, SuperMatrix*); -// // extern void countnz (const int, int *, int *, int *, sGlobalLU_t *); -// // extern void fixupL (const int, const int *, sGlobalLU_t *); - -extern void sallocateA (int, int, float **, int **, int **); -extern void dallocateA (int, int, double **, int **, int **); -extern void callocateA (int, int, lscomplex **, int **, int **); -extern void zallocateA (int, int, ldcomplex **, int **, int **); -extern void sgstrf (superlu_options_t*, SuperMatrix*, - int, int, int*, void *, int, int *, int *, - SuperMatrix *, SuperMatrix *, sGlobalLU_t *, SuperLUStat_t*, int *); -extern void dgstrf (superlu_options_t*, SuperMatrix*, - int, int, int*, void *, int, int *, int *, - SuperMatrix *, SuperMatrix *, dGlobalLU_t *, SuperLUStat_t*, int *); -extern void cgstrf (superlu_options_t*, SuperMatrix*, - int, int, int*, void *, int, int *, int *, - SuperMatrix *, SuperMatrix *, cGlobalLU_t *, SuperLUStat_t*, int *); -extern void zgstrf (superlu_options_t*, SuperMatrix*, - int, int, int*, void *, int, int *, int *, - SuperMatrix *, SuperMatrix *, zGlobalLU_t *, SuperLUStat_t*, int *); -extern int ssnode_dfs (const int, const int, const int *, const int *, - const int *, int *, int *, sGlobalLU_t *); -extern int dsnode_dfs (const int, const int, const int *, const int *, - const int *, int *, int *, dGlobalLU_t *); -extern int csnode_dfs (const int, const int, const int *, const int *, - const int *, int *, int *, cGlobalLU_t *); -extern int zsnode_dfs (const int, const int, const int *, const int *, - const int *, int *, int *, zGlobalLU_t *); -extern int ssnode_bmod (const int, const int, const int, float *, - float *, sGlobalLU_t *, SuperLUStat_t*); -extern int dsnode_bmod (const int, const int, const int, double *, - double *, dGlobalLU_t *, SuperLUStat_t*); -extern int csnode_bmod (const int, const int, const int, lscomplex *, - lscomplex *, cGlobalLU_t *, SuperLUStat_t*); -extern int zsnode_bmod (const int, const int, const int, ldcomplex *, - ldcomplex *, zGlobalLU_t *, SuperLUStat_t*); -extern void spanel_dfs (const int, const int, const int, SuperMatrix *, - int *, int *, float *, int *, int *, int *, - int *, int *, int *, int *, sGlobalLU_t *); -extern void dpanel_dfs (const int, const int, const int, SuperMatrix *, - int *, int *, double *, int *, int *, int *, - int *, int *, int *, int *, dGlobalLU_t *); -extern void cpanel_dfs (const int, const int, const int, SuperMatrix *, - int *, int *, lscomplex *, int *, int *, int *, - int *, int *, int *, int *, cGlobalLU_t *); -extern void zpanel_dfs (const int, const int, const int, SuperMatrix *, - int *, int *, ldcomplex *, int *, int *, int *, - int *, int *, int *, int *, zGlobalLU_t *); -extern void spanel_bmod (const int, const int, const int, const int, - float *, float *, int *, int *, - sGlobalLU_t *, SuperLUStat_t*); -extern void dpanel_bmod (const int, const int, const int, const int, - double *, double *, int *, int *, - dGlobalLU_t *, SuperLUStat_t*); -extern void cpanel_bmod (const int, const int, const int, const int, - lscomplex *, lscomplex *, int *, int *, - cGlobalLU_t *, SuperLUStat_t*); -extern void zpanel_bmod (const int, const int, const int, const int, - ldcomplex *, ldcomplex *, int *, int *, - zGlobalLU_t *, SuperLUStat_t*); -extern int scolumn_dfs (const int, const int, int *, int *, int *, int *, - int *, int *, int *, int *, int *, sGlobalLU_t *); -extern int dcolumn_dfs (const int, const int, int *, int *, int *, int *, - int *, int *, int *, int *, int *, dGlobalLU_t *); -extern int ccolumn_dfs (const int, const int, int *, int *, int *, int *, - int *, int *, int *, int *, int *, cGlobalLU_t *); -extern int zcolumn_dfs (const int, const int, int *, int *, int *, int *, - int *, int *, int *, int *, int *, zGlobalLU_t *); -extern int scolumn_bmod (const int, const int, float *, - float *, int *, int *, int, sGlobalLU_t *, SuperLUStat_t*); -extern int dcolumn_bmod (const int, const int, double *, - double *, int *, int *, int, dGlobalLU_t *, SuperLUStat_t*); -extern int ccolumn_bmod (const int, const int, lscomplex *, - lscomplex *, int *, int *, int, cGlobalLU_t *, SuperLUStat_t*); -extern int zcolumn_bmod (const int, const int, ldcomplex *, - ldcomplex *, int *, int *, int, zGlobalLU_t *, SuperLUStat_t*); -extern int scopy_to_ucol (int, int, int *, int *, int *, - float *, sGlobalLU_t *); -extern int dcopy_to_ucol (int, int, int *, int *, int *, - double *, dGlobalLU_t *); -extern int ccopy_to_ucol (int, int, int *, int *, int *, - lscomplex *, cGlobalLU_t *); -extern int zcopy_to_ucol (int, int, int *, int *, int *, - ldcomplex *, zGlobalLU_t *); -extern int spivotL (const int, const float, int *, int *, - int *, int *, int *, sGlobalLU_t *, SuperLUStat_t*); -extern int dpivotL (const int, const double, int *, int *, - int *, int *, int *, dGlobalLU_t *, SuperLUStat_t*); -extern int cpivotL (const int, const float, int *, int *, - int *, int *, int *, cGlobalLU_t *, SuperLUStat_t*); -extern int zpivotL (const int, const double, int *, int *, - int *, int *, int *, zGlobalLU_t *, SuperLUStat_t*); -extern void spruneL (const int, const int *, const int, const int, - const int *, const int *, int *, sGlobalLU_t *); -extern void dpruneL (const int, const int *, const int, const int, - const int *, const int *, int *, dGlobalLU_t *); -extern void cpruneL (const int, const int *, const int, const int, - const int *, const int *, int *, cGlobalLU_t *); -extern void zpruneL (const int, const int *, const int, const int, - const int *, const int *, int *, zGlobalLU_t *); -extern void sreadmt (int *, int *, int *, float **, int **, int **); -extern void dreadmt (int *, int *, int *, double **, int **, int **); -extern void creadmt (int *, int *, int *, lscomplex **, int **, int **); -extern void zreadmt (int *, int *, int *, ldcomplex **, int **, int **); -extern void sGenXtrue (int, int, float *, int); -extern void dGenXtrue (int, int, double *, int); -extern void cGenXtrue (int, int, lscomplex *, int); -extern void zGenXtrue (int, int, ldcomplex *, int); -extern void sFillRHS (trans_t, int, float *, int, SuperMatrix *, - SuperMatrix *); -extern void dFillRHS (trans_t, int, double *, int, SuperMatrix *, - SuperMatrix *); -extern void cFillRHS (trans_t, int, lscomplex *, int, SuperMatrix *, - SuperMatrix *); -extern void zFillRHS (trans_t, int, ldcomplex *, int, SuperMatrix *, - SuperMatrix *); -extern void sgstrs (trans_t, SuperMatrix *, SuperMatrix *, int *, int *, - SuperMatrix *, SuperLUStat_t*, int *); -extern void dgstrs (trans_t, SuperMatrix *, SuperMatrix *, int *, int *, - SuperMatrix *, SuperLUStat_t*, int *); -extern void cgstrs (trans_t, SuperMatrix *, SuperMatrix *, int *, int *, - SuperMatrix *, SuperLUStat_t*, int *); -extern void zgstrs (trans_t, SuperMatrix *, SuperMatrix *, int *, int *, - SuperMatrix *, SuperLUStat_t*, int *); - - -/* Driver related */ - -extern void sgsequ (SuperMatrix *, float *, float *, float *, - float *, float *, int *); -extern void dgsequ (SuperMatrix *, double *, double *, double *, - double *, double *, int *); -extern void cgsequ (SuperMatrix *, float *, float *, float *, - float *, float *, int *); -extern void zgsequ (SuperMatrix *, double *, double *, double *, - double *, double *, int *); -extern void slaqgs (SuperMatrix *, float *, float *, float, - float, float, char *); -extern void dlaqgs (SuperMatrix *, double *, double *, double, - double, double, char *); -extern void claqgs (SuperMatrix *, float *, float *, float, - float, float, char *); -extern void zlaqgs (SuperMatrix *, double *, double *, double, - double, double, char *); -extern void sgscon (char *, SuperMatrix *, SuperMatrix *, - float, float *, SuperLUStat_t*, int *); -extern void dgscon (char *, SuperMatrix *, SuperMatrix *, - double, double *, SuperLUStat_t*, int *); -extern void cgscon (char *, SuperMatrix *, SuperMatrix *, - float, float *, SuperLUStat_t*, int *); -extern void zgscon (char *, SuperMatrix *, SuperMatrix *, - double, double *, SuperLUStat_t*, int *); - -extern float sPivotGrowth(int, SuperMatrix *, int *, - SuperMatrix *, SuperMatrix *); -extern double dPivotGrowth(int, SuperMatrix *, int *, - SuperMatrix *, SuperMatrix *); -extern float cPivotGrowth(int, SuperMatrix *, int *, - SuperMatrix *, SuperMatrix *); -extern double zPivotGrowth(int, SuperMatrix *, int *, - SuperMatrix *, SuperMatrix *); -extern void sgsrfs (trans_t, SuperMatrix *, SuperMatrix *, - SuperMatrix *, int *, int *, char *, float *, - float *, SuperMatrix *, SuperMatrix *, - float *, float *, SuperLUStat_t*, int *); -extern void dgsrfs (trans_t, SuperMatrix *, SuperMatrix *, - SuperMatrix *, int *, int *, char *, double *, - double *, SuperMatrix *, SuperMatrix *, - double *, double *, SuperLUStat_t*, int *); -extern void cgsrfs (trans_t, SuperMatrix *, SuperMatrix *, - SuperMatrix *, int *, int *, char *, float *, - float *, SuperMatrix *, SuperMatrix *, - float *, float *, SuperLUStat_t*, int *); -extern void zgsrfs (trans_t, SuperMatrix *, SuperMatrix *, - SuperMatrix *, int *, int *, char *, double *, - double *, SuperMatrix *, SuperMatrix *, - double *, double *, SuperLUStat_t*, int *); - -extern int sp_strsv (char *, char *, char *, SuperMatrix *, - SuperMatrix *, float *, SuperLUStat_t*, int *); -extern int sp_dtrsv (char *, char *, char *, SuperMatrix *, - SuperMatrix *, double *, SuperLUStat_t*, int *); -extern int sp_ctrsv (char *, char *, char *, SuperMatrix *, - SuperMatrix *, lscomplex *, SuperLUStat_t*, int *); -extern int sp_ztrsv (char *, char *, char *, SuperMatrix *, - SuperMatrix *, ldcomplex *, SuperLUStat_t*, int *); -extern int sp_sgemv (char *, float, SuperMatrix *, float *, - int, float, float *, int); -extern int sp_dgemv (char *, double, SuperMatrix *, double *, - int, double, double *, int); -extern int sp_cgemv (char *, lscomplex, SuperMatrix *, lscomplex *, - int, lscomplex, lscomplex *, int); -extern int sp_zgemv (char *, ldcomplex, SuperMatrix *, ldcomplex *, - int, ldcomplex, ldcomplex *, int); - -extern int sp_sgemm (char *, char *, int, int, int, float, - SuperMatrix *, float *, int, float, - float *, int); -extern int sp_dgemm (char *, char *, int, int, int, double, - SuperMatrix *, double *, int, double, - double *, int); -extern int sp_cgemm (char *, char *, int, int, int, lscomplex, - SuperMatrix *, lscomplex *, int, lscomplex, - lscomplex *, int); -extern int sp_zgemm (char *, char *, int, int, int, ldcomplex, - SuperMatrix *, ldcomplex *, int, ldcomplex, - ldcomplex *, int); - -/* Memory-related */ -extern int sLUMemInit (fact_t, void *, int, int, int, int, int, - float, SuperMatrix *, SuperMatrix *, - sGlobalLU_t *, int **, float **); -extern int dLUMemInit (fact_t, void *, int, int, int, int, int, - double, SuperMatrix *, SuperMatrix *, - dGlobalLU_t *, int **, double **); -extern int cLUMemInit (fact_t, void *, int, int, int, int, int, - float, SuperMatrix *, SuperMatrix *, - cGlobalLU_t *, int **, lscomplex **); -extern int zLUMemInit (fact_t, void *, int, int, int, int, int, - double, SuperMatrix *, SuperMatrix *, - zGlobalLU_t *, int **, ldcomplex **); -extern void sSetRWork (int, int, float *, float **, float **); -extern void dSetRWork (int, int, double *, double **, double **); -extern void cSetRWork (int, int, lscomplex *, lscomplex **, lscomplex **); -extern void zSetRWork (int, int, ldcomplex *, ldcomplex **, ldcomplex **); -extern void sLUWorkFree (int *, float *, sGlobalLU_t *); -extern void dLUWorkFree (int *, double *, dGlobalLU_t *); -extern void cLUWorkFree (int *, lscomplex *, cGlobalLU_t *); -extern void zLUWorkFree (int *, ldcomplex *, zGlobalLU_t *); -extern int sLUMemXpand (int, int, MemType, int *, sGlobalLU_t *); -extern int dLUMemXpand (int, int, MemType, int *, dGlobalLU_t *); -extern int cLUMemXpand (int, int, MemType, int *, cGlobalLU_t *); -extern int zLUMemXpand (int, int, MemType, int *, zGlobalLU_t *); - -extern float *floatMalloc(int); -extern double *doubleMalloc(int); -extern lscomplex *complexMalloc(int); -extern ldcomplex *doublecomplexMalloc(int); -extern float *floatCalloc(int); -extern double *doubleCalloc(int); -extern lscomplex *complexCalloc(int); -extern ldcomplex *doublecomplexCalloc(int); -extern int smemory_usage(const int, const int, const int, const int); -extern int dmemory_usage(const int, const int, const int, const int); -extern int cmemory_usage(const int, const int, const int, const int); -extern int zmemory_usage(const int, const int, const int, const int); -extern int sQuerySpace (SuperMatrix *, SuperMatrix *, mem_usage_t *); -extern int dQuerySpace (SuperMatrix *, SuperMatrix *, mem_usage_t *); -extern int cQuerySpace (SuperMatrix *, SuperMatrix *, mem_usage_t *); -extern int zQuerySpace (SuperMatrix *, SuperMatrix *, mem_usage_t *); - -/* Auxiliary routines */ -extern void sreadhb(FILE *, int *, int *, int *, float **, int **, int **); -extern void dreadhb(FILE *, int *, int *, int *, double **, int **, int **); -extern void creadhb(FILE *, int *, int *, int *, lscomplex **, int **, int **); -extern void zreadhb(FILE *, int *, int *, int *, ldcomplex **, int **, int **); -extern void sCompRow_to_CompCol(int, int, int, float*, int*, int*, - float **, int **, int **); -extern void dCompRow_to_CompCol(int, int, int, double*, int*, int*, - double **, int **, int **); -extern void cCompRow_to_CompCol(int, int, int, lscomplex*, int*, int*, - lscomplex **, int **, int **); -extern void zCompRow_to_CompCol(int, int, int, ldcomplex*, int*, int*, - ldcomplex **, int **, int **); -extern void sfill (float *, int, float); -extern void dfill (double *, int, double); -extern void cfill (lscomplex *, int, lscomplex); -extern void zfill (ldcomplex *, int, ldcomplex); -extern void sinf_norm_error (int, SuperMatrix *, float *); -extern void dinf_norm_error (int, SuperMatrix *, double *); -extern void cinf_norm_error (int, SuperMatrix *, lscomplex *); -extern void zinf_norm_error (int, SuperMatrix *, ldcomplex *); -// extern void PrintPerf (SuperMatrix *, SuperMatrix *, mem_usage_t *, -// float, float, float *, float *, char *); - -/* Routines for debugging */ -extern void sPrint_CompCol_Matrix(char *, SuperMatrix *); -extern void dPrint_CompCol_Matrix(char *, SuperMatrix *); -extern void cPrint_CompCol_Matrix(char *, SuperMatrix *); -extern void zPrint_CompCol_Matrix(char *, SuperMatrix *); -extern void sPrint_SuperNode_Matrix(char *, SuperMatrix *); -extern void dPrint_SuperNode_Matrix(char *, SuperMatrix *); -extern void cPrint_SuperNode_Matrix(char *, SuperMatrix *); -extern void zPrint_SuperNode_Matrix(char *, SuperMatrix *); -extern void sPrint_Dense_Matrix(char *, SuperMatrix *); -extern void dPrint_Dense_Matrix(char *, SuperMatrix *); -extern void cPrint_Dense_Matrix(char *, SuperMatrix *); -extern void zPrint_Dense_Matrix(char *, SuperMatrix *); -// extern void print_lu_col(char *, int, int, int *, sGlobalLU_t *); -// extern void check_tempv(int, float *); - -/* Reordering routine */ - - -#ifdef __cplusplus - } -#endif - -#endif /* __SUPERLU_SP_DEFS */ - diff --git a/src/external/arpack++/include/arlspen.h b/src/external/arpack++/include/arlspen.h deleted file mode 100644 index 52e2bb1a..00000000 --- a/src/external/arpack++/include/arlspen.h +++ /dev/null @@ -1,679 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLSPen.h. - Arpack++ class ARluSymPencil definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARLSPEN_H -#define ARLSPEN_H - -#include - -#include "arch.h" -#include "arerror.h" -#include "blas1c.h" -#include "superluc.h" -#include "arlspdef.h" -#include "arlutil.h" -#include "arlsmat.h" - - -template -class ARluSymPencil -{ - - protected: - - bool factored; - int* permc; - int* permr; - char part; - char uplo; - ARluSymMatrix* A; - ARluSymMatrix* B; - SuperMatrix L; - SuperMatrix U; - SuperLUStat_t stat; - - virtual void Copy(const ARluSymPencil& other); - - void ClearMem(); - - void SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], - int yind[], int ny, ARTYPE z[], int zind[], int& nz); - - void ExpandAsB(int n, NCformat& AsB); - - void SubtractAsB(int n, ARTYPE sigma, NCformat& A, - NCformat& B, NCformat& AsB); - - public: - - bool IsFactored() { return factored; } - - void FactorAsB(ARTYPE sigma); - - void MultAv(ARTYPE* v, ARTYPE* w) { A->MultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - - void MultInvAsBv(ARTYPE* v, ARTYPE* w); - - void DefineMatrices(ARluSymMatrix& Ap, ARluSymMatrix& Bp); - - ARluSymPencil(); - // Short constructor that does nothing. - - ARluSymPencil(ARluSymMatrix& Ap, ARluSymMatrix& Bp); - // Long constructor. - - ARluSymPencil(const ARluSymPencil& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymPencil() { ClearMem(); } - // Destructor. - - ARluSymPencil& operator=(const ARluSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARluSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymPencil:: -Copy(const ARluSymPencil& other) -{ - - factored = other.factored; - part = other.part; - uplo = other.uplo; - A = other.A; - B = other.B; - - // Throwing the original factorization away (this procedure - // is really awkward, but it is necessary because there - // is no copy function for matrices L and U in the SuperLU - // library and it is not a good idea to do this kind of deep - // copy here). - - if (factored) { - ArpackError(ArpackError::DISCARDING_FACTORS, "ARluSymPencil"); - factored = false; - } - -} // Copy. - - -template -void ARluSymPencil::ClearMem() -{ - - if (factored) { - Destroy_SuperNode_Matrix(&L); - Destroy_CompCol_Matrix(&U); - StatFree(&stat); - delete[] permc; - delete[] permr; - permc = NULL; - permr = NULL; - } - -} // ClearMem. - - -template -void ARluSymPencil:: -SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], - int yind[], int ny, ARTYPE z[], int zind[], int& nz) -// A strongly sequential (and inefficient) sparse saxpy algorithm. -{ - - int ix, iy; - - nz = 0; - if ((nx == 0) || (a == (ARTYPE)0)) { - copy(ny,y,1,z,1); - for (iy=0; iy!=ny; iy++) zind[iy] = yind[iy]; - nz = ny; - return; - } - if (ny == 0) { - copy(nx,x,1,z,1); - scal(nx,a,z,1); - for (ix=0; ix!=nx; ix++) zind[ix] = xind[ix]; - nz = nx; - return; - } - ix = 0; - iy = 0; - while (true) { - if (xind[ix] == yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]+y[iy++]; - if ((ix == nx)||(iy == ny)) break; - } - else if (xind[ix] < yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]; - if (ix == nx) break; - } - else { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - if (iy == ny) break; - } - } - while (iy < ny) { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - } - while (ix < nx) { - zind[nz] = xind[ix]; - z[nz++] = x[ix++]; - } - -} // SparseSaxpy. - - -template -void ARluSymPencil::ExpandAsB(int n, NCformat& AsB) -{ - - int i, j, k; - int *pcol, *pos, *col, *ind; - ARTYPE *val; - - // simplifying the notation. - - val = (ARTYPE*)AsB.nzval; - ind = AsB.rowind; - col = AsB.colptr; - - // Initializing vectors. - - pcol = new int[n+1]; - pos = new int[n+1]; - for (i=0; i<=n; i++) pcol[i] = col[i]; - for (i=0; i<=n; i++) pos[i] = 0; - - // Counting the elements in each column of AsB. - - if (uplo == 'U') { - - for (i=0; i!=n; i++) { - k = pcol[i+1]; - if ((k!=pcol[i])&&(ind[k-1]==i)) k--; - for (j=pcol[i]; j0; i--) col[i] += pos[i-1]; - - // Expanding A. - - if (uplo == 'U') { - - for (i=n-1; i>=0; i--) { - pos[i] = col[i]+pcol[i+1]-pcol[i]; - k = pos[i]-1; - for (j=pcol[i+1]-1; j>=pcol[i]; j--) { - val[k] = val[j]; - ind[k--] = ind[j]; - } - } - for (i=1; icol[i])&&(ind[k-1]==i)) k--; - for (j=col[i]; j=0; i--) { - k = col[i+1]-1; - for (j=pcol[i+1]-1; j>=pcol[i]; j--) { - val[k] = val[j]; - ind[k--] = ind[j]; - } - pos[i] = col[i]; - } - for (i=0; i<(n-1); i++) { - k = col[i+1]-pcol[i+1]+pcol[i]; - if ((k -void ARluSymPencil:: -SubtractAsB(int n, ARTYPE sigma, NCformat& matA, NCformat& matB, NCformat& AsB) -{ - - int i, acol, bcol, asbcol, scol; - ARTYPE* anzval; - ARTYPE* bnzval; - ARTYPE* asbnzval; - - // Quitting function if A->uplo is not equal to B->uplo. - - if ((A->uplo != B->uplo)&&(sigma != (ARTYPE)0)) { - throw ArpackError(ArpackError::DIFFERENT_TRIANGLES, - "ARluSymPencil::SubtractAsB"); - } - uplo = A->uplo; - - // Telling the compiler that nzval must ve viewed as a vector of ARTYPE. - - anzval = (ARTYPE*)matA.nzval; - bnzval = (ARTYPE*)matB.nzval; - asbnzval = (ARTYPE*)AsB.nzval; - - // Subtracting sigma*B from A. - - AsB.colptr[0] = 0; - asbcol = 0; - - for (i=0; i!=n; i++) { - bcol = matB.colptr[i]; - acol = matA.colptr[i]; - SparseSaxpy(-sigma, &bnzval[bcol], &matB.rowind[bcol], - matB.colptr[i+1]-bcol, &anzval[acol], &matA.rowind[acol], - matA.colptr[i+1]-acol, &asbnzval[asbcol], - &AsB.rowind[asbcol], scol); - asbcol += scol; - AsB.colptr[i+1] = asbcol; - } - - // Expanding AsB. - - ExpandAsB(n, AsB); - -} // SubtractAsB. - - -// template -// void ARluSymPencil::FactorAsB(ARTYPE sigma) -// { -// -// // Quitting the function if A and B were not defined. -// -// if (!(A->IsDefined()&&B->IsDefined())) { -// throw ArpackError(ArpackError::DATA_UNDEFINED, -// "ARluSymPencil::FactorAsB"); -// } -// -// // Quitting the function if A and B are not square. -// -// if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { -// throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, -// "ARluSymPencil::FactorAsB"); -// } -// -// // Defining local variables. -// -// int nnzi, info; -// int* etree; -// int* irowi; -// int* pcoli; -// ARTYPE* asb; -// SuperMatrix AsB; -// SuperMatrix AC; -// NCformat* Astore; -// NCformat* Bstore; -// NCformat* AsBstore; -// -// // Deleting old versions of L, U, perm_r and perm_c. -// -// ClearMem(); -// -// // Setting default values for gstrf parameters. -// -// ARTYPE drop_tol = (ARTYPE)0; -// int panel_size = sp_ienv(1); -// int relax = sp_ienv(2); -// -// // Defining A and B format. -// -// Astore = (NCformat*)A->A.Store; -// Bstore = (NCformat*)B->A.Store; -// -// // Creating a temporary matrix AsB. -// -// nnzi = (Astore->nnz+Bstore->nnz)*2; -// irowi = new int[nnzi]; -// pcoli = new int[A->ncols()+1]; -// asb = new ARTYPE[nnzi]; -// Create_CompCol_Matrix(&AsB, A->nrows(), A->ncols(), nnzi, asb, -// irowi, pcoli, NC, GE); -// -// // Subtracting sigma*B from A and storing the result on AsB. -// -// AsBstore = (NCformat*)AsB.Store; -// SubtractAsB(A->ncols(), sigma, *Astore, *Bstore, *AsBstore); -// -// // Reserving memory for some vectors used in matrix decomposition. -// -// etree = new int[A->ncols()]; -// if (permc == NULL) permc = new int[A->ncols()]; -// if (permr == NULL) permr = new int[A->ncols()]; -// -// // Defining LUStat. -// -// StatInit(panel_size, relax); -// -// // Defining the column permutation of matrix AsB -// // (using minimum degree ordering on AsB'*AsB). -// -// get_perm_c(A->order, &AsB, permc); -// -// // Permuting columns of AsB and -// // creating the elimination tree of AsB'*AsB. -// -// sp_preorder("N", &AsB, permc, etree, &AC); -// -// // Decomposing AsB. -// -// gstrf("N",&AC, A->threshold, drop_tol, relax, panel_size, etree, -// NULL, 0, permr, permc, &L, &U, &info); -// -// // Deleting AC, AsB and etree. -// -// Destroy_CompCol_Permuted(&AC); -// Destroy_CompCol_Matrix(&AsB); -// delete[] etree; -// -// factored = (info == 0); -// -// // Handling errors. -// -// if (info < 0) { // Illegal argument. -// throw ArpackError(ArpackError::PARAMETER_ERROR, -// "ARluSymPencil::FactorAsB"); -// } -// else if (info > A->ncols()) { // Memory is not sufficient. -// throw ArpackError(ArpackError::MEMORY_OVERFLOW, -// "ARluSymPencil::FactorAsB"); -// } -// else if (info > 0) { // Matrix is singular. -// throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, -// "ARluSymPencil::FactorAsB"); -// } -// -// } // FactorAsB. - -template -void ARluSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARluSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARluSymPencil::FactorAsB"); - } - - // Defining local variables. - - int nnzi, info; - int* etree; - int* irowi; - int* pcoli; - ARTYPE* asb; - SuperMatrix AsB; - SuperMatrix AC; - NCformat* Astore; - NCformat* Bstore; - NCformat* AsBstore; - - // Deleting old versions of L, U, perm_r and perm_c. - - ClearMem(); - - // Setting default values for gstrf parameters. - - int panel_size = sp_ienv(1); - int relax = sp_ienv(2); - superlu_options_t options; - /* Set the default input options: - options.Fact = DOFACT; - options.Equil = YES; - options.ColPerm = COLAMD; - options.DiagPivotThresh = 1.0; - options.Trans = NOTRANS; - options.IterRefine = NOREFINE; - options.SymmetricMode = NO; - options.PivotGrowth = NO; - options.ConditionNumber = NO; - options.PrintStat = YES; - */ - set_default_options(&options); - - /* Now we modify the default options to use the symmetric mode. */ - options.SymmetricMode = YES; - options.ColPerm = MMD_AT_PLUS_A; - options.DiagPivotThresh = A->threshold; - - // Defining A and B format. - - Astore = (NCformat*)A->A.Store; - Bstore = (NCformat*)B->A.Store; - - // Creating a temporary matrix AsB. - - nnzi = (Astore->nnz+Bstore->nnz)*2; - irowi = new int[nnzi]; - pcoli = new int[A->ncols()+1]; - asb = new ARTYPE[nnzi]; - Create_CompCol_Matrix(&AsB, A->nrows(), A->ncols(), nnzi, asb, - irowi, pcoli, SLU_NC, SLU_GE); - - // Subtracting sigma*B from A and storing the result on AsB. - - AsBstore = (NCformat*)AsB.Store; - SubtractAsB(A->ncols(), sigma, *Astore, *Bstore, *AsBstore); - - // Reserving memory for some vectors used in matrix decomposition. - - etree = new int[A->ncols()]; - if (permc == NULL) permc = new int[A->ncols()]; - if (permr == NULL) permr = new int[A->ncols()]; - - // Defining LUStat. - -// StatInit(panel_size, relax); - SuperLUStat_t stat; - StatInit(&stat); - - // Defining the column permutation of matrix AsB - // (using minimum degree ordering on AsB'*AsB). - - get_perm_c(A->order, &AsB, permc); - - // Permuting columns of AsB and - // creating the elimination tree of AsB'*AsB. - - sp_preorder(&options, &AsB, permc, etree, &AC); - - // Decomposing AsB. - -// gstrf("N",&AC, A->threshold, drop_tol, relax, panel_size, etree, -// NULL, 0, permr, permc, &L, &U, &info); - gstrf(&options, &AC, relax, panel_size, etree, - NULL, 0, permc, permr, &L, &U, &stat, &info); - - // Deleting AC, AsB and etree. - - Destroy_CompCol_Permuted(&AC); - Destroy_CompCol_Matrix(&AsB); - delete[] etree; - - factored = (info == 0); - - // Handling errors. - - if (info < 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARluSymPencil::FactorAsB"); - } - else if (info > A->ncols()) { // Memory is not sufficient. - throw ArpackError(ArpackError::MEMORY_OVERFLOW, - "ARluSymPencil::FactorAsB"); - } - else if (info > 0) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARluSymPencil::FactorAsB"); - } - -} // FactorAsB. - -template -void ARluSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - copy(A->ncols(), w, 1, v, 1); - B->MultInvv(w, w); - -} // MultInvBAv. - - -template -void ARluSymPencil::MultInvAsBv(ARTYPE* v, ARTYPE* w) -{ - - // Quitting the function if AsB was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARluSymPencil::MultInvAsBv"); - } - - // Solving AsB.w = v. - - int info; - SuperMatrix RHS; - - copy(A->nrows(), v, 1, w, 1); - Create_Dense_Matrix(&RHS, A->nrows(), 1, w, A->nrows(), SLU_DN, SLU_GE); -// gstrs("N", &L, &U, permr, permc, &RHS, &info); - trans_t trans = NOTRANS; - StatInit(&stat); - - gstrs(trans, &L, &U, permc, permr, &RHS, &stat, &info); - - Destroy_SuperMatrix_Store(&RHS); // delete RHS.Store; - -} // MultInvAsBv. - - -template -inline void ARluSymPencil:: -DefineMatrices(ARluSymMatrix& Ap, ARluSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - permc = NULL; - permr = NULL; - - if ((A->n != B->n)||(A->m != B->m)) { - throw ArpackError(ArpackError::INCOMPATIBLE_SIZES, - "ARluSymMatrix::DefineMatrices"); - } - -} // DefineMatrices. - - -template -inline ARluSymPencil::ARluSymPencil() -{ - - factored = false; - part = 'N'; - permr = NULL; - permc = NULL; - -} // Short constructor. - - -template -inline ARluSymPencil:: -ARluSymPencil(ARluSymMatrix& Ap, ARluSymMatrix& Bp) -{ - - factored = false; - DefineMatrices(Ap, Bp); - -} // Long constructor. - - -template -ARluSymPencil& ARluSymPencil:: -operator=(const ARluSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLSPEN_H diff --git a/src/external/arpack++/include/arlssym.h b/src/external/arpack++/include/arlssym.h deleted file mode 100644 index fdab7e6b..00000000 --- a/src/external/arpack++/include/arlssym.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLSSym.h. - Arpack++ class ARluSymStdEig definition - (SuperLU version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Kristi Maschhoff - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARLSSYM_H -#define ARLSSYM_H - -#include -#include -#include "arch.h" -#include "arssym.h" -#include "arlsmat.h" - - -template -class ARluSymStdEig: - public virtual ARSymStdEig > { - - protected: - - // a) Protected function: - - virtual void Copy(const ARluSymStdEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // b) Public functions: - - // b.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // b.2) Constructors and destructor. - - ARluSymStdEig() { } - // Short constructor. - - ARluSymStdEig(int nevp, ARluSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymStdEig(int nevp, ARluSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluSymStdEig(const ARluSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymStdEig() { } - // Destructor. - - // c) Operators. - - ARluSymStdEig& operator=(const ARluSymStdEig& other); - // Assignment operator. - -}; // class ARluSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymStdEig::Copy(const ARluSymStdEig& other) -{ - - ARStdEig >:: Copy(other); - if (this->mode > 2) this->objOP->FactorAsI(this->sigmaR); - -} // Copy. - - -template -inline void ARluSymStdEig::ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI(this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(this->objOP, &ARluSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, &ARluSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARluSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, &ARluSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARluSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, &ARluSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluSymStdEig& ARluSymStdEig:: -operator=(const ARluSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARLSSYM_H diff --git a/src/external/arpack++/include/arlsupm.h b/src/external/arpack++/include/arlsupm.h deleted file mode 100644 index 7c8d2e8a..00000000 --- a/src/external/arpack++/include/arlsupm.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLSupM.h. - Unaltered copy of supermatrix.h (from SuperLU package). -*/ - -#ifndef __SUPERLU_SUPERMATRIX /* allow multiple inclusions */ -#define __SUPERLU_SUPERMATRIX - - -/******************************************** - * The matrix types are defined as follows. * - ********************************************/ -typedef enum { - SLU_NC, /* column-wise, no supernode */ - SLU_NCP, /* column-wise, column-permuted, no supernode - (The consecutive columns of nonzeros, after permutation, - may not be stored contiguously.) */ - SLU_NR, /* row-wize, no supernode */ - SLU_SC, /* column-wise, supernode */ - SLU_SCP, /* supernode, column-wise, permuted */ - SLU_SR, /* row-wise, supernode */ - SLU_DN, /* Fortran style column-wise storage for dense matrix */ - SLU_NR_loc /* distributed compressed row format */ -} Stype_t; - -typedef enum { - SLU_S, /* single */ - SLU_D, /* double */ - SLU_C, /* single complex */ - SLU_Z /* double complex */ -} Dtype_t; - -typedef enum { - SLU_GE, /* general */ - SLU_TRLU, /* lower triangular, unit diagonal */ - SLU_TRUU, /* upper triangular, unit diagonal */ - SLU_TRL, /* lower triangular */ - SLU_TRU, /* upper triangular */ - SLU_SYL, /* symmetric, store lower half */ - SLU_SYU, /* symmetric, store upper half */ - SLU_HEL, /* Hermitian, store lower half */ - SLU_HEU /* Hermitian, store upper half */ -} Mtype_t; - -typedef struct { - Stype_t Stype; /* Storage type: interprets the storage structure - pointed to by *Store. */ - Dtype_t Dtype; /* Data type. */ - Mtype_t Mtype; /* Matrix type: describes the mathematical property of - the matrix. */ - int nrow; /* number of rows */ - int ncol; /* number of columns */ - void *Store; /* pointer to the actual storage of the matrix */ -} SuperMatrix; - -/*********************************************** - * The storage schemes are defined as follows. * - ***********************************************/ - -/* Stype == SLU_NC (Also known as Harwell-Boeing sparse matrix format) */ -typedef struct { - int nnz; /* number of nonzeros in the matrix */ - void *nzval; /* pointer to array of nonzero values, packed by column */ - int *rowind; /* pointer to array of row indices of the nonzeros */ - int *colptr; /* pointer to array of beginning of columns in nzval[] - and rowind[] */ - /* Note: - Zero-based indexing is used; - colptr[] has ncol+1 entries, the last one pointing - beyond the last column, so that colptr[ncol] = nnz. */ -} NCformat; - -/* Stype == SLU_NR */ -typedef struct { - int nnz; /* number of nonzeros in the matrix */ - void *nzval; /* pointer to array of nonzero values, packed by raw */ - int *colind; /* pointer to array of columns indices of the nonzeros */ - int *rowptr; /* pointer to array of beginning of rows in nzval[] - and colind[] */ - /* Note: - Zero-based indexing is used; - rowptr[] has nrow+1 entries, the last one pointing - beyond the last row, so that rowptr[nrow] = nnz. */ -} NRformat; - -/* Stype == SLU_SC */ -typedef struct { - int nnz; /* number of nonzeros in the matrix */ - int nsuper; /* number of supernodes, minus 1 */ - void *nzval; /* pointer to array of nonzero values, packed by column */ - int *nzval_colptr;/* pointer to array of beginning of columns in nzval[] */ - int *rowind; /* pointer to array of compressed row indices of - rectangular supernodes */ - int *rowind_colptr;/* pointer to array of beginning of columns in rowind[] */ - int *col_to_sup; /* col_to_sup[j] is the supernode number to which column - j belongs; mapping from column to supernode number. */ - int *sup_to_col; /* sup_to_col[s] points to the start of the s-th - supernode; mapping from supernode number to column. - e.g.: col_to_sup: 0 1 2 2 3 3 3 4 4 4 4 4 4 (ncol=12) - sup_to_col: 0 1 2 4 7 12 (nsuper=4) */ - /* Note: - Zero-based indexing is used; - nzval_colptr[], rowind_colptr[], col_to_sup and - sup_to_col[] have ncol+1 entries, the last one - pointing beyond the last column. - For col_to_sup[], only the first ncol entries are - defined. For sup_to_col[], only the first nsuper+2 - entries are defined. */ -} SCformat; - -/* Stype == SLU_SCP */ -typedef struct { - int nnz; /* number of nonzeros in the matrix */ - int nsuper; /* number of supernodes */ - void *nzval; /* pointer to array of nonzero values, packed by column */ - int *nzval_colbeg;/* nzval_colbeg[j] points to beginning of column j - in nzval[] */ - int *nzval_colend;/* nzval_colend[j] points to one past the last element - of column j in nzval[] */ - int *rowind; /* pointer to array of compressed row indices of - rectangular supernodes */ - int *rowind_colbeg;/* rowind_colbeg[j] points to beginning of column j - in rowind[] */ - int *rowind_colend;/* rowind_colend[j] points to one past the last element - of column j in rowind[] */ - int *col_to_sup; /* col_to_sup[j] is the supernode number to which column - j belongs; mapping from column to supernode. */ - int *sup_to_colbeg; /* sup_to_colbeg[s] points to the start of the s-th - supernode; mapping from supernode to column.*/ - int *sup_to_colend; /* sup_to_colend[s] points to one past the end of the - s-th supernode; mapping from supernode number to - column. - e.g.: col_to_sup: 0 1 2 2 3 3 3 4 4 4 4 4 4 (ncol=12) - sup_to_colbeg: 0 1 2 4 7 (nsuper=4) - sup_to_colend: 1 2 4 7 12 */ - /* Note: - Zero-based indexing is used; - nzval_colptr[], rowind_colptr[], col_to_sup and - sup_to_col[] have ncol+1 entries, the last one - pointing beyond the last column. */ -} SCPformat; - -/* Stype == SLU_NCP */ -typedef struct { - int nnz; /* number of nonzeros in the matrix */ - void *nzval; /* pointer to array of nonzero values, packed by column */ - int *rowind;/* pointer to array of row indices of the nonzeros */ - /* Note: nzval[]/rowind[] always have the same length */ - int *colbeg;/* colbeg[j] points to the beginning of column j in nzval[] - and rowind[] */ - int *colend;/* colend[j] points to one past the last element of column - j in nzval[] and rowind[] */ - /* Note: - Zero-based indexing is used; - The consecutive columns of the nonzeros may not be - contiguous in storage, because the matrix has been - postmultiplied by a column permutation matrix. */ -} NCPformat; - -/* Stype == SLU_DN */ -typedef struct { - int lda; /* leading dimension */ - void *nzval; /* array of size lda*ncol to represent a dense matrix */ -} DNformat; - -/* Stype == SLU_NR_loc (Distributed Compressed Row Format) */ -typedef struct { - int nnz_loc; /* number of nonzeros in the local submatrix */ - int m_loc; /* number of rows local to this processor */ - int fst_row; /* global index of the first row */ - void *nzval; /* pointer to array of nonzero values, packed by row */ - int *rowptr; /* pointer to array of beginning of rows in nzval[] - and colind[] */ - int *colind; /* pointer to array of column indices of the nonzeros */ - /* Note: - Zero-based indexing is used; - rowptr[] has n_loc + 1 entries, the last one pointing - beyond the last row, so that rowptr[n_loc] = nnz_loc.*/ -} NRformat_loc; - - -#endif /* __SUPERLU_SUPERMATRIX */ diff --git a/src/external/arpack++/include/arlutil.h b/src/external/arpack++/include/arlutil.h deleted file mode 100644 index 664ca61b..00000000 --- a/src/external/arpack++/include/arlutil.h +++ /dev/null @@ -1,432 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARLUtil.h. - Unaltered copy of util.h (from SuperLU package) - and superlu_enum_consts.h -*/ - - - -#ifndef __SUPERLU_ENUM_CONSTS /* allow multiple inclusions */ -#define __SUPERLU_ENUM_CONSTS - -/*********************************************************************** - * Enumerate types - ***********************************************************************/ -typedef enum {NO, YES} yes_no_t; -typedef enum {DOFACT, SamePattern, SamePattern_SameRowPerm, FACTORED} fact_t; -typedef enum {NOROWPERM, LargeDiag, MY_PERMR} rowperm_t; -typedef enum {NATURAL, MMD_ATA, MMD_AT_PLUS_A, COLAMD, - METIS_AT_PLUS_A, PARMETIS, ZOLTAN, MY_PERMC} colperm_t; -typedef enum {NOTRANS, TRANS, CONJ} trans_t; -typedef enum {NOEQUIL, ROW, COL, BOTH} DiagScale_t; -typedef enum {NOREFINE, SLU_SINGLE=1, SLU_DOUBLE, SLU_EXTRA} IterRefine_t; -typedef enum {LUSUP, UCOL, LSUB, USUB, LLVL, ULVL} MemType; -typedef enum {HEAD, TAIL} stack_end_t; -typedef enum {SYSTEM, USER} LU_space_t; -typedef enum {ONE_NORM, TWO_NORM, INF_NORM} norm_t; -typedef enum {SILU, SMILU_1, SMILU_2, SMILU_3} milu_t; -#if 0 -typedef enum {NODROP = 0x0000, - DROP_BASIC = 0x0001, /* ILU(tau) */ - DROP_PROWS = 0x0002, /* ILUTP: keep p maximum rows */ - DROP_COLUMN = 0x0004, /* ILUTP: for j-th column, - p = gamma * nnz(A(:,j)) */ - DROP_AREA = 0x0008, /* ILUTP: for j-th column, use - nnz(F(:,1:j)) / nnz(A(:,1:j)) - to limit memory growth */ - DROP_SECONDARY = 0x000E, /* PROWS | COLUMN | AREA */ - DROP_DYNAMIC = 0x0010, - DROP_INTERP = 0x0100} rule_t; -#endif - - -/* - * The following enumerate type is used by the statistics variable - * to keep track of flop count and time spent at various stages. - * - * Note that not all of the fields are disjoint. - */ -typedef enum { - COLPERM, /* find a column ordering that minimizes fills */ - ROWPERM, /* find a row ordering maximizes diagonal. */ - RELAX, /* find artificial supernodes */ - ETREE, /* compute column etree */ - EQUIL, /* equilibrate the original matrix */ - SYMBFAC, /* symbolic factorization. */ - DIST, /* distribute matrix. */ - FACT, /* perform LU factorization */ - COMM, /* communication for factorization */ - SOL_COMM,/* communication for solve */ - RCOND, /* estimate reciprocal condition number */ - SOLVE, /* forward and back solves */ - REFINE, /* perform iterative refinement */ - TRSV, /* fraction of FACT spent in xTRSV */ - GEMV, /* fraction of FACT spent in xGEMV */ - FERR, /* estimate error bounds after iterative refinement */ - NPHASES /* total number of phases */ -} PhaseType; - - -#endif /* __SUPERLU_ENUM_CONSTS */ - - - -#ifndef __SUPERLU_UTIL /* allow multiple inclusions */ -#define __SUPERLU_UTIL - -#include -#include -#include -/* -#ifndef __STDC__ -#include -#endif -*/ -#include - -/*********************************************************************** - * Macros - ***********************************************************************/ -#define FIRSTCOL_OF_SNODE(i) (xsup[i]) -/* No of marker arrays used in the symbolic factorization, - each of size n */ -#define NO_MARKER 3 -#define NUM_TEMPV(m,w,t,b) ( SUPERLU_MAX(m, (t + b)*w) ) - -#ifndef USER_ABORT -#define USER_ABORT(msg) superlu_abort_and_exit(msg) -#endif - -#define ABORT(err_msg) \ - { char msg[256];\ - sprintf(msg,"%s at line %d in file %s\n",err_msg,__LINE__, __FILE__);\ - USER_ABORT(msg); } - - -#ifndef USER_MALLOC -#if 1 -#define USER_MALLOC(size) superlu_malloc(size) -#else -/* The following may check out some uninitialized data */ -#define USER_MALLOC(size) memset (superlu_malloc(size), '\x0F', size) -#endif -#endif - -#define SUPERLU_MALLOC(size) USER_MALLOC(size) - -#ifndef USER_FREE -#define USER_FREE(addr) superlu_free(addr) -#endif - -#define SUPERLU_FREE(addr) USER_FREE(addr) - -#define CHECK_MALLOC(where) { \ - extern int superlu_malloc_total; \ - printf("%s: malloc_total %d Bytes\n", \ - where, superlu_malloc_total); \ -} - -#define SUPERLU_MAX(x, y) ( (x) > (y) ? (x) : (y) ) -#define SUPERLU_MIN(x, y) ( (x) < (y) ? (x) : (y) ) - -/********************************************************* - * Macros used for easy access of sparse matrix entries. * - *********************************************************/ -#define L_SUB_START(col) ( Lstore->rowind_colptr[col] ) -#define L_SUB(ptr) ( Lstore->rowind[ptr] ) -#define L_NZ_START(col) ( Lstore->nzval_colptr[col] ) -#define L_FST_SUPC(superno) ( Lstore->sup_to_col[superno] ) -#define U_NZ_START(col) ( Ustore->colptr[col] ) -#define U_SUB(ptr) ( Ustore->rowind[ptr] ) - - -/*********************************************************************** - * Constants - ***********************************************************************/ -#define EMPTY (-1) -/*#define NO (-1)*/ -#define FALSE 0 -#define TRUE 1 - -#define NO_MEMTYPE 4 /* 0: lusup; - 1: ucol; - 2: lsub; - 3: usub */ - -#define GluIntArray(n) (5 * (n) + 5) - -/* Dropping rules */ -#define NODROP ( 0x0000 ) -#define DROP_BASIC ( 0x0001 ) /* ILU(tau) */ -#define DROP_PROWS ( 0x0002 ) /* ILUTP: keep p maximum rows */ -#define DROP_COLUMN ( 0x0004 ) /* ILUTP: for j-th column, - p = gamma * nnz(A(:,j)) */ -#define DROP_AREA ( 0x0008 ) /* ILUTP: for j-th column, use - nnz(F(:,1:j)) / nnz(A(:,1:j)) - to limit memory growth */ -#define DROP_SECONDARY ( 0x000E ) /* PROWS | COLUMN | AREA */ -#define DROP_DYNAMIC ( 0x0010 ) /* adaptive tau */ -#define DROP_INTERP ( 0x0100 ) /* use interpolation */ - - -#if 1 -#define MILU_ALPHA (1.0e-2) /* multiple of drop_sum to be added to diagonal */ -#else -#define MILU_ALPHA 1.0 /* multiple of drop_sum to be added to diagonal */ -#endif - - -/*********************************************************************** - * Type definitions - ***********************************************************************/ -typedef float flops_t; -typedef unsigned char Logical; - -/* - *-- This contains the options used to control the solution process. - * - * Fact (fact_t) - * Specifies whether or not the factored form of the matrix - * A is supplied on entry, and if not, how the matrix A should - * be factorizaed. - * = DOFACT: The matrix A will be factorized from scratch, and the - * factors will be stored in L and U. - * = SamePattern: The matrix A will be factorized assuming - * that a factorization of a matrix with the same sparsity - * pattern was performed prior to this one. Therefore, this - * factorization will reuse column permutation vector - * ScalePermstruct->perm_c and the column elimination tree - * LUstruct->etree. - * = SamePattern_SameRowPerm: The matrix A will be factorized - * assuming that a factorization of a matrix with the same - * sparsity pattern and similar numerical values was performed - * prior to this one. Therefore, this factorization will reuse - * both row and column scaling factors R and C, both row and - * column permutation vectors perm_r and perm_c, and the - * data structure set up from the previous symbolic factorization. - * = FACTORED: On entry, L, U, perm_r and perm_c contain the - * factored form of A. If DiagScale is not NOEQUIL, the matrix - * A has been equilibrated with scaling factors R and C. - * - * Equil (yes_no_t) - * Specifies whether to equilibrate the system (scale A's row and - * columns to have unit norm). - * - * ColPerm (colperm_t) - * Specifies what type of column permutation to use to reduce fill. - * = NATURAL: use the natural ordering - * = MMD_ATA: use minimum degree ordering on structure of A'*A - * = MMD_AT_PLUS_A: use minimum degree ordering on structure of A'+A - * = COLAMD: use approximate minimum degree column ordering - * = MY_PERMC: use the ordering specified by the user - * - * Trans (trans_t) - * Specifies the form of the system of equations: - * = NOTRANS: A * X = B (No transpose) - * = TRANS: A**T * X = B (Transpose) - * = CONJ: A**H * X = B (Transpose) - * - * IterRefine (IterRefine_t) - * Specifies whether to perform iterative refinement. - * = NO: no iterative refinement - * = SLU_SINGLE: perform iterative refinement in single precision - * = SLU_DOUBLE: perform iterative refinement in double precision - * = SLU_EXTRA: perform iterative refinement in extra precision - * - * DiagPivotThresh (double, in [0.0, 1.0]) (only for sequential SuperLU) - * Specifies the threshold used for a diagonal entry to be an - * acceptable pivot. - * - * SymmetricMode (yest_no_t) - * Specifies whether to use symmetric mode. Symmetric mode gives - * preference to diagonal pivots, and uses an (A'+A)-based column - * permutation algorithm. - * - * PivotGrowth (yes_no_t) - * Specifies whether to compute the reciprocal pivot growth. - * - * ConditionNumber (ues_no_t) - * Specifies whether to compute the reciprocal condition number. - * - * RowPerm (rowperm_t) (only for SuperLU_DIST or ILU) - * Specifies whether to permute rows of the original matrix. - * = NO: not to permute the rows - * = LargeDiag: make the diagonal large relative to the off-diagonal - * = MY_PERMR: use the permutation given by the user - * - * ILU_DropRule (int) - * Specifies the dropping rule: - * = DROP_BASIC: Basic dropping rule, supernodal based ILUTP(tau). - * = DROP_PROWS: Supernodal based ILUTP(p,tau), p = gamma * nnz(A)/n. - * = DROP_COLUMN: Variant of ILUTP(p,tau), for j-th column, - * p = gamma * nnz(A(:,j)). - * = DROP_AREA: Variation of ILUTP, for j-th column, use - * nnz(F(:,1:j)) / nnz(A(:,1:j)) to control memory. - * = DROP_DYNAMIC: Modify the threshold tau during factorizaion: - * If nnz(L(:,1:j)) / nnz(A(:,1:j)) > gamma - * tau_L(j) := MIN(tau_0, tau_L(j-1) * 2); - * Otherwise - * tau_L(j) := MAX(tau_0, tau_L(j-1) / 2); - * tau_U(j) uses the similar rule. - * NOTE: the thresholds used by L and U are separate. - * = DROP_INTERP: Compute the second dropping threshold by - * interpolation instead of sorting (default). - * In this case, the actual fill ratio is not - * guaranteed to be smaller than gamma. - * Note: DROP_PROWS, DROP_COLUMN and DROP_AREA are mutually exclusive. - * ( Default: DROP_BASIC | DROP_AREA ) - * - * ILU_DropTol (double) - * numerical threshold for dropping. - * - * ILU_FillFactor (double) - * Gamma in the secondary dropping. - * - * ILU_Norm (norm_t) - * Specify which norm to use to measure the row size in a - * supernode: infinity-norm, 1-norm, or 2-norm. - * - * ILU_FillTol (double) - * numerical threshold for zero pivot perturbation. - * - * ILU_MILU (milu_t) - * Specifies which version of MILU to use. - * - * ILU_MILU_Dim (double) - * Dimension of the PDE if available. - * - * ReplaceTinyPivot (yes_no_t) (only for SuperLU_DIST) - * Specifies whether to replace the tiny diagonals by - * sqrt(epsilon)*||A|| during LU factorization. - * - * SolveInitialized (yes_no_t) (only for SuperLU_DIST) - * Specifies whether the initialization has been performed to the - * triangular solve. - * - * RefineInitialized (yes_no_t) (only for SuperLU_DIST) - * Specifies whether the initialization has been performed to the - * sparse matrix-vector multiplication routine needed in iterative - * refinement. - * - * PrintStat (yes_no_t) - * Specifies whether to print the solver's statistics. - */ -typedef struct { - fact_t Fact; - yes_no_t Equil; - colperm_t ColPerm; - trans_t Trans; - IterRefine_t IterRefine; - double DiagPivotThresh; - yes_no_t SymmetricMode; - yes_no_t PivotGrowth; - yes_no_t ConditionNumber; - rowperm_t RowPerm; - int ILU_DropRule; - double ILU_DropTol; /* threshold for dropping */ - double ILU_FillFactor; /* gamma in the secondary dropping */ - norm_t ILU_Norm; /* infinity-norm, 1-norm, or 2-norm */ - double ILU_FillTol; /* threshold for zero pivot perturbation */ - milu_t ILU_MILU; - double ILU_MILU_Dim; /* Dimension of PDE (if available) */ - yes_no_t ParSymbFact; - yes_no_t ReplaceTinyPivot; /* used in SuperLU_DIST */ - yes_no_t SolveInitialized; - yes_no_t RefineInitialized; - yes_no_t PrintStat; - int nnzL, nnzU; /* used to store nnzs for now */ - int num_lookaheads; /* num of levels in look-ahead */ - yes_no_t lookahead_etree; /* use etree computed from the - serial symbolic factorization */ - yes_no_t SymPattern; /* symmetric factorization */ -} superlu_options_t; - -/*! \brief Headers for 4 types of dynamatically managed memory */ -typedef struct e_node { - int size; /* length of the memory that has been used */ - void *mem; /* pointer to the new malloc'd store */ -} ExpHeader; - -typedef struct { - int size; - int used; - int top1; /* grow upward, relative to &array[0] */ - int top2; /* grow downward */ - void *array; -} LU_stack_t; - -typedef struct { - int *panel_histo; /* histogram of panel size distribution */ - double *utime; /* running time at various phases */ - flops_t *ops; /* operation count at various phases */ - int TinyPivots; /* number of tiny pivots */ - int RefineSteps; /* number of iterative refinement steps */ - int expansions; /* number of memory expansions */ -} SuperLUStat_t; - -typedef struct { - float for_lu; - float total_needed; -} mem_usage_t; - - -/*********************************************************************** - * Prototypes - ***********************************************************************/ -#ifdef __cplusplus -extern "C" { -#endif - -extern void Destroy_SuperMatrix_Store(SuperMatrix *); -extern void Destroy_CompCol_Matrix(SuperMatrix *); -extern void Destroy_CompRow_Matrix(SuperMatrix *); -extern void Destroy_SuperNode_Matrix(SuperMatrix *); -extern void Destroy_CompCol_Permuted(SuperMatrix *); -extern void Destroy_Dense_Matrix(SuperMatrix *); -extern void get_perm_c(int, SuperMatrix *, int *); -extern void set_default_options(superlu_options_t *options); -extern void ilu_set_default_options(superlu_options_t *options); -extern void sp_preorder (superlu_options_t *, SuperMatrix*, int*, int*, - SuperMatrix*); -extern void superlu_abort_and_exit(char*); -extern void *superlu_malloc (size_t); -extern int *intMalloc (int); -extern int *intCalloc (int); -extern void superlu_free (void*); -extern void SetIWork (int, int, int, int *, int **, int **, int **, - int **, int **, int **, int **); -extern int sp_coletree (int *, int *, int *, int, int, int *); -extern void relax_snode (const int, int *, const int, int *, int *); -extern void heap_relax_snode (const int, int *, const int, int *, int *); -extern int mark_relax(int, int *, int *, int *, int *, int *, int *); -extern void ilu_relax_snode (const int, int *, const int, int *, - int *, int *); -extern void ilu_heap_relax_snode (const int, int *, const int, int *, - int *, int*); -extern void resetrep_col (const int, const int *, int *); -extern int spcoletree (int *, int *, int *, int, int, int *); -extern int *TreePostorder (int, int *); -extern double SuperLU_timer_ (); -extern int sp_ienv (int); -extern int lsame_ (char *, char *); -extern int xerbla_ (char *, int *); -extern void ifill (int *, int, int); -extern void snode_profile (int, int *); -extern void super_stats (int, int *); -extern void check_repfnz(int, int, int, int *); -extern void PrintSumm (char *, int, int, int); -extern void StatInit(SuperLUStat_t *); -extern void StatPrint (SuperLUStat_t *); -extern void StatFree(SuperLUStat_t *); -extern void print_panel_seg(int, int, int, int, int *, int *); -extern int print_int_vec(char *,int, int *); -extern int slu_PrintInt10(char *, int, int *); - -#ifdef __cplusplus - } -#endif - -#endif /* __SUPERLU_UTIL */ diff --git a/src/external/arpack++/include/armat.h b/src/external/arpack++/include/armat.h deleted file mode 100644 index 52df53e3..00000000 --- a/src/external/arpack++/include/armat.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARMat.h - Generic matrix template with a matrix-vector product. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARMAT_H -#define ARMAT_H - -template -class ARMatrix { - - protected: - - int m, n; // Number of rows and columns. - bool defined; - - public: - - ARMatrix() { defined = false; } - // Short constructor. - - ARMatrix(int nrows, int ncols = 0) - // Long constructor. - { - m = nrows; - n = (ncols?ncols:nrows); - defined = false; - } // Constructor. - - virtual ~ARMatrix() { } - // Destructor. - - int nrows() { return m; } - - int ncols() { return n; } - - bool IsDefined() { return defined; } - - virtual void MultMv(ARTYPE* v, ARTYPE* w) = 0; - // Matrix-vector product: w = A*v. - -}; // ARMatrix. - -#endif // ARMAT_H - diff --git a/src/external/arpack++/include/arpackf.h b/src/external/arpack++/include/arpackf.h deleted file mode 100644 index 6445b469..00000000 --- a/src/external/arpack++/include/arpackf.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE arpackf.h - ARPACK FORTRAN routines. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARPACKF_H -#define ARPACKF_H - -#include "arch.h" - -extern "C" -{ - -// debug "common" statement. - - extern struct { - ARint logfil, ndigit, mgetv0; - ARint msaupd, msaup2, msaitr, mseigt, msapps, msgets, mseupd; - ARint mnaupd, mnaup2, mnaitr, mneigt, mnapps, mngets, mneupd; - ARint mcaupd, mcaup2, mcaitr, mceigt, mcapps, mcgets, mceupd; - } F77NAME(debug); - - -// double precision symmetric routines. - - void F77NAME(dsaupd)(ARint *ido, char *bmat, ARint *n, const char *which, - ARint *nev, double *tol, double *resid, - ARint *ncv, double *V, ARint *ldv, - ARint *iparam, ARint *ipntr, double *workd, - double *workl, ARint *lworkl, ARint *info); - - void F77NAME(dseupd)(ARlogical *rvec, char *HowMny, ARlogical *select, - double *d, double *Z, ARint *ldz, - double *sigma, char *bmat, ARint *n, - const char *which, ARint *nev, double *tol, - double *resid, ARint *ncv, double *V, - ARint *ldv, ARint *iparam, ARint *ipntr, - double *workd, double *workl, - ARint *lworkl, ARint *info); - -// double precision nonsymmetric routines. - - void F77NAME(dnaupd)(ARint *ido, char *bmat, ARint *n, const char *which, - ARint *nev, double *tol, double *resid, - ARint *ncv, double *V, ARint *ldv, - ARint *iparam, ARint *ipntr, double *workd, - double *workl, ARint *lworkl, ARint *info); - - void F77NAME(dneupd)(ARlogical *rvec, char *HowMny, ARlogical *select, - double *dr, double *di, double *Z, - ARint *ldz, double *sigmar, - double *sigmai, double *workev, - char *bmat, ARint *n, const char *which, - ARint *nev, double *tol, double *resid, - ARint *ncv, double *V, ARint *ldv, - ARint *iparam, ARint *ipntr, - double *workd, double *workl, - ARint *lworkl, ARint *info); - -// single precision symmetric routines. - - void F77NAME(ssaupd)(ARint *ido, char *bmat, ARint *n, const char *which, - ARint *nev, float *tol, float *resid, - ARint *ncv, float *V, ARint *ldv, - ARint *iparam, ARint *ipntr, float *workd, - float *workl, ARint *lworkl, ARint *info); - - void F77NAME(sseupd)(ARlogical *rvec, char *HowMny, ARlogical *select, - float *d, float *Z, ARint *ldz, - float *sigma, char *bmat, ARint *n, - const char *which, ARint *nev, float *tol, - float *resid, ARint *ncv, float *V, - ARint *ldv, ARint *iparam, ARint *ipntr, - float *workd, float *workl, - ARint *lworkl, ARint *info); - -// single precision nonsymmetric routines. - - void F77NAME(snaupd)(ARint *ido, char *bmat, ARint *n, const char *which, - ARint *nev, float *tol, float *resid, - ARint *ncv, float *V, ARint *ldv, - ARint *iparam, ARint *ipntr, float *workd, - float *workl, ARint *lworkl, ARint *info); - - void F77NAME(sneupd)(ARlogical *rvec, char *HowMny, ARlogical *select, - float *dr, float *di, float *Z, - ARint *ldz, float *sigmar, - float *sigmai, float *workev, char *bmat, - ARint *n, const char *which, ARint *nev, - float *tol, float *resid, ARint *ncv, - float *V, ARint *ldv, ARint *iparam, - ARint *ipntr, float *workd, float *workl, - ARint *lworkl, ARint *info); - -#ifdef ARCOMP_H - -// single precision complex routines. - - void F77NAME(cnaupd)(ARint *ido, char *bmat, ARint *n, const char *which, - ARint *nev, float *tol, arcomplex *resid, - ARint *ncv, arcomplex *V, ARint *ldv, - ARint *iparam, ARint *ipntr, arcomplex *workd, - arcomplex *workl, ARint *lworkl, - float *rwork, ARint *info); - - void F77NAME(cneupd)(ARlogical *rvec, char *HowMny, ARlogical *select, - arcomplex *d, arcomplex *Z, ARint *ldz, - arcomplex *sigma, arcomplex *workev, - char *bmat, ARint *n, const char *which, ARint *nev, - float *tol, arcomplex *resid, ARint *ncv, - arcomplex *V, ARint *ldv, ARint *iparam, - ARint *ipntr, arcomplex *workd, - arcomplex *workl, ARint *lworkl, - float *rwork, ARint *info); - -// double precision complex routines. - - void F77NAME(znaupd)(ARint *ido, char *bmat, ARint *n, const char *which, - ARint *nev, double *tol, arcomplex *resid, - ARint *ncv, arcomplex *V, ARint *ldv, - ARint *iparam, ARint *ipntr, arcomplex *workd, - arcomplex *workl, ARint *lworkl, - double *rwork, ARint *info); - - void F77NAME(zneupd)(ARlogical *rvec, char *HowMny, ARlogical *select, - arcomplex *d, arcomplex *Z, ARint *ldz, - arcomplex *sigma, arcomplex *workev, - char *bmat, ARint *n, const char *which, ARint *nev, - double *tol, arcomplex *resid, ARint *ncv, - arcomplex *V, ARint *ldv, ARint *iparam, - ARint *ipntr, arcomplex *workd, - arcomplex *workl, ARint *lworkl, - double *rwork, ARint *info); - -} - -#endif // ARCOMP_H - -#endif // ARPACKF_H diff --git a/src/external/arpack++/include/arrgcomp.h b/src/external/arpack++/include/arrgcomp.h deleted file mode 100644 index d3b2e3d0..00000000 --- a/src/external/arpack++/include/arrgcomp.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARRGComp.h. - Arpack++ class ARrcCompGenEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARRGCOMP_H -#define ARRGCOMP_H - -#include -#include -#include "arch.h" -#include "arrscomp.h" -#include "arrgeig.h" - -template -class ARrcCompGenEig: - virtual public ARrcGenEig >, - virtual public ARrcCompStdEig { - - public: - - // a) Constructors and destructor. - - ARrcCompGenEig() { } - // Short constructor (Does nothing but calling base classes constructors). - - ARrcCompGenEig(int np, int nevp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARrcCompGenEig(int np, int nevp, arcomplex sigmap, - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, arcomplex* residp = NULL, - bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARrcCompGenEig(const ARrcCompGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARrcCompGenEig() { } - // Destructor. - - // b) Operators. - - ARrcCompGenEig& operator=(const ARrcCompGenEig& other); - // Assignment operator. - -}; // class ARrcCompGenEig. - - -// ------------------------------------------------------------------------ // -// ARrcCompGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline ARrcCompGenEig:: -ARrcCompGenEig(int np, int nevp, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, arcomplex* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARrcCompGenEig:: -ARrcCompGenEig(int np, int nevp, arcomplex sigmap, const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shif and invert mode). - - -template -ARrcCompGenEig& ARrcCompGenEig:: -operator=(const ARrcCompGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARRGCOMP_H - diff --git a/src/external/arpack++/include/arrgeig.h b/src/external/arpack++/include/arrgeig.h deleted file mode 100644 index d9c709ec..00000000 --- a/src/external/arpack++/include/arrgeig.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARRGEig.h. - Arpack++ class ARrcGenEig definition. - Derived from ARrcStdEig, this class implements the - reverse communication interface for generalized problems. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARRGEIG_H -#define ARRGEIG_H - -#include "arch.h" -#include "arerror.h" -#include "arrseig.h" - -// ARrcGenEig class definition. - -template -class ARrcGenEig: virtual public ARrcStdEig { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - void NoShift(); - // Turns the problem to regular mode. - - - // a.2) Constructors and destructor. - - ARrcGenEig(); - // Short constructor that does almost nothing. - - ARrcGenEig(const ARrcGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARrcGenEig() { } - // Destructor (presently meaningless). - - // b) Operators. - - ARrcGenEig& operator=(const ARrcGenEig& other); - // Assignment operator. - -}; // class ARrcGenEig. - - -// ------------------------------------------------------------------------ // -// ARrcGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARrcGenEig::NoShift() -{ - - this->sigmaR = (ARTYPE)0; - this->sigmaI = 0.0; - this->mode = 2; - this->iparam[7] = this->mode; - this->Restart(); - -} // NoShift. - - -template -inline ARrcGenEig::ARrcGenEig() -{ - - this->bmat = 'G'; // This is a generalized problem. - this->NoShift(); - -} // Short constructor. - - -template -ARrcGenEig& ARrcGenEig:: -operator=(const ARrcGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARRGEIG_H - diff --git a/src/external/arpack++/include/arrgnsym.h b/src/external/arpack++/include/arrgnsym.h deleted file mode 100644 index 1dbd3be7..00000000 --- a/src/external/arpack++/include/arrgnsym.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARRGNSym.h. - Arpack++ class ARrcNonSymGenEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARRGNSYM_H -#define ARRGNSYM_H - -#include -#include -#include "arch.h" -#include "arrsnsym.h" -#include "arrgeig.h" - -template -class ARrcNonSymGenEig: - virtual public ARrcGenEig, - virtual public ARrcNonSymStdEig { - - protected: - - // a) Protected variables: - - char part; - - - // b) Protected functions: - - char CheckPart(char partp); - - virtual void Copy(const ARrcNonSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that provides access to internal variables' values. - - ARFLOAT GetShiftImag() { return this->sigmaI; } - // Returns the imaginary part of the shift (when in shift and invert mode). - - - // c.2) Functions that allow changes in problem parameters. - - void ChangePart(char partp); - // Changes "part" to 'R' (real) or 'I' (imaginary). - - virtual void ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp = 0.0); - // Turns the problem to shift-and-invert mode - // with shift defined by sigmaRp and sigmaIp. - - virtual void SetShiftInvertMode(ARFLOAT sigmaRp); - // Turns the problem to real shift-and-invert mode with sigmaRp as shift. - - virtual void SetComplexShiftMode(char partp, ARFLOAT sigmaRp, - ARFLOAT sigmaIp); - // Turns the problem to complex shift-and-invert mode with shift - // defined by sigmaRp and sigmaIp. - - - // c.3) Constructors and destructor. - - ARrcNonSymGenEig() { part = 'R'; } - // Short constructor that does almost nothing. - - ARrcNonSymGenEig(int np, int nevp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARrcNonSymGenEig(int np, int nevp, ARFLOAT sigmap, - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, ARFLOAT* residp = NULL, - bool ishiftp = true); - // Long constructor (real shift and invert mode). - - ARrcNonSymGenEig(int np, int nevp, - char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp, - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, ARFLOAT* residp = NULL, - bool ishiftp = true); - // Long constructor (complex shift and invert mode). - - ARrcNonSymGenEig(const ARrcNonSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARrcNonSymGenEig() { } - // Destructor. - - // d) Operators. - - ARrcNonSymGenEig& operator=(const ARrcNonSymGenEig& other); - // Assignment operator. - -}; // class ARrcNonSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARrcNonSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline char ARrcNonSymGenEig::CheckPart(char partp) -{ - if ((partp != 'R') && (partp != 'I')) { - throw ArpackError(ArpackError::PART_UNDEFINED); - } - return partp; -} // CheckPart. - - -template -inline void ARrcNonSymGenEig:: -Copy(const ARrcNonSymGenEig& other) -{ - - ARrcStdEig::Copy(other); - part = other.part; - -} // Copy. - - -template -inline void ARrcNonSymGenEig::ChangePart(char partp) -{ - - part = CheckPart(partp); - if (part == 'R') { - this->mode = 3; // Real part. - } - else { - this->mode = 4; // Imaginary part. - } - this->iparam[7] = this->mode; - this->Restart(); - -} // ChangePart. - - -template -inline void ARrcNonSymGenEig:: -ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = sigmaIp; - ChangePart(part); - -} // ChangeShift. - - -template -inline void ARrcNonSymGenEig:: -SetShiftInvertMode(ARFLOAT sigmaRp) -{ - - part = 'R'; - ChangeShift(sigmaRp); - -} // SetShiftInvertMode. - - -template -inline void ARrcNonSymGenEig:: -SetComplexShiftMode(char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - part = CheckPart(partp); - ChangeShift(sigmaRp, sigmaIp); - -} // SetComplexShiftMode. - - -template -inline ARrcNonSymGenEig:: -ARrcNonSymGenEig(int np, int nevp, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) -{ - - part = 'R'; // Considering mode = 3 in ChangeShift. - this->NoShift(); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARrcNonSymGenEig:: -ARrcNonSymGenEig(int np, int nevp, ARFLOAT sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) -{ - - SetShiftInvertMode(sigmap); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - - -} // Long constructor (real shift and invert mode). - - -template -inline ARrcNonSymGenEig:: -ARrcNonSymGenEig(int np, int nevp, char partp, ARFLOAT sigmaRp, - ARFLOAT sigmaIp, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) -{ - - SetComplexShiftMode(partp, sigmaRp, sigmaIp); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARrcNonSymGenEig& ARrcNonSymGenEig:: -operator=(const ARrcNonSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARRGNSYM_H - diff --git a/src/external/arpack++/include/arrgsym.h b/src/external/arpack++/include/arrgsym.h deleted file mode 100644 index 86f110d8..00000000 --- a/src/external/arpack++/include/arrgsym.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARRGSym.h. - Arpack++ class ARrcSymGenEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARRGSYM_H -#define ARRGSYM_H - -#include -#include -#include "arch.h" -#include "arrssym.h" -#include "arrgeig.h" - -template -class ARrcSymGenEig: - virtual public ARrcGenEig, - virtual public ARrcSymStdEig { - - protected: - - // a) Protected variable: - - char InvertMode; - - - // b) Protected functions: - - char CheckInvertMode(char InvertModep); - - virtual void Copy(const ARrcSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - void ChangeInvertMode(char InvertModep); - // Changes "InvertMode" to 'S' (shift-and-invert), - // 'B' (buckling) or 'C' (cayley) mode. - - virtual void ChangeShift(ARFLOAT sigmap); - // Changes shift value. - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - // Turns problem to shift and invert mode with shift defined by sigmap. - - virtual void SetBucklingMode(ARFLOAT sigmap); - // Turns problem to buckling mode with shift defined by sigmap. - - virtual void SetCayleyMode(ARFLOAT sigmap); - // Turns problem to Cayley mode with shift defined by sigmap. - - - // c.2) Constructors and destructor. - - ARrcSymGenEig() { InvertMode = 'S'; } - // Short constructor that does almost nothing. - - ARrcSymGenEig(int np, int nevp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARrcSymGenEig(char invertmodep, int np, int nevp, ARFLOAT sigmap, - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift-and-invert, buckling and Cayley modes). - - ARrcSymGenEig(const ARrcSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARrcSymGenEig() { } - // Destructor. - - // d) Operators. - - ARrcSymGenEig& operator=(const ARrcSymGenEig& other); - // Assignment operator. - -}; // class ARrcSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARrcSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline char ARrcSymGenEig::CheckInvertMode(char InvertModep) -{ - if ((InvertModep != 'S') && (InvertModep != 'B') && (InvertModep != 'C')) { - throw ArpackError(ArpackError::INVMODE_UNDEFINED); - } - return InvertModep; - -} // CheckInvertMode. - - -template -inline void ARrcSymGenEig::Copy(const ARrcSymGenEig& other) -{ - - ARrcStdEig::Copy(other); - InvertMode = other.InvertMode; - -} // Copy. - - -template -inline void ARrcSymGenEig::ChangeInvertMode(char InvertModep) -{ - - InvertMode = CheckInvertMode(InvertModep); - switch (InvertMode) { - case 'S': - this->mode = 3; // Shift and invert mode. - break; - case 'B': - this->mode = 4; // Buckling mode. - break; - case 'C': - this->mode = 5; // Cayley mode. - break; - } - this->iparam[7] = this->mode; - this->Restart(); - -} // ChangeInvertMode. - - -template -inline void ARrcSymGenEig::ChangeShift(ARFLOAT sigmap) -{ - - this->sigmaR = sigmap; - this->sigmaI = 0.0; - ChangeInvertMode(InvertMode); - -} // ChangeShift. - - -template -void ARrcSymGenEig::SetShiftInvertMode(ARFLOAT sigmap) - -{ - - InvertMode = 'S'; - ChangeShift(sigmap); - -} // SetShiftInvertMode. - - -template -void ARrcSymGenEig::SetBucklingMode(ARFLOAT sigmap) - -{ - - InvertMode = 'B'; - ChangeShift(sigmap); - -} // SetBucklingMode. - - -template -void ARrcSymGenEig::SetCayleyMode(ARFLOAT sigmap) - -{ - - InvertMode = 'C'; - ChangeShift(sigmap); - -} // SetCayleyMode. - - -template -inline ARrcSymGenEig:: -ARrcSymGenEig(int np, int nevp, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - InvertMode = 'S'; // Considering mode = 3 in ChangeShift. - this->NoShift(); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARrcSymGenEig:: -ARrcSymGenEig(char InvertModep, int np, int nevp, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - InvertMode = CheckInvertMode(InvertModep); // InvertMode = 'S', 'B', 'C'. - ChangeShift(sigmap); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift-and-invert, buckling and Cayley modes). - - -template -ARrcSymGenEig& ARrcSymGenEig:: -operator=(const ARrcSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARRGSYM_H - diff --git a/src/external/arpack++/include/arrscomp.h b/src/external/arpack++/include/arrscomp.h deleted file mode 100644 index 40977551..00000000 --- a/src/external/arpack++/include/arrscomp.h +++ /dev/null @@ -1,375 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARRSComp.h. - Arpack++ class ARrcCompStdEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARRSCOMP_H -#define ARRSCOMP_H - -#include -#include -#include "arch.h" -#include "arerror.h" -#include "debug.h" -#include "arrseig.h" -#include "caupp.h" -#include "ceupp.h" - -template -class ARrcCompStdEig: virtual public ARrcStdEig > { - - protected: - - // a) Protected functions: - - // a.1) Memory control functions. - - void WorkspaceAllocate(); - // Allocates workspace for complex problems. - - - // a.2) Functions that handle original FORTRAN ARPACK code. - - void Aupp(); - // Interface to FORTRAN subroutines CNAUPD and ZNAUPD. - - void Eupp(); - // Interface to FORTRAN subroutines CNEUPD and ZNEUPD. - - public: - - // b) Public functions: - - // b.1) Trace functions. - - void Trace(const int digit = -5, const int getv0 = 0, const int aupd = 1, - const int aup2 = 0, const int aitr = 0, const int eigt = 0, - const int apps = 0, const int gets = 0, const int eupd = 0) - { - cTraceOn(digit, getv0, aupd, aup2, aitr, eigt, apps, gets, eupd); - } - // Turns on trace mode. - - - // b.2) Functions that perform all calculations in one step. - - int Eigenvalues(arcomplex* &EigValp, bool ivec = false, - bool ischur = false); - // Overrides array EigValp with the eigenvalues of the problem. - // Also calculates eigenvectors and Schur vectors if requested. - - int EigenValVectors(arcomplex* &EigVecp, - arcomplex* &EigValp, bool ischur = false); - // Overrides array EigVecp sequentially with the eigenvectors of the - // given eigen-problem. Also stores the eigenvalues in EigValp. - // Calculates Schur vectors if requested. - - - // b.3) Functions that return elements of vectors and matrices. - - arcomplex Eigenvalue(int i); - // Provides i-eth eigenvalue. - - arcomplex Eigenvector(int i, int j); - // Provides element j of the i-eth eigenvector. - - - // b.4) Functions that use STL vector class. - -#ifdef STL_VECTOR_H - - vector >* StlEigenvalues(bool ivec = false, - bool ischur = false); - // Calculates the eigenvalues and stores them in a single STL vector. - // Also calculates eigenvectors and Schur vectors if requested. - - vector >* StlEigenvector(int i); - // Returns the i-th eigenvector in a STL vector. - -#endif // #ifdef STL_VECTOR_H. - - - // b.5) Constructors and destructor. - - ARrcCompStdEig() { } - // Short constructor. - - ARrcCompStdEig(int np, int nevp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARrcCompStdEig(int np, int nevp, arcomplex sigma, - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, arcomplex* residp = NULL, - bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARrcCompStdEig(const ARrcCompStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARrcCompStdEig() { } - // Destructor. - - // c) Operators. - - ARrcCompStdEig& operator=(const ARrcCompStdEig& other); - // Assignment operator. - -}; // class ARrcCompStdEig. - - -// ------------------------------------------------------------------------ // -// ARrcCompStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARrcCompStdEig::WorkspaceAllocate() -{ - - this->lworkl = this->ncv*(3*this->ncv+6); - this->lworkv = 2*this->ncv; - this->lrwork = this->ncv; - this->workl = new arcomplex[this->lworkl+1]; - this->workv = new arcomplex[this->lworkv+1]; - this->rwork = new ARFLOAT[this->lrwork+1]; - -} // WorkspaceAllocate. - - -template -inline void ARrcCompStdEig::Aupp() -{ - - caupp(this->ido, this->bmat, this->n, this->which, this->nev, this->tol, this->resid, this->ncv, this->V, this->n, - this->iparam, this->ipntr, this->workd, this->workl, this->lworkl, this->rwork, this->info); - -} // Aupp. - - -template -inline void ARrcCompStdEig::Eupp() -{ - - ceupp(this->rvec, this->HowMny, this->EigValR, this->EigVec, this->n, this->sigmaR, this->workv, - this->bmat, this->n, this->which, this->nev, this->tol, this->resid, this->ncv, this->V, this->n, this->iparam, - this->ipntr, this->workd, this->workl, this->lworkl, this->rwork, this->info); - -} // Eupp. - - -template -int ARrcCompStdEig:: -Eigenvalues(arcomplex* &EigValp, bool ivec, bool ischur) -{ - - if (this->ValuesOK) { // Eigenvalues are available . - if (EigValp == NULL) { // Moving eigenvalues. - EigValp = this->EigValR; - this->EigValR = NULL; - this->newVal = false; - this->ValuesOK = false; - } - else { // Copying eigenvalues. - copy(this->nconv,this->EigValR,1,EigValp,1); - } - } - else { - if (this->newVal) { - delete[] this->EigValR; - this->newVal = false; - } - if (EigValp == NULL) { - try { EigValp = new arcomplex[this->ValSize()]; } - catch (ArpackError) { return 0; } - } - this->EigValR = EigValp; - if (ivec) { // Finding eigenvalues and eigenvectors. - this->nconv = this->FindEigenvectors(ischur); - } - else { // Finding eigenvalues only. - this->nconv = this->FindEigenvalues(); - } - this->EigValR = NULL; - } - return this->nconv; - -} // Eigenvalues(EigValp, ivec, ischur). - - -template -int ARrcCompStdEig:: -EigenValVectors(arcomplex* &EigVecp, arcomplex* &EigValp, - bool ischur) -{ - - if (this->ValuesOK) { // Eigenvalues are already available. - this->nconv = Eigenvalues(EigValp, false); - this->nconv = this->Eigenvectors(EigVecp, ischur); - } - else { // Eigenvalues and vectors are not available. - if (this->newVec) { - delete[] this->EigVec; - this->newVec = false; - } - if (this->newVal) { - delete[] this->EigValR; - this->newVal = false; - } - try { - if (EigVecp == NULL) EigVecp = new arcomplex[this->ValSize()*this->n]; - if (EigValp == NULL) EigValp = new arcomplex[this->ValSize()]; - } - catch (ArpackError) { return 0; } - this->EigVec = EigVecp; - this->EigValR = EigValp; - this->nconv = this->FindEigenvectors(ischur); - this->EigVec = NULL; - this->EigValR = NULL; - } - return this->nconv; - -} // EigenValVectors(EigVecp, EigValp, ischur). - - -template -inline arcomplex ARrcCompStdEig::Eigenvalue(int i) -// calcula e retorna um autovalor. - -{ - - // Returning i-eth eigenvalue. - - if (!this->ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "Eigenvalue(i)"); - } - else if ((i>=this->nconv)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "Eigenvalue(i)"); - } - return this->EigValR[i]; - -} // Eigenvalue(i). - - -template -inline arcomplex ARrcCompStdEig:: -Eigenvector(int i, int j) -{ - - // Returning element j of i-eth eigenvector. - - if (!this->VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "Eigenvector(i,j)"); - } - else if ((i>=this->nconv)||(i<0)||(j>=this->n)||(j<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "Eigenvector(i,j)"); - } - return this->EigVec[i*this->n+j]; - -} // Eigenvector(i,j). - - -#ifdef STL_VECTOR_H - -template -inline vector >* ARrcCompStdEig:: -StlEigenvalues(bool ivec, bool ischur) -{ - - // Returning the eigenvalues in a STL vector. - - vector >* ValR; - arcomplex* ValPtr; - - try { - ValR = new vector >(ValSize()); - } - catch (ArpackError) { return NULL; } - ValPtr = ValR->begin(); - nconv = Eigenvalues(ValPtr, ivec, ischur); - return ValR; - -} // StlEigenvalues. - - -template -inline vector >* ARrcCompStdEig:: -StlEigenvector(int i) -{ - - // Returning the i-th eigenvector in a STL vector. - - vector >* Vec; - - if (!VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "StlEigenvector(i)"); - } - else if ((i>=ValSize())||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "StlEigenvector(i)"); - } - try { - Vec = new vector >(&EigVec[i*n], &EigVec[(i+1)*n]); - } - catch (ArpackError) { return NULL; } - return Vec; - -} // StlEigenvector(i). - -#endif // #ifdef STL_VECTOR_H. - - -template -inline ARrcCompStdEig:: -ARrcCompStdEig(int np, int nevp, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, arcomplex* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARrcCompStdEig:: -ARrcCompStdEig(int np, int nevp, arcomplex sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, int maxitp, - arcomplex* residp, bool ishiftp) - -{ - - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARrcCompStdEig& ARrcCompStdEig:: -operator=(const ARrcCompStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARRSCOMP_H - diff --git a/src/external/arpack++/include/arrseig.h b/src/external/arpack++/include/arrseig.h deleted file mode 100644 index 57cba476..00000000 --- a/src/external/arpack++/include/arrseig.h +++ /dev/null @@ -1,1515 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARRSEig.h. - Arpack++ base class ARrcStdEig definition. - This class implements c++ version of the reverse - communication interface for standard problems. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARRSEIG_H -#define ARRSEIG_H - -#include -#include -#include -#include "arch.h" -#include "arerror.h" -#include "debug.h" -#include "blas1c.h" - - -// ARrcStdEig class definition. - -template -class ARrcStdEig { - - protected: - - // a) Protected variables: - - // a.1) User defined parameters. - - int n; // Dimension of the eigenproblem. - int nev; // Number of eigenvalues to be computed. 0 < nev < n-1. - int ncv; // Number of Arnoldi vectors generated at each iteration. - int maxit; // Maximum number of Arnoldi update iterations allowed. - std::string which; // Specify which of the Ritz values of OP to compute. - ARFLOAT tol; // Stopping criterion (relative accuracy of Ritz values). - ARFLOAT sigmaI; // Imaginary part of shift (for nonsymmetric problems). - ARTYPE sigmaR; // Shift (real part only if problem is nonsymmetric). - ARTYPE *resid; // Initial residual vector. - - - // a.2) Internal variables. - - bool rvec; // Indicates if eigenvectors/Schur vectors were - // requested (or only eigenvalues will be determined). - bool newRes; // Indicates if a new "resid" vector was created. - bool newVal; // Indicates if a new "EigValR" vector was created. - bool newVec; // Indicates if a new "EigVec" vector was created. - bool PrepareOK; // Indicates if internal variables were correctly set. - bool BasisOK; // Indicates if an Arnoldi basis was found. - bool ValuesOK; // Indicates if eigenvalues were calculated. - bool VectorsOK; // Indicates if eigenvectors were determined. - bool SchurOK; // Indicates if Schur vectors were determined. - bool AutoShift; // Indicates if implicit shifts will be generated - // internally (or will be supplied by the user). - char bmat; // Indicates if the problem is a standard ('I') or - // generalized ('G") eigenproblem. - char HowMny; // Indicates if eigenvectors ('A') or Schur vectors ('P') - // were requested (not referenced if rvec = false). - int ido; // Original ARPACK reverse communication flag. - int info; // Original ARPACK error flag. - int mode; // Indicates the type of the eigenproblem (regular, - // shift and invert, etc). - int lworkl; // Dimension of array workl. - int lworkv; // Dimension of array workv. - int lrwork; // Dimension of array rwork. - int iparam[12]; // Vector that handles original ARPACK parameters. - int ipntr[15]; // Vector that handles original ARPACK pointers. - ARFLOAT *rwork; // Original ARPACK internal vector. - ARTYPE *workl; // Original ARPACK internal vector. - ARTYPE *workd; // Original ARPACK internal vector. - ARTYPE *workv; // Original ARPACK internal vector. - ARTYPE *V; // Arnoldi basis / Schur vectors. - - - // a.3) Pure output variables. - - int nconv; // Number of "converged" Ritz values. - ARFLOAT *EigValI; // Imaginary part of eigenvalues (nonsymmetric problems). - ARTYPE *EigValR; // Eigenvalues (real part only if problem is nonsymmetric). - ARTYPE *EigVec; // Eigenvectors. - - - // b) Protected functions: - - // b.1) Memory control functions. - - bool OverV() { return (EigVec == &V[1]); } - // Indicates whether EigVec overrides V or no. - - virtual int ValSize() { return nev; } - // Provides the size of array EigVal. - // Redefined in ARrcNonSymStdEig. - - void ClearFirst(); - // Clears some boolean variables in order to define a entire new problem. - - void ClearBasis(); - // Clear some boolean variables in order to recalculate the arnoldi basis. - - void ClearMem(); - // Clears workspace. - - virtual void ValAllocate(); - // Creates arrays EigValR and EigValI. - // Redefined in ARrcNonSymStdEig. - - virtual void VecAllocate(bool newV = true); - // Creates array EigVec. - - virtual void WorkspaceAllocate(); - // Function that must be defined by a derived class. - // Redefined in ARrc[Sym|NonSym|Complex]StdEig. - - - // b.2) Functions that call the original ARPACK FORTRAN code. - - virtual void Aupp() { - throw ArpackError(ArpackError::NOT_IMPLEMENTED, "Aupp"); - } - // Interface to FORTRAN subroutines __AUPD. - // Must be defined by a derived class. - // Redefined in ARrc[Sym|NonSym|Complex]StdEig. - - void AuppError(); - // Handles errors occurred in function Aupp. - - virtual void Eupp() { - throw ArpackError(ArpackError::NOT_IMPLEMENTED, "Eupp"); - } - // Interface to FORTRAN subroutines __EUPD. - // Must be defined by a derived class. - // Redefined in ARrc[Sym|NonSym|Complex]Eig. - - void EuppError(); - // Handles errors occurred in function Eupp. - - - // b.3) Functions that check user defined parameters. - - int CheckN(int np); - // Does range checking on ncv. - - int CheckNcv(int ncvp); - // Forces ncv to conform to its ranges. - - virtual int CheckNev(int nevp); - // Does range checking on nev. - // Redefined in ARrcNonSymStdEig. - - int CheckMaxit(int maxitp); - // Forces maxit to be greater than zero. - - virtual std::string CheckWhich(const std::string& whichp); - // Determines if the value of variable "which" is valid. - // Redefined in ARrcSymStdEig. - - - // b.4) Functions that set internal variables. - - void Restart(); - - virtual void Prepare(); - // Defines internal variables and allocates working arrays. - - virtual void Copy(const ARrcStdEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - public: - - // c) Public functions: - - // c.1) Function that stores user defined parameters. - - virtual void DefineParameters(int np, int nevp, const std::string& whichp="LM", - int ncvp=0, ARFLOAT tolp=0.0, int maxitp=0, - ARTYPE* residp=NULL, bool ishiftp=true); - // Set values of problem parameters (also called by constructors). - // Redefined in ARrcStdEig and ARrcGenEig. - - - // c.2) Functions that detect if output data is ready. - - bool ParametersDefined() { return PrepareOK; } - // Indicates if all internal variables and arrays were defined. - - bool ArnoldiBasisFound() { return BasisOK; } - // Indicates if an Arnoldi basis is available. - - bool EigenvaluesFound() { return ValuesOK; } - // Indicates if the requested eigenvalues are available. - - bool EigenvectorsFound() { return VectorsOK; } - // Indicates if the requested eigenvectors are available. - - bool SchurVectorsFound() { return SchurOK; } - // Indicates if the Schur vectors are available. - - - // c.3) Functions that provides access to internal variables' values. - - int ConvergedEigenvalues() { return nconv; } - // Provides the number of "converged" eigenvalues found so far. - - int GetMaxit() { return maxit; } - // Returns the maximum number of Arnoldi update iterations allowed. - - int GetIter(); - // Returns the actual number of Arnoldi iterations taken. - - ARFLOAT GetTol() { return tol; } - // Returns the stopping criterion. - - int GetN() { return n; } - // Returns the dimension of the problem. - - int GetNev() { return nev; } - // Returns the number of eigenvalues to be computed. - - int GetNcv() { return ncv; } - // Returns the number of Arnoldi vectors generated at each iteration.. - - const std::string& GetWhich() { return which; } - // Returns "which". - - ARTYPE GetShift() { return sigmaR; } - // Returns the shift (when in shift and invert mode). - - bool GetAutoShift() { return AutoShift; } - // Returns "AutoShift". - - int GetMode() { return mode; } - // Returns the type of problem (regular, shift and invert, etc). - - - // c.4) Functions that allow changes in problem parameters. - - void ChangeMaxit(int maxitp); - // Changes the maximum number of Arnoldi update iterations allowed. - - void ChangeTol(ARFLOAT tolp); - // Changes the stopping criterion. - - virtual void ChangeNev(int nevp); - // Changes the number of eigenvalues to be computed. - - virtual void ChangeNcv(int ncvp); - // Changes the number of Arnoldi vectors generated at each iteration.. - - virtual void ChangeWhich(const std::string& whichp); - // Changes "which". - - virtual void ChangeShift(ARTYPE sigmaRp); - // Turns the problem to shift-and-invert mode with shift defined by sigmaRp. - // Redefined in many derived classes. - - virtual void NoShift(); - // Turns the problem to regular mode. - // Redefined in ARrcGenEig. - - void InvertAutoShift(); - // Inverts "AutoShift". - - virtual void SetRegularMode() { NoShift(); } - // Turns problem to regular mode. - - virtual void SetShiftInvertMode(ARTYPE sigmaRp) { ChangeShift(sigmaRp); } - // Turns problem to regular mode. - - // c.5) Trace functions. - - virtual void Trace() { - throw ArpackError(ArpackError::NOT_IMPLEMENTED, "Trace"); - } - // Turns on trace mode. - // Must be defined by a derived class. - // Redefined in ARrc[Sym|NonSym|Complex]StdEig. - - void NoTrace() { TraceOff(); } - // Turns off trace mode. - - - // c.6) Functions that permit step by step execution of ARPACK. - - int GetNp() { return iparam[8]; } - // Returns the number of shifts that must be supplied after a call to - // TakeStep() when shifts are provided by the user (AutoShift = false). - - int GetIdo() { return ido; } - // Indicates the type of operation the user must perform between two - // successive calls to function TakeStep(). - // ido = -1: compute y = OP*x, where GetVector() gives a pointer to the - // vector x, and PutVector() indicates where to store y. - // ido = 1: compute y = OP*x, where GetVector() gives a pointer to the - // vector x, and PutVector() indicates where to store y. - // When solving generalized problems, a pointer to the product - // B*x is also available by using GetProd(). - // ido = 2: compute y = B*x, where GetVector() gives a pointer to the - // vector x, and PutVector() indicates where to store y. - // ido = 3: compute shifts, where PutVector() indicates where to store them. - - virtual ARTYPE* GetVector(); - // When ido = -1, 1 or 2 and the user must perform a product in the form - // y <- M*x, this function indicates where x is stored. When ido = 3, this - // function indicates where the eigenvalues of the current Hessenberg - // matrix are located. - - ARTYPE* GetProd(); - // When ido = 1 and the user must perform a product in the form - // y <- M*B*x, this function indicates where B*x is stored. - - virtual ARTYPE* PutVector(); - // When ido = -1, 1 or 2 and the user must perform a product in the form - // y <- M*x, this function indicates where to store y. When ido = 3, this - // function indicates where to store the shifts. - // Redefined in ARrcSymStdEig. - - virtual int TakeStep(); - // Calls Aupp once if there is no Arnoldi basis available. - - - // c.7) Functions that perform final calculations. - - virtual int FindArnoldiBasis(); - // Determines the Arnoldi basis related to the given problem. - // This function has no meaning for this class. It is maintained - // here for compatibility with all derived classes. - // Redefined in ARStdEig, ARGenEig and ARSymGenEig. - - virtual int FindEigenvalues(); - // Determines nev approximated eigenvalues of the given eigen-problem. - // Redefined in ARNonSymGenEig. - - virtual int FindEigenvectors(bool schurp = false); - // Determines nev approximated eigenvectors of the given eigen-problem - // Optionally also determines nev Schur vectors that span the desired - // invariant subspace. - // Redefined in ARNonSymGenEig. - - virtual int FindSchurVectors(); - // Determines nev Schur vectors that span the desired invariant subspace. - // Redefined in ARrcSymStdEig and ARNonSymGenEig. - - - // c.8) Function that perform calculations using user supplied data structure. - - virtual int Eigenvectors(ARTYPE* &EigVecp, bool ischur = false); - // Overrides array EigVecp sequentially with the eigenvectors of the - // given eigen-problem. Also calculates Schur vectors if requested. - - - // c.9) Functions that return elements of vectors and matrices. - - ARTYPE ArnoldiBasisVector(int i, int j); - // Furnishes element j of the i-eth Arnoldi basis vector. - - ARTYPE SchurVector(int i, int j); - // Furnishes element j of the i-eth Schur vector. - - ARTYPE ResidualVector(int i); - // Furnishes element j of the residual vector. - - - // c.10) Functions that provide raw access to internal vectors and matrices. - - ARTYPE* RawArnoldiBasisVectors(); - // Provides raw access to Arnoldi basis vectors elements. - - ARTYPE* RawArnoldiBasisVector(int i); - // Provides raw access to Arnoldi basis vector i. - - ARTYPE* RawEigenvalues(); - // Provides raw access to eigenvalues. - - ARTYPE* RawEigenvectors(); - // Provides raw access to eigenvectors elements. - - ARTYPE* RawEigenvector(int i); - // Provides raw access to eigenvector i. - - ARTYPE* RawSchurVectors(); - // Provides raw access to Schur vectors elements. - - ARTYPE* RawSchurVector(int i); - // Provides raw access to Schur vector i. - - ARTYPE* RawResidualVector(); - // Provides raw access to residual vector elements. - - - // c.11) Functions that use STL vector class. - -#ifdef STL_VECTOR_H - - vector* StlArnoldiBasisVectors(); - // Returns a copy of the Arnoldi basis in a single STL vector. - - vector* StlArnoldiBasisVector(int i); - // Returns the i-th Arnoldi basis vector in a STL vector. - - vector* StlEigenvectors(bool ischur = false); - // Calculates the eigenvectors and stores them sequentially in a - // single STL vector. Also calculates Schur vectors if requested. - - vector* StlSchurVectors(); - // Returns a copy of the Schur vectors in a single STL vector. - - vector* StlSchurVector(int i); - // Returns the i-th Schur vector in a STL vector. - - vector* StlResidualVector(); - // Returns the residual vector in a STL vector. - -#endif // #ifdef STL_VECTOR_H. - - - // c.12) Constructors and destructor. - - ARrcStdEig(); - // Short constructor that does almost nothing. - - ARrcStdEig(const ARrcStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARrcStdEig() { ClearMem(); } - // Very simple destructor. - - // d) Operators: - - ARrcStdEig& operator=(const ARrcStdEig& other); - // Assignment operator. - -}; // class ARrcStdEig. - - -// ------------------------------------------------------------------------ // -// ARrcStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARrcStdEig::ClearFirst() -{ - - PrepareOK = newVal = newVec = newRes = false; - -} // ClearFirst. - - -template -inline void ARrcStdEig::ClearBasis() -{ - - BasisOK = ValuesOK = VectorsOK = SchurOK = false; - -} // ClearBasis. - - -template -void ARrcStdEig::ClearMem() -{ - - // Deleting working arrays. - - if (workl) delete[] workl; - if (workd) delete[] workd; - if (workv) delete[] workv; - if (rwork) delete[] rwork; - if (V) delete[] V; - - workl = NULL; - workd = NULL; - workv = NULL; - rwork = NULL; - V = NULL; - - // Deleting input and output arrays. - - if (newRes) { - delete[] resid; - newRes = false; - resid = NULL; // Salwen. Mar 3, 2000. - } - - if (newVal) { - delete[] EigValR; - delete[] EigValI; - newVal = false; - } - EigValR=NULL; - EigValI=NULL; - - if (newVec) { - delete[] EigVec; - newVec = false; - } - EigVec=NULL; - - // Adjusting boolean variables. - - ClearFirst(); - -} // ClearMem. - - -template -inline void ARrcStdEig::ValAllocate() -{ - - if (EigValR == NULL) { // Creating a new array EigValR. - EigValR = new ARTYPE[ValSize()]; - newVal = true; - } - -} // ValAllocate. - -template -inline void ARrcStdEig::VecAllocate(bool newV) -{ - - if (EigVec == NULL) { - if (newV) { // Creating a new array EigVec. - EigVec = new ARTYPE[ValSize()*n]; - newVec = true; - } - else { // Using V to store EigVec. - EigVec = &V[1]; - } - } - -} // VecAllocate. - - -template -void ARrcStdEig::WorkspaceAllocate() -{ - - lworkl = 0; - lworkv = 0; - lrwork = 0; - -} // WorkspaceAllocate. - - -template -void ARrcStdEig::AuppError() -{ - - switch (info) { - case 0: - return; - case -8: - throw ArpackError(ArpackError::LAPACK_ERROR, "Aupp"); - case -9: - throw ArpackError(ArpackError::START_RESID_ZERO, "Aupp"); - case -9999: - throw ArpackError(ArpackError::ARNOLDI_NOT_BUILD, "Aupp"); - case 1: - ArpackError(ArpackError::MAX_ITERATIONS, "Aupp"); - return; - case 3: - ArpackError(ArpackError::NO_SHIFTS_APPLIED, "Aupp"); - return; - default : - throw ArpackError(ArpackError::AUPP_ERROR, "Aupp"); - } - -} // AuppError. - - -template -void ARrcStdEig::EuppError() -{ - - switch (info) { - case 0: - return; - case -8: - case -9: - throw ArpackError(ArpackError::LAPACK_ERROR, "Eupp"); - case -14: - throw ArpackError(ArpackError::NOT_ACCURATE_EIG, "Eupp"); - case 1: - throw ArpackError(ArpackError::REORDERING_ERROR, "Eupp"); - default : - throw ArpackError(ArpackError::EUPP_ERROR, "Eupp"); - } - -} // EuppError. - - -template -inline int ARrcStdEig::CheckN(int np) -{ - - if (np < 2) { - throw ArpackError(ArpackError::N_SMALLER_THAN_2); - } - return np; - -} // CheckN. - - -template -inline int ARrcStdEig::CheckNcv(int ncvp) -{ - - // Adjusting ncv if ncv <= nev or ncv > n. - - if (ncvp < nev+1) { - if (ncvp) ArpackError::Set(ArpackError::NCV_OUT_OF_BOUNDS); - return ((2*nev+1)>n)?n:(2*nev+1); - } - else if (ncvp > n) { - ArpackError::Set(ArpackError::NCV_OUT_OF_BOUNDS); - return n; - } - else { - return ncvp; - } - -} // CheckNcv. - - -template -inline int ARrcStdEig::CheckNev(int nevp) -{ - - if ((nevp<1)||(nevp>=n)) { - throw ArpackError(ArpackError::NEV_OUT_OF_BOUNDS); - } - return nevp; - -} // CheckNev. - - -template -inline int ARrcStdEig::CheckMaxit(int maxitp) -{ - - if (maxitp >= 1) return maxitp; - if (maxitp < 0) ArpackError::Set(ArpackError::MAXIT_NON_POSITIVE); - return 100*nev; - -} // CheckMaxit. - -template -std::string ARrcStdEig::CheckWhich(const std::string& whichp) -{ - - switch (whichp[0]) { // The first ought to be S or L. - case 'S': - case 'L': - switch (whichp[1]) { // The second must be M, R or I. - case 'M': - case 'R': - case 'I': - return whichp; - } - default : - throw ArpackError(ArpackError::WHICH_UNDEFINED); - } - -} // CheckWhich. - - -template -void ARrcStdEig::Restart() -{ - - nconv=0; // No eigenvalues found yet. - ido =0; // First call to AUPP. - iparam[1]=(int)AutoShift; // Shift strategy used. - iparam[3]=maxit; // Maximum number of Arnoldi iterations allowed. - iparam[4]=1; // Blocksize must be 1. - info =(int)(!newRes); // Starting vector used. - ClearBasis(); - -} // Restart. - - -template -void ARrcStdEig::Prepare() -{ - - // Deleting old stuff. - - ClearMem(); - - // Defining internal variables. - - try { - - if (resid == NULL) { // Using a random starting vector. - resid = new ARTYPE[n]; - newRes = true; - } - - // Setting dimensions of working arrays. - - workd = new ARTYPE[3*n+1]; - V = new ARTYPE[n*ncv+1]; - WorkspaceAllocate(); - - } - catch (ArpackError) { // Returning from here if an error has occurred. - ArpackError(ArpackError::CANNOT_PREPARE, "Prepare"); - return; - } - - Restart(); - - // That's all. - - PrepareOK = true; - -} // Prepare. - - -template -void ARrcStdEig::Copy(const ARrcStdEig& other) -{ - - // Defining local variables. - - int i; - - // Copying variables that belong to fundamental types. - - n = other.n; - nev = other.nev; - ncv = other.ncv; - maxit = other.maxit; - which = other.which; - tol = other.tol; - sigmaI = other.sigmaI; - sigmaR = other.sigmaR; - rvec = other.rvec; - newRes = other.newRes; - newVal = other.newVal; - newVec = other.newVec; - PrepareOK = other.PrepareOK; - BasisOK = other.BasisOK; - ValuesOK = other.ValuesOK; - VectorsOK = other.VectorsOK; - SchurOK = other.SchurOK; - AutoShift = other.AutoShift; - bmat = other.bmat; - HowMny = other.HowMny; - ido = other.ido; - info = other.info; - mode = other.mode; - nconv = other.nconv; - - // Copying arrays with static dimension. - - for (i=0; i<12; i++) iparam[i] = other.iparam[i]; - for (i=0; i<15; i++) ipntr[i] = other.ipntr[i]; - - // Returning from here if "other" was not initialized. - - if (!PrepareOK) return; - - // Copying dynamic variables. - - workd = new ARTYPE[3*n+1]; // workd. - copy(3*n+1,other.workd,1,workd,1); - - V = new ARTYPE[n*ncv+1]; // V. - copy(n*ncv+1,other.V,1,V,1); - - if (newRes) { // resid. - resid = new ARTYPE[n]; - copy(n,other.resid,1,resid,1); - } - else { - resid = other.resid; - } - - if (newVec) { // EigVec. - EigVec = new ARTYPE[ValSize()*n]; - copy(ValSize()*n,other.EigVec,1,EigVec,1); - } - else if (other.EigVec == (&other.V[1])) { - EigVec = &V[1]; - } - else { - EigVec = other.EigVec; - } - - if (newVal) { // EigValR and EigValI. - EigValR = new ARTYPE[ValSize()]; - copy(ValSize(),other.EigValR,1,EigValR,1); - if (other.EigValI != NULL) { - EigValI = new ARFLOAT[ValSize()]; - copy(ValSize(),other.EigValI,1,EigValI,1); - } - else { - EigValI = NULL; - } - } - else { - EigValR = other.EigValR; - EigValI = other.EigValI; - } - - WorkspaceAllocate(); // lworkl, workl, workv and rwork. - if (lworkl) copy(lworkl+1,other.workl,1,workl,1); - if (lworkv) copy(lworkv+1,other.workv,1,workv,1); - if (lrwork) copy(lrwork+1,other.rwork,1,rwork,1); - -} // Copy. - - -template -void ARrcStdEig:: -DefineParameters(int np, int nevp, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARTYPE* residp, bool ishiftp) - -{ - - // Setting user defined parameters. - - try { - n = CheckN(np); - nev = ARrcStdEig::CheckNev(nevp); - ncv = ARrcStdEig::CheckNcv(ncvp); - which = ARrcStdEig::CheckWhich(whichp); - maxit = ARrcStdEig::CheckMaxit(maxitp); - tol = tolp; - resid = residp; - AutoShift = ishiftp; - } - - // Returning from here if an error has occurred. - - catch (ArpackError) { - ArpackError(ArpackError::PARAMETER_ERROR, "DefineParameter"); - return; - } - - // Setting internal variables. - - ClearFirst(); - Prepare(); - -} // DefineParameters. - - -template -int ARrcStdEig::GetIter() -{ - - if (BasisOK || ValuesOK || VectorsOK || SchurOK) { - return iparam[3]; - } - else { - return 0; - } - -} // GetIter. - - -template -inline void ARrcStdEig::ChangeMaxit(int maxitp) -{ - - maxit = CheckMaxit(maxitp); - Restart(); - -} // ChangeMaxit. - - -template -inline void ARrcStdEig::ChangeTol(ARFLOAT tolp) -{ - - if (tolp < tol) Restart(); - tol = tolp; - -} // ChangeTol. - - -template -void ARrcStdEig::ChangeNev(int nevp) -{ - - try { - nev = CheckNev(nevp); - ncv = CheckNcv(ncv); - } - catch (ArpackError) { return; } - if (PrepareOK) Prepare(); - -} // ChangeNev. - - -template -inline void ARrcStdEig::ChangeNcv(int ncvp) -{ - - ncv = CheckNcv(ncvp); - if (PrepareOK) Prepare(); - -} // ChangeNcv. - - -template -void ARrcStdEig::ChangeWhich(const std::string& whichp) -{ - - try { which = CheckWhich(whichp); } - catch (ArpackError) { return; } - Restart(); - -} // ChangeWhich. - - -template -inline void ARrcStdEig::ChangeShift(ARTYPE sigmaRp) -{ - - sigmaR = sigmaRp; - sigmaI = 0.0; - mode = 3; - iparam[7] = mode; - Restart(); - -} // ChangeShift. - - -template -inline void ARrcStdEig::NoShift() -{ - - sigmaR = (ARTYPE)0; - sigmaI = 0.0; - mode = 1; - iparam[7] = mode; - Restart(); - -} // NoShift. - - -template -void ARrcStdEig::InvertAutoShift() -{ - - AutoShift = !AutoShift; - Restart(); - -} // InvertAutoShift. - - -template -ARTYPE* ARrcStdEig::GetVector() -{ - - switch (ido) { - case -1: - case 1: - case 2: - return &workd[ipntr[1]]; - case 3: - return &workl[ipntr[6]]; - default: - throw ArpackError(ArpackError::CANNOT_GET_VECTOR, "GetVector"); - } - -} // GetVector. - - -template -ARTYPE* ARrcStdEig::GetProd() -{ - - if (ido != 1) { - throw ArpackError(ArpackError::CANNOT_GET_PROD, "GetProd"); - } - return &workd[ipntr[3]]; - -} // GetProd. - - -template -ARTYPE* ARrcStdEig::PutVector() -{ - - switch (ido) { - case -1: - case 1: - case 2: - return &workd[ipntr[2]]; - case 3: - return &workl[ipntr[14]]; - default: - throw ArpackError(ArpackError::CANNOT_PUT_VECTOR, "PutVector"); - } - -} // PutVector. - - -template -int ARrcStdEig::TakeStep() -{ - - // Requiring the definition of all internal variables. - - if (!PrepareOK) { - - throw ArpackError(ArpackError::PREPARE_NOT_OK, "TakeStep"); - - } - else if (!BasisOK) { - - // Taking a step if the Arnoldi basis is not available. - - Aupp(); - - // Checking if convergence was obtained. - - if (ido==99) { - nconv = iparam[5]; - AuppError(); - if (info >= 0) BasisOK = true; - } - } - - return ido; - -} // TakeStep. - - -template -inline int ARrcStdEig::FindArnoldiBasis() -{ - - if (!BasisOK) { - throw ArpackError(ArpackError::CANNOT_FIND_BASIS, "FindArnoldiBasis"); - } - return nconv; - -} // FindArnoldiBasis. - - -template -int ARrcStdEig::FindEigenvalues() -{ - - // Determining eigenvalues if they are not available. - - if (!ValuesOK) { - try { - ValAllocate(); - nconv = FindArnoldiBasis(); - rvec = false; - HowMny = 'A'; - if (nconv>0) { - Eupp(); - EuppError(); - } - } - catch (ArpackError) { - ArpackError(ArpackError::CANNOT_FIND_VALUES, "FindEigenvalues"); - return 0; - } - if (newVal) ValuesOK = true; - } - return nconv; - -} // FindEigenvalues. - - -template -int ARrcStdEig::FindEigenvectors(bool schurp) -{ - - // Determining eigenvectors if they are not available. - - if (!VectorsOK) { - try { - ValAllocate(); - VecAllocate(schurp); - nconv = FindArnoldiBasis(); - rvec = true; - HowMny = 'A'; - if (nconv>0) { - Eupp(); - EuppError(); - } - } - catch (ArpackError) { - ArpackError(ArpackError::CANNOT_FIND_VECTORS, "FindEigenvectors"); - return 0; - } - BasisOK = false; - if (newVal) ValuesOK = true; - if (newVec || OverV()) VectorsOK = true; - if (!OverV()) SchurOK = true; - } - return nconv; - -} // FindEigenvectors. - - -template -int ARrcStdEig::FindSchurVectors() -{ - - // Determining Schur vectors if they are not available. - - if (!SchurOK) { - try { - ValAllocate(); - nconv = FindArnoldiBasis(); - rvec = true; - HowMny = 'P'; - if (nconv>0) { - Eupp(); - EuppError(); - } - } - catch (ArpackError) { - ArpackError(ArpackError::CANNOT_FIND_SCHUR, "FindSchurVectors"); - return 0; - } - BasisOK = false; - if (newVal) ValuesOK = true; - SchurOK =true; - } - return nconv; - -} // FindSchurVectors. - - -template -int ARrcStdEig:: -Eigenvectors(ARTYPE* &EigVecp, bool ischur) -{ - - // Overriding EigVecp with the converged eigenvectors. - - if (VectorsOK) { // Eigenvectors are available. - if ((EigVecp == NULL) && (newVec)) { // Moving eigenvectors. - EigVecp = EigVec; - EigVec = NULL; - newVec = false; - VectorsOK = false; - } - else { // Copying eigenvectors. - if (EigVecp == NULL) { - try { EigVecp = new ARTYPE[ValSize()*n]; } - catch (ArpackError) { return 0; } - } - copy(ValSize()*n,EigVec,1,EigVecp,1); - } - } - else { // Eigenvectors are not available. - if (newVec) { - delete[] EigVec; - newVec = false; - } - if (EigVecp == NULL) { - try { EigVecp = new ARTYPE[ValSize()*n]; } - catch (ArpackError) { return 0; } - } - EigVec = EigVecp; - nconv = FindEigenvectors(ischur); - EigVec = NULL; - } - return nconv; - -} // Eigenvectors(EigVecp, ischur). - - -template -inline ARTYPE ARrcStdEig::ArnoldiBasisVector(int i, int j) -{ - - // Returning element j of Arnoldi basis vector i. - - if (!BasisOK) { - throw ArpackError(ArpackError::BASIS_NOT_OK, "ArnoldiBasisVector(i,j)"); - } - else if ((i>=ncv)||(i<0)||(j>=n)||(j<0)) { - throw ArpackError(ArpackError::RANGE_ERROR,"ArnoldiBasisVector(i,j)"); - } - return V[i*n+j+1]; - -} // ArnoldiBasisVector(i,j). - - -template -inline ARTYPE ARrcStdEig::SchurVector(int i, int j) -{ - - // Returning element j of Schur vector i. - - if (!SchurOK) { - throw ArpackError(ArpackError::SCHUR_NOT_OK, "SchurVector(i,j)"); - } - else if ((i>=nconv)||(i<0)||(j>=n)||(j<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "SchurVector(i,j)"); - } - return V[i*n+j+1]; - -} // SchurVector(i,j). - - -template -inline ARTYPE ARrcStdEig::ResidualVector(int i) -{ - - // Returning element i of the residual vector. - - if ((!newRes)||(!(BasisOK||ValuesOK||VectorsOK||SchurOK))) { - throw ArpackError(ArpackError::RESID_NOT_OK, "ResidualVector(i)"); - } - else if ((i>=n)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "ResidualVector(i)"); - } - return resid[i]; - -} // ResidualVector(i). - - -template -inline ARTYPE* ARrcStdEig::RawArnoldiBasisVectors() -{ - - // Returning a constant pointer to Arnoldi basis. - - if (!BasisOK) { - throw ArpackError(ArpackError::BASIS_NOT_OK, "RawArnoldiBasisVectors"); - } - return &V[1]; - -} // RawArnoldiBasisVectors. - - -template -inline ARTYPE* ARrcStdEig::RawArnoldiBasisVector(int i) -{ - - // Returning a constant pointer to Arnoldi basis vector i. - - if (!BasisOK) { - throw ArpackError(ArpackError::BASIS_NOT_OK, "RawArnoldiBasisVector(i)"); - } - else if ((i>=ncv)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR,"RawArnoldiBasisVector(i)"); - } - return &V[i*n+1]; - -} // RawArnoldiBasisVector(i). - - -template -inline ARTYPE* ARrcStdEig::RawEigenvalues() -{ - - if (!ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "RawEigenvalues"); - } - return EigValR; - -} // RawEigenvalues. - - -template -inline ARTYPE* ARrcStdEig::RawEigenvectors() -{ - - if (!VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "RawEigenvectors"); - } - return EigVec; - -} // RawEigenvectors. - - -template -inline ARTYPE* ARrcStdEig::RawEigenvector(int i) -{ - - // Returning a constant pointer to eigenvector i. - - if (!VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "RawEigenvector(i)"); - } - else if ((i>=ValSize())||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "RawEigenvector(i)"); - } - return &EigVec[i*n]; - -} // RawEigenvector(i). - - -template -inline ARTYPE* ARrcStdEig::RawSchurVectors() -{ - - if (!SchurOK) { - throw ArpackError(ArpackError::SCHUR_NOT_OK, "RawSchurVectors"); - } - return &V[1]; - -} // RawSchurVectors. - - -template -inline ARTYPE* ARrcStdEig::RawSchurVector(int i) -{ - - // Returning a constant pointer to Schur vector i. - - if (!SchurOK) { - throw ArpackError(ArpackError::SCHUR_NOT_OK, "RawSchurVector(i)"); - } - else if ((i>=nev)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "RawSchurVector(i)"); - } - return &V[i*n+1]; - -} // RawSchurVector(i). - - -template -inline ARTYPE* ARrcStdEig::RawResidualVector() -{ - - if (!newRes) { - throw ArpackError(ArpackError::RESID_NOT_OK, "RawResidualVector"); - } - return resid; - -} // RawResidualVector. - - -#ifdef STL_VECTOR_H // Defining some functions that use STL vector class. - -template -inline vector* ARrcStdEig::StlArnoldiBasisVectors() -{ - - // Returning the Arnoldi basis in a single STL vector. - - vector* StlBasis; - - if (!BasisOK) { - nconv = FindArnoldiBasis(); - } - try { - StlBasis = new vector(&V[1], &V[n*ncv+1]); - } - catch (ArpackError) { return NULL; } - return StlBasis; - -} // StlArnoldiBasisVectors. - - -template -inline vector* ARrcStdEig::StlArnoldiBasisVector(int i) -{ - - // Returning the i-th Arnoldi basis vector in a STL vector. - - vector* StlBasis; - - if (!BasisOK) { - throw ArpackError(ArpackError::BASIS_NOT_OK, "StlArnoldiBasisVector(i)"); - } - else if ((i>=ncv)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR,"StlArnoldiBasisVector(i)"); - } - try { - StlBasis = new vector(&V[i*n+1], &V[(i+1)*n+1]); - } - catch (ArpackError) { return NULL; } - return StlBasis; - -} // StlArnoldiBasisVector(i). - - -template -inline vector* ARrcStdEig::StlEigenvectors(bool ischur) -{ - - // Returning the eigenvector in a single STL vector. - - vector* StlEigVec; - ARTYPE* VecPtr; - - try { StlEigVec = new vector(ValSize()*n); } - catch (ArpackError) { return NULL; } - VecPtr = StlEigVec->begin(); - nconv = Eigenvectors(VecPtr, ischur); - return StlEigVec; - -} // StlEigenvectors. - - -template -inline vector* ARrcStdEig::StlSchurVectors() -{ - - vector* StlSchurVec; - - if (!SchurOK) { - nconv = FindSchurVectors(); - } - try { - StlSchurVec = new vector(&V[1], &V[nev*n+1]); - } - catch (ArpackError) { return NULL; } - return StlSchurVec; - -} // StlSchurVectors. - - -template -inline vector* ARrcStdEig::StlSchurVector(int i) -{ - - // Returning the i-th Schur vector in a STL vector. - - vector* StlSchurVec; - - if (!SchurOK) { - throw ArpackError(ArpackError::SCHUR_NOT_OK, "StlSchurVector(i)"); - } - else if ((i>=nev)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "StlSchurVector(i)"); - } - try { - StlSchurVec = new vector(&V[i*n+1], &V[(i+1)*n+1]); - } - catch (ArpackError) { return NULL; } - return StlSchurVec; - -} // StlSchurVector(i). - - -template -inline vector* ARrcStdEig::StlResidualVector() -{ - - // Returning the residual vector in a STL vector. - - vector* StlResid; - - if (!newRes) { - throw ArpackError(ArpackError::RESID_NOT_OK, "StlResidualVector"); - } - try { - StlResid = new vector(resid, &resid[n]); - } - catch (ArpackError) { return NULL; } - return StlResid; - -} // StlResidualVector. - -#endif // #ifdef STL_VECTOR_H. - - -template -inline ARrcStdEig::ARrcStdEig() -{ - - resid = NULL; - rwork = NULL; - workl = NULL; - workd = NULL; - workv = NULL; - V = NULL; - EigValR = NULL; - EigValI = NULL; - EigVec = NULL; - bmat = 'I'; // This is a standard problem. - ClearFirst(); - NoShift(); - NoTrace(); - -} // Short constructor. - - -template -ARrcStdEig& ARrcStdEig:: -operator=(const ARrcStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARRSEIG_H - diff --git a/src/external/arpack++/include/arrsnsym.h b/src/external/arpack++/include/arrsnsym.h deleted file mode 100644 index 106b10df..00000000 --- a/src/external/arpack++/include/arrsnsym.h +++ /dev/null @@ -1,861 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARRSNSym.h. - Arpack++ class ARrcNonSymStdEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARRSNSYM_H -#define ARRSNSYM_H - -#include - -#include "arch.h" -#include "arerror.h" -#include "debug.h" -#include "arrseig.h" -#include "naupp.h" -#include "neupp.h" - - -template -class ARrcNonSymStdEig: public virtual ARrcStdEig { - - protected: - - // a) Protected functions: - - // a.1) Memory control functions. - - int ValSize() { return this->nev+1; } - // Provides the size of array EigVal. - - void ValAllocate(); - // Creates arrays EigValR and EigValI. - - void WorkspaceAllocate(); - // Allocates workspace for nonsymmetric problems. - - - // a.2) Functions that handle original FORTRAN ARPACK code. - - void Aupp(); - // Interface to FORTRAN subroutines SNAUPD and DNAUPD. - - void Eupp(); - // Interface to FORTRAN subroutines SNEUPD and DNEUPD. - - - // a.3) Functions that check user defined parameters. - - int CheckNev(int nevp); - // Does Range checking on nev. - - - // a.4) Auxiliary functions required when using STL vector class. - - bool ConjEigVec(int i); - // Indicates if EigVec[i] is the second eigenvector in - // a complex conjugate pair. - -#ifdef ARCOMP_H -#ifdef STL_VECTOR_H - - vector >* GenComplex(vector* RealPart, - vector* ImagPart, - bool conj = false); - // Generates a complex vector Complex = RealPart + I*ImagPart - // (or Complex = RealPart - I*ImagPart, if conj = true). - - vector >* GenComplex(int dim, ARFLOAT* RealPart, - ARFLOAT* ImagPart, - bool conj = false); - // Generates a complex vector Complex = RealPart + I*ImagPart - // (or Complex = RealPart - I*ImagPart, if conj = true). dim - // is the length of RealPart and ImagPart. - - vector >* GenComplex(int dim, ARFLOAT* RealPart); - // Generates a complex vector from a real vector. dim is the - // length of RealPart. - -#endif // STL_VECTOR_H. -#endif // ARCOMP_H. - - public: - - // b) Public functions: - - // b.1) Trace functions. - - void Trace(const int digit = -5, const int getv0 = 0, const int aupd = 1, - const int aup2 = 0, const int aitr = 0, const int eigt = 0, - const int apps = 0, const int gets = 0, const int eupd = 0) - { - nTraceOn(digit, getv0, aupd, aup2, aitr, eigt, apps, gets, eupd); - } - // Turns on trace mode. - - - // b.2) Functions that permit step by step execution of ARPACK. - - ARFLOAT* GetVectorImag(); - // When ido = 3, this function indicates where the imaginary part - // of the eigenvalues of the current Hessenberg matrix are located. - - - // b.3) Functions that perform all calculations in one step. - - int Eigenvalues(ARFLOAT* &EigValRp, ARFLOAT* &EigValIp, - bool ivec = false, bool ischur = false); - // Overrides arrays EigValRp with the real part and EigValIp - // with the imaginary part of the eigenvalues of the problem. - // Calculates eigenvectors and Schur vectors if requested. - - int EigenValVectors(ARFLOAT* &EigVecp, ARFLOAT* &EigValRp, - ARFLOAT* &EigValIp, bool ischur = false); - // Overrides array EigVecp sequentially with the eigenvectors of the - // given eigen-problem. Also stores the eigenvalues in EigValRp and - // EigValIp. Calculates Schur vectors if requested. - - - // b.4) Functions that return elements of vectors and matrices. - -#ifdef ARCOMP_H - arcomplex Eigenvalue(int i); - // Furnishes i-eth eigenvalue. -#endif // ARCOMP_H. - - ARFLOAT EigenvalueReal(int i); - // Provides the real part of the i-eth eigenvalue. - - ARFLOAT EigenvalueImag(int i); - // Provides the imaginary part of the i-eth eigenvalue. - -#ifdef ARCOMP_H - arcomplex Eigenvector(int i, int j); - // Furnishes element j of the i-eth eigenvector. -#endif // ARCOMP_H. - - ARFLOAT EigenvectorReal(int i, int j); - // Provides the real part of element j of the i-eth eigenvector. - - ARFLOAT EigenvectorImag(int i, int j); - // Provides the imaginary part of element j of the i-eth eigenvector. - - - // b.5) Functions that provide raw access to internal vectors and matrices. - - ARFLOAT* RawEigenvaluesImag(); - // Provides raw access to the imaginary part of eigenvalues. - - - // b.6) Functions that use STL vector class. - -#ifdef STL_VECTOR_H - -#ifdef ARCOMP_H - vector >* StlEigenvalues(bool ivec = false, - bool ischur = false); - // Calculates the eigenvalues and stores them in a single STL vector. - // Also calculates eigenvectors and Schur vectors if requested. -#endif // ARCOMP_H. - - vector* StlEigenvaluesReal(); - // Returns the real part of the eigenvalues. - - vector* StlEigenvaluesImag(); - // Returns the imaginary part of the eigenvalues. - -#ifdef ARCOMP_H - vector >* StlEigenvector(int i); - // Returns the i-th eigenvector. -#endif // ARCOMP_H. - - vector* StlEigenvectorReal(int i); - // Returns the real part of the i-th eigenvector. - - vector* StlEigenvectorImag(int i); - // Returns the imaginary part of the i-th eigenvector. - -#endif // STL_VECTOR_H. - - - // b.7) Constructors and destructor. - - ARrcNonSymStdEig() { } - // Short constructor. - - ARrcNonSymStdEig(int np, int nevp, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, ARFLOAT* residp = NULL, - bool ishiftp = true); - // Long constructor (regular mode). - - ARrcNonSymStdEig(int np, int nevp, ARFLOAT sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARrcNonSymStdEig(const ARrcNonSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARrcNonSymStdEig() { } - // Destructor. - - // c) Operators. - - ARrcNonSymStdEig& operator=(const ARrcNonSymStdEig& other); - // Assignment operator. - -}; // class ARrcNonSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARrcNonSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARrcNonSymStdEig::ValAllocate() -{ - - if (this->EigValR == NULL) { - this->EigValR = new ARFLOAT[ValSize()]; - this->EigValI = new ARFLOAT[ValSize()]; - this->newVal = true; - } - -} // ValAllocate. - - -template -inline void ARrcNonSymStdEig::WorkspaceAllocate() -{ - - this->lworkl = 3*this->ncv*(this->ncv+2); - this->lworkv = 3*this->ncv; - this->lrwork = 0; - this->workl = new ARFLOAT[this->lworkl+1]; - this->workv = new ARFLOAT[this->lworkv+1]; - -} // WorkspaceAllocate. - - -template -inline void ARrcNonSymStdEig::Aupp() -{ - - naupp(this->ido,this-> bmat, this->n, this->which, this->nev, this->tol, this->resid, this->ncv, this->V, this->n, - this->iparam, this->ipntr, this->workd, this->workl, this->lworkl, this->info); - -} // Aupp. - - -template -inline void ARrcNonSymStdEig::Eupp() -{ - - neupp(this->rvec, this->HowMny, this->EigValR, this->EigValI, this->EigVec, this->n, this->sigmaR, - this->sigmaI, this->workv, this->bmat, this->n, this->which, this->nev, this->tol, this->resid, this->ncv, this->V, - this->n, this->iparam, this->ipntr, this->workd, this->workl, this->lworkl, this->info); - -} // Eupp. - - -template -inline int ARrcNonSymStdEig::CheckNev(int nevp) -{ - std::cout << nevp << "ddddd22g\n";fflush(stdout); - if ((nevp<=1)||(nevp>=(this->n-1))) { // nev must satisfy 1 < nev < n-1. - std::cout << nevp << "dddd21d22g\n";fflush(stdout); - throw ArpackError(ArpackError::NEV_OUT_OF_BOUNDS); - } - return nevp; - -} // CheckNev. - - -template -bool ARrcNonSymStdEig::ConjEigVec(int i) -{ - - if (this->EigValI[i] == (ARFLOAT)0.0) return false; - int j = i-1; - while ((j >= 0) && (this->EigValI[j] != (ARFLOAT)0.0)) j--; - if (((i-j)%2) == 0) { - return true; - } - else { - return false; - } - -} // ConjEigVec. - - -#ifdef STL_VECTOR_H // Defining functions that use STL vector class. -#ifdef ARCOMP_H - -template -vector >* ARrcNonSymStdEig:: -GenComplex(vector* RealPart, vector* ImagPart, bool conj) -{ - - // Defining variables. - - vector >* Result; - try { - Result = new vector >(ValSize()); - } - catch (ArpackError) { return NULL; } - ARFLOAT* rp = RealPart->begin(); - ARFLOAT* ip = ImagPart->begin(); - ARFLOAT* end = RealPart->end(); - arcomplex* s = Result->begin(); - - // Creating a complex vector. - - if (!conj) { - while (rp != end) *s++ = arcomplex(*rp++, *ip++); - } - else { - while (rp != end) *s++ = arcomplex(*rp++, -(*ip++)); - } - - return Result; - -} // GenComplex (vector version). - - -template -vector >* ARrcNonSymStdEig:: -GenComplex(int dim, ARFLOAT* RealPart, ARFLOAT* ImagPart, bool conj) -{ - - // Defining variables. - - vector >* Result; - try { - Result = new vector >(dim); - } - catch (ArpackError) { return NULL; } - ARFLOAT* rp = RealPart; - ARFLOAT* ip = ImagPart; - ARFLOAT* end = &RealPart[dim]; - arcomplex* s = Result->begin(); - - // Creating a complex vector. - - if (!conj) { - while (rp != end) *s++ = arcomplex(*rp++, *ip++); - } - else { - while (rp != end) *s++ = arcomplex(*rp++, -(*ip++)); - } - - return Result; - -} // GenComplex (ARFLOAT* version). - - -template -vector >* ARrcNonSymStdEig:: -GenComplex(int dim, ARFLOAT* RealPart) -{ - - // Defining variables. - - vector >* Result; - try { - Result = new vector >(dim); - } - catch (ArpackError) { return NULL; } - ARFLOAT* rp = RealPart; - ARFLOAT* end = &RealPart[dim]; - arcomplex* s = Result->begin(); - - // Copying a real vector into a complex vector. - - while (rp != end) *s++ = *rp++; - - return Result; - -} // GenComplex. - -#endif // ARCOMP_H. -#endif // STL_VECTOR_H. - - -template -ARFLOAT* ARrcNonSymStdEig::GetVectorImag() -{ - - if (this->ido != 3) { - throw ArpackError(ArpackError::CANNOT_GET_VECTOR, "GetVectorImag"); - } - return &this->workl[this->ipntr[6]]; - -} // GetVectorImag. - - -template -int ARrcNonSymStdEig:: -Eigenvalues(ARFLOAT* &EigValRp, ARFLOAT* &EigValIp, bool ivec, bool ischur) -{ - - if (this->ValuesOK) { // Eigenvalues are available. - if ((EigValRp == NULL)&&(EigValIp == NULL)) { // Moving eigenvalues. - EigValRp = this->EigValR; - EigValIp = this->EigValI; - this->EigValR = NULL; - this->EigValI = NULL; - this->newVal = false; - this->ValuesOK = false; - } - else { // Copying eigenvalues. - try { - if (EigValRp == NULL) EigValRp = new ARFLOAT[ValSize()]; - if (EigValIp == NULL) EigValIp = new ARFLOAT[ValSize()]; - } - catch (ArpackError) { return 0; } - copy(this->nconv,this->EigValR,1,EigValRp,1); - copy(this->nconv,this->EigValI,1,EigValIp,1); - } - } - else { - if (this->newVal) { - delete[] this->EigValR; - delete[] this->EigValI; - this->newVal = false; - } - try { - if (EigValRp == NULL) EigValRp = new ARFLOAT[ValSize()]; - if (EigValIp == NULL) EigValIp = new ARFLOAT[ValSize()]; - } - catch (ArpackError) { return 0; } - this->EigValR = EigValRp; - this->EigValI = EigValIp; - if (ivec) { // Finding eigenvalues and vectors. - this->nconv = this->FindEigenvectors(ischur); - } - else { // Finding eigenvalues only. - this->nconv = this->FindEigenvalues(); - } - this->EigValR = NULL; - this->EigValI = NULL; - } - return this->nconv; - -} // Eigenvalues(EigValRp, EigValIp, ivec, ischur). - - -template -int ARrcNonSymStdEig:: -EigenValVectors(ARFLOAT* &EigVecp, ARFLOAT* &EigValRp, - ARFLOAT* &EigValIp, bool ischur) -{ - - if (this->ValuesOK) { // Eigenvalues are already available . - this->nconv = Eigenvalues(EigValRp, EigValIp, false); - this->nconv = this->Eigenvectors(EigVecp, ischur); - } - else { // Eigenvalues ans vectors are not available. - if (this->newVec) { - delete[] this->EigVec; - this->newVec = false; - } - if (this->newVal) { - delete[] this->EigValR; - delete[] this->EigValI; - this->newVal = false; - } - try { - if (EigVecp == NULL) EigVecp = new ARFLOAT[ValSize()*this->n]; - if (EigValRp == NULL) EigValRp = new ARFLOAT[ValSize()]; - if (EigValIp == NULL) EigValIp = new ARFLOAT[ValSize()]; - } - catch (ArpackError) { return 0; } - this->EigVec = EigVecp; - this->EigValR = EigValRp; - this->EigValI = EigValIp; - this->nconv = this->FindEigenvectors(ischur); - this->EigVec = NULL; - this->EigValR = NULL; - this->EigValI = NULL; - } - return this->nconv; - -} // EigenValVectors(EigVecp, EigValRp, EigValIp, ischur). - - -#ifdef ARCOMP_H -template -inline arcomplex ARrcNonSymStdEig::Eigenvalue(int i) -{ - - // Returning i-eth eigenvalue. - - if (!this->ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "Eigenvalue(i)"); - } - else if ((i>=this->nconv)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "Eigenvalue(i)"); - } - return arcomplex(this->EigValR[i],this->EigValI[i]); - -} // Eigenvalue(i). -#endif // ARCOMP_H - - -template -inline ARFLOAT ARrcNonSymStdEig::EigenvalueReal(int i) -{ - - // Returning the real part of i-eth eigenvalue. - - if (!this->ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "EigenvalueReal(i)"); - } - else if ((i>=this->nconv)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "EigenvalueReal(i)"); - } - return this->EigValR[i]; - -} // EigenvalueReal(i). - - -template -inline ARFLOAT ARrcNonSymStdEig::EigenvalueImag(int i) -{ - - // Returning the imaginary part of i-eth eigenvalue. - - if (!this->ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "EigenvalueImag(i)"); - } - else if ((i>=this->nconv)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "EigenvalueImag(i)"); - } - return this->EigValI[i]; - -} // EigenvalueImag(i). - - -#ifdef ARCOMP_H -template -inline arcomplex ARrcNonSymStdEig:: -Eigenvector(int i, int j) -{ - - // Returning element j of i-eth eigenvector. - - if ((!this->VectorsOK)||(!this->ValuesOK)) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "Eigenvector(i,j)"); - } - else if ((i>=this->nconv)||(i<0)||(j>=this->n)||(j<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "Eigenvector(i,j)"); - } - if (this->EigValI[i]==(ARFLOAT)0.0) { // Real eigenvalue. - return arcomplex(this->EigVec[i*this->n+j],(ARFLOAT)0.0); - } - else { // Complex eigenvalue. - if (this->EigValI[i]>(ARFLOAT)0.0) { // with positive imaginary part. - return arcomplex(this->EigVec[i*this->n+j], this->EigVec[(i+1)*this->n+j]); - } - else { // with negative imaginary part. - return arcomplex(this->EigVec[(i-1)*this->n+j], -this->EigVec[i*this->n+j]); - } - } - -} // Eigenvector(i,j). -#endif // ARCOMP_H - - -template -inline ARFLOAT ARrcNonSymStdEig::EigenvectorReal(int i, int j) -{ - - // Returning the real part of element j of i-eth eigenvector. - - if (!this->VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "EigenvectorReal(i,j)"); - } - else if ((i>=this->nconv)||(i<0)||(j>=this->n)||(j<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "EigenvectorReal(i,j)"); - } - return this->EigVec[i*this->n+j]; - -} // EigenvectorReal(i,j). - - -template -inline ARFLOAT ARrcNonSymStdEig::EigenvectorImag(int i, int j) -{ - - // Returning the imaginary part of element j of i-eth eigenvector. - - if ((!this->VectorsOK)||(!this->ValuesOK)) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "EigenvectorImag(i,j)"); - } - else if ((i>=this->nconv)||(i<0)||(j>=this->n)||(j<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "EigenvectorImag(i,j)"); - } - if (this->EigValI[i]==(ARFLOAT)0.0) { // Real eigenvalue. - return (ARFLOAT)0.0; - } - else { // Complex eigenvalue. - if (this->EigValI[i]>(ARFLOAT)0.0) { // with positive imaginary part. - return this->EigVec[(i+1)*this->n+j]; - } - else { // with negative imaginary part. - return -this->EigVec[i*this->n+j]; - } - } - -} // EigenvectorImag(i,j). - - -template -inline ARFLOAT* ARrcNonSymStdEig::RawEigenvaluesImag() -{ - - if (!this->ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "RawEigenvaluesImag"); - } - return this->EigValI; - -} // RawEigenvaluesImag. - - -#ifdef STL_VECTOR_H // Defining some functions that use STL vector class. - -#ifdef ARCOMP_H -template -inline vector >* ARrcNonSymStdEig:: -StlEigenvalues(bool ivec, bool ischur) -{ - - // Returning the eigenvalues in a STL vector. - - // Defining temporary variables. - - vector* StlEigValR; - vector* StlEigValI; - ARFLOAT* ValRPtr; - ARFLOAT* ValIPtr; - - try { - StlEigValR = new vector(ValSize()); - StlEigValI = new vector(ValSize()); - } - catch (ArpackError) { return NULL; } - - // Finding Eigenvalues. - - ValRPtr = StlEigValR->begin(); - ValIPtr = StlEigValI->begin(); - nconv = Eigenvalues(ValRPtr, ValIPtr, ivec, ischur); - vector >* Val = GenComplex(StlEigValR, StlEigValI); - - // Deleting temporary variables. - - delete StlEigValR; - delete StlEigValI; - - return Val; - -} // StlEigenvalues. -#endif // ARCOMP_H. - - -template -inline vector* ARrcNonSymStdEig::StlEigenvaluesReal() -{ - - // Returning the real part of the eigenvalues in a STL vector. - - vector* StlEigValR; - - if (!ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "StlEigenvaluesReal"); - } - try { - StlEigValR = new vector(EigValR, &EigValR[ValSize()]); - } - catch (ArpackError) { return NULL; } - return StlEigValR; - -} // StlEigenvaluesReal. - - -template -inline vector* ARrcNonSymStdEig::StlEigenvaluesImag() -{ - - // Returning the imaginary part of the eigenvalues in a STL vector. - - vector* StlEigValI; - - if (!ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "StlEigenvaluesImag"); - } - try { - StlEigValI = new vector(EigValI, &EigValI[ValSize()]); - } - catch (ArpackError) { return NULL; } - return StlEigValI; - -} // StlEigenvaluesImag. - - -#ifdef ARCOMP_H -template -inline vector >* ARrcNonSymStdEig:: -StlEigenvector(int i) -{ - - // Returning the i-th eigenvector in a STL vector. - - if (!VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "StlEigenvector(i)"); - } - else if ((i>=ValSize())||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "StlEigenvector(i)"); - } - if (EigValI[i] == (ARFLOAT)0.0) { // Real eigenvector. - return GenComplex(n, &EigVec[i*n]); - } - else if (!ConjEigVec(i)) { // First eigenvector in a conjugate pair. - return GenComplex(n, &EigVec[i*n], &EigVec[(i+1)*n]); - } - else { // Second eigenvector in a conjugate pair. - return GenComplex(n, &EigVec[(i-1)*n], &EigVec[i*n], true); - } - -} // StlEigenvector(i). -#endif // ARCOMP_H. - - -template -inline vector* ARrcNonSymStdEig::StlEigenvectorReal(int i) -{ - - // Returning the real part of the i-th eigenvector in a STL vector. - - vector* Vec; - - if (!VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "StlEigenvectorReal(i)"); - } - else if ((i>=ValSize())||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "StlEigenvectorReal(i)"); - } - if (!ConjEigVec(i)) { // Real eigenvector or first in a conj. pair. - try { - Vec = new vector(&EigVec[i*n], &EigVec[(i+1)*n]); - } - catch (ArpackError) { return NULL; } - return Vec; - } - else { // Second eigenvector in a conjugate pair. - try { - Vec = new vector(&EigVec[(i-1)*n], &EigVec[i*n]); - } - catch (ArpackError) { return NULL; } - return Vec; - } - -} // StlEigenvectorReal(i). - - -template -inline vector* ARrcNonSymStdEig::StlEigenvectorImag(int i) -{ - - // Returning the imaginary part of the i-th eigenvector in a STL vector. - - vector* Vec; - - if (!VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "StlEigenvectorImag(i)"); - } - else if ((i>=ValSize())||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "StlEigenvectorImag(i)"); - } - if (EigValI[i] == (ARFLOAT)0.0) { // Real eigenvector. - try { - Vec = new vector(ValSize(), (ARFLOAT)0.0); - } - catch (ArpackError) { return NULL; } - return Vec; - } - else if (!ConjEigVec(i)) { // First eigenvector in a conjugate pair. - try { - Vec = new vector(&EigVec[(i+1)*n], &EigVec[(i+2)*n]); - } - catch (ArpackError) { return NULL; } - return Vec; - } - else { // Second eigenvector in a conjugate pair. - try { - Vec = new vector(&EigVec[i*n], &EigVec[(i+1)*n]); - } - catch (ArpackError) { return NULL; } - for (ARFLOAT* s = Vec->begin(); s != Vec->end(); s++) *s = -(*s); - return Vec; - } - -} // StlEigenvectorImag(i). - -#endif // STL_VECTOR_H. - - -template -inline ARrcNonSymStdEig:: -ARrcNonSymStdEig(int np, int nevp, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARrcNonSymStdEig:: -ARrcNonSymStdEig(int np, int nevp, ARFLOAT sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARrcNonSymStdEig& ARrcNonSymStdEig:: -operator=(const ARrcNonSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARRSNSYM_H - diff --git a/src/external/arpack++/include/arrssym.h b/src/external/arpack++/include/arrssym.h deleted file mode 100644 index 1a38fdd1..00000000 --- a/src/external/arpack++/include/arrssym.h +++ /dev/null @@ -1,424 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARRSSym.h. - Arpack++ class ARrcSymStdEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARRSSYM_H -#define ARRSSYM_H - -#include -#include -#include "arch.h" -#include "arerror.h" -#include "debug.h" -#include "arrseig.h" -#include "saupp.h" -#include "seupp.h" - - -template -class ARrcSymStdEig: public virtual ARrcStdEig { - - protected: - - // a) Protected functions: - - // a.1) Memory control functions. - - void WorkspaceAllocate(); - // Allocates workspace for symmetric problems. - - - // a.2) Functions that handle original FORTRAN ARPACK code. - - void Aupp(); - // Interface to FORTRAN subroutines SSAUPD and DSAUPD. - - void Eupp(); - // Interface to FORTRAN subroutines SSEUPD and DSEUPD. - - - // a.3) Functions that check user defined parameters. - - std::string CheckWhich(const std::string& whichp); - // Determines if the value of variable "which" is valid. - - - public: - - // b) Public functions: - - // b.1) Trace functions. - - void Trace(const int digit = -5, const int getv0 = 0, const int aupd = 1, - const int aup2 = 0, const int aitr = 0, const int eigt = 0, - const int apps = 0, const int gets = 0, const int eupd = 0) - { - sTraceOn(digit, getv0, aupd, aup2, aitr, eigt, apps, gets, eupd); - } - // Turns on trace mode. - - - // b.2) Functions that permit step by step execution of ARPACK. - - ARFLOAT* PutVector(); - // When ido = -1, 1 or 2 and the user must perform a product in the form - // y <- M*x, this function indicates where to store y. When ido = 3, this - // function indicates where to store the shifts. - - - // b.3) Functions that perform all calculations in one step. - - int FindSchurVectors() { - throw ArpackError(ArpackError::SCHUR_UNDEFINED, "FindSchurVectors"); - return 0; // Only to avoid warning messages emitted by some compilers. - } - // For symmetric problems, Schur vectors are eigenvectors. - - int Eigenvalues(ARFLOAT* &EigValp, bool ivec = false, bool ischur = false); - // Overrides array EigValp with the eigenvalues of the problem. - // Also calculates eigenvectors and Schur vectors if requested. - - int EigenValVectors(ARFLOAT* &EigVecp, ARFLOAT* &EigValp, - bool ischur = false); - // Overrides array EigVecp sequentially with the eigenvectors of the - // given eigen-problem. Also stores the eigenvalues in EigValp. - // Calculates Schur vectors if requested. - - - // b.4) Functions that return elements of vectors and matrices. - - ARFLOAT Eigenvalue(int i); - // Provides i-eth eigenvalue. - - ARFLOAT Eigenvector(int i, int j); - // Provides element j of the i-eth eigenvector. - - - // b.5) Functions that use STL vector class. - -#ifdef STL_VECTOR_H - - vector* StlEigenvalues(bool ivec = false, bool ischur = false); - // Calculates the eigenvalues and stores them in a single STL vector. - // Also calculates eigenvectors and Schur vectors if requested. - - vector* StlEigenvector(int i); - // Returns the i-th eigenvector in a STL vector. - -#endif // #ifdef STL_VECTOR_H. - - - // b.6) Constructors and destructor. - - ARrcSymStdEig() { } - // Short constructor. - - ARrcSymStdEig(int np, int nevp, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, ARFLOAT* residp = NULL, - bool ishiftp = true); - // Long constructor (regular mode). - - ARrcSymStdEig(int np, int nevp, ARFLOAT sigmap, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARrcSymStdEig(const ARrcSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARrcSymStdEig() { } - // Destructor. - - // c) Operators. - - ARrcSymStdEig& operator=(const ARrcSymStdEig& other); - // Assignment operator. - -}; // class ARrcSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARrcSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARrcSymStdEig::WorkspaceAllocate() -{ - - this->lworkl = this->ncv*(this->ncv+9); - this->lworkv = 0; - this->lrwork = 0; - this->workl = new ARFLOAT[this->lworkl+1]; - -} // WorkspaceAllocate. - - -template -inline void ARrcSymStdEig::Aupp() -{ - - saupp(this->ido, this->bmat, this->n, this->which, this->nev, this->tol, this->resid, this->ncv, this->V, this->n, - this->iparam, this->ipntr, this->workd, this->workl, this->lworkl, this->info); - -} // Aupp. - - -template -inline void ARrcSymStdEig::Eupp() -{ - - seupp(this->rvec, this->HowMny, this->EigValR, this->EigVec, this->n, this->sigmaR, this->bmat, - this->n, this->which, this->nev, this->tol, this->resid, this->ncv, this->V, this->n, this->iparam, - this->ipntr, this->workd, this->workl, this->lworkl, this->info); - -} // Eupp. - - -template -std::string ARrcSymStdEig::CheckWhich(const std::string& whichp) -{ - - switch (whichp[0]) { - case 'B': // The options are: BE, ... - return "BE"; - case 'L': // LA, LM, ... - case 'S': // SA, SM. - switch (whichp[1]){ - case 'A': - case 'M': - return whichp; - } - default: - throw ArpackError(ArpackError::WHICH_UNDEFINED); - } - -} // CheckWhich. - - -template -ARFLOAT* ARrcSymStdEig::PutVector() -{ - - switch (this->ido) { - case -1: - case 1: // Returning OP*x. - case 2: - return &this->workd[this->ipntr[2]]; // Returning B*x. - case 3: - return &this->workl[this->ipntr[11]]; // Returning shifts. - default: - throw ArpackError(ArpackError::CANNOT_PUT_VECTOR, "PutVector"); - } - -} // PutVector. - - -template -int ARrcSymStdEig:: -Eigenvalues(ARFLOAT* &EigValp, bool ivec, bool ischur) -{ - - if (this->ValuesOK) { // Eigenvalues are available. - if (EigValp == NULL) { // Moving eigenvalues. - EigValp = this->EigValR; - this->EigValR = NULL; - this->newVal = false; - this->ValuesOK = false; - } - else { // Copying eigenvalues. - copy(this->nconv,this->EigValR,1,EigValp,1); - } - } - else { // Eigenvalues are not available. - if (this->newVal) { - delete[] this->EigValR; - this->newVal = false; - } - if (EigValp == NULL) { - try { EigValp = new ARFLOAT[this->ValSize()]; } - catch (ArpackError) { return 0; } - } - this->EigValR = EigValp; - if (ivec) { // Finding eigenvalues and eigenvectors. - this->nconv = this->FindEigenvectors(ischur); - } - else { // Finding eigenvalues only. - this->nconv = this->FindEigenvalues(); - } - this->EigValR = NULL; - } - return this->nconv; - -} // Eigenvalues(EigValp, ivec, ischur). - - -template -int ARrcSymStdEig:: -EigenValVectors(ARFLOAT* &EigVecp, ARFLOAT* &EigValp, bool ischur) -{ - - if (this->ValuesOK) { // Eigenvalues are already available . - this->nconv = Eigenvalues(EigValp, false); - this->nconv = this->Eigenvectors(EigVecp, ischur); - } - else { // Eigenvalues and vectors are not available. - try { - if (EigVecp == NULL) EigVecp = new ARFLOAT[this->ValSize()*this->n]; - if (EigValp == NULL) EigValp = new ARFLOAT[this->ValSize()]; - } - catch (ArpackError) { return 0; } - if (this->newVec) { - delete[] this->EigVec; - this->newVec = false; - } - if (this->newVal) { - delete[] this->EigValR; - this->newVal = false; - } - this->EigVec = EigVecp; - this->EigValR = EigValp; - this->nconv = this->FindEigenvectors(ischur); - this->EigVec = NULL; - this->EigValR = NULL; - } - return this->nconv; - -} // EigenValVectors(EigVecp, EigValp, ischur). - - -template -inline ARFLOAT ARrcSymStdEig::Eigenvalue(int i) -{ - - // Returning i-eth eigenvalue. - - if (!this->ValuesOK) { - throw ArpackError(ArpackError::VALUES_NOT_OK, "Eigenvalue(i)"); - } - else if ((i>=this->nconv)||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "Eigenvalue(i)"); - } - return this->EigValR[i]; - -} // Eigenvalue(i). - - -template -inline ARFLOAT ARrcSymStdEig::Eigenvector(int i, int j) -{ - - // Returning element j of i-eth eigenvector. - - if (!this->VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "Eigenvector(i,j)"); - } - else if ((i>=this->nconv)||(i<0)||(j>=this->n)||(j<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "Eigenvector(i,j)"); - } - return this->EigVec[i*this->n+j]; - -} // Eigenvector(i,j). - - -#ifdef STL_VECTOR_H - -template -inline vector* ARrcSymStdEig:: -StlEigenvalues(bool ivec, bool ischur) -{ - - // Returning the eigenvalues in a STL vector. - - vector* StlEigValR; - ARFLOAT* ValPtr; - - try { StlEigValR = new vector(ValSize()); } - catch (ArpackError) { return NULL; } - ValPtr = StlEigValR->begin(); - nconv = Eigenvalues(ValPtr, ivec, ischur); - return StlEigValR; - -} // StlEigenvalues. - - -template -inline vector* ARrcSymStdEig::StlEigenvector(int i) -{ - - // Returning the i-th eigenvector in a STL vector. - - vector* Vec; - - if (!VectorsOK) { - throw ArpackError(ArpackError::VECTORS_NOT_OK, "StlEigenvector(i)"); - } - else if ((i>=ValSize())||(i<0)) { - throw ArpackError(ArpackError::RANGE_ERROR, "StlEigenvector(i)"); - } - try { - Vec = new vector(&EigVec[i*n], &EigVec[(i+1)*n]); - } - catch (ArpackError) { return NULL; } - return Vec; - -} // StlEigenvector(i). - -#endif // #ifdef STL_VECTOR_H. - - -template -inline ARrcSymStdEig:: -ARrcSymStdEig(int np, int nevp, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARrcSymStdEig:: -ARrcSymStdEig(int np, int nevp, ARFLOAT sigmap, const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, ARFLOAT* residp, - bool ishiftp) - -{ - - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARrcSymStdEig& ARrcSymStdEig:: -operator=(const ARrcSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARRSSYM_H - diff --git a/src/external/arpack++/include/arscomp.h b/src/external/arpack++/include/arscomp.h deleted file mode 100644 index 709833d7..00000000 --- a/src/external/arpack++/include/arscomp.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARSComp.h. - Arpack++ class ARCompStdEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARSCOMP_H -#define ARSCOMP_H - -#include - -#include "arch.h" -#include "arseig.h" -#include "arrscomp.h" - -template -class ARCompStdEig: - virtual public ARStdEig, ARFOP>, - virtual public ARrcCompStdEig { - - public: - - // a) Constructors and destructor. - - ARCompStdEig() { } - // Short constructor. - - ARCompStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(arcomplex[],arcomplex[]), - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARCompStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(arcomplex[],arcomplex[]), - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARCompStdEig(const ARCompStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARCompStdEig() { } - // Destructor. - - // b) Operators. - - ARCompStdEig& operator=(const ARCompStdEig& other); - // Assignment operator. - -}; // class ARCompStdEig. - - -// ------------------------------------------------------------------------ // -// ARCompStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline ARCompStdEig:: -ARCompStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(arcomplex[],arcomplex[]), - const std::string& whichp, int ncvp, ARFLOAT tolp, int maxitp, - arcomplex* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(np, nevp, objOPp, MultOPxp, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARCompStdEig:: -ARCompStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(arcomplex[],arcomplex[]), - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, objOPp, MultOPxp, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARCompStdEig& ARCompStdEig:: -operator=(const ARCompStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARSCOMP_H diff --git a/src/external/arpack++/include/arseig.h b/src/external/arpack++/include/arseig.h deleted file mode 100644 index edac9661..00000000 --- a/src/external/arpack++/include/arseig.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARSEig.h. - Arpack++ class ARStdEig definition. - This class is the base class for all - standard and generalized problem templates. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARSEIG_H -#define ARSEIG_H - -#include -#include "arch.h" -#include "arerror.h" -#include "arrseig.h" - -// ARStdEig class definition. - -template -class ARStdEig: virtual public ARrcStdEig { - - public: - - // a) Notation. - - typedef void (ARFOP::* TypeOPx)(ARTYPE[], ARTYPE[]); - - - protected: - - // b) User defined parameters. - - ARFOP *objOP; // Object that has MultOPx as a member function. - TypeOPx MultOPx; // Function that evaluates the product OP*x. - - // c) Protected functions. - - virtual void Copy(const ARStdEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // d) Public functions: - - // d.1) Function that stores user defined parameters. - - virtual void DefineParameters(int np, int nevp, ARFOP* objOPp, - TypeOPx MultOPxp, const std::string& whichp="LM", - int ncvp=0, ARFLOAT tolp=0.0, int maxitp=0, - ARTYPE* residp=NULL, bool ishiftp=true); - // Set values of problem parameters (also called by constructors). - // Redefined in ARGenEigenProblem. - - // d.2) Function that allow changes in problem parameters. - - void ChangeMultOPx(ARFOP* objOPp, TypeOPx MultOPxp); - // Changes the matrix-vector function that performs OP*x. - - virtual void SetRegularMode(ARFOP* objOPp, TypeOPx MultOPxp); - // Turns problem to regular mode. - - virtual void SetShiftInvertMode(ARTYPE sigmap, ARFOP* objOPp, - TypeOPx MultOPxp); - // Turns problem to shift and invert mode with shift defined by sigmap. - - // d.3) Function that permits step by step execution of ARPACK. - - virtual void Iterate() { - throw ArpackError(ArpackError::NOT_IMPLEMENTED, "Iterate"); - } - // Takes one iteration of IRA method. - - - // d.4) Function that performs all calculations in one step. - - virtual int FindArnoldiBasis(); - // Determines the Arnoldi basis related to the given problem. - // Redefined in ARGenEigenProblem and ARSymGenEigenProblem. - - - // d.5) Constructor and destructor. - - ARStdEig() { } - // Constructor that does nothing but calling base class constructor. - - ARStdEig(const ARStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARStdEig() { } - // Very simple destructor. - - // e) Operators. - - ARStdEig& operator=(const ARStdEig& other); - // Assignment operator. - -}; // class ARStdEig. - - -// ------------------------------------------------------------------------ // -// ARStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARStdEig:: -Copy(const ARStdEig& other) -{ - - ARrcStdEig::Copy(other); - objOP = other.objOP; - MultOPx = other.MultOPx; - -} // Copy. - - -template -void ARStdEig:: -DefineParameters(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARTYPE[], ARTYPE[]), const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, ARTYPE* residp, - bool ishiftp) - - -{ - - ARrcStdEig::DefineParameters(np, nevp, whichp, ncvp, tolp, - maxitp, residp, ishiftp); - objOP = objOPp; - MultOPx = MultOPxp; - -} // DefineParameters. - - -template -inline void ARStdEig:: -ChangeMultOPx(ARFOP* objOPp, void (ARFOP::* MultOPxp)(ARTYPE[], ARTYPE[])) -{ - - objOP = objOPp; - MultOPx = MultOPxp; - this->Restart(); - -} // ChangeMultOPx. - - -template -inline void ARStdEig:: -SetRegularMode(ARFOP* objOPp, void (ARFOP::* MultOPxp)(ARTYPE[], ARTYPE[])) -{ - - ChangeMultOPx(objOPp, MultOPxp); - this->NoShift(); - -} // SetRegularMode. - - -template -inline void ARStdEig:: -SetShiftInvertMode(ARTYPE sigmap, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARTYPE[], ARTYPE[])) -{ - - ChangeMultOPx(objOPp, MultOPxp); - this->ChangeShift(sigmap); - -} // SetShiftInvertMode. - - -template -int ARStdEig::FindArnoldiBasis() -{ - - if (!this->BasisOK) this->Restart(); - - // Changing to auto shift mode. - - if (!this->AutoShift) { - ArpackError::Set(ArpackError::CHANGING_AUTOSHIFT, "FindArnoldiBasis"); - this->AutoShift=true; - } - - // ARPACK main loop. - - while (!this->BasisOK) { - - // Calling Aupp. - - try { this->TakeStep(); } - catch (ArpackError) { - ArpackError(ArpackError::CANNOT_FIND_BASIS, "FindArnoldiBasis"); - return 0; - } - - if ((this->ido == -1) || (this->ido == 1)) { - - // Performing Matrix vector multiplication: y <- OP*x. - - (objOP->*MultOPx)(&this->workd[this->ipntr[1]],&this->workd[this->ipntr[2]]); - - } - - } - return this->nconv; - -} // FindArnoldiBasis. - - -template -ARStdEig& ARStdEig:: -operator=(const ARStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARSEIG_H - diff --git a/src/external/arpack++/include/arsnsym.h b/src/external/arpack++/include/arsnsym.h deleted file mode 100644 index 4bb9d512..00000000 --- a/src/external/arpack++/include/arsnsym.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARSNSym.h. - Arpack++ class ARNonSymStdEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARSNSYM_H -#define ARSNSYM_H - -#include -#include -#include "arch.h" -#include "arseig.h" -#include "arrsnsym.h" - - -template -class ARNonSymStdEig: - public virtual ARStdEig, - public virtual ARrcNonSymStdEig { - - public: - - // a) Constructors and destructor. - - ARNonSymStdEig() { } - // Short constructor. - - ARNonSymStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARNonSymStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, ARFLOAT* residp = NULL, - bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARNonSymStdEig(const ARNonSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARNonSymStdEig() { } - // Destructor. - - // b) Operators. - - ARNonSymStdEig& operator=(const ARNonSymStdEig& other); - // Assignment operator. - -}; // class ARNonSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARNonSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline ARNonSymStdEig:: -ARNonSymStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - const std::string& whichp, int ncvp, ARFLOAT tolp, int maxitp, - ARFLOAT* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(np, nevp, objOPp, MultOPxp, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARNonSymStdEig:: -ARNonSymStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, objOPp, MultOPxp, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARNonSymStdEig& ARNonSymStdEig:: -operator=(const ARNonSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARSNSYM_H diff --git a/src/external/arpack++/include/arssym.h b/src/external/arpack++/include/arssym.h deleted file mode 100644 index 41c46e8c..00000000 --- a/src/external/arpack++/include/arssym.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARSSym.h. - Arpack++ class ARSymStdEig definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARSSYM_H -#define ARSSYM_H - -#include -#include -#include "arch.h" -#include "arseig.h" -#include "arrssym.h" - - -template -class ARSymStdEig: - public virtual ARStdEig, - public virtual ARrcSymStdEig { - - public: - - // a) Constructors and destructor. - - ARSymStdEig() { } - // Short constructor. - - ARSymStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - const std::string& whichp = "LM", int ncvp = 0, ARFLOAT tolp = 0.0, - int maxitp = 0, ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARSymStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigmap, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, ARFLOAT* residp = NULL, - bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARSymStdEig(const ARSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARSymStdEig() { } - // Destructor. - - // b) Operators. - - ARSymStdEig& operator=(const ARSymStdEig& other); - // Assignment operator. - -}; // class ARSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline ARSymStdEig:: -ARSymStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->NoShift(); - this->DefineParameters(np, nevp, objOPp, MultOPxp, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARSymStdEig:: -ARSymStdEig(int np, int nevp, ARFOP* objOPp, - void (ARFOP::* MultOPxp)(ARFLOAT[], ARFLOAT[]), - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->ChangeShift(sigmap); - this->DefineParameters(np, nevp, objOPp, MultOPxp, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (shift and invert mode). - - -template -ARSymStdEig& ARSymStdEig:: -operator=(const ARSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARSSYM_H - diff --git a/src/external/arpack++/include/arugcomp.h b/src/external/arpack++/include/arugcomp.h deleted file mode 100644 index 7f34ca0d..00000000 --- a/src/external/arpack++/include/arugcomp.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUGComp.h. - Arpack++ class ARluCompGenEig definition - (umfpack version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARUGCOMP_H -#define ARUGCOMP_H - -#include -#include -#include "arch.h" -#include "arunsmat.h" -#include "arunspen.h" -#include "arrseig.h" -#include "argcomp.h" - - -template -class ARluCompGenEig: - public virtual - ARCompGenEig, ARFLOAT >, - ARumNonSymPencil, ARFLOAT > > { - - private: - - // a) Data structure used to store matrices. - - ARumNonSymPencil, ARFLOAT > Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluCompGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(arcomplex sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(arcomplex sigmap); - - // c.2) Constructors and destructor. - - ARluCompGenEig() { } - // Short constructor. - - ARluCompGenEig(int nevp, ARumNonSymMatrix, ARFLOAT>& A, - ARumNonSymMatrix, ARFLOAT>& B, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluCompGenEig(int nevp, ARumNonSymMatrix, ARFLOAT>& A, - ARumNonSymMatrix, ARFLOAT>& B, - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluCompGenEig(const ARluCompGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluCompGenEig() { } - - // d) Operators. - - ARluCompGenEig& operator=(const ARluCompGenEig& other); - // Assignment operator. - -}; // class ARluCompGenEig. - - -// ------------------------------------------------------------------------ // -// ARluCompGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluCompGenEig:: -Copy(const ARluCompGenEig& other) -{ - - ARCompGenEig, ARFLOAT >, - ARumNonSymPencil, ARFLOAT> >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - -} // Copy. - - -template -inline void ARluCompGenEig:: -ChangeShift(arcomplex sigmaRp) -{ - - this->objOP->FactorAsB(sigmaRp); - ARrcStdEig >::ChangeShift(sigmaRp); - -} // ChangeShift. - - -template -inline void ARluCompGenEig::SetRegularMode() -{ - - ARStdEig, - ARumNonSymPencil, ARFLOAT> >:: - SetRegularMode(&Pencil, - &ARumNonSymPencil, ARFLOAT>::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluCompGenEig:: -SetShiftInvertMode(arcomplex sigmap) -{ - - ARCompGenEig, ARFLOAT>, - ARumNonSymPencil, ARFLOAT> >:: - SetShiftInvertMode(sigmap, &Pencil, - &ARumNonSymPencil,ARFLOAT>::MultInvAsBv); - -} // SetShiftInvertMode. - - -template -inline ARluCompGenEig:: -ARluCompGenEig(int nevp, ARumNonSymMatrix, ARFLOAT>& A, - ARumNonSymMatrix, ARFLOAT>& B, const std::string& whichp, - int ncvp, ARFLOAT tolp, int maxitp, - arcomplex* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->NoShift(); - DefineParameters(A.ncols(), nevp, &Pencil, - &ARumNonSymPencil, ARFLOAT>::MultInvBAv, - &Pencil, - &ARumNonSymPencil, ARFLOAT>::MultBv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluCompGenEig:: -ARluCompGenEig(int nevp, ARumNonSymMatrix, ARFLOAT>& A, - ARumNonSymMatrix, ARFLOAT>& B, - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - DefineParameters(A.ncols(), nevp, &Pencil, - &ARumNonSymPencil, ARFLOAT>::MultInvAsBv, - &Pencil, - &ARumNonSymPencil, ARFLOAT>::MultBv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - SetShiftInvertMode(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluCompGenEig& ARluCompGenEig:: -operator=(const ARluCompGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUGCOMP_H diff --git a/src/external/arpack++/include/arugnsym.h b/src/external/arpack++/include/arugnsym.h deleted file mode 100644 index 3061e68b..00000000 --- a/src/external/arpack++/include/arugnsym.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUGNSym.h. - Arpack++ class ARluNonSymGenEig definition - (umfpack version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARUGNSYM_H -#define ARUGNSYM_H - -#include -#include -#include "arch.h" -#include "arunsmat.h" -#include "arunspen.h" -#include "argnsym.h" - - -template -class ARluNonSymGenEig: - public virtual ARNonSymGenEig, - ARumNonSymPencil > { - - protected: - - // a) Data structure used to store matrices. - - ARumNonSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluNonSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp = 0.0); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetComplexShiftMode(char partp, ARFLOAT sigmaRp, - ARFLOAT sigmaIp); - - // c.2) Constructors and destructor. - - ARluNonSymGenEig() { } - // Short constructor. - - ARluNonSymGenEig(int nevp, ARumNonSymMatrix& A, - ARumNonSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluNonSymGenEig(int nevp, ARumNonSymMatrix& A, - ARumNonSymMatrix& B, ARFLOAT sigma, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (real shift and invert mode). - - ARluNonSymGenEig(int nevp, ARumNonSymMatrix& A, - ARumNonSymMatrix& B, char partp, - ARFLOAT sigmaRp, ARFLOAT sigmaIp, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (complex shift and invert mode). - - ARluNonSymGenEig(const ARluNonSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluNonSymGenEig& operator=(const ARluNonSymGenEig& other); - // Assignment operator. - -}; // class ARluNonSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluNonSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymGenEig:: -Copy(const ARluNonSymGenEig& other) -{ - - ARNonSymGenEig, - ARumNonSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - -} // Copy. - - -template -inline void ARluNonSymGenEig:: -ChangeShift(ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - if (sigmaIp == 0.0) { - this->objOP->FactorAsB(sigmaRp); - } - else { - this->objOP->FactorAsB(sigmaRp, sigmaIp, this->part); - } - ARrcNonSymGenEig::ChangeShift(sigmaRp, sigmaIp); - -} // ChangeShift. - - -template -inline void ARluNonSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARumNonSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluNonSymGenEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARNonSymGenEig, - ARumNonSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, - &ARumNonSymPencil::MultInvAsBv); - -} // SetShiftInvertMode. - - -template -inline void ARluNonSymGenEig:: -SetComplexShiftMode(char partp, ARFLOAT sigmaRp, ARFLOAT sigmaIp) -{ - - ARNonSymGenEig, - ARumNonSymPencil >:: - SetComplexShiftMode(partp, sigmaRp, sigmaIp, &Pencil, - &ARumNonSymPencil::MultInvAsBv, - &Pencil, &ARumNonSymPencil::MultAv); - -} // SetComplexShiftMode. - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARumNonSymMatrix& A, - ARumNonSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->NoShift(); - DefineParameters(A.ncols(), nevp, &Pencil, - &ARumNonSymPencil::MultInvBAv, &Pencil, - &ARumNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARumNonSymMatrix& A, - ARumNonSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - DefineParameters(A.ncols(), nevp, &Pencil, - &ARumNonSymPencil::MultInvAsBv, &Pencil, - &ARumNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - SetShiftInvertMode(sigmap); - -} // Long constructor (real shift and invert mode). - - -template -inline ARluNonSymGenEig:: -ARluNonSymGenEig(int nevp, ARumNonSymMatrix& A, - ARumNonSymMatrix& B, - char partp, ARFLOAT sigmaRp, - ARFLOAT sigmaIp, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - DefineParameters(A.ncols(), nevp, &Pencil, - &ARumNonSymPencil::MultInvAsBv, &Pencil, - &ARumNonSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - SetComplexShiftMode(partp, sigmaRp, sigmaIp); - -} // Long constructor (complex shift and invert mode). - - -template -ARluNonSymGenEig& ARluNonSymGenEig:: -operator=(const ARluNonSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUGNSYM_H diff --git a/src/external/arpack++/include/arugsym.h b/src/external/arpack++/include/arugsym.h deleted file mode 100644 index d57dc451..00000000 --- a/src/external/arpack++/include/arugsym.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUGSym.h. - Arpack++ class ARluSymGenEig definition - (UMFPACK version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Kristi Maschhoff - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARUGSYM_H -#define ARUGSYM_H - -#include -#include -#include "arch.h" -#include "arusmat.h" -#include "aruspen.h" -#include "argsym.h" - - -template -class ARluSymGenEig: - public virtual ARSymGenEig, - ARumSymPencil > { - - private: - - // a) Data structure used to store matrices. - - ARumSymPencil Pencil; - - // b) Protected functions: - - virtual void Copy(const ARluSymGenEig& other); - // Makes a deep copy of "other" over "this" object. - // Old values are not deleted (this function is to be used - // by the copy constructor and the assignment operator only). - - - public: - - // c) Public functions: - - // c.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmap); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - virtual void SetBucklingMode(ARFLOAT sigmap); - - virtual void SetCayleyMode(ARFLOAT sigmap); - - // c.2) Constructors and destructor. - - ARluSymGenEig() { } - // Short constructor. - - ARluSymGenEig(int nevp, ARumSymMatrix& A, - ARumSymMatrix& B, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymGenEig(char InvertModep, int nevp, ARumSymMatrix& A, - ARumSymMatrix& B, ARFLOAT sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert, buckling and Cayley modes). - - ARluSymGenEig(const ARluSymGenEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymGenEig() { } - // Destructor. - - // d) Operators. - - ARluSymGenEig& operator=(const ARluSymGenEig& other); - // Assignment operator. - -}; // class ARluSymGenEig. - - -// ------------------------------------------------------------------------ // -// ARluSymGenEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymGenEig:: -Copy(const ARluSymGenEig& other) -{ - - ARSymGenEig, - ARumSymPencil >:: Copy(other); - Pencil = other.Pencil; - this->objOP = &Pencil; - this->objB = &Pencil; - this->objA = &Pencil; - -} // Copy. - - -template -inline void ARluSymGenEig::ChangeShift(ARFLOAT sigmap) -{ - - this->objOP->FactorAsB(sigmap); - ARrcSymGenEig::ChangeShift(sigmap); - -} // ChangeShift. - - -template -inline void ARluSymGenEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(&Pencil, &ARumSymPencil::MultInvBAv); - -} // SetRegularMode. - - -template -inline void ARluSymGenEig:: -SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARumSymPencil >:: - SetShiftInvertMode(sigmap, &Pencil, &ARumSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARumSymPencil::MultBv); - -} // SetShiftInvertMode. - - -template -inline void ARluSymGenEig:: -SetBucklingMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARumSymPencil >:: - SetBucklingMode(sigmap, &Pencil, &ARumSymPencil::MultInvAsBv); - this->ChangeMultBx(&Pencil, &ARumSymPencil::MultAv); - -} // SetBucklingMode. - - -template -inline void ARluSymGenEig:: -SetCayleyMode(ARFLOAT sigmap) -{ - - ARSymGenEig, ARumSymPencil >:: - SetCayleyMode(sigmap, &Pencil, &ARumSymPencil::MultInvAsBv, - &Pencil, &ARumSymPencil::MultAv); - this->ChangeMultBx(&Pencil, &ARumSymPencil::MultBv); - -} // SetCayleyMode. - - -template -inline ARluSymGenEig:: -ARluSymGenEig(int nevp, ARumSymMatrix& A, - ARumSymMatrix& B, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->InvertMode = 'S'; - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARumSymPencil::MultInvBAv, &Pencil, - &ARumSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymGenEig:: -ARluSymGenEig(char InvertModep, int nevp, ARumSymMatrix& A, - ARumSymMatrix& B, ARFLOAT sigmap, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - Pencil.DefineMatrices(A, B); - this->DefineParameters(A.ncols(), nevp, &Pencil, - &ARumSymPencil::MultInvAsBv, &Pencil, - &ARumSymPencil::MultBv, whichp, - ncvp, tolp, maxitp, residp, ishiftp); - this->InvertMode = this->CheckInvertMode(InvertModep); - switch (this->InvertMode) { - case 'B': // Buckling mode. - this->ChangeMultBx(&Pencil, &ARumSymPencil::MultAv); - case 'S': // Shift and invert mode. - this->ChangeShift(sigmap); - break; - case 'C': // Cayley mode. - this->SetCayleyMode(sigmap); - } - -} // Long constructor (shift and invert, buckling and Cayley modes). - - -template -ARluSymGenEig& ARluSymGenEig:: -operator=(const ARluSymGenEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUGSYM_H diff --git a/src/external/arpack++/include/arunsmat.h b/src/external/arpack++/include/arunsmat.h deleted file mode 100644 index 801b02e3..00000000 --- a/src/external/arpack++/include/arunsmat.h +++ /dev/null @@ -1,646 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUNSMat.h. - Arpack++ class ARumNonSymMatrix definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "arunspen.h" - -#ifndef ARUNSMAT_H -#define ARUNSMAT_H - -#include -#include -#include "arch.h" -#include "armat.h" -#include "arhbmat.h" -#include "arerror.h" -#include "blas1c.h" -#include "umfpackc.h" - -template class ARumNonSymPencil; - -template -class ARumNonSymMatrix: public ARMatrix { - - friend class ARumNonSymPencil; - friend class ARumNonSymPencil; - - protected: - - bool factored; - int fillin; - int nnz; - int lvalue; - int lindex; - int keep[20]; - int icntl[20]; - int info[40]; - int* irow; - int* pcol; - int* index; - double threshold; - ARTYPE cntl[10]; - ARTYPE rinfo[20]; - ARTYPE* a; - ARTYPE* value; - ARhbMatrix mat; - - bool DataOK(); - - void ClearMem(); - - virtual void Copy(const ARumNonSymMatrix& other); - - void SubtractAsI(ARTYPE sigma); - - void CreateStructure(); - - void ThrowError(); - - public: - - int nzeros() { return nnz; } - - int FillFact() { return fillin; } - - bool IsSymmetric() { return bool(icntl[5]); } - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultMtv(ARTYPE* v, ARTYPE* w); - - void MultMtMv(ARTYPE* v, ARTYPE* w); - - void MultMMtv(ARTYPE* v, ARTYPE* w); - - void Mult0MMt0v(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, double thresholdp = 0.1, - int fillinp = 9, bool simest = false, - bool reducible = true, bool check = true); // Square. - - void DefineMatrix(int mp, int np, int nnzp, ARTYPE* ap, - int* irowp, int* pcolp); // Rectangular. - - ARumNonSymMatrix(): ARMatrix() { factored = false; } - // Short constructor that does nothing. - - ARumNonSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, double thresholdp = 0.1, - int fillinp = 9, bool simest = false, - bool reducible = true, bool check = true); - // Long constructor (square matrix). - - ARumNonSymMatrix(int mp, int np, int nnzp, ARTYPE* ap, - int* irowp, int* pcolp); - // Long constructor (rectangular matrix). - - ARumNonSymMatrix(const std::string& name, double thresholdp = 0.1, - int fillinp = 9, bool simest = false, - bool reducible = true, bool check = true); - // Long constructor (Harwell-Boeing file). - - ARumNonSymMatrix(const ARumNonSymMatrix& other) { Copy(other); } - // Copy constructor. - - virtual ~ARumNonSymMatrix() { ClearMem(); } - // Destructor. - - ARumNonSymMatrix& operator=(const ARumNonSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARumNonSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -bool ARumNonSymMatrix::DataOK() -{ - - int i, j, k; - - // Checking if pcol is in ascending order. - - i = 0; - while ((i!=this->n)&&(pcol[i]<=pcol[i+1])) i++; - if (i!=this->n) return false; - - // Checking if irow components are in order and within bounds. - - for (i=0; i!=this->n; i++) { - j = pcol[i]; - k = pcol[i+1]-1; - if (j<=k) { - if ((irow[j]<0)||(irow[k]>=this->n)) return false; - while ((j!=k)&&(irow[j] -inline void ARumNonSymMatrix::ClearMem() -{ - - if (factored) { - delete[] value; - delete[] index; - value = NULL; - index = NULL; - } - -} // ClearMem. - - -template -inline void ARumNonSymMatrix:: -Copy(const ARumNonSymMatrix& other) -{ - - // Local variable. - - int i; - - // Copying very fundamental variables and user-defined parameters. - - this->m = other.m; - this->n = other.n; - this->defined = other.defined; - factored = other.factored; - fillin = other.fillin; - nnz = other.nnz; - lvalue = other.lvalue; - lindex = other.lindex; - irow = other.irow; - pcol = other.pcol; - a = other.a; - threshold = other.threshold; - - // Returning from here if "other" was not initialized. - - if (!this->defined) return; - - // Copying arrays with static dimension. - - for (i=0; i<20; i++) keep[i] = other.keep[i]; - for (i=0; i<20; i++) icntl[i] = other.icntl[i]; - for (i=0; i<40; i++) info[i] = other.info[i]; - for (i=0; i<10; i++) cntl[i] = other.cntl[i]; - for (i=0; i<20; i++) rinfo[i] = other.rinfo[i]; - - // Returning from here if "other" was not factored. - - if (!factored) return; - - value = new ARTYPE[lvalue]; - index = new int[lindex]; - - for (i=0; i -void ARumNonSymMatrix::SubtractAsI(ARTYPE sigma) -{ - - int i, j, k, ki, end; - - // Subtracting sigma from diagonal elements. - - k = 0; - ki = this->n+1; - index[0] = 1; - - for (i=0; i!=this->n; i++) { - - j = pcol[i]; - end = pcol[i+1]; - - // Copying superdiagonal elements of column i. - - while ((irow[j] < i)&&(j < end)) { - value[k++] = a[j]; - index[ki++] = irow[j++]+1; - } - - // Verifying if A(i,i) exists. - - if ((irow[j] == i)&&(j < end)) { // A(i,i) exists, subtracting sigma. - value[k++] = a[j++] - sigma; - } - else { // A(i,i) does not exist. - value[k++] = -sigma; - } - index[ki++] = i+1; - - // Copying subdiagonal elements of column i. - - while (j < end ) { - value[k++] = a[j]; - index[ki++] = irow[j++]+1; - } - - index[i+1] = k+1; - - } - -} // SubtractAsI. - - -template -inline void ARumNonSymMatrix::CreateStructure() -{ - - int dimfact = (((fillin+1)*nnz)<(this->n*this->n)) ? (fillin+1)*nnz : this->n*this->n; - - this->ClearMem(); - - lindex = 30*this->n+dimfact; // ????? - lvalue = dimfact; - - value = new ARTYPE[lvalue]; - index = new int[lindex]; - -} // CreateStructure. - - -template -inline void ARumNonSymMatrix::ThrowError() -{ - - if (info[0] < -2) { // Memory is not suficient. - throw ArpackError(ArpackError::INSUFICIENT_MEMORY, - "ARumNonSymMatrix::FactorA"); - } - else if (info[0] > 3) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARumNonSymMatrix::FactorA"); - } - else if (info[0] != 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARumNonSymMatrix::FactorA"); - } - -} // ThrowError. - - -template -void ARumNonSymMatrix::FactorA() -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED,"ARumNonSymMatrix::FactorA"); - } - - // Quitting the function if A is not square. - - if (this->m != this->n) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARumNonSymMatrix::FactorA"); - } - - // Defining local variables. - - int i; - int *pi, *pj; - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Copying A to (value, index); - - copy(nnz, a, 1, value, 1); - pi=pcol; - pj=index; - for (i=0; i<=this->n; i++) *pj++ = (*pi++)+1; - pi=irow; - for (i=0; in, nnz, 0, false, lvalue, lindex, value, - index, keep, cntl, icntl, info, rinfo); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorA. - - -template -void ARumNonSymMatrix::FactorAsI(ARTYPE sigma) -{ - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARumNonSymMatrix::FactorAsI"); - } - - // Quitting the function if A is not square. - - if (this->m != this->n) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARumNonSymMatrix::FactorAsI"); - } - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Subtracting sigma*I from A. - - SubtractAsI(sigma); - - // Decomposing AsI. - - um2fa(this->n, nnz, 0, false, lvalue, lindex, value, - index, keep, cntl, icntl, info, rinfo); - - // Handling errors. - - ThrowError(); - - factored = true; - -} // FactorAsI. - - -template -void ARumNonSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ - - int i,j; - ARTYPE t; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARumNonSymMatrix::MultMv"); - } - - // Determining w = M.v. - - for (i=0; i!=this->m; i++) w[i]=(ARTYPE)0; - - for (i=0; i!=this->n; i++) { - t = v[i]; - for (j=pcol[i]; j!=pcol[i+1]; j++) { - w[irow[j]] += t*a[j]; - } - } - -} // MultMv. - - -template -void ARumNonSymMatrix::MultMtv(ARTYPE* v, ARTYPE* w) -{ - - int i,j; - ARTYPE t; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED,"ARumNonSymMatrix::MultMtv"); - } - - // Determining w = M'.v. - - for (i=0; i!=this->n; i++) { - t = (ARTYPE)0; - for (j=pcol[i]; j!=pcol[i+1]; j++) { - t += v[irow[j]]*a[j]; - } - w[i] = t; - } - -} // MultMtv. - - -template -void ARumNonSymMatrix::MultMtMv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE* t = new ARTYPE[this->m]; - - MultMv(v,t); - MultMtv(t,w); - - delete[] t; - -} // MultMtMv. - - -template -void ARumNonSymMatrix::MultMMtv(ARTYPE* v, ARTYPE* w) -{ - - ARTYPE* t = new ARTYPE[this->n]; - - MultMtv(v,t); - MultMv(t,w); - - delete[] t; - -} // MultMMtv. - - -template -void ARumNonSymMatrix::Mult0MMt0v(ARTYPE* v, ARTYPE* w) -{ - - MultMv(&v[this->m],w); - MultMtv(v,&w[this->m]); - -} // Mult0MMt0v. - - -template -void ARumNonSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARumNonSymMatrix::MultInvv"); - } - - // Solving A.w = v (or AsI.w = v). - - ARTYPE* space = new ARTYPE[2*this->n]; - - um2so(this->n, 0, false, lvalue, lindex, value, index, - keep, v, w, space, cntl, icntl, info, rinfo); - - delete[] space; - -} // MultInvv. - - -template -inline void ARumNonSymMatrix:: -DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, double thresholdp, int fillinp, - bool simest, bool reducible, bool check) -{ - - // Defining member variables. - - this->m = np; - this->n = np; - nnz = nnzp; - a = ap; - irow = irowp; - pcol = pcolp; - pcol[this->n] = nnz; - fillin = (fillinp>2) ? fillinp : 2; - threshold = thresholdp; - value = NULL; - index = NULL; - - // Preparing umfpack. - - um21i(keep, cntl, icntl, threshold, simest, reducible); - - // Checking data. - - if ((check)&&(!DataOK())) { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARumNonSymMatrix::DefineMatrix"); - } - else { - this->defined = true; - } - -} // DefineMatrix (square). - - -template -inline void ARumNonSymMatrix:: -DefineMatrix(int mp, int np, int nnzp, ARTYPE* ap, int* irowp, int* pcolp) -{ - - // Defining member variables. - - this->m = mp; - this->n = np; - nnz = nnzp; - a = ap; - irow = irowp; - pcol = pcolp; - pcol[this->n] = nnz; - fillin = 0; - this->defined = true; - -} // DefineMatrix (rectangular). - - -template -inline ARumNonSymMatrix:: -ARumNonSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, double thresholdp, int fillinp, - bool simest, bool reducible, bool check): ARMatrix(np) -{ - - factored = false; - DefineMatrix(np, nnzp, ap, irowp, pcolp, thresholdp, - fillinp, simest, reducible, check); - -} // Long constructor (square matrix). - - -template -inline ARumNonSymMatrix:: -ARumNonSymMatrix(int mp, int np, int nnzp, ARTYPE* ap, - int* irowp, int* pcolp) : ARMatrix(mp, np) -{ - - factored = false; - DefineMatrix(mp, np, nnzp, ap, irowp, pcolp); - -} // Long constructor (rectangular matrix). - - -template -ARumNonSymMatrix:: -ARumNonSymMatrix(const std::string& name, double thresholdp, int fillinp, - bool simest, bool reducible, bool check) -{ - - factored = false; - - try { - mat.Define(name); - } - catch (ArpackError) { // Returning from here if an error has occurred. - throw ArpackError(ArpackError::CANNOT_READ_FILE, "ARumNonSymMatrix"); - } - - if (mat.NCols()==mat.NRows()) { - DefineMatrix(mat.NCols(), mat.NonZeros(), (ARTYPE*)mat.Entries(), - mat.RowInd(), mat.ColPtr(), thresholdp, - fillinp, simest, reducible, check); - } - else { - DefineMatrix(mat.NRows(), mat.NCols(), mat.NonZeros(), - (ARTYPE*)mat.Entries(), mat.RowInd(), mat.ColPtr()); - } - -} // Long constructor (Harwell-Boeing file). - - -template -ARumNonSymMatrix& ARumNonSymMatrix:: -operator=(const ARumNonSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUNSMAT_H diff --git a/src/external/arpack++/include/arunspen.h b/src/external/arpack++/include/arunspen.h deleted file mode 100644 index 771b3746..00000000 --- a/src/external/arpack++/include/arunspen.h +++ /dev/null @@ -1,527 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUNSPen.h. - Arpack++ class ARumNonSymPencil definition. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARUNSPEN_H -#define ARUNSPEN_H - -#include "arch.h" -#include "arerror.h" -#include "blas1c.h" -#include "umfpackc.h" -#include "arunsmat.h" - - -template -class ARumNonSymPencil -{ - - protected: - - char part; - ARumNonSymMatrix* A; - ARumNonSymMatrix* B; - ARumNonSymMatrix AsB; -#ifdef ARCOMP_H - ARumNonSymMatrix, ARFLOAT> AsBc; -#endif - - virtual void Copy(const ARumNonSymPencil& other); - - void SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], - int yind[], int ny, ARTYPE z[], int zind[], int& nz); - -#ifdef ARCOMP_H - void SparseSaxpy(arcomplex a, ARFLOAT x[], int xind[], int nx, - ARFLOAT y[], int yind[], int ny, - arcomplex z[], int zind[], int& nz); -#endif - - void SubtractAsB(ARTYPE sigma); - -#ifdef ARCOMP_H - void SubtractAsB(ARFLOAT sigmaR, ARFLOAT sigmaI); -#endif - - public: - -#ifdef ARCOMP_H - bool IsFactored() { return (AsB.IsFactored()||AsBc.IsFactored()); } -#else - bool IsFactored() { return AsB.IsFactored(); } -#endif - - bool IsSymmetric() { return AsB.IsSymmetric(); } - - void FactorAsB(ARTYPE sigma); - -#ifdef ARCOMP_H - void FactorAsB(ARFLOAT sigmaR, ARFLOAT sigmaI, char partp = 'R'); -#endif - - void MultAv(ARTYPE* v, ARTYPE* w) { A->MultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - -#ifdef ARCOMP_H - void MultInvAsBv(arcomplex* v, arcomplex* w); -#endif - - void MultInvAsBv(ARFLOAT* v, ARFLOAT* w); - - void DefineMatrices(ARumNonSymMatrix& Ap, - ARumNonSymMatrix& Bp); - - ARumNonSymPencil() { part = 'N'; } - // Short constructor that does nothing. - - ARumNonSymPencil(ARumNonSymMatrix& Ap, - ARumNonSymMatrix& Bp); - // Long constructor. - - ARumNonSymPencil(const ARumNonSymPencil& other) { Copy(other); } - // Copy constructor. - - virtual ~ARumNonSymPencil() { } - // Destructor. - - ARumNonSymPencil& operator=(const ARumNonSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARumNonSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARumNonSymPencil:: -Copy(const ARumNonSymPencil& other) -{ - - part = other.part; - A = other.A; - B = other.B; - AsB = other.AsB; -#ifdef ARCOMP_H - AsBc = other.AsBc; -#endif - -} // Copy. - - -template -void ARumNonSymPencil:: -SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], - int yind[], int ny, ARTYPE z[], int zind[], int& nz) -// A strongly sequential (and inefficient) sparse saxpy algorithm. -{ - - int ix, iy; - - nz = 0; - if ((nx == 0) || (a == (ARTYPE)0)) { - copy(ny,y,1,z,1); - for (iy=0; iy!=ny; iy++) zind[iy] = yind[iy]; - nz = ny; - return; - } - if (ny == 0) { - copy(nx,x,1,z,1); - scal(nx,a,z,1); - for (ix=0; ix!=nx; ix++) zind[ix] = xind[ix]; - nz = nx; - return; - } - ix = 0; - iy = 0; - while (true) { - if (xind[ix] == yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]+y[iy++]; - if ((ix == nx)||(iy == ny)) break; - } - else if (xind[ix] < yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]; - if (ix == nx) break; - } - else { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - if (iy == ny) break; - } - } - while (iy < ny) { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - } - while (ix < nx) { - zind[nz] = xind[ix]; - z[nz++] = x[ix++]; - } - -} // SparseSaxpy (ARTYPE). - - -#ifdef ARCOMP_H -template -void ARumNonSymPencil:: -SparseSaxpy(arcomplex a, ARFLOAT x[], int xind[], int nx, - ARFLOAT y[], int yind[], int ny, - arcomplex z[], int zind[], int& nz) -// A strongly sequential (and inefficient) sparse saxpy algorithm. -{ - - int ix, iy; - - nz = 0; - if ((nx == 0) || (a == arcomplex(0.0,0.0))) { - for (iy=0; iy!=ny; iy++) { - z[iy] = arcomplex(y[iy],0.0); - zind[iy] = yind[iy]; - } - nz = ny; - return; - } - if (ny == 0) { - for (ix=0; ix!=ny; ix++) { - z[ix] = a*arcomplex(x[ix],0.0); - zind[ix] = xind[ix]; - } - nz = nx; - return; - } - ix = 0; - iy = 0; - while (true) { - if (xind[ix] == yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]+y[iy++]; - if ((ix == nx)||(iy == ny)) break; - } - else if (xind[ix] < yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]; - if (ix == nx) break; - } - else { - zind[nz] = yind[iy]; - z[nz++] = arcomplex(y[iy++], 0.0); - if (iy == ny) break; - } - } - while (iy < ny) { - zind[nz] = yind[iy]; - z[nz++] = arcomplex(y[iy++], 0.0); - } - while (ix < nx) { - zind[nz] = xind[ix]; - z[nz++] = arcomplex(x[ix++], 0.0); - } - -} // SparseSaxpy (arcomplex). -#endif // ARCOMP_H. - - -template -void ARumNonSymPencil::SubtractAsB(ARTYPE sigma) -{ - - int i, acol, bcol, asbcol, scol; - - // Subtracting sigma*B from A. - - AsB.index[0] = 0; - asbcol = 0; - - for (i=0; i!=AsB.n; i++) { - bcol = B->pcol[i]; - acol = A->pcol[i]; - SparseSaxpy(-sigma, &B->a[bcol], &B->irow[bcol], B->pcol[i+1]-bcol, - &A->a[acol], &A->irow[acol], A->pcol[i+1]-acol, - &AsB.value[asbcol], &AsB.index[asbcol+AsB.n+1], scol); - asbcol += scol; - AsB.index[i+1] = asbcol; - } - - AsB.nnz = asbcol; - - // Adding one to all elements of vector index - // because the decomposition function was written in FORTRAN. - - for (i=0; i<=AsB.n+AsB.nnz; i++) AsB.index[i]++; - -} // SubtractAsB (ARTYPE shift). - - -#ifdef ARCOMP_H -template -void ARumNonSymPencil:: -SubtractAsB(ARFLOAT sigmaR, ARFLOAT sigmaI) -{ - - int i, acol, bcol, asbcol, scol; - arcomplex sigma; - - // Subtracting sigma*B from A. - - sigma = arcomplex(sigmaR, sigmaI); - AsBc.index[0] = 0; - asbcol = 0; - - for (i=0; i!=AsBc.n; i++) { - bcol = B->pcol[i]; - acol = A->pcol[i]; - SparseSaxpy(-sigma, &B->a[bcol], &B->irow[bcol], B->pcol[i+1]-bcol, - &A->a[acol], &A->irow[acol], A->pcol[i+1]-acol, - &AsBc.value[asbcol], &AsBc.index[asbcol+AsBc.n+1], scol); - asbcol += scol; - AsBc.index[i+1] = asbcol; - } - - AsBc.nnz = asbcol; - - // Adding one to all elements of vector index - // because the decomposition function was written in FORTRAN. - - for (i=0; i<=AsBc.n+AsBc.nnz; i++) AsBc.index[i]++; - -} // SubtractAsB (arcomplex shift). -#endif // ARCOMP_H - - -template -void ARumNonSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARumNonSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARumNonSymPencil::FactorAsB"); - } - - // Defining matrix AsB. - - if (!AsB.IsDefined()) { - - int fillin = A->fillin > B->fillin ? A->fillin : B->fillin; - AsB.DefineMatrix(A->ncols(), A->nzeros(), A->a, A->irow, - A->pcol, A->threshold, fillin, - (A->IsSymmetric() && B->IsSymmetric()), - A->icntl[3], false); - AsB.nnz = A->nzeros()+B->nzeros(); // temporary value. - - } - - // Reserving memory for some vectors used in matrix decomposition. - - AsB.CreateStructure(); // AsB.nnz must be set to A->nzeros()+B->nzeros(). - - // Subtracting sigma*B from A and storing the result on AsB. - - SubtractAsB(sigma); - - // Decomposing AsB. - - um2fa(AsB.n, AsB.index[AsB.n], 0, false, AsB.lvalue, AsB.lindex, AsB.value, - AsB.index, AsB.keep, AsB.cntl, AsB.icntl, AsB.info, AsB.rinfo); - - // Handling errors. - - AsB.ThrowError(); - - AsB.factored = true; - -} // FactorAsB (ARTYPE shift). - - -#ifdef ARCOMP_H -template -void ARumNonSymPencil:: -FactorAsB(ARFLOAT sigmaR, ARFLOAT sigmaI, char partp) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARumNonSymPencil::FactorAsB"); - } - - // Quitting the function if A and B are not square. - - if ((A->nrows() != A->ncols()) || (B->nrows() != B->ncols())) { - throw ArpackError(ArpackError::NOT_SQUARE_MATRIX, - "ARumNonSymPencil::FactorAsB"); - } - - // Defining matrix AsB. - - if (!AsBc.IsDefined()) { - - part = partp; - int fillin = A->fillin > B->fillin ? A->fillin : B->fillin; - AsBc.DefineMatrix(A->ncols(), A->nzeros(), 0, 0, - A->pcol, A->threshold, fillin, - (A->IsSymmetric() && B->IsSymmetric()), - A->icntl[3], false); - AsBc.nnz = A->nzeros()+B->nzeros(); // temporary value. - - } - - // Reserving memory for some vectors used in matrix decomposition. - - AsBc.CreateStructure(); // AsBc.nnz must be set to A->nzeros()+B->nzeros(). - - // Subtracting sigma*B from A and storing the result on AsBc. - - SubtractAsB(sigmaR, sigmaI); - - // Decomposing AsB. - - um2fa(AsBc.n, AsBc.index[AsBc.n], 0, false, AsBc.lvalue, AsBc.lindex, - AsBc.value, AsBc.index, AsBc.keep, AsBc.cntl, AsBc.icntl, - AsBc.info, AsBc.rinfo); - - // Handling errors. - - AsBc.ThrowError(); - - AsBc.factored = true; - -} // FactorAsB (arcomplex shift). -#endif // ARCOMP_H. - - -template -void ARumNonSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - B->MultInvv(w, w); - -} // MultInvBAv. - - -#ifdef ARCOMP_H - -template -void ARumNonSymPencil:: -MultInvAsBv(arcomplex* v, arcomplex* w) -{ - - AsB.MultInvv((ARTYPE*)v,(ARTYPE*)w); - -} // MultInvAsBv (arcomplex). - -#endif // ARCOMP_H. - - -template -void ARumNonSymPencil::MultInvAsBv(ARFLOAT* v, ARFLOAT* w) -{ - - if (part == 'N') { // shift is real. - - AsB.MultInvv((ARTYPE*)v,(ARTYPE*)w); - - } - else { // shift is complex. - -#ifdef ARCOMP_H - - int i; - arcomplex *tv, *tw; - - tv = new arcomplex[AsBc.ncols()]; - tw = new arcomplex[AsBc.ncols()]; - - for (i=0; i!=AsBc.ncols(); i++) tv[i] = arcomplex(v[i], 0.0); - - AsBc.MultInvv(tv, tw); - - if (part=='I') { - for (i=0; i!=AsBc.ncols(); i++) w[i] = imag(tw[i]); - } - else { - for (i=0; i!=AsBc.ncols(); i++) w[i] = real(tw[i]); - } - - delete[] tv; - delete[] tw; - -#endif // ARCOMP_H. - - } - -} // MultInvAsBv (ARFLOAT). - - -template -inline void ARumNonSymPencil:: -DefineMatrices(ARumNonSymMatrix& Ap, - ARumNonSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - - if ((A->n != B->n)||(A->m != B->m)) { - throw ArpackError(ArpackError::INCOMPATIBLE_SIZES, - "ARumNonSymMatrix::DefineMatrices"); - } - -} // DefineMatrices. - - -template -inline ARumNonSymPencil:: -ARumNonSymPencil(ARumNonSymMatrix& Ap, - ARumNonSymMatrix& Bp) -{ - - DefineMatrices(Ap, Bp); - -} // Long constructor. - - -template -ARumNonSymPencil& ARumNonSymPencil:: -operator=(const ARumNonSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUNSPEN_H diff --git a/src/external/arpack++/include/aruscomp.h b/src/external/arpack++/include/aruscomp.h deleted file mode 100644 index 13ddff71..00000000 --- a/src/external/arpack++/include/aruscomp.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUSComp.h. - Arpack++ class ARluCompStdEig definition - (umfpack version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARUSCOMP_H -#define ARUSCOMP_H - -#include -#include -#include "arch.h" -#include "arscomp.h" -#include "arunsmat.h" -#include "arrseig.h" - - -template -class ARluCompStdEig: - public virtual ARCompStdEig, ARFLOAT> > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(arcomplex sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(arcomplex sigmap); - - // a.2) Constructors and destructor. - - ARluCompStdEig() { } - // Short constructor. - - ARluCompStdEig(int nevp, ARumNonSymMatrix, ARFLOAT>& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluCompStdEig(int nevp, ARumNonSymMatrix, ARFLOAT>& A, - arcomplex sigma, const std::string& whichp = "LM", - int ncvp = 0, ARFLOAT tolp = 0.0, int maxitp = 0, - arcomplex* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluCompStdEig(const ARluCompStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluCompStdEig() { } - // Destructor. - - - // b) Operators. - - ARluCompStdEig& operator=(const ARluCompStdEig& other); - // Assignment operator. - -}; // class ARluCompStdEig. - - -// ------------------------------------------------------------------------ // -// ARluCompStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluCompStdEig:: -ChangeShift(arcomplex sigmaRp) -{ - - this->objOP->FactorAsI(sigmaRp); - ARrcStdEig >::ChangeShift(sigmaRp); - -} // ChangeShift. - - -template -inline void ARluCompStdEig::SetRegularMode() -{ - - ARStdEig, - ARumNonSymMatrix, ARFLOAT> >:: - SetRegularMode(this->objOP, - &ARumNonSymMatrix, ARFLOAT>::MultMv); - -} // SetRegularMode. - - -template -inline void ARluCompStdEig:: -SetShiftInvertMode(arcomplex sigmap) -{ - - ARStdEig, - ARumNonSymMatrix, ARFLOAT> >:: - SetShiftInvertMode(sigmap, this->objOP, - &ARumNonSymMatrix,ARFLOAT>::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluCompStdEig:: -ARluCompStdEig(int nevp, ARumNonSymMatrix, ARFLOAT>& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, arcomplex* residp, bool ishiftp) - -{ - - this->NoShift(); - DefineParameters(A.ncols(), nevp, &A, - &ARumNonSymMatrix, ARFLOAT>::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluCompStdEig:: -ARluCompStdEig(int nevp, ARumNonSymMatrix, ARFLOAT>& A, - arcomplex sigmap, const std::string& whichp, int ncvp, - ARFLOAT tolp, int maxitp, arcomplex* residp, - bool ishiftp) - -{ - - DefineParameters(A.ncols(), nevp, &A, - &ARumNonSymMatrix, ARFLOAT>::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluCompStdEig& ARluCompStdEig:: -operator=(const ARluCompStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUSCOMP_H diff --git a/src/external/arpack++/include/arusmat.h b/src/external/arpack++/include/arusmat.h deleted file mode 100644 index f5dae63a..00000000 --- a/src/external/arpack++/include/arusmat.h +++ /dev/null @@ -1,743 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUSMat.h. - Arpack++ class ARumSymMatrix definition. - - Modified to work with Umfpack v5.?? - Martin Reuter - Date 02/28/2013 - - Arpack++ Author: - Francisco Gomes - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - - -#include "aruspen.h" - -#ifndef ARUSMAT_H -#define ARUSMAT_H - -#include -#include -#include "arch.h" -#include "armat.h" -#include "arhbmat.h" -#include "arerror.h" -//#include "blas1c.h" -#include "umfpackc.h" - -template class ARumSymPencil; - -template -class ARumSymMatrix: public ARMatrix { - - friend class ARumSymPencil; - - protected: - - bool factored; - char uplo; - int nnz; - /* int fillin; - int lvalue; - int lindex; - int keep[20]; - int icntl[20]; - int info[40]; - ARTYPE cntl[10]; - ARTYPE rinfo[20]; - int* index; - ARTYPE* value;*/ - int* irow; - int* pcol; - int status; - double threshold; - ARTYPE* a; - ARhbMatrix mat; - void* Numeric; - int* Ap; - int* Ai; - ARTYPE* Ax; - - bool DataOK(); - - virtual void Copy(const ARumSymMatrix& other); - - void ClearMem(); - - void ExpandA(ARTYPE sigma = (ARTYPE)0); - -// void CreateStructure(); - - void ThrowError(); - - public: - - int nzeros() { return nnz; } - -// int FillFact() { return fillin; } - - bool IsFactored() { return factored; } - - void FactorA(); - - void FactorAsI(ARTYPE sigma); - - void MultMv(ARTYPE* v, ARTYPE* w); - - void MultInvv(ARTYPE* v, ARTYPE* w); - - void DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, char uplop = 'L', double thresholdp = 0.1, - int fillinp = 9, bool reducible = true, bool check = true); - - ARumSymMatrix(): ARMatrix() - { - factored = false; - Numeric = NULL; - Ap = NULL; - Ai = NULL; - Ax = NULL; - } - // Short constructor that does nothing. - - ARumSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, char uplop = 'L', double thresholdp = 0.1, - int fillinp = 9, bool reducible = true, bool check = true); - // Long constructor. - - ARumSymMatrix(const std::string& name, double thresholdp = 0.1, int fillinp = 9, - bool reducible = true, bool check = true); - // Long constructor (Harwell-Boeing file). - - ARumSymMatrix(const ARumSymMatrix& other) { Copy(other); } - // Copy constructor. - - virtual ~ARumSymMatrix() { ClearMem(); } - // Destructor. - - ARumSymMatrix& operator=(const ARumSymMatrix& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARumSymMatrix member functions definition. // -// ------------------------------------------------------------------------ // - - -template -bool ARumSymMatrix::DataOK() -{ - - int i, j, k; - - // Checking if pcol is in ascending order. - - i = 0; - while ((i!=this->n)&&(pcol[i]<=pcol[i+1])) i++; - if (i!=this->n) return false; - - // Checking if irow components are in order and within bounds. - - for (i=0; i!=this->n; i++) { - j = pcol[i]; - k = pcol[i+1]-1; - if (j<=k) { - if (uplo == 'U') { - if ((irow[j]<0)||(irow[k]>i)) return false; - } - else { // uplo == 'L'. - if ((irow[j]=this->n)) return false; - } - while ((j!=k)&&(irow[j] -inline void ARumSymMatrix::ClearMem() -{ - - if (factored) - { - if (Numeric) umfpack_di_free_numeric (&Numeric); - //if (value) delete[] value; - //if (index) delete[] index; - //value = NULL; - //index = NULL; - if (Ai) delete [] Ai; - Ai = NULL; - if (Ap) delete [] Ap; - Ap = NULL; - if (Ax) delete [] Ax; - Ax = NULL; - } - -} // ClearMem. - - - -template -void ARumSymMatrix::Copy(const ARumSymMatrix& other) -{ - - // Copying very fundamental variables. - ClearMem(); - - // Copying very fundamental variables and user-defined parameters. - - this->m = other.m; - this->n = other.n; - this->defined = other.defined; - factored = other.factored; - //fillin = other.fillin; - nnz = other.nnz; - //lvalue = other.lvalue; - //lindex = other.lindex; - irow = other.irow; - pcol = other.pcol; - a = other.a; - threshold = other.threshold; - uplo = other.uplo; - - // Returning from here if "other" was not initialized. - - if (!this->defined) return; - - // Returning from here if "other" was not factored. - - if (!factored) return; - - factored = false; - -} // Copy. - -template -void ARumSymMatrix::ExpandA(ARTYPE sigma) -{ -std::cout <<"ARumSymMatrix::ExpandA(" << sigma << ") ..." << std::flush; - - ClearMem(); - - // Checking if sigma is zero. - bool subtract = (sigma != (ARTYPE)0); - - int mynnz = 2*nnz; - if (subtract) mynnz = 2*nnz + this->n; // some space for the diag entries just in case - - // create triples (i,j,value) - int * tripi = new int[mynnz]; - int * tripj = new int[mynnz]; - ARTYPE* tripx = new ARTYPE[mynnz]; - int count = 0; - int i,j; -// if (uplo == 'U') - { - for (i=0; i != this->n; i++) - { - bool founddiag = false; - for (j=pcol[i]; j<(pcol[i+1]); j++) - { - - if (i == irow[j]) // on diag - { - tripi[count] = i; - tripj[count] = irow[j]; - if (subtract) - { - tripx[count] = a[j]-sigma; - founddiag = true; - } - else tripx[count] = a[j]; - count++; - } - else - { - - tripi[count] = i; - tripj[count] = irow[j]; - tripx[count] = a[j]; - count++; - tripj[count] = i; - tripi[count] = irow[j]; - tripx[count] = a[j]; - count++; - } - } - if (subtract && ! founddiag) - { - tripi[count] = i; - tripj[count] = i; - tripx[count] = -sigma; - count++; - } - } - } - - // convert triples to Ax Ap Ai - Ap = new int[this->n+1]; - Ai = new int[count]; - Ax = new ARTYPE[count]; - status = umfpack_di_triplet_to_col (this->n, this->n, count, tripi, tripj, tripx, Ap, Ai, Ax, (int *)NULL) ; - if (status != UMFPACK_OK) - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymMatrix::ExpandA"); - if (Ap[this->n] != count) - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymMatrix::ExpandA"); - - - // cleanup - delete [] tripi; - delete [] tripj; - delete [] tripx; - - //std::cout << std::endl << std::endl; - //double Control [UMFPACK_CONTROL]; - //Control [UMFPACK_PRL] = 3; - //status = umfpack_di_report_matrix(this->n, this->n,Ap, Ai, Ax,0,Control); - //std::cout << " status: " << status << std::endl; - //std::cout << std::endl << std::endl; - - std::cout <<" done!" << std::endl; - -} - -/*template -void ARumSymMatrix::ExpandA(ARTYPE sigma) -{ - - bool subtract; - int i, j, k, ki; - - // Checking if sigma is zero. - - subtract = (sigma != (ARTYPE)0); - - // Filling index with zeros. - - for (i=0; i<=this->n; i++) index[i] = 0; - - // Counting the elements in each column of A. - - if (uplo == 'U') { - - for (i=0; i!=this->n; i++) { - k = pcol[i+1]; - if ((k!=pcol[i])&&(irow[k-1]==i)) { - k--; - } - else { - if (subtract) index[i]++; - } - for (j=pcol[i]; jn; i++) { - k = pcol[i]; - if ((k!=pcol[i+1])&&(irow[k]==i)) { - k++; - } - else { - if (subtract) index[i]++; - } - for (j=k; jn; i++) index[i+1]+=index[i]; - - // Adding pcol to index. - - for (i=this->n; i>0; i--) index[i] = index[i-1]+pcol[i]; - index[0] = pcol[0]; - - // Expanding A. - - ki = this->n+1; - - if (uplo == 'U') { - - for (i=0; in; i++) { - for (j=pcol[i]; j<(pcol[i+1]-1); j++) { - index[ki+index[i]] = irow[j]+1; - index[ki+index[irow[j]]] = i+1; - value[index[i]++] = a[j]; - value[index[irow[j]]++] = a[j]; - } - if ((pcol[i]!=pcol[i+1])&&(irow[j]==i)) { - index[ki+index[i]] = i+1; - if (subtract) { - value[index[i]++] = a[j]-sigma; - } - else { - value[index[i]++] = a[j]; - } - } - else { - if (subtract) { - index[ki+index[i]] = i+1; - value[index[i]++] = -sigma; - } - } - } - - } - else { // uplo == 'L' - - for (i=0; in; i++) { - k=pcol[i]; - if ((k!=pcol[i+1])&&(irow[k]==i)) { - index[ki+index[i]] = i+1; - if (subtract) { - value[index[i]++] = a[k]-sigma; - } - else { - value[index[i]++] = a[k]; - } - k++; - } - else { - if (subtract) { - index[ki+index[i]] = i+1; - value[index[i]++] = -sigma; - } - } - for (j=k; jn; i>0; i--) { - index[i] = index[i-1]+1; - } - index[0] = 1; - -} // ExpandA.*/ - - -/*template -inline void ARumSymMatrix::CreateStructure() -{ - - int dimfact = (((fillin+1)*nnz*2)<(this->n*this->n)) ? (fillin+1)*nnz*2 : this->n*this->n; - - ClearMem(); - - lindex = 30*this->n+dimfact; // ????? - lvalue = dimfact; - - value = new ARTYPE[lvalue]; - index = new int[lindex]; - -} // CreateStructure. -*/ - -template -inline void ARumSymMatrix::ThrowError() -{ - - if (status== -1) { // Memory is not suficient. - throw ArpackError(ArpackError::INSUFICIENT_MEMORY, - "ARumSymMatrix::FactorA"); - } - else if (status == 1) { // Matrix is singular. - throw ArpackError(ArpackError::MATRIX_IS_SINGULAR, - "ARumSymMatrix::FactorA"); - } - else if (status != 0) { // Illegal argument. - throw ArpackError(ArpackError::PARAMETER_ERROR, - "ARumSymMatrix::FactorA"); - } - -} // ThrowError. - - -template -void ARumSymMatrix::FactorA() -{ - -std::cout <<"ARumSymMatrix::FactorA " << std::endl; - - // Quitting the function if A was not defined. - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARumSymMatrix::FactorA"); - } - - ExpandA(); // create Ap Ai Ax - - void *Symbolic ; - status = umfpack_di_symbolic (this->n, this->n, Ap, Ai, Ax, &Symbolic, NULL, NULL) ; - ThrowError(); - status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL) ; - ThrowError(); - umfpack_di_free_symbolic (&Symbolic) ; - -/* - - // Reserving memory for some vectors used in matrix decomposition. - - CreateStructure(); - - // Copying A to (value, index); - - ExpandA(); - - // Decomposing A. - - um2fa(this->n, index[this->n], 0, false, lvalue, lindex, value, - index, keep, cntl, icntl, info, rinfo); -*/ - - factored = true; - -} // FactorA. - - -template -void ARumSymMatrix::FactorAsI(ARTYPE sigma) -{ -std::cout <<"ARumSymMatrix::FactorAsI " << sigma << std::endl; - - // Quitting the function if A was not defined. - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARumSymMatrix::FactorAsI"); - } - - // Reserving memory for some vectors used in matrix decomposition. - //CreateStructure(); - - // Subtracting sigma*I from A. - ExpandA(sigma); - - // Decomposing AsI. - double Info [UMFPACK_INFO], Control [UMFPACK_CONTROL]; - umfpack_di_defaults (Control) ; - //std::cout << " Ap[n] = " << Ap[this->n] << std::flush; - - void *Symbolic ; - status = umfpack_di_symbolic (this->n, this->n, Ap, Ai, Ax, &Symbolic, Control, Info) ; - //std::cout << " symbolic status: " << status << std::endl; - ThrowError(); - status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL) ; - //std::cout << " numeric status: " << status << std::endl; - ThrowError(); - umfpack_di_free_symbolic (&Symbolic) ; - -// // Decomposing AsI. -// um2fa(this->n, index[this->n], 0, false, lvalue, lindex, value, -// index, keep, cntl, icntl, info, rinfo); - - - factored = true; - -} // FactorAsI. - - -template -void ARumSymMatrix::MultMv(ARTYPE* v, ARTYPE* w) -{ -//std::cout <<"ARumSymMatrix::MultMv ..." << std::flush; - - int i,j,k; - ARTYPE t; - - // Quitting the function if A was not defined. - - if (!this->IsDefined()) { - throw ArpackError(ArpackError::DATA_UNDEFINED, "ARumSymMatrix::MultMv"); - } - - // Determining w = M.v. - - for (i=0; i!=this->m; i++) w[i]=(ARTYPE)0; - - if (uplo == 'U') { - - for (i=0; i!=this->n; i++) { - t = v[i]; - k = pcol[i+1]; - if ((k!=pcol[i])&&(irow[k-1]==i)) { - w[i] += t*a[k-1]; - k--; - } - for (j=pcol[i]; jn; i++) { - t = v[i]; - k = pcol[i]; - if ((k!=pcol[i+1])&&(irow[k]==i)) { - w[i] += t*a[k]; - k++; - } - for (j=k; j -void ARumSymMatrix::MultInvv(ARTYPE* v, ARTYPE* w) -{ -//std::cout <<"ARumSymMatrix::MultInvv ..." << std::flush; - - // Quitting the function if A (or AsI) was not factored. - - if (!IsFactored()) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARumSymMatrix::MultInvv"); - } - - // Solving A.w = v (or AsI.w = v). - -// ARTYPE* space = new ARTYPE[2*this->n]; -// um2so(this->n, 0, false, lvalue, lindex, value, index, -// keep, v, w, space, cntl, icntl, info, rinfo); -// delete[] space; - - status = umfpack_di_solve (UMFPACK_A, Ap, Ai, Ax, w, v, Numeric, NULL, NULL) ; - if (status != UMFPACK_OK) - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymMatrix::MultInvv"); - -} // MultInvv. - - -template -inline void ARumSymMatrix:: -DefineMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, char uplop, double thresholdp, - int fillinp, bool reducible, bool check) -{ - - this->m = np; - this->n = np; - nnz = nnzp; - a = ap; - irow = irowp; - pcol = pcolp; - pcol[this->n] = nnz; - uplo = uplop; -// fillin = (fillinp>2) ? fillinp : 2; - threshold = thresholdp; -// value = NULL; -// index = NULL; - -// // Preparing umfpack. -// -// um21i(keep, cntl, icntl, threshold, true, reducible); - - // Checking data. - if ((check)&&(!DataOK())) { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARumSymMatrix::DefineMatrix"); - } - - this->defined = true; - -} // DefineMatrix. - - -template -inline ARumSymMatrix:: -ARumSymMatrix(int np, int nnzp, ARTYPE* ap, int* irowp, - int* pcolp, char uplop, double thresholdp, - int fillinp, bool reducible, bool check) : ARMatrix(np) -{ - Numeric = NULL; - Ap = NULL; - Ai = NULL; - Ax = NULL; - factored = false; - DefineMatrix(np, nnzp, ap, irowp, pcolp, uplop, - thresholdp, fillinp, reducible, check); - -} // Long constructor. - - -template -ARumSymMatrix:: -ARumSymMatrix(const std::string& file, double thresholdp, int fillinp, - bool reducible, bool check) -{ - Numeric = NULL; - Ap = NULL; - Ai = NULL; - Ax = NULL; - - factored = false; - - try { - mat.Define(file); - } - catch (ArpackError) { // Returning from here if an error has occurred. - throw ArpackError(ArpackError::CANNOT_READ_FILE, "ARumSymMatrix"); - } - - if ((mat.NCols() == mat.NRows()) && (mat.IsSymmetric())) { - - DefineMatrix(mat.NCols(), mat.NonZeros(), (ARTYPE*)mat.Entries(), - mat.RowInd(), mat.ColPtr(), 'L', thresholdp, - fillinp, reducible, check); - } - else { - throw ArpackError(ArpackError::INCONSISTENT_DATA, - "ARumSymMatrix::ARluSymMatrix"); - } - -} // Long constructor (Harwell-Boeing file). - - -template -ARumSymMatrix& ARumSymMatrix:: -operator=(const ARumSymMatrix& other) -{ - - if (this != &other) { // Stroustrup suggestion. - ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUSMAT_H diff --git a/src/external/arpack++/include/arusnsym.h b/src/external/arpack++/include/arusnsym.h deleted file mode 100644 index 644ee27d..00000000 --- a/src/external/arpack++/include/arusnsym.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUSNSym.h. - Arpack++ class ARluNonSymStdEig definition - (umfpack version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARUSNSYM_H -#define ARUSNSYM_H - -#include -#include -#include "arch.h" -#include "arsnsym.h" -#include "arunsmat.h" - - -template -class ARluNonSymStdEig: - public virtual ARNonSymStdEig > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // a.2) Constructors and destructor. - - ARluNonSymStdEig() { } - // Short constructor. - - ARluNonSymStdEig(int nevp, ARumNonSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluNonSymStdEig(int nevp, ARumNonSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluNonSymStdEig(const ARluNonSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluNonSymStdEig() { } - // Destructor. - - // b) Operators. - - ARluNonSymStdEig& operator=(const ARluNonSymStdEig& other); - // Assignment operator. - -}; // class ARluNonSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluNonSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluNonSymStdEig::ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI(this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluNonSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(this->objOP, &ARumNonSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluNonSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, - &ARumNonSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluNonSymStdEig:: -ARluNonSymStdEig(int nevp, ARumNonSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->NoShift(); - DefineParameters(A.ncols(), nevp, &A, - &ARumNonSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluNonSymStdEig:: -ARluNonSymStdEig(int nevp, ARumNonSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - DefineParameters(A.ncols(), nevp, &A, - &ARumNonSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluNonSymStdEig& ARluNonSymStdEig:: -operator=(const ARluNonSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUSNSYM_H diff --git a/src/external/arpack++/include/aruspen.h b/src/external/arpack++/include/aruspen.h deleted file mode 100644 index bd2a5994..00000000 --- a/src/external/arpack++/include/aruspen.h +++ /dev/null @@ -1,543 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUSPen.h. - Arpack++ class ARumSymPencil definition. - - Modified to work with Umfpack v5.?? - Martin Reuter - Date 02/28/2013 - - Arpack++ Author: - Francisco Gomes - - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARUSPEN_H -#define ARUSPEN_H - -//#include "arch.h" -//#include "arerror.h" -//#include "lapackc.h" -#include "arusmat.h" -#include "blas1c.h" - - -template -class ARumSymPencil -{ - - protected: - - ARumSymMatrix* A; - ARumSymMatrix* B; - //ARumSymMatrix AsB; - void* Numeric; - int* Ap; - int* Ai; - ARTYPE* Ax; - - virtual void Copy(const ARumSymPencil& other); - -// void SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], -// int yind[], int ny, ARTYPE z[], int zind[], int& nz); - - void ExpandAsB(ARTYPE sigma); - -// void SubtractAsB(ARTYPE sigma); - void ClearMem(); - - public: - - bool IsFactored() { return (Numeric != NULL); } - - void FactorAsB(ARTYPE sigma); - - void MultAv(ARTYPE* v, ARTYPE* w) { A->MultMv(v,w); } - - void MultBv(ARTYPE* v, ARTYPE* w) { B->MultMv(v,w); } - - void MultInvBAv(ARTYPE* v, ARTYPE* w); - - //void MultInvAsBv(ARTYPE* v, ARTYPE* w) { AsB.MultInvv(v,w); } - void MultInvAsBv(ARTYPE* v, ARTYPE* w); - - void DefineMatrices(ARumSymMatrix& Ap, ARumSymMatrix& Bp); - - //ARumSymPencil() { AsB.factored = false; } - ARumSymPencil() { Numeric = NULL; Ap = NULL; Ai = NULL; Ax = NULL; } - // Short constructor that does nothing. - - ARumSymPencil(ARumSymMatrix& Ap, ARumSymMatrix& Bp); - // Long constructor. - - ARumSymPencil(const ARumSymPencil& other) { Copy(other); } - // Copy constructor. - - virtual ~ARumSymPencil() { } - // Destructor. - - ARumSymPencil& operator=(const ARumSymPencil& other); - // Assignment operator. - -}; - -// ------------------------------------------------------------------------ // -// ARumSymPencil member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARumSymPencil::ClearMem() -{ - - if (Numeric) umfpack_di_free_numeric (&Numeric); - if (Ai) delete [] Ai; - Ai = NULL; - if (Ap) delete [] Ap; - Ap = NULL; - if (Ax) delete [] Ax; - Ax = NULL; - -} // ClearMem. - - - -template -inline void ARumSymPencil::Copy(const ARumSymPencil& other) -{ - ClearMem(); - A = other.A; - B = other.B; -// AsB = other.AsB; - -} // Copy. - - -/*template -void ARumSymPencil:: -SparseSaxpy(ARTYPE a, ARTYPE x[], int xind[], int nx, ARTYPE y[], - int yind[], int ny, ARTYPE z[], int zind[], int& nz) -// A strongly sequential (and inefficient) sparse saxpy algorithm. -{ - - int ix, iy; - - nz = 0; - if ((nx == 0) || (a == (ARTYPE)0)) { - copy(ny,y,1,z,1); - for (iy=0; iy!=ny; iy++) zind[iy] = yind[iy]; - nz = ny; - return; - } - if (ny == 0) { - copy(nx,x,1,z,1); - scal(nx,a,z,1); - for (ix=0; ix!=nx; ix++) zind[ix] = xind[ix]; - nz = nx; - return; - } - ix = 0; - iy = 0; - while (true) { - if (xind[ix] == yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]+y[iy++]; - if ((ix == nx)||(iy == ny)) break; - } - else if (xind[ix] < yind[iy]) { - zind[nz] = xind[ix]; - z[nz++] = a*x[ix++]; - if (ix == nx) break; - } - else { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - if (iy == ny) break; - } - } - while (iy < ny) { - zind[nz] = yind[iy]; - z[nz++] = y[iy++]; - } - while (ix < nx) { - zind[nz] = xind[ix]; - z[nz++] = x[ix++]; - } - -} // SparseSaxpy. - - -template -void ARumSymPencil::ExpandAsB() -{ - - int i, j, k, n; - int *pcol, *irow, *index, *pos; - ARTYPE *value; - - // Initializing variables. - - n = AsB.n; - index = AsB.index; - value = AsB.value; - irow = &index[n+1]; - pcol = new int[AsB.n+1]; - pos = new int[AsB.n+1]; - for (i=0; i<=n; i++) pcol[i] = index[i]; - for (i=0; i<=n; i++) pos[i] = 0; - - // Counting the elements in each column of AsB. - - if (AsB.uplo == 'U') { - - for (i=0; i!=n; i++) { - k = pcol[i+1]; - if ((k!=pcol[i])&&(irow[k-1]==i)) k--; - for (j=pcol[i]; j0; i--) index[i] += pos[i-1]; - - // Expanding A. - - if (AsB.uplo == 'U') { - - for (i=n-1; i>=0; i--) { - pos[i] = index[i]+pcol[i+1]-pcol[i]; - k = pos[i]-1; - for (j=pcol[i+1]-1; j>=pcol[i]; j--) { - value[k] = value[j]; - irow[k--] = irow[j]; - } - } - for (i=1; iindex[i])&&(irow[k-1]==i)) k--; - for (j=index[i]; j=0; i--) { - k = index[i+1]-1; - for (j=pcol[i+1]-1; j>=pcol[i]; j--) { - value[k] = value[j]; - irow[k--] = irow[j]; - } - pos[i] = index[i]; - } - for (i=0; i<(n-1); i++) { - k = index[i+1]-pcol[i+1]+pcol[i]; - if ((k -void ARumSymPencil::SubtractAsB(ARTYPE sigma) -{ - - int i, acol, bcol, asbcol, scol; - - // Quitting function if A->uplo is not equal to B->uplo. - - if ((A->uplo != B->uplo)&&(sigma != (ARTYPE)0)) { - throw ArpackError(ArpackError::DIFFERENT_TRIANGLES, - "ARumSymPencil::SubtractAsB"); - } - AsB.uplo = A->uplo; - - // Subtracting sigma*B from A. - - AsB.index[0] = 0; - asbcol = 0; - - for (i=0; i!=AsB.n; i++) { - bcol = B->pcol[i]; - acol = A->pcol[i]; - SparseSaxpy(-sigma, &B->a[bcol], &B->irow[bcol], B->pcol[i+1]-bcol, - &A->a[acol], &A->irow[acol], A->pcol[i+1]-acol, - &AsB.value[asbcol], &AsB.index[asbcol+AsB.n+1], scol); - asbcol += scol; - AsB.index[i+1] = asbcol; - } - - // Expanding AsB. - - ExpandAsB(); - - // Adding one to all elements of vector index - // because the decomposition function was written in FORTRAN. - - for (i=0; i<=AsB.n+AsB.nnz; i++) AsB.index[i]++; - -} // SubtractAsB. */ - - -template -void ARumSymPencil::ExpandAsB(ARTYPE sigma) -{ -std::cout <<"ARumSymPencil::ExpandAsB(" << sigma << ") ..." << std::flush; - - ClearMem(); - - int mynnz = 2*A->nnz+2*B->nnz; - if (sigma == 0.0) - mynnz = 2*A->nnz; - - // create triples (i,j,value) - int * tripi = new int[mynnz]; - int * tripj = new int[mynnz]; - ARTYPE* tripx = new ARTYPE[mynnz]; - if (tripi == NULL || tripj == NULL || tripx ==NULL) - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymPencil::ExpandAsB out of memory (1)"); - - int count = 0; - int i,j; - for (i=0; i < A->n; i++) - { - // create triplets from A - for (j=A->pcol[i]; j<(A->pcol[i+1]); j++) - { - tripi[count] = i; - tripj[count] = A->irow[j]; - tripx[count] = A->a[j]; - count++; - if (i != A->irow[j]) // not on diag - { - tripj[count] = i; - tripi[count] = A->irow[j]; - tripx[count] = A->a[j]; - count++; - } - } - - if (sigma != 0.0) - { - // create triplets from -sigma B - for (j=B->pcol[i]; j<(B->pcol[i+1]); j++) - { - tripi[count] = i; - tripj[count] = B->irow[j]; - tripx[count] = -sigma * B->a[j]; - count++; - if (i != B->irow[j]) // not on diag - { - tripj[count] = i; - tripi[count] = B->irow[j]; - tripx[count] = tripx[count-1]; - count++; - } - } - } - - } - - //Write_Triplet_Matrix("A-aruspen.asc",tripi,tripj,tripx,count); - - std::cout<< " ( N = " << A->n << " NNZ = " << count << " )" << std::flush; - //std::cout<< " size double " << sizeof(double) << " size ARTYPE " << sizeof(ARTYPE) << std::endl; - // convert triples (A-sigma B) to Ax Ap Ai - Ap = new int[A->n + 1]; - Ai = new int[count]; - Ax = new ARTYPE[count]; - if (!Ap || !Ai || !Ax ) - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymPencil::ExpandAsB out of memory (2)"); - - int status = umfpack_di_triplet_to_col (A->n, A->n, count, tripi, tripj, tripx, Ap, Ai, Ax, (int *)NULL) ; - if (status != UMFPACK_OK) - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymPencil::ExpandAsB triplet to col"); - - // cleanup - delete [] tripi; - delete [] tripj; - delete [] tripx; - - //std::cout << std::endl << std::endl; - //double Control [UMFPACK_CONTROL]; - //Control [UMFPACK_PRL] = 3; - //status = umfpack_di_report_matrix(A->n, A->n,Ap, Ai, Ax,0,Control); - //std::cout << " status: " << status << std::endl; - //std::cout << std::endl << std::endl; - - std::cout <<" done!" << std::endl; -} - -template -void ARumSymPencil::FactorAsB(ARTYPE sigma) -{ - - // Quitting the function if A and B were not defined. - - if (!(A->IsDefined()&&B->IsDefined())) { - throw ArpackError(ArpackError::DATA_UNDEFINED, - "ARumSymPencil::FactorAsB"); - } - - - // Subtracting sigma*B from A and storing the result - ExpandAsB(sigma); - - // Decomposing AsB. - double Info [UMFPACK_INFO], Control [UMFPACK_CONTROL]; - umfpack_di_defaults (Control) ; - //std::cout <<" loaded defaults" << std::endl; - void *Symbolic ; - int status = umfpack_di_symbolic (A->n, A->n, Ap, Ai, Ax, &Symbolic, Control, Info) ; - std::cout << " symbolic status: " << status << std::endl; - if (status != UMFPACK_OK) - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymPencil::FactorAsB symbolic"); - status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, Control, Info) ; - std::cout << " numeric status: " << status << std::endl; - if (status == 1) - { - std::cout << " WARNING: MATRIX IS SINGULAR " << std::endl; - //throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymPencil::FactorAsB numeric (matrix singular)"); - } - if (status < UMFPACK_OK) - { - std::cout << " ERROR CODE: " << status << std::endl; - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymPencil::FactorAsB numeric"); - } - umfpack_di_free_symbolic (&Symbolic) ; - -//exit(0); - - // Decomposing AsB. - - //um2fa(AsB.n, AsB.index[AsB.n], 0, false, AsB.lvalue, AsB.lindex, AsB.value, - // AsB.index, AsB.keep, AsB.cntl, AsB.icntl, AsB.info, AsB.rinfo); - - // Handling errors. - - // AsB.ThrowError(); - - // AsB.factored = true; - -} // FactorAsB (ARTYPE shift). - - -template -void ARumSymPencil::MultInvBAv(ARTYPE* v, ARTYPE* w) -{ - - if (!B->IsFactored()) B->FactorA(); - - A->MultMv(v, w); - copy(A->ncols(), w, 1, v, 1); - B->MultInvv(w, w); - -} // MultInvBAv. - -template -void ARumSymPencil::MultInvAsBv(ARTYPE* v, ARTYPE* w) -{ - if (!Numeric) { - throw ArpackError(ArpackError::NOT_FACTORED_MATRIX, - "ARchSymPencil::MultInvAsBv"); - } - - // Solving A.w = v (or AsI.w = v). - int status = umfpack_di_solve (UMFPACK_A, Ap, Ai, Ax, w, v, Numeric, NULL, NULL) ; - if (status == 1) - { - std::cout << " WARNING: MATRIX IS SINGULAR " << std::endl; - //throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymPencil::FactorAsB numeric (matrix singular)"); - } - if (status < UMFPACK_OK) - { - std::cout << " ERROR CODE: " << status << std::endl; - throw ArpackError(ArpackError::PARAMETER_ERROR, "ARumSymPencil::MultInvAsBv"); - - } - -} // MultInvAsBv - -template -inline void ARumSymPencil:: -DefineMatrices(ARumSymMatrix& Ap, ARumSymMatrix& Bp) -{ - - A = &Ap; - B = &Bp; - - if (A->n != B->n) { - throw ArpackError(ArpackError::INCOMPATIBLE_SIZES, - "ARumSymMatrix::DefineMatrices"); - } - -} // DefineMatrices. - - -template -inline ARumSymPencil:: -ARumSymPencil(ARumSymMatrix& Ap, ARumSymMatrix& Bp) -{ - Numeric = NULL; - Ap = NULL; - Ai = NULL; - Ax = NULL; - - //AsB.factored = false; - DefineMatrices(Ap, Bp); - - -} // Long constructor. - - -template -ARumSymPencil& ARumSymPencil:: -operator=(const ARumSymPencil& other) -{ - - if (this != &other) { // Stroustrup suggestion. - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUSPEN_H diff --git a/src/external/arpack++/include/arussym.h b/src/external/arpack++/include/arussym.h deleted file mode 100644 index f1a53cf8..00000000 --- a/src/external/arpack++/include/arussym.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ARUSSym.h. - Arpack++ class ARluSymStdEig definition - (UMFPACK version). - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef ARUSSYM_H -#define ARUSSYM_H - -#include -#include -#include "arch.h" -#include "arssym.h" -#include "arusmat.h" - - -template -class ARluSymStdEig: - public virtual ARSymStdEig > { - - public: - - // a) Public functions: - - // a.1) Functions that allow changes in problem parameters. - - virtual void ChangeShift(ARFLOAT sigmaRp); - - virtual void SetRegularMode(); - - virtual void SetShiftInvertMode(ARFLOAT sigmap); - - // a.2) Constructors and destructor. - - ARluSymStdEig() { } - // Short constructor. - - ARluSymStdEig(int nevp, ARumSymMatrix& A, - const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (regular mode). - - ARluSymStdEig(int nevp, ARumSymMatrix& A, - ARFLOAT sigma, const std::string& whichp = "LM", int ncvp = 0, - ARFLOAT tolp = 0.0, int maxitp = 0, - ARFLOAT* residp = NULL, bool ishiftp = true); - // Long constructor (shift and invert mode). - - ARluSymStdEig(const ARluSymStdEig& other) { Copy(other); } - // Copy constructor. - - virtual ~ARluSymStdEig() { } - // Destructor. - - // b) Operators. - - ARluSymStdEig& operator=(const ARluSymStdEig& other); - // Assignment operator. - -}; // class ARluSymStdEig. - - -// ------------------------------------------------------------------------ // -// ARluSymStdEig member functions definition. // -// ------------------------------------------------------------------------ // - - -template -inline void ARluSymStdEig::ChangeShift(ARFLOAT sigmaRp) -{ - - this->sigmaR = sigmaRp; - this->sigmaI = 0.0; - this->mode = 3; - this->iparam[7] = this->mode; - - this->objOP->FactorAsI(this->sigmaR); - this->Restart(); - -} // ChangeShift. - - -template -inline void ARluSymStdEig::SetRegularMode() -{ - - ARStdEig >:: - SetRegularMode(this->objOP, &ARumSymMatrix::MultMv); - -} // SetRegularMode. - - -template -inline void ARluSymStdEig::SetShiftInvertMode(ARFLOAT sigmap) -{ - - ARStdEig >:: - SetShiftInvertMode(sigmap, this->objOP, &ARumSymMatrix::MultInvv); - -} // SetShiftInvertMode. - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARumSymMatrix& A, - const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) -{ - - this->NoShift(); - this->DefineParameters(A.ncols(), nevp, &A, &ARumSymMatrix::MultMv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - -} // Long constructor (regular mode). - - -template -inline ARluSymStdEig:: -ARluSymStdEig(int nevp, ARumSymMatrix& A, - ARFLOAT sigmap, const std::string& whichp, int ncvp, ARFLOAT tolp, - int maxitp, ARFLOAT* residp, bool ishiftp) - -{ - - this->DefineParameters(A.ncols(), nevp, &A, &ARumSymMatrix::MultInvv, - whichp, ncvp, tolp, maxitp, residp, ishiftp); - ChangeShift(sigmap); - -} // Long constructor (shift and invert mode). - - -template -ARluSymStdEig& ARluSymStdEig:: -operator=(const ARluSymStdEig& other) -{ - - if (this != &other) { // Stroustrup suggestion. - this->ClearMem(); - Copy(other); - } - return *this; - -} // operator=. - - -#endif // ARUSSYM_H diff --git a/src/external/arpack++/include/blas1c.h b/src/external/arpack++/include/blas1c.h deleted file mode 100644 index b24fca52..00000000 --- a/src/external/arpack++/include/blas1c.h +++ /dev/null @@ -1,367 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE blas1c.h. - Interface to blas 1 and blas 2 FORTRAN routines. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#include "arch.h" -#include "blas1f.h" - -#ifndef BLAS1C_H -#define BLAS1C_H - -// ASSUM - -inline float assum(const ARint &n, const float dx[], const ARint &incx) { - return F77NAME(sasum)(&n, dx, &incx); -} // assum (float) - -inline double assum(const ARint &n, const double dx[], const ARint &incx) { - return F77NAME(dasum)(&n, dx, &incx); -} // assum (double) - -#ifdef ARCOMP_H -inline float assum(const ARint &n, const arcomplex dx[], - const ARint &incx) { - return F77NAME(scasum)(&n, dx, &incx); -} // assum (arcomplex) - -inline double assum(const ARint &n, const arcomplex dx[], - const ARint &incx) { - return F77NAME(dzasum)(&n, dx, &incx); -} // assum (arcomplex) -#endif - -// AXPY - -inline void axpy(const ARint &n, const float &da, const float dx[], - const ARint &incx, float dy[], const ARint &incy) { - F77NAME(saxpy)(&n, &da, dx, &incx, dy, &incy); -} // axpy (float) - -inline void axpy(const ARint &n, const double &da, const double dx[], - const ARint &incx, double dy[], const ARint &incy) { - F77NAME(daxpy)(&n, &da, dx, &incx, dy, &incy); -} // axpy (double) - -#ifdef ARCOMP_H -inline void axpy(const ARint &n, const arcomplex &da, - const arcomplex dx[], const ARint &incx, - arcomplex dy[], const ARint &incy) { - F77NAME(caxpy)(&n, &da, dx, &incx, dy, &incy); -} // axpy (arcomplex) - -inline void axpy(const ARint &n, const arcomplex &da, - const arcomplex dx[], const ARint &incx, - arcomplex dy[], const ARint &incy) { - F77NAME(zaxpy)(&n, &da, dx, &incx, dy, &incy); -} // axpy (arcomplex) -#endif - -// COPY - -inline void copy(const ARint &n, const float dx[], const ARint &incx, - float dy[], const ARint &incy) { - if (dx != dy) F77NAME(scopy)(&n, dx, &incx, dy, &incy); -} // copy (float) - -inline void copy(const ARint &n, const double dx[], const ARint &incx, - double dy[], const ARint &incy) { - if (dx != dy) F77NAME(dcopy)(&n, dx, &incx, dy, &incy); -} // copy (double) - -#ifdef ARCOMP_H -inline void copy(const ARint &n, const arcomplex dx[], - const ARint &incx, arcomplex dy[], - const ARint &incy) { - if (dx != dy) F77NAME(ccopy)(&n, dx, &incx, dy, &incy); -} // copy (arcomplex) - -inline void copy(const ARint &n, const arcomplex dx[], - const ARint &incx, arcomplex dy[], - const ARint &incy) { - if (dx != dy) F77NAME(zcopy)(&n, dx, &incx, dy, &incy); -} // copy (arcomplex) -#endif - -// DOT - -inline float dot(const ARint &n, const float dx[], const ARint &incx, - const float dy[], const ARint &incy) { - return F77NAME(sdot)(&n, dx, &incx, dy, &incy); -} // dot (float) - -inline double dot(const ARint &n, const double dx[], const ARint &incx, - const double dy[], const ARint &incy) { - return F77NAME(ddot)(&n, dx, &incx, dy, &incy); -} // dot (double) - -#ifdef ARCOMP_H -inline arcomplex dotc(const ARint &n, const arcomplex dx[], - const ARint &incx,const arcomplex dy[], - const ARint &incy) { - arcomplex tmp; - F77NAME(cdotc)(&tmp, &n, dx, &incx, dy, &incy); - return tmp; -} // dotc (arcomplex) - -inline arcomplex dotc(const ARint &n, const arcomplex dx[], - const ARint &incx, const arcomplex dy[], - const ARint &incy) { - arcomplex tmp; - F77NAME(zdotc)(&tmp, &n, dx, &incx, dy, &incy); - return tmp; -} // dotc (arcomplex) - -inline arcomplex dotu(const ARint &n, const arcomplex dx[], - const ARint &incx, const arcomplex dy[], - const ARint &incy) { - arcomplex tmp; - F77NAME(cdotu)(&tmp, &n, dx, &incx, dy, &incy); - return tmp; -} // dotu (arcomplex) - -inline arcomplex dotu(const ARint &n, const arcomplex dx[], - const ARint &incx, const arcomplex dy[], - const ARint &incy) { - arcomplex tmp; - F77NAME(zdotu)(&tmp, &n, dx, &incx, dy, &incy); - return tmp; -} // dotu (arcomplex) -#endif - -// NRM2 - -inline float nrm2(const ARint &n, const float dx[], const ARint &incx) { - return F77NAME(snrm2)(&n, dx, &incx); -} // nrm2 (float) - -inline double nrm2(const ARint &n, const double dx[], const ARint &incx) { - return F77NAME(dnrm2)(&n, dx, &incx); -} // nrm2 (double) - -#ifdef ARCOMP_H -inline float nrm2(const ARint &n, const arcomplex dx[], - const ARint &incx) { - return F77NAME(scnrm2)(&n, dx, &incx); -} // nrm2 (complex ) - -inline double nrm2(const ARint &n, const arcomplex dx[], - const ARint &incx) { - return F77NAME(dznrm2)(&n, dx, &incx); -} // nrm2 (complex ) -#endif - -// ROT - -inline void rot(const ARint &n, float dx[], const ARint &incx, float dy[], - const ARint &incy, const float &c, const float &s) { - F77NAME(srot)(&n, dx, &incx, dy, &incy, &c, &s); -} // rot (float) - -inline void rot(const ARint &n, double dx[], const ARint &incx, - double dy[], const ARint &incy, const double &c, - const double &s) { - F77NAME(drot)(&n, dx, &incx, dy, &incy, &c, &s); -} // rot (double) - -// ROTG - -inline void rotg(float &da, float &db, float &c, float &s) { - F77NAME(srotg)(&da, &db, &c, &s); -} // rotg (float) - -inline void rotg(double &da, double &db, double &c, double &s) { - F77NAME(drotg)(&da, &db, &c, &s); -} // rotg (double) - -// SCAL - -inline void scal(const ARint &n, float &da, float dx[], const ARint &incx) { - F77NAME(sscal)(&n, &da, dx, &incx); -} // scal (float) - -inline void scal(const ARint &n, double &da, double dx[], const ARint &incx) { - F77NAME(dscal)(&n, &da, dx, &incx); -} // scal (double) - -#ifdef ARCOMP_H -inline void scal(const ARint &n, const arcomplex &da, - arcomplex dx[], const ARint &incx) { - F77NAME(cscal)(&n, &da, dx, &incx); -} // scal (arcomplex) - -inline void scal(const ARint &n, const arcomplex &da, - arcomplex dx[], const ARint &incx) { - F77NAME(zscal)(&n, &da, dx, &incx); -} // scal (arcomplex) - -inline void sscal(const ARint &n, const float &da, arcomplex dx[], - const ARint &incx) { - F77NAME(csscal)(&n, &da, dx, &incx); -} // sscal (arcomplex) - -inline void sscal(const ARint &n, const double &da, arcomplex dx[], - const ARint &incx) { - F77NAME(zdscal)(&n, &da, dx, &incx); -} // sscal (arcomplex) -#endif - -// SWAP - -inline void swap(const ARint &n, float dx[], const ARint &incx, - float dy[], const ARint &incy) { - F77NAME(sswap)(&n, dx, &incx, dy, &incy); -} // swap (float) - -inline void swap(const ARint &n, double dx[], const ARint &incx, - double dy[], const ARint &incy) { - F77NAME(dswap)(&n, dx, &incx, dy, &incy); -} // swap (double) - -#ifdef ARCOMP_H -inline void swap(const ARint &n, arcomplex dx[], const ARint &incx, - arcomplex dy[], const ARint &incy) { - F77NAME(cswap)(&n, dx, &incx, dy, &incy); -} // swap (arcomplex) - -inline void swap(const ARint &n, arcomplex dx[], const ARint &incx, - arcomplex dy[], const ARint &incy) { - F77NAME(zswap)(&n, dx, &incx, dy, &incy); -} // swap (arcomplex) -#endif - -// AMAX - -inline ARint amax(const ARint &n, const float dx[], const ARint &incx) { - return F77NAME(isamax)(&n, dx, &incx); -} // amax (float) - -inline ARint amax(const ARint &n, const double dx[], const ARint &incx) { - return F77NAME(idamax)(&n, dx, &incx); -} // amax (double) - -#ifdef ARCOMP_H -inline ARint amax(const ARint &n, const arcomplex dx[], - const ARint &incx) { - return F77NAME(icamax)(&n, dx, &incx); -} // amax (arcomplex) - -inline ARint amax(const ARint &n, const arcomplex dx[], - const ARint &incx) { - return F77NAME(izamax)(&n, dx, &incx); -} // amax (arcomplex) -#endif - -// GEMV - -inline void gemv(const char* trans, const ARint &m, const ARint &n, - const float &alpha, const float a[], const ARint &lda, - const float x[], const ARint &incx, const float &beta, - float y[], const ARint &incy) { - F77NAME(sgemv)(trans, &m, &n, &alpha, a, &lda, - x, &incx, &beta, y, &incy); -} // gemv (float) - -inline void gemv(const char* trans, const ARint &m, const ARint &n, - const double &alpha, const double a[], const ARint &lda, - const double x[], const ARint &incx, const double &beta, - double y[], const ARint &incy) { - F77NAME(dgemv)(trans, &m, &n, &alpha, a, &lda, - x, &incx, &beta, y, &incy); -} // gemv (double) - -#ifdef ARCOMP_H -inline void gemv(const char* trans, const ARint &m, - const ARint &n, const arcomplex &alpha, - const arcomplex a[], const ARint &lda, - const arcomplex x[], const ARint &incx, - const arcomplex &beta, arcomplex y[], - const ARint &incy) { - F77NAME(cgemv)(trans, &m, &n, &alpha, a, &lda, - x, &incx, &beta, y, &incy); -} // gemv (arcomplex) - -inline void gemv(const char* trans, const ARint &m, - const ARint &n, const arcomplex &alpha, - const arcomplex a[], const ARint &lda, - const arcomplex x[], const ARint &incx, - const arcomplex &beta, arcomplex y[], - const ARint &incy) { - F77NAME(zgemv)(trans, &m, &n, &alpha, a, &lda, - x, &incx, &beta, y, &incy); -} // gemv (arcomplex) -#endif - -// GBMV - -inline void gbmv(const char* trans, const ARint &m, const ARint &n, - const ARint &kl, const ARint &ku, const float &alpha, - const float a[], const ARint &lda, const float x[], - const ARint &incx, const float &beta, float y[], - const ARint &incy) { - F77NAME(sgbmv)(trans, &m, &n, &kl, &ku, &alpha, a, &lda, - x, &incx, &beta, y, &incy); -} // gbmv (float) - -inline void gbmv(const char* trans, const ARint &m, const ARint &n, - const ARint &kl, const ARint &ku, const double &alpha, - const double a[], const ARint &lda, const double x[], - const ARint &incx, const double &beta, double y[], - const ARint &incy) { - F77NAME(dgbmv)(trans, &m, &n, &kl, &ku, &alpha, a, &lda, - x, &incx, &beta, y, &incy); -} // gbmv (double) - -#ifdef ARCOMP_H -inline void gbmv(const char* trans, const ARint &m, - const ARint &n, const ARint &kl, - const ARint &ku, const arcomplex &alpha, - const arcomplex a[], const ARint &lda, - const arcomplex x[], const ARint &incx, - const arcomplex &beta, arcomplex y[], - const ARint &incy) { - F77NAME(cgbmv)(trans, &m, &n, &kl, &ku, &alpha, a, &lda, - x, &incx, &beta, y, &incy); -} // gbmv (arcomplex) - -inline void gbmv(const char* trans, const ARint &m, - const ARint &n, const ARint &kl, - const ARint &ku, const arcomplex &alpha, - const arcomplex a[], const ARint &lda, - const arcomplex x[], const ARint &incx, - const arcomplex &beta, arcomplex y[], - const ARint &incy) { - F77NAME(zgbmv)(trans, &m, &n, &kl, &ku, &alpha, a, &lda, - x, &incx, &beta, y, &incy); -} // gbmv (arcomplex) -#endif - -// SBMV - -inline void sbmv(const char* uplo, const ARint &n, const ARint &k, - const float &alpha, const float a[], const ARint &lda, - const float x[], const ARint &incx, const float &beta, - float y[], const ARint &incy) { - F77NAME(ssbmv)(uplo, &n, &k, &alpha, a, &lda, x, &incx, &beta, y, &incy); -} // sbmv (float) - -inline void sbmv(const char* uplo, const ARint &n, const ARint &k, - const double &alpha, const double a[], const ARint &lda, - const double x[], const ARint &incx, const double &beta, - double y[], const ARint &incy) { - F77NAME(dsbmv)(uplo, &n, &k, &alpha, a, &lda, x, &incx, &beta, y, &incy); -} // sbmv (double) - - -#endif // BLAS1C_H diff --git a/src/external/arpack++/include/blas1f.h b/src/external/arpack++/include/blas1f.h deleted file mode 100644 index ad0dcaeb..00000000 --- a/src/external/arpack++/include/blas1f.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE blas1f.h - BLAS 1 and BLAS 2 FORTRAN routines. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef BLAS1F_H -#define BLAS1F_H - -#include "arch.h" - -extern "C" -{ - - // Single precision real routines. - - float F77NAME(sasum)(const ARint *n, const float *dx, const ARint *incx); - - void F77NAME(saxpy)(const ARint *n, const float *da, const float *dx, - const ARint *incx, float *dy, const ARint *incy); - - void F77NAME(scopy)(const ARint *n, const float *dx, const ARint *incx, - float *dy, const ARint *incy); - - float F77NAME(sdot)(const ARint *n, const float *dx, const ARint *incx, - const float *dy, const ARint *incy); - - float F77NAME(snrm2)(const ARint *n, const float *dx, const ARint *incx); - - void F77NAME(srot)(const ARint *n, float *dx, const ARint *incx, float *dy, - const ARint *incy, const float *c, const float *s); - - void F77NAME(srotg)(float *da, float *db, float *c, float *s); - - void F77NAME(sscal)(const ARint *n, float *da, float *dx, const ARint *incx); - - void F77NAME(sswap)(const ARint *n, float *dx, const ARint *incx, - float *dy, const ARint *incy); - - ARint F77NAME(isamax)(const ARint *n, const float *dx, const ARint *incx); - - void F77NAME(sgemv)(const char* trans, const ARint *m, const ARint *n, - const float *alpha, const float *a, const ARint *lda, - const float *x, const ARint *incx, const float *beta, - float *y, const ARint *incy); - - void F77NAME(sgbmv)(const char* trans, const ARint *m, const ARint *n, - const ARint *kl, const ARint *ku, const float *alpha, - const float *a, const ARint *lda, const float *x, - const ARint *incx, const float *beta, float *y, - const ARint *incy); - - void F77NAME(ssbmv)(const char* uplo, const ARint *n, const ARint *k, - const float *alpha, const float *a, const ARint *lda, - const float *x, const ARint *incx, const float *beta, - float *y, const ARint *incy); - -// Double precision real routines. - - double F77NAME(dasum)(const ARint *n, const double *dx, const ARint *incx); - - void F77NAME(daxpy)(const ARint *n, const double *da, const double *dx, - const ARint *incx, double *dy, const ARint *incy); - - void F77NAME(dcopy)(const ARint *n, const double *dx, const ARint *incx, - double *dy, const ARint *incy); - - double F77NAME(ddot)(const ARint *n, const double *dx, const ARint *incx, - const double *dy, const ARint *incy); - - double F77NAME(dnrm2)(const ARint *n, const double *dx, const ARint *incx); - - void F77NAME(drot)(const ARint *n, double *dx, const ARint *incx, double *dy, - const ARint *incy, const double *c, const double *s); - - void F77NAME(drotg)(double *da, double *db, double *c, double *s); - - void F77NAME(dscal)(const ARint *n, double *da, double *dx, const ARint *incx); - - void F77NAME(dswap)(const ARint *n, double *dx, const ARint *incx, - double *dy, const ARint *incy); - - ARint F77NAME(idamax)(const ARint *n, const double *dx, const ARint *incx); - - void F77NAME(dgemv)(const char* trans, const ARint *m, const ARint *n, - const double *alpha, const double *a, const ARint *lda, - const double *x, const ARint *incx, const double *beta, - double *y, const ARint *incy); - - void F77NAME(dgbmv)(const char* trans, const ARint *m, const ARint *n, - const ARint *kl, const ARint *ku, const double *alpha, - const double *a, const ARint *lda, const double *x, - const ARint *incx, const double *beta, double *y, - const ARint *incy); - - void F77NAME(dsbmv)(const char* uplo, const ARint *n, const ARint *k, - const double *alpha, const double *a, const ARint *lda, - const double *x, const ARint *incx, const double *beta, - double *y, const ARint *incy); - - // Single precision complex routines. - -#ifdef ARCOMP_H - - void F77NAME(cdotc)(arcomplex *c, const ARint *n, - const arcomplex *cx, const ARint *incx, - const arcomplex *cy, const ARint *incy); - - void F77NAME(cdotu)(arcomplex *c, const ARint *n, - const arcomplex *cx, const ARint *incx, - const arcomplex *cy, const ARint *incy); - - void F77NAME(caxpy)(const ARint *n, const arcomplex *da, - const arcomplex *dx, const ARint *incx, - arcomplex *dy, const ARint *incy); - - void F77NAME(ccopy)(const ARint *n, const arcomplex *dx, - const ARint *incx, arcomplex *dy, - const ARint *incy); - - float F77NAME(scasum)(const ARint *n, const arcomplex *dx, - const ARint *incx); - - float F77NAME(scnrm2)(const ARint *n, const arcomplex *dx, - const ARint *incx); - - void F77NAME(csscal)(const ARint *n, const float *da, arcomplex *dx, - const ARint *incx); - - void F77NAME(cscal)(const ARint *n, const arcomplex *da, - arcomplex *dx, const ARint *incx); - - ARint F77NAME(icamax)(const ARint *n, const arcomplex *dx, - const ARint *incx); - - void F77NAME(cswap)(const ARint *n, arcomplex *dx, - const ARint *incx, arcomplex *dy, - const ARint *incy); - - void F77NAME(cgemv)(const char* trans, const ARint *m, - const ARint *n, const arcomplex *alpha, - const arcomplex *a, const ARint *lda, - const arcomplex *x, const ARint *incx, - const arcomplex *beta, arcomplex *y, - const ARint *incy); - - void F77NAME(cgbmv)(const char* trans, const ARint *m, - const ARint *n, const ARint *kl, - const ARint *ku, const arcomplex *alpha, - const arcomplex *a, const ARint *lda, - const arcomplex *x, const ARint *incx, - const arcomplex *beta, arcomplex *y, - const ARint *incy); - - // Double precision complex routines. - - void F77NAME(zdotc)(arcomplex *c, const ARint *n, - const arcomplex *cx, const ARint *incx, - const arcomplex *cy, const ARint *incy); - - void F77NAME(zdotu)(arcomplex *c, const ARint *n, - const arcomplex *cx, const ARint *incx, - const arcomplex *cy, const ARint *incy); - - void F77NAME(zaxpy)(const ARint *n, const arcomplex *da, - const arcomplex *dx, const ARint *incx, - arcomplex *dy, const ARint *incy); - - void F77NAME(zcopy)(const ARint *n, const arcomplex *dx, - const ARint *incx, arcomplex *dy, - const ARint *incy); - - double F77NAME(dzasum)(const ARint *n, const arcomplex *dx, - const ARint *incx); - - double F77NAME(dznrm2)(const ARint *n, const arcomplex *dx, - const ARint *incx); - - void F77NAME(zdscal)(const ARint *n, const double *da, arcomplex *dx, - const ARint *incx); - - void F77NAME(zscal)(const ARint *n, const arcomplex *da, - arcomplex *dx, const ARint *incx); - - ARint F77NAME(izamax)(const ARint *n, const arcomplex *dx, - const ARint *incx); - - void F77NAME(zswap)(const ARint *n, arcomplex *dx, - const ARint *incx, arcomplex *dy, - const ARint *incy); - - void F77NAME(zgemv)(const char* trans, const ARint *m, - const ARint *n, const arcomplex *alpha, - const arcomplex *a, const ARint *lda, - const arcomplex *x, const ARint *incx, - const arcomplex *beta, arcomplex *y, - const ARint *incy); - - void F77NAME(zgbmv)(const char* trans, const ARint *m, - const ARint *n, const ARint *kl, - const ARint *ku, const arcomplex *alpha, - const arcomplex *a, const ARint *lda, - const arcomplex *x, const ARint *incx, - const arcomplex *beta, arcomplex *y, - const ARint *incy); - -#endif // ARCOMP_H - -} -#endif // BLAS1F_H - diff --git a/src/external/arpack++/include/caupp.h b/src/external/arpack++/include/caupp.h deleted file mode 100644 index 3bc97c28..00000000 --- a/src/external/arpack++/include/caupp.h +++ /dev/null @@ -1,320 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE caupp.h. - Interface to ARPACK subroutines znaupd and cnaupd. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef CAUPP_H -#define CAUPP_H - -#include -#include "arch.h" -#include "arpackf.h" - -inline void caupp(ARint& ido, char bmat, ARint n, const std::string& which, ARint nev, - double& tol, arcomplex resid[], ARint ncv, - arcomplex V[], ARint ldv, ARint iparam[], - ARint ipntr[], arcomplex workd[], - arcomplex workl[], ARint lworkl, double rwork[], - ARint& info) - -/* - c++ version of ARPACK routine znaupd that implements the - Reverse communication interface for the Implicitly Restarted Arnoldi - iteration. This is intended to be used to find a few eigenpairs of a - complex linear operator OP with respect to a semi-inner product defined - by a hermitian positive semi-definite real matrix B. B may be the - identity matrix. NOTE: if both OP and B are real, then naupp should - be used. - - The computed approximate eigenvalues are called Ritz values and - the corresponding approximate eigenvectors are called Ritz vectors. - - caupp is usually called iteratively to solve one of the - following problems: - - Mode 1: A*x = lambda*x. - ===> OP = A and B = I. - - Mode 2: A*x = lambda*M*x, M symmetric positive definite - ===> OP = inv[M]*A and B = M. - ===> (If M can be factored see remark 3 below) - - Mode 3: A*x = lambda*M*x, M symmetric semi-definite - ===> OP = inv[A - sigma*M]*M and B = M. - ===> shift-and-invert mode - If OP*x = amu*x, then lambda = sigma + 1/amu. - - - NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v - should be accomplished either by a direct method - using a sparse matrix factorization and solving - - [A - sigma*M]*w = v or M*w = v, - - or through an iterative method for solving these systems. If - an iterative method is used, the convergence test must be more - stringent than the accuracy requirements for the eigenvalue - approximations. - - Parameters: - - ido (Input / Output) Reverse communication flag. ido must be - zero on the first call to caupp. ido will be set - internally to indicate the type of operation to be - performed. Control is then given back to the calling - routine which has the responsibility to carry out the - requested operation and call caupp with the result. The - operand is given in workd[ipntr[1]], the result must be - put in workd[ipntr[2]]. - ido = 0: first call to the reverse communication interface. - ido = -1: compute Y = OP * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - This is for the initialization phase to force the - starting vector into the range of OP. - ido = 1: compute Y = OP * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - In mode 3 and 4, the vector B * X is already - available in workd[ipntr[3]]. It does not - need to be recomputed in forming OP * X. - ido = 2: compute Y = B * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - ido = 3: compute the iparam[8] real and imaginary parts - of the shifts where inptr[14] is the pointer - into workl for placing the shifts. See Remark - 5 below. - ido = 99: done. - bmat (Input) bmat specifies the type of the matrix B that defines - the semi-inner product for the operator OP. - bmat = 'I' -> standard eigenvalue problem A*x = lambda*x; - bmat = 'G' -> generalized eigenvalue problem A*x = lambda*M*x. - n (Input) Dimension of the eigenproblem. - nev (Input) Number of eigenvalues to be computed. 0 < nev <= n-1. - which (Input) Specify which of the Ritz values of OP to compute. - 'LM' - compute the nev eigenvalues of largest magnitude. - 'SM' - compute the nev eigenvalues of smallest magnitude. - 'LR' - compute the nev eigenvalues of largest real part. - 'SR' - compute the nev eigenvalues of smallest real part. - 'LI' - compute the nev eigenvalues of largest imaginary part. - 'SI' - compute the nev eigenvalues of smallest imaginary part. - tol (Input) Stopping criterion: the relative accuracy of the - Ritz value is considered acceptable if BOUNDS[i] <= - tol*abs(RITZ[i]),where ABS(RITZ[i]) is the magnitude when - RITZ[i] is complex. If tol<=0.0 is passed, the machine - precision as computed by the LAPACK auxiliary subroutine - _LAMCH is used. - resid (Input / Output) Array of length n. - On input: - If info==0, a random initial residual vector is used. - If info!=0, resid contains the initial residual vector, - possibly from a previous run. - On output: - resid contains the final residual vector. - ncv (Input) Number of Arnoldi vectors that are generated at each - iteration. After the startup phase in which nev Arnoldi - vectors are generated, the algorithm generates ncv-nev - Arnoldi vectors at each subsequent update iteration. Most of - the cost in generating each Arnoldi vector is in the - matrix-vector product OP*x. - NOTE: ncv must satisfy nev+1 <= ncv <= n. - V (Output) Array of length ncv*n+1. V contains the ncv Arnoldi - basis vectors. The first element V[0] is never referenced. - ldv (Input) Dimension of the basis vectors contained in V. This - parameter MUST be set to n. - iparam (Input / Output) Array of length 12. - iparam[1] = ISHIFT: method for selecting the implicit shifts. - The shifts selected at each iteration are used to restart - the Arnoldi iteration in an implicit fashion. - ------------------------------------------------------------- - ISHIFT = 0: the shifts are to be provided by the user via - reverse communication. The ncv eigenvalues of - the Hessenberg matrix H are returned in the part - of workl array corresponding to RITZ. - ISHIFT = 1: exact shifts with respect to the current - Hessenberg matrix H. This is equivalent to - restarting the iteration from the beginning - after updating the starting vector with a linear - combination of Ritz vectors associated with the - "wanted" eigenvalues. - ISHIFT = 2: other choice of internal shift to be defined. - ------------------------------------------------------------- - iparam[2] is no longer referenced. - iparam[3] = MXITER - On INPUT: maximum number of Arnoldi update iterations allowed. - On OUTPUT: actual number of Arnoldi update iterations taken. - iparam[4] = NB: blocksize to be used in the recurrence. - The code currently works only for NB = 1. - iparam[5] = NCONV: number of "converged" Ritz values. - This represents the number of Ritz values that satisfy - the convergence criterion. - iparam[6] is no longer referenced. - iparam[7] = MODE. On input determines what type of - eigenproblem is being solved. Must be 1, 2 or 3. - iparam[8] = NP. When ido = 3 and the user provides shifts - through reverse communication (iparam[1]=0), caupp returns - NP, the number of shifts the user is to provide. - 0 < NP <=ncv-nev. See Remark 5 below. - iparam[9] = total number of OP*x operations. - iparam[10] = total number of B*x operations if bmat='G'. - iparam[11] = total number of steps of re-orthogonalization. - ipntr (Output) Array of length 15. Pointer to mark the starting - locations in the workd and workl arrays for matrices/vectors - used by the Arnoldi iteration. - ipntr[1] : pointer to the current operand vector X in workd. - ipntr[2] : pointer to the current result vector Y in workd. - ipntr[3] : pointer to the vector B * X in workd when used in - the shift-and-invert mode. - ipntr[4] : pointer to the next available location in workl - that is untouched by the program. - ipntr[5] : pointer to the ncv by ncv upper Hessenberg matrix - H in workl. - ipntr[6] : pointer to the ritz value array RITZ. - ipntr[7] : pointer to the (projected) ritz vector array Q. - ipntr[8] : pointer to the error BOUNDS array in workl. - ipntr[14]: pointer to the NP shifts in workl. See Remark 5. - Note: ipntr[9:13] is only referenced by ceupp. See Remark 2. - ipntr[9] : pointer to the ncv RITZ values of the - original system. - ipntr[10]: Not Used - ipntr[11]: pointer to the ncv corresponding error bounds. - ipntr[12]: pointer to the ncv by ncv upper triangular - Schur matrix for H. - ipntr[13]: pointer to the ncv by ncv matrix of eigenvectors - of the upper Hessenberg matrix H. Only referenced by - ceupp if RVEC = true. See Remark 2 below. - workd (Input / Output) Array of length 3*n+1. - Distributed array to be used in the basic Arnoldi iteration - for reverse communication. The user should not use workd as - temporary workspace during the iteration. - workl (Output) Array of length lworkl+1. Private (replicated) array - on each PE or array allocated on the front end. - lworkl (Input) lworkl must be at least 3*ncv*ncv+5*ncv. - RWORK (Workspace) Array of length ncv. Private (replicated) array on - each PE or array allocated on the front end. - info (Input / Output) On input, if info = 0, a randomly initial - residual vector is used, otherwise resid contains the initial - residual vector, possibly from a previous run. - On output, info works as a error flag: - = 0 : Normal exit. - = 1 : Maximum number of iterations taken. All possible - eigenvalues of OP has been found. iparam[5] - returns the number of wanted converged Ritz values. - = 3 : No shifts could be applied during a cycle of the - Implicitly restarted Arnoldi iteration. One - possibility is to increase the size of ncv relative - to nev. See remark 4 below. - = -1 : n must be positive. - = -2 : nev must be positive. - = -3 : ncv must satisfy nev+1 <= ncv <= n. - = -4 : The maximum number of Arnoldi update iterations - allowed must be greater than zero. - = -5 : which must be one of 'LM','SM','LR','SR','LI','SI'. - = -6 : bmat must be one of 'I' or 'G'. - = -7 : Length of private work array is not sufficient. - = -8 : Error return from LAPACK eigenvalue calculation. - = -9 : Starting vector is zero. - = -10 : iparam[7] must be 1, 2 or 3. - = -11 : iparam[7] = 1 and bmat = 'G' are incompatible. - = -12 : iparam[1] must be equal to 0 or 1. - = -13 : nev and which = 'BE' are incompatible. - = -9999: Could not build an Arnoldi factorization. iparam[5] - returns the size of the current Arnoldi factorization. - The user is advised to check that enough workspace - and array storage has been allocated. - - Remarks: - 1. The computed Ritz values are approximate eigenvalues of OP. The - selection of "which" should be made with this in mind when using - Mode = 3. When operating in Mode = 3 setting which = 'LM' will - compute the nev eigenvalues of the original problem that are - closest to the shift sigma . After convergence, approximate - eigenvalues of the original problem may be obtained with the - ARPACK subroutine ceupp. - 2. If a basis for the invariant subspace corresponding to the converged - Ritz values is needed, the user must call ceupp immediately following - completion of caupp. This is new starting with release 2 of ARPACK. - 3. If M can be factored into a Cholesky factorization M = LL' - then Mode = 2 should not be selected. Instead one should use - Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular - linear systems should be solved with L and L' rather - than computing inverses. After convergence, an approximate - eigenvector z of the original problem is recovered by solving - L'z = x where x is a Ritz vector of OP. - 4. At present there is no a-priori analysis to guide the selection - of ncv relative to nev. The only formal requrement is that ncv - >= nev + 1. However, it is recommended that ncv >= 2*nev. If many - problems of the same type are to be solved, one should experiment - with increasing ncv while keeping nev fixed for a given test - problem. This will usually decrease the required number of OP*x - operations but it also increases the work and storage required to - maintain the orthogonal basis vectors. The optimal "cross-over" - with respect to CPU time is problem dependent and must be - determined empirically. - 5. When iparam[1] = 0, and ido = 3, the user needs to provide the - NP = iparam[8] complex shifts in locations - workl[ipntr[14]], workl[ipntr[14]+1], ... , workl[ipntr[14]+NP]. - Eigenvalues of the current upper Hessenberg matrix are located in - workl[ipntr[6]] through workl[ipntr[6]+ncv-1]. They are ordered - according to the order defined by "which". The associated Ritz - estimates are located in workl[ipntr[8]], workl[ipntr[8]+1], ..., - workl[ipntr[8]+ncv-1]. - - References: - 1. D.C. Sorensen, "Implicit Application of Polynomial Filters in - a k-Step Arnoldi Method", SIAM J. Matr. Anal. Apps., 13 (1992), - pp 357-385. - 2. R.B. Lehoucq, "Analysis and Implementation of an Implicitly - Restarted Arnoldi Iteration", Rice University Technical Report - TR95-13, Department of Computational and Applied Mathematics. - 3. B.N. Parlett & Y. Saad, "_Complex_ Shift and Invert Strategies for - Double precision Matrices", Linear Algebra and its Applications, - vol 88/89, pp 575-595, (1987). -*/ - -{ - - F77NAME(znaupd)(&ido, &bmat, &n, which.c_str(), &nev, &tol, resid, &ncv, - &V[1], &ldv, &iparam[1], &ipntr[1], &workd[1], - &workl[1], &lworkl, &rwork[1], &info); - -} // caupp (arcomplex). - -inline void caupp(ARint& ido, char bmat, ARint n, const std::string& which, ARint nev, - float& tol, arcomplex resid[], ARint ncv, - arcomplex V[], ARint ldv, ARint iparam[], - ARint ipntr[], arcomplex workd[], - arcomplex workl[], ARint lworkl, float rwork[], - ARint& info) - -/* - c++ version of ARPACK routine cnaupd. The only difference between - cnaupd and znaupd is that in the former function all vectors have - single precision elements and in the latter all vectors have double - precision elements. -*/ - -{ - - F77NAME(cnaupd)(&ido, &bmat, &n, which.c_str(), &nev, &tol, resid, &ncv, - &V[1], &ldv, &iparam[1], &ipntr[1], &workd[1], - &workl[1], &lworkl, &rwork[1], &info); - -} // caupp (arcomplex). - -#endif // CAUPP_H - - - diff --git a/src/external/arpack++/include/ceupp.h b/src/external/arpack++/include/ceupp.h deleted file mode 100644 index b8dc8865..00000000 --- a/src/external/arpack++/include/ceupp.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE ceupp.h. - Interface to ARPACK subroutines zneupd and cneupd. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef CEUPP_H -#define CEUPP_H - -#include -#include -#include "arch.h" -#include "arpackf.h" - -inline void ceupp(bool rvec, char HowMny, arcomplex d[], - arcomplex Z[], ARint ldz, arcomplex sigma, - arcomplex workev[], char bmat, ARint n, const std::string& which, - ARint nev, double tol, arcomplex resid[], ARint ncv, - arcomplex V[], ARint ldv, ARint iparam[], - ARint ipntr[], arcomplex workd[], - arcomplex workl[], ARint lworkl, double rwork[], - ARint& info) - -/* - c++ version of ARPACK routine zneupd. - This subroutine returns the converged approximations to eigenvalues - of A*z = lambda*B*z and (optionally): - - (1) the corresponding approximate eigenvectors, - (2) an orthonormal basis for the associated approximate - invariant subspace, - - There is negligible additional cost to obtain eigenvectors. An - orthonormal basis is always computed. There is an additional storage cost - of n*nev if both are requested (in this case a separate array Z must be - supplied). - The approximate eigenvalues and eigenvectors of A*z = lambda*B*z - are derived from approximate eigenvalues and eigenvectors of - of the linear operator OP prescribed by the MODE selection in the - call to caupp. caupp must be called before this routine is called. - These approximate eigenvalues and vectors are commonly called Ritz - values and Ritz vectors respectively. They are referred to as such - in the comments that follow. The computed orthonormal basis for the - invariant subspace corresponding to these Ritz values is referred to - as a Schur basis. - See documentation in the header of the subroutine caupp for - definition of OP as well as other terms and the relation of computed - Ritz values and Ritz vectors of OP with respect to the given problem - A*z = lambda*B*z. For a brief description, see definitions of - iparam[7], MODE and which in the documentation of caupp. - - Parameters: - - rvec (Input) Specifies whether a basis for the invariant subspace - corresponding to the converged Ritz value approximations for - the eigenproblem A*z = lambda*B*z is computed. - rvec = false: Compute Ritz values only. - rvec = true : Compute the Ritz vectors or Schur vectors. - See Remarks below. - HowMny (Input) Specifies the form of the basis for the invariant - subspace corresponding to the converged Ritz values that - is to be computed. - = 'A': Compute nev Ritz vectors; - = 'P': Compute nev Schur vectors; - d (Output) Array of dimension nev+1. D contains the Ritz - approximations to the eigenvalues lambda for A*z = lambda*B*z. - Z (Output) Array of dimension nev*n. If rvec = TRUE. and - HowMny = 'A', then Z contains approximate eigenvectors (Ritz - vectors) corresponding to the NCONV=iparam[5] Ritz values for - eigensystem A*z = lambda*B*z. - If rvec = .FALSE. or HowMny = 'P', then Z is not referenced. - NOTE: If if rvec = .TRUE. and a Schur basis is not required, - the array Z may be set equal to first nev+1 columns of - the Arnoldi basis array V computed by caupp. In this - case the Arnoldi basis will be destroyed and overwritten - with the eigenvector basis. - ldz (Input) Dimension of the vectors contained in Z. This - parameter MUST be set to n. - sigma (Input) If iparam[7] = 3, sigma represents the shift. Not - referenced if iparam[7] = 1 or 2. - workv (Workspace) Array of dimension 2*ncv. - V (Input/Output) Array of dimension n*ncv+1. - Upon Input: V contains the ncv vectors of the Arnoldi basis - for OP as constructed by caupp. - Upon Output: If rvec = TRUE the first NCONV=iparam[5] columns - contain approximate Schur vectors that span the - desired invariant subspace. - NOTE: If the array Z has been set equal to first nev+1 columns - of the array V and rvec = TRUE. and HowMny = 'A', then - the Arnoldi basis held by V has been overwritten by the - desired Ritz vectors. If a separate array Z has been - passed then the first NCONV=iparam[5] columns of V will - contain approximate Schur vectors that span the desired - invariant subspace. - workl (Input / Output) Array of length lworkl+1. - workl[1:ncv*ncv+3*ncv] contains information obtained in - caupp. They are not changed by ceupp. - workl[ncv*ncv+3*ncv+1:3*ncv*ncv+4*ncv] holds the untransformed - Ritz values, the untransformed error estimates of the Ritz - values, the upper triangular matrix for H, and the associated - matrix representation of the invariant subspace for H. - ipntr (Input / Output) Array of length 14. Pointer to mark the - starting locations in the workl array for matrices/vectors - used by caupp and ceupp. - ipntr[9]: pointer to the ncv RITZ values of the original - system. - ipntr[11]: pointer to the ncv corresponding error estimates. - ipntr[12]: pointer to the ncv by ncv upper triangular - Schur matrix for H. - ipntr[13]: pointer to the ncv by ncv matrix of eigenvectors - of the upper Hessenberg matrix H. Only referenced - by ceupp if rvec = TRUE. See Remark 2 below. - info (Output) Error flag. - = 0 : Normal exit. - = 1 : The Schur form computed by LAPACK routine csheqr - could not be reordered by LAPACK routine ztrsen. - Re-enter subroutine ceupp with iparam[5] = ncv and - increase the size of the array D to have - dimension at least dimension ncv and allocate at least - ncv columns for Z. NOTE: Not necessary if Z and V share - the same space. Please notify the authors if this error - occurs. - = -1 : n must be positive. - = -2 : nev must be positive. - = -3 : ncv must satisfy nev+1 <= ncv <= n. - = -5 : which must be one of 'LM','SM','LR','SR','LI','SI'. - = -6 : bmat must be one of 'I' or 'G'. - = -7 : Length of private work workl array is not sufficient. - = -8 : Error return from LAPACK eigenvalue calculation. - This should never happened. - = -9 : Error return from calculation of eigenvectors. - Informational error from LAPACK routine ztrevc. - = -10: iparam[7] must be 1, 2 or 3. - = -11: iparam[7] = 1 and bmat = 'G' are incompatible. - = -12: HowMny = 'S' not yet implemented. - = -13: HowMny must be one of 'A' or 'P' if rvec = TRUE. - = -14: caupp did not find any eigenvalues to sufficient - accuracy. - - NOTE: The following arguments - - bmat, n, which, nev, tol, resid, ncv, V, ldv, iparam, - ipntr, workd, workl, lworkl, rwork, info - - must be passed directly to ceupp following the last call - to caupp. These arguments MUST NOT BE MODIFIED between - the the last call to caupp and the call to ceupp. - - Remarks - 1. Currently only HowMny = 'A' and 'P' are implemented. - 2. Schur vectors are an orthogonal representation for the basis of - Ritz vectors. Thus, their numerical properties are often superior. - Let X' denote the transpose of X. If rvec = .TRUE. then the - relationship A * V[:,1:iparam[5]] = V[:,1:iparam[5]] * T, and - V[:,1:iparam[5]]' * V[:,1:iparam[5]] = I are approximately satisfied. - Here T is the leading submatrix of order iparam[5] of the real - upper quasi-triangular matrix stored workl[ipntr[12]]. -*/ - -{ - - ARint irvec; - ARlogical* iselect; - arcomplex* iZ; - - irvec = (ARint) rvec; - iselect = new ARlogical[ncv]; - iZ = (Z == NULL) ? &V[1] : Z; - - F77NAME(zneupd)(&irvec, &HowMny, iselect, d, iZ, &ldz, &sigma, - &workev[1], &bmat, &n, which.c_str(), &nev, &tol, resid, - &ncv, &V[1], &ldv, &iparam[1], &ipntr[1], - &workd[1], &workl[1], &lworkl, &rwork[1], &info); - - delete[] iselect; - -} // ceupp (arcomplex). - -inline void ceupp(bool rvec, char HowMny, arcomplex d[], - arcomplex Z[], ARint ldz, arcomplex sigma, - arcomplex workev[], char bmat, ARint n, const std::string& which, - ARint nev, float tol, arcomplex resid[], ARint ncv, - arcomplex V[], ARint ldv, ARint iparam[], - ARint ipntr[], arcomplex workd[], - arcomplex workl[], ARint lworkl, float rwork[], - ARint& info) - -/* - c++ version of ARPACK routine cneupd. The only difference between - cneupd and zneupd is that in the former function all vectors have - single precision elements and in the latter all vectors have double - precision elements. -*/ - -{ - - ARint irvec; - ARlogical* iselect; - arcomplex* iZ; - - irvec = (ARint) rvec; - iselect = new ARlogical[ncv]; - iZ = (Z == NULL) ? &V[1] : Z; - - F77NAME(cneupd)(&irvec, &HowMny, iselect, d, iZ, &ldz, &sigma, - &workev[1], &bmat, &n, which.c_str(), &nev, &tol, resid, - &ncv, &V[1], &ldv, &iparam[1], &ipntr[1], - &workd[1], &workl[1], &lworkl, &rwork[1], &info); - - delete[] iselect; - -} // ceupp (arcomplex). - -#endif // CEUPP_H diff --git a/src/external/arpack++/include/cholmodc.h b/src/external/arpack++/include/cholmodc.h deleted file mode 100644 index f1300cbb..00000000 --- a/src/external/arpack++/include/cholmodc.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE CHOLMODc.h. - Interface to CHOLMOD routines. - - Author of this class: - Martin Reuter - Date 11/05/2012 - - Arpack++ Author: - Francisco Gomes - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef CHOLMODC_H -#define CHOLMODC_H - -#include "cholmod.h" -#include - -inline void Write_Cholmod_Sparse_Matrix(const std::string & fname, - cholmod_sparse* A, cholmod_common *c) -{ - std::ofstream myfile; - myfile.open ( fname.c_str() ); - cholmod_triplet * T = cholmod_sparse_to_triplet(A,c); - //std::cout << " [ " << std::endl; - myfile.precision(20); - for (unsigned int i=0;innz;i++) - { - myfile << ((int*)T->i)[i]+1 << " " << ((int*)T->j)[i]+1 << " " << ((double*)T->x)[i] << std::endl; - } - //std::cout << " ] " << std::endl; - myfile.close(); - - cholmod_free_triplet(&T,c); - -} - -// Create_Cholmod_Sparse_Matrix -inline cholmod_sparse* Create_Cholmod_Sparse_Matrix(int m, int n, int nnz, - double* a, int* irow, int* pcol, char uplo, cholmod_common *c) -{ - - cholmod_sparse* A = new cholmod_sparse; - A->nrow = m; - A->ncol = n; - A->nzmax = nnz; - A->p = pcol; - A->i = irow; - A->nz = NULL; - A->x = a; - A->z = NULL; - if (uplo == 'L') A->stype = -1; - else A->stype = 1; - A->itype = CHOLMOD_INT; - A->xtype = CHOLMOD_REAL; // real - A->dtype = CHOLMOD_DOUBLE; // double - A->sorted = 0; - A->packed = 1; - - return A; - - -/* double* hd = new double[nnz]; - int* hi = new int[nnz]; - int* hp = new int[n+1]; - - int col,j; - int counter=0; - int counter2=0; - for (col=0;col= col) ||(uplo == 'U' && row <= col)) - { - hd[counter2] = a[counter]; - hi[counter2] = irow[counter]; - counter2++; - //std::cout << " In : " << std::flush; - } - //else std::cout << " Out : " << std::flush; - - //std::cout << row+1 << " " << col+1 << " " << a[counter] << std::endl; - counter++; - } - - } - hp[n] = counter2; - - - cholmod_sparse* A = new cholmod_sparse; - A->nrow = m; - A->ncol = n; - A->nzmax = counter2; - A->p = hp; - A->i = hi; - A->nz = NULL; - A->x = hd; - A->z = NULL; - if (uplo == 'L') A->stype = -1; - else A->stype = 1; - A->itype = CHOLMOD_INT; - A->xtype = CHOLMOD_REAL; // real - A->dtype = CHOLMOD_DOUBLE; // double - A->sorted = 0; - A->packed = 1; - - //cholmod_sparse* As = cholmod_copy_sparse(A,c); - - return A;*/ - -} // Create_Cholmod_Sparse_Matrix (double). - -// Create_Cholmod_Dense_Matrix (from Triplet) -inline cholmod_dense* Create_Cholmod_Dense_Matrix(int m, int n, - double* a, cholmod_common *c) -{ - - - cholmod_dense* A = new cholmod_dense; - A->nrow = m; - A->ncol = n; - A->nzmax = m*n; - A->d = m; - A->x = a; - A->z = NULL; - A->xtype = CHOLMOD_REAL; // real - A->dtype = CHOLMOD_DOUBLE; // double - -// cholmod_dense* As = cholmod_copy_dense(A,c); - - return A; - -} // Create_Cholmod_Dense_Matrix (double). - -// Create_Cholmod_Dense_Matrix (from Triplet) -inline void Get_Cholmod_Dense_Data(cholmod_dense* A, int n, double* a) -{ - memcpy(a,A->x,n*sizeof(double)); - -// for (int i = 0;ix)[i]; - -} // Create_Cholmod_Dense_Matrix (double). - - - -#endif // CHOLMODC_H diff --git a/src/external/arpack++/include/debug.h b/src/external/arpack++/include/debug.h deleted file mode 100644 index 2ceab437..00000000 --- a/src/external/arpack++/include/debug.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE debug.h. - Interface to ARPACK FORTRAN debugging facilities. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef DEBUG_H -#define DEBUG_H - -#include "arch.h" -#include "arpackf.h" - - -inline void TraceOff() - -/* - This function sets all ARPACK FORTRAN debug variables to zero. -*/ - -{ - - F77NAME(debug).logfil = 6; - F77NAME(debug).ndigit = 0; - F77NAME(debug).mgetv0 = 0; - F77NAME(debug).msaupd = 0; - F77NAME(debug).msaup2 = 0; - F77NAME(debug).msaitr = 0; - F77NAME(debug).mseigt = 0; - F77NAME(debug).msapps = 0; - F77NAME(debug).msgets = 0; - F77NAME(debug).mseupd = 0; - F77NAME(debug).mnaupd = 0; - F77NAME(debug).mnaup2 = 0; - F77NAME(debug).mnaitr = 0; - F77NAME(debug).mneigt = 0; - F77NAME(debug).mnapps = 0; - F77NAME(debug).mngets = 0; - F77NAME(debug).mneupd = 0; - F77NAME(debug).mcaupd = 0; - F77NAME(debug).mcaup2 = 0; - F77NAME(debug).mcaitr = 0; - F77NAME(debug).mceigt = 0; - F77NAME(debug).mcapps = 0; - F77NAME(debug).mcgets = 0; - F77NAME(debug).mceupd = 0; - -} // TraceOff. - - -inline void sTraceOn(const ARint digit, const ARint getv0, const ARint aupd, - const ARint aup2, const ARint aitr, const ARint eigt, - const ARint apps, const ARint gets, const ARint eupd) - -/* - This function sets the values of all ARPACK FORTRAN debug - variables corresponding to real symmetric eigenvalue problems. -*/ - -{ - - F77NAME(debug).logfil = 6; - F77NAME(debug).ndigit = digit; - F77NAME(debug).mgetv0 = getv0; - F77NAME(debug).msaupd = aupd; - F77NAME(debug).msaup2 = aup2; - F77NAME(debug).msaitr = aitr; - F77NAME(debug).mseigt = eigt; - F77NAME(debug).msapps = apps; - F77NAME(debug).msgets = gets; - F77NAME(debug).mseupd = eupd; - -} // sTraceOn. - - -inline void nTraceOn(const ARint digit, const ARint getv0, const ARint aupd, - const ARint aup2, const ARint aitr, const ARint eigt, - const ARint apps, const ARint gets, const ARint eupd) - -/* - This function sets the values of all ARPACK FORTRAN debug - variables corresponding to real nonsymmetric eigenvalue problems. -*/ - -{ - - F77NAME(debug).logfil = 6; - F77NAME(debug).ndigit = digit; - F77NAME(debug).mgetv0 = getv0; - F77NAME(debug).mnaupd = aupd; - F77NAME(debug).mnaup2 = aup2; - F77NAME(debug).mnaitr = aitr; - F77NAME(debug).mneigt = eigt; - F77NAME(debug).mnapps = apps; - F77NAME(debug).mngets = gets; - F77NAME(debug).mneupd = eupd; - -} // nTraceOn. - - -inline void cTraceOn(const ARint digit, const ARint getv0, const ARint aupd, - const ARint aup2, const ARint aitr, const ARint eigt, - const ARint apps, const ARint gets, const ARint eupd) - -/* - This function sets the values of all ARPACK FORTRAN debug - variables corresponding to complex eigenvalue problems. -*/ - -{ - - F77NAME(debug).logfil = 6; - F77NAME(debug).ndigit = digit; - F77NAME(debug).mgetv0 = getv0; - F77NAME(debug).mcaupd = aupd; - F77NAME(debug).mcaup2 = aup2; - F77NAME(debug).mcaitr = aitr; - F77NAME(debug).mceigt = eigt; - F77NAME(debug).mcapps = apps; - F77NAME(debug).mcgets = gets; - F77NAME(debug).mceupd = eupd; - -} // cTraceOn. - - -#endif // DEBUG_H diff --git a/src/external/arpack++/include/lapackc.h b/src/external/arpack++/include/lapackc.h deleted file mode 100644 index 47dd05bc..00000000 --- a/src/external/arpack++/include/lapackc.h +++ /dev/null @@ -1,306 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE lapackc.h. - Interface to LAPACK FORTRAN routines. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#include "arch.h" -#include "lapackf.h" - -#ifndef LAPACKC_H -#define LAPACKC_H - - -// LAPY2 - -inline float lapy2(const float &x, const float &y) { - return F77NAME(slapy2)(&x, &y); -} // lapy2 (float) - -inline double lapy2(const double &x, const double &y) { - return F77NAME(dlapy2)(&x, &y); -} // lapy2 (double) - - -// LACPY - -inline void lacpy(const char* uplo, const ARint &m, const ARint &n, - const float a[], const ARint &lda, float b[], - const ARint &ldb) { - F77NAME(slacpy)(uplo, &m, &n, a, &lda, b, &ldb); -} // lacpy (float) - -inline void lacpy(const char* uplo, const ARint &m, const ARint &n, - const double a[], const ARint &lda, double b[], - const ARint &ldb) { - F77NAME(dlacpy)(uplo, &m, &n, a, &lda, b, &ldb); -} // lacpy (double) - -#ifdef ARCOMP_H -inline void lacpy(const char* uplo, const ARint &m, const ARint &n, - const arcomplex a[], const ARint &lda, - arcomplex b[], const ARint &ldb) { - F77NAME(clacpy)(uplo, &m, &n, a, &lda, b, &ldb); -} // lacpy (arcomplex) - -inline void lacpy(const char* uplo, const ARint &m, const ARint &n, - const arcomplex a[], const ARint &lda, - arcomplex b[], const ARint &ldb) { - F77NAME(zlacpy)(uplo, &m, &n, a, &lda, b, &ldb); -} // lacpy (arcomplex) -#endif - - -// GTTRF - -inline void gttrf(const ARint &n, float dl[], float d[], float du[], - float du2[], ARint ipiv[], ARint &info) { - F77NAME(sgttrf)(&n, dl, d, du, du2, ipiv, &info); -} // gttrf (float) - -inline void gttrf(const ARint &n, double dl[], double d[], double du[], - double du2[], ARint ipiv[], ARint &info) { - F77NAME(dgttrf)(&n, dl, d, du, du2, ipiv, &info); -} // gttrf (double) - -#ifdef ARCOMP_H -inline void gttrf(const ARint &n, arcomplex dl[], arcomplex d[], - arcomplex du[], arcomplex du2[], ARint ipiv[], - ARint &info) { - F77NAME(cgttrf)(&n, dl, d, du, du2, ipiv, &info); -} // gttrf (arcomplex) - -inline void gttrf(const ARint &n, arcomplex dl[], arcomplex d[], - arcomplex du[], arcomplex du2[], ARint ipiv[], - ARint &info) { - F77NAME(zgttrf)(&n, dl, d, du, du2, ipiv, &info); -} // gttrf (arcomplex) -#endif - - -// GTTRS - -inline void gttrs(const char* trans, const ARint &n, const ARint &nrhs, - const float dl[], const float d[], const float du[], - const float du2[], const ARint ipiv[], float b[], - const ARint &ldb, ARint &info) { - F77NAME(sgttrs)(trans, &n, &nrhs, dl, d, du, du2, ipiv, b, &ldb, &info); -} // gttrs (float) - -inline void gttrs(const char* trans, const ARint &n, const ARint &nrhs, - const double dl[], const double d[], const double du[], - const double du2[], const ARint ipiv[], double b[], - const ARint &ldb, ARint &info) { - F77NAME(dgttrs)(trans, &n, &nrhs, dl, d, du, du2, ipiv, b, &ldb, &info); -} // gttrs (double) - -#ifdef ARCOMP_H - -inline void gttrs(const char* trans, const ARint &n, const ARint &nrhs, - const arcomplex dl[], const arcomplex d[], - const arcomplex du[], const arcomplex du2[], - const ARint ipiv[], arcomplex b[], - const ARint &ldb, ARint &info) { - F77NAME(cgttrs)(trans, &n, &nrhs, dl, d, du, du2, ipiv, b, &ldb, &info); -} // gttrs (arcomplex) - -inline void gttrs(const char* trans, const ARint &n, const ARint &nrhs, - const arcomplex dl[], const arcomplex d[], - const arcomplex du[], const arcomplex du2[], - const ARint ipiv[], arcomplex b[], - const ARint &ldb, ARint &info) { - F77NAME(zgttrs)(trans, &n, &nrhs, dl, d, du, du2, ipiv, b, &ldb, &info); -} // gttrs (arcomplex) -#endif - - -// GBTRF - -inline void gbtrf(const ARint &m, const ARint &n, const ARint &kl, - const ARint &ku, float ab[], const ARint &ldab, - ARint ipiv[], ARint &info) { - F77NAME(sgbtrf)(&m, &n, &kl, &ku, ab, &ldab, ipiv, &info); -} // gbtrf (float) - -inline void gbtrf(const ARint &m, const ARint &n, const ARint &kl, - const ARint &ku, double ab[], const ARint &ldab, - ARint ipiv[], ARint &info) { - F77NAME(dgbtrf)(&m, &n, &kl, &ku, ab, &ldab, ipiv, &info); -} // gbtrf (double) - -#ifdef ARCOMP_H -inline void gbtrf(const ARint &m, const ARint &n, const ARint &kl, - const ARint &ku, arcomplex ab[], - const ARint &ldab, ARint ipiv[], ARint &info) { - F77NAME(cgbtrf)(&m, &n, &kl, &ku, ab, &ldab, ipiv, &info); -} // gbtrf (arcomplex) - -inline void gbtrf(const ARint &m, const ARint &n, const ARint &kl, - const ARint &ku, arcomplex ab[], - const ARint &ldab, ARint ipiv[], ARint &info) { - F77NAME(zgbtrf)(&m, &n, &kl, &ku, ab, &ldab, ipiv, &info); -} // gbtrf (arcomplex) -#endif - - -// GBTRS - -inline void gbtrs(const char* trans, const ARint &n, const ARint &kl, - const ARint &ku, const ARint &nrhs, const float ab[], - const ARint &ldab, const ARint ipiv[], float b[], - const ARint &ldb, ARint &info) { - F77NAME(sgbtrs)(trans, &n, &kl, &ku, &nrhs, ab, &ldab, ipiv, b, &ldb, &info); -} // gbtrs (float) - -inline void gbtrs(const char* trans, const ARint &n, const ARint &kl, - const ARint &ku, const ARint &nrhs, const double ab[], - const ARint &ldab, const ARint ipiv[], double b[], - const ARint &ldb, ARint &info) { - F77NAME(dgbtrs)(trans, &n, &kl, &ku, &nrhs, ab, &ldab, ipiv, b, &ldb, &info); -} // gbtrs (double) - -#ifdef ARCOMP_H -inline void gbtrs(const char* trans, const ARint &n, const ARint &kl, - const ARint &ku, const ARint &nrhs, - const arcomplex ab[], const ARint &ldab, - const ARint ipiv[], arcomplex b[], - const ARint &ldb, ARint &info) { - F77NAME(cgbtrs)(trans, &n, &kl, &ku, &nrhs, ab, &ldab, ipiv, b, &ldb, &info); -} // gbtrs (arcomplex) - -inline void gbtrs(const char* trans, const ARint &n, const ARint &kl, - const ARint &ku, const ARint &nrhs, - const arcomplex ab[], const ARint &ldab, - const ARint ipiv[], arcomplex b[], - const ARint &ldb, ARint &info) { - F77NAME(zgbtrs)(trans, &n, &kl, &ku, &nrhs, ab, &ldab, ipiv, b, &ldb, &info); -} // gbtrs (arcomplex) -#endif - - -// GETRF - -inline void getrf(const ARint &m, const ARint &n, float A[], - const ARint &lda, ARint ipiv[], ARint &info) { - F77NAME(sgetrf)(&m, &n, A, &lda, ipiv, &info); -} // getrf (float) - -inline void getrf(const ARint &m, const ARint &n, double A[], - const ARint &lda, ARint ipiv[], ARint &info) { - F77NAME(dgetrf)(&m, &n, A, &lda, ipiv, &info); -} // getrf (double) - -#ifdef ARCOMP_H -inline void getrf(const ARint &m, const ARint &n, arcomplex A[], - const ARint &lda, ARint ipiv[], ARint &info) { - F77NAME(cgetrf)(&m, &n, A, &lda, ipiv, &info); -} // getrf (arcomplex) - -inline void getrf(const ARint &m, const ARint &n, arcomplex A[], - const ARint &lda, ARint ipiv[], ARint &info) { - F77NAME(zgetrf)(&m, &n, A, &lda, ipiv, &info); -} // getrf (arcomplex) -#endif - -// GETRS - -inline void getrs(const char* trans, const ARint &n, const ARint &nrhs, - const float A[], const ARint &lda, const ARint ipiv[], - float b[], const ARint &ldb, ARint &info) { - F77NAME(sgetrs)(trans, &n, &nrhs, A, &lda, ipiv, b, &ldb, &info); -} // getrs (float) - -inline void getrs(const char* trans, const ARint &n, const ARint &nrhs, - const double A[], const ARint &lda, const ARint ipiv[], - double b[], const ARint &ldb, ARint &info) { - F77NAME(dgetrs)(trans, &n, &nrhs, A, &lda, ipiv, b, &ldb, &info); -} // getrs (double) - -#ifdef ARCOMP_H -inline void getrs(const char* trans, const ARint &n, const ARint &nrhs, - const arcomplex A[], const ARint &lda, - const ARint ipiv[], arcomplex b[], - const ARint &ldb, ARint &info) { - F77NAME(cgetrs)(trans, &n, &nrhs, A, &lda, ipiv, b, &ldb, &info); -} // getrs (arcomplex) - -inline void getrs(const char* trans, const ARint &n, const ARint &nrhs, - const arcomplex A[], const ARint &lda, - const ARint ipiv[], arcomplex b[], - const ARint &ldb, ARint &info) { - F77NAME(zgetrs)(trans, &n, &nrhs, A, &lda, ipiv, b, &ldb, &info); -} // getrs (arcomplex) -#endif - -// PTTRF - -inline void pttrf(const ARint &n, float d[], float e[], ARint &info) { - F77NAME(spttrf)(&n, d, e, &info); -} // pttrf (float) - -inline void pttrf(const ARint &n, double d[], double e[], ARint &info) { - F77NAME(dpttrf)(&n, d, e, &info); -} // pttrf (double) - -// PTTRS - -inline void pttrs(const ARint &n, const ARint &nrhs, - const float d[], const float e[], float b[], - const ARint &ldb, ARint &info) { - F77NAME(spttrs)(&n, &nrhs, d, e, b, &ldb, &info); -} // pttrs (float) - -inline void pttrs(const ARint &n, const ARint &nrhs, - const double d[], const double e[], double b[], - const ARint &ldb, ARint &info) { - F77NAME(dpttrs)(&n, &nrhs, d, e, b, &ldb, &info); -} // pttrs (double) - - -// SPTRF - -inline void sptrf(const char* trans, const ARint &n, float ap[], - ARint ipiv[], ARint &info) { - F77NAME(ssptrf)(trans, &n, ap, ipiv, &info); -} // sptrf (float) - -inline void sptrf(const char* trans, const ARint &n, double ap[], - ARint ipiv[], ARint &info) { - F77NAME(dsptrf)(trans, &n, ap, ipiv, &info); -} // sptrf (double) - - -// SPTRS - -inline void sptrs(const char* trans, const ARint &n, const ARint &nrhs, - float ap[], ARint ipiv[], float b[], - const ARint &ldb, ARint &info) { - F77NAME(ssptrs)(trans, &n, &nrhs, ap, ipiv, b, &ldb, &info); -} // sptrs (float) - -inline void sptrs(const char* trans, const ARint &n, const ARint &nrhs, - double ap[], ARint ipiv[], double b[], - const ARint &ldb, ARint &info) { - F77NAME(dsptrs)(trans, &n, &nrhs, ap, ipiv, b, &ldb, &info); -} // sptrs (double) - - -inline void second(const float &t) { - F77NAME(second)(&t); -} - -#endif // LAPACKC_H - - - - diff --git a/src/external/arpack++/include/lapackf.h b/src/external/arpack++/include/lapackf.h deleted file mode 100644 index 2d6209b0..00000000 --- a/src/external/arpack++/include/lapackf.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE lapackf.h. - Interface to LAPACK FORTRAN routines. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef LAPACKF_H -#define LAPACKF_H - -#include "arch.h" - -extern "C" -{ - - // Single precision real routines. - - float F77NAME(slapy2)(const float *x, const float *y); - - void F77NAME(slacpy)(const char* uplo, const ARint *m, const ARint *n, - const float *a, const ARint *lda, float *b, - const ARint *ldb); - - void F77NAME(sgttrf)(const ARint *n, float *dl, float *d, float *du, - float *du2, ARint *ipiv, ARint *info); - - void F77NAME(sgbtrf)(const ARint *m, const ARint *n, const ARint *kl, - const ARint *ku, float *ab, const ARint *ldab, - ARint *ipiv, ARint *info); - - void F77NAME(sgetrf)(const ARint *m, const ARint *n, float *A, - const ARint *lda, ARint *ipiv, ARint *info); - - void F77NAME(sgttrs)(const char* trans, const ARint *n, - const ARint *nrhs, const float *dl, - const float *d, const float *du, - const float *du2, const ARint *ipiv, - float* b, const ARint *ldb, ARint *info); - - void F77NAME(sgbtrs)(const char* trans, const ARint *n, - const ARint *kl, const ARint *ku, - const ARint *nrhs, const float *ab, - const ARint *ldab, const ARint *ipiv, - float *b, const ARint *ldb, ARint *info); - - void F77NAME(sgetrs)(const char* trans, const ARint *n, - const ARint *nrhs, const float *A, - const ARint *lda, const ARint *ipiv, - float* b, const ARint *ldb, ARint *info); - - void F77NAME(spttrf)(const ARint *n, float *d, float *e, ARint *info); - - void F77NAME(spttrs)(const ARint *n, const ARint *nrhs, - const float *d, const float *e, float *b, - const ARint *ldb, ARint *info); - - void F77NAME(ssptrf)(const char* trans, const ARint *n, - float *ap, ARint *ipiv, ARint *info); - - void F77NAME(ssptrs)(const char* trans, const ARint *n, - const ARint *nrhs, float *ap, ARint *ipiv, - float *b, const ARint *ldb, ARint *info); - - // Double precision real routines. - - double F77NAME(dlapy2)(const double *x, const double *y); - - void F77NAME(dlacpy)(const char* uplo, const ARint *m, const ARint *n, - const double *a, const ARint *lda, double *b, - const ARint *ldb); - - void F77NAME(dgttrf)(const ARint *n, double *dl, double *d, double *du, - double *du2, ARint *ipiv, ARint *info); - - void F77NAME(dgbtrf)(const ARint *m, const ARint *n, const ARint *kl, - const ARint *ku, double *ab, const ARint *ldab, - ARint *ipiv, ARint *info); - - void F77NAME(dgetrf)(const ARint *m, const ARint *n, double *A, - const ARint *lda, ARint *ipiv, ARint *info); - - void F77NAME(dgttrs)(const char* trans, const ARint *n, - const ARint *nrhs, const double *dl, - const double *d, const double *du, - const double *du2, const ARint *ipiv, - double* b, const ARint *ldb, ARint *info); - - void F77NAME(dgbtrs)(const char* trans, const ARint *n, - const ARint *kl, const ARint *ku, - const ARint *nrhs, const double *ab, - const ARint *ldab, const ARint *ipiv, - double *b, const ARint *ldb, ARint *info); - - void F77NAME(dgetrs)(const char* trans, const ARint *n, - const ARint *nrhs, const double *A, - const ARint *lda, const ARint *ipiv, - double* b, const ARint *ldb, ARint *info); - - void F77NAME(dpttrf)(const ARint *n, double *d, double *e, ARint *info); - - void F77NAME(dpttrs)(const ARint *n, const ARint *nrhs, - const double *d, const double *e, double *b, - const ARint *ldb, ARint *info); - - void F77NAME(dsptrf)(const char* trans, const ARint *n, - double *ap, ARint *ipiv, ARint *info); - - void F77NAME(dsptrs)(const char* trans, const ARint *n, - const ARint *nrhs, double *ap, ARint *ipiv, - double *b, const ARint *ldb, ARint *info); - -#ifdef ARCOMP_H - - // Single precision complex routines. - - void F77NAME(clacpy)(const char* uplo, const ARint *m, const ARint *n, - const arcomplex *a, const ARint *lda, - arcomplex *b, const ARint *ldb); - - void F77NAME(cgttrf)(const ARint *n, arcomplex *dl, - arcomplex *d, arcomplex *du, - arcomplex *du2, ARint *ipiv, - ARint *info); - - void F77NAME(cgbtrf)(const ARint *m, const ARint *n, const ARint *kl, - const ARint *ku, arcomplex *ab, - const ARint *ldab, ARint *ipiv, ARint *info); - - void F77NAME(cgetrf)(const ARint *m, const ARint *n, arcomplex *A, - const ARint *lda, ARint *ipiv, ARint *info); - - void F77NAME(cgttrs)(const char *trans, const ARint *n, - const ARint *nrhs, const arcomplex *dl, - const arcomplex *d, const arcomplex *du, - const arcomplex *du2, const ARint *ipiv, - arcomplex* b, const ARint *ldb, - ARint *info); - - void F77NAME(cgbtrs)(const char* trans, const ARint *n, - const ARint *kl, const ARint *ku, - const ARint *nrhs, const arcomplex *ab, - const ARint *ldab, const ARint *ipiv, - arcomplex *b, const ARint *ldb, - ARint *info); - - void F77NAME(cgetrs)(const char* trans, const ARint *n, - const ARint *nrhs, const arcomplex *A, - const ARint *lda, const ARint *ipiv, - arcomplex* b, const ARint *ldb, ARint *info); - - // Double precision complex routines. - - void F77NAME(zlacpy)(const char* uplo, const ARint *m, const ARint *n, - const arcomplex *a, const ARint *lda, - arcomplex *b, const ARint *ldb); - - void F77NAME(zgttrf)(const ARint *n, arcomplex *dl, - arcomplex *d, arcomplex *du, - arcomplex *du2, ARint *ipiv, - ARint *info); - - void F77NAME(zgbtrf)(const ARint *m, const ARint *n, const ARint *kl, - const ARint *ku, arcomplex *ab, - const ARint *ldab, ARint *ipiv, ARint *info); - - void F77NAME(zgetrf)(const ARint *m, const ARint *n, arcomplex *A, - const ARint *lda, ARint *ipiv, ARint *info); - - void F77NAME(zgttrs)(const char *trans, const ARint *n, - const ARint *nrhs, const arcomplex *dl, - const arcomplex *d, const arcomplex *du, - const arcomplex *du2, const ARint *ipiv, - arcomplex* b, const ARint *ldb, - ARint *info); - - void F77NAME(zgbtrs)(const char* trans, const ARint *n, - const ARint *kl, const ARint *ku, - const ARint *nrhs, const arcomplex *ab, - const ARint *ldab, const ARint *ipiv, - arcomplex *b, const ARint *ldb, - ARint *info); - - void F77NAME(zgetrs)(const char* trans, const ARint *n, - const ARint *nrhs, const arcomplex *A, - const ARint *lda, const ARint *ipiv, - arcomplex* b, const ARint *ldb, ARint *info); - -#endif // ARCOMP_H - - void F77NAME(second)(const float *T); - -} -#endif // LAPACKF_H - - - - - diff --git a/src/external/arpack++/include/naupp.h b/src/external/arpack++/include/naupp.h deleted file mode 100644 index d29b258c..00000000 --- a/src/external/arpack++/include/naupp.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE naupp.h. - Interface to ARPACK subroutines dnaupd and snaupd. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef NAUPP_H -#define NAUPP_H - -#include -#include "arch.h" -#include "arpackf.h" - -inline void naupp(ARint& ido, char bmat, ARint n, const std::string& which, ARint nev, - double& tol, double resid[], ARint ncv, double V[], - ARint ldv, ARint iparam[], ARint ipntr[], double workd[], - double workl[], ARint lworkl, ARint& info) - -/* - c++ version of ARPACK routine dnaupd that implements a variant of - the Arnoldi method. This routine computes approximations to a few - eigenpairs of a linear operator "OP" with respect to a semi-inner - product defined by a symmetric positive semi-definite real matrix - B. B may be the identity matrix. NOTE: If the linear operator "OP" - is real and symmetric with respect to the real positive semi-definite - symmetric matrix B, i.e. B*OP = (OP')*B, then subroutine saupp - should be used instead. - - The computed approximate eigenvalues are called Ritz values and - the corresponding approximate eigenvectors are called Ritz vectors. - - naupp is usually called iteratively to solve one of the - following problems: - - Mode 1: A*x = lambda*x. - ===> OP = A and B = I. - - Mode 2: A*x = lambda*M*x, M symmetric positive definite - ===> OP = inv[M]*A and B = M. - ===> (If M can be factored see remark 3 below) - - Mode 3: A*x = lambda*M*x, M symmetric semi-definite - ===> OP = Real_Part{ inv[A - sigma*M]*M } and B = M. - ===> shift-and-invert mode (in real arithmetic) - If OP*x = amu*x, then - amu = 1/2 * [ 1/(lambda-sigma) + 1/(lambda-conjg(sigma)) ]. - Note: If sigma is real, i.e. imaginary part of sigma is zero; - Real_Part{ inv[A - sigma*M]*M } == inv[A - sigma*M]*M - amu == 1/(lambda-sigma). - - Mode 4: A*x = lambda*M*x, M symmetric semi-definite - ===> OP = Imaginary_Part{ inv[A - sigma*M]*M } and B = M. - ===> shift-and-invert mode (in real arithmetic) - If OP*x = amu*x, then - amu = 1/2i * [ 1/(lambda-sigma) - 1/(lambda-conjg(sigma)) ]. - - Both mode 3 and 4 give the same enhancement to eigenvalues close to - the (complex) shift sigma. However, as lambda goes to infinity, - the operator OP in mode 4 dampens the eigenvalues more strongly than - does OP defined in mode 3. - - NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v should - be accomplished either by a direct method using a sparse matrix - factorization and solving - - [A - sigma*M]*w = v or M*w = v, - - or through an iterative method for solving these systems. If an - iterative method is used, the convergence test must be more - stringent than the accuracy requirements for the eigenvalue - approximations. - - Parameters: - - ido (Input / Output) Reverse communication flag. ido must be - zero on the first call to naupp. ido will be set - internally to indicate the type of operation to be - performed. Control is then given back to the calling - routine which has the responsibility to carry out the - requested operation and call naupp with the result. The - operand is given in workd[ipntr[1]], the result must be - put in workd[ipntr[2]]. - ido = 0: first call to the reverse communication interface. - ido = -1: compute Y = OP * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - This is for the initialization phase to force the - starting vector into the range of OP. - ido = 1: compute Y = OP * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - In mode 3 and 4, the vector B * X is already - available in workd[ipntr[3]]. It does not - need to be recomputed in forming OP * X. - ido = 2: compute Y = B * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - ido = 3: compute the iparam[8] real and imaginary parts - of the shifts where inptr[14] is the pointer - into workl for placing the shifts. See Remark - 5 below. - ido = 99: done. - bmat (Input) bmat specifies the type of the matrix B that defines - the semi-inner product for the operator OP. - bmat = 'I' -> standard eigenvalue problem A*x = lambda*x; - bmat = 'G' -> generalized eigenvalue problem A*x = lambda*M*x. - n (Input) Dimension of the eigenproblem. - nev (Input) Number of eigenvalues to be computed. 0 < nev < n-1. - which (Input) Specify which of the Ritz values of OP to compute. - 'LM' - compute the NEV eigenvalues of largest magnitude. - 'SM' - compute the NEV eigenvalues of smallest magnitude. - 'LR' - compute the NEV eigenvalues of largest real part. - 'SR' - compute the NEV eigenvalues of smallest real part. - 'LI' - compute the NEV eigenvalues of largest imaginary part. - 'SI' - compute the NEV eigenvalues of smallest imaginary part. - tol (Input) Stopping criterion: the relative accuracy of the - Ritz value is considered acceptable if BOUNDS[i] <= - tol*abs(RITZ[i]),where ABS(RITZ[i]) is the magnitude when - RITZ[i] is complex. If tol<=0.0 is passed, the machine - precision as computed by the LAPACK auxiliary subroutine - _LAMCH is used. - resid (Input / Output) Array of length n. - On input: - If info==0, a random initial residual vector is used. - If info!=0, resid contains the initial residual vector, - possibly from a previous run. - On output: - resid contains the final residual vector. - ncv (Input) Number of Arnoldi vectors that are generated at each - iteration. After the startup phase in which nev Arnoldi - vectors are generated, the algorithm generates ncv-nev - Arnoldi vectors at each subsequent update iteration. Most of - the cost in generating each Arnoldi vector is in the - matrix-vector product OP*x. - NOTE: 2 <= NCV-NEV in order that complex conjugate pairs of - Ritz values are kept together (see remark 4 below). - V (Output) Double precision array of length ncv*n+1. V contains - the ncv Arnoldi basis vectors. The first element V[0] is never - referenced. - ldv (Input) Dimension of the basis vectors contianed in V. This - parameter MUST be set to n. - iparam (Input / Output) Array of length 12. - iparam[1] = ISHIFT: method for selecting the implicit shifts. - The shifts selected at each iteration are used to restart - the Arnoldi iteration in an implicit fashion. - ------------------------------------------------------------- - ISHIFT = 0: the shifts are provided by the user via - reverse communication. The real and imaginary - parts of the NCV eigenvalues of the Hessenberg - matrix H are returned in the part of the WORKL - array corresponding to RITZR and RITZI. See remark - 5 below. - ISHIFT = 1: exact shifts with respect to the current - Hessenberg matrix H. This is equivalent to - restarting the iteration with a starting vector - that is a linear combination of approximate Schur - vectors associated with the "wanted" Ritz values. - ------------------------------------------------------------- - iparam[2] is no longer referenced. - iparam[3] = MXITER - On INPUT: maximum number of Arnoldi update iterations allowed. - On OUTPUT: actual number of Arnoldi update iterations taken. - iparam[4] = NB: blocksize to be used in the recurrence. - The code currently works only for NB = 1. - iparam[5] = NCONV: number of "converged" Ritz values. - This represents the number of Ritz values that satisfy - the convergence criterion. - iparam[6] is no longer referenced. - iparam[7] = MODE. On INPUT determines what type of - eigenproblem is being solved. Must be 1,2,3,4. - iparam[8] = NP. When ido = 3 and the user provides shifts - through reverse communication (iparam[1]=0), naupp returns - NP, the number of shifts the user is to provide. - 0 < NP <=ncv-nev. See Remark 5 below. - iparam[9] = total number of OP*x operations. - iparam[10] = total number of B*x operations if bmat='G'. - iparam[11] = total number of steps of re-orthogonalization. - ipntr (Output) Array of length 14. Pointer to mark the starting - locations in the workd and workl arrays for matrices/vectors - used by the Arnoldi iteration. - ipntr[1] : pointer to the current operand vector X in workd. - ipntr[2] : pointer to the current result vector Y in workd. - ipntr[3] : pointer to the vector B * X in workd when used in - the shift-and-invert mode. - ipntr[4] : pointer to the next available location in workl - that is untouched by the program. - ipntr[5] : pointer to the ncv by ncv upper Hessenberg matrix - H in workl. - ipntr[6] : pointer to the real part of the ritz value array - RITZR in workl. - ipntr[7] : pointer to the imaginary part of the ritz value - array RITZI in workl. - ipntr[8] : pointer to the Ritz estimates in array workl - associated with the Ritz values located in RITZR - and RITZI in workl. - ipntr[14]: pointer to the np shifts in workl. See Remark 6. - Note: ipntr[9:13] is only referenced by neupp. See Remark 2. - ipntr[9] : pointer to the real part of the ncv RITZ values of - the original system. - ipntr[10]: pointer to the imaginary part of the ncv RITZ values - of the original system. - ipntr[11]: pointer to the ncv corresponding error bounds. - ipntr[12]: pointer to the ncv by ncv upper quasi-triangular - Schur matrix for H. - ipntr[13]: pointer to the ncv by ncv matrix of eigenvectors - of the upper Hessenberg matrix H. Only referenced by - neupp if rvec == TRUE. See Remark 2 below. - workd (Input / Output) Array of length 3*N+1. - Distributed array to be used in the basic Arnoldi iteration - for reverse communication. The user should not use workd as - temporary workspace during the iteration. Upon termination - workd[1:n] contains B*resid[1:n]. If the Ritz vectors are - desired subroutine neupp uses this output. - workl (Output) Array of length lworkl+1. Private (replicated) array - on each PE or array allocated on the front end. - lworkl (Input) lworkl must be at least 3*ncv*(ncv+2). - info (Input / Output) On input, if info = 0, a randomly initial - residual vector is used, otherwise resid contains the initial - residual vector, possibly from a previous run. - On output, info works as a error flag: - = 0 : Normal exit. - = 1 : Maximum number of iterations taken. All possible - eigenvalues of OP has been found. iparam[5] - returns the number of wanted converged Ritz values. - = 3 : No shifts could be applied during a cycle of the - Implicitly restarted Arnoldi iteration. One - possibility is to increase the size of NCV relative - to nev. See remark 4 below. - = -1 : n must be positive. - = -2 : nev must be positive. - = -3 : ncv must satisfy nev+2 <= ncv <= n. - = -4 : The maximum number of Arnoldi update iterations - allowed must be greater than zero. - = -5 : which must be one of 'LM','SM','LR','SR','LI','SI'. - = -6 : bmat must be one of 'I' or 'G'. - = -7 : Length of private work array workl is not sufficient. - = -8 : Error return from LAPACK eigenvalue calculation. - = -9 : Starting vector is zero. - = -10 : iparam[7] must be 1,2,3,4. - = -11 : iparam[7] = 1 and bmat = 'G' are incompatible. - = -12 : iparam[1] must be equal to 0 or 1. - = -13 : nev and which = 'BE' are incompatible. - = -9999: Could not build an Arnoldi factorization. iparam[5] - returns the size of the current Arnoldi factorization. - The user is advised to check that enough workspace - and array storage has been allocated. - - Remarks: - 1. The computed Ritz values are approximate eigenvalues of OP. The - selection of "which" should be made with this in mind when - Mode = 3 and 4. After convergence, approximate eigenvalues of the - original problem may be obtained with the ARPACK subroutine neupp. - 2. If a basis for the invariant subspace corresponding to the converged - Ritz values is needed, the user must call neupp immediately following - completion of naupp. This is new starting with release 2 of ARPACK. - 3. If M can be factored into a Cholesky factorization M = LL' - then Mode = 2 should not be selected. Instead one should use - Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular - linear systems should be solved with L and L' rather - than computing inverses. After convergence, an approximate - eigenvector z of the original problem is recovered by solving - L'z = x where x is a Ritz vector of OP. - 4. At present there is no a-priori analysis to guide the selection - of ncv relative to nev. The only formal requrement is that ncv - >= nev+2. However, it is recommended that ncv >= 2*nev+1. If many - problems of the same type are to be solved, one should experiment - with increasing ncv while keeping ncv fixed for a given test - problem. This will usually decrease the required number of OP*x - operations but it also increases the work and storage required to - maintain the orthogonal basis vectors. The optimal "cross-over" - with respect to CPU time is problem dependent and must be - determined empirically. - 5. When iparam[1] = 0, and ido = 3, the user needs to provide the - NP = iparam[8] real and imaginary parts of the shifts in locations - real part imaginary part - ----------------------- -------------- - 1 workl[ipntr[14]] workl[ipntr[14]+NP] - 2 workl[ipntr[14]+1] workl[ipntr[14]+NP+1] - . . - . . - . . - NP workl[ipntr[14]+NP-1] workl[ipntr[14]+2*NP-1]. - - Only complex conjugate pairs of shifts may be applied and the pairs - must be placed in consecutive locations. The real part of the - eigenvalues of the current upper Hessenberg matrix are located in - workl[ipntr[6]] through workl[ipntr[6]+ncv-1] and the imaginary part - in workl[ipntr[7]] through workl[ipntr[7]+ncv-1]. They are ordered - according to the order defined by which. The complex conjugate pairs - are kept together and the associated Ritz estimates are located in - workl[ipntr[8]], workl[ipntr[8]+1], ... , workl[ipntr[8]+ncv-1]. -*/ - -{ - - F77NAME(dnaupd)(&ido, &bmat, &n, which.c_str(), &nev, &tol, resid, &ncv, - &V[1], &ldv, &iparam[1], &ipntr[1], &workd[1], &workl[1], - &lworkl, &info); - -} // naupp (double). - -inline void naupp(ARint& ido, char bmat, ARint n, const std::string& which, ARint nev, - float& tol, float resid[], ARint ncv, float V[], - ARint ldv, ARint iparam[], ARint ipntr[], float workd[], - float workl[], ARint lworkl, ARint& info) - -/* - c++ version of ARPACK routine snaupd. The only difference between - snaupd and dnaupd is that in the former function all vectors have - single precision elements and in the latter all vectors have double - precision elements. -*/ - -{ - - F77NAME(snaupd)(&ido, &bmat, &n, which.c_str(), &nev, &tol, resid, &ncv, - &V[1], &ldv, &iparam[1], &ipntr[1], &workd[1], &workl[1], - &lworkl, &info); - -} // naupp (float). - -#endif // NAUPP_H - diff --git a/src/external/arpack++/include/neupp.h b/src/external/arpack++/include/neupp.h deleted file mode 100644 index 2b102d77..00000000 --- a/src/external/arpack++/include/neupp.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE neupp.h. - Interface to ARPACK subroutines dneupd and sneupd. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef NEUPP_H -#define NEUPP_H - -#include -#include -#include "arch.h" -#include "arpackf.h" - -inline void neupp(bool rvec, char HowMny, double dr[], - double di[], double Z[], ARint ldz, double sigmar, - double sigmai, double workv[], char bmat, ARint n, - const std::string& which, ARint nev, double tol, double resid[], - ARint ncv, double V[], ARint ldv, ARint iparam[], - ARint ipntr[], double workd[], double workl[], - ARint lworkl, ARint& info) - -/* - c++ version of ARPACK routine dneupd. - This subroutine returns the converged approximations to eigenvalues - of A*z = lambda*B*z and (optionally): - - (1) the corresponding approximate eigenvectors, - (2) an orthonormal basis for the associated approximate - invariant subspace, - - There is negligible additional cost to obtain eigenvectors. An - orthonormal basis is always computed. There is an additional storage cost - of n*nev if both are requested (in this case a separate array Z must be - supplied). - The approximate eigenvalues and eigenvectors of A*z = lambda*B*z - are derived from approximate eigenvalues and eigenvectors of - of the linear operator OP prescribed by the MODE selection in the - call to naupp. naupp must be called before this routine is called. - These approximate eigenvalues and vectors are commonly called Ritz - values and Ritz vectors respectively. They are referred to as such - in the comments that follow. The computed orthonormal basis for the - invariant subspace corresponding to these Ritz values is referred to - as a Schur basis. - See documentation in the header of the subroutine naupp for - definition of OP as well as other terms and the relation of computed - Ritz values and Ritz vectors of OP with respect to the given problem - A*z = lambda*B*z. For a brief description, see definitions of - iparam[7], MODE and which in the documentation of naupp. - - Parameters: - - rvec (Input) Specifies whether Ritz vectors corresponding to the - Ritz value approximations to the eigenproblem A*z = lambda*B*z - are computed. - rvec = false: Compute Ritz values only. - rvec = true : Compute the Ritz vectors or Schur vectors. - See Remarks below. - HowMny (Input) Specifies the form of the basis for the invariant - subspace corresponding to the converged Ritz values that - is to be computed. - = 'A': Compute nev Ritz vectors; - = 'P': Compute nev Schur vectors; - dr (Output) Array of dimension nev+1. - If iparam[7] = 1,2 or 3 and sigmai=0.0 then on exit: dr - contains the real part of the Ritz approximations to the - eigenvalues of A*z = lambda*B*z. - If iparam[7] = 3, 4 and sigmai is not equal to zero, then on - exit: dr contains the real part of the Ritz values of OP - computed by naupp. A further computation must be performed by - the user to transform the Ritz values computed for OP by naupp - to those of the original system A*z = lambda*B*z. See remark 3. - di (Output) Array of dimension nev+1. - On exit, di contains the imaginary part of the Ritz value - approximations to the eigenvalues of A*z = lambda*B*z - associated with dr. - NOTE: When Ritz values are complex, they will come in complex - conjugate pairs. If eigenvectors are requested, the - corresponding Ritz vectors will also come in conjugate - pairs and the real and imaginary parts of these are - represented in two consecutive columns of the array Z - (see below). - Z (Output) Array of dimension nev*n if rvec = TRUE and HowMny = - 'A'. if rvec = TRUE. and HowMny = 'A', then the contains - approximate eigenvectors (Ritz vectors) corresponding to the - NCONV=iparam[5] Ritz values for eigensystem A*z = lambda*B*z. - The complex Ritz vector associated with the Ritz value - with positive imaginary part is stored in two consecutive - columns. The first column holds the real part of the Ritz - vector and the second column holds the imaginary part. The - Ritz vector associated with the Ritz value with negative - imaginary part is simply the complex conjugate of the Ritz - vector associated with the positive imaginary part. - If rvec = .FALSE. or HowMny = 'P', then Z is not referenced. - NOTE: If if rvec = .TRUE. and a Schur basis is not required, - the array Z may be set equal to first nev+1 columns of - the Arnoldi basis array V computed by naupp. In this - case the Arnoldi basis will be destroyed and overwritten - with the eigenvector basis. - ldz (Input) Dimension of the vectors contained in Z. This - parameter MUST be set to n. - sigmar (Input) If iparam[7] = 3 or 4, represents the real part of - the shift. Not referenced if iparam[7] = 1 or 2. - sigmai (Input) If iparam[7] = 3 or 4, represents the imaginary part - of the shift. Not referenced if iparam[7] = 1 or 2. See - remark 3 below. - workv (Workspace) Array of dimension 3*ncv. - V (Input/Output) Array of dimension n*ncv+1. - Upon Input: V contains the ncv vectors of the Arnoldi basis - for OP as constructed by naupp. - Upon Output: If rvec = TRUE the first NCONV=iparam[5] columns - contain approximate Schur vectors that span the - desired invariant subspace. See Remark 2 below. - NOTE: If the array Z has been set equal to first nev+1 columns - of the array V and rvec = TRUE. and HowMny = 'A', then - the Arnoldi basis held by V has been overwritten by the - desired Ritz vectors. If a separate array Z has been - passed then the first NCONV=iparam[5] columns of V will - contain approximate Schur vectors that span the desired - invariant subspace. - workl (Input / Output) Array of length lworkl+1. - workl[1:ncv*ncv+3*ncv] contains information obtained in - naupp. They are not changed by neupp. - workl[ncv*ncv+3*ncv+1:3*ncv*ncv+6*ncv] holds the real and - imaginary part of the untransformed Ritz values, the upper - quasi-triangular matrix for H, and the associated matrix - representation of the invariant subspace for H. - ipntr (Input / Output) Array of length 14. Pointer to mark the - starting locations in the workl array for matrices/vectors - used by naupp and neupp. - ipntr[9]: pointer to the real part of the ncv RITZ values - of the original system. - ipntr[10]: pointer to the imaginary part of the ncv RITZ - values of the original system. - ipntr[11]: pointer to the ncv corresponding error bounds. - ipntr[12]: pointer to the ncv by ncv upper quasi-triangular - Schur matrix for H. - ipntr[13]: pointer to the ncv by ncv matrix of eigenvectors - of the upper Hessenberg matrix H. Only referenced - by neupp if rvec = TRUE. See Remark 2 below. - info (Output) Error flag. - = 0 : Normal exit. - = 1 : The Schur form computed by LAPACK routine dlahqr - could not be reordered by LAPACK routine dtrsen. - Re-enter subroutine neupp with iparam[5] = ncv and - increase the size of the arrays DR and DI to have - dimension at least dimension ncv and allocate at least - ncv columns for Z. NOTE: Not necessary if Z and V share - the same space. Please notify the authors if this error - occurs. - = -1 : n must be positive. - = -2 : nev must be positive. - = -3 : ncv must satisfy nev+2 <= ncv <= n. - = -5 : which must be one of 'LM','SM','LR','SR','LI','SI'. - = -6 : bmat must be one of 'I' or 'G'. - = -7 : Length of private work workl array is not sufficient. - = -8 : Error return from calculation of a real Schur form. - Informational error from LAPACK routine dlahqr. - = -9 : Error return from calculation of eigenvectors. - Informational error from LAPACK routine dtrevc. - = -10: iparam[7] must be 1,2,3,4. - = -11: iparam[7] = 1 and bmat = 'G' are incompatible. - = -12: HowMny = 'S' not yet implemented - = -13: HowMny must be one of 'A' or 'P' if rvec = TRUE. - = -14: naupp did not find any eigenvalues to sufficient - accuracy. - - NOTE: The following arguments - - bmat, n, which, nev, tol, resid, ncv, V, ldv, iparam, - ipntr, workd, workl, lworkl, info - - must be passed directly to neupp following the last call - to naupp. These arguments MUST NOT BE MODIFIED between - the the last call to naupp and the call to neupp. - - Remarks - 1. Currently only HowMny = 'A' and 'P' are implemented. - 2. Schur vectors are an orthogonal representation for the basis of - Ritz vectors. Thus, their numerical properties are often superior. - Let X' denote the transpose of X. If rvec = .TRUE. then the - relationship A * V[:,1:iparam[5]] = V[:,1:iparam[5]] * T, and - V[:,1:iparam[5]]' * V[:,1:iparam[5]] = I are approximately satisfied. - Here T is the leading submatrix of order iparam[5] of the real - upper quasi-triangular matrix stored workl[ipntr[12]]. That is, - T is block upper triangular with 1-by-1 and 2-by-2 diagonal blocks; - each 2-by-2 diagonal block has its diagonal elements equal and its - off-diagonal elements of opposite sign. Corresponding to each - 2-by-2 diagonal block is a complex conjugate pair of Ritz values. - The real Ritz values are stored on the diagonal of T. - 3. If iparam[7] = 3 or 4 and sigmai is not equal zero, then the user - must form the iparam[5] Rayleigh quotients in order to transform the - Ritz values computed by naupp for OP to those of A*z = lambda*B*z. - Set rvec = TRUE. and HowMny = 'A', and compute - Z[:,I]' * A * Z[:,I] if di[I] = 0. - If di[I] is not equal to zero and di[I+1] = - D[I], - then the desired real and imaginary parts of the Ritz value are - Z[:,I]' * A * Z[:,I] + Z[:,I+1]' * A * Z[:,I+1], - Z[:,I]' * A * Z[:,I+1] - Z[:,I+1]' * A * Z[:,I], respectively. - Another possibility is to set rvec = .true. and HowMny = 'P' and - compute V[:,1:iparam[5]]' * A * V[:,1:iparam[5]] and then an upper - quasi-triangular matrix of order iparam[5] is computed. See remark - 2 above. -*/ - -{ - - ARint irvec; - ARlogical* iselect; - double* iZ; - - irvec = (ARint) rvec; - iselect = new ARlogical[ncv]; - iZ = (Z == NULL) ? &V[1] : Z; - - F77NAME(dneupd)(&irvec, &HowMny, iselect, dr, di, iZ, &ldz, &sigmar, - &sigmai, &workv[1], &bmat, &n, which.c_str(), &nev, &tol, - resid, &ncv, &V[1], &ldv, &iparam[1], &ipntr[1], - &workd[1], &workl[1], &lworkl, &info); - - delete[] iselect; - -} // neupp (double). - -inline void neupp(bool rvec, char HowMny, float dr[], - float di[], float Z[], ARint ldz, float sigmar, - float sigmai, float workv[], char bmat, ARint n, - const std::string& which, ARint nev, float tol, float resid[], - ARint ncv, float V[], ARint ldv, ARint iparam[], - ARint ipntr[], float workd[], float workl[], - ARint lworkl, ARint& info) - -/* - c++ version of ARPACK routine sneupd. The only difference between - sneupd and dneupd is that in the former function all vectors have - single precision elements and in the latter all vectors have double - precision elements. -*/ - -{ - - ARint irvec; - ARlogical* iselect; - float* iZ; - - irvec = (ARint) rvec; - iselect = new ARlogical[ncv]; - iZ = (Z == NULL) ? &V[1] : Z; - - F77NAME(sneupd)(&irvec, &HowMny, iselect, dr, di, iZ, &ldz, &sigmar, - &sigmai, &workv[1], &bmat, &n, which.c_str(), &nev, &tol, - resid, &ncv, &V[1], &ldv, &iparam[1], &ipntr[1], - &workd[1], &workl[1], &lworkl, &info ); - - delete[] iselect; - -} // neupp (float). - -#endif // NEUPP_H - diff --git a/src/external/arpack++/include/saupp.h b/src/external/arpack++/include/saupp.h deleted file mode 100644 index a0c256cb..00000000 --- a/src/external/arpack++/include/saupp.h +++ /dev/null @@ -1,321 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE saupp.h. - Interface to ARPACK subroutines dsaupd and ssaupd. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef SAUPP_H -#define SAUPP_H - -#include -#include "arch.h" -#include "arpackf.h" - -inline void saupp(ARint& ido, char bmat, ARint n, const std::string& which, ARint nev, - double& tol, double resid[], ARint ncv, double V[], - ARint ldv, ARint iparam[], ARint ipntr[], double workd[], - double workl[], ARint lworkl, ARint& info) - -/* - c++ version of ARPACK routine dsaupd that implements a variant of - the Lanczos method. This method has been designed to compute - approximations to a few eigenpairs of a linear operator OP that is - real and symmetric with respect to a real positive semi-definite - symmetric matrix B, i.e. - - B*OP = (OP')*B. - - where A' denotes transpose of A. In the standard eigenproblem B is - the identity matrix. Another way to express this condition is - - < x,OPy > = < OPx,y > where < z,w > = z'Bw. - - The computed approximate eigenvalues are called Ritz values and - the corresponding approximate eigenvectors are called Ritz vectors. - - saupp is usually called iteratively to solve one of the - following problems: - - Mode 1: A*x = lambda*x, A symmetric - ===> OP = A and B = I. - - Mode 2: A*x = lambda*M*x, A symmetric, M symmetric positive definite - ===> OP = inv[M]*A and B = M. - ===> (If M can be factored see remark 3 below) - - Mode 3: K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite - ===> OP = (inv[K - sigma*M])*M and B = M. - ===> Shift-and-Invert mode - - Mode 4: K*x = lambda*KG*x, K symmetric positive semi-definite, - KG symmetric indefinite - ===> OP = (inv[K - sigma*KG])*K and B = K. - ===> Buckling mode - - Mode 5: A*x = lambda*M*x, A symmetric, M symmetric positive semi-definite - ===> OP = inv[A - sigma*M]*[A + sigma*M] and B = M. - ===> Cayley transformed mode - - NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v should be - accomplished either by a direct method using a sparse matrix - factorization and solving - - [A - sigma*M]*w = v or M*w = v, - - or through an iterative method for solving these systems. If an - iterative method is used, the convergence test must be more - stringent than the accuracy requirements for the eigenvalue - approximations. - - Parameters: - - ido (Input / Output) Reverse communication flag. ido must be - zero on the first call to saupp. ido will be set - internally to indicate the type of operation to be - performed. Control is then given back to the calling - routine which has the responsibility to carry out the - requested operation and call saupp with the result. The - operand is given in workd[ipntr[1]], the result must be - put in workd[ipntr[2]]. (If Mode = 2 see remark 5 below). - ido = 0: first call to the reverse communication interface. - ido = -1: compute Y = OP * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - This is for the initialization phase to force the - starting vector into the range of OP. - ido = 1: compute Y = OP * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - In mode 3,4 and 5, the vector B * X is already - available in workd[ipntr[3]]. It does not - need to be recomputed in forming OP * X. - ido = 2: compute Y = B * X where - ipntr[1] is the pointer into workd for X, - ipntr[2] is the pointer into workd for Y. - ido = 3: compute the iparam[8] shifts where - ipntr[11] is the pointer into workl for - placing the shifts. See remark 6 below. - ido = 99: done. - bmat (Input) bmat specifies the type of the matrix B that defines - the semi-inner product for the operator OP. - bmat = 'I' -> standard eigenvalue problem A*x = lambda*x; - bmat = 'G' -> generalized eigenvalue problem A*x = lambda*B*x. - n (Input) Dimension of the eigenproblem. - nev (Input) Number of eigenvalues to be computed. 0 < nev < n. - which (Input) Specify which of the Ritz values of OP to compute. - 'LA' - compute the nev largest (algebraic) eigenvalues. - 'SA' - compute the nev smallest (algebraic) eigenvalues. - 'LM' - compute the nev largest (in magnitude) eigenvalues. - 'SM' - compute the nev smallest (in magnitude) eigenvalues. - 'BE' - compute nev eigenvalues, half from each end of the - spectrum. When NEV is odd, compute one more from the - high end than from the low end. - (see remark 1 below) - tol (Input) Stopping criterion: the relative accuracy of the - Ritz value is considered acceptable if BOUNDS[i] <= - tol*abs(RITZ[i]). If tol<=0.0 is passed, the machine - precision as computed by the LAPACK auxiliary subroutine - _LAMCH is used. - resid (Input / Output) Array of length n. - On input: - If info==0, a random initial residual vector is used. - If info!=0, resid contains the initial residual vector, - possibly from a previous run. - On output: - resid contains the final residual vector. - ncv (Input) Number of Lanczos vectors that are generated at each - iteration. After the startup phase in which nev Lanczos - vectors are generated, the algorithm generates ncv-nev - Lanczos vectors at each subsequent update iteration. Most of - the cost in generating each Lanczos vector is in the - matrix-vector product OP*x. (See remark 4 below). - V (Output) Double precision array of length ncv*n+1. V contains - the ncv Lanczos basis vectors. The first element V[0] is never - referenced. - ldv (Input) Dimension of the basis vectors contianed in V. This - parameter MUST be set to n. - iparam (Input / Output) Array of length 12. - iparam[1] = ISHIFT: method for selecting the implicit shifts. - The shifts selected at each iteration are used to restart - the Arnoldi iteration in an implicit fashion. - ------------------------------------------------------------- - ISHIFT = 0: the shifts are provided by the user via - reverse communication. The NCV eigenvalues of - the current tridiagonal matrix T are returned in - the part of workl array corresponding to RITZ. - See remark 6 below. - ISHIFT = 1: exact shifts with respect to the reduced - tridiagonal matrix T. This is equivalent to - restarting the iteration with a starting vector - that is a linear combination of Ritz vectors - associated with the "wanted" Ritz values. - ------------------------------------------------------------- - iparam[2] is no longer referenced. - iparam[3] = MXITER - On INPUT: maximum number of Arnoldi update iterations allowed. - On OUTPUT: actual number of Arnoldi update iterations taken. - iparam[4] = NB: blocksize to be used in the recurrence. - The code currently works only for NB = 1. - iparam[5] = NCONV: number of "converged" Ritz values. - This represents the number of Ritz values that satisfy - the convergence criterion. - iparam[6] is no longer referenced. - iparam[7] = MODE. On INPUT determines what type of - eigenproblem is being solved. Must be 1,2,3,4,5. - iparam[8] = NP. When ido = 3 and the user provides shifts - through reverse communication (iparam[1]=0), saupp returns - NP, the number of shifts the user is to provide. - 0 < NP <=ncv-nev. See Remark 6 below. - iparam[9] = total number of OP*x operations. - iparam[10] = total number of B*x operations if bmat='G'. - iparam[11] = total number of steps of re-orthogonalization. - ipntr (Output) Array of length 12. Pointer to mark the starting - locations in the workd and workl arrays for matrices/vectors - used by the Lanczos iteration. - ipntr[1] : pointer to the current operand vector X in workd. - ipntr[2] : pointer to the current result vector Y in workd. - ipntr[3] : pointer to the vector B * X in workd when used in - the shift-and-invert mode. - ipntr[4] : pointer to the next available location in workl - that is untouched by the program. - ipntr[5] : pointer to the ncv by 2 tridiagonal matrix T in - workl. - ipntr[6] : pointer to the ncv RITZ values array in workl. - ipntr[7] : pointer to the Ritz estimates in array workl - associated with the Ritz values located in RITZ - in workl. - ipntr[11]: pointer to the np shifts in workl. See Remark 6. - Note: ipntr[8:10] is only referenced by seupp. See Remark 2. - ipntr[8] : pointer to the ncv RITZ values of the original - system. - ipntr[9] : pointer to the ncv corresponding error bounds. - ipntr[10]: pointer to the ncv by ncv matrix of eigenvectors - of the tridiagonal matrix T. Only referenced by - seupp if RVEC = TRUE. See Remarks. - workd (Input / Output) Array of length 3*N+1. - Distributed array to be used in the basic Arnoldi iteration - for reverse communication. The user should not use workd as - temporary workspace during the iteration. Upon termination - workd[1:n] contains B*resid[1:n]. If the Ritz vectors are - desired subroutine seupp uses this output. - workl (Output) Array of length lworkl+1. Private (replicated) array - on each PE or array allocated on the front end. - lworkl (Input) lworkl must be at least ncv*(ncv+8). - info (Input / Output) On input, if info = 0, a randomly initial - residual vector is used, otherwise resid contains the initial - residual vector, possibly from a previous run. - On output, info works as a error flag: - = 0 : Normal exit. - = 1 : Maximum number of iterations taken. All possible - eigenvalues of OP has been found. iparam[5] - returns the number of wanted converged Ritz values. - = 3 : No shifts could be applied during a cycle of the - Implicitly restarted Arnoldi iteration. One - possibility is to increase the size of NCV relative - to nev. See remark 4 below. - = -1 : n must be positive. - = -2 : nev must be positive. - = -3 : ncv must satisfy nev < ncv <= n. - = -4 : The maximum number of Arnoldi update iterations allowed - must be greater than zero. - = -5 : which must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. - = -6 : bmat must be one of 'I' or 'G'. - = -7 : Length of private work array workl is not sufficient. - = -8 : Error return from trid. eigenvalue calculation; - Informational error from LAPACK routine dsteqr. - = -9 : Starting vector is zero. - = -10 : iparam[7] must be 1,2,3,4,5. - = -11 : iparam[7] = 1 and bmat = 'G' are incompatible. - = -12 : iparam[1] must be equal to 0 or 1. - = -13 : nev and which = 'BE' are incompatible. - = -9999: Could not build an Arnoldi factorization. iparam[5] - returns the size of the current Arnoldi factorization. - The user is advised to check that enough workspace - and array storage has been allocated. - - Remarks: - 1. The converged Ritz values are always returned in ascending - algebraic order. The computed Ritz values are approximate - eigenvalues of OP. The selection of "which" should be made - with this in mind when Mode = 3,4,5. After convergence, - approximate eigenvalues of the original problem may be obtained - with the ARPACK subroutine seupp. - 2. If the Ritz vectors corresponding to the converged Ritz values are - needed, the user must call seupp immediately following completion - of saupp. This is new starting with version 2.1 of ARPACK. - 3. If M can be factored into a Cholesky factorization M = LL' - then Mode = 2 should not be selected. Instead one should use - Mode = 1 with OP = inv(L)*A*inv(L'). Appropriate triangular - linear systems should be solved with L and L' rather - than computing inverses. After convergence, an approximate - eigenvector z of the original problem is recovered by solving - L'z = x where x is a Ritz vector of OP. - 4. At present there is no a-priori analysis to guide the selection - of ncv relative to nev. The only formal requrement is that - ncv > nev. However, it is recommended that ncv >= 2*nev. If many - problems of the same type are to be solved, one should experiment - with increasing ncv while keeping nev fixed for a given test - problem. This will usually decrease the required number of OP*x - operations but it also increases the work and storage required to - maintain the orthogonal basis vectors. The optimal "cross-over" - with respect to CPU time is problem dependent and must be - determined empirically. - 5. If iparam[7] = 2 then in the Reverse commuication interface the - user must do the following. When ido = 1, Y = OP * X is to be - computed. When iparam[7] = 2 OP = inv(B)*A. After computing A*X - the user must overwrite X with A*X. Y is then the solution to the - linear set of equations B*Y = A*X. - 6. When iparam[1] = 0, and ido = 3, the user needs to provide the - NP = iparam[8] shifts in locations: - 1 workl[ipntr[11]] - 2 workl[ipntr[11]+1] - . - . - . - NP workl[ipntr[11]+NP-1]. - The eigenvalues of the current tridiagonal matrix are located in - workl[ipntr[6]] through workl[ipntr[6]+ncv]. They are in the - order defined by which. The associated Ritz estimates are located in - workl[ipntr[8]], workl[ipntr[8]+1], ... , workl[ipntr[8]+ncv-1]. -*/ - -{ - - F77NAME(dsaupd)(&ido, &bmat, &n, which.c_str(), &nev, &tol, resid, &ncv, - &V[1], &ldv, &iparam[1], &ipntr[1], &workd[1], &workl[1], - &lworkl, &info); - -} // saupp (double). - -inline void saupp(ARint& ido, char bmat, ARint n, const std::string& which, ARint nev, - float& tol, float resid[], ARint ncv, float V[], - ARint ldv, ARint iparam[], ARint ipntr[], float workd[], - float workl[], ARint lworkl, ARint& info) - -/* - c++ version of ARPACK routine ssaupd. The only difference between - ssaupd and dsaupd is that in the former function all vectors have - single precision elements and in the latter all vectors have double - precision elements. -*/ - -{ - - F77NAME(ssaupd)(&ido, &bmat, &n, which.c_str(), &nev, &tol, resid, &ncv, - &V[1], &ldv, &iparam[1], &ipntr[1], &workd[1], &workl[1], - &lworkl, &info); - -} // saupp (float). - -#endif // SAUPP_H - diff --git a/src/external/arpack++/include/seupp.h b/src/external/arpack++/include/seupp.h deleted file mode 100644 index 5e5c6051..00000000 --- a/src/external/arpack++/include/seupp.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE seupp.h. - Interface to ARPACK subroutines dseupd and sseupd. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef SEUPP_H -#define SEUPP_H - -#include -#include -#include "arch.h" -#include "arpackf.h" - -inline void seupp(bool rvec, char HowMny, double d[], double Z[], - ARint ldz, double sigma, char bmat, ARint n, - const std::string& which, ARint nev, double tol, double resid[], - ARint ncv, double V[], ARint ldv, ARint iparam[], - ARint ipntr[], double workd[], double workl[], - ARint lworkl, ARint& info) - -/* - c++ version of ARPACK routine dseupd. - This subroutine returns the converged approximations to eigenvalues - of A*z = lambda*B*z and (optionally): - - (1) the corresponding approximate eigenvectors, - (2) an orthonormal (Lanczos) basis for the associated approximate - invariant subspace, - - There is negligible additional cost to obtain eigenvectors. An orthonormal - (Lanczos) basis is always computed. There is an additional storage cost - of n*nev if both are requested (in this case a separate array Z must be - supplied). - These quantities are obtained from the Lanczos factorization computed - by saupp for the linear operator OP prescribed by the MODE selection - (see IPARAM[7] in saupp documentation). saupp must be called before - this routine is called. These approximate eigenvalues and vectors are - commonly called Ritz values and Ritz vectors respectively. They are - referred to as such in the comments that follow. The computed orthonormal - basis for the invariant subspace corresponding to these Ritz values is - referred to as a Lanczos basis. - See documentation in the header of the subroutine dsaupp for a definition - of OP as well as other terms and the relation of computed Ritz values - and vectors of OP with respect to the given problem A*z = lambda*B*z. - The approximate eigenvalues of the original problem are returned in - ascending algebraic order. The user may elect to call this routine - once for each desired Ritz vector and store it peripherally if desired. - There is also the option of computing a selected set of these vectors - with a single call. - - Parameters: - - rvec (Input) Specifies whether Ritz vectors corresponding to the - Ritz value approximations to the eigenproblem A*z = lambda*B*z - are computed. - rvec = false: Compute Ritz values only. - rvec = true : Compute Ritz vectors. - HowMny (Input) Specifies how many Ritz vectors are wanted and the - form of Z, the matrix of Ritz vectors. See remark 1 below. - The only option already implemented is HowMny = 'A'. - d (Output) Array of dimension nev. On exit, d contains the Ritz - value approximations to the eigenvalues of A*z = lambda*B*z. - The values are returned in ascending order. If iparam[7] = - 3, 4, 5 then d represents the Ritz values of OP computed by - dsaupp transformed to those of the original eigensystem A*z = - lambda*B*z. If iparam[7] = 1,2 then the Ritz values of OP are - the same as the those of A*z = lambda*B*z. - Z (Output) Array of dimension nev*n if HowMny = 'A'. On - exit, Z contains the B-orthonormal Ritz vectors of the - eigensystem A*z = lambda*B*z corresponding to the Ritz value - approximations. If rvec = false then Z is not referenced. - NOTE: The array Z may be set equal to first nev columns of - the Arnoldi/Lanczos basis array V computed by dsaupp. - ldz (Input) Dimension of the vectors contained in Z. This - parameter MUST be set to n. - sigma (Input) If iparam[7] = 3,4,5 represents the shift. Not - referenced if iparam[7] = 1 or 2. - workl (Input / Output) Array of length lworkl+1. - workl[1:4*ncv] contains information obtained in saupp. - They are not changed by seupp. workl[4*ncv+1:ncv*(ncv+8)] - holds the untransformed Ritz values, the computed error - estimates, and the associated eigenvector matrix of H. - Note: ipntr[8:10] contains the pointer into workl for - addresses of the above information computed by seupp. - ipntr (Input / Output) Array of length 12. Pointer to mark the - starting locations in the workl array for matrices/vectors - used by dsaupp and seupp. - ipntr[8] : pointer to the RITZ values of the original system. - ipntr[9] : pointer to the ncv corresponding error bounds. - ipntr[10]: pointer to the ncv by ncv matrix of eigenvectors - of the tridiagonal matrix T. Only referenced by - seupp if rvec = true. See Remarks. - info (Output) Error flag. - = 0 : Normal exit. - = -1 : n must be positive. - = -2 : nev must be positive. - = -3 : ncv must satisfy nev < ncv <= n. - = -5 : which must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'. - = -6 : bmat must be one of 'I' or 'G'. - = -7 : Length of private work workl array is not sufficient. - = -8 : Error return from trid. eigenvalue calculation; - Information error from LAPACK routine dsteqr. - = -9 : Starting vector is zero. - = -10: iparam[7] must be 1,2,3,4,5. - = -11: iparam[7] = 1 and bmat = 'G' are incompatible. - = -12: nev and which = 'BE' are incompatible. - = -14: dsaupp did not find any eigenvalues to sufficient - accuracy. - = -15: HowMny must be one of 'A' or 'S' if rvec = true. - = -16: HowMny = 'S' not yet implemented. - - NOTE: The following arguments - - bmat, n, which, nev, tol, resid, ncv, V, ldv, iparam, - ipntr, workd, workl, lworkl, info - - must be passed directly to seupp following the last call - to saupp. These arguments MUST NOT BE MODIFIED between - the the last call to saupp and the call to seupp. - - Remarks - 1. The converged Ritz values are always returned in increasing - (algebraic) order. - 2. Currently only HowMny = 'A' is implemented. It is included at - this stage for the user who wants to incorporate it. -*/ - -{ - - ARint irvec; - ARlogical* iselect; - double* iZ; - - irvec = (ARint) rvec; - iselect = new ARlogical[ncv]; - iZ = (Z == NULL) ? &V[1] : Z; - - F77NAME(dseupd)(&irvec, &HowMny, iselect, d, iZ, &ldz, &sigma, &bmat, - &n, which.c_str(), &nev, &tol, resid, &ncv, &V[1], &ldv, &iparam[1], - &ipntr[1], &workd[1], &workl[1], &lworkl, &info ); - - delete[] iselect; - -} // seupp (double). - -inline void seupp(bool rvec, char HowMny, float d[], float Z[], - ARint ldz, float sigma, char bmat, ARint n, - const std::string& which, ARint nev, float tol, float resid[], - ARint ncv, float V[], ARint ldv, ARint iparam[], - ARint ipntr[], float workd[], float workl[], - ARint lworkl, ARint& info) - -/* - c++ version of ARPACK routine sseupd. The only difference between - sseupd and dseupd is that in the former function all vectors have - single precision elements and in the latter all vectors have double - precision elements. -*/ - -{ - - ARint irvec; - ARlogical* iselect; - float* iZ; - - irvec = (ARint) rvec; - iselect = new ARlogical[ncv]; - iZ = (Z == NULL) ? &V[1] : Z; - - F77NAME(sseupd)(&irvec, &HowMny, iselect, d, iZ, &ldz, &sigma, &bmat, - &n, which.c_str(), &nev, &tol, resid, &ncv, &V[1], &ldv, &iparam[1], - &ipntr[1], &workd[1], &workl[1], &lworkl, &info ); - - delete[] iselect; - -} // seupp (float). - -#endif // SEUPP_H - diff --git a/src/external/arpack++/include/superluc.h b/src/external/arpack++/include/superluc.h deleted file mode 100644 index cdc7085d..00000000 --- a/src/external/arpack++/include/superluc.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE SuperLUc.h. - Interface to SuperLU routines. - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef SUPERLUC_H -#define SUPERLUC_H - -#include "arch.h" -#include "arlspdef.h" -#include "arlsupm.h" -#include "arlcomp.h" - -// gstrf. - -inline void gstrf(superlu_options_t *options, SuperMatrix *A, - int relax, int panel_size, int *etree, void *work, int lwork, - int *perm_c, int *perm_r, SuperMatrix *L, SuperMatrix *U, - SuperLUStat_t *stat, int *info) -{ - if (A->Dtype == SLU_D) { // calling the double precision routine. - dGlobalLU_t Glu; - dgstrf(options,A,relax, - panel_size,etree,work,lwork,perm_c,perm_r,L,U,&Glu,stat,info); - } - else if (A->Dtype == SLU_S) { // calling the single precision routine. - sGlobalLU_t Glu; - sgstrf(options,A,relax, - panel_size,etree,work,lwork,perm_c,perm_r,L,U,&Glu,stat,info); - } - else if (A->Dtype == SLU_Z) { // calling the double precision complex routine. -#ifdef ARCOMP_H - zGlobalLU_t Glu; - zgstrf(options,A,relax, - panel_size,etree,work,lwork,perm_c,perm_r,L,U,&Glu,stat,info); -#endif - } - else { // calling the single precision complex routine. -#ifdef ARCOMP_H - cGlobalLU_t Glu; - cgstrf(options,A,relax, - panel_size,etree,work,lwork,perm_c,perm_r,L,U,&Glu,stat,info); -#endif - } - -} // gstrf. - - -inline void gstrs(trans_t trans, SuperMatrix *L, SuperMatrix *U, - int *perm_c, int *perm_r, SuperMatrix *B, SuperLUStat_t* stat, int *info) -{ - - if (L->Dtype == SLU_D) { // calling the double precision routine. - dgstrs(trans,L,U,perm_c,perm_r,B,stat,info); - } - else if (L->Dtype == SLU_S) { // calling the single precision routine. - sgstrs(trans,L,U,perm_c,perm_r,B,stat,info); - } - else if (L->Dtype == SLU_Z) { // calling the double precision complex routine. -#ifdef ARCOMP_H - zgstrs(trans,L,U,perm_c,perm_r,B,stat,info); -#endif - } - else { // calling the single precision complex routine. -#ifdef ARCOMP_H - cgstrs(trans,L,U,perm_c,perm_r,B,stat,info); -#endif - } - -} // gstrs. - - -// Create_CompCol_Matrix. - -inline void Create_CompCol_Matrix(SuperMatrix* A, int m, int n, int nnz, - double* a, int* irow, int* pcol, - Stype_t S, Mtype_t M) -{ - - dCreate_CompCol_Matrix(A,m,n,nnz,a,irow,pcol,S,SLU_D,M); - -} // Create_CompCol_Matrix (double). - -inline void Create_CompCol_Matrix(SuperMatrix* A, int m, int n, int nnz, - float* a, int* irow, int* pcol, - Stype_t S, Mtype_t M) -{ - - sCreate_CompCol_Matrix(A,m,n,nnz,a,irow,pcol,S,SLU_S,M); - -} // Create_CompCol_Matrix (float). - -#ifdef ARCOMP_H - -inline void Create_CompCol_Matrix(SuperMatrix* A, int m, int n, int nnz, - arcomplex* a, int* irow, int* pcol, - Stype_t S, Mtype_t M) -{ - - zCreate_CompCol_Matrix(A,m,n,nnz,(ldcomplex*)a,irow,pcol,S,SLU_Z,M); - -} // Create_CompCol_Matrix (complex). - -inline void Create_CompCol_Matrix(SuperMatrix* A, int m, int n, int nnz, - arcomplex* a, int* irow, int* pcol, - Stype_t S, Mtype_t M) -{ - - cCreate_CompCol_Matrix(A,m,n,nnz,(lscomplex*)a,irow,pcol,S,SLU_C,M); - -} // Create_CompCol_Matrix (complex). - -#endif // ARCOMP_H. - - -// Create_Dense_Matrix. - -inline void Create_Dense_Matrix(SuperMatrix* A, int m, int n, double* x, - int ldx, Stype_t S, Mtype_t M) -{ - - dCreate_Dense_Matrix(A,m,n,x,ldx,S,SLU_D,M); - -} // Create_Dense_Matrix (double). - -inline void Create_Dense_Matrix(SuperMatrix* A, int m, int n, float* x, - int ldx, Stype_t S, Mtype_t M) -{ - - sCreate_Dense_Matrix(A,m,n,x,ldx,S,SLU_S,M); - -} // Create_Dense_Matrix (float). - -#ifdef ARCOMP_H - -inline void Create_Dense_Matrix(SuperMatrix* A, int m, int n, arcomplex* x, - int ldx, Stype_t S, Mtype_t M) -{ - - zCreate_Dense_Matrix(A,m,n,(ldcomplex*)x,ldx,S,SLU_Z,M); - -} // Create_Dense_Matrix (complex). - -inline void Create_Dense_Matrix(SuperMatrix* A, int m, int n, arcomplex* x, - int ldx, Stype_t S, Mtype_t M) -{ - - cCreate_Dense_Matrix(A,m,n,(lscomplex*)x,ldx,S,SLU_C,M); - -} // Create_Dense_Matrix (complex). - -#endif // ARCOMP_H. - -#endif // SUPERLUC_H diff --git a/src/external/arpack++/include/umfpackc.h b/src/external/arpack++/include/umfpackc.h deleted file mode 100644 index 8c084ad0..00000000 --- a/src/external/arpack++/include/umfpackc.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - ARPACK++ v1.2 2/20/2000 - c++ interface to ARPACK code. - - MODULE UMFPACKc.h. - Interface to UMFPACK routines. - - Author of this class: - Martin Reuter - Date 2/28/2013 - - Arpack++ Author: - Francisco Gomes - - ARPACK Authors - Richard Lehoucq - Danny Sorensen - Chao Yang - Dept. of Computational & Applied Mathematics - Rice University - Houston, Texas -*/ - -#ifndef UMFPACKC_H -#define UMFPACKC_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define UMFPACK_INFO 90 -#define UMFPACK_CONTROL 20 -#define UMFPACK_OK (0) -#define UMFPACK_A (0) /* Ax=b */ -#define UMFPACK_PRL 0 /* print level */ - -void umfpack_di_defaults -( - double Control [UMFPACK_CONTROL] -) ; - - -int umfpack_di_symbolic -( - int n_row, - int n_col, - const int Ap [ ], - const int Ai [ ], - const double Ax [ ], - void **Symbolic, - const double Control [UMFPACK_CONTROL], - double Info [UMFPACK_INFO] -) ; - -int umfpack_di_numeric -( - const int Ap [ ], - const int Ai [ ], - const double Ax [ ], - void *Symbolic, - void **Numeric, - const double Control [UMFPACK_CONTROL], - double Info [UMFPACK_INFO] -) ; - -void umfpack_di_free_symbolic -( - void **Symbolic -) ; - -void umfpack_di_free_numeric -( - void **Numeric -) ; - -int umfpack_di_triplet_to_col -( - int n_row, - int n_col, - int nz, - const int Ti [ ], - const int Tj [ ], - const double Tx [ ], - int Ap [ ], - int Ai [ ], - double Ax [ ], - int Map [ ] -) ; - -int umfpack_di_solve -( - int sys, - const int Ap [ ], - const int Ai [ ], - const double Ax [ ], - double X [ ], - const double B [ ], - void *Numeric, - const double Control [UMFPACK_CONTROL], - double Info [UMFPACK_INFO] -) ; - -int umfpack_di_report_matrix -( - int n_row, - int n_col, - const int Ap [ ], - const int Ai [ ], - const double Ax [ ], - int col_form, - const double Control [UMFPACK_CONTROL] -) ; - -#ifdef __cplusplus - } -#endif - -//#include "umfpack.h" -#include - -inline void Write_Triplet_Matrix(const std::string & fname, int * tripi, - int * tripj, double* tripx, unsigned int nnz) -{ - std::ofstream myfile; - myfile.open ( fname.c_str() ); - myfile.precision(20); - for (unsigned int i=0;innz;i++) - { - myfile << ((int*)T->i)[i]+1 << " " << ((int*)T->j)[i]+1 << " " << ((double*)T->x)[i] << std::endl; - } - //std::cout << " ] " << std::endl; - myfile.close(); - - cholmod_free_triplet(&T,c); - -} - -// Create_Cholmod_Sparse_Matrix -inline cholmod_sparse* Create_Cholmod_Sparse_Matrix(int m, int n, int nnz, - double* a, int* irow, int* pcol, char uplo, cholmod_common *c) -{ - - cholmod_sparse* A = new cholmod_sparse; - A->nrow = m; - A->ncol = n; - A->nzmax = nnz; - A->p = pcol; - A->i = irow; - A->nz = NULL; - A->x = a; - A->z = NULL; - if (uplo == 'L') A->stype = -1; - else A->stype = 1; - A->itype = CHOLMOD_INT; - A->xtype = CHOLMOD_REAL; // real - A->dtype = CHOLMOD_DOUBLE; // double - A->sorted = 0; - A->packed = 1; - - return A; - - - - -} // Create_Cholmod_Sparse_Matrix (double). - -// Create_Cholmod_Dense_Matrix (from Triplet) -inline cholmod_dense* Create_Cholmod_Dense_Matrix(int m, int n, - double* a, cholmod_common *c) -{ - - - cholmod_dense* A = new cholmod_dense; - A->nrow = m; - A->ncol = n; - A->nzmax = m*n; - A->d = m; - A->x = a; - A->z = NULL; - A->xtype = CHOLMOD_REAL; // real - A->dtype = CHOLMOD_DOUBLE; // double - -// cholmod_dense* As = cholmod_copy_dense(A,c); - - return A; - -} // Create_Cholmod_Dense_Matrix (double). - -// Create_Cholmod_Dense_Matrix (from Triplet) -inline void Get_Cholmod_Dense_Data(cholmod_dense* A, int n, double* a) -{ - memcpy(a,A->x,n*sizeof(double)); - -// for (int i = 0;ix)[i]; - -} // Create_Cholmod_Dense_Matrix (double). - -*/ - -#endif // UMFPACKC_H diff --git a/src/external/cmake-files/Boost.cmake b/src/external/cmake-files/Boost.cmake deleted file mode 100644 index 48dad5e6..00000000 --- a/src/external/cmake-files/Boost.cmake +++ /dev/null @@ -1,36 +0,0 @@ -set(BOOST_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}) -function(GetBoost) - find_path(BOOST_DIR NAMES boost PATHS ${BOOST_CMAKE_DIR}/../_deps/boost-src) - - if (NOT BOOST_DIR) - - set(BOOST_URL "https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2" CACHE STRING "Boost download URL") - set(BOOST_URL_SHA256 "f0397ba6e982c4450f27bf32a2a83292aba035b827a5623a14636ea583318c41" CACHE STRING "Boost download URL SHA256 checksum") - - include(FetchContent) - set(FETCHCONTENT_BASE_DIR "${BOOST_CMAKE_DIR}/../_deps") - FetchContent_Declare( - Boost - URL ${BOOST_URL} - URL_HASH SHA256=${BOOST_URL_SHA256} - ) - FetchContent_GetProperties(Boost) - - if(NOT Boost_POPULATED) - message(STATUS "Fetching Boost") - FetchContent_Populate(Boost) - message(STATUS "Fetching Boost - done") - set(BOOST_DIR ${boost_SOURCE_DIR}) - endif() - - message(STATUS "Using downloaded Boost library at ${BOOST_DIR}") - - else () - message(STATUS "Boost Library found: ${BOOST_DIR}") - - endif() - - include_directories(${BOOST_DIR}) - include_directories(${BOOST_DIR}/boost) - -endfunction() diff --git a/src/external/cmake-files/Eigen.cmake b/src/external/cmake-files/Eigen.cmake deleted file mode 100644 index 974a6f18..00000000 --- a/src/external/cmake-files/Eigen.cmake +++ /dev/null @@ -1,32 +0,0 @@ -set(EIGEN_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}) -function(GetEigen) - find_path(EIGEN_DIR NAMES Eigen PATHS ${EIGEN_CMAKE_DIR}/../_deps/eigen-src) - - if (NOT EIGEN_DIR) - include(FetchContent) - set(FETCHCONTENT_BASE_DIR "${EIGEN_CMAKE_DIR}/../_deps") - FetchContent_Declare( - eigen - GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git - GIT_TAG 3.4.0 - ) - - FetchContent_GetProperties(eigen) - - if(NOT eigen_POPULATED) - message(STATUS "Eigen library not found locally, downloading it.") - FetchContent_Populate(eigen) - endif() - - set(EIGEN_DIR ${eigen_SOURCE_DIR}) - message(STATUS "Using downloaded Eigen library at: ${EIGEN_DIR}") - - else () - - message(STATUS "Eigen Library found: ${EIGEN_DIR}") - - endif() - - include_directories(${EIGEN_DIR}) - -endfunction() diff --git a/src/external/cmake-files/LPSolve.cmake b/src/external/cmake-files/LPSolve.cmake deleted file mode 100644 index b3f3cf98..00000000 --- a/src/external/cmake-files/LPSolve.cmake +++ /dev/null @@ -1,32 +0,0 @@ -set(LP_SOLVE_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}) -function(GetLPSolve) - find_path(LP_SOLVE_DIR NAMES lpsolve.h PATHS ${LP_SOLVE_CMAKE_DIR}/../_deps/lpsolve-src) - - if (NOT LP_SOLVE_DIR) - include(FetchContent) - set(FETCHCONTENT_BASE_DIR "${LP_SOLVE_CMAKE_DIR}/../_deps") - FetchContent_Declare( - lpsolve - URL https://webwerks.dl.sourceforge.net/project/lpsolve/lpsolve/5.5.2.11/lp_solve_5.5.2.11_source.tar.gz - URL_HASH MD5=a829a8d9c60ff81dc72ff52363703886 - ) - - FetchContent_GetProperties(lpsolve) - - if(NOT lpsolve_POPULATED) - message(STATUS "lp_solve library not found locally, downloading it.") - FetchContent_Populate(lpsolve) - endif() - - set(LP_SOLVE_DIR "${lpsolve_SOURCE_DIR}") - message(STATUS "Using downloaded lp_solve at: ${LP_SOLVE_DIR}") - - else() - - message(STATUS "lp_solve library found: ${LP_SOLVE_DIR}") - - endif() - - include_directories(${LP_SOLVE_DIR}) - -endfunction() diff --git a/src/external/cmake-files/QD.cmake b/src/external/cmake-files/QD.cmake deleted file mode 100644 index b65e4f51..00000000 --- a/src/external/cmake-files/QD.cmake +++ /dev/null @@ -1,54 +0,0 @@ -set(QD_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}) -function(GetQD) - find_path(QD_DIR NAMES config.h PATHS ${QD_CMAKE_DIR}/../_deps/qd-src/) - - if (NOT QD_DIR) - include(FetchContent) - set(FETCHCONTENT_BASE_DIR "${QD_CMAKE_DIR}/../_deps") - FetchContent_Declare( - qd - URL https://www.davidhbailey.com/dhbsoftware/qd-2.3.23.tar.gz - ) - - FetchContent_GetProperties(qd) - - if(NOT qd_POPULATED) - message(STATUS "QD library not found locally, downloading it.") - FetchContent_Populate(qd) - endif() - - set(QD_DIR "${qd_SOURCE_DIR}") - message(STATUS "Using downloaded QD at: ${QD_DIR}") - - else() - - message(STATUS "QD library found: ${QD_DIR}") - - endif() - - include_directories(BEFORE "${QD_DIR}/include/") - message(STATUS "configuring the QD library") - execute_process( - COMMAND ./configure - WORKING_DIRECTORY ${QD_DIR} - OUTPUT_FILE CMD_OUTPUT - RESULT_VARIABLE EXECUTE - ) - if(NOT ${EXECUTE} EQUAL "0") - message(FATAL_ERROR "./configure QD library failed") - endif() - - execute_process( - COMMAND make - WORKING_DIRECTORY ${QD_DIR} - OUTPUT_FILE qd_compilation.txt - RESULT_VARIABLE EXECUTE_MAKE - ) - - if(NOT ${EXECUTE_MAKE} EQUAL "0") - message(FATAL_ERROR "building the QD library failed") - endif() - - find_library(QD_LIB NAMES libqd.a PATHS "${QD_DIR}/src/.libs") - -endfunction() diff --git a/src/external/minimum_ellipsoid/bnmin_main.h b/src/external/minimum_ellipsoid/bnmin_main.h deleted file mode 100644 index fec751a2..00000000 --- a/src/external/minimum_ellipsoid/bnmin_main.h +++ /dev/null @@ -1,87 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis - -// This file is converted from BNMin1 (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) by Apostolos Chalkis - -// Original copyright notice: - -/** - Bojan Nikolic - Initial version 2008 - - This file is part of BNMin1 and is licensed under GNU General - Public License version 2. - - \file bnmin_main.cxx - -*/ -#ifndef BNMIN_MAIN_H -#define BNMIN_MAIN_H - -#include -#include - -#include - -//#include "bnmin_main1.h" -//#include "config.h" - -//namespace Minim { - - inline const char * version(void) - { - //return PACKAGE_VERSION; - return "11"; - } - - class BaseErr: - public std::runtime_error - { - public: - BaseErr(const std::string &s): - std::runtime_error(s) - { - } - - }; - - class NParsErr: - public BaseErr - { - public: - NParsErr(const std::string &fname, - size_t expected, - size_t received): - BaseErr( (boost::format("In function %s expected %i but received %i pars ") - % fname - % expected - % received).str()) - { - } - - - }; - - /*BaseErr::BaseErr(const std::string &s): - std::runtime_error(s) - { - } - - NParsErr::NParsErr(const std::string &fname, - size_t expected, - size_t received): - BaseErr( (boost::format("In function %s expected %i but received %i pars ") - % fname - % expected - % received).str()) - { - }*/ - - -#endif - -//} - - diff --git a/src/external/minimum_ellipsoid/khach.h b/src/external/minimum_ellipsoid/khach.h deleted file mode 100644 index 7718673a..00000000 --- a/src/external/minimum_ellipsoid/khach.h +++ /dev/null @@ -1,220 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis - -// This file is converted from BNMin1 (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) by Apostolos Chalkis - -// Original copyright notice: - -/** - Bojan Nikolic - Initial version 2010 - - This file is part of BNMin1 and is licensed under GNU General - Public License version 2 - - \file ellipsoids.cxx - - Computation and use of ellipsoids releated to sets of points -*/ -#ifndef KHACH_H -#define KHACH_H - -#include -#include -#include - -//#include "khach1.h" -//#include "mcpoint1.h" -#include "mcpoint.h" -//#include "bnmin_main1.h" -//#include "bnmin_main2.h" - -//#include "../bnmin_main.hxx" - -//namespace Minim { - - template - using MTT = Eigen::Matrix; - - template - using VTT = Eigen::Matrix; - - struct KhachiyanEllipsoid - { - Eigen::Matrix Q; - Eigen::Matrix c; - }; - - template - inline bool is_nan(const Eigen::MatrixBase& x) - { - return ((x.array() == x.array())).all(); - } - - template - bool InvertMatrix(const MTT &input, - MTT &inverse) - { - inverse = input.inverse(); - return !is_nan(inverse); - } - - - inline void InvertLP(const MTT &Lambdap, - MTT &LpInv) - { - bool res = InvertMatrix(Lambdap, LpInv); - if (not res) - { - // throw an error of your choice here! - // throw MatrixErr("Could not invert matrix: ", - // Lambdap); - } - } - - inline void Lift(const MTT &A, MTT &Ap) - { - Ap.resize(A.rows()+1, A.cols()); - Ap.topLeftCorner(A.rows(), A.cols()) = A; - Ap.row(Ap.rows()-1).setConstant(1.0); - } - - inline void genDiag(const VTT &p, MTT &res) - { - res.setZero(p.size(), p.size()); - - for(size_t i=0; i &Ap, - const VTT &p, - MTT &Lambdap) - { - - MTT dp(p.size(), p.size()); - genDiag(p, dp); - - dp = dp * Ap.transpose(); - Lambdap.noalias() = Ap * dp; - } - - inline double KhachiyanIter(const MTT &Ap, VTT &p) - { - /// Dimensionality of the problem - const size_t d = Ap.rows()-1; - - MTT Lp; - MTT M; - KaLambda(Ap, p, Lp); - MTT ILp(Lp.rows(), Lp.cols()); - InvertLP(Lp, ILp); - M.noalias() = ILp * Ap; - M = Ap.transpose() * M; - - double maxval=0; - size_t maxi=0; - for(size_t i=0; i maxval) - { - maxval=M(i,i); - maxi=i; - } - } - const double step_size=(maxval -d - 1)/((d+1)*(maxval-1)); - VTT newp = p*(1-step_size); - newp(maxi) += step_size; - - const double err= (newp-p).norm(); - p = newp; - return err; - - } - - inline void KaInvertDual(const MTT &A, - const VTT &p, - MTT &Q, - VTT &c) - { - const size_t d = A.rows(); - MTT dp(p.size(), p.size()); - genDiag(p, dp); - - MTT PN; - PN.noalias() = dp * A.transpose(); - PN = A * PN; - - VTT M2; - M2.noalias() = A * p; - - MTT M3; - M3.noalias() = M2 * M2.transpose(); - - MTT invert(PN.rows(), PN.cols()); - InvertLP(PN- M3, invert); - Q.noalias() = (invert/d); - c.noalias() = A * p; - - } - - inline double KhachiyanAlgo(const MTT &A, - double eps, - size_t maxiter, - MTT &Q, - VTT &c) - { - VTT p(A.cols()); - p.setConstant(1.0/A.cols()); - - MTT Ap; - Lift(A, Ap); - - double ceps=eps*2; - for (size_t i=0; ieps; ++i) - { - ceps=KhachiyanIter(Ap, p); - } - - KaInvertDual(A, p, Q, c); - - return ceps; - - - } - - inline double KhachiyanAlgo(const std::set &ss, - double eps, - size_t maxiter, - KhachiyanEllipsoid &res) - { - const size_t d=ss.begin()->p.size(); - MTT A(d, ss.size()); - - size_t j=0; - for (std::set::const_iterator i=ss.begin(); - i != ss.end(); - ++i) - { - for(size_t k=0; k p[k]; - ++j; - } - - MTT Q(d,d); - VTT c(d); - - const double ceps=KhachiyanAlgo(A, eps, maxiter, - Q, c); - res.Q=Q; - res.c=c; - return ceps; - } - -#endif - -//} diff --git a/src/external/minimum_ellipsoid/mcpoint.h b/src/external/minimum_ellipsoid/mcpoint.h deleted file mode 100644 index 0b9c7c5d..00000000 --- a/src/external/minimum_ellipsoid/mcpoint.h +++ /dev/null @@ -1,477 +0,0 @@ -// VolEsti (volume computation and sampling library) - -// Copyright (c) 2012-2018 Vissarion Fisikopoulos -// Copyright (c) 2018 Apostolos Chalkis - -// This file is converted from BNMin1 (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) by Apostolos Chalkis - -// Original copyright notice: - -/** - Bojan Nikolic - Initial version 2009 - - This file is part of BNMin1 and is licensed under GNU General - Public License version 2 - - \file mcpoint.cxx -*/ -#ifndef MCPOINT_H -#define MCPOINT_H - -#include -#include -#include - -#include -#include - -//exclude gsl library Apostolos Chalkis -//#include -//#include - -//#include "mcpoint1.h" -//#include "mcpoint2.h" -#include "bnmin_main.h" -//#include "bnmin_main2.h" - -//namespace Minim { - struct MCPoint - { - /// The actual parameters - std::vector p; - /// Log-likelihood of this point - double ll; - /// A vector to store derived quantities at sample of the - /// distribution - std::vector fval; - - /// Default constructor allowed, fill in the data later - MCPoint(void): - p(0), - ll(-9999), - fval(0) - { - } - - /** \Construct with supplied position vector - */ - MCPoint(const std::vector &p): - p(p), - ll(-9999), - fval(0) - { - } - - /** \brief The parameter vector has n values - */ - MCPoint(size_t np): - p(np), - ll(-9999), - fval(0) - { - } - - MCPoint(const MCPoint &other): - p(other.p), - ll(other.ll), - fval(other.fval) - { - } - - MCPoint & operator=(const MCPoint &other) - { - p=other.p; - ll=other.ll; - fval=other.fval; - return *this; - } - - - }; - - inline bool operator< (const MCPoint &a, const MCPoint &b) - { - return a.ll < b.ll; - } - - struct WPPoint: - public MCPoint - { - /** \brief Weight factor - */ - double w; - - WPPoint(void): - w(0.0) - { - } - - WPPoint(const std::vector &p, - double w): - MCPoint(p), - w(w) - { - } - - /** \brief Construct from MCPoint and a supplied weight - */ - WPPoint(const MCPoint &mp, - double w): - MCPoint(mp), - w(w) - { - } - - }; - - /* - MCPoint::MCPoint(void): - p(0), - ll(-9999), - fval(0) - { - } - - MCPoint::MCPoint(const std::vector &p): - p(p), - ll(-9999), - fval(0) - { - } - - MCPoint::MCPoint(size_t np): - p(np), - ll(-9999), - fval(0) - { - } - - MCPoint::MCPoint(const MCPoint &other): - p(other.p), - ll(other.ll), - fval(other.fval) - { - } - - MCPoint &MCPoint::operator=(const MCPoint &other) - { - p=other.p; - ll=other.ll; - fval=other.fval; - return *this; - }*/ - - - inline void moment1(const std::list &l, - std::vector &res) - { - const size_t n=l.begin()->p.size(); - res=std::vector(n, 0.0); - for(std::list::const_iterator i=l.begin(); - i!= l.end(); - ++i) - { - for (size_t j=0; jp[j] * i->w * exp(- i->ll)); - } - } - } - - inline void moment1(const std::list &l, - double Z, - std::vector &res) - { - moment1(l,res); - for(size_t j=0; j &l, - const std::vector &m1, - std::vector &res) - { - const size_t n=m1.size(); - res=std::vector(n, 0.0); - for(std::list::const_iterator i=l.begin(); - i!= l.end(); - ++i) - { - for (size_t j=0; jp[j]-m1[j],2.0) * i->w * exp(- i->ll)); - } - } - } - - inline void moment2(const std::list &l, - const std::vector &m1, - double Z, - std::vector &res) - { - moment2(l, m1, res); - for(size_t j=0; j &s, - std::vector &res) - { - const size_t n=s.begin()->p.size(); - res=std::vector(n, 0.0); - - size_t N=0; - for(std::set::const_iterator i=s.begin(); - i!= s.end(); - ++i) - { - if(i->p.size() != n) - { - throw NParsErr("moment1", n, i->p.size()); - } - for (size_t j=0; jp[j]); - } - ++N; - } - - for(size_t j=0; j &s, - const std::vector &m1, - std::vector &res) - { - const size_t n=m1.size(); - res=std::vector(n, 0.0); - - size_t N=0; - for(std::set::const_iterator i=s.begin(); - i!= s.end(); - ++i) - { - for (size_t j=0; jp[j]-m1[j], 2); - } - ++N; - } - - for(size_t j=0; j &s, - const std::vector &m1, - std::vector &res) - { - const size_t n=m1.size(); - res=std::vector(n*n, 0.0); - - size_t N=0; - for(std::set::const_iterator i=s.begin(); - i!= s.end(); - ++i) - { - for (size_t j=0; jp[j]-m1[j])*(i->p[k]-m1[k]); - } - } - ++N; - } - - for(size_t j=0; j &s, - std::vector &res) - { - std::vector m1; - moment1(s, m1); - omoment2(s, m1, res); - } - - - inline void StdDev(const std::set &s, - std::vector &res) - { - std::vector m1, m2; - moment1(s, m1); - moment2(s, m1, m2); - res.resize(m2.size()); - for(size_t j=0; j &cv, - std::vector &eigvals, - std::vector &eigvects) - { - const size_t n=sqrt(cv.size()); - gsl_matrix_view m - = gsl_matrix_view_array (const_cast(&cv[0]), n, n); - - gsl_vector *eval = gsl_vector_alloc (n); - gsl_matrix *evec = gsl_matrix_alloc (n, n); - - gsl_eigen_symmv_workspace * w = - gsl_eigen_symmv_alloc (n); - - gsl_eigen_symmv (&m.matrix, - eval, - evec, - w); - - gsl_eigen_symmv_free (w); - - gsl_eigen_symmv_sort (eval, - evec, - GSL_EIGEN_SORT_ABS_ASC); - - eigvals.resize(n); - eigvects.resize(n*n); - for(size_t j=0; j &l, - double Z, - const std::vector &low, - const std::vector &high, - size_t nbins, - std::vector &res) - { - const size_t ndim=low.size(); - - //res.resize(pow(nbins, static_cast(ndim))); - res.resize( static_cast( pow(static_cast(nbins), static_cast(ndim)) ) ); - std::fill(res.begin(), res.end(), 0.0); - - - std::vector deltas(ndim); - for(size_t i=0; i::const_iterator i=l.begin(); - i!= l.end(); - ++i) - { - bool inside=true; - size_t k=0; - for (size_t j=0; jp[j]-low[j])/deltas[j]); - if (dimi >= 0 and dimi < (int)nbins) - { - k+= dimi * static_cast( pow(static_cast(nbins), static_cast(ndim-j-1)) ); - } - else - { - inside=false; - } - } - if (inside) - { - res[k]+= i->w * exp(- i->ll); - } - } - } - - - inline void marginHist(const std::list &l, - size_t pi, - double Z, - double low, - double high, - size_t nbins, - std::vector &res) - { - res.resize(nbins); - std::fill(res.begin(), res.end(), - 0.0); - - const double d=(high-low)/nbins; - for(std::list::const_iterator i=l.begin(); - i!= l.end(); - ++i) - { - int k=int((i->p[pi]-low)/d); - if (k > 0 and k < (int)nbins) - { - res[k]+= i->w * exp(- i->ll); - } - } - - for(size_t i=0; i &l, - double Z, - size_t i, - double ilow, - double ihigh, - size_t j, - double jlow, - double jhigh, - size_t nbins, - std::vector &res) - { - // Two dimensions only - res.resize( static_cast( pow(static_cast(nbins), static_cast(2)) ) ); - std::fill(res.begin(), res.end(), - 0.0); - const double idelta=(ihigh-ilow)/nbins; - const double jdelta=(jhigh-jlow)/nbins; - - for(std::list::const_iterator p=l.begin(); - p!= l.end(); - ++p) - { - - int dimi = int((p->p[i]-ilow)/idelta); - int dimj = int((p->p[j]-jlow)/jdelta); - - if (dimi >= 0 and dimi<((int)nbins) and dimj >= 0 and dimj < ((int)nbins)) - { - const size_t k= dimi*nbins + dimj; - res[k]+= p->w * exp(- p->ll); - } - - } - } -//} - -#endif diff --git a/src/include b/src/include deleted file mode 160000 index 5f9d5661..00000000 --- a/src/include +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5f9d5661fe4fa24ee818205fdbd700e3d41381eb diff --git a/src/external/lpsolve/build/lp_solve/Makefile b/src/lpsolve/build/lp_solve/Makefile similarity index 100% rename from src/external/lpsolve/build/lp_solve/Makefile rename to src/lpsolve/build/lp_solve/Makefile diff --git a/src/external/lpsolve/build/lp_solve/colamd.c b/src/lpsolve/build/lp_solve/colamd.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/colamd.c rename to src/lpsolve/build/lp_solve/colamd.c diff --git a/src/external/lpsolve/build/lp_solve/commonlib.c b/src/lpsolve/build/lp_solve/commonlib.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/commonlib.c rename to src/lpsolve/build/lp_solve/commonlib.c diff --git a/src/external/lpsolve/build/lp_solve/ini.c b/src/lpsolve/build/lp_solve/ini.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/ini.c rename to src/lpsolve/build/lp_solve/ini.c diff --git a/src/external/lpsolve/build/lp_solve/lp_BFP1.c b/src/lpsolve/build/lp_solve/lp_BFP1.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_BFP1.c rename to src/lpsolve/build/lp_solve/lp_BFP1.c diff --git a/src/external/lpsolve/build/lp_solve/lp_BFP2.c b/src/lpsolve/build/lp_solve/lp_BFP2.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_BFP2.c rename to src/lpsolve/build/lp_solve/lp_BFP2.c diff --git a/src/external/lpsolve/build/lp_solve/lp_Hash.c b/src/lpsolve/build/lp_solve/lp_Hash.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_Hash.c rename to src/lpsolve/build/lp_solve/lp_Hash.c diff --git a/src/external/lpsolve/build/lp_solve/lp_LUSOL.c b/src/lpsolve/build/lp_solve/lp_LUSOL.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_LUSOL.c rename to src/lpsolve/build/lp_solve/lp_LUSOL.c diff --git a/src/external/lpsolve/build/lp_solve/lp_MDO.c b/src/lpsolve/build/lp_solve/lp_MDO.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_MDO.c rename to src/lpsolve/build/lp_solve/lp_MDO.c diff --git a/src/external/lpsolve/build/lp_solve/lp_MPS.c b/src/lpsolve/build/lp_solve/lp_MPS.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_MPS.c rename to src/lpsolve/build/lp_solve/lp_MPS.c diff --git a/src/external/lpsolve/build/lp_solve/lp_SOS.c b/src/lpsolve/build/lp_solve/lp_SOS.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_SOS.c rename to src/lpsolve/build/lp_solve/lp_SOS.c diff --git a/src/external/lpsolve/build/lp_solve/lp_crash.c b/src/lpsolve/build/lp_solve/lp_crash.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_crash.c rename to src/lpsolve/build/lp_solve/lp_crash.c diff --git a/src/external/lpsolve/build/lp_solve/lp_lib.c b/src/lpsolve/build/lp_solve/lp_lib.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_lib.c rename to src/lpsolve/build/lp_solve/lp_lib.c diff --git a/src/external/lpsolve/build/lp_solve/lp_matrix.c b/src/lpsolve/build/lp_solve/lp_matrix.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_matrix.c rename to src/lpsolve/build/lp_solve/lp_matrix.c diff --git a/src/external/lpsolve/build/lp_solve/lp_mipbb.c b/src/lpsolve/build/lp_solve/lp_mipbb.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_mipbb.c rename to src/lpsolve/build/lp_solve/lp_mipbb.c diff --git a/src/external/lpsolve/build/lp_solve/lp_params.c b/src/lpsolve/build/lp_solve/lp_params.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_params.c rename to src/lpsolve/build/lp_solve/lp_params.c diff --git a/src/external/lpsolve/build/lp_solve/lp_presolve.c b/src/lpsolve/build/lp_solve/lp_presolve.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_presolve.c rename to src/lpsolve/build/lp_solve/lp_presolve.c diff --git a/src/external/lpsolve/build/lp_solve/lp_price.c b/src/lpsolve/build/lp_solve/lp_price.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_price.c rename to src/lpsolve/build/lp_solve/lp_price.c diff --git a/src/external/lpsolve/build/lp_solve/lp_pricePSE.c b/src/lpsolve/build/lp_solve/lp_pricePSE.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_pricePSE.c rename to src/lpsolve/build/lp_solve/lp_pricePSE.c diff --git a/src/external/lpsolve/build/lp_solve/lp_report.c b/src/lpsolve/build/lp_solve/lp_report.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_report.c rename to src/lpsolve/build/lp_solve/lp_report.c diff --git a/src/external/lpsolve/build/lp_solve/lp_rlp.c b/src/lpsolve/build/lp_solve/lp_rlp.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_rlp.c rename to src/lpsolve/build/lp_solve/lp_rlp.c diff --git a/src/external/lpsolve/build/lp_solve/lp_scale.c b/src/lpsolve/build/lp_solve/lp_scale.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_scale.c rename to src/lpsolve/build/lp_solve/lp_scale.c diff --git a/src/external/lpsolve/build/lp_solve/lp_simplex.c b/src/lpsolve/build/lp_solve/lp_simplex.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_simplex.c rename to src/lpsolve/build/lp_solve/lp_simplex.c diff --git a/src/external/lpsolve/build/lp_solve/lp_utils.c b/src/lpsolve/build/lp_solve/lp_utils.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_utils.c rename to src/lpsolve/build/lp_solve/lp_utils.c diff --git a/src/external/lpsolve/build/lp_solve/lp_wlp.c b/src/lpsolve/build/lp_solve/lp_wlp.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lp_wlp.c rename to src/lpsolve/build/lp_solve/lp_wlp.c diff --git a/src/external/lpsolve/build/lp_solve/lusol.c b/src/lpsolve/build/lp_solve/lusol.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lusol.c rename to src/lpsolve/build/lp_solve/lusol.c diff --git a/src/external/lpsolve/build/lp_solve/lusol1.c b/src/lpsolve/build/lp_solve/lusol1.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lusol1.c rename to src/lpsolve/build/lp_solve/lusol1.c diff --git a/src/external/lpsolve/build/lp_solve/lusol2.c b/src/lpsolve/build/lp_solve/lusol2.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lusol2.c rename to src/lpsolve/build/lp_solve/lusol2.c diff --git a/src/external/lpsolve/build/lp_solve/lusol6a.c b/src/lpsolve/build/lp_solve/lusol6a.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lusol6a.c rename to src/lpsolve/build/lp_solve/lusol6a.c diff --git a/src/external/lpsolve/build/lp_solve/lusol6l0.c b/src/lpsolve/build/lp_solve/lusol6l0.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lusol6l0.c rename to src/lpsolve/build/lp_solve/lusol6l0.c diff --git a/src/external/lpsolve/build/lp_solve/lusol6u.c b/src/lpsolve/build/lp_solve/lusol6u.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lusol6u.c rename to src/lpsolve/build/lp_solve/lusol6u.c diff --git a/src/external/lpsolve/build/lp_solve/lusol7a.c b/src/lpsolve/build/lp_solve/lusol7a.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lusol7a.c rename to src/lpsolve/build/lp_solve/lusol7a.c diff --git a/src/external/lpsolve/build/lp_solve/lusol8a.c b/src/lpsolve/build/lp_solve/lusol8a.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/lusol8a.c rename to src/lpsolve/build/lp_solve/lusol8a.c diff --git a/src/external/lpsolve/build/lp_solve/mmio.c b/src/lpsolve/build/lp_solve/mmio.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/mmio.c rename to src/lpsolve/build/lp_solve/mmio.c diff --git a/src/external/lpsolve/build/lp_solve/myblas.c b/src/lpsolve/build/lp_solve/myblas.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/myblas.c rename to src/lpsolve/build/lp_solve/myblas.c diff --git a/src/external/lpsolve/build/lp_solve/yacc_read.c b/src/lpsolve/build/lp_solve/yacc_read.c similarity index 100% rename from src/external/lpsolve/build/lp_solve/yacc_read.c rename to src/lpsolve/build/lp_solve/yacc_read.c diff --git a/src/external/lpsolve/headers/include/RlpSolve.h b/src/lpsolve/headers/include/RlpSolve.h similarity index 100% rename from src/external/lpsolve/headers/include/RlpSolve.h rename to src/lpsolve/headers/include/RlpSolve.h diff --git a/src/external/lpsolve/headers/include/RlpSolveLink.h b/src/lpsolve/headers/include/RlpSolveLink.h similarity index 100% rename from src/external/lpsolve/headers/include/RlpSolveLink.h rename to src/lpsolve/headers/include/RlpSolveLink.h diff --git a/src/external/lpsolve/headers/include/colamd.h b/src/lpsolve/headers/include/colamd.h similarity index 100% rename from src/external/lpsolve/headers/include/colamd.h rename to src/lpsolve/headers/include/colamd.h diff --git a/src/external/lpsolve/headers/include/commonlib.h b/src/lpsolve/headers/include/commonlib.h similarity index 100% rename from src/external/lpsolve/headers/include/commonlib.h rename to src/lpsolve/headers/include/commonlib.h diff --git a/src/external/lpsolve/headers/include/ini.h b/src/lpsolve/headers/include/ini.h similarity index 100% rename from src/external/lpsolve/headers/include/ini.h rename to src/lpsolve/headers/include/ini.h diff --git a/src/external/lpsolve/headers/include/lp_BFP.h b/src/lpsolve/headers/include/lp_BFP.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_BFP.h rename to src/lpsolve/headers/include/lp_BFP.h diff --git a/src/external/lpsolve/headers/include/lp_Hash.h b/src/lpsolve/headers/include/lp_Hash.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_Hash.h rename to src/lpsolve/headers/include/lp_Hash.h diff --git a/src/external/lpsolve/headers/include/lp_LUSOL.h b/src/lpsolve/headers/include/lp_LUSOL.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_LUSOL.h rename to src/lpsolve/headers/include/lp_LUSOL.h diff --git a/src/external/lpsolve/headers/include/lp_MDO.h b/src/lpsolve/headers/include/lp_MDO.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_MDO.h rename to src/lpsolve/headers/include/lp_MDO.h diff --git a/src/external/lpsolve/headers/include/lp_MPS.h b/src/lpsolve/headers/include/lp_MPS.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_MPS.h rename to src/lpsolve/headers/include/lp_MPS.h diff --git a/src/external/lpsolve/headers/include/lp_SOS.h b/src/lpsolve/headers/include/lp_SOS.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_SOS.h rename to src/lpsolve/headers/include/lp_SOS.h diff --git a/src/external/lpsolve/headers/include/lp_bit.h b/src/lpsolve/headers/include/lp_bit.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_bit.h rename to src/lpsolve/headers/include/lp_bit.h diff --git a/src/external/lpsolve/headers/include/lp_crash.h b/src/lpsolve/headers/include/lp_crash.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_crash.h rename to src/lpsolve/headers/include/lp_crash.h diff --git a/src/external/lpsolve/headers/include/lp_lib.h b/src/lpsolve/headers/include/lp_lib.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_lib.h rename to src/lpsolve/headers/include/lp_lib.h diff --git a/src/external/lpsolve/headers/include/lp_matrix.h b/src/lpsolve/headers/include/lp_matrix.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_matrix.h rename to src/lpsolve/headers/include/lp_matrix.h diff --git a/src/external/lpsolve/headers/include/lp_mipbb.h b/src/lpsolve/headers/include/lp_mipbb.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_mipbb.h rename to src/lpsolve/headers/include/lp_mipbb.h diff --git a/src/external/lpsolve/headers/include/lp_presolve.h b/src/lpsolve/headers/include/lp_presolve.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_presolve.h rename to src/lpsolve/headers/include/lp_presolve.h diff --git a/src/external/lpsolve/headers/include/lp_price.h b/src/lpsolve/headers/include/lp_price.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_price.h rename to src/lpsolve/headers/include/lp_price.h diff --git a/src/external/lpsolve/headers/include/lp_pricePSE.h b/src/lpsolve/headers/include/lp_pricePSE.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_pricePSE.h rename to src/lpsolve/headers/include/lp_pricePSE.h diff --git a/src/external/lpsolve/headers/include/lp_report.h b/src/lpsolve/headers/include/lp_report.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_report.h rename to src/lpsolve/headers/include/lp_report.h diff --git a/src/external/lpsolve/headers/include/lp_rlp.h b/src/lpsolve/headers/include/lp_rlp.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_rlp.h rename to src/lpsolve/headers/include/lp_rlp.h diff --git a/src/external/lpsolve/headers/include/lp_scale.h b/src/lpsolve/headers/include/lp_scale.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_scale.h rename to src/lpsolve/headers/include/lp_scale.h diff --git a/src/external/lpsolve/headers/include/lp_simplex.h b/src/lpsolve/headers/include/lp_simplex.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_simplex.h rename to src/lpsolve/headers/include/lp_simplex.h diff --git a/src/external/lpsolve/headers/include/lp_types.h b/src/lpsolve/headers/include/lp_types.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_types.h rename to src/lpsolve/headers/include/lp_types.h diff --git a/src/external/lpsolve/headers/include/lp_utils.h b/src/lpsolve/headers/include/lp_utils.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_utils.h rename to src/lpsolve/headers/include/lp_utils.h diff --git a/src/external/lpsolve/headers/include/lp_wlp.h b/src/lpsolve/headers/include/lp_wlp.h similarity index 100% rename from src/external/lpsolve/headers/include/lp_wlp.h rename to src/lpsolve/headers/include/lp_wlp.h diff --git a/src/external/lpsolve/headers/include/lpkit.h b/src/lpsolve/headers/include/lpkit.h similarity index 100% rename from src/external/lpsolve/headers/include/lpkit.h rename to src/lpsolve/headers/include/lpkit.h diff --git a/src/external/lpsolve/headers/include/lusol.h b/src/lpsolve/headers/include/lusol.h similarity index 100% rename from src/external/lpsolve/headers/include/lusol.h rename to src/lpsolve/headers/include/lusol.h diff --git a/src/external/lpsolve/headers/include/mmio.h b/src/lpsolve/headers/include/mmio.h similarity index 100% rename from src/external/lpsolve/headers/include/mmio.h rename to src/lpsolve/headers/include/mmio.h diff --git a/src/external/lpsolve/headers/include/myblas.h b/src/lpsolve/headers/include/myblas.h similarity index 100% rename from src/external/lpsolve/headers/include/myblas.h rename to src/lpsolve/headers/include/myblas.h diff --git a/src/external/lpsolve/headers/include/yacc_read.h b/src/lpsolve/headers/include/yacc_read.h similarity index 100% rename from src/external/lpsolve/headers/include/yacc_read.h rename to src/lpsolve/headers/include/yacc_read.h diff --git a/src/external/lpsolve/headers/run_headers/colamd.h b/src/lpsolve/headers/run_headers/colamd.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/colamd.h rename to src/lpsolve/headers/run_headers/colamd.h diff --git a/src/external/lpsolve/headers/run_headers/commonlib.h b/src/lpsolve/headers/run_headers/commonlib.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/commonlib.h rename to src/lpsolve/headers/run_headers/commonlib.h diff --git a/src/external/lpsolve/headers/run_headers/declare.h b/src/lpsolve/headers/run_headers/declare.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/declare.h rename to src/lpsolve/headers/run_headers/declare.h diff --git a/src/external/lpsolve/headers/run_headers/fortify.h b/src/lpsolve/headers/run_headers/fortify.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/fortify.h rename to src/lpsolve/headers/run_headers/fortify.h diff --git a/src/external/lpsolve/headers/run_headers/hbio.h b/src/lpsolve/headers/run_headers/hbio.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/hbio.h rename to src/lpsolve/headers/run_headers/hbio.h diff --git a/src/external/lpsolve/headers/run_headers/ini.h b/src/lpsolve/headers/run_headers/ini.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/ini.h rename to src/lpsolve/headers/run_headers/ini.h diff --git a/src/external/lpsolve/headers/run_headers/lp_BFP.h b/src/lpsolve/headers/run_headers/lp_BFP.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_BFP.h rename to src/lpsolve/headers/run_headers/lp_BFP.h diff --git a/src/external/lpsolve/headers/run_headers/lp_BFP1.h b/src/lpsolve/headers/run_headers/lp_BFP1.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_BFP1.h rename to src/lpsolve/headers/run_headers/lp_BFP1.h diff --git a/src/external/lpsolve/headers/run_headers/lp_BFP2.h b/src/lpsolve/headers/run_headers/lp_BFP2.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_BFP2.h rename to src/lpsolve/headers/run_headers/lp_BFP2.h diff --git a/src/external/lpsolve/headers/run_headers/lp_Hash.c b/src/lpsolve/headers/run_headers/lp_Hash.c similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_Hash.c rename to src/lpsolve/headers/run_headers/lp_Hash.c diff --git a/src/external/lpsolve/headers/run_headers/lp_Hash.h b/src/lpsolve/headers/run_headers/lp_Hash.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_Hash.h rename to src/lpsolve/headers/run_headers/lp_Hash.h diff --git a/src/external/lpsolve/headers/run_headers/lp_LUSOL.h b/src/lpsolve/headers/run_headers/lp_LUSOL.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_LUSOL.h rename to src/lpsolve/headers/run_headers/lp_LUSOL.h diff --git a/src/external/lpsolve/headers/run_headers/lp_MDO.h b/src/lpsolve/headers/run_headers/lp_MDO.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_MDO.h rename to src/lpsolve/headers/run_headers/lp_MDO.h diff --git a/src/external/lpsolve/headers/run_headers/lp_MPS.h b/src/lpsolve/headers/run_headers/lp_MPS.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_MPS.h rename to src/lpsolve/headers/run_headers/lp_MPS.h diff --git a/src/external/lpsolve/headers/run_headers/lp_SOS.h b/src/lpsolve/headers/run_headers/lp_SOS.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_SOS.h rename to src/lpsolve/headers/run_headers/lp_SOS.h diff --git a/src/external/lpsolve/headers/run_headers/lp_crash.c b/src/lpsolve/headers/run_headers/lp_crash.c similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_crash.c rename to src/lpsolve/headers/run_headers/lp_crash.c diff --git a/src/external/lpsolve/headers/run_headers/lp_crash.h b/src/lpsolve/headers/run_headers/lp_crash.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_crash.h rename to src/lpsolve/headers/run_headers/lp_crash.h diff --git a/src/external/lpsolve/headers/run_headers/lp_explicit.h b/src/lpsolve/headers/run_headers/lp_explicit.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_explicit.h rename to src/lpsolve/headers/run_headers/lp_explicit.h diff --git a/src/external/lpsolve/headers/run_headers/lp_fortify.h b/src/lpsolve/headers/run_headers/lp_fortify.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_fortify.h rename to src/lpsolve/headers/run_headers/lp_fortify.h diff --git a/src/external/lpsolve/headers/run_headers/lp_lib.h b/src/lpsolve/headers/run_headers/lp_lib.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_lib.h rename to src/lpsolve/headers/run_headers/lp_lib.h diff --git a/src/external/lpsolve/headers/run_headers/lp_matrix.h b/src/lpsolve/headers/run_headers/lp_matrix.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_matrix.h rename to src/lpsolve/headers/run_headers/lp_matrix.h diff --git a/src/external/lpsolve/headers/run_headers/lp_mipbb.h b/src/lpsolve/headers/run_headers/lp_mipbb.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_mipbb.h rename to src/lpsolve/headers/run_headers/lp_mipbb.h diff --git a/src/external/lpsolve/headers/run_headers/lp_presolve.h b/src/lpsolve/headers/run_headers/lp_presolve.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_presolve.h rename to src/lpsolve/headers/run_headers/lp_presolve.h diff --git a/src/external/lpsolve/headers/run_headers/lp_price.h b/src/lpsolve/headers/run_headers/lp_price.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_price.h rename to src/lpsolve/headers/run_headers/lp_price.h diff --git a/src/external/lpsolve/headers/run_headers/lp_pricePSE.h b/src/lpsolve/headers/run_headers/lp_pricePSE.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_pricePSE.h rename to src/lpsolve/headers/run_headers/lp_pricePSE.h diff --git a/src/external/lpsolve/headers/run_headers/lp_report.h b/src/lpsolve/headers/run_headers/lp_report.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_report.h rename to src/lpsolve/headers/run_headers/lp_report.h diff --git a/src/external/lpsolve/headers/run_headers/lp_rlp.h b/src/lpsolve/headers/run_headers/lp_rlp.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_rlp.h rename to src/lpsolve/headers/run_headers/lp_rlp.h diff --git a/src/external/lpsolve/headers/run_headers/lp_scale.h b/src/lpsolve/headers/run_headers/lp_scale.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_scale.h rename to src/lpsolve/headers/run_headers/lp_scale.h diff --git a/src/external/lpsolve/headers/run_headers/lp_simplex.h b/src/lpsolve/headers/run_headers/lp_simplex.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_simplex.h rename to src/lpsolve/headers/run_headers/lp_simplex.h diff --git a/src/external/lpsolve/headers/run_headers/lp_types.h b/src/lpsolve/headers/run_headers/lp_types.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_types.h rename to src/lpsolve/headers/run_headers/lp_types.h diff --git a/src/external/lpsolve/headers/run_headers/lp_utils.h b/src/lpsolve/headers/run_headers/lp_utils.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_utils.h rename to src/lpsolve/headers/run_headers/lp_utils.h diff --git a/src/external/lpsolve/headers/run_headers/lp_wlp.h b/src/lpsolve/headers/run_headers/lp_wlp.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lp_wlp.h rename to src/lpsolve/headers/run_headers/lp_wlp.h diff --git a/src/external/lpsolve/headers/run_headers/lpkit.h b/src/lpsolve/headers/run_headers/lpkit.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lpkit.h rename to src/lpsolve/headers/run_headers/lpkit.h diff --git a/src/external/lpsolve/headers/run_headers/lpsolve.h b/src/lpsolve/headers/run_headers/lpsolve.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lpsolve.h rename to src/lpsolve/headers/run_headers/lpsolve.h diff --git a/src/external/lpsolve/headers/run_headers/lusol.h b/src/lpsolve/headers/run_headers/lusol.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusol.h rename to src/lpsolve/headers/run_headers/lusol.h diff --git a/src/external/lpsolve/headers/run_headers/lusol1.h b/src/lpsolve/headers/run_headers/lusol1.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusol1.h rename to src/lpsolve/headers/run_headers/lusol1.h diff --git a/src/external/lpsolve/headers/run_headers/lusol2.h b/src/lpsolve/headers/run_headers/lusol2.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusol2.h rename to src/lpsolve/headers/run_headers/lusol2.h diff --git a/src/external/lpsolve/headers/run_headers/lusol6a.h b/src/lpsolve/headers/run_headers/lusol6a.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusol6a.h rename to src/lpsolve/headers/run_headers/lusol6a.h diff --git a/src/external/lpsolve/headers/run_headers/lusol6l0.h b/src/lpsolve/headers/run_headers/lusol6l0.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusol6l0.h rename to src/lpsolve/headers/run_headers/lusol6l0.h diff --git a/src/external/lpsolve/headers/run_headers/lusol6u.h b/src/lpsolve/headers/run_headers/lusol6u.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusol6u.h rename to src/lpsolve/headers/run_headers/lusol6u.h diff --git a/src/external/lpsolve/headers/run_headers/lusol7a.h b/src/lpsolve/headers/run_headers/lusol7a.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusol7a.h rename to src/lpsolve/headers/run_headers/lusol7a.h diff --git a/src/external/lpsolve/headers/run_headers/lusol8a.h b/src/lpsolve/headers/run_headers/lusol8a.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusol8a.h rename to src/lpsolve/headers/run_headers/lusol8a.h diff --git a/src/external/lpsolve/headers/run_headers/lusolio.h b/src/lpsolve/headers/run_headers/lusolio.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/lusolio.h rename to src/lpsolve/headers/run_headers/lusolio.h diff --git a/src/external/lpsolve/headers/run_headers/mmio.h b/src/lpsolve/headers/run_headers/mmio.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/mmio.h rename to src/lpsolve/headers/run_headers/mmio.h diff --git a/src/external/lpsolve/headers/run_headers/myblas.h b/src/lpsolve/headers/run_headers/myblas.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/myblas.h rename to src/lpsolve/headers/run_headers/myblas.h diff --git a/src/external/lpsolve/headers/run_headers/sparselib.h b/src/lpsolve/headers/run_headers/sparselib.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/sparselib.h rename to src/lpsolve/headers/run_headers/sparselib.h diff --git a/src/external/lpsolve/headers/run_headers/ufortify.h b/src/lpsolve/headers/run_headers/ufortify.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/ufortify.h rename to src/lpsolve/headers/run_headers/ufortify.h diff --git a/src/external/lpsolve/headers/run_headers/yacc_read.h b/src/lpsolve/headers/run_headers/yacc_read.h similarity index 100% rename from src/external/lpsolve/headers/run_headers/yacc_read.h rename to src/lpsolve/headers/run_headers/yacc_read.h diff --git a/src/volesti b/src/volesti new file mode 160000 index 00000000..5bf9188f --- /dev/null +++ b/src/volesti @@ -0,0 +1 @@ +Subproject commit 5bf9188fb11fddab17de98fabc505e993216ca5f From 91c28b354e356242b2011d431fd81359e0e0b588 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Thu, 29 Feb 2024 12:08:06 +0200 Subject: [PATCH 13/17] Update with lpSolve 5.6.19 --- src/Makevars | 10 +- src/Makevars.win | 10 +- src/lpSolve/DESCRIPTION | 19 + src/lpSolve/MD5 | 94 + src/lpSolve/NAMESPACE | 3 + src/lpSolve/R/lp.R | 273 ++ src/lpSolve/R/lp.assign.R | 114 + src/lpSolve/R/lp.transport.R | 155 + src/lpSolve/R/make.q8.R | 47 + src/lpSolve/R/print.lp.R | 11 + src/lpSolve/R/zzz.R | 3 + src/lpSolve/man/lp.Rd | 136 + src/lpSolve/man/lp.assign.Rd | 56 + src/lpSolve/man/lp.object.Rd | 22 + src/lpSolve/man/lp.transport.Rd | 84 + src/lpSolve/man/make.q8.Rd | 21 + src/lpSolve/man/print.lp.Rd | 25 + .../build/lp_solve => lpSolve/src}/Makefile | 4 +- src/lpSolve/src/Makevars | 1 + src/lpSolve/src/Makevars.win | 1 + .../build/lp_solve => lpSolve/src}/colamd.c | 3 +- .../run_headers => lpSolve/src}/colamd.h | 0 .../lp_solve => lpSolve/src}/commonlib.c | 205 +- .../run_headers => lpSolve/src}/commonlib.h | 0 .../run_headers => lpSolve/src}/declare.h | 0 .../run_headers => lpSolve/src}/fortify.h | 0 src/lpSolve/src/hbio.c | 1631 ++++++++ .../run_headers => lpSolve/src}/hbio.h | 0 .../build/lp_solve => lpSolve/src}/ini.c | 4 +- .../headers/run_headers => lpSolve/src}/ini.h | 0 src/lpSolve/src/init.c | 22 + src/lpSolve/src/isfixedvar.c | 19 + .../run_headers => lpSolve/src}/lp_BFP.h | 0 .../run_headers => lpSolve/src}/lp_BFP1.h | 0 .../run_headers => lpSolve/src}/lp_BFP2.h | 0 .../run_headers => lpSolve/src}/lp_Hash.c | 0 .../run_headers => lpSolve/src}/lp_Hash.h | 0 .../build/lp_solve => lpSolve/src}/lp_LUSOL.c | 75 +- .../run_headers => lpSolve/src}/lp_LUSOL.h | 0 .../build/lp_solve => lpSolve/src}/lp_MDO.c | 4 +- .../headers/include => lpSolve/src}/lp_MDO.h | 0 .../build/lp_solve => lpSolve/src}/lp_MPS.c | 498 +-- .../run_headers => lpSolve/src}/lp_MPS.h | 0 .../build/lp_solve => lpSolve/src}/lp_SOS.c | 73 +- .../run_headers => lpSolve/src}/lp_SOS.h | 2 +- .../run_headers => lpSolve/src}/lp_crash.c | 0 .../run_headers => lpSolve/src}/lp_crash.h | 0 .../run_headers => lpSolve/src}/lp_explicit.h | 0 .../run_headers => lpSolve/src}/lp_fortify.h | 0 .../build/lp_solve => lpSolve/src}/lp_lib.c | 1550 +++---- .../run_headers => lpSolve/src}/lp_lib.h | 0 .../lp_solve => lpSolve/src}/lp_matrix.c | 706 +--- .../run_headers => lpSolve/src}/lp_matrix.h | 6 +- .../build/lp_solve => lpSolve/src}/lp_mipbb.c | 146 +- .../run_headers => lpSolve/src}/lp_mipbb.h | 0 .../lp_solve => lpSolve/src}/lp_params.c | 68 +- .../lp_solve => lpSolve/src}/lp_presolve.c | 578 +-- .../run_headers => lpSolve/src}/lp_presolve.h | 0 .../build/lp_solve => lpSolve/src}/lp_price.c | 137 +- .../run_headers => lpSolve/src}/lp_price.h | 0 .../lp_solve => lpSolve/src}/lp_pricePSE.c | 45 +- .../run_headers => lpSolve/src}/lp_pricePSE.h | 0 .../lp_solve => lpSolve/src}/lp_report.c | 66 +- .../run_headers => lpSolve/src}/lp_report.h | 0 src/lpSolve/src/lp_rlp.c | 1477 +++++++ .../run_headers => lpSolve/src}/lp_rlp.h | 6 +- .../build/lp_solve => lpSolve/src}/lp_scale.c | 132 +- .../run_headers => lpSolve/src}/lp_scale.h | 0 .../lp_solve => lpSolve/src}/lp_simplex.c | 98 +- .../run_headers => lpSolve/src}/lp_simplex.h | 0 .../run_headers => lpSolve/src}/lp_types.h | 1 - .../build/lp_solve => lpSolve/src}/lp_utils.c | 141 +- .../run_headers => lpSolve/src}/lp_utils.h | 0 .../build/lp_solve => lpSolve/src}/lp_wlp.c | 248 +- .../headers/include => lpSolve/src}/lp_wlp.h | 0 .../run_headers => lpSolve/src}/lpkit.h | 0 src/lpSolve/src/lpslink56.c | 633 +++ .../run_headers => lpSolve/src}/lpsolve.h | 0 .../build/lp_solve => lpSolve/src}/lusol.c | 89 +- .../run_headers => lpSolve/src}/lusol.h | 0 .../run_headers => lpSolve/src}/lusol1.h | 0 .../run_headers => lpSolve/src}/lusol2.h | 0 .../run_headers => lpSolve/src}/lusol6a.h | 0 .../run_headers => lpSolve/src}/lusol6l0.h | 0 .../run_headers => lpSolve/src}/lusol6u.h | 0 .../run_headers => lpSolve/src}/lusol7a.h | 0 .../run_headers => lpSolve/src}/lusol8a.h | 0 src/lpSolve/src/lusolio.c | 299 ++ .../run_headers => lpSolve/src}/lusolio.h | 0 .../build/lp_solve => lpSolve/src}/mmio.c | 193 +- .../run_headers => lpSolve/src}/mmio.h | 0 src/lpSolve/src/myblas.c | 849 ++++ .../run_headers => lpSolve/src}/myblas.h | 0 src/lpSolve/src/sparselib.c | 967 +++++ .../run_headers => lpSolve/src}/sparselib.h | 0 .../run_headers => lpSolve/src}/ufortify.h | 0 src/lpSolve/src/yacc_read.c | 1114 +++++ .../run_headers => lpSolve/src}/yacc_read.h | 4 +- src/lpsolve/build/lp_solve/lp_BFP1.c | 206 - src/lpsolve/build/lp_solve/lp_BFP2.c | 177 - src/lpsolve/build/lp_solve/lp_Hash.c | 238 -- src/lpsolve/build/lp_solve/lp_crash.c | 863 ---- src/lpsolve/build/lp_solve/lp_rlp.c | 2484 ----------- src/lpsolve/build/lp_solve/lusol1.c | 3725 ----------------- src/lpsolve/build/lp_solve/lusol2.c | 204 - src/lpsolve/build/lp_solve/lusol6a.c | 867 ---- src/lpsolve/build/lp_solve/lusol6l0.c | 163 - src/lpsolve/build/lp_solve/lusol6u.c | 176 - src/lpsolve/build/lp_solve/lusol7a.c | 704 ---- src/lpsolve/build/lp_solve/lusol8a.c | 279 -- src/lpsolve/build/lp_solve/myblas.c | 102 - src/lpsolve/build/lp_solve/yacc_read.c | 1289 ------ src/lpsolve/headers/include/RlpSolve.h | 15 - src/lpsolve/headers/include/RlpSolveLink.h | 294 -- src/lpsolve/headers/include/colamd.h | 286 -- src/lpsolve/headers/include/commonlib.h | 334 -- src/lpsolve/headers/include/ini.h | 17 - src/lpsolve/headers/include/lp_BFP.h | 82 - src/lpsolve/headers/include/lp_Hash.h | 43 - src/lpsolve/headers/include/lp_LUSOL.h | 63 - src/lpsolve/headers/include/lp_MPS.h | 33 - src/lpsolve/headers/include/lp_SOS.h | 108 - src/lpsolve/headers/include/lp_bit.h | 17 - src/lpsolve/headers/include/lp_crash.h | 27 - src/lpsolve/headers/include/lp_lib.h | 2297 ---------- src/lpsolve/headers/include/lp_matrix.h | 257 -- src/lpsolve/headers/include/lp_mipbb.h | 64 - src/lpsolve/headers/include/lp_presolve.h | 111 - src/lpsolve/headers/include/lp_price.h | 99 - src/lpsolve/headers/include/lp_pricePSE.h | 28 - src/lpsolve/headers/include/lp_report.h | 42 - src/lpsolve/headers/include/lp_rlp.h | 2453 ----------- src/lpsolve/headers/include/lp_scale.h | 31 - src/lpsolve/headers/include/lp_simplex.h | 34 - src/lpsolve/headers/include/lp_types.h | 330 -- src/lpsolve/headers/include/lp_utils.h | 146 - src/lpsolve/headers/include/lpkit.h | 34 - src/lpsolve/headers/include/lusol.h | 357 -- src/lpsolve/headers/include/mmio.h | 134 - src/lpsolve/headers/include/myblas.h | 50 - src/lpsolve/headers/include/yacc_read.h | 57 - src/lpsolve/headers/run_headers/lp_MDO.h | 18 - src/lpsolve/headers/run_headers/lp_wlp.h | 21 - 143 files changed, 9880 insertions(+), 22657 deletions(-) create mode 100644 src/lpSolve/DESCRIPTION create mode 100644 src/lpSolve/MD5 create mode 100644 src/lpSolve/NAMESPACE create mode 100644 src/lpSolve/R/lp.R create mode 100644 src/lpSolve/R/lp.assign.R create mode 100644 src/lpSolve/R/lp.transport.R create mode 100644 src/lpSolve/R/make.q8.R create mode 100644 src/lpSolve/R/print.lp.R create mode 100644 src/lpSolve/R/zzz.R create mode 100644 src/lpSolve/man/lp.Rd create mode 100644 src/lpSolve/man/lp.assign.Rd create mode 100644 src/lpSolve/man/lp.object.Rd create mode 100644 src/lpSolve/man/lp.transport.Rd create mode 100644 src/lpSolve/man/make.q8.Rd create mode 100644 src/lpSolve/man/print.lp.Rd rename src/{lpsolve/build/lp_solve => lpSolve/src}/Makefile (91%) create mode 100644 src/lpSolve/src/Makevars create mode 100644 src/lpSolve/src/Makevars.win rename src/{lpsolve/build/lp_solve => lpSolve/src}/colamd.c (99%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/colamd.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/commonlib.c (73%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/commonlib.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/declare.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/fortify.h (100%) create mode 100644 src/lpSolve/src/hbio.c rename src/{lpsolve/headers/run_headers => lpSolve/src}/hbio.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/ini.c (96%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/ini.h (100%) create mode 100644 src/lpSolve/src/init.c create mode 100644 src/lpSolve/src/isfixedvar.c rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_BFP.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_BFP1.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_BFP2.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_Hash.c (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_Hash.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_LUSOL.c (90%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_LUSOL.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_MDO.c (99%) rename src/{lpsolve/headers/include => lpSolve/src}/lp_MDO.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_MPS.c (77%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_MPS.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_SOS.c (95%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_SOS.h (99%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_crash.c (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_crash.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_explicit.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_fortify.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_lib.c (86%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_lib.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_matrix.c (83%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_matrix.h (99%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_mipbb.c (90%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_mipbb.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_params.c (90%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_presolve.c (91%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_presolve.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_price.c (94%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_price.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_pricePSE.c (93%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_pricePSE.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_report.c (95%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_report.h (100%) create mode 100644 src/lpSolve/src/lp_rlp.c rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_rlp.h (99%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_scale.c (88%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_scale.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_simplex.c (96%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_simplex.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_types.h (98%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_utils.c (90%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lp_utils.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lp_wlp.c (56%) rename src/{lpsolve/headers/include => lpSolve/src}/lp_wlp.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lpkit.h (100%) create mode 100644 src/lpSolve/src/lpslink56.c rename src/{lpsolve/headers/run_headers => lpSolve/src}/lpsolve.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/lusol.c (91%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusol.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusol1.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusol2.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusol6a.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusol6l0.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusol6u.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusol7a.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusol8a.h (100%) create mode 100644 src/lpSolve/src/lusolio.c rename src/{lpsolve/headers/run_headers => lpSolve/src}/lusolio.h (100%) rename src/{lpsolve/build/lp_solve => lpSolve/src}/mmio.c (81%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/mmio.h (100%) create mode 100644 src/lpSolve/src/myblas.c rename src/{lpsolve/headers/run_headers => lpSolve/src}/myblas.h (100%) create mode 100644 src/lpSolve/src/sparselib.c rename src/{lpsolve/headers/run_headers => lpSolve/src}/sparselib.h (100%) rename src/{lpsolve/headers/run_headers => lpSolve/src}/ufortify.h (100%) create mode 100644 src/lpSolve/src/yacc_read.c rename src/{lpsolve/headers/run_headers => lpSolve/src}/yacc_read.h (92%) delete mode 100644 src/lpsolve/build/lp_solve/lp_BFP1.c delete mode 100644 src/lpsolve/build/lp_solve/lp_BFP2.c delete mode 100644 src/lpsolve/build/lp_solve/lp_Hash.c delete mode 100644 src/lpsolve/build/lp_solve/lp_crash.c delete mode 100644 src/lpsolve/build/lp_solve/lp_rlp.c delete mode 100644 src/lpsolve/build/lp_solve/lusol1.c delete mode 100644 src/lpsolve/build/lp_solve/lusol2.c delete mode 100644 src/lpsolve/build/lp_solve/lusol6a.c delete mode 100644 src/lpsolve/build/lp_solve/lusol6l0.c delete mode 100644 src/lpsolve/build/lp_solve/lusol6u.c delete mode 100644 src/lpsolve/build/lp_solve/lusol7a.c delete mode 100644 src/lpsolve/build/lp_solve/lusol8a.c delete mode 100644 src/lpsolve/build/lp_solve/myblas.c delete mode 100644 src/lpsolve/build/lp_solve/yacc_read.c delete mode 100644 src/lpsolve/headers/include/RlpSolve.h delete mode 100644 src/lpsolve/headers/include/RlpSolveLink.h delete mode 100644 src/lpsolve/headers/include/colamd.h delete mode 100644 src/lpsolve/headers/include/commonlib.h delete mode 100644 src/lpsolve/headers/include/ini.h delete mode 100644 src/lpsolve/headers/include/lp_BFP.h delete mode 100644 src/lpsolve/headers/include/lp_Hash.h delete mode 100644 src/lpsolve/headers/include/lp_LUSOL.h delete mode 100644 src/lpsolve/headers/include/lp_MPS.h delete mode 100644 src/lpsolve/headers/include/lp_SOS.h delete mode 100644 src/lpsolve/headers/include/lp_bit.h delete mode 100644 src/lpsolve/headers/include/lp_crash.h delete mode 100644 src/lpsolve/headers/include/lp_lib.h delete mode 100644 src/lpsolve/headers/include/lp_matrix.h delete mode 100644 src/lpsolve/headers/include/lp_mipbb.h delete mode 100644 src/lpsolve/headers/include/lp_presolve.h delete mode 100644 src/lpsolve/headers/include/lp_price.h delete mode 100644 src/lpsolve/headers/include/lp_pricePSE.h delete mode 100644 src/lpsolve/headers/include/lp_report.h delete mode 100644 src/lpsolve/headers/include/lp_rlp.h delete mode 100644 src/lpsolve/headers/include/lp_scale.h delete mode 100644 src/lpsolve/headers/include/lp_simplex.h delete mode 100644 src/lpsolve/headers/include/lp_types.h delete mode 100644 src/lpsolve/headers/include/lp_utils.h delete mode 100644 src/lpsolve/headers/include/lpkit.h delete mode 100644 src/lpsolve/headers/include/lusol.h delete mode 100644 src/lpsolve/headers/include/mmio.h delete mode 100644 src/lpsolve/headers/include/myblas.h delete mode 100644 src/lpsolve/headers/include/yacc_read.h delete mode 100644 src/lpsolve/headers/run_headers/lp_MDO.h delete mode 100644 src/lpsolve/headers/run_headers/lp_wlp.h diff --git a/src/Makevars b/src/Makevars index 9fd194e7..69d45b5b 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,12 +1,12 @@ -PKG_CPPFLAGS=-Ivolesti/external -Ilpsolve/headers/run_headers -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra +PKG_CPPFLAGS=-Ivolesti/external -IlpSolve/src -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra PKG_CXXFLAGS= -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-Llpsolve/build/lp_solve -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-LlpSolve/src -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): lpsolve/build/lp_solve/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a +$(SHLIB): lpSolve/src/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a -lpsolve/build/lp_solve/liblp_solve.a: - @(cd lpsolve/build/lp_solve && $(MAKE) liblp_solve.a \ +lpSolve/src/liblp_solve.a: + @(cd lpSolve/src && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" RANLIB="$(RANLIB)") diff --git a/src/Makevars.win b/src/Makevars.win index 093db7e9..27abfb91 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,12 +1,12 @@ -PKG_CPPFLAGS=-Ivolesti/external -Ilpsolve/headers/run_headers -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra +PKG_CPPFLAGS=-Ivolesti/external -IlpSolve/src -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra PKG_CXXFLAGS= -lm -ldl -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-Llpsolve/build/lp_solve -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-LlpSolve/src -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): lpsolve/build/lp_solve/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a +$(SHLIB): lpSolve/src/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a -lpsolve/build/lp_solve/liblp_solve.a: - @(cd lpsolve/build/lp_solve && $(MAKE) liblp_solve.a \ +lpSolve/src/liblp_solve.a: + @(cd lpSolve/src && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS) -DUSRDLL -DINLINE=static" \ CFLAGS="$(CFLAGS)" CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" \ RANLIB="$(RANLIB)") diff --git a/src/lpSolve/DESCRIPTION b/src/lpSolve/DESCRIPTION new file mode 100644 index 00000000..a7942b4e --- /dev/null +++ b/src/lpSolve/DESCRIPTION @@ -0,0 +1,19 @@ +Package: lpSolve +Version: 5.6.19 +Title: Interface to 'Lp_solve' v. 5.5 to Solve Linear/Integer Programs +Author: Michel Berkelaar and others +Maintainer: Gábor Csárdi +Description: Lp_solve is freely available (under LGPL 2) software for + solving linear, integer and mixed integer programs. In this + implementation we supply a "wrapper" function in C and some R + functions that solve general linear/integer problems, + assignment problems, and transportation problems. This version + calls lp_solve version 5.5. +License: LGPL-2 +URL: https://github.com/gaborcsardi/lpSolve +BugReports: https://github.com/gaborcsardi/lpSolve/issues +Encoding: UTF-8 +NeedsCompilation: yes +Packaged: 2023-09-12 18:07:47 UTC; gaborcsardi +Repository: CRAN +Date/Publication: 2023-09-13 11:20:02 UTC diff --git a/src/lpSolve/MD5 b/src/lpSolve/MD5 new file mode 100644 index 00000000..5583d4b7 --- /dev/null +++ b/src/lpSolve/MD5 @@ -0,0 +1,94 @@ +eed30fcee198872214cb36077153750d *DESCRIPTION +5d954745be287207485c9af3411e4a9d *NAMESPACE +c3c52167c5e85957227571cad076ff4f *R/lp.R +89b2e3700cc47dcb8fb39f951603e008 *R/lp.assign.R +693f4392cf60f9cf8b89ffca74dc7126 *R/lp.transport.R +1b08b692aa9466144a38a6610140141d *R/make.q8.R +e8c949571124717686a90610235a4e01 *R/print.lp.R +82d9d64c4e3e654e990c97ab7e4a599e *R/zzz.R +4e2f2ffcdec5627a6f1eceb7920300a6 *man/lp.Rd +495aa3d5e82dd181e4c6c51b4886d983 *man/lp.assign.Rd +06c29ffbfa3881831f21cb49ac4e0761 *man/lp.object.Rd +d037a6837540cf8e544b484f8e00a830 *man/lp.transport.Rd +cae07d4610fee186d872be8b72b4a3a4 *man/make.q8.Rd +a002ef080a26026bc3c5f043b9b036c0 *man/print.lp.Rd +50b1d9e03d07b9e0c2181f6b290ae5e3 *src/Makevars +c2ab53abec8229377522a31738ba7b10 *src/Makevars.win +76e3867049928b1eb34a883c54bbb7cb *src/colamd.c +85250c070bb1c6f41cb2ba4b70f0f3ab *src/colamd.h +149cb4963976fa10b7f88c492ea967d0 *src/commonlib.c +d411f086bd6e138a7c1b00a71c7362b8 *src/commonlib.h +6bafa998341646f528777d87971d79f8 *src/declare.h +0c7ea81c7229f9afe1b4d6708a2c9b5d *src/fortify.h +dc441364a9259f0b973b83c9ed746306 *src/hbio.c +9640c6cfa3574729cec66868a7063c50 *src/hbio.h +7ed886f0f1677ca7141e80cb1151129f *src/ini.c +4504a63202117a0f2c5a94345094ef39 *src/ini.h +fe7898386bf89130afb8b539ed40a729 *src/init.c +385f8a752e28d168a462ff199e8a626c *src/isfixedvar.c +ec1603dafa740c89a5c13d30662e96bf *src/lp_BFP.h +6d387ddea7e501c9f45ac8862b10d630 *src/lp_BFP1.h +5ecae67efa4b7bbce92ad29204ca0297 *src/lp_BFP2.h +f5857b472a5e5d5da3297ccb746d590e *src/lp_Hash.c +ce0a0d53e631a97b88fadc584331913c *src/lp_Hash.h +8fcdff02e2674a8a8dd7208d24bdb840 *src/lp_LUSOL.c +fa869ca21a3fbb48dbf789ad6cb63a51 *src/lp_LUSOL.h +5d59a4b965236021be19b4f484a93db8 *src/lp_MDO.c +07bc5ba751db2629a38b7932cc70cbe4 *src/lp_MDO.h +78e881d0dd9ecc0a8c09f2623e0aafec *src/lp_MPS.c +7871ebb358485f8e8c3ef9443980d497 *src/lp_MPS.h +a38903d6d76b4facd163017c55f89389 *src/lp_SOS.c +70c13990a39ffce5c0222bf58ea8d0f3 *src/lp_SOS.h +856e487693dd757a70b65f2de0c99f51 *src/lp_crash.c +91c43ad6fde554bb8bfa84b4d2de50d9 *src/lp_crash.h +eb42ea6b62c3173610f3ffd652719e24 *src/lp_explicit.h +1175c90bc8cc00005069b734eacee99c *src/lp_fortify.h +5012b501ccb10aab9ddabe983938bd50 *src/lp_lib.c +f61de993e64612611a9552db3d3d682c *src/lp_lib.h +670b7b1092aa5b134f8e7c22e1952c2b *src/lp_matrix.c +0e0411fc13742423c3ad9daf4898a207 *src/lp_matrix.h +241a136626b10914f972ab159fb6b890 *src/lp_mipbb.c +6a9e5268c49558066d7c1e6d513a030b *src/lp_mipbb.h +285f3eab3c9e2dff2a438f93acfbe3a1 *src/lp_params.c +24b3596a0955674eccc4bbec2da57070 *src/lp_presolve.c +94b37b4e5490ec5bfd9a7878a3757cb8 *src/lp_presolve.h +19e0dbc4e1bfed660a66d53ef1386c43 *src/lp_price.c +dc0064126161d25e11fca8b5e1157ed0 *src/lp_price.h +6a4339fad7430c21e5a1b42a9efa3914 *src/lp_pricePSE.c +dc50c729e494a1e9c696a583fc8071b9 *src/lp_pricePSE.h +155eb77552c988866e08d52660566212 *src/lp_report.c +c63130e91e32ad6ccb07ed74b858b2f6 *src/lp_report.h +29dd83c8ebcd859a24358acf121bec81 *src/lp_rlp.c +c02fa3e4c7d0ee6205084dc503bb20a2 *src/lp_rlp.h +c0d6d17b778a6abddce060ae7c4eb485 *src/lp_scale.c +58d5ec0d2d218782a74448fc2c2d49cf *src/lp_scale.h +695799aa902e816047d86d070d75724d *src/lp_simplex.c +15a71c4bc5331ff6006d2ab46e8ad5dd *src/lp_simplex.h +5e54874bc04eed6bcc7cea3e6e303fb7 *src/lp_types.h +312b52b479860d4884cc3dedc9914da7 *src/lp_utils.c +2f4334f740e62ab78cb7c30a35f65a00 *src/lp_utils.h +f1c11764f310c464d1d939445a549830 *src/lp_wlp.c +e38ffb7e5dbfb1aa5d6589ac0e6be58b *src/lp_wlp.h +238c51b4c76117e4f774a6646517d626 *src/lpkit.h +2cc58edd5502d11455c509b95138c750 *src/lpslink56.c +7ef44c14a40fe999edd2e437bec993fd *src/lpsolve.h +3dc305aea45e7e242ebb66d80c8a6f8f *src/lusol.c +52b8d1269fab176c6c81632c544de26d *src/lusol.h +c226ea7cd9844e8885331b69148e1732 *src/lusol1.h +a5801e5989ac98e0b250e0eee8c2a41c *src/lusol2.h +a93d1dfb72e8c130df6c450137e0ea0d *src/lusol6a.h +e92bf5c4a3f3345bf38c2d06fa96b14b *src/lusol6l0.h +eb600f9e480a8cb363ae81901ad4be32 *src/lusol6u.h +2962a1720d6e6a10b727899a44b1b9c5 *src/lusol7a.h +46a3891578edc8cd114808cf564699f6 *src/lusol8a.h +ad1867767ee064effa32ea9e880fe632 *src/lusolio.c +3e67bb130740b63befbb2c6ed01989b8 *src/lusolio.h +4ad03c261870928d5ae228f2c3b25600 *src/mmio.c +7ff75e953e30ec3c26bffce310809500 *src/mmio.h +7d7a35fefee657e82631e36573f62e97 *src/myblas.c +c8a3b2f20143aae3f8caec9d28055bd2 *src/myblas.h +b08069fb86201ded0b9dcb009c209d12 *src/sparselib.c +4bae4e6a2367d3b9ef07b7dae5e608ba *src/sparselib.h +ca9689ad85eac09f9bdb3f3db52222d0 *src/ufortify.h +9029bb6578493b199eaef28ffe0648f8 *src/yacc_read.c +b2919866cb9ee057db8606f3bd02eb3d *src/yacc_read.h diff --git a/src/lpSolve/NAMESPACE b/src/lpSolve/NAMESPACE new file mode 100644 index 00000000..5a51f0d2 --- /dev/null +++ b/src/lpSolve/NAMESPACE @@ -0,0 +1,3 @@ +useDynLib("lpSolve") +export("lp", "lp.transport", "lp.assign", "make.q8") +S3method("print", "lp") diff --git a/src/lpSolve/R/lp.R b/src/lpSolve/R/lp.R new file mode 100644 index 00000000..b45b746c --- /dev/null +++ b/src/lpSolve/R/lp.R @@ -0,0 +1,273 @@ +lp <- function(direction = "min", objective.in, const.mat, const.dir, const.rhs, + transpose.constraints = TRUE, int.vec, presolve = 0, compute.sens = 0, + binary.vec, all.int=FALSE, all.bin=FALSE, scale=196, dense.const, + num.bin.solns=1, use.rw=FALSE, timeout = 0L) +{ + # + # lp: solve a general linear program + # + # Arguments: + # direction: Character: direction of optimization: "min" (default) + # or "max." + # objective.in: Numeric vector (or one-column data frame) of + # coefficients of objective function + # const.mat: Matrix of numeric constraint coefficients, one row + # per constraint, one column per variable (unless + # transpose.constraints = FALSE; see below). + # const.dir: Vector of character strings giving the direction of the + # constraints: each value should be one of "<," "<=," "=," "==," + # ">," or ">=." + # const.rhs: Vector of numeric values for the right-hand sides + # of the constraints. + # transpose.constraints: By default each constraint occupies a + # row of const.mat, and that matrix needs to be transposed before + # being passed to the optimizing code. For very large + # constraint matrices it may be wiser to construct the + # constraints in a matrix column-by-column. In that case set + # transpose.constraints to FALSE. + # int.vec: Numeric vector giving the indices of variables that are + # required to be integer. The length of this vector will + # therefore be the number of integer variables. + # presolve: Numeric: Should presolve be done (in lp_solve)? + # Default: 0 (no). + # A non-zero value means "yes." Currently mostly ignored. + # compute.sens: Numeric: compute sensitivities? Default 0 (no). + # Any non-zero value means "yes." + # binary.vec: Numeric vector giving indices of binary variables + # all.int: logical: True if all variables should be integers. This + # overrides anything in int.vec. + # all.bin: logical: True if all variables should be binary. This + # overrides anything in binary.vec. + # dense.const: alternative specification of constraints in the + # form of a three-column matrix or data frame. If a row contains + # (i, j, k), it means "constraint i, variable j = value k." This + # is ignored if const.mat is supplied. + # scale: integer giving scaling. Possible values can be found in + # in the lpSolve documentatation. 0 = no scaling. Default: 196. + # num.bin.solns: If all.bin is True, we will attempt to find up to + # num.bin.solns solutions and return them in a matrix. + # use.rw: Work around a bug when num.bin.solns=TRUE by writing each + # solution out to a file and reading it back in. + # timeout: set as timeout variable in lpslink. + # + # Set up the direction. + # + if(direction == "min") + direction <- 0 + else if (direction == "max") + direction <- 1 + else stop ("Direction must be 'max' or 'min'") + # + # Convert one-column data frame objective to vector. Add leading 0 to + # obejctive. + # + if(is.data.frame(objective.in)) { + if(ncol(objective.in) > 1) + stop("Objective vector has more than one column") + objective.in <- unlist(objective.in) + names(objective.in) <- NULL + } + # + # Set up solution, status, x.count (= number of variables) + # + objective <- c(0, objective.in) + solution <- numeric(length(objective.in)) + status <- objval <- 0 + x.count <- length(objective.in) + constraints <- 0 + const.count <- 0 + # + # Constraint matrix stuff. If const.mat is passed in, convert it + # to a matrix if necessary (and set NA's to 0). Also transpose + # if necessary. + # + use.dense <- FALSE + if (!missing (const.mat)) { + dense.const <- 0; dense.const.nrow = 0; dense.ctr = 0 + if(is.data.frame(const.mat)) { + cm <- as.numeric(unlist(const.mat)) + names(cm) <- NULL + const.mat <- matrix(cm, nrow = nrow(const.mat)) + } + const.mat[is.na(const.mat)] <- 0 + if(transpose.constraints) + const.mat <- t(const.mat) + const.count <- ncol(const.mat) + } + else + { + # + # If there was no const.mat, our constraints are in dense.const (or + # there are no constraints). If there's a dense.const, it needs to have + # three columns, each row having , and . + # The number of constraints is the largest value in the const column. + # Check to make sure every value in the range is present, and that + # there aren't references to variables that don't exist. It will + # be convenient to sort this by constraint number. + # + if (missing (dense.const)) { + dense.const <- 0; dense.const.nrow = 0; dense.ctr = 0 + } else { + if (!is.matrix (dense.const)) + stop ("Dense constraints need to be matrix or data frame.") + if (is.data.frame (dense.const)) dense.const <- as.matrix (dense.const) + if (ncol (dense.const) != 3) + stop ("Dense constraints must be in three columns.") + dimnames(dense.const) <- list (NULL, c("Const", "Var", "Value")) + dense.const <- dense.const[order(dense.const[,"Const"]),,drop=FALSE] + const.indices <- unique (dense.const[,"Const"]) + if (length(const.indices) != max (const.indices)) + stop ("Error in constraint numbering") + const.count <- max (const.indices) + # + # It's convenient to send in one other piece of information. The + # "dense.ctr" vector's ith entry says how many non-zero elements + # are found in constraint i. Once we have that we don't need the + # "Const" column of dense.const, either. + # + dense.ctr <- table (dense.const[,"Const"]) + names(dense.ctr) <- NULL + dense.const <- dense.const[,c("Var", "Value"), drop=FALSE] + dense.const.nrow <- nrow (dense.const) + use.dense <- TRUE + }} + # + # Set up constraint signs... + # + const.dir.num <- rep(-1, length(const.dir)) + const.dir.num[const.dir == "<" | const.dir == "<="] <- 1 + const.dir.num[const.dir == "=" | const.dir == "=="] <- 3 + const.dir.num[const.dir == ">" | const.dir == ">="] <- 2 + if(any(const.dir.num == -1)) + stop("Unknown constraint direction found\n") + # + # ...constraint count, and right-hand sides. + # + if(is.data.frame(const.rhs)) + const.rhs <- as.matrix(const.rhs) + const.rhs <- c(const.rhs) + names(const.rhs) <- NULL + # + # For regular (non-dense) constraints, set up big matrix of + # constraint info; add a 0 on the front. + # + if (!missing (const.mat)) { + big.const.mat <- rbind(const.mat, const.dir.num, const.rhs) + constraints <- c(0, c(big.const.mat)) + } + # + # For dense constraints, just add rows of directions and rhs's + # to dense.ctr, which will then be a 3-row matrix w/ const.count cols. + # + else if (!missing (dense.const)) { + dense.ctr <- rbind (dense.ctr, const.dir.num, const.rhs) + big.const.mat <- 0 + constraints <- 0 + } + # + # Set up int.vec. If all.int is present (and TRUE), use that. + # + if (!missing (all.int) && all.int) { + int.vec <- 1:length(solution) + int.count <- length(int.vec) + } + else + { + if(missing(int.vec)) { + int.count <- 0 + int.vec <- 0 + } + else + int.count <- length(int.vec) + } + # + # Do the same for binary.vec. + # + if (!missing (all.bin) && all.bin) { + binary.vec <- 1:length(solution) + bin.count <- length(binary.vec) + } + else + { + if(missing(binary.vec)) { + bin.count <- 0 + binary.vec <- 0 + } + else + bin.count <- length(binary.vec) + } + # If all variables are binary, set all.bin to TRUE. + if (length(binary.vec) == length(solution)) all.bin <- TRUE + # + # If more than one solution is called for, prepare for that. "Solution" will need + # one extra element. + # + if (num.bin.solns > 1) + if (all.bin) + solution <- c(0, rep (solution, num.bin.solns)) + else { + warning ("Num.bin.solns can only be > 1 if all variables are binary") + num.bin.solns <- 1 + } + # + # Set up sensitivity stuff. + # + sens.coef.from <- sens.coef.to <- 0 + duals <- duals.from <- duals.to <- 0 + if(compute.sens != 0) { + sens.coef.from <- sens.coef.to <- numeric(x.count) + duals <- duals.from <- duals.to <- numeric(x.count + const.count) + } + # + + if (num.bin.solns > 1 && use.rw == TRUE) + tmp <- tempfile () + else + tmp <- "Nobody will ever look at this" + if (missing (dense.const) || !is.matrix (dense.const)) + { + dense.col <- dense.val <- 0 + } + else + { + dense.col = dense.const[,"Var"] + dense.val = dense.const[,"Value"] + } + lp.out <- .C("lpslink", + direction = as.integer(direction), + x.count = as.integer(x.count), + objective = as.double(objective), + const.count = as.integer(const.count), + constraints = as.double(constraints), + int.count = as.integer(int.count), + int.vec = as.integer(int.vec), + bin.count = as.integer(bin.count), + binary.vec = as.integer(binary.vec), + num.bin.solns = as.integer (num.bin.solns), + objval = as.double(objval), + solution = as.double(solution), + presolve = as.integer(presolve), + compute.sens = as.integer(compute.sens), + sens.coef.from = as.double(sens.coef.from), + sens.coef.to = as.double(sens.coef.to), + duals = as.double(duals), + duals.from = as.double(duals.from), + duals.to = as.double(duals.to), + scale = as.integer (scale), + use.dense = as.integer (use.dense), + dense.col = as.integer (dense.col), + dense.val = as.double (dense.val), + dense.const.nrow = as.integer (dense.const.nrow), + dense.ctr = as.double (dense.ctr), + use.rw = as.integer (use.rw), + tmp = as.character(tmp), + status = as.integer(status), + timeout = as.integer(timeout), + PACKAGE="lpSolve") + lp.out$objective <- objective.in + lp.out$constraints <- big.const.mat + if(any(names(version) == "language")) + class(lp.out) <- "lp" + else oldClass(lp.out) <- "lp" + return(lp.out) +} diff --git a/src/lpSolve/R/lp.assign.R b/src/lpSolve/R/lp.assign.R new file mode 100644 index 00000000..067e3092 --- /dev/null +++ b/src/lpSolve/R/lp.assign.R @@ -0,0 +1,114 @@ +lp.assign <- function (cost.mat, direction="min", presolve = 0, compute.sens = 0) +{ +# +# lp.assign: use lpsolve.dll to solve an assignment problem. This +# is a linear program with an ixj matrix of decision variables, +# and i+j constraints: that the rows and columns all add up to one. +# +# Arguments: +# cost.mat: matrix or data.frame of costs +# direction: "min" (default) or "max" +# presolve: numeric. Presolve? Default 0. Currently ignored. +# compute.sens: numeric. Compute sensitivities? Default 0 (no). +# Any non-zero number means "yes" and, in that +# case, presolving is attempted. +# +# Return value: list from lpsolve, including objective and +# assignments. +# +# Check for the lpslink function, dyn.open if needed. (It should +# from data.frame if needed. +# + if (!is.matrix(cost.mat)) + stop("Matrix of costs required.") + if (is.data.frame(cost.mat)) + cost.mat <- as.matrix(cost.mat) +# +# Set up the stuff. +# + nr <- nrow(cost.mat) + nc <- ncol(cost.mat) + rnum.signs <- rep (3, nr) + row.rhs <- rep (1, nr) + cnum.signs <- rep (3, nc) + col.rhs <- rep (1, nc) + if (direction == "min") + direction <- as.integer(0) + else + if (direction == "max") + direction <- as.integer (1) + else + stop ("Direction must be 'min' or 'max'") + varcount <- as.integer(nr * nc) + objective <- as.double(c(0, c(t(cost.mat)))) +# +# Set up the row and column constraints. Each is of the +# "=1" type, represented by 3 (for "equals") 1. +# + const.count <- as.integer(nr + nc) + intcount <- as.integer(varcount) # number of integers + intvec <- as.integer(1:varcount) # indicators of integers +# +# Prepare objective value, integer indicators, solution, and status +# + objval <- as.double(0) + int.count <- nc * nr + integers <- as.integer (numeric (int.count)) + solution <- as.double(numeric(nc * nr)) + status <- as.integer(0) +# +# Set up sensitivity stuff +# + sens.coef.from <- sens.coef.to <- 0 + duals <- duals.from <- duals.to <- 0 + if (compute.sens) { + sens.coef.from <- sens.coef.to <- numeric(varcount) + duals <- duals.from <- duals.to <- numeric(varcount + + const.count) + } + ## costs <- as.double (c(0, c(cost.mat))) + lps.out <- .C("lp_transbig", + direction = direction, + rcount = as.integer (nr), + ccount = as.integer (nc), + costs = objective, + rsigns = as.integer (rnum.signs), + rrhs = as.double (row.rhs), + csigns = as.integer (cnum.signs), + crhs = as.double (col.rhs), + objval = objval, + int.count = int.count, + integers = integers, + solution = solution, + presolve = as.integer(presolve), + compute.sens = as.integer(compute.sens), + sens.coef.from = as.double(sens.coef.from), + sens.coef.to = as.double(sens.coef.to), + duals = as.double(duals), + duals.from = as.double(duals.from), + duals.to = as.double(duals.to), + status = status, PACKAGE="lpSolve") +# +# Reset solution back into matrix form. +# + lps.out$solution = matrix(lps.out$solution, nr, nc, byrow = TRUE) + if (length(duals) > 0) { + lps.out$sens.coef.from <- matrix(lps.out$sens.coef.from, + nr, nc, byrow = TRUE) + lps.out$sens.coef.to <- matrix(lps.out$sens.coef.to, + nr, nc, byrow = TRUE) + lps.out$duals <- matrix(lps.out$duals, nr, nc, byrow = TRUE) + lps.out$duals.from <- matrix(lps.out$duals.from, nr, + nc, byrow = TRUE) + lps.out$duals.to <- matrix(lps.out$duals.to, nr, nc, + byrow = TRUE) + } +# +# Reset the costs, to which we had to add a 0 +# + lps.out$costs <- cost.mat + if(any(names(version) == "language")) + class(lps.out) <- "lp" + else oldClass(lps.out) <- "lp" + lps.out +} diff --git a/src/lpSolve/R/lp.transport.R b/src/lpSolve/R/lp.transport.R new file mode 100644 index 00000000..75a7b2c2 --- /dev/null +++ b/src/lpSolve/R/lp.transport.R @@ -0,0 +1,155 @@ +lp.transport <- function (cost.mat, direction = "min", row.signs, row.rhs, col.signs, + col.rhs, presolve = 0, compute.sens = 0, integers = 1:(nc * nr)) +{ +# +# lp.transport: use lpsolve.dll to solve a transportation problem. +# This is a linear program with an ixj matrix of decision variables, +# and constraints on the row and column sums (and no others) +# +# Arguments: cost.mat: matrix or data.frame of costs +# dir: direction ("min" or "max") +# row.signs: signs for row constraints +# row.rhs: values for row constraints +# col.signs: signs for column constraints +# col.rhs: values for column constraints +# presolve: Numeric: should we presolve? Default 0 (no); non-0 +# values means "yes." Currently mostly ignored. +# compute.sens: Numeric: compute sensitivities? Default 0 (no); +# non-zero value means "yes." +# integers: Indicator of integer variables: default, all. +# +# Return value: list from lpsolve, including objective and optimal values. +# +# Check that the cost matrix is in fact a matrix; convert +# from data.frame if needed. +# + if (!is.matrix(cost.mat)) + stop("Matrix of costs required.") + if (is.data.frame(cost.mat)) + cost.mat <- as.matrix(cost.mat) +# +# Set up the stuff. +# + nr <- nrow(cost.mat) + nc <- ncol(cost.mat) + # + # Ensure that row stuff is of the correct size. + # + if (is.matrix(row.signs)) + row.signs <- as.vector(row.signs) + if (length(row.signs) != nr) + stop(paste("Error: We have", length(row.signs), "signs, but", + nr, "rows")) + if (is.matrix(row.rhs)) + row.rhs <- as.vector(row.rhs) + if (length(row.rhs) != nr) + stop(paste("Error: We have", length(row.rhs), "rhs's, but", + nr, "rows")) + # + # Ensure that col stuff is of the correct size. + # + if (is.matrix(col.signs)) + col.signs <- as.vector(col.signs) + if (length(col.signs) != nc) + stop(paste("Error: We have", length(col.signs), "signs, but", + nc, "columns")) + if (is.matrix(col.rhs)) + col.rhs <- as.vector(col.rhs) + if (length(col.rhs) != nc) + stop(paste("Error: We have", length(col.rhs), "rhs's, but", + nc, "rows")) + if (direction == "min") + direction <- as.integer(0) + else + if (direction == "max") + direction <- as.integer(1) + else + stop ("Direction should be 'min' or 'max'") + varcount <- as.integer(nr * nc) # no of vars + objective <- as.double(c(0, c(t(cost.mat)))) + if (is.null (integers)) { + int.count <- 0 + integers <- 0 + } + else + int.count <- length(integers) + const.count <- as.integer(nr + nc) # no of constraints + rnum.signs <- rep(-1, nr) # sign holder +# +# Set the signs: <, >, = turn into 1,2,3 respectively. We also +# allow those followed by another "=". Anything else is an error. +# + rnum.signs[row.signs == "<" | row.signs == "<="] <- 1 + rnum.signs[row.signs == "=" | row.signs == "=="] <- 3 + rnum.signs[row.signs == ">" | row.signs == ">="] <- 2 + if (any(rnum.signs == -1)) + stop(paste("Unknown row sign in position ", which(rnum.signs == + -1)[1])) +# +# Column signs. +# + cnum.signs <- rep(-1, nc) + cnum.signs[col.signs == "<" | col.signs == "<="] <- 1 + cnum.signs[col.signs == "=" | col.signs == "=="] <- 3 + cnum.signs[col.signs == ">" | col.signs == ">="] <- 2 + if (any(cnum.signs == -1)) + stop(paste("Unknown column sign in position ", which(cnum.signs == + -1)[1])) +# +# A few more things, plus dual action. +# + objval <- as.double(0) + solution <- as.double(numeric(nc * nr)) + status <- as.integer(0) + sens.coef.from <- sens.coef.to <- 0 + duals <- duals.from <- duals.to <- 0 + if (compute.sens) { + sens.coef.from <- sens.coef.to <- numeric(varcount) + duals <- duals.from <- duals.to <- numeric(varcount + + const.count) + } +# +# Stick a zero on the front of costs, and off we go. +# + costs <- as.double (c(0, c(cost.mat))) + lps.out <- .C("lp_transbig", + direction = direction, + rcount = as.integer (nr), + ccount = as.integer (nc), + costs = costs, + rsigns = as.integer (rnum.signs), + rrhs = as.double (row.rhs), + csigns = as.integer (cnum.signs), + crhs = as.double (col.rhs), + objval = objval, + int.count = int.count, + integers = integers, + solution = solution, + presolve = as.integer(presolve), + compute.sens = as.integer(compute.sens), + sens.coef.from = as.double(sens.coef.from), + sens.coef.to = as.double(sens.coef.to), + duals = as.double(duals), + duals.from = as.double(duals.from), + duals.to = as.double(duals.to), + status = status, PACKAGE="lpSolve") +# +# Set solution back into a matrix. +# + lps.out$solution = matrix(lps.out$solution, nr, nc, byrow = FALSE) + if (length(duals) > 0) { + lps.out$sens.coef.from <- matrix(lps.out$sens.coef.from, + nr, nc, byrow = TRUE) + lps.out$sens.coef.to <- matrix(lps.out$sens.coef.to, + nr, nc, byrow = TRUE) + lps.out$duals <- matrix(lps.out$duals, nr, nc, byrow = TRUE) + lps.out$duals.from <- matrix(lps.out$duals.from, nr, + nc, byrow = TRUE) + lps.out$duals.to <- matrix(lps.out$duals.to, nr, nc, + byrow = TRUE) + } + if(any(names(version) == "language")) + class(lps.out) <- "lp" + else oldClass(lps.out) <- "lp" + lps.out +} diff --git a/src/lpSolve/R/make.q8.R b/src/lpSolve/R/make.q8.R new file mode 100644 index 00000000..f07d35b0 --- /dev/null +++ b/src/lpSolve/R/make.q8.R @@ -0,0 +1,47 @@ +make.q8 <- function () +{ +# +# Q8: Set up sparse constraints (in the "lp" package sense) for the 8 queens problem. +# +# "Chess" holds the chess board. +# +chess <- matrix (1:64, 8, 8, byrow=T) +# +# Row and column constraints. Recall that each constraint has three entries. +# +row.const <- cbind (rep (1:8, each=8), c(t(chess)), 1) +col.const <- cbind (rep (9:16, each=8), c(chess), 1) +# +# Diagonals, bottom left to top right. +# +const.ctr <- 17 +chess <- matrix (1:64, 8, 8, byrow=T) +row.chess <- row(chess); col.chess <- col(chess) +d1.const <- NULL +rplusc <- row.chess + col.chess +for (i in 3:15) { +d1.const <- rbind (d1.const, cbind (const.ctr, chess[rplusc == i], 1)) +const.ctr <- const.ctr + 1 +} +# +# Now the other way. +# +start <- seq (49,1, by=-8) +for (i in start) { +d1.const <- rbind (d1.const, cbind (const.ctr, seq (i, 64, by=9), 1)) +const.ctr <- const.ctr + 1 +} +# +# Also a few more! +# +for (i in 2:7) +{ +d1.const <- rbind (d1.const, cbind (const.ctr, seq (i, chess[9-i,8], by=9), 1)) +const.ctr <- const.ctr + 1 +} +# +# Return all constraints. +# +rbind (row.const, col.const, d1.const) +} + diff --git a/src/lpSolve/R/print.lp.R b/src/lpSolve/R/print.lp.R new file mode 100644 index 00000000..c726575b --- /dev/null +++ b/src/lpSolve/R/print.lp.R @@ -0,0 +1,11 @@ +print.lp <- function(x, ...) +{ + if(x$status == 0) { + cat("Success: the objective function is", x$objval, "\n") + if (any (names(x) == "num.bin.solns") && x$num.bin.solns > 1) + cat ("\t", x$num.bin.solns, "solutions returned\n") + } + else if(x$status == 2) + cat("Error: no feasible solution found") + else cat("Error: status", x$status, "\n") +} diff --git a/src/lpSolve/R/zzz.R b/src/lpSolve/R/zzz.R new file mode 100644 index 00000000..737da057 --- /dev/null +++ b/src/lpSolve/R/zzz.R @@ -0,0 +1,3 @@ +# .First.lib <- function(libname, pkgname) { +# library.dynam ("lpSolve", pkgname, libname) +# } diff --git a/src/lpSolve/man/lp.Rd b/src/lpSolve/man/lp.Rd new file mode 100644 index 00000000..bc0db301 --- /dev/null +++ b/src/lpSolve/man/lp.Rd @@ -0,0 +1,136 @@ +\name{lp} +\alias{lp} +\title{Linear and Integer Programming} +\description{Interface to \code{lp_solve} linear/integer programming system} +\usage{ +lp (direction = "min", objective.in, const.mat, const.dir, const.rhs, + transpose.constraints = TRUE, int.vec, presolve=0, compute.sens=0, + binary.vec, all.int=FALSE, all.bin=FALSE, scale = 196, dense.const, + num.bin.solns=1, use.rw=FALSE, timeout = 0L) +} +\arguments{ +\item{direction}{Character string giving direction of optimization: +"min" (default) or "max."} +\item{objective.in}{Numeric vector of coefficients of objective function} +\item{const.mat}{Matrix of numeric constraint coefficients, one row +per constraint, one column per variable (unless transpose.constraints = +FALSE; see below).} +\item{const.dir}{Vector of character strings giving the direction of +the constraint: each value should be one of "<," "<=," "=," "==," ">," or ">=". +(In each pair the two values are identical.)} +\item{const.rhs}{Vector of numeric values for the right-hand sides of +the constraints.} +\item{transpose.constraints}{By default each constraint occupies a row +of const.mat, and that matrix needs to be transposed before being passed +to the optimizing code. For very large constraint matrices it may be wiser +to construct the constraints in a matrix column-by-column. In that case set +transpose.constraints to FALSE.} +\item{int.vec}{Numeric vector giving the indices of variables that are +required to be integer. The length of this vector will therefore be the +number of integer variables.} +\item{presolve}{Numeric: presolve? Default 0 (no); any +non-zero value means "yes." Currently ignored.} +\item{compute.sens}{Numeric: compute sensitivity? Default 0 (no); any +non-zero value means "yes."} +\item{binary.vec}{Numeric vector like int.vec giving the indices of variables +that are required to be binary.} +\item{all.int}{Logical: should all variables be integer? Default: FALSE.} +\item{all.bin}{Logical: should all variables be binary? Default: FALSE.} +\item{scale}{Integer: value for lpSolve scaling. Details can be found in +the lpSolve documentation. Set to 0 for no scaling. Default: 196} +\item{dense.const}{Three column dense constraint array. This is ignored if +const.mat is supplied. Otherwise the columns are constraint number, column +number, and value; there should be one row for each non-zero entry in the +constraint matrix.} +\item{num.bin.solns}{Integer: if all.bin=TRUE, the user can request up to +num.bin.solns optimal solutions to be returned.} +\item{use.rw}{Logical: if TRUE and num.bin.solns > 1, write the lp out to a +file and read it back in for each solution after the first. This is just to +defeat a bug somewhere. Although the default is FALSE, we recommend you set +this to TRUE if you need num.bin.solns > 1, until the bug is found.} +\item{timeout}{Integer: timeout variable in seconds, defaults to 0L which means +no limit is set.} +} +\details{ +This function calls the \code{lp_solve} 5.5 solver. That system has many options not +supported here. The current version is maintained at +\url{https://lpsolve.sourceforge.net/5.5/} + +Note that every variable is assumed to be >= 0! +} +\value{ +An lp object. See \code{\link{lp.object}} for details. +} +\author{Sam Buttrey, \email{buttrey@nps.edu}} +\seealso{\code{\link{lp.assign}}, \code{\link{lp.transport}}} +\examples{ +# +# Set up problem: maximize +# x1 + 9 x2 + x3 subject to +# x1 + 2 x2 + 3 x3 <= 9 +# 3 x1 + 2 x2 + 2 x3 <= 15 +# +f.obj <- c(1, 9, 1) +f.con <- matrix (c(1, 2, 3, 3, 2, 2), nrow=2, byrow=TRUE) +f.dir <- c("<=", "<=") +f.rhs <- c(9, 15) +# +# Now run. +# +lp ("max", f.obj, f.con, f.dir, f.rhs) +\dontrun{Success: the objective function is 40.5} +lp ("max", f.obj, f.con, f.dir, f.rhs)$solution +\dontrun{[1] 0.0 4.5 0.0} +# +# The same problem using the dense constraint approach: +# +f.con.d <- matrix (c(rep (1:2,each=3), rep (1:3, 2), t(f.con)), ncol=3) +lp ("max", f.obj, , f.dir, f.rhs, dense.const=f.con.d) +\dontrun{Success: the objective function is 40.5} +# +# Get sensitivities +# +lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$sens.coef.from +\dontrun{[1] -1e+30 2e+00 -1e+30} +lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$sens.coef.to +\dontrun{[1] 4.50e+00 1.00e+30 1.35e+01} +# +# Right now the dual values for the constraints and the variables are +# combined, constraints coming first. So in this example... +# +lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals +\dontrun{[1] 4.5 0.0 -3.5 0.0 -10.5} +# +# ...the duals of the constraints are 4.5 and 0, and of the variables, +# -3.5, 0.0, -10.5. Here are the lower and upper limits on these: +# +lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals.from +\dontrun{[1] 0e+00 -1e+30 -1e+30 -1e+30 -6e+00} +lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals.to +\dontrun{[1] 1.5e+01 1.0e+30 3.0e+00 1.0e+30 3.0e+00} +# +# Run again, this time requiring that all three variables be integer +# +lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3) +\dontrun{Success: the objective function is 37} +lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3)$solution +\dontrun{[1] 1 4 0} +# +# You can get sensitivities in the integer case, but they're harder to +# interpret. +# +lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3, compute.sens=TRUE)$duals +\dontrun{[1] 1 0 0 7 0} +# +# Here's an example in which we want more than one solution to a problem +# in which all variables are binary: the 8-queens problem, +# with dense constraints. +# +chess.obj <- rep (1, 64) +q8 <- make.q8 () +chess.dir <- rep (c("=", "<"), c(16, 26)) +chess.rhs <- rep (1, 42) +lp ('max', chess.obj, , chess.dir, chess.rhs, dense.const = q8, + all.bin=TRUE, num.bin.solns=3) +} +\keyword{optimize} diff --git a/src/lpSolve/man/lp.assign.Rd b/src/lpSolve/man/lp.assign.Rd new file mode 100644 index 00000000..c9753dae --- /dev/null +++ b/src/lpSolve/man/lp.assign.Rd @@ -0,0 +1,56 @@ +\name{lp.assign} +\alias{lp.assign} +\title{Integer Programming for the Assignment Problem} +\description{Interface to \code{lp_solve} linear/integer programming +system specifically for solving assignment problems} +\usage{ +lp.assign (cost.mat, direction = "min", presolve = 0, compute.sens = 0) +} +\arguments{ +\item{cost.mat}{Matrix of costs: the ij-th element is the cost of +assigning source i to destination j.} +\item{direction}{Character vector, length 1, containing either "min" +(the default) or "max"} +\item{presolve}{Numeric: presolve? Default 0 (no); any +non-zero value means "yes." Currently ignored.} +\item{compute.sens}{Numeric: compute sensitivity? Default 0 (no); any +non-zero value means "yes." In that case presolving is attempted.} +} +\details{ +This is a particular integer programming problem. All the decision variables +are assumed to be integers; each row has the constraint that its entries +must add up to 1 (so that there is one 1 and the remaining entries are 0) +and each column has the same constraint. This is assumed to be a +minimization problem. +} +\value{ +An \code{\link{lp}} object. See documentation for details. The constraints +are assumed (each row adds to 1, each column adds to 1, and no others) and +are not returned. +} +\author{Sam Buttrey, \email{buttrey@nps.edu}} +\seealso{\code{\link{lp}}, \code{\link{lp.transport}}} +\examples{ +assign.costs <- matrix (c(2, 7, 7, 2, 7, 7, 3, 2, 7, 2, 8, 10, 1, 9, 8, 2), 4, 4) +\dontrun{ +> assign.costs + [,1] [,2] [,3] [,4] +[1,] 2 7 7 1 +[2,] 7 7 2 9 +[3,] 7 3 8 8 +[4,] 2 2 10 2 +} +lp.assign (assign.costs) +\dontrun{Success: the objective function is 8} +lp.assign (assign.costs)$solution +\dontrun{ + [,1] [,2] [,3] [,4] +[1,] 0 0 0 1 +[2,] 0 0 1 0 +[3,] 0 1 0 0 +[4,] 1 0 0 0 +} +} +\keyword{optimize} + + diff --git a/src/lpSolve/man/lp.object.Rd b/src/lpSolve/man/lp.object.Rd new file mode 100644 index 00000000..afc2db24 --- /dev/null +++ b/src/lpSolve/man/lp.object.Rd @@ -0,0 +1,22 @@ +\name{lp.object} +\alias{lp.object} +\title{LP (linear programming) object} +\description{Structure of lp object} +\value{ +An lp.object is a list containing the following elements: +\item{direction}{Optimization direction, as entered} +\item{x.count}{Number of variables in objective function} +\item{objective}{Vector of objective function coefficients, as entered} +\item{const.count}{Number of constraints entered} +\item{constraints}{Constraint matrix, as entered (not returned +by \code{\link{lp.assign}} or \code{\link{lp.transport}})} +\item{int.count}{Number of integer variables} +\item{int.vec}{Vector of integer variables' indices, as entered} +\item{objval}{{Value of objective function at optimum}} +\item{solution}{Vector of optimal coefficients} +\item{num.bin.solns}{Numeric indicator of number of solutions returned} +\item{status}{Numeric indicator: 0 = success, 2 = no feasible solution} +} +\author{Sam Buttrey, \email{buttrey@nps.edu}} +\seealso{\code{\link{lp}}, \code{\link{lp.assign}}, \code{\link{lp.transport}}} +\keyword{optimize} diff --git a/src/lpSolve/man/lp.transport.Rd b/src/lpSolve/man/lp.transport.Rd new file mode 100644 index 00000000..e9c1ff8a --- /dev/null +++ b/src/lpSolve/man/lp.transport.Rd @@ -0,0 +1,84 @@ +\name{lp.transport} +\alias{lp.transport} +\title{Integer Programming for the Transportation Problem} +\description{Interface to \code{lp_solve} linear/integer programming +system specifically for solving transportation problems} +\usage{ +lp.transport (cost.mat, direction="min", row.signs, row.rhs, col.signs, + col.rhs, presolve=0, compute.sens=0, integers = 1:(nc*nr) ) +} +\arguments{ +\item{cost.mat}{Matrix of costs; ij-th element is the cost of transporting +one item from source i to destination j.} +\item{direction}{Character, length 1: "min" or "max"} +\item{row.signs}{Vector of character strings giving the direction of the +row constraints: each value should be one of "<," "<=," "=," "==," ">," +or ">=." (In each pair the two values are identical.)} +\item{row.rhs}{Vector of numeric values for the right-hand sides of the +row constraints.} +\item{col.signs}{Vector of character strings giving the direction of the +column constraints: each value should be one of "<," "<=," "=," "==," ">," +or ">=."} +\item{col.rhs}{Vector of numeric values for the right-hand sides of the +column constraints.} +\item{presolve}{Numeric: presolve? Default 0 (no); any +non-zero value means "yes." Currently ignored.} +\item{compute.sens}{Numeric: compute sensitivity? Default 0 (no); any +non-zero value means "yes."} +\item{integers}{Vector of integers whose ith element gives the index +of the ith integer variable. Its length will be the number of integer +variables. Default: all variables are integer. Set to NULL to have no +variables be integer.} +} +\details{ +This is a particular integer programming problem. All the decision variables +are assumed to be integers, and there is one constraint per row and one per +column (and no others). This is assumed to be a minimization problem. +} +\value{ +An \code{\link{lp}} object. Constraints are implicit and not returned. +See documentation for details. +} +\references{Example problem from Bronson (1981), \emph{Operations Research}, +Scahum's Outline Series, McGraw-Hill.} +\author{Sam Buttrey, \email{buttrey@nps.edu}} +\seealso{\code{\link{lp.assign}}, \code{\link{lp.transport}}} + +\examples{ +# +# Transportation problem, Bronson, problem 9.1, p. 86 +# +# Set up cost matrix +# +costs <- matrix (10000, 8, 5); costs[4,1] <- costs[-4,5] <- 0 +costs[1,2] <- costs[2,3] <- costs[3,4] <- 7; costs[1,3] <- costs[2,4] <- 7.7 +costs[5,1] <- costs[7,3] <- 8; costs[1,4] <- 8.4; costs[6,2] <- 9 +costs[8,4] <- 10; costs[4,2:4] <- c(.7, 1.4, 2.1) +# +# Set up constraint signs and right-hand sides. +# +row.signs <- rep ("<", 8) +row.rhs <- c(200, 300, 350, 200, 100, 50, 100, 150) +col.signs <- rep (">", 5) +col.rhs <- c(250, 100, 400, 500, 200) +# +# Run +# +lp.transport (costs, "min", row.signs, row.rhs, col.signs, col.rhs) +\dontrun{Success: the objective function is 7790} +lp.transport (costs, "min", row.signs, row.rhs, col.signs, col.rhs)$solution +\dontrun{ + [,1] [,2] [,3] [,4] [,5] +[1,] 0 100 0 100 0 +[2,] 0 0 300 0 0 +[3,] 0 0 0 350 0 +[4,] 200 0 0 0 0 +[5,] 50 0 0 0 50 +[6,] 0 0 0 0 50 +[7,] 0 0 100 0 0 +[8,] 0 0 0 50 100 +} +} +\keyword{optimize} + + diff --git a/src/lpSolve/man/make.q8.Rd b/src/lpSolve/man/make.q8.Rd new file mode 100644 index 00000000..23cd2f7a --- /dev/null +++ b/src/lpSolve/man/make.q8.Rd @@ -0,0 +1,21 @@ +\name{make.q8} +\alias{make.q8} +\alias{8-queens problem} +\title{Generate sparse constraint matrix for 8-queens problem} +\description{Generate sparse constraint matrix for 8-queens problem} +\usage{ make.q8 () } +\arguments{None.} +\details{ +Sparse constraints come in a three-column matrix or data frame. Each row +gives the row number, column number, and value of a particular non-zero +entry in the constraint matrix. This function produces the sparse constraint +matrix for the 8-queens problem (in which the object is to place eight queens +on a chessboard with no two sharing a row, column or diagonal). The resulting +sparse representation is 252 x 3, compared to 42 x 64 for the usual +representation.} +\value{ +A 252 x 3 numeric matrix. See \link{lp} for the complete example. +} +\author{Sam Buttrey, \email{buttrey@nps.edu}} +\seealso{\code{\link{lp}}} +\keyword{optimize} diff --git a/src/lpSolve/man/print.lp.Rd b/src/lpSolve/man/print.lp.Rd new file mode 100644 index 00000000..bbe94027 --- /dev/null +++ b/src/lpSolve/man/print.lp.Rd @@ -0,0 +1,25 @@ +\name{print.lp} +\alias{print.lp} +\title{Print an lp object} +\description{Print method for lp objects} +\usage{ +\method{print}{lp} (x, \ldots) +} +\arguments{ +\item{x}{List with items named \code{objval} and \code{status}. +Normally this will have been called by \code{\link{lp}}, +\code{\link{lp.assign}}, or \code{\link{lp.transport}}.} +\item{...}{Other arguments, all currently ignored} +} +\details{ +This function prints the objective function value, together with the +word "Success" if the operation is successful, or an indication of the +error if not. If multiple solutions have been produced (because this was +an all-binary problem and lp was called with num.bin.solns > 1) the number +of solutions is also displayed.} +\value{ +None +} +\author{Sam Buttrey, \email{buttrey@nps.edu}} +\seealso{\code{\link{lp}}, \code{\link{lp.assign}}, \code{\link{lp.transport}}} +\keyword{optimize} diff --git a/src/lpsolve/build/lp_solve/Makefile b/src/lpSolve/src/Makefile similarity index 91% rename from src/lpsolve/build/lp_solve/Makefile rename to src/lpSolve/src/Makefile index 3f53931a..4ff5fbc4 100644 --- a/src/lpsolve/build/lp_solve/Makefile +++ b/src/lpSolve/src/Makefile @@ -1,4 +1,4 @@ -LP_SOLVE_CPPFLAGS=$(CPPFLAGS) -I../../headers/include \ +LP_SOLVE_CPPFLAGS=$(CPPFLAGS) -I. \ -I$(R_INCLUDE_DIR) \ -DYY_NEVER_INTERACTIVE \ -DPARSER_LP -DINVERSE_ACTIVE=INVERSE_LUSOL \ @@ -9,7 +9,7 @@ LP_SOLVE_SOURCES=colamd.c lp_MDO.c lp_mipbb.c lp_rlp.c mmio.c commonlib.c \ lp_presolve.c lp_simplex.c yacc_read.c ini.c lp_crash.c \ lp_price.c lp_utils.c lp_Hash.c lp_lib.c lp_pricePSE.c \ lp_wlp.c lp_LUSOL.c lp_matrix.c lp_report.c lusol.c \ - myblas.c + myblas.c isfixedvar.c LP_SOLVE_OBJECTS=$(LP_SOLVE_SOURCES:.c=.o) diff --git a/src/lpSolve/src/Makevars b/src/lpSolve/src/Makevars new file mode 100644 index 00000000..e0c65cc3 --- /dev/null +++ b/src/lpSolve/src/Makevars @@ -0,0 +1 @@ +PKG_CPPFLAGS=-I . -DINTEGERTIME -DPARSER_LP -DBUILDING_FOR_R -DYY_NEVER_INTERACTIVE -DUSRDLL -DCLOCKTIME -DRoleIsExternalInvEngine -DINVERSE_ACTIVE=INVERSE_LUSOL -DINLINE=static -DParanoia diff --git a/src/lpSolve/src/Makevars.win b/src/lpSolve/src/Makevars.win new file mode 100644 index 00000000..44050058 --- /dev/null +++ b/src/lpSolve/src/Makevars.win @@ -0,0 +1 @@ +PKG_CPPFLAGS=-I . -DINTEGERTIME -DPARSER_LP -DBUILDING_FOR_R -DYY_NEVER_INTERACTIVE -DUSRDLL -DCLOCKTIME -DRoleIsExternalInvEngine -DINVERSE_ACTIVE=INVERSE_LUSOL -DWIN32 -DINLINE=static -DParanoia diff --git a/src/lpsolve/build/lp_solve/colamd.c b/src/lpSolve/src/colamd.c similarity index 99% rename from src/lpsolve/build/lp_solve/colamd.c rename to src/lpSolve/src/colamd.c index f48c6f56..292be7a3 100644 --- a/src/lpsolve/build/lp_solve/colamd.c +++ b/src/lpSolve/src/colamd.c @@ -754,7 +754,8 @@ /* Use printf in standard C environment, for debugging and statistics output. */ /* Output is generated only if debugging is enabled at compile time, or if */ /* the caller explicitly calls colamd_report or symamd_report. */ -#define PRINTF printf +#include +#define PRINTF Rprintf /* In C, matrices are 0-based and indices are reported as such in *_report */ #define INDEX(i) (i) diff --git a/src/lpsolve/headers/run_headers/colamd.h b/src/lpSolve/src/colamd.h similarity index 100% rename from src/lpsolve/headers/run_headers/colamd.h rename to src/lpSolve/src/colamd.h diff --git a/src/lpsolve/build/lp_solve/commonlib.c b/src/lpSolve/src/commonlib.c similarity index 73% rename from src/lpsolve/build/lp_solve/commonlib.c rename to src/lpSolve/src/commonlib.c index f38ec30f..a7f91c7d 100644 --- a/src/lpsolve/build/lp_solve/commonlib.c +++ b/src/lpSolve/src/commonlib.c @@ -1,10 +1,8 @@ #include -#if defined INTEGERTIME || defined CLOCKTIME || defined PosixTime +#ifdef INTEGERTIME # include -#elif defined EnhTime -# include #else # include #endif @@ -16,7 +14,6 @@ #endif #include #include -#include #include #include "commonlib.h" @@ -24,32 +21,8 @@ # include "lp_fortify.h" #endif -#if defined FPUexception -/* FPU exception masks */ -unsigned int clearFPU() -{ - return( _clearfp() ); -} -unsigned int resetFPU(unsigned int mask) -{ - _clearfp(); - mask = _controlfp( mask, 0xfffff); - return( mask ); -} -unsigned int catchFPU(unsigned int mask) -{ - /* Always call _clearfp before enabling/unmasking a FPU exception */ - unsigned int u = _clearfp(); - - /* Set the new mask by not-and'ing it with the previous settings */ - u = _controlfp(0, 0); - mask = u & ~mask; - mask = _controlfp(mask, _MCW_EM); +#include - /* Return the previous mask */ - return( u ); -} -#endif /* Math operator equivalence function */ int intpow(int base, int exponent) @@ -361,7 +334,7 @@ int CMP_CALLMODEL compareINT(const void *current, const void *candidate) } int CMP_CALLMODEL compareREAL(const void *current, const void *candidate) { - return( CMP_COMPARE( *(LPSREAL *) current, *(LPSREAL *) candidate ) ); + return( CMP_COMPARE( *(REAL *) current, *(REAL *) candidate ) ); } /* Heap sort function (procedurally based on the Numerical Recipes version, @@ -493,124 +466,6 @@ void hpsortex(void *attributes, int count, int offset, int recsize, MYBOOL desce } } -/* This is a "specialized generic" version of C.A.R Hoare's Quick Sort algorithm. - It will handle arrays that are already sorted, and arrays with duplicate keys. - There are two versions here; one extended conventional with optional tag data - for each sortable value, and a version for the QSORTrec format. The QSORTrec - format i.a. includes the ability for to do linked list sorting. If the passed - comparison operator is NULL, the comparison is assumed to be for integers. */ -#define QS_IS_switch LINEARSEARCH /* Threshold for switching to insertion sort */ - -void qsortex_swap(void *attributes, int l, int r, int recsize, - void *tags, int tagsize, char *save, char *savetag) -{ - MEMCOPY(save, CMP_ATTRIBUTES(l), recsize); - MEMCOPY(CMP_ATTRIBUTES(l), CMP_ATTRIBUTES(r), recsize); - MEMCOPY(CMP_ATTRIBUTES(r), save, recsize); - if(tags != NULL) { - MEMCOPY(savetag, CMP_TAGS(l), tagsize); - MEMCOPY(CMP_TAGS(l), CMP_TAGS(r), tagsize); - MEMCOPY(CMP_TAGS(r), savetag, tagsize); - } -} - -int qsortex_sort(void *attributes, int l, int r, int recsize, int sortorder, findCompare_func findCompare, - void *tags, int tagsize, char *save, char *savetag) -{ - register int i, j, nmove = 0; - char *v; - - /* Perform the a fast QuickSort */ - if((r-l) > QS_IS_switch) { - i = (r+l)/2; - - /* Tri-Median Method */ - if(sortorder*findCompare(CMP_ATTRIBUTES(l), CMP_ATTRIBUTES(i)) > 0) - { nmove++; qsortex_swap(attributes, l,i, recsize, tags, tagsize, save, savetag); } - if(sortorder*findCompare(CMP_ATTRIBUTES(l), CMP_ATTRIBUTES(r)) > 0) - { nmove++; qsortex_swap(attributes, l,r, recsize, tags, tagsize, save, savetag); } - if(sortorder*findCompare(CMP_ATTRIBUTES(i), CMP_ATTRIBUTES(r)) > 0) - { nmove++; qsortex_swap(attributes, i,r, recsize, tags, tagsize, save, savetag); } - - j = r-1; - qsortex_swap(attributes, i,j, recsize, tags, tagsize, save, savetag); - i = l; - v = CMP_ATTRIBUTES(j); - for(;;) { - while(sortorder*findCompare(CMP_ATTRIBUTES(++i), v) < 0); - while(sortorder*findCompare(CMP_ATTRIBUTES(--j), v) > 0); - if(j < i) break; - nmove++; qsortex_swap(attributes, i,j, recsize, tags, tagsize, save, savetag); - } - nmove++; qsortex_swap(attributes, i,r-1, recsize, tags, tagsize, save, savetag); - nmove += qsortex_sort(attributes, l,j, recsize, sortorder, findCompare, tags, tagsize, save, savetag); - nmove += qsortex_sort(attributes, i+1,r, recsize, sortorder, findCompare, tags, tagsize, save, savetag); - } - return( nmove ); -} - -int qsortex_finish(void *attributes, int lo0, int hi0, int recsize, int sortorder, findCompare_func findCompare, - void *tags, int tagsize, char *save, char *savetag) -{ - int i, j, nmove = 0; - - /* This is actually InsertionSort, which is faster for local sorts */ - for(i = lo0+1; i <= hi0; i++) { - - /* Save bottom-most item */ - MEMCOPY(save, CMP_ATTRIBUTES(i), recsize); - if(tags != NULL) - MEMCOPY(savetag, CMP_TAGS(i), tagsize); - - /* Shift down! */ - j = i; - while ((j > lo0) && (sortorder*findCompare(CMP_ATTRIBUTES(j-1), save) > 0)) { - MEMCOPY(CMP_ATTRIBUTES(j), CMP_ATTRIBUTES(j-1), recsize); - if(tags != NULL) - MEMCOPY(CMP_TAGS(j), CMP_TAGS(j-1), tagsize); - j--; - nmove++; - } - - /* Store bottom-most item at the top */ - MEMCOPY(CMP_ATTRIBUTES(j), save, recsize); - if(tags != NULL) - MEMCOPY(CMP_TAGS(j), savetag, tagsize); - } - return( nmove ); -} - -int qsortex(void *attributes, int count, int offset, int recsize, MYBOOL descending, findCompare_func findCompare, void *tags, int tagsize) -{ - int iswaps = 0, sortorder = (descending ? -1 : 1); - char *save = NULL, *savetag = NULL; - - /* Check and initialize to zero-based arrays */ - if(count <= 1) - goto Finish; - attributes = (void *) CMP_ATTRIBUTES(offset); - save = (char *) malloc(recsize); - if((tagsize <= 0) && (tags != NULL)) - tags = NULL; - else if(tags != NULL) { - tags = (void *) CMP_TAGS(offset); - savetag = (char *) malloc(tagsize); - } - count--; - - /* Perform sort */ - iswaps = qsortex_sort(attributes, 0, count, recsize, sortorder, findCompare, tags, tagsize, save, savetag); -#if QS_IS_switch > 0 - iswaps += qsortex_finish(attributes, 0, count, recsize, sortorder, findCompare, tags, tagsize, save, savetag); -#endif - -Finish: - FREE(save); - FREE(savetag); - return( iswaps ); -} - -#undef QS_IS_switch /* This is a "specialized generic" version of C.A.R Hoare's Quick Sort algorithm. It will handle arrays that are already sorted, and arrays with duplicate keys. @@ -735,10 +590,10 @@ MYBOOL QS_execute(UNIONTYPE QSORTrec a[], int count, findCompare_func findCompar /* Simple specialized bubble/insertion sort functions */ -int sortByREAL(int *item, LPSREAL *weight, int size, int offset, MYBOOL unique) +int sortByREAL(int *item, REAL *weight, int size, int offset, MYBOOL unique) { int i, ii, saveI; - LPSREAL saveW; + REAL saveW; for(i = 1; i < size; i++) { ii = i+offset-1; @@ -785,10 +640,10 @@ int sortByINT(int *item, int *weight, int size, int offset, MYBOOL unique) } return(0); } -LPSREAL sortREALByINT(LPSREAL *item, int *weight, int size, int offset, MYBOOL unique) +REAL sortREALByINT(REAL *item, int *weight, int size, int offset, MYBOOL unique) { int i, ii, saveW; - LPSREAL saveI; + REAL saveI; for(i = 1; i < size; i++) { ii = i+offset-1; @@ -819,35 +674,11 @@ double timeNow(void) return((double)time(NULL)); #elif defined CLOCKTIME return((double)clock()/CLOCKS_PER_SEC /* CLK_TCK */); -#elif defined PosixTime - struct timespec t; -# if 0 - clock_gettime(CLOCK_REALTIME, &t); - return( (double) t.tv_sec + (double) t.tv_nsec/1.0e9 ); -# else - static double timeBase; - - clock_gettime(CLOCK_MONOTONIC, &t); - if(timeBase == 0) - timeBase = clockNow() - ((double) t.tv_sec + (double) t.tv_nsec/1.0e9); - return( timeBase + (double) t.tv_sec + (double) t.tv_nsec/1.0e9 ); -# endif -#elif defined EnhTime - static LARGE_INTEGER freq; - static double timeBase; - LARGE_INTEGER now; - - QueryPerformanceCounter(&now); - if(timeBase == 0) { - QueryPerformanceFrequency(&freq); - timeBase = clockNow() - (double) now.QuadPart/(double) freq.QuadPart; - } - return( timeBase + (double) now.QuadPart/(double) freq.QuadPart ); #else - + struct timeb buf; - - return((double)0); + ftime(&buf); + return((double)buf.time+((double) buf.millitm)/1000.0); #endif } @@ -895,8 +726,8 @@ void blockWriteBOOL(FILE *output, char *label, MYBOOL *myvector, int first, int fprintf(output, "\n"); } -/* List a vector of LPSREAL values for the given index range */ -void blockWriteREAL(FILE *output, char *label, LPSREAL *myvector, int first, int last) +/* List a vector of REAL values for the given index range */ +void blockWriteREAL(FILE *output, char *label, REAL *myvector, int first, int last) { int i, k = 0; @@ -916,22 +747,22 @@ void blockWriteREAL(FILE *output, char *label, LPSREAL *myvector, int first, int /* CONSOLE vector and matrix printing routines */ -void printvec( int n, LPSREAL *x, int modulo ) +void printvec( int n, REAL *x, int modulo ) { int i; if (modulo <= 0) modulo = 5; for (i = 1; i<=n; i++) { if(mod(i, modulo) == 1) - printf("\n%2d:%12g", i, x[i]); + Rprintf("\n%2d:%12g", i, x[i]); else - printf(" %2d:%12g", i, x[i]); + Rprintf(" %2d:%12g", i, x[i]); } - if(i % modulo != 0) printf("\n"); + if(i % modulo != 0) Rprintf("\n"); } -void printmatUT( int size, int n, LPSREAL *U, int modulo) +void printmatUT( int size, int n, REAL *U, int modulo) { int i, ll; ll = 0; @@ -942,7 +773,7 @@ void printmatUT( int size, int n, LPSREAL *U, int modulo) } -void printmatSQ( int size, int n, LPSREAL *X, int modulo) +void printmatSQ( int size, int n, REAL *X, int modulo) { int i, ll; ll = 0; diff --git a/src/lpsolve/headers/run_headers/commonlib.h b/src/lpSolve/src/commonlib.h similarity index 100% rename from src/lpsolve/headers/run_headers/commonlib.h rename to src/lpSolve/src/commonlib.h diff --git a/src/lpsolve/headers/run_headers/declare.h b/src/lpSolve/src/declare.h similarity index 100% rename from src/lpsolve/headers/run_headers/declare.h rename to src/lpSolve/src/declare.h diff --git a/src/lpsolve/headers/run_headers/fortify.h b/src/lpSolve/src/fortify.h similarity index 100% rename from src/lpsolve/headers/run_headers/fortify.h rename to src/lpSolve/src/fortify.h diff --git a/src/lpSolve/src/hbio.c b/src/lpSolve/src/hbio.c new file mode 100644 index 00000000..2ea44ad8 --- /dev/null +++ b/src/lpSolve/src/hbio.c @@ -0,0 +1,1631 @@ + +/* Harwell-Boeing File I/O in C + V. 1.0 + + National Institute of Standards and Technology, MD. + K.A. Remington + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + NOTICE + + Permission to use, copy, modify, and distribute this software and + its documentation for any purpose and without fee is hereby granted + provided that the above copyright notice appear in all copies and + that both the copyright notice and this permission notice appear in + supporting documentation. + + Neither the Author nor the Institution (National Institute of Standards + and Technology) make any representations about the suitability of this + software for any purpose. This software is provided "as is" without + expressed or implied warranty. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + --------------------- + INTERFACE DESCRIPTION + --------------------- + --------------- + QUERY FUNCTIONS + --------------- + + FUNCTION: + + int readHB_info(const char *filename, int *M, int *N, int *nz, + char **Type, int *Nrhs) + + DESCRIPTION: + + The readHB_info function opens and reads the header information from + the specified Harwell-Boeing file, and reports back the number of rows + and columns in the stored matrix (M and N), the number of nonzeros in + the matrix (nz), the 3-character matrix type(Type), and the number of + right-hand-sides stored along with the matrix (Nrhs). This function + is designed to retrieve basic size information which can be used to + allocate arrays. + + FUNCTION: + + int readHB_header(FILE* in_file, char* Title, char* Key, char* Type, + int* Nrow, int* Ncol, int* Nnzero, int* Nrhs, int* Nrhsix, + char* Ptrfmt, char* Indfmt, char* Valfmt, char* Rhsfmt, + int* Ptrcrd, int* Indcrd, int* Valcrd, int* Rhscrd, + char *Rhstype) + + DESCRIPTION: + + More detailed than the readHB_info function, readHB_header() reads from + the specified Harwell-Boeing file all of the header information. + + + ------------------------------ + DOUBLE PRECISION I/O FUNCTIONS + ------------------------------ + FUNCTION: + + int readHB_newmat_double(const char *filename, int *M, int *N, *int nz, + int **colptr, int **rowind, double**val) + + int readHB_mat_double(const char *filename, int *colptr, int *rowind, + double*val) + + + DESCRIPTION: + + This function opens and reads the specified file, interpreting its + contents as a sparse matrix stored in the Harwell/Boeing standard + format. (See readHB_aux_double to read auxillary vectors.) + -- Values are interpreted as double precision numbers. -- + + The "mat" function uses _pre-allocated_ vectors to hold the index and + nonzero value information. + + The "newmat" function allocates vectors to hold the index and nonzero + value information, and returns pointers to these vectors along with + matrix dimension and number of nonzeros. + + FUNCTION: + + int readHB_aux_double(const char* filename, const char AuxType, double b[]) + + int readHB_newaux_double(const char* filename, const char AuxType, double** b) + + DESCRIPTION: + + This function opens and reads from the specified file auxillary vector(s). + The char argument Auxtype determines which type of auxillary vector(s) + will be read (if present in the file). + + AuxType = 'F' right-hand-side + AuxType = 'G' initial estimate (Guess) + AuxType = 'X' eXact solution + + If Nrhs > 1, all of the Nrhs vectors of the given type are read and + stored in column-major order in the vector b. + + The "newaux" function allocates a vector to hold the values retrieved. + The "mat" function uses a _pre-allocated_ vector to hold the values. + + FUNCTION: + + int writeHB_mat_double(const char* filename, int M, int N, + int nz, const int colptr[], const int rowind[], + const double val[], int Nrhs, const double rhs[], + const double guess[], const double exact[], + const char* Title, const char* Key, const char* Type, + char* Ptrfmt, char* Indfmt, char* Valfmt, char* Rhsfmt, + const char* Rhstype) + + DESCRIPTION: + + The writeHB_mat_double function opens the named file and writes the specified + matrix and optional auxillary vector(s) to that file in Harwell-Boeing + format. The format arguments (Ptrfmt,Indfmt,Valfmt, and Rhsfmt) are + character strings specifying "Fortran-style" output formats -- as they + would appear in a Harwell-Boeing file. They are used to produce output + which is as close as possible to what would be produced by Fortran code, + but note that "D" and "P" edit descriptors are not supported. + If NULL, the following defaults will be used: + Ptrfmt = Indfmt = "(8I10)" + Valfmt = Rhsfmt = "(4E20.13)" + + ----------------------- + CHARACTER I/O FUNCTIONS + ----------------------- + FUNCTION: + + int readHB_mat_char(const char* filename, int colptr[], int rowind[], + char val[], char* Valfmt) + int readHB_newmat_char(const char* filename, int* M, int* N, int* nonzeros, + int** colptr, int** rowind, char** val, char** Valfmt) + + DESCRIPTION: + + This function opens and reads the specified file, interpreting its + contents as a sparse matrix stored in the Harwell/Boeing standard + format. (See readHB_aux_char to read auxillary vectors.) + -- Values are interpreted as char strings. -- + (Used to translate exact values from the file into a new storage format.) + + The "mat" function uses _pre-allocated_ arrays to hold the index and + nonzero value information. + + The "newmat" function allocates char arrays to hold the index + and nonzero value information, and returns pointers to these arrays + along with matrix dimension and number of nonzeros. + + FUNCTION: + + int readHB_aux_char(const char* filename, const char AuxType, char b[]) + int readHB_newaux_char(const char* filename, const char AuxType, char** b, + char** Rhsfmt) + + DESCRIPTION: + + This function opens and reads from the specified file auxillary vector(s). + The char argument Auxtype determines which type of auxillary vector(s) + will be read (if present in the file). + + AuxType = 'F' right-hand-side + AuxType = 'G' initial estimate (Guess) + AuxType = 'X' eXact solution + + If Nrhs > 1, all of the Nrhs vectors of the given type are read and + stored in column-major order in the vector b. + + The "newaux" function allocates a character array to hold the values + retrieved. + The "mat" function uses a _pre-allocated_ array to hold the values. + + FUNCTION: + + int writeHB_mat_char(const char* filename, int M, int N, + int nz, const int colptr[], const int rowind[], + const char val[], int Nrhs, const char rhs[], + const char guess[], const char exact[], + const char* Title, const char* Key, const char* Type, + char* Ptrfmt, char* Indfmt, char* Valfmt, char* Rhsfmt, + const char* Rhstype) + + DESCRIPTION: + + The writeHB_mat_char function opens the named file and writes the specified + matrix and optional auxillary vector(s) to that file in Harwell-Boeing + format. The format arguments (Ptrfmt,Indfmt,Valfmt, and Rhsfmt) are + character strings specifying "Fortran-style" output formats -- as they + would appear in a Harwell-Boeing file. Valfmt and Rhsfmt must accurately + represent the character representation of the values stored in val[] + and rhs[]. + + If NULL, the following defaults will be used for the integer vectors: + Ptrfmt = Indfmt = "(8I10)" + Valfmt = Rhsfmt = "(4E20.13)" + + +*/ + +/*---------------------------------------------------------------------*/ +/* If zero-based indexing is desired, _SP_base should be set to 0 */ +/* This will cause indices read from H-B files to be decremented by 1 */ +/* and indices written to H-B files to be incremented by 1 */ +/* <<< Standard usage is _SP_base = 1 >>> */ +#ifndef _SP_base +#define _SP_base 1 +#endif +/*---------------------------------------------------------------------*/ + +#include "hbio.h" +#include +#include +#include + +char *substr(const char* S, const int pos, const int len); +void upcase(char* S); +void IOHBTerminate(char* message); + +int readHB_info(const char* filename, int* M, int* N, int* nz, char** Type, + int* Nrhs) +{ +/****************************************************************************/ +/* The readHB_info function opens and reads the header information from */ +/* the specified Harwell-Boeing file, and reports back the number of rows */ +/* and columns in the stored matrix (M and N), the number of nonzeros in */ +/* the matrix (nz), and the number of right-hand-sides stored along with */ +/* the matrix (Nrhs). */ +/* */ +/* For a description of the Harwell Boeing standard, see: */ +/* Duff, et al., ACM TOMS Vol.15, No.1, March 1989 */ +/* */ +/* ---------- */ +/* **CAVEAT** */ +/* ---------- */ +/* ** If the input file does not adhere to the H/B format, the ** */ +/* ** results will be unpredictable. ** */ +/* */ +/****************************************************************************/ + FILE *in_file; + int Ptrcrd, Indcrd, Valcrd, Rhscrd; + int Nrow, Ncol, Nnzero, Nrhsix; + char *mat_type; + char Title[73], Key[9], Rhstype[4]; + char Ptrfmt[17], Indfmt[17], Valfmt[21], Rhsfmt[21]; + + mat_type = (char *) malloc(4); + if ( mat_type == NULL ) IOHBTerminate("Insufficient memory for mat_typen"); + + if ( (in_file = fopen( filename, "r")) == NULL ) { + REprintf("Error: Cannot open file: %s\n",filename); + return 0; + } + + readHB_header(in_file, Title, Key, mat_type, &Nrow, &Ncol, &Nnzero, + Nrhs, &Nrhsix, + Ptrfmt, Indfmt, Valfmt, Rhsfmt, + &Ptrcrd, &Indcrd, &Valcrd, &Rhscrd, Rhstype); + fclose(in_file); + *Type = mat_type; + *(*Type+3) = '\0'; + *M = Nrow; + *N = Ncol; + *nz = Nnzero; + if (Rhscrd == 0) {*Nrhs = 0;} + +/* In verbose mode, print some of the header information: */ +/* + if (verbose == 1) + { + printf("Reading from Harwell-Boeing file %s (verbose on)...\n",filename); + printf(" Title: %s\n",Title); + printf(" Key: %s\n",Key); + printf(" The stored matrix is %i by %i with %i nonzeros.\n", + *M, *N, *nz ); + printf(" %i right-hand--side(s) stored.\n",*Nrhs); + } +*/ + + return 1; + +} + + + +int readHB_header(FILE* in_file, char* Title, char* Key, char* Type, + int* Nrow, int* Ncol, int* Nnzero, int* Nrhs, int* Nrhsix, + char* Ptrfmt, char* Indfmt, char* Valfmt, char* Rhsfmt, + int* Ptrcrd, int* Indcrd, int* Valcrd, int* Rhscrd, + char *Rhstype) +{ +/*************************************************************************/ +/* Read header information from the named H/B file... */ +/*************************************************************************/ + int Totcrd,Neltvl; + char line[BUFSIZ]; + +/* First line: */ + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + if ( sscanf(line,"%*s") < 0 ) + IOHBTerminate("iohb.c: Null (or blank) first line of HB file.\n"); + (void) sscanf(line, "%72c%8[^\n]", Title, Key); + *(Key+8) = '\0'; + *(Title+72) = '\0'; + +/* Second line: */ + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + if ( sscanf(line,"%*s") < 0 ) + IOHBTerminate("iohb.c: Null (or blank) second line of HB file.\n"); + if ( sscanf(line,"%i",&Totcrd) != 1) Totcrd = 0; + if ( sscanf(line,"%*i%i",Ptrcrd) != 1) *Ptrcrd = 0; + if ( sscanf(line,"%*i%*i%i",Indcrd) != 1) *Indcrd = 0; + if ( sscanf(line,"%*i%*i%*i%i",Valcrd) != 1) *Valcrd = 0; + if ( sscanf(line,"%*i%*i%*i%*i%i",Rhscrd) != 1) *Rhscrd = 0; + +/* Third line: */ + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + if ( sscanf(line,"%*s") < 0 ) + IOHBTerminate("iohb.c: Null (or blank) third line of HB file.\n"); + if ( sscanf(line, "%3c", Type) != 1) + IOHBTerminate("iohb.c: Invalid Type info, line 3 of Harwell-Boeing file.\n"); + upcase(Type); + if ( sscanf(line,"%*3c%i",Nrow) != 1) *Nrow = 0 ; + if ( sscanf(line,"%*3c%*i%i",Ncol) != 1) *Ncol = 0 ; + if ( sscanf(line,"%*3c%*i%*i%i",Nnzero) != 1) *Nnzero = 0 ; + if ( sscanf(line,"%*3c%*i%*i%*i%i",&Neltvl) != 1) Neltvl = 0 ; + +/* Fourth line: */ + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + if ( sscanf(line,"%*s") < 0 ) + IOHBTerminate("iohb.c: Null (or blank) fourth line of HB file.\n"); + if ( sscanf(line, "%16c",Ptrfmt) != 1) + IOHBTerminate("iohb.c: Invalid format info, line 4 of Harwell-Boeing file.\n"); + if ( sscanf(line, "%*16c%16c",Indfmt) != 1) + IOHBTerminate("iohb.c: Invalid format info, line 4 of Harwell-Boeing file.\n"); + if ( sscanf(line, "%*16c%*16c%20c",Valfmt) != 1) + IOHBTerminate("iohb.c: Invalid format info, line 4 of Harwell-Boeing file.\n"); + sscanf(line, "%*16c%*16c%*20c%20c",Rhsfmt); + *(Ptrfmt+16) = '\0'; + *(Indfmt+16) = '\0'; + *(Valfmt+20) = '\0'; + *(Rhsfmt+20) = '\0'; + +/* (Optional) Fifth line: */ + if (*Rhscrd != 0 ) + { + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + if ( sscanf(line,"%*s") < 0 ) + IOHBTerminate("iohb.c: Null (or blank) fifth line of HB file.\n"); + if ( sscanf(line, "%3c", Rhstype) != 1) + IOHBTerminate("iohb.c: Invalid RHS type information, line 5 of Harwell-Boeing file.\n"); + if ( sscanf(line, "%*3c%i", Nrhs) != 1) *Nrhs = 0; + if ( sscanf(line, "%*3c%*i%i", Nrhsix) != 1) *Nrhsix = 0; + } + return 1; +} + + +int readHB_mat_double(const char* filename, int colptr[], int rowind[], + double val[]) +{ +/****************************************************************************/ +/* This function opens and reads the specified file, interpreting its */ +/* contents as a sparse matrix stored in the Harwell/Boeing standard */ +/* format and creating compressed column storage scheme vectors to hold */ +/* the index and nonzero value information. */ +/* */ +/* ---------- */ +/* **CAVEAT** */ +/* ---------- */ +/* Parsing real formats from Fortran is tricky, and this file reader */ +/* does not claim to be foolproof. It has been tested for cases when */ +/* the real values are printed consistently and evenly spaced on each */ +/* line, with Fixed (F), and Exponential (E or D) formats. */ +/* */ +/* ** If the input file does not adhere to the H/B format, the ** */ +/* ** results will be unpredictable. ** */ +/* */ +/****************************************************************************/ + FILE *in_file; + int i,j,ind,col,offset,count,last,Nrhs,Nrhsix; + int Ptrcrd, Indcrd, Valcrd, Rhscrd; + int Nrow, Ncol, Nnzero, Nentries; + int Ptrperline, Ptrwidth, Indperline, Indwidth; + int Valperline, Valwidth, Valprec; + int Valflag; /* Indicates 'E','D', or 'F' float format */ + char* ThisElement; + char Title[73], Key[9], Type[4], Rhstype[4]; + char Ptrfmt[17], Indfmt[17], Valfmt[21], Rhsfmt[21]; + char line[BUFSIZ]; + + if ( (in_file = fopen( filename, "r")) == NULL ) { + REprintf("Error: Cannot open file: %s\n",filename); + return 0; + } + + readHB_header(in_file, Title, Key, Type, &Nrow, &Ncol, &Nnzero, + &Nrhs, &Nrhsix, + Ptrfmt, Indfmt, Valfmt, Rhsfmt, + &Ptrcrd, &Indcrd, &Valcrd, &Rhscrd, Rhstype); + +/* Parse the array input formats from Line 3 of HB file */ + ParseIfmt(Ptrfmt,&Ptrperline,&Ptrwidth); + ParseIfmt(Indfmt,&Indperline,&Indwidth); + if ( Type[0] != 'P' ) { /* Skip if pattern only */ + ParseRfmt(Valfmt,&Valperline,&Valwidth,&Valprec,&Valflag); + } + +/* Read column pointer array: */ + + offset = 1-_SP_base; /* if base 0 storage is declared (via macro definition), */ + /* then storage entries are offset by 1 */ + + ThisElement = (char *) malloc(Ptrwidth+1); + if ( ThisElement == NULL ) IOHBTerminate("Insufficient memory for ThisElement."); + *(ThisElement+Ptrwidth) = '\0'; + count=0; + for (i=0;i Ncol) break; + strncpy(ThisElement,line+col,Ptrwidth); + /* ThisElement = substr(line,col,Ptrwidth); */ + colptr[count] = atoi(ThisElement)-offset; + count++; col += Ptrwidth; + } + } + free(ThisElement); + +/* Read row index array: */ + + ThisElement = (char *) malloc(Indwidth+1); + if ( ThisElement == NULL ) IOHBTerminate("Insufficient memory for ThisElement."); + *(ThisElement+Indwidth) = '\0'; + count = 0; + for (i=0;i=0;j--) { + ThisElement[j] = ThisElement[j-1]; + if ( ThisElement[j] == '+' || ThisElement[j] == '-' ) { + ThisElement[j-1] = Valflag; + break; + } + } + } + val[count] = atof(ThisElement); + count++; col += Valwidth; + } + } + free(ThisElement); + } + + fclose(in_file); + return 1; +} + +int readHB_newmat_double(const char* filename, int* M, int* N, int* nonzeros, + int** colptr, int** rowind, double** val) +{ + int Nrhs; + char *Type; + + readHB_info(filename, M, N, nonzeros, &Type, &Nrhs); + + *colptr = (int *)malloc((*N+1)*sizeof(int)); + if ( *colptr == NULL ) IOHBTerminate("Insufficient memory for colptr.\n"); + *rowind = (int *)malloc(*nonzeros*sizeof(int)); + if ( *rowind == NULL ) IOHBTerminate("Insufficient memory for rowind.\n"); + if ( Type[0] == 'C' ) { +/* + REprintf("Warning: Reading complex data from HB file %s.\n",filename); + REprintf(" Real and imaginary parts will be interlaced in val[].\n"); +*/ + /* Malloc enough space for real AND imaginary parts of val[] */ + *val = (double *)malloc(*nonzeros*sizeof(double)*2); + if ( *val == NULL ) IOHBTerminate("Insufficient memory for val.\n"); + } else { + if ( Type[0] != 'P' ) { + /* Malloc enough space for real array val[] */ + *val = (double *)malloc(*nonzeros*sizeof(double)); + if ( *val == NULL ) IOHBTerminate("Insufficient memory for val.\n"); + } + } /* No val[] space needed if pattern only */ + return readHB_mat_double(filename, *colptr, *rowind, *val); + +} + +int readHB_aux_double(const char* filename, const char AuxType, double b[]) +{ +/****************************************************************************/ +/* This function opens and reads the specified file, placing auxillary */ +/* vector(s) of the given type (if available) in b. */ +/* Return value is the number of vectors successfully read. */ +/* */ +/* AuxType = 'F' full right-hand-side vector(s) */ +/* AuxType = 'G' initial Guess vector(s) */ +/* AuxType = 'X' eXact solution vector(s) */ +/* */ +/* ---------- */ +/* **CAVEAT** */ +/* ---------- */ +/* Parsing real formats from Fortran is tricky, and this file reader */ +/* does not claim to be foolproof. It has been tested for cases when */ +/* the real values are printed consistently and evenly spaced on each */ +/* line, with Fixed (F), and Exponential (E or D) formats. */ +/* */ +/* ** If the input file does not adhere to the H/B format, the ** */ +/* ** results will be unpredictable. ** */ +/* */ +/****************************************************************************/ + FILE *in_file; + int i,j,n,maxcol,start,stride,col,last,linel; + int Ptrcrd, Indcrd, Valcrd, Rhscrd; + int Nrow, Ncol, Nnzero, Nentries; + int Nrhs, Nrhsix, nvecs, rhsi; + int Rhsperline, Rhswidth, Rhsprec; + int Rhsflag; + char *ThisElement; + char Title[73], Key[9], Type[4], Rhstype[4]; + char Ptrfmt[17], Indfmt[17], Valfmt[21], Rhsfmt[21]; + char line[BUFSIZ]; + + if ((in_file = fopen( filename, "r")) == NULL) { + REprintf("Error: Cannot open file: %s\n",filename); + return 0; + } + + readHB_header(in_file, Title, Key, Type, &Nrow, &Ncol, &Nnzero, + &Nrhs, &Nrhsix, + Ptrfmt, Indfmt, Valfmt, Rhsfmt, + &Ptrcrd, &Indcrd, &Valcrd, &Rhscrd, Rhstype); + + if (Nrhs <= 0) + { + REprintf("Warn: Attempt to read auxillary vector(s) when none are present.\n"); + return 0; + } + if (Rhstype[0] != 'F' ) + { + REprintf("Warn: Attempt to read auxillary vector(s) which are not stored in Full form.\n"); + REprintf(" Rhs must be specified as full. \n"); + return 0; + } + +/* If reading complex data, allow for interleaved real and imaginary values. */ + if ( Type[0] == 'C' ) { + Nentries = 2*Nrow; + } else { + Nentries = Nrow; + } + + nvecs = 1; + + if ( Rhstype[1] == 'G' ) nvecs++; + if ( Rhstype[2] == 'X' ) nvecs++; + + if ( AuxType == 'G' && Rhstype[1] != 'G' ) { + REprintf("Warn: Attempt to read auxillary Guess vector(s) when none are present.\n"); + return 0; + } + if ( AuxType == 'X' && Rhstype[2] != 'X' ) { + REprintf("Warn: Attempt to read auxillary eXact solution vector(s) when none are present.\n"); + return 0; + } + + ParseRfmt(Rhsfmt, &Rhsperline, &Rhswidth, &Rhsprec,&Rhsflag); + maxcol = Rhsperline*Rhswidth; + +/* Lines to skip before starting to read RHS values... */ + n = Ptrcrd + Indcrd + Valcrd; + + for (i = 0; i < n; i++) + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + +/* start - number of initial aux vector entries to skip */ +/* to reach first vector requested */ +/* stride - number of aux vector entries to skip between */ +/* requested vectors */ + if ( AuxType == 'F' ) start = 0; + else if ( AuxType == 'G' ) start = Nentries; + else start = (nvecs-1)*Nentries; + stride = (nvecs-1)*Nentries; + + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + linel= strchr(line,'\n')-line; + col = 0; +/* Skip to initial offset */ + + for (i=0;i= ( maxcol= ( maxcol=0;j--) { + ThisElement[j] = ThisElement[j-1]; + if ( ThisElement[j] == '+' || ThisElement[j] == '-' ) { + ThisElement[j-1] = Rhsflag; + break; + } + } + } + b[i] = atof(ThisElement); + col += Rhswidth; + } + +/* Skip any interleaved Guess/eXact vectors */ + + for (i=0;i= ( maxcol 0 ) { + if ( Rhsfmt == NULL ) Rhsfmt = Valfmt; + ParseRfmt(Rhsfmt,&Rhsperline,&Rhswidth,&Rhsprec, &Rhsflag); + if (Rhsflag == 'F') + snprintf(rformat,sizeof(rformat),"%% %d.%df",Rhswidth,Rhsprec); + else + snprintf(rformat,sizeof(rformat),"%% %d.%dE",Rhswidth,Rhsprec); + if (Rhsflag == 'D') *strchr(Rhsfmt,'D') = 'E'; + rhscrd = nrhsentries/Rhsperline; + if ( nrhsentries%Rhsperline != 0) rhscrd++; + if ( Rhstype[1] == 'G' ) rhscrd+=rhscrd; + if ( Rhstype[2] == 'X' ) rhscrd+=rhscrd; + rhscrd*=Nrhs; + } else rhscrd = 0; + + totcrd = 4+ptrcrd+indcrd+valcrd+rhscrd; + + +/* Print header information: */ + + fprintf(out_file,"%-72s%-8s\n%14d%14d%14d%14d%14d\n",Title, Key, totcrd, + ptrcrd, indcrd, valcrd, rhscrd); + fprintf(out_file,"%3s%11s%14d%14d%14d\n",Type," ", M, N, nz); + fprintf(out_file,"%-16s%-16s%-20s", Ptrfmt, Indfmt, Valfmt); + if ( Nrhs != 0 ) { +/* Print Rhsfmt on fourth line and */ +/* optional fifth header line for auxillary vector information: */ + fprintf(out_file,"%-20s\n%-14s%d\n",Rhsfmt,Rhstype,Nrhs); + } else fprintf(out_file,"\n"); + + offset = 1-_SP_base; /* if base 0 storage is declared (via macro definition), */ + /* then storage entries are offset by 1 */ + +/* Print column pointers: */ + for (i=0;i 0 ) { + for (i=0;i Ncol) break; + strncpy(ThisElement,line+col,Ptrwidth); + /*ThisElement = substr(line,col,Ptrwidth);*/ + colptr[count] = atoi(ThisElement)-offset; + count++; col += Ptrwidth; + } + } + free(ThisElement); + +/* Read row index array: */ + + ThisElement = (char *) malloc(Indwidth+1); + if ( ThisElement == NULL ) IOHBTerminate("Insufficient memory for ThisElement."); + *(ThisElement+Indwidth) = '\0'; + count = 0; + for (i=0;i=0;j--) { + ThisElement[j] = ThisElement[j-1]; + if ( ThisElement[j] == '+' || ThisElement[j] == '-' ) { + ThisElement[j-1] = Valflag; + break; + } + } + } + count++; col += Valwidth; + } + } + } + + return 1; +} + +int readHB_newmat_char(const char* filename, int* M, int* N, int* nonzeros, int** colptr, + int** rowind, char** val, char** Valfmt) +{ + FILE *in_file; + int Nrhs,Nrhsix; + int Ptrcrd, Indcrd, Valcrd, Rhscrd; + int Valperline, Valwidth, Valprec; + int Valflag; /* Indicates 'E','D', or 'F' float format */ + char Title[73], Key[9], Type[4], Rhstype[4]; + char Ptrfmt[17], Indfmt[17], Rhsfmt[21]; + + if ((in_file = fopen( filename, "r")) == NULL) { + REprintf("Error: Cannot open file: %s\n",filename); + return 0; + } + + *Valfmt = (char *)malloc(21*sizeof(char)); + if ( *Valfmt == NULL ) IOHBTerminate("Insufficient memory for Valfmt."); + readHB_header(in_file, Title, Key, Type, M, N, nonzeros, + &Nrhs, &Nrhsix, + Ptrfmt, Indfmt, (*Valfmt), Rhsfmt, + &Ptrcrd, &Indcrd, &Valcrd, &Rhscrd, Rhstype); + fclose(in_file); + ParseRfmt(*Valfmt,&Valperline,&Valwidth,&Valprec,&Valflag); + + *colptr = (int *)malloc((*N+1)*sizeof(int)); + if ( *colptr == NULL ) IOHBTerminate("Insufficient memory for colptr.\n"); + *rowind = (int *)malloc(*nonzeros*sizeof(int)); + if ( *rowind == NULL ) IOHBTerminate("Insufficient memory for rowind.\n"); + if ( Type[0] == 'C' ) { +/* + REprintf("Warning: Reading complex data from HB file %s.\n",filename); + REprintf(" Real and imaginary parts will be interlaced in val[].\n"); +*/ + /* Malloc enough space for real AND imaginary parts of val[] */ + *val = (char *)malloc(*nonzeros*Valwidth*sizeof(char)*2); + if ( *val == NULL ) IOHBTerminate("Insufficient memory for val.\n"); + } else { + if ( Type[0] != 'P' ) { + /* Malloc enough space for real array val[] */ + *val = (char *)malloc(*nonzeros*Valwidth*sizeof(char)); + if ( *val == NULL ) IOHBTerminate("Insufficient memory for val.\n"); + } + } /* No val[] space needed if pattern only */ + return readHB_mat_char(filename, *colptr, *rowind, *val, *Valfmt); + +} + +int readHB_aux_char(const char* filename, const char AuxType, char b[]) +{ +/****************************************************************************/ +/* This function opens and reads the specified file, placing auxilary */ +/* vector(s) of the given type (if available) in b : */ +/* Return value is the number of vectors successfully read. */ +/* */ +/* AuxType = 'F' full right-hand-side vector(s) */ +/* AuxType = 'G' initial Guess vector(s) */ +/* AuxType = 'X' eXact solution vector(s) */ +/* */ +/* ---------- */ +/* **CAVEAT** */ +/* ---------- */ +/* Parsing real formats from Fortran is tricky, and this file reader */ +/* does not claim to be foolproof. It has been tested for cases when */ +/* the real values are printed consistently and evenly spaced on each */ +/* line, with Fixed (F), and Exponential (E or D) formats. */ +/* */ +/* ** If the input file does not adhere to the H/B format, the ** */ +/* ** results will be unpredictable. ** */ +/* */ +/****************************************************************************/ + FILE *in_file; + int i,j,n,maxcol,start,stride,col,last,linel,nvecs,rhsi; + int Nrow, Ncol, Nnzero, Nentries,Nrhs,Nrhsix; + int Ptrcrd, Indcrd, Valcrd, Rhscrd; + int Rhsperline, Rhswidth, Rhsprec; + int Rhsflag; + char Title[73], Key[9], Type[4], Rhstype[4]; + char Ptrfmt[17], Indfmt[17], Valfmt[21], Rhsfmt[21]; + char line[BUFSIZ]; + char *ThisElement; + + if ((in_file = fopen( filename, "r")) == NULL) { + REprintf("Error: Cannot open file: %s\n",filename); + return 0; + } + + readHB_header(in_file, Title, Key, Type, &Nrow, &Ncol, &Nnzero, + &Nrhs, &Nrhsix, + Ptrfmt, Indfmt, Valfmt, Rhsfmt, + &Ptrcrd, &Indcrd, &Valcrd, &Rhscrd, Rhstype); + + if (Nrhs <= 0) + { + REprintf("Warn: Attempt to read auxillary vector(s) when none are present.\n"); + return 0; + } + if (Rhstype[0] != 'F' ) + { + REprintf("Warn: Attempt to read auxillary vector(s) which are not stored in Full form.\n"); + REprintf(" Rhs must be specified as full. \n"); + return 0; + } + +/* If reading complex data, allow for interleaved real and imaginary values. */ + if ( Type[0] == 'C' ) { + Nentries = 2*Nrow; + } else { + Nentries = Nrow; + } + + nvecs = 1; + + if ( Rhstype[1] == 'G' ) nvecs++; + if ( Rhstype[2] == 'X' ) nvecs++; + + if ( AuxType == 'G' && Rhstype[1] != 'G' ) { + REprintf("Warn: Attempt to read auxillary Guess vector(s) when none are present.\n"); + return 0; + } + if ( AuxType == 'X' && Rhstype[2] != 'X' ) { + REprintf("Warn: Attempt to read auxillary eXact solution vector(s) when none are present.\n"); + return 0; + } + + ParseRfmt(Rhsfmt, &Rhsperline, &Rhswidth, &Rhsprec,&Rhsflag); + maxcol = Rhsperline*Rhswidth; + +/* Lines to skip before starting to read RHS values... */ + n = Ptrcrd + Indcrd + Valcrd; + + for (i = 0; i < n; i++) + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + +/* start - number of initial aux vector entries to skip */ +/* to reach first vector requested */ +/* stride - number of aux vector entries to skip between */ +/* requested vectors */ + if ( AuxType == 'F' ) start = 0; + else if ( AuxType == 'G' ) start = Nentries; + else start = (nvecs-1)*Nentries; + stride = (nvecs-1)*Nentries; + + if ( fgets(line, BUFSIZ, in_file) ) + IOHBTerminate("iohb.c: Error in input\n"); + linel= strchr(line,'\n')-line; + if ( sscanf(line,"%*s") < 0 ) + IOHBTerminate("iohb.c: Null (or blank) line in auxillary vector data region of HB file.\n"); + col = 0; +/* Skip to initial offset */ + + for (i=0;i= ( maxcol= ( maxcol=0;j--) { + ThisElement[j] = ThisElement[j-1]; + if ( ThisElement[j] == '+' || ThisElement[j] == '-' ) { + ThisElement[j-1] = Rhsflag; + break; + } + } + } + col += Rhswidth; + } + b+=Nentries*Rhswidth; + +/* Skip any interleaved Guess/eXact vectors */ + + for (i=0;i= ( maxcol 0 ) { + if ( Rhsfmt == NULL ) Rhsfmt = Valfmt; + ParseRfmt(Rhsfmt,&Rhsperline,&Rhswidth,&Rhsprec, &Rhsflag); + snprintf(rformat,sizeof(rformat),"%%%ds",Rhswidth); + rhscrd = nrhsentries/Rhsperline; + if ( nrhsentries%Rhsperline != 0) rhscrd++; + if ( Rhstype[1] == 'G' ) rhscrd+=rhscrd; + if ( Rhstype[2] == 'X' ) rhscrd+=rhscrd; + rhscrd*=Nrhs; + } else rhscrd = 0; + + totcrd = 4+ptrcrd+indcrd+valcrd+rhscrd; + + +/* Print header information: */ + + fprintf(out_file,"%-72s%-8s\n%14d%14d%14d%14d%14d\n",Title, Key, totcrd, + ptrcrd, indcrd, valcrd, rhscrd); + fprintf(out_file,"%3s%11s%14d%14d%14d\n",Type," ", M, N, nz); + fprintf(out_file,"%-16s%-16s%-20s", Ptrfmt, Indfmt, Valfmt); + if ( Nrhs != 0 ) { +/* Print Rhsfmt on fourth line and */ +/* optional fifth header line for auxillary vector information: */ + fprintf(out_file,"%-20s\n%-14s%d\n",Rhsfmt,Rhstype,Nrhs); + } else fprintf(out_file,"\n"); + + offset = 1-_SP_base; /* if base 0 storage is declared (via macro definition), */ + /* then storage entries are offset by 1 */ + +/* Print column pointers: */ + for (i=0;i 0 ) { + for (j=0;j +void upcase(char* S) +{ +/* Convert S to uppercase */ + int i,len; + len = strlen(S); + for (i=0;i< len;i++) + S[i] = toupper(S[i]); +} + +void IOHBTerminate(char* message) +{ +/* fprintf(stderr,message); +** exit(1); */ + error(message); +} + diff --git a/src/lpsolve/headers/run_headers/hbio.h b/src/lpSolve/src/hbio.h similarity index 100% rename from src/lpsolve/headers/run_headers/hbio.h rename to src/lpSolve/src/hbio.h diff --git a/src/lpsolve/build/lp_solve/ini.c b/src/lpSolve/src/ini.c similarity index 96% rename from src/lpsolve/build/lp_solve/ini.c rename to src/lpSolve/src/ini.c index df263f1e..064aa867 100644 --- a/src/lpsolve/build/lp_solve/ini.c +++ b/src/lpSolve/src/ini.c @@ -2,8 +2,6 @@ #include #include -#include "lp_lib.h" - #include "ini.h" FILE *ini_create(char *filename) @@ -58,7 +56,7 @@ int ini_readdata(FILE *fp, char *data, int szdata, int withcomment) *ptr = 0; } - l = (int) strlen(data); + l = strlen(data); while((l > 0) && (isspace(data[l - 1]))) l--; data[l] = 0; diff --git a/src/lpsolve/headers/run_headers/ini.h b/src/lpSolve/src/ini.h similarity index 100% rename from src/lpsolve/headers/run_headers/ini.h rename to src/lpSolve/src/ini.h diff --git a/src/lpSolve/src/init.c b/src/lpSolve/src/init.c new file mode 100644 index 00000000..99b7451b --- /dev/null +++ b/src/lpSolve/src/init.c @@ -0,0 +1,22 @@ +#include // for NULL +#include + +/* FIXME: + Check these declarations against the C/Fortran source code. +*/ + +/* .C calls */ +extern void lp_transbig(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *); +extern void lpslink(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *); + +static const R_CMethodDef CEntries[] = { + {"lp_transbig", (DL_FUNC) &lp_transbig, 20}, + {"lpslink", (DL_FUNC) &lpslink, 29}, + {NULL, NULL, 0} +}; + +void R_init_lpSolve(DllInfo *dll) +{ + R_registerRoutines(dll, CEntries, NULL, NULL, NULL); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/lpSolve/src/isfixedvar.c b/src/lpSolve/src/isfixedvar.c new file mode 100644 index 00000000..70d6af47 --- /dev/null +++ b/src/lpSolve/src/isfixedvar.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include + +#include "lp_types.h" +#include "lp_utils.h" +#include "lp_lib.h" + +MYBOOL is_fixedvar(lprec *lp, int variable) +{ + if((lp->bb_bounds != NULL && lp->bb_bounds->UBzerobased) || (variable <= lp->rows)) + return( (MYBOOL) (lp->upbo[variable] < lp->epsprimal) ); + else + return( (MYBOOL) (lp->upbo[variable]-lp->lowbo[variable] < lp->epsprimal) ); +} /* is_fixedvar */ + + diff --git a/src/lpsolve/headers/run_headers/lp_BFP.h b/src/lpSolve/src/lp_BFP.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_BFP.h rename to src/lpSolve/src/lp_BFP.h diff --git a/src/lpsolve/headers/run_headers/lp_BFP1.h b/src/lpSolve/src/lp_BFP1.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_BFP1.h rename to src/lpSolve/src/lp_BFP1.h diff --git a/src/lpsolve/headers/run_headers/lp_BFP2.h b/src/lpSolve/src/lp_BFP2.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_BFP2.h rename to src/lpSolve/src/lp_BFP2.h diff --git a/src/lpsolve/headers/run_headers/lp_Hash.c b/src/lpSolve/src/lp_Hash.c similarity index 100% rename from src/lpsolve/headers/run_headers/lp_Hash.c rename to src/lpSolve/src/lp_Hash.c diff --git a/src/lpsolve/headers/run_headers/lp_Hash.h b/src/lpSolve/src/lp_Hash.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_Hash.h rename to src/lpSolve/src/lp_Hash.h diff --git a/src/lpsolve/build/lp_solve/lp_LUSOL.c b/src/lpSolve/src/lp_LUSOL.c similarity index 90% rename from src/lpsolve/build/lp_solve/lp_LUSOL.c rename to src/lpSolve/src/lp_LUSOL.c index 934518ef..a7b308b1 100644 --- a/src/lpsolve/build/lp_solve/lp_LUSOL.c +++ b/src/lpSolve/src/lp_LUSOL.c @@ -34,9 +34,8 @@ #endif /* Include routines common to factorization engine implementations */ -#include "lp_BFP1.c" -#include "lp_BFP2.c" - +#include "lp_BFP1.h" +#include "lp_BFP2.h" /* MUST MODIFY */ char * BFP_CALLMODEL bfp_name(void) @@ -62,16 +61,16 @@ MYBOOL BFP_CALLMODEL bfp_resize(lprec *lp, int newsize) /* Data specific to the factorization engine */ if(lu->LUSOL != NULL) { - if(newsize > 0 || 1) + if(newsize > 0) LUSOL_sizeto(lu->LUSOL, newsize, newsize, 0); else { LUSOL_free(lu->LUSOL); lu->LUSOL = NULL; } } - else if(newsize > 0 || 1) { + else if(newsize > 0) { int asize; - LPSREAL bsize; + REAL bsize; lu->LUSOL = LUSOL_create(NULL, 0, LUSOL_PIVMOD_TPP, bfp_pivotmax(lp)*0); @@ -108,7 +107,7 @@ MYBOOL BFP_CALLMODEL bfp_resize(lprec *lp, int newsize) #endif /* Try to minimize memory allocation if we have a large number of unit columns */ - bsize = (LPSREAL) lp->get_nonzeros(lp); + bsize = (REAL) lp->get_nonzeros(lp); if(newsize > lp->columns) bsize += newsize; else @@ -168,14 +167,14 @@ int BFP_CALLMODEL bfp_memallocated(lprec *lp) int mem; LUSOLrec *LUSOL = lp->invB->LUSOL; - mem = sizeof(LPSREAL) * (LUSOL->lena+LUSOL->maxm+LUSOL_RP_LASTITEM); + mem = sizeof(REAL) * (LUSOL->lena+LUSOL->maxm+LUSOL_RP_LASTITEM); mem += sizeof(int) * (2*LUSOL->lena+5*LUSOL->maxm+5*LUSOL->maxn+LUSOL_IP_LASTITEM); if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TCP) - mem += sizeof(LPSREAL) * LUSOL->maxn + 2*sizeof(LPSREAL)*LUSOL->maxn; + mem += sizeof(REAL) * LUSOL->maxn + 2*sizeof(REAL)*LUSOL->maxn; else if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TRP) - mem += sizeof(LPSREAL) * LUSOL->maxn; + mem += sizeof(REAL) * LUSOL->maxn; if(!LUSOL->luparm[LUSOL_IP_KEEPLU]) - mem += sizeof(LPSREAL) * LUSOL->maxn; + mem += sizeof(REAL) * LUSOL->maxn; return( mem ); } @@ -206,9 +205,9 @@ int BFP_CALLMODEL bfp_preparefactorization(lprec *lp) /* LOCAL HELPER ROUTINE - Replace a basis column with corresponding slack */ int bfp_LUSOLsetcolumn(lprec *lp, int posnr, int colnr) { - int inform; + int inform; /* was int nz, inform; */ - //@FS: unused// nz = lp->get_lpcolumn(lp, colnr, lp->invB->LUSOL->w + bfp_rowoffset(lp), NULL, NULL); + /* nz = lp->get_lpcolumn(lp, colnr, lp->invB->LUSOL->w + bfp_rowoffset(lp), NULL, NULL); */ inform = LUSOL_replaceColumn(lp->invB->LUSOL, posnr, lp->invB->LUSOL->w); return( inform ); } @@ -321,15 +320,14 @@ void bfp_LUSOLtighten(lprec *lp) case FALSE: lp->report(lp, infolevel, "bfp_factorize: Very hard numerics, but cannot tighten LUSOL thresholds further.\n"); break; case TRUE: lp->report(lp, infolevel, "bfp_factorize: Frequent refact pivot count %d at iter %.0f; tightened thresholds.\n", - lp->invB->num_pivots, (LPSREAL) lp->get_total_iter(lp)); + lp->invB->num_pivots, (REAL) lp->get_total_iter(lp)); break; default: lp->report(lp, infolevel, "bfp_factorize: LUSOL switched to %s pivoting model to enhance stability.\n", LUSOL_pivotLabel(lp->invB->LUSOL)); } } -#define is_fixedvar is_fixedvar_ /* resolves a compiler warning/error conflict with lp_lib.h */ - +#if 0 static MYBOOL is_fixedvar(lprec *lp, int variable) { if((lp->bb_bounds != NULL && lp->bb_bounds->UBzerobased) || (variable <= lp->rows)) @@ -337,6 +335,7 @@ static MYBOOL is_fixedvar(lprec *lp, int variable) else return( (MYBOOL) (lp->upbo[variable]-lp->lowbo[variable] < lp->epsprimal) ); } /* is_fixedvar */ +#endif /* MUST MODIFY */ int BFP_CALLMODEL bfp_factorize(lprec *lp, int uservars, int Bsize, MYBOOL *usedpos, MYBOOL final) @@ -380,7 +379,7 @@ int BFP_CALLMODEL bfp_factorize(lprec *lp, int uservars, int Bsize, MYBOOL *used if(inform != LUSOL_INFORM_LUSUCCESS) { int singularcols, replacedcols = 0; - LPSREAL hold; + REAL hold; /* Make sure we do not tighten factorization pivot criteria too often, and simply accept the substitution of slack columns into the basis */ @@ -394,7 +393,7 @@ int BFP_CALLMODEL bfp_factorize(lprec *lp, int uservars, int Bsize, MYBOOL *used singularities++; singularcols = LUSOL->luparm[LUSOL_IP_SINGULARITIES]; - hold = (LPSREAL) lp->get_total_iter(lp); + hold = (REAL) lp->get_total_iter(lp); lp->report(lp, NORMAL, "bfp_factorize: Resolving %d singularit%s at refact %d, iter %.0f\n", singularcols, my_plural_y(singularcols), lp->invB->num_refact, hold); @@ -474,8 +473,8 @@ int BFP_CALLMODEL bfp_factorize(lprec *lp, int uservars, int Bsize, MYBOOL *used MYBOOL BFP_CALLMODEL bfp_finishupdate(lprec *lp, MYBOOL changesign) /* Was addetacol() in versions of lp_solve before 4.0.1.8 - KE */ { - int i, k, deltarows = bfp_rowoffset(lp); - LPSREAL DIAG, VNORM; + int i, k, /* kcol, */ deltarows = bfp_rowoffset(lp); + REAL DIAG, VNORM; INVrec *lu = lp->invB; LUSOLrec *LUSOL = lu->LUSOL; @@ -491,14 +490,14 @@ MYBOOL BFP_CALLMODEL bfp_finishupdate(lprec *lp, MYBOOL changesign) lu->user_colcount--; if(lu->col_enter > lu->dimcount-deltarows) lu->user_colcount++; - //@FS: unused// kcol = lu->col_pos; + /* kcol = lu->col_pos; */ lu->col_pos = 0; /* Do standard update */ #ifdef LUSOLSafeFastUpdate /* NB! Defined in lusol.h */ if(TRUE || !changesign) { if(changesign) { - LPSREAL *temp = LUSOL->vLU6L; + REAL *temp = LUSOL->vLU6L; for(i = 1, temp++; i <= lp->rows+deltarows; i++, temp++) if(*temp != 0) *temp = -(*temp); @@ -536,7 +535,7 @@ MYBOOL BFP_CALLMODEL bfp_finishupdate(lprec *lp, MYBOOL changesign) /* Additional KE logic to reduce maximum pivot count based on the density of B */ if(!lu->force_refact) { VNORM = lp->rows+1; - VNORM = 1.0 - pow((LPSREAL) LUSOL->nelem/VNORM/VNORM, 0.2); + VNORM = 1.0 - pow((REAL) LUSOL->nelem/VNORM/VNORM, 0.2); lu->force_refact = (MYBOOL) (lu->num_pivots > VNORM*lp->bfp_pivotmax(lp)); } #endif @@ -547,12 +546,12 @@ MYBOOL BFP_CALLMODEL bfp_finishupdate(lprec *lp, MYBOOL changesign) /* int infolevel = NORMAL; */ int infolevel = DETAILED; lp->report(lp, infolevel, "bfp_finishupdate: Failed at iter %.0f, pivot %d;\n%s\n", - (LPSREAL) (lp->total_iter+lp->current_iter), lu->num_pivots, LUSOL_informstr(LUSOL, i)); + (REAL) (lp->total_iter+lp->current_iter), lu->num_pivots, LUSOL_informstr(LUSOL, i)); if(i == LUSOL_INFORM_ANEEDMEM) { /* To compress used memory and realloc, if necessary */ lp->invert(lp, INITSOL_USEZERO, FALSE); if(i != LUSOL_INFORM_LUSUCCESS) lp->report(lp, NORMAL, "bfp_finishupdate: Insufficient memory at iter %.0f;\n%s\n", - (LPSREAL) (lp->total_iter+lp->current_iter), LUSOL_informstr(LUSOL, i)); + (REAL) (lp->total_iter+lp->current_iter), LUSOL_informstr(LUSOL, i)); } else if(i == LUSOL_INFORM_RANKLOSS) { /* To fix rank loss and clear cumulative errors */ #if 0 @@ -568,7 +567,7 @@ MYBOOL BFP_CALLMODEL bfp_finishupdate(lprec *lp, MYBOOL changesign) i = LUSOL->luparm[LUSOL_IP_INFORM]; if(i != LUSOL_INFORM_LUSUCCESS) lp->report(lp, NORMAL, "bfp_finishupdate: Recovery attempt unsuccessful at iter %.0f;\n%s\n", - (LPSREAL) (lp->total_iter+lp->current_iter), LUSOL_informstr(LUSOL, i)); + (REAL) (lp->total_iter+lp->current_iter), LUSOL_informstr(LUSOL, i)); else lp->report(lp, infolevel, "bfp_finishupdate: Correction or recovery was successful.\n"); } @@ -579,7 +578,7 @@ MYBOOL BFP_CALLMODEL bfp_finishupdate(lprec *lp, MYBOOL changesign) /* MUST MODIFY */ -void BFP_CALLMODEL bfp_ftran_normal(lprec *lp, LPSREAL *pcol, int *nzidx) +void BFP_CALLMODEL bfp_ftran_normal(lprec *lp, REAL *pcol, int *nzidx) { int i; INVrec *lu; @@ -591,13 +590,13 @@ void BFP_CALLMODEL bfp_ftran_normal(lprec *lp, LPSREAL *pcol, int *nzidx) if(i != LUSOL_INFORM_LUSUCCESS) { lu->status = BFP_STATUS_ERROR; lp->report(lp, NORMAL, "bfp_ftran_normal: Failed at iter %.0f, pivot %d;\n%s\n", - (LPSREAL) (lp->total_iter+lp->current_iter), lu->num_pivots, LUSOL_informstr(lu->LUSOL, i)); + (REAL) (lp->total_iter+lp->current_iter), lu->num_pivots, LUSOL_informstr(lu->LUSOL, i)); } } /* MAY MODIFY */ -void BFP_CALLMODEL bfp_ftran_prepare(lprec *lp, LPSREAL *pcol, int *nzidx) +void BFP_CALLMODEL bfp_ftran_prepare(lprec *lp, REAL *pcol, int *nzidx) { int i; INVrec *lu; @@ -609,13 +608,13 @@ void BFP_CALLMODEL bfp_ftran_prepare(lprec *lp, LPSREAL *pcol, int *nzidx) if(i != LUSOL_INFORM_LUSUCCESS) { lu->status = BFP_STATUS_ERROR; lp->report(lp, NORMAL, "bfp_ftran_prepare: Failed at iter %.0f, pivot %d;\n%s\n", - (LPSREAL) (lp->total_iter+lp->current_iter), lu->num_pivots, LUSOL_informstr(lu->LUSOL, i)); + (REAL) (lp->total_iter+lp->current_iter), lu->num_pivots, LUSOL_informstr(lu->LUSOL, i)); } } /* MUST MODIFY */ -void BFP_CALLMODEL bfp_btran_normal(lprec *lp, LPSREAL *prow, int *nzidx) +void BFP_CALLMODEL bfp_btran_normal(lprec *lp, REAL *prow, int *nzidx) { int i; INVrec *lu; @@ -627,7 +626,7 @@ void BFP_CALLMODEL bfp_btran_normal(lprec *lp, LPSREAL *prow, int *nzidx) if(i != LUSOL_INFORM_LUSUCCESS) { lu->status = BFP_STATUS_ERROR; lp->report(lp, NORMAL, "bfp_btran_normal: Failed at iter %.0f, pivot %d;\n%s\n", - (LPSREAL) (lp->total_iter+lp->current_iter), lu->num_pivots, LUSOL_informstr(lu->LUSOL, i)); + (REAL) (lp->total_iter+lp->current_iter), lu->num_pivots, LUSOL_informstr(lu->LUSOL, i)); } /* Check performance data */ @@ -635,13 +634,13 @@ void BFP_CALLMODEL bfp_btran_normal(lprec *lp, LPSREAL *prow, int *nzidx) if(lu->num_pivots == 1) { if(lu->LUSOL->luparm[LUSOL_IP_ACCELERATION] > 0) lp->report(lp, NORMAL, "RowL0 R:%10.7f C:%10.7f NZ:%10.7f\n", - (LPSREAL) lu->LUSOL->luparm[LUSOL_IP_ROWCOUNT_L0] / lu->LUSOL->m, - (LPSREAL) lu->LUSOL->luparm[LUSOL_IP_COLCOUNT_L0] / lu->LUSOL->m, - (LPSREAL) lu->LUSOL->luparm[LUSOL_IP_NONZEROS_L0] / pow((LPSREAL) lu->LUSOL->m, 2)); + (REAL) lu->LUSOL->luparm[LUSOL_IP_ROWCOUNT_L0] / lu->LUSOL->m, + (REAL) lu->LUSOL->luparm[LUSOL_IP_COLCOUNT_L0] / lu->LUSOL->m, + (REAL) lu->LUSOL->luparm[LUSOL_IP_NONZEROS_L0] / pow((REAL) lu->LUSOL->m, 2)); else lp->report(lp, NORMAL, "ColL0 C:%10.7f NZ:%10.7f\n", - (LPSREAL) lu->LUSOL->luparm[LUSOL_IP_COLCOUNT_L0] / lu->LUSOL->m, - (LPSREAL) lu->LUSOL->luparm[LUSOL_IP_NONZEROS_L0] / pow((LPSREAL) lu->LUSOL->m, 2)); + (REAL) lu->LUSOL->luparm[LUSOL_IP_COLCOUNT_L0] / lu->LUSOL->m, + (REAL) lu->LUSOL->luparm[LUSOL_IP_NONZEROS_L0] / pow((REAL) lu->LUSOL->m, 2)); } #endif @@ -651,7 +650,7 @@ void BFP_CALLMODEL bfp_btran_normal(lprec *lp, LPSREAL *prow, int *nzidx) int BFP_CALLMODEL bfp_findredundant(lprec *lp, int items, getcolumnex_func cb, int *maprow, int *mapcol) { int i, j, nz = 0, m = 0, n = 0, *nzrows = NULL; - LPSREAL *nzvalues = NULL, *arraymax = NULL; + REAL *nzvalues = NULL, *arraymax = NULL; LUSOLrec *LUSOL; /* Are we capable of finding redundancy with this BFP? */ diff --git a/src/lpsolve/headers/run_headers/lp_LUSOL.h b/src/lpSolve/src/lp_LUSOL.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_LUSOL.h rename to src/lpSolve/src/lp_LUSOL.h diff --git a/src/lpsolve/build/lp_solve/lp_MDO.c b/src/lpSolve/src/lp_MDO.c similarity index 99% rename from src/lpsolve/build/lp_solve/lp_MDO.c rename to src/lpSolve/src/lp_MDO.c index 6f7807c5..12172298 100644 --- a/src/lpsolve/build/lp_solve/lp_MDO.c +++ b/src/lpSolve/src/lp_MDO.c @@ -62,8 +62,8 @@ STATIC int prepareMDO(lprec *lp, MYBOOL *usedpos, int *colorder, int *data, int int offset = 0, Bnz = 0, Tnz; MYBOOL dotally = (MYBOOL) (rowmap == NULL); MATrec *mat = lp->matA; - LPSREAL hold; - LPSREAL *value; + REAL hold; + REAL *value; int *rownr; if(dotally) diff --git a/src/lpsolve/headers/include/lp_MDO.h b/src/lpSolve/src/lp_MDO.h similarity index 100% rename from src/lpsolve/headers/include/lp_MDO.h rename to src/lpSolve/src/lp_MDO.h diff --git a/src/lpsolve/build/lp_solve/lp_MPS.c b/src/lpSolve/src/lp_MPS.c similarity index 77% rename from src/lpsolve/build/lp_solve/lp_MPS.c rename to src/lpSolve/src/lp_MPS.c index 810f73f8..6d2f60b4 100644 --- a/src/lpsolve/build/lp_solve/lp_MPS.c +++ b/src/lpSolve/src/lp_MPS.c @@ -109,7 +109,7 @@ STATIC void namecpy(char *into, char *from) present */ /* scan_line for fixed MPS format */ -STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char *field2, char *field3, +STATIC int scan_lineFIXED(int section, char* line, char *field1, char *field2, char *field3, double *field4, char *field5, double *field6) { int items = 0, line_len; @@ -131,10 +131,6 @@ STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char line += 4; if(line_len >= 5) { /* name */ - if (line[-1] != ' ') { - report(lp, IMPORTANT, "MPS_readfile: invalid data card; column 4 must be blank\n"); - return(-1); - } namecpy(field2, line); items++; } @@ -144,10 +140,6 @@ STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char line += 10; if(line_len >= 14) { /* name */ - if (line[-1] != ' ' || line[-2] != ' ') { - report(lp, IMPORTANT, "MPS_readfile: invalid data card; columns 13-14 must be blank\n"); - return(-1); - } namecpy(field3, line); items++; } @@ -157,10 +149,6 @@ STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char line += 10; if(line_len >= 25) { /* number */ - if (line[-1] != ' ' || line[-2] != ' ') { - report(lp, IMPORTANT, "MPS_readfile: invalid data card; columns 23-24 must be blank\n"); - return(-1); - } strncpy(buf, line, 15); buf[15] = '\0'; for(ptr1 = ptr2 = buf; ; ptr1++) @@ -169,10 +157,8 @@ STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char break; /* *field4 = atof(buf); */ *field4 = strtod(buf, &ptr1); - if(*ptr1) { - report(lp, IMPORTANT, "MPS_readfile: invalid number in columns 25-36 \n"); + if(*ptr1) return(-1); - } items++; } else @@ -181,10 +167,6 @@ STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char line += 15; if(line_len >= 40) { /* name */ - if (line[-1] != ' ' || line[-2] != ' ' || line[-3] != ' ') { - report(lp, IMPORTANT, "MPS_readfile: invalid data card; columns 37-39 must be blank\n"); - return(-1); - } namecpy(field5, line); items++; } @@ -193,10 +175,6 @@ STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char line += 10; if(line_len >= 50) { /* number */ - if (line[-1] != ' ' || line[-2] != ' ') { - report(lp, IMPORTANT, "MPS_readfile: invalid data card; columns 48-49 must be blank\n"); - return(-1); - } strncpy(buf, line, 15); buf[15] = '\0'; for(ptr1 = ptr2 = buf; ; ptr1++) @@ -205,10 +183,8 @@ STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char break; /* *field6 = atof(buf); */ *field6 = strtod(buf, &ptr1); - if(*ptr1) { - report(lp, IMPORTANT, "MPS_readfile: invalid number in columns 50-61 \n"); + if(*ptr1) return(-1); - } items++; } else @@ -244,11 +220,11 @@ STATIC int lenfield(char *line, int line_len) } /* scan_line for fixed MPS format */ -STATIC int scan_lineFREE(lprec *lp, int section, char* line, char *field1, char *field2, char *field3, +STATIC int scan_lineFREE(int section, char* line, char *field1, char *field2, char *field3, double *field4, char *field5, double *field6) { int items = 0, line_len, len; - char buf[256], *ptr1 = NULL, *ptr2; + char buf[256], *ptr1, *ptr2; line_len = (int) strlen(line); while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' '))) @@ -268,10 +244,6 @@ STATIC int scan_lineFREE(lprec *lp, int section, char* line, char *field1, char strncpy(buf, line, len); buf[len] = '\0'; sscanf(buf, "%s", field1); - if(section == MPSBOUNDS) { - for(ptr1 = field1; *ptr1; ptr1++) - *ptr1=(char)toupper(*ptr1); - } items++; } else @@ -323,58 +295,23 @@ STATIC int scan_lineFREE(lprec *lp, int section, char* line, char *field1, char items++; ptr1 = field3; } - else if((section == MPSBOUNDS) && - ((strcmp(field1, "FR") == 0) || (strcmp(field1, "MI") == 0) || (strcmp(field1, "PL") == 0) || (strcmp(field1, "BV") == 0))) - /* field3 *is* the variable name */; else { - /* Some free MPS formats allow that field 2 is not provided after the first time. - The fieldname is then the same as the the defined field the line before. - In that case field2 shifts to field3, field1 shifts to field 2. - This situation is tested by checking if field3 is numerical AND there are an even number of fields after. - */ - char *line1 = line; - int line_len1 = line_len; - int items1 = 0; - - while (line_len1 > 0) { - len = lenfield(line1, line_len1); - if (len > 0) { - line1 += len; - line_len1 -= len; - items1++; - } - len = spaces(line1, line_len1); - line1 += len; - line_len1 -= len; - } - if ((items1 % 2) == 0) { - *field4 = strtod(field3, &ptr1); - if(*ptr1 == 0) { - strcpy(field3, field2); - if ((section == MPSROWS) || (section == MPSBOUNDS) /* || (section == MPSSOS) */) - *field2 = 0; - else { - strcpy(field2, field1); - *field1 = 0; - } - items++; - } - else - ptr1 = NULL; + *field4 = strtod(field3, &ptr1); + if(*ptr1 == 0) { + strcpy(field3, field2); + if ((section == MPSROWS) || (section == MPSBOUNDS) /* || (section == MPSSOS) */) + *field2 = 0; + else { + strcpy(field2, field1); + *field1 = 0; + } + items++; } else ptr1 = NULL; } } - else { - ptr1 = NULL; - if((section == MPSBOUNDS) && - ((strcmp(field1, "FR") == 0) || (strcmp(field1, "MI") == 0) || (strcmp(field1, "PL") == 0) || (strcmp(field1, "BV") == 0))) { - strcpy(field3, field2); - *field2 = 0; - items++; - } - } + else ptr1 = NULL; if(ptr1 == NULL) { len = lenfield(line, line_len); @@ -440,7 +377,7 @@ STATIC int scan_lineFREE(lprec *lp, int section, char* line, char *field1, char *field1 = 0; } - if((section != MPSOBJNAME) && (section != MPSBOUNDS)) { + if(section != MPSOBJNAME) { for(ptr1 = field1; *ptr1; ptr1++) *ptr1=(char)toupper(*ptr1); } @@ -448,8 +385,8 @@ STATIC int scan_lineFREE(lprec *lp, int section, char* line, char *field1, char return(items); } -STATIC int addmpscolumn(lprec *lp, MYBOOL Int_section, int typeMPS, MYBOOL *Column_ready, - int *count, LPSREAL *Last_column, int *Last_columnno, char *Last_col_name) +STATIC int addmpscolumn(lprec *lp, MYBOOL Int_section, MYBOOL *Column_ready, + int *count, REAL *Last_column, int *Last_columnno, char *Last_col_name) { int ok = TRUE; @@ -458,19 +395,15 @@ STATIC int addmpscolumn(lprec *lp, MYBOOL Int_section, int typeMPS, MYBOOL *Colu if (ok) { ok = set_col_name(lp, lp->columns, Last_col_name); } - if (ok) { + if (ok) set_int(lp, lp->columns, Int_section); - if ((Int_section) && (typeMPS & MPSIBM)) - set_bounds(lp, lp->columns, 10.0 / DEF_INFINITE, DEF_INFINITE / 10.0); - } } *Column_ready = FALSE; *count = 0; return(ok); } -#if 0 -STATIC MYBOOL appendmpsitem(int *count, int rowIndex[], LPSREAL rowValue[]) +STATIC MYBOOL appendmpsitem(int *count, int rowIndex[], REAL rowValue[]) { int i = *count; @@ -485,39 +418,6 @@ STATIC MYBOOL appendmpsitem(int *count, int rowIndex[], LPSREAL rowValue[]) (*count)++; return( TRUE ); } -#endif - -STATIC MYBOOL appendmpsitem(int *count, int rowIndex[], LPSREAL rowValue[]) -{ - int i = *count; - - /* Check for non-negativity of the index */ - if(rowIndex[i] < 0) - return( FALSE ); - - /* Move the element so that the index list is sorted ascending */ - while((i > 0) && (rowIndex[i] < rowIndex[i-1])) { - swapINT (rowIndex+i, rowIndex+i-1); - swapREAL(rowValue+i, rowValue+i-1); - i--; - } - - /* Add same-indexed items (which is rarely encountered), and shorten the list */ - if((i < *count) && (rowIndex[i] == rowIndex[i+1])) { - int ii = i + 1; - rowValue[i] += rowValue[ii]; - (*count)--; - while(ii < *count) { - rowIndex[ii] = rowIndex[ii+1]; - rowValue[ii] = rowValue[ii+1]; - ii++; - } - } - - /* Update the count and return */ - (*count)++; - return( TRUE ); -} MYBOOL MPS_readfile(lprec **newlp, char *filename, int typeMPS, int verbose) { @@ -551,11 +451,11 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func MYBOOL Int_section, Column_ready, Column_ready1, Unconstrained_rows_found = FALSE, OF_found = FALSE, CompleteStatus = FALSE; double field4, field6; - LPSREAL *Last_column = NULL; + REAL *Last_column = NULL; int count = 0, *Last_columnno = NULL; int OBJSENSE = ROWTYPE_EMPTY; lprec *lp; - int (*scan_line)(lprec *lp, int section, char* line, char *field1, char *field2, char *field3, + int (*scan_line)(int section, char* line, char *field1, char *field2, char *field3, double *field4, char *field5, double *field6); if(newlp == NULL) @@ -565,15 +465,17 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func else lp = *newlp; - if((typeMPS & MPSFIXED) == MPSFIXED) - scan_line = scan_lineFIXED; - else if((typeMPS & MPSFREE) == MPSFREE) - scan_line = scan_lineFREE; - else { - report(lp, IMPORTANT, "MPS_readfile: Unrecognized MPS line type.\n"); - if (*newlp == NULL) + switch(typeMPS) { + case MPSFIXED: + scan_line = scan_lineFIXED; + break; + case MPSFREE: + scan_line = scan_lineFREE; + break; + default: + report(lp, IMPORTANT, "MPS_readfile: Unrecognized MPS line type.\n"); delete_lp(lp); - return( CompleteStatus ); + return( CompleteStatus ); } if (lp != NULL) { @@ -612,11 +514,11 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func if (!set_lp_name(lp, probname)) break; } - else if(((typeMPS & MPSFREE) == MPSFREE) && (strcmp(tmp, "OBJSENSE") == 0)) { + else if((typeMPS == MPSFREE) && (strcmp(tmp, "OBJSENSE") == 0)) { section = MPSOBJSENSE; report(lp, FULL, "Switching to OBJSENSE section\n"); } - else if(((typeMPS & MPSFREE) == MPSFREE) && (strcmp(tmp, "OBJNAME") == 0)) { + else if((typeMPS == MPSFREE) && (strcmp(tmp, "OBJNAME") == 0)) { section = MPSOBJNAME; report(lp, FULL, "Switching to OBJNAME section\n"); } @@ -634,7 +536,7 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func report(lp, FULL, "Switching to COLUMNS section\n"); } else if(strcmp(tmp, "RHS") == 0) { - if (!addmpscolumn(lp, Int_section, typeMPS, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) + if (!addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) break; section = MPSRHS; report(lp, FULL, "Switching to RHS section\n"); @@ -666,7 +568,7 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func } } else { /* normal line, process */ - items = scan_line(lp, section, line, field1, field2, field3, &field4, field5, &field6); + items = scan_line(section, line, field1, field2, field3, &field4, field5, &field6); if(items < 0){ report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line); break; @@ -764,7 +666,7 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func } if(Column_ready) { /* Added ability to handle non-standard "same as above" column name */ - if (addmpscolumn(lp, Int_section, typeMPS, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) { + if (addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) { strcpy(Last_col_name, field2); NZ = 0; } @@ -794,7 +696,7 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func if(row > lp->rows) report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field3); Last_columnno[count] = row; - Last_column[count] = (LPSREAL)field4; + Last_column[count] = (REAL)field4; if(appendmpsitem(&count, Last_columnno, Last_column)) { NZ++; Column_ready = TRUE; @@ -806,7 +708,7 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func if(row > lp->rows) report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field5); Last_columnno[count] = row; - Last_column[count] = (LPSREAL)field6; + Last_column[count] = (REAL)field6; if(appendmpsitem(&count, Last_columnno, Last_column)) { NZ++; Column_ready = TRUE; @@ -838,16 +740,12 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func } if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { - if ((row == 0) && ((typeMPS & MPSNEGOBJCONST) == MPSNEGOBJCONST)) - field4 = -field4; - set_rh(lp, row, (LPSREAL)field4); + set_rh(lp, row, (REAL)field4); } if(items == 6) { if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { - if ((row == 0) && ((typeMPS & MPSNEGOBJCONST) == MPSNEGOBJCONST)) - field6 = -field6; - set_rh(lp, row, (LPSREAL)field6); + set_rh(lp, row, (REAL)field6); } } @@ -864,7 +762,7 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func var = find_var(lp, field3, FALSE); if(var < 0){ /* bound on undefined var in COLUMNS section ... */ Column_ready = TRUE; - if (!addmpscolumn(lp, FALSE, typeMPS, &Column_ready, &count, Last_column, Last_columnno, field3)) + if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field3)) break; Column_ready = TRUE; var = find_var(lp, field3, TRUE); @@ -872,16 +770,14 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func if(var < 0) /* undefined var and could add ... */; else if(strcmp(field1, "UP") == 0) { /* upper bound */ - /* if(!set_bounds(lp, var, get_lowbo(lp, var), field4)) */ - if(!set_upbo(lp, var, field4)) + if(!set_bounds(lp, var, get_lowbo(lp, var), field4)) break; } else if(strcmp(field1, "SC") == 0) { /* upper bound */ if(field4 == 0) field4 = lp->infinite; - /* if(!set_bounds(lp, var, get_lowbo(lp, var), field4)) */ - if(!set_upbo(lp, var, field4)) + if(!set_bounds(lp, var, get_lowbo(lp, var), field4)) break; set_semicont(lp, var, TRUE); } @@ -889,26 +785,22 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func /* upper bound */ if(field4 == 0) field4 = lp->infinite; - /* if(!set_bounds(lp, var, get_lowbo(lp, var), field4)) */ - if(!set_upbo(lp, var, field4)) + if(!set_bounds(lp, var, get_lowbo(lp, var), field4)) break; set_int(lp, var, TRUE); set_semicont(lp, var, TRUE); } else if(strcmp(field1, "LO") == 0) { /* lower bound */ - /* if(!set_bounds(lp, var, field4, get_upbo(lp, var))) */ - if(!set_lowbo(lp, var, field4)) + if(!set_bounds(lp, var, field4, get_upbo(lp, var))) break; } - else if(strcmp(field1, "PL") == 0) { /* plus-ranged variable */ - /* if(!set_bounds(lp, var, get_lowbo(lp, var), lp->infinite)) */ - if(!set_upbo(lp, var, lp->infinite)) + else if(strcmp(field1, "PL") == 0) { /* plus-ranged variable */ + if(!set_bounds(lp, var, get_lowbo(lp, var), lp->infinite)) break; - } + } else if(strcmp(field1, "MI") == 0) { /* minus-ranged variable */ - /* if(!set_bounds(lp, var, -lp->infinite, get_upbo(lp, var))) */ - if(!set_lowbo(lp, var, -lp->infinite)) + if(!set_bounds(lp, var, -lp->infinite, get_upbo(lp, var))) break; } else if(strcmp(field1, "FR") == 0) { /* free variable */ @@ -924,14 +816,12 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func } /* AMPL bounds type UI and LI added by E.Imamura (CRIEPI) */ else if(strcmp(field1, "UI") == 0) { /* upper bound for integer variable */ - /* if(!set_bounds(lp, var, get_lowbo(lp, var), field4)) */ - if(!set_upbo(lp, var, field4)) + if(!set_bounds(lp, var, get_lowbo(lp, var), field4)) break; set_int(lp, var, TRUE); } else if(strcmp(field1, "LI") == 0) { /* lower bound for integer variable - corrected by KE */ - /* if(!set_bounds(lp, var, field4, get_upbo(lp, var))) */ - if(!set_lowbo(lp, var, field4)) + if(!set_bounds(lp, var, field4, get_upbo(lp, var))) break; set_int(lp, var, TRUE); } @@ -1093,7 +983,7 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func /* lp_solve needs a name for the SOS */ if(variant == 0) { if(strlen(field3) == 0) /* CPLEX format does not provide a SOS name; create one */ - sprintf(field3, "SOS_%d", SOS_count(lp) + 1); + snprintf(field3, sizeof(field3), "SOS_%d", SOS_count(lp) + 1); } else { /* Remap XPRESS format name */ strcpy(field3, field1); @@ -1115,7 +1005,7 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func var = find_var(lp, field, FALSE); /* Native lp_solve and XPRESS formats */ if(var < 0){ /* SOS on undefined var in COLUMNS section ... */ Column_ready = TRUE; - if (!addmpscolumn(lp, FALSE, typeMPS, &Column_ready, &count, Last_column, Last_columnno, field)) + if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field)) break; Column_ready = TRUE; var = find_var(lp, field, TRUE); @@ -1140,29 +1030,10 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func CompleteStatus = FALSE; } - if(CompleteStatus == FALSE) { - if (*newlp == NULL) - delete_lp(lp); - } - else { - if (typeMPS & MPSIBM) { - LPSREAL lower, upper; - - for (var = 1; var <= lp->columns; var++) - if (is_int(lp, var)) { - lower = get_lowbo(lp, var); - upper = get_upbo(lp, var); - if ((lower == 10.0 / DEF_INFINITE) && (upper == DEF_INFINITE / 10.0)) - upper = 1.0; - if (lower == 10.0 / DEF_INFINITE) - lower = 0.0; - if (upper == DEF_INFINITE / 10.0) - upper = lp->infinite; - set_bounds(lp, var, lower, upper); - } - } + if(CompleteStatus == FALSE) + delete_lp(lp); + else *newlp = lp; - } if(Last_column != NULL) FREE(Last_column); if(Last_columnno != NULL) @@ -1172,12 +1043,12 @@ MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func return( CompleteStatus ); } -static void number(char *str,LPSREAL value) +static void number(char *str,REAL value) { char __str[80], *_str; int i; - /* sprintf(_str,"%12.6G",value); */ + /* snprintf(_str,sizeof(__str),"%12.6G",value); */ _str=__str+2; if (value>=0.0) if ((value!=0.0) && ((value>0.99999999e12) || (value<0.0001))) { @@ -1185,7 +1056,7 @@ static void number(char *str,LPSREAL value) do { n--; - i=sprintf(_str,"%*.*E",n,n-6,(double) value); + i=snprintf(_str,sizeof(__str)-2,"%*.*E",n,n-6,(double) value); if (i>12) { char *ptr=strchr(_str,'E'); @@ -1203,11 +1074,11 @@ static void number(char *str,LPSREAL value) int n=13; do { - i=sprintf(_str,"%*.0f",--n,(double) value); + i=snprintf(_str,sizeof(__str)-2,"%*.0f",--n,(double) value); } while (i>12); } else { - if (((i=sprintf(_str,"%12.10f",(double) value))>12) && (_str[12]>='5')) { + if (((i=snprintf(_str,sizeof(__str)-2,"%12.10f",(double) value))>12) && (_str[12]>='5')) { for (i=11;i>=0;i--) if (_str[i]!='.') { if (++_str[i]>'9') _str[i]='0'; @@ -1225,7 +1096,7 @@ static void number(char *str,LPSREAL value) do { n--; - i=sprintf(_str,"%*.*E",n,n-7,(double) value); + i=snprintf(_str,sizeof(__str)-2,"%*.*E",n,n-7,(double) value); if (i>12) { char *ptr=strchr(_str,'E'); @@ -1243,11 +1114,11 @@ static void number(char *str,LPSREAL value) int n=13; do { - i=sprintf(_str,"%*.0f",--n,(double) value); + i=snprintf(_str,sizeof(__str)-2,"%*.0f",--n,(double) value); } while (i>12); } else - if (((i=sprintf(_str,"%12.9f",(double) value))>12) && (_str[12]>='5')) { + if (((i=snprintf(_str,sizeof(__str)-2,"%12.9f",(double) value))>12) && (_str[12]>='5')) { for (i=11;i>=1;i--) if (_str[i]!='.') { if (++_str[i]>'9') _str[i]='0'; @@ -1259,30 +1130,34 @@ static void number(char *str,LPSREAL value) *(--_str)=' '; } } - _str[12] = '\0'; strcpy(str, _str); + _str[12] ='\0'; strcpy(str, _str); // strncpy(str,_str,12); str[12] = '\0'; } -static char *formatnumber12(char *numberbuffer, double a) +static char numberbuffer[15]; + +static char *formatnumber12(double a) { #if 0 - return(sprintf(numberbuffer, "%12g", a)); + return(snprintf(numberbuffer, sizeof(numberbuffer), "%12g", a)); #else number(numberbuffer, a); return(numberbuffer); #endif } -STATIC char *MPSnameFIXED(char *name0, char *name) +STATIC char *MPSnameFIXED(char *name) { - sprintf(name0, "%-8.8s", name); + static char name0[9]; + + snprintf(name0, sizeof(name0), "%-8.8s", name); return(name0); } -STATIC char *MPSnameFREE(char *name0, char *name) +STATIC char *MPSnameFREE(char *name) { if(strlen(name) < 8) - return(MPSnameFIXED(name0, name)); + return(MPSnameFIXED(name)); else return(name); } @@ -1294,34 +1169,40 @@ static void write_data(void *userhandle, write_modeldata_func write_modeldata, c va_start(ap, format); vsnprintf(buff, DEF_STRBUFSIZE, format, ap); - va_end(ap); write_modeldata(userhandle, buff); + va_end(ap); } -MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_modeldata_func write_modeldata) +MYBOOL MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_modeldata_func write_modeldata) { int i, j, jj, je, k, marker, putheader, ChangeSignObj = FALSE, *idx, *idx1; MYBOOL ok = TRUE, names_used; - LPSREAL a, *val, *val1; - char * (*MPSname)(char *name0, char *name); - char numberbuffer[15]; - char name0[9]; - - if((typeMPS & MPSFIXED) == MPSFIXED) { - MPSname = MPSnameFIXED; - ChangeSignObj = is_maxim(lp); - } - else if((typeMPS & MPSFREE) == MPSFREE) { - MPSname = MPSnameFREE; - } - else { - report(lp, IMPORTANT, "MPS_writefile: unrecognized MPS name type.\n"); + REAL a, *val, *val1; +/* "Output" not defined. Excluded April 19 2006 SEB. */ +/* FILE *output = stdout; */ + char * (*MPSname)(char *name); + + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "MPS_writefile: Cannot write to MPS file while in row entry mode.\n"); return(FALSE); } + switch(typeMPS) { + case MPSFIXED: + MPSname = MPSnameFIXED; + ChangeSignObj = is_maxim(lp); + break; + case MPSFREE: + MPSname = MPSnameFREE; + break; + default: + report(lp, IMPORTANT, "MPS_writefile: unrecognized MPS name type.\n"); + return(FALSE); + } + names_used = lp->names_used; - if((typeMPS & MPSFIXED) == MPSFIXED) { + if(typeMPS == MPSFIXED) { /* Check if there is no variable name where the first 8 charachters are equal to the first 8 characters of anothe variable */ if(names_used) for(i = 1; (i <= lp->columns) && (ok); i++) @@ -1336,9 +1217,6 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ lp->names_used = FALSE; ok = TRUE; } - - memset(numberbuffer, 0, sizeof(numberbuffer)); - marker = 0; /* First write metadata in structured comment form (lp_solve style) */ @@ -1356,8 +1234,8 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ write_data(userhandle, write_modeldata, "*\n"); /* Write the MPS content */ - write_data(userhandle, write_modeldata, "NAME %s\n", MPSname(name0, get_lp_name(lp))); - if(((typeMPS & MPSFREE) == MPSFREE) && (is_maxim(lp))) + write_data(userhandle, write_modeldata, "NAME %s\n", MPSname(get_lp_name(lp))); + if((typeMPS == MPSFREE) && (is_maxim(lp))) write_data(userhandle, write_modeldata, "OBJSENSE\n MAX\n"); write_data(userhandle, write_modeldata, "ROWS\n"); for(i = 0; i <= lp->rows; i++) { @@ -1371,7 +1249,7 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ } else write_data(userhandle, write_modeldata, " E "); - write_data(userhandle, write_modeldata, "%s\n", MPSname(name0, get_row_name(lp, i))); + write_data(userhandle, write_modeldata, "%s\n", MPSname(get_row_name(lp, i))); } allocREAL(lp, &val, 1 + lp->rows, TRUE); @@ -1398,17 +1276,17 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ a = *(val1++); if (k == 0) { write_data(userhandle, write_modeldata, " %s", - MPSname(name0, get_col_name(lp, i))); + MPSname(get_col_name(lp, i))); write_data(userhandle, write_modeldata, " %s %s", - MPSname(name0, get_row_name(lp, j)), -/* formatnumber12(numberbuffer, (double) a)); */ - formatnumber12(numberbuffer, (double) (a * (j == 0 && ChangeSignObj ? -1 : 1)))); - } + MPSname(get_row_name(lp, j)), +/* formatnumber12((double) a)); */ + formatnumber12((double) (a * (j == 0 && ChangeSignObj ? -1 : 1)))); + } else write_data(userhandle, write_modeldata, " %s %s\n", - MPSname(name0, get_row_name(lp, j)), - formatnumber12(numberbuffer, (double) (a * (j == 0 && ChangeSignObj ? -1 : 1)))); -/* formatnumber12(numberbuffer, (double) a)); */ + MPSname(get_row_name(lp, j)), + formatnumber12((double) (a * (j == 0 && ChangeSignObj ? -1 : 1)))); +/* formatnumber12((double) a)); */ } if(k == 0) write_data(userhandle, write_modeldata, "\n"); @@ -1427,19 +1305,17 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ a = lp->orig_rhs[i]; if(a) { a = unscaled_value(lp, a, i); - if ((i == 0) && ((typeMPS & MPSNEGOBJCONST) == MPSNEGOBJCONST)) - a = -a; if((i == 0) || is_chsign(lp, i)) a = my_flipsign(a); k = 1 - k; if(k == 0) write_data(userhandle, write_modeldata, " RHS %s %s", - MPSname(name0, get_row_name(lp, i)), - formatnumber12(numberbuffer, (double)a)); + MPSname(get_row_name(lp, i)), + formatnumber12((double)a)); else write_data(userhandle, write_modeldata, " %s %s\n", - MPSname(name0, get_row_name(lp, i)), - formatnumber12(numberbuffer, (double)a)); + MPSname(get_row_name(lp, i)), + formatnumber12((double)a)); } } if(k == 0) @@ -1459,12 +1335,12 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ k = 1 - k; if(k == 0) write_data(userhandle, write_modeldata, " RGS %s %s", - MPSname(name0, get_row_name(lp, i)), - formatnumber12(numberbuffer, (double)a)); + MPSname(get_row_name(lp, i)), + formatnumber12((double)a)); else write_data(userhandle, write_modeldata, " %s %s\n", - MPSname(name0, get_row_name(lp, i)), - formatnumber12(numberbuffer, (double)a)); + MPSname(get_row_name(lp, i)), + formatnumber12((double)a)); } } if(k == 0) @@ -1483,8 +1359,8 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ putheader = FALSE; } write_data(userhandle, write_modeldata, " FX BND %s %s\n", - MPSname(name0, get_col_name(lp, j)), - formatnumber12(numberbuffer, (double)a)); + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); } else if(is_binary(lp, j)) { if(putheader) { @@ -1492,7 +1368,7 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ putheader = FALSE; } write_data(userhandle, write_modeldata, " BV BND %s\n", - MPSname(name0, get_col_name(lp, j))); + MPSname(get_col_name(lp, j))); } else if(is_unbounded(lp, j)) { if(putheader) { @@ -1500,29 +1376,12 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ putheader = FALSE; } write_data(userhandle, write_modeldata, " FR BND %s\n", - MPSname(name0, get_col_name(lp, j))); + MPSname(get_col_name(lp, j))); } else { - if((lp->orig_lowbo[i] != 0) || (is_int(lp, j))) { /* Some solvers like CPLEX need to have a bound on a variable if it is integer, but not binary else it is interpreted as binary which is not ment */ - a = lp->orig_lowbo[i]; - a = unscaled_value(lp, a, i); - if(putheader) { - write_data(userhandle, write_modeldata, "BOUNDS\n"); - putheader = FALSE; - } - if(lp->orig_lowbo[i] != -lp->infinite) - write_data(userhandle, write_modeldata, " LO BND %s %s\n", - MPSname(name0, get_col_name(lp, j)), - formatnumber12(numberbuffer, (double)a)); - else - write_data(userhandle, write_modeldata, " MI BND %s\n", - MPSname(name0, get_col_name(lp, j))); - } - - if((lp->orig_upbo[i] < lp->infinite) || (is_semicont(lp, j))) { + if(lp->orig_upbo[i] < lp->infinite) { a = lp->orig_upbo[i]; - if(a < lp->infinite) - a = unscaled_value(lp, a, i); + a = unscaled_value(lp, a, i); if(putheader) { write_data(userhandle, write_modeldata, "BOUNDS\n"); putheader = FALSE; @@ -1530,17 +1389,32 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ if(is_semicont(lp, j)) { if(is_int(lp, j)) write_data(userhandle, write_modeldata, " SI BND %s %s\n", - MPSname(name0, get_col_name(lp, j)), - (a < lp->infinite) ? formatnumber12(numberbuffer, (double)a) : " "); + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); else write_data(userhandle, write_modeldata, " SC BND %s %s\n", - MPSname(name0, get_col_name(lp, j)), - (a < lp->infinite) ? formatnumber12(numberbuffer, (double)a) : " "); + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); } else write_data(userhandle, write_modeldata, " UP BND %s %s\n", - MPSname(name0, get_col_name(lp, j)), - formatnumber12(numberbuffer, (double)a)); + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); + } + if(lp->orig_lowbo[i] != 0) { + a = lp->orig_lowbo[i]; + a = unscaled_value(lp, a, i); + if(putheader) { + write_data(userhandle, write_modeldata, "BOUNDS\n"); + putheader = FALSE; + } + if(lp->orig_lowbo[i] != -lp->infinite) + write_data(userhandle, write_modeldata, " LO BND %s %s\n", + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); + else + write_data(userhandle, write_modeldata, " MI BND %s\n", + MPSname(get_col_name(lp, j))); } } } @@ -1556,12 +1430,12 @@ MYBOOL __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_ } write_data(userhandle, write_modeldata, " S%1d SOS %s %s\n", SOS->sos_list[i]->type, - MPSname(name0, SOS->sos_list[i]->name), - formatnumber12(numberbuffer, (double) SOS->sos_list[i]->priority)); + MPSname(SOS->sos_list[i]->name), + formatnumber12((double) SOS->sos_list[i]->priority)); for(j = 1; j <= SOS->sos_list[i]->size; j++) { write_data(userhandle, write_modeldata, " SOS %s %s\n", - MPSname(name0, get_col_name(lp, SOS->sos_list[i]->members[j])), - formatnumber12(numberbuffer, (double) SOS->sos_list[i]->weights[j])); + MPSname(get_col_name(lp, SOS->sos_list[i]->members[j])), + formatnumber12((double) SOS->sos_list[i]->weights[j])); } } @@ -1580,21 +1454,16 @@ static int __WINAPI write_lpdata(void *userhandle, char *buf) MYBOOL MPS_writefile(lprec *lp, int typeMPS, char *filename) { - FILE *output = stdout; + FILE *output; /* = stdout; */ MYBOOL ok; - if (filename != NULL) { - ok = ((output = fopen(filename, "w")) != NULL); - if(!ok) - return(ok); - } - else - output = lp->outstream; + ok = ((output = fopen(filename, "w")) != NULL); + if(!ok) + return(ok); ok = MPS_writefileex(lp, typeMPS, (void *) output, write_lpdata); - if (filename != NULL) - fclose(output); + fclose(output); return(ok); } @@ -1603,8 +1472,7 @@ MYBOOL MPS_writehandle(lprec *lp, int typeMPS, FILE *output) { MYBOOL ok; - if (output != NULL) - set_outputstream(lp, output); + set_outputstream(lp, output); output = lp->outstream; @@ -1631,7 +1499,7 @@ static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL isrow) #else static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL tryrowfirst) { - int in = -1; + int in; /* Have we defined our own variable names? */ if(lp->names_used) { @@ -1647,8 +1515,7 @@ static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL tryrowfirst) } } /* If not, see if we can match the standard name mask */ - - if(in == -1) { + else { if(strncmp(varname, (tryrowfirst ? ROWNAMEMASK : COLNAMEMASK), 1) == 0) { /* Fail if we did not successfully scan as a valid integer */ if((sscanf(varname + 1, "%d", &in) != 1) || @@ -1674,16 +1541,19 @@ MYBOOL MPS_readBAS(lprec *lp, int typeMPS, char *filename, char *info) int ib, in, items, Lineno = 0; MYBOOL ok; FILE *input = stdin; - int (*scan_line)(lprec *lp, int section, char* line, char *field1, char *field2, char *field3, + int (*scan_line)(int section, char* line, char *field1, char *field2, char *field3, double *field4, char *field5, double *field6); - if((typeMPS & MPSFIXED) == MPSFIXED) - scan_line = scan_lineFIXED; - else if((typeMPS & MPSFREE) == MPSFREE) - scan_line = scan_lineFREE; - else { - report(lp, IMPORTANT, "MPS_readBAS: unrecognized MPS line type.\n"); - return(FALSE); + switch(typeMPS) { + case MPSFIXED: + scan_line = scan_lineFIXED; + break; + case MPSFREE: + scan_line = scan_lineFREE; + break; + default: + report(lp, IMPORTANT, "MPS_readBAS: unrecognized MPS line type.\n"); + return(FALSE); } ok = (MYBOOL) ((filename != NULL) && ((input = fopen(filename,"r")) != NULL)); @@ -1733,7 +1603,7 @@ MYBOOL MPS_readBAS(lprec *lp, int typeMPS, char *filename, char *info) } } else { /* normal line, process */ - items = scan_line(lp, /* MPSRHS */ MPSBOUNDS, line, field1, field2, field3, &field4, field5, &field6); + items = scan_line(MPSRHS, line, field1, field2, field3, &field4, field5, &field6); if(items < 0){ report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line); break; @@ -1791,18 +1661,20 @@ MYBOOL MPS_writeBAS(lprec *lp, int typeMPS, char *filename) int ib, in; MYBOOL ok; char name1[100], name2[100]; - FILE *output = stdout; - char * (*MPSname)(char *name0, char *name); - char name0[9]; + FILE *output; /* = stdout; */ + char * (*MPSname)(char *name); /* Set name formatter */ - if((typeMPS & MPSFIXED) == MPSFIXED) - MPSname = MPSnameFIXED; - else if((typeMPS & MPSFREE) == MPSFREE) - MPSname = MPSnameFREE; - else { - report(lp, IMPORTANT, "MPS_writeBAS: unrecognized MPS name type.\n"); - return(FALSE); + switch(typeMPS) { + case MPSFIXED: + MPSname = MPSnameFIXED; + break; + case MPSFREE: + MPSname = MPSnameFREE; + break; + default: + report(lp, IMPORTANT, "MPS_writeBAS: unrecognized MPS name type.\n"); + return(FALSE); } /* Open the file for writing */ @@ -1832,16 +1704,16 @@ MYBOOL MPS_writeBAS(lprec *lp, int typeMPS, char *filename) /* Check if we have a basic/non-basic variable pair */ if((ib <= lp->sum) && (in <= lp->sum)) { - strcpy(name1, MPSname(name0, (ib <= lp->rows ? get_row_name(lp, ib) : + strcpy(name1, MPSname((ib <= lp->rows ? get_row_name(lp, ib) : get_col_name(lp, ib-lp->rows)))); - strcpy(name2, MPSname(name0, (in <= lp->rows ? get_row_name(lp, in) : + strcpy(name2, MPSname((in <= lp->rows ? get_row_name(lp, in) : get_col_name(lp, in-lp->rows)))); fprintf(output, " %2s %s %s\n", (lp->is_lower[in] ? "XL" : "XU"), name1, name2); } /* Otherwise just write the bound state of the non-basic variable */ else if(in <= lp->sum) { - strcpy(name1, MPSname(name0, (in <= lp->rows ? get_row_name(lp, in) : + strcpy(name1, MPSname((in <= lp->rows ? get_row_name(lp, in) : get_col_name(lp, in-lp->rows)))); fprintf(output, " %2s %s\n", (lp->is_lower[in] ? "LL" : "UL"), name1); } diff --git a/src/lpsolve/headers/run_headers/lp_MPS.h b/src/lpSolve/src/lp_MPS.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_MPS.h rename to src/lpSolve/src/lp_MPS.h diff --git a/src/lpsolve/build/lp_solve/lp_SOS.c b/src/lpSolve/src/lp_SOS.c similarity index 95% rename from src/lpsolve/build/lp_solve/lp_SOS.c rename to src/lpSolve/src/lp_SOS.c index 512ab548..d1f9b00a 100644 --- a/src/lpsolve/build/lp_solve/lp_SOS.c +++ b/src/lpSolve/src/lp_SOS.c @@ -136,7 +136,7 @@ STATIC void free_SOSgroup(SOSgroup **group) } /* SOS record functions */ -STATIC SOSrec *create_SOSrec(SOSgroup *group, char *name, int type, int priority, int size, int *variables, LPSREAL *weights) +STATIC SOSrec *create_SOSrec(SOSgroup *group, char *name, int type, int priority, int size, int *variables, REAL *weights) { SOSrec *SOS; @@ -167,7 +167,7 @@ STATIC SOSrec *create_SOSrec(SOSgroup *group, char *name, int type, int priority } -STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, LPSREAL *weights) +STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, REAL *weights) { int i, oldsize, newsize, nn; lprec *lp = SOS->parent->lp; @@ -234,7 +234,7 @@ STATIC int make_SOSchain(lprec *lp, MYBOOL forceresort) { int i, j, k, n; MYBOOL *hold = NULL; - LPSREAL *order, sum, weight; + REAL *order, sum, weight; SOSgroup *group = lp->SOS; /* PART A: Resort individual SOS member lists, if specified */ @@ -446,7 +446,7 @@ STATIC MYBOOL SOS_shift_col(SOSgroup *group, int sosindex, int column, int delta int i, ii, n, nn, nr; int changed; int *list; - LPSREAL *weights; + REAL *weights; #ifdef Paranoia lprec *lp = group->lp; @@ -965,7 +965,7 @@ MYBOOL SOS_is_full(SOSgroup *group, int sosindex, int column, MYBOOL activeonly) MYBOOL SOS_can_activate(SOSgroup *group, int sosindex, int column) { - int i, n, nn, nz, *list; + int i, n, nn, *list; lprec *lp; if(group == NULL) @@ -996,44 +996,16 @@ MYBOOL SOS_can_activate(SOSgroup *group, int sosindex, int column) n = list[0]+1; nn = list[n]; -#if 0 - /* Accept if the SOS is empty */ + /* Accept if the SOS is empty */ if(list[n+1] == 0) return(TRUE); -#endif - /* Cannot activate a variable if the SOS is full */ + /* Cannot activate a variable if the SOS is full */ if(list[n+nn] != 0) return(FALSE); - /* Check if there are variables quasi-active via non-zero lower bounds */ - nz = 0; - for(i = 1; i < n; i++) - if(lp->bb_bounds->lowbo[lp->rows+abs(list[i])] > 0) { - nz++; - /* Reject outright if selected column has a non-zero lower bound */ - if(list[i] == column) - return(FALSE); - } -#ifdef Paranoia - if(nz > nn) - report(lp, SEVERE, "SOS_can_activate: Found too many non-zero member variables for SOS index %d\n", sosindex); -#endif - for(i = 1; i <= nn; i++) { - if(list[n+i] == 0) - break; - if(lp->bb_bounds->lowbo[lp->rows+list[n+i]] == 0) - nz++; - } - if(nz == nn) - return(FALSE); - - /* Accept if the SOS is empty */ - if(list[n+1] == 0) - return(TRUE); - - /* Check if we can set variable active in SOS2..SOSn - (must check left and right neighbours if one variable is already active) */ + /* Check if we can set variable active in SOS2..SOSn + (must check left and right neighbours if one variable is already active) */ if(nn > 1) { /* Find the variable that was last activated; @@ -1047,27 +1019,26 @@ MYBOOL SOS_can_activate(SOSgroup *group, int sosindex, int column) i--; nn = list[n+i]; - /* SOS accepts an additional variable; confirm neighbourness of candidate; - Search for the SOS set index of the last activated variable */ + /* SOS accepts an additional variable; confirm neighbourness of candidate; + Search for the SOS set index of the last activated variable */ n = list[0]; for(i = 1; i <= n; i++) - if(abs(list[i]) == nn) - break; + if(abs(list[i]) == nn) break; if(i > n) { report(lp, CRITICAL, "SOS_can_activate: Internal index error at SOS %d\n", sosindex); return(FALSE); } - /* SOS accepts an additional variable; confirm neighbourness of candidate */ + /* SOS accepts an additional variable; confirm neighbourness of candidate */ - /* Check left neighbour */ + /* Check left neighbour */ if((i > 1) && (list[i-1] == column)) return(TRUE); - /* Check right neighbour */ + /* Check right neighbour */ if((i < n) && (list[i+1] == column)) return(TRUE); - /* It is not the right neighbour; return false */ + /* It is not the right neighbour; return false */ return(FALSE); } } @@ -1205,7 +1176,7 @@ MYBOOL SOS_unmark(SOSgroup *group, int sosindex, int column) } -int SOS_fix_unmarked(SOSgroup *group, int sosindex, int variable, LPSREAL *bound, LPSREAL value, MYBOOL isupper, +int SOS_fix_unmarked(SOSgroup *group, int sosindex, int variable, REAL *bound, REAL value, MYBOOL isupper, int *diffcount, DeltaVrec *changelog) { int i, ii, count, n, nn, nLeft, nRight, *list; @@ -1285,7 +1256,7 @@ int SOS_fix_unmarked(SOSgroup *group, int sosindex, int variable, LPSREAL *bound } int *SOS_get_candidates(SOSgroup *group, int sosindex, int column, MYBOOL excludetarget, - LPSREAL *upbound, LPSREAL *lobound) + REAL *upbound, REAL *lobound) { int i, ii, j, n, nn = 0, *list, *candidates = NULL; lprec *lp = group->lp; @@ -1355,11 +1326,11 @@ int *SOS_get_candidates(SOSgroup *group, int sosindex, int column, MYBOOL exclud } -int SOS_fix_list(SOSgroup *group, int sosindex, int variable, LPSREAL *bound, +int SOS_fix_list(SOSgroup *group, int sosindex, int variable, REAL *bound, int *varlist, MYBOOL isleft, DeltaVrec *changelog) { int i, ii, jj, count = 0; - LPSREAL value = 0; + REAL value = 0; lprec *lp = group->lp; #ifdef Paranoia @@ -1412,7 +1383,7 @@ int SOS_fix_list(SOSgroup *group, int sosindex, int variable, LPSREAL *bound, return( count ); } -int SOS_is_satisfied(SOSgroup *group, int sosindex, LPSREAL *solution) +int SOS_is_satisfied(SOSgroup *group, int sosindex, REAL *solution) /* Determine if the SOS is satisfied for the current solution vector; The return code is in the range [-2..+2], depending on the type of satisfaction. Positive return value means too many non-zero values, @@ -1526,7 +1497,7 @@ int SOS_is_satisfied(SOSgroup *group, int sosindex, LPSREAL *solution) return( status ); } -MYBOOL SOS_is_feasible(SOSgroup *group, int sosindex, LPSREAL *solution) +MYBOOL SOS_is_feasible(SOSgroup *group, int sosindex, REAL *solution) /* Determine if the SOS is feasible up to the current SOS variable */ { int i, n, nn, *list; diff --git a/src/lpsolve/headers/run_headers/lp_SOS.h b/src/lpSolve/src/lp_SOS.h similarity index 99% rename from src/lpsolve/headers/run_headers/lp_SOS.h rename to src/lpSolve/src/lp_SOS.h index d33fe7d7..27df12ce 100644 --- a/src/lpsolve/headers/run_headers/lp_SOS.h +++ b/src/lpSolve/src/lp_SOS.h @@ -96,7 +96,7 @@ MYBOOL SOS_set_marked(SOSgroup *group, int sosindex, int column, MYBOOL asactive MYBOOL SOS_unmark(SOSgroup *group, int sosindex, int column); int SOS_fix_unmarked(SOSgroup *group, int sosindex, int variable, REAL *bound, REAL value, MYBOOL isupper, int *diffcount, DeltaVrec *changelog); -int SOS_fix_list(SOSgroup *group, int sosindex, int variable, REAL *bound, +int SOS_fix_list(SOSgroup *group, int sosindex, int variable, REAL *bound, int *varlist, MYBOOL isleft, DeltaVrec *changelog); int SOS_is_satisfied(SOSgroup *group, int sosindex, REAL *solution); MYBOOL SOS_is_feasible(SOSgroup *group, int sosindex, REAL *solution); diff --git a/src/lpsolve/headers/run_headers/lp_crash.c b/src/lpSolve/src/lp_crash.c similarity index 100% rename from src/lpsolve/headers/run_headers/lp_crash.c rename to src/lpSolve/src/lp_crash.c diff --git a/src/lpsolve/headers/run_headers/lp_crash.h b/src/lpSolve/src/lp_crash.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_crash.h rename to src/lpSolve/src/lp_crash.h diff --git a/src/lpsolve/headers/run_headers/lp_explicit.h b/src/lpSolve/src/lp_explicit.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_explicit.h rename to src/lpSolve/src/lp_explicit.h diff --git a/src/lpsolve/headers/run_headers/lp_fortify.h b/src/lpSolve/src/lp_fortify.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_fortify.h rename to src/lpSolve/src/lp_fortify.h diff --git a/src/lpsolve/build/lp_solve/lp_lib.c b/src/lpSolve/src/lp_lib.c similarity index 86% rename from src/lpsolve/build/lp_solve/lp_lib.c rename to src/lpSolve/src/lp_lib.c index ade49ba2..aa577aa4 100644 --- a/src/lpsolve/build/lp_solve/lp_lib.c +++ b/src/lpSolve/src/lp_lib.c @@ -52,7 +52,6 @@ #include "lp_mipbb.h" #include "lp_report.h" #include "lp_MDO.h" -#include "lp_bit.h" #if INVERSE_ACTIVE==INVERSE_LUMOD #include "lp_LUMOD.h" @@ -86,7 +85,15 @@ # include "lp_fortify.h" #endif -#define sensrejvar TRUE + +/* ---------------------------------------------------------------------------------- */ +/* Define some globals */ +/* ---------------------------------------------------------------------------------- */ +int callcount = 0; + +/* buttrey remove */ +int buttrey_thing = 0; +FILE *buttrey_debugfile; /* Return lp_solve version information */ void __WINAPI lp_solve_version(int *majorversion, int *minorversion, int *release, int *build) @@ -101,33 +108,37 @@ void __WINAPI lp_solve_version(int *majorversion, int *minorversion, int *releas (*build) = BUILD; } -// lp_bit.h functions -MYINLINE void set_biton(MYBOOL *bitarray, int item) + +/* ---------------------------------------------------------------------------------- */ +/* Various interaction elements */ +/* ---------------------------------------------------------------------------------- */ + +#if defined INLINE +INLINE void set_biton(MYBOOL *bitarray, int item) { bitarray[item / 8] |= (1 << (item % 8)); } - -/* -MYINLINE void set_bitoff(MYBOOL *bitarray, int item) +INLINE MYBOOL is_biton(MYBOOL *bitarray, int item) { - bitarray[item / 8] &= ~(1 << (item % 8)); + return( (MYBOOL) ((bitarray[item / 8] & (1 << (item % 8))) != 0) ); } -*/ - -MYINLINE MYBOOL is_biton(MYBOOL *bitarray, int item) +#else +void set_biton(MYBOOL *bitarray, int item); +MYBOOL set_bitoff(MYBOOL *bitarray, int item); +MYBOOL is_biton(MYBOOL *bitarray, int item); +#endif +/* This next line went with "if defined INLINE" above SEB April 14 2006 */ +#if 0 +INLINE void set_bitoff(MYBOOL *bitarray, int item) { - return( (MYBOOL) ((bitarray[item / 8] & (1 << (item % 8))) != 0) ); + bitarray[item / 8] &= ~(1 << (item % 8)); } - - -/* ---------------------------------------------------------------------------------- */ -/* Various interaction elements */ -/* ---------------------------------------------------------------------------------- */ +#endif MYBOOL __WINAPI userabort(lprec *lp, int message) { - MYBOOL abort; - int spx_save; + static MYBOOL abort; + static int spx_save; spx_save = lp->spx_status; lp->spx_status = RUNNING; @@ -146,8 +157,10 @@ MYBOOL __WINAPI userabort(lprec *lp, int message) STATIC int yieldformessages(lprec *lp) { + static double currenttime; + if((lp->sectimeout > 0) && - ((timeNow()-lp->timestart)-(LPSREAL)lp->sectimeout>0)) + (((currenttime = timeNow()) -lp->timestart)-(REAL)lp->sectimeout>0)) lp->spx_status = TIMEOUT; if(lp->ctrlc != NULL) { @@ -165,15 +178,15 @@ STATIC int yieldformessages(lprec *lp) void __WINAPI set_outputstream(lprec *lp, FILE *stream) { - if((lp->outstream != NULL) && (lp->outstream != stdout)) { + if((lp->outstream != NULL) ) { /* && (lp->outstream != stdout)) { */ if(lp->streamowned) fclose(lp->outstream); else fflush(lp->outstream); } - if(stream == NULL) - lp->outstream = stdout; - else +/* if(stream == NULL) +** lp->outstream = stdout; +** else */ lp->outstream = stream; lp->streamowned = FALSE; } @@ -181,7 +194,7 @@ void __WINAPI set_outputstream(lprec *lp, FILE *stream) MYBOOL __WINAPI set_outputfile(lprec *lp, char *filename) { MYBOOL ok; - FILE *output = stdout; + FILE *output; /* = stdout; */ ok = (MYBOOL) ((filename == NULL) || (*filename == 0) || ((output = fopen(filename,"w")) != NULL)); if(ok) { @@ -195,7 +208,7 @@ MYBOOL __WINAPI set_outputfile(lprec *lp, char *filename) return(ok); } -LPSREAL __WINAPI time_elapsed(lprec *lp) +REAL __WINAPI time_elapsed(lprec *lp) { if(lp->timeend > 0) return(lp->timeend - lp->timestart); @@ -234,88 +247,64 @@ void __WINAPI put_msgfunc(lprec *lp, lphandleint_func newmsg, void *msghandle, i /* ---------------------------------------------------------------------------------- */ /* DLL exported function */ /* ---------------------------------------------------------------------------------- */ -lprec * __WINAPI read_MPS(char *filename, int options) +lprec * __WINAPI read_MPS(char *filename, int verbose) { lprec *lp = NULL; - int typeMPS; - typeMPS = (options & ~0x07) >> 2; - if ((typeMPS & (MPSFIXED|MPSFREE)) == 0) - typeMPS |= MPSFIXED; - if(MPS_readfile(&lp, filename, typeMPS, options & 0x07)) + if(MPS_readfile(&lp, filename, MPSFIXED, verbose)) return( lp ); else return( NULL ); } -lprec * __WINAPI read_mps(FILE *filename, int options) +lprec * __WINAPI read_mps(FILE *filename, int verbose) { lprec *lp = NULL; - int typeMPS; - typeMPS = (options & ~0x07) >> 2; - if ((typeMPS & (MPSFIXED|MPSFREE)) == 0) - typeMPS |= MPSFIXED; - if(MPS_readhandle(&lp, filename, typeMPS, options & 0x07)) + if(MPS_readhandle(&lp, filename, MPSFIXED, verbose)) return( lp ); else return( NULL ); } -/* #if defined develop */ -lprec * __WINAPI read_mpsex(void *userhandle, read_modeldata_func read_modeldata, int options) +#if defined develop +lprec * __WINAPI read_mpsex(void *userhandle, read_modeldata_func read_modeldata, int verbose) { lprec *lp = NULL; - int typeMPS; - typeMPS = (options & ~0x07) >> 2; - if ((typeMPS & (MPSFIXED|MPSFREE)) == 0) - typeMPS |= MPSFIXED; - if(MPS_readex(&lp, userhandle, read_modeldata, typeMPS, options & 0x07)) + if(MPS_readex(&lp, userhandle, read_modeldata, MPSFIXED, verbose)) return( lp ); else return( NULL ); } -/* #endif */ -lprec * __WINAPI read_freeMPS(char *filename, int options) +#endif +lprec * __WINAPI read_freeMPS(char *filename, int verbose) { lprec *lp = NULL; - int typeMPS; - typeMPS = (options & ~0x07) >> 2; - typeMPS &= ~MPSFIXED; - typeMPS |= MPSFREE; - if(MPS_readfile(&lp, filename, typeMPS, options & 0x07)) + if(MPS_readfile(&lp, filename, MPSFREE, verbose)) return( lp ); else return( NULL ); } -lprec * __WINAPI read_freemps(FILE *filename, int options) +lprec * __WINAPI read_freemps(FILE *filename, int verbose) { lprec *lp = NULL; - int typeMPS; - typeMPS = (options & ~0x07) >> 2; - typeMPS &= ~MPSFIXED; - typeMPS |= MPSFREE; - if(MPS_readhandle(&lp, filename, typeMPS, options & 0x07)) + if(MPS_readhandle(&lp, filename, MPSFREE, verbose)) return( lp ); else return( NULL ); } -/* #if defined develop */ -lprec * __WINAPI read_freempsex(void *userhandle, read_modeldata_func read_modeldata, int options) +#if defined develop +lprec * __WINAPI read_freempsex(void *userhandle, read_modeldata_func read_modeldata, int verbose) { lprec *lp = NULL; - int typeMPS; - typeMPS = (options & ~0x07) >> 2; - typeMPS &= ~MPSFIXED; - typeMPS |= MPSFREE; - if(MPS_readex(&lp, userhandle, read_modeldata, typeMPS, options & 0x07)) + if(MPS_readex(&lp, userhandle, read_modeldata, MPSFREE, verbose)) return( lp ); else return( NULL ); } -/* #endif */ +#endif MYBOOL __WINAPI write_mps(lprec *lp, char *filename) { return(MPS_writefile(lp, MPSFIXED, filename)); @@ -462,10 +451,6 @@ void __WINAPI unscale(lprec *lp) } int __WINAPI solve(lprec *lp) { -#if defined FPUexception - catchFPU(_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW); -#endif - if(has_BFP(lp)) { lp->solvecount++; if(is_add_rowmode(lp)) @@ -585,7 +570,6 @@ MYBOOL __WINAPI is_anti_degen(lprec *lp, int testmask) void __WINAPI set_presolve(lprec *lp, int presolvemode, int maxloops) { - presolvemode &= ~PRESOLVE_REDUCEMIP; /* disable PRESOLVE_REDUCEMIP since it is very rare that this is effective, and also that it adds code complications and delayed presolve effects that are not captured properly. */ lp->do_presolve = presolvemode; lp->presolveloops = maxloops; } @@ -630,7 +614,7 @@ int __WINAPI get_bb_rule(lprec *lp) return(lp->bb_rule); } -/* INLINE */ MYBOOL is_bb_rule(lprec *lp, int bb_rule) +INLINE MYBOOL is_bb_rule(lprec *lp, int bb_rule) { return( (MYBOOL) ((lp->bb_rule & NODE_STRATEGYMASK) == bb_rule) ); } @@ -665,17 +649,17 @@ int __WINAPI get_bb_depthlimit(lprec *lp) return(lp->bb_limitlevel); } -void __WINAPI set_obj_bound(lprec *lp, LPSREAL bb_heuristicOF) +void __WINAPI set_obj_bound(lprec *lp, REAL bb_heuristicOF) { lp->bb_heuristicOF = bb_heuristicOF; } -LPSREAL __WINAPI get_obj_bound(lprec *lp) +REAL __WINAPI get_obj_bound(lprec *lp) { return(lp->bb_heuristicOF); } -void __WINAPI set_mip_gap(lprec *lp, MYBOOL absolute, LPSREAL mip_gap) +void __WINAPI set_mip_gap(lprec *lp, MYBOOL absolute, REAL mip_gap) { if(absolute) lp->mip_absgap = mip_gap; @@ -683,7 +667,7 @@ void __WINAPI set_mip_gap(lprec *lp, MYBOOL absolute, LPSREAL mip_gap) lp->mip_relgap = mip_gap; } -LPSREAL __WINAPI get_mip_gap(lprec *lp, MYBOOL absolute) +REAL __WINAPI get_mip_gap(lprec *lp, MYBOOL absolute) { if(absolute) return(lp->mip_absgap); @@ -725,7 +709,7 @@ int __WINAPI get_var_branch(lprec *lp, int colnr) return(lp->bb_varbranch[colnr - 1]); } -static void set_infiniteex(lprec *lp, LPSREAL infinite, MYBOOL init) +static void set_infiniteex(lprec *lp, REAL infinite, MYBOOL init) { int i; @@ -744,7 +728,7 @@ static void set_infiniteex(lprec *lp, LPSREAL infinite, MYBOOL init) } -MYBOOL __WINAPI is_infinite(lprec *lp, LPSREAL value) +MYBOOL __WINAPI is_infinite(lprec *lp, REAL value) { #if 1 return( (MYBOOL) (fabs(value) >= lp->infinite) ); @@ -756,79 +740,79 @@ MYBOOL __WINAPI is_infinite(lprec *lp, LPSREAL value) #endif } -void __WINAPI set_infinite(lprec *lp, LPSREAL infinite) +void __WINAPI set_infinite(lprec *lp, REAL infinite) { set_infiniteex(lp, infinite, FALSE); } -LPSREAL __WINAPI get_infinite(lprec *lp) +REAL __WINAPI get_infinite(lprec *lp) { return(lp->infinite); } -void __WINAPI set_epsperturb(lprec *lp, LPSREAL epsperturb) +void __WINAPI set_epsperturb(lprec *lp, REAL epsperturb) { lp->epsperturb = epsperturb; } -LPSREAL __WINAPI get_epsperturb(lprec *lp) +REAL __WINAPI get_epsperturb(lprec *lp) { return(lp->epsperturb); } -void __WINAPI set_epspivot(lprec *lp, LPSREAL epspivot) +void __WINAPI set_epspivot(lprec *lp, REAL epspivot) { lp->epspivot = epspivot; } -LPSREAL __WINAPI get_epspivot(lprec *lp) +REAL __WINAPI get_epspivot(lprec *lp) { return(lp->epspivot); } -void __WINAPI set_epsint(lprec *lp, LPSREAL epsint) +void __WINAPI set_epsint(lprec *lp, REAL epsint) { lp->epsint = epsint; } -LPSREAL __WINAPI get_epsint(lprec *lp) +REAL __WINAPI get_epsint(lprec *lp) { return(lp->epsint); } -void __WINAPI set_epsb(lprec *lp, LPSREAL epsb) +void __WINAPI set_epsb(lprec *lp, REAL epsb) { lp->epsprimal = MAX(epsb, lp->epsmachine); } -LPSREAL __WINAPI get_epsb(lprec *lp) +REAL __WINAPI get_epsb(lprec *lp) { return(lp->epsprimal); } -void __WINAPI set_epsd(lprec *lp, LPSREAL epsd) +void __WINAPI set_epsd(lprec *lp, REAL epsd) { lp->epsdual = MAX(epsd, lp->epsmachine); /* Mainly used as tolerance for reduced cost */ } -LPSREAL __WINAPI get_epsd(lprec *lp) +REAL __WINAPI get_epsd(lprec *lp) { return(lp->epsdual); } -void __WINAPI set_epsel(lprec *lp, LPSREAL epsel) +void __WINAPI set_epsel(lprec *lp, REAL epsel) { lp->epsvalue = MAX(epsel, lp->epsmachine); } -LPSREAL __WINAPI get_epsel(lprec *lp) +REAL __WINAPI get_epsel(lprec *lp) { return(lp->epsvalue); } MYBOOL __WINAPI set_epslevel(lprec *lp, int epslevel) { - LPSREAL SPX_RELAX, MIP_RELAX; + REAL SPX_RELAX, MIP_RELAX; switch(epslevel) { case EPS_TIGHT: SPX_RELAX = 1; @@ -880,14 +864,14 @@ MYBOOL __WINAPI is_scaletype(lprec *lp, int scaletype) return((MYBOOL) (scaletype == testtype)); } -void __WINAPI set_scalelimit(lprec *lp, LPSREAL scalelimit) +void __WINAPI set_scalelimit(lprec *lp, REAL scalelimit) /* Set the relative scaling convergence criterion for the active scaling mode; the integer part specifies the maximum number of iterations (default = 5). */ { lp->scalelimit = fabs(scalelimit); } -LPSREAL __WINAPI get_scalelimit(lprec *lp) +REAL __WINAPI get_scalelimit(lprec *lp) { return(lp->scalelimit); } @@ -974,17 +958,17 @@ int __WINAPI get_bb_floorfirst(lprec *lp) return(lp->bb_floorfirst); } -void __WINAPI set_break_at_value(lprec *lp, LPSREAL break_at_value) +void __WINAPI set_break_at_value(lprec *lp, REAL break_at_value) { lp->bb_breakOF = break_at_value; } -LPSREAL __WINAPI get_break_at_value(lprec *lp) +REAL __WINAPI get_break_at_value(lprec *lp) { return(lp->bb_breakOF); } -void __WINAPI set_negrange(lprec *lp, LPSREAL negrange) +void __WINAPI set_negrange(lprec *lp, REAL negrange) { if(negrange <= 0) lp->negrange = negrange; @@ -992,7 +976,7 @@ void __WINAPI set_negrange(lprec *lp, LPSREAL negrange) lp->negrange = 0.0; } -LPSREAL __WINAPI get_negrange(lprec *lp) +REAL __WINAPI get_negrange(lprec *lp) { return(lp->negrange); } @@ -1012,11 +996,9 @@ COUNTER __WINAPI get_total_iter(lprec *lp) return(lp->total_iter + lp->current_iter); } -LPSREAL __WINAPI get_objective(lprec *lp) +REAL __WINAPI get_objective(lprec *lp) { - if(lp->spx_status == OPTIMAL) - ; - else if(!lp->basis_valid) { + if(!lp->basis_valid) { report(lp, CRITICAL, "get_objective: Not a valid basis\n"); return(0.0); } @@ -1029,7 +1011,7 @@ int __WINAPI get_nonzeros(lprec *lp) return( mat_nonzeros(lp->matA) ); } -MYBOOL __WINAPI set_mat(lprec *lp, int rownr, int colnr, LPSREAL value) +MYBOOL __WINAPI set_mat(lprec *lp, int rownr, int colnr, REAL value) { if((rownr < 0) || (rownr > lp->rows)) { report(lp, IMPORTANT, "set_mat: Row %d out of range\n", rownr); @@ -1053,9 +1035,9 @@ MYBOOL __WINAPI set_mat(lprec *lp, int rownr, int colnr, LPSREAL value) return( mat_setvalue(lp->matA, rownr, colnr, value, FALSE) ); } -LPSREAL __WINAPI get_working_objective(lprec *lp) +REAL __WINAPI get_working_objective(lprec *lp) { - LPSREAL value = 0.0; + REAL value = 0.0; if(!lp->basis_valid) report(lp, CRITICAL, "get_working_objective: Not a valid basis\n"); @@ -1067,7 +1049,7 @@ LPSREAL __WINAPI get_working_objective(lprec *lp) return(value); } -LPSREAL __WINAPI get_var_primalresult(lprec *lp, int index) +REAL __WINAPI get_var_primalresult(lprec *lp, int index) { if((index < 0) || (index > lp->presolve_undo->orig_sum)) { report(lp, IMPORTANT, "get_var_primalresult: Index %d out of range\n", index); @@ -1079,9 +1061,9 @@ LPSREAL __WINAPI get_var_primalresult(lprec *lp, int index) return( lp->best_solution[index] ); } -LPSREAL __WINAPI get_var_dualresult(lprec *lp, int index) +REAL __WINAPI get_var_dualresult(lprec *lp, int index) { - LPSREAL *duals; + REAL *duals; if((index < 0) || (index > lp->presolve_undo->orig_sum)) { report(lp, IMPORTANT, "get_var_dualresult: Index %d out of range\n", index); @@ -1099,11 +1081,9 @@ LPSREAL __WINAPI get_var_dualresult(lprec *lp, int index) return( duals[index] ); } -MYBOOL __WINAPI get_variables(lprec *lp, LPSREAL *var) +MYBOOL __WINAPI get_variables(lprec *lp, REAL *var) { - if(lp->spx_status == OPTIMAL) - ; - else if(!lp->basis_valid) { + if(!lp->basis_valid) { report(lp, CRITICAL, "get_variables: Not a valid basis\n"); return(FALSE); } @@ -1112,11 +1092,9 @@ MYBOOL __WINAPI get_variables(lprec *lp, LPSREAL *var) return(TRUE); } -MYBOOL __WINAPI get_ptr_variables(lprec *lp, LPSREAL **var) +MYBOOL __WINAPI get_ptr_variables(lprec *lp, REAL **var) { - if(lp->spx_status == OPTIMAL) - ; - else if(!lp->basis_valid) { + if(!lp->basis_valid) { report(lp, CRITICAL, "get_ptr_variables: Not a valid basis\n"); return(FALSE); } @@ -1126,11 +1104,9 @@ MYBOOL __WINAPI get_ptr_variables(lprec *lp, LPSREAL **var) return(TRUE); } -MYBOOL __WINAPI get_constraints(lprec *lp, LPSREAL *constr) +MYBOOL __WINAPI get_constraints(lprec *lp, REAL *constr) { - if(lp->spx_status == OPTIMAL) - ; - else if(!lp->basis_valid) { + if(!lp->basis_valid) { report(lp, CRITICAL, "get_constraints: Not a valid basis\n"); return(FALSE); } @@ -1139,11 +1115,9 @@ MYBOOL __WINAPI get_constraints(lprec *lp, LPSREAL *constr) return(TRUE); } -MYBOOL __WINAPI get_ptr_constraints(lprec *lp, LPSREAL **constr) +MYBOOL __WINAPI get_ptr_constraints(lprec *lp, REAL **constr) { - if(lp->spx_status == OPTIMAL) - ; - else if(!lp->basis_valid) { + if(!lp->basis_valid) { report(lp, CRITICAL, "get_ptr_constraints: Not a valid basis\n"); return(FALSE); } @@ -1153,9 +1127,9 @@ MYBOOL __WINAPI get_ptr_constraints(lprec *lp, LPSREAL **constr) return(TRUE); } -MYBOOL __WINAPI get_sensitivity_rhs(lprec *lp, LPSREAL *duals, LPSREAL *dualsfrom, LPSREAL *dualstill) +MYBOOL __WINAPI get_sensitivity_rhs(lprec *lp, REAL *duals, REAL *dualsfrom, REAL *dualstill) { - LPSREAL *duals0, *dualsfrom0, *dualstill0; + REAL *duals0, *dualsfrom0, *dualstill0; if(!lp->basis_valid) { report(lp, CRITICAL, "get_sensitivity_rhs: Not a valid basis\n"); @@ -1177,7 +1151,7 @@ MYBOOL __WINAPI get_sensitivity_rhs(lprec *lp, LPSREAL *duals, LPSREAL *dualsfro return(TRUE); } -MYBOOL __WINAPI get_ptr_sensitivity_rhs(lprec *lp, LPSREAL **duals, LPSREAL **dualsfrom, LPSREAL **dualstill) +MYBOOL __WINAPI get_ptr_sensitivity_rhs(lprec *lp, REAL **duals, REAL **dualsfrom, REAL **dualstill) { if(!lp->basis_valid) { report(lp, CRITICAL, "get_ptr_sensitivity_rhs: Not a valid basis\n"); @@ -1214,9 +1188,9 @@ MYBOOL __WINAPI get_ptr_sensitivity_rhs(lprec *lp, LPSREAL **duals, LPSREAL **du return(TRUE); } -MYBOOL __WINAPI get_sensitivity_objex(lprec *lp, LPSREAL *objfrom, LPSREAL *objtill, LPSREAL *objfromvalue, LPSREAL *objtillvalue) +MYBOOL __WINAPI get_sensitivity_objex(lprec *lp, REAL *objfrom, REAL *objtill, REAL *objfromvalue, REAL *objtillvalue) { - LPSREAL *objfrom0, *objtill0, *objfromvalue0, *objtillvalue0; + REAL *objfrom0, *objtill0, *objfromvalue0, *objtillvalue0; if(!lp->basis_valid) { report(lp, CRITICAL, "get_sensitivity_objex: Not a valid basis\n"); @@ -1240,12 +1214,12 @@ MYBOOL __WINAPI get_sensitivity_objex(lprec *lp, LPSREAL *objfrom, LPSREAL *objt return(TRUE); } -MYBOOL __WINAPI get_sensitivity_obj(lprec *lp, LPSREAL *objfrom, LPSREAL *objtill) +MYBOOL __WINAPI get_sensitivity_obj(lprec *lp, REAL *objfrom, REAL *objtill) { return(get_sensitivity_objex(lp, objfrom, objtill, NULL, NULL)); } -MYBOOL __WINAPI get_ptr_sensitivity_objex(lprec *lp, LPSREAL **objfrom, LPSREAL **objtill, LPSREAL **objfromvalue, LPSREAL **objtillvalue) +MYBOOL __WINAPI get_ptr_sensitivity_objex(lprec *lp, REAL **objfrom, REAL **objtill, REAL **objfromvalue, REAL **objtillvalue) { if(!lp->basis_valid) { report(lp, CRITICAL, "get_ptr_sensitivity_objex: Not a valid basis\n"); @@ -1289,7 +1263,7 @@ MYBOOL __WINAPI get_ptr_sensitivity_objex(lprec *lp, LPSREAL **objfrom, LPSREAL return(TRUE); } -MYBOOL __WINAPI get_ptr_sensitivity_obj(lprec *lp, LPSREAL **objfrom, LPSREAL **objtill) +MYBOOL __WINAPI get_ptr_sensitivity_obj(lprec *lp, REAL **objfrom, REAL **objtill) { return(get_ptr_sensitivity_objex(lp, objfrom, objtill, NULL, NULL)); } @@ -1388,10 +1362,7 @@ lprec * __WINAPI make_lp(int rows, int columns) { lprec *lp; -# if defined FORTIFY - /* Fortify_EnterScope(); */ -# endif - + callcount++; if(rows < 0 || columns < 0) return(NULL); @@ -1403,7 +1374,6 @@ lprec * __WINAPI make_lp(int rows, int columns) lp->names_used = FALSE; lp->use_row_names = TRUE; lp->use_col_names = TRUE; - lp->rowcol_name = NULL; /* Do standard initializations ------------------------------------------------------------ */ #if 1 @@ -1516,7 +1486,6 @@ lprec * __WINAPI make_lp(int rows, int columns) lp->msghandle = NULL; lp->timecreate = timeNow(); - return(lp); } @@ -1554,7 +1523,6 @@ void __WINAPI delete_lp(lprec *lp) if(lp == NULL) return; - FREE(lp->rowcol_name); FREE(lp->lp_name); FREE(lp->ex_status); if(lp->names_used) { @@ -1656,36 +1624,6 @@ void __WINAPI delete_lp(lprec *lp) FREE(lp); -# if defined FORTIFY - /* Fortify_LeaveScope(); */ -# endif -} - -static MYBOOL get_SOS(lprec *lp, int index, char *name, int *sostype, int *priority, int *count, int *sosvars, LPSREAL *weights) -{ - SOSrec *SOS; - - if((index < 1) || (index > SOS_count(lp))) - return( FALSE ); - SOS = lp->SOS->sos_list[index-1]; - if(name != NULL) - strcpy(name, SOS->name); - if(sostype != NULL) - *sostype = SOS->type; - if(priority != NULL) - *priority = SOS->priority; - if(count != NULL) { - *count = SOS->size; - if(sosvars != NULL) { - int i; - for(i = 1; i <= *count; i++) { - sosvars[i-1] = SOS->members[i]; - if(weights != NULL) - weights[i-1] = SOS->weights[i]; - } - } - } - return( TRUE ); } /* Make a copy of the existing model using (mostly) high-level @@ -1693,132 +1631,70 @@ static MYBOOL get_SOS(lprec *lp, int index, char *name, int *sostype, int *prior lprec* __WINAPI copy_lp(lprec *lp) { int i, n, *idx = NULL; - LPSREAL hold, *val = NULL, infinite; + REAL hold, *val = NULL; lprec *newlp = NULL; - char buf[256], ok = FALSE; - int sostype, priority, count, *sosvars, rows, columns; - LPSREAL *weights = NULL; #if 0 if(lp->wasPresolved) return( newlp ); #endif - rows = get_Nrows(lp); - columns = get_Ncolumns(lp); - - if(!allocINT(lp, &idx, rows+1, FALSE) || - !allocREAL(lp, &val, rows+1, FALSE)) + if(!allocINT(lp, &idx, lp->rows+1, FALSE) || + !allocREAL(lp, &val, lp->rows+1, FALSE)) goto Finish; /* Create the new object */ - newlp = make_lp(rows, 0); - if(newlp == NULL) - goto Finish; - if(!resize_lp(newlp, rows, columns)) - goto Finish; + newlp = make_lp(lp->rows, 0); + resize_lp(newlp, lp->rows, lp->columns); set_sense(newlp, is_maxim(lp)); - set_use_names(newlp, FALSE, is_use_names(lp, FALSE)); - set_use_names(newlp, TRUE, is_use_names(lp, TRUE)); - if(!set_lp_name(newlp, get_lp_name(lp))) - goto Finish; - /* set_algopt(newlp, get_algopt(lp)); */ /* v6 */ - set_verbose(newlp, get_verbose(lp)); - /* Transfer standard simplex parameters */ + /* Transfer parameters */ set_epspivot(newlp, get_epspivot(lp)); set_epsel(newlp, get_epsel(lp)); set_epsb(newlp, get_epsb(lp)); set_epsd(newlp, get_epsd(lp)); + set_epsint(newlp, get_epsint(lp)); set_pivoting(newlp, get_pivoting(lp)); set_negrange(newlp, lp->negrange); set_infinite(newlp, get_infinite(lp)); set_presolve(newlp, get_presolve(lp), get_presolveloops(lp)); set_scaling(newlp, get_scaling(lp)); - set_scalelimit(newlp, get_scalelimit(lp)); set_simplextype(newlp, get_simplextype(lp)); - set_epsperturb(newlp, get_epsperturb(lp)); - set_anti_degen(newlp, get_anti_degen(lp)); - set_improve(newlp, get_improve(lp)); - set_basiscrash(newlp, get_basiscrash(lp)); - set_maxpivot(newlp, get_maxpivot(lp)); - set_timeout(newlp, get_timeout(lp)); - - /* Transfer MILP parameters */ - set_epsint(newlp, get_epsint(lp)); - set_bb_rule(newlp, get_bb_rule(lp)); - set_bb_depthlimit(newlp, get_bb_depthlimit(lp)); - set_bb_floorfirst(newlp, get_bb_floorfirst(lp)); - set_mip_gap(newlp, TRUE, get_mip_gap(lp, TRUE)); - set_mip_gap(newlp, FALSE, get_mip_gap(lp, FALSE)); - set_break_at_first(newlp, is_break_at_first(lp)); - set_break_at_value(newlp, get_break_at_value(lp)); /* Set RHS and range */ - infinite = get_infinite(lp); - for(i = 0; i <= rows; i++) { + for(i = 0; i <= lp->rows; i++) { if(i > 0) - if(!set_constr_type(newlp, i, get_constr_type(lp, i))) - goto Finish; - if(!set_rh(newlp, i, get_rh(lp, i))) - goto Finish; - if((i > 0) && ((hold = get_rh_range(lp, i)) < infinite)) - if(!set_rh_range(newlp, i, hold)) - goto Finish; - if(lp->names_used && lp->use_row_names && (lp->row_name[i] != NULL) && (lp->row_name[i]->name != NULL)) - if(!set_row_name(newlp, i, get_row_name(lp, i))) - goto Finish; + set_constr_type(newlp, 0, get_constr_type(lp, i)); + set_rh(newlp, i, get_rh(lp, 0)); + if((i > 0) && ((hold = get_rh_range(lp, i)) < lp->infinite)) + set_rh_range(newlp, i, hold); + if(lp->names_used) + set_row_name(newlp, i, get_row_name(lp, i)); } - /* Load the constraint matrix and variable definitions */ - for(i = 1; i <= columns; i++) { + for(i = 1; i <= lp->columns; i++) { n = get_columnex(lp, i, val, idx); - if ((n < 0) || (!add_columnex(newlp, n, val, idx))) - goto Finish; - if(is_binary(lp, i)) { - if (!set_binary(newlp, i, TRUE)) - goto Finish; - } + add_columnex(newlp, n, val, idx); + if(is_binary(lp, i)) + set_binary(newlp, i, TRUE); else { if(is_int(lp, i)) - if(!set_int(newlp, i, TRUE)) - goto Finish; + set_int(newlp, i, TRUE); if((hold = get_lowbo(lp, i)) != 0) - if(!set_lowbo(newlp, i, hold)) - goto Finish; - if((hold = get_upbo(lp, i)) < infinite) - if(!set_upbo(newlp, i, hold)) - goto Finish; + set_lowbo(newlp, i, hold); + if((hold = get_upbo(lp, i)) < lp->infinite) + set_upbo(newlp, i, hold); } if(is_semicont(lp, i)) - if(!set_semicont(newlp, i, TRUE)) - goto Finish; - if(lp->names_used && lp->use_col_names && (lp->col_name[i] != NULL) && (lp->col_name[i]->name != NULL)) - if(!set_col_name(newlp, i, get_col_name(lp, i))) - goto Finish; - } - - /* copy SOS data */ - for(i = 1; get_SOS(lp, i, buf, &sostype, &priority, &count, NULL, NULL); i++) - if (count) { - if(!allocINT(lp, &sosvars, count, FALSE) || - !allocREAL(lp, &weights, count, FALSE)) - n = 0; - else { - get_SOS(lp, i, buf, &sostype, &priority, &count, sosvars, weights); - n = add_SOS(newlp, buf, sostype, priority, count, sosvars, weights); - } - FREE(weights); - FREE(sosvars); - if(n == 0) - goto Finish; - } + set_semicont(newlp, i, TRUE); + if(lp->names_used) + set_col_name(newlp, i, get_col_name(lp, i)); + } -#if 0 /* Other parameters set if the source model was previously solved */ if(lp->solvecount > 0) { MEMCOPY(newlp->scalars, lp->scalars, lp->sum+1); - MEMCOPY(newlp->var_basic, lp->var_basic, rows+1); + MEMCOPY(newlp->var_basic, lp->var_basic, lp->rows+1); MEMCOPY(newlp->is_basic, lp->is_basic, lp->sum+1); MEMCOPY(newlp->is_lower, lp->is_lower, lp->sum+1); MEMCOPY(newlp->solution, lp->solution, lp->sum+1); @@ -1829,14 +1705,9 @@ lprec* __WINAPI copy_lp(lprec *lp) newlp->solutioncount = lp->solutioncount; newlp->solvecount = lp->solvecount; } -#endif - - ok = TRUE; /* Clean up before returning */ Finish: - if(!ok) - free_lp(&newlp); FREE(val); FREE(idx); @@ -1846,7 +1717,7 @@ MYBOOL __WINAPI dualize_lp(lprec *lp) { int i, n; MATrec *mat = lp->matA; - LPSREAL *item; + REAL *item; /* Are we allowed to perform the operation? */ if((MIP_count(lp) > 0) || (lp->solvecount > 0)) @@ -1866,8 +1737,7 @@ MYBOOL __WINAPI dualize_lp(lprec *lp) swapINT(&lp->rows, &lp->columns); swapINT(&lp->rows_alloc, &lp->columns_alloc); swapREAL(lp->orig_rhs, lp->orig_obj); - if ((lp->rhs != NULL) && (lp->obj != NULL)) - swapREAL(lp->rhs, lp->obj); + swapREAL(lp->rhs, lp->obj); /* Reallocate storage */ /* @@ -1979,25 +1849,22 @@ STATIC void varmap_add(lprec *lp, int base, int delta) STATIC void varmap_delete(lprec *lp, int base, int delta, LLrec *varmap) { int i, ii, j; - MYBOOL preparecompact = (MYBOOL) (varmap != NULL); + MYBOOL preparecompact; presolveundorec *psundo = lp->presolve_undo; /* Set the model "dirty" if we are deleting row of constraint */ - lp->model_is_pure &= (MYBOOL) ((lp->solutioncount == 0) && !preparecompact); + lp->model_is_pure = FALSE; /* Don't do anything if 1) variables aren't locked yet, or 2) the constraint was added after the variables were locked */ if(!lp->varmap_locked) { -#if 0 +#if 1 if(lp->names_used) varmap_lock(lp); else - return; -#else - if(!lp->model_is_pure && lp->names_used) - varmap_lock(lp); #endif + return; } /* Do mass deletion via a linked list */ @@ -2043,7 +1910,6 @@ STATIC void varmap_delete(lprec *lp, int base, int delta, LLrec *varmap) 2) shift the deleted variable to original mappings left 3) decrement all subsequent original-to-current pointers */ - if(varmap_canunlock(lp)) lp->varmap_locked = FALSE; for(i = base; i < base-delta; i++) { ii = psundo->var_to_orig[i]; if(ii > 0) @@ -2164,7 +2030,7 @@ STATIC MYBOOL shift_rowcoldata(lprec *lp, int base, int delta, LLrec *usedmap, M /* Note: Assumes that "lp->sum" and "lp->rows" HAVE NOT been updated to the new counts */ { int i, ii; - LPSREAL lodefault; + REAL lodefault; /* Shift data right/down (insert), and set default values in positive delta-gap */ if(delta > 0) { @@ -2453,8 +2319,7 @@ STATIC MYBOOL shift_coldata(lprec *lp, int base, int delta, LLrec *usedmap) { int i, ii; - if(lp->bb_totalnodes == 0) - free_duals(lp); + free_duals(lp); /* Shift A matrix data */ if(lp->matA->is_roworder) @@ -2688,12 +2553,6 @@ STATIC MYBOOL shift_coldata(lprec *lp, int base, int delta, LLrec *usedmap) /* Utility group for incrementing row and column vector storage space */ STATIC void inc_rows(lprec *lp, int delta) { - int i; - - if(lp->names_used && (lp->row_name != NULL)) - for(i = lp->rows + delta; i > lp->rows; i--) - lp->row_name[i] = NULL; - lp->rows += delta; if(lp->matA->is_roworder) lp->matA->columns += delta; @@ -2703,12 +2562,6 @@ STATIC void inc_rows(lprec *lp, int delta) STATIC void inc_columns(lprec *lp, int delta) { - int i; - - if(lp->names_used && (lp->col_name != NULL)) - for(i = lp->columns + delta; i > lp->columns; i--) - lp->col_name[i] = NULL; - lp->columns += delta; if(lp->matA->is_roworder) lp->matA->rows += delta; @@ -2813,12 +2666,7 @@ STATIC MYBOOL inc_row_space(lprec *lp, int deltarows) rowsum = lp->matA->columns_alloc; } else { -#if 0 - if((lp->rows_alloc > 0) && (lp->rows + deltarows > lp->rows_alloc)) - i = deltarows; /* peno 25/12/06 */ - else -#endif - i -= lp->matA->rows_alloc; + i -= lp->matA->rows_alloc; SETMIN(i, deltarows); if(i > 0) inc_matrow_space(lp->matA, i); @@ -2972,7 +2820,7 @@ STATIC MYBOOL inc_col_space(lprec *lp, int deltacols) /* Problem manipulation routines */ -MYBOOL __WINAPI set_obj(lprec *lp, int colnr, LPSREAL value) +MYBOOL __WINAPI set_obj(lprec *lp, int colnr, REAL value) { if(colnr <= 0) colnr = set_rh(lp, 0, value); @@ -2981,11 +2829,11 @@ MYBOOL __WINAPI set_obj(lprec *lp, int colnr, LPSREAL value) return((MYBOOL) colnr); } -MYBOOL __WINAPI set_obj_fnex(lprec *lp, int count, LPSREAL *row, int *colno) +MYBOOL __WINAPI set_obj_fnex(lprec *lp, int count, REAL *row, int *colno) { MYBOOL chsgn = is_maxim(lp); int i, ix; - LPSREAL value; + REAL value; if(row == NULL) return( FALSE ); @@ -3016,7 +2864,7 @@ MYBOOL __WINAPI set_obj_fnex(lprec *lp, int count, LPSREAL *row, int *colno) return(TRUE); } -MYBOOL __WINAPI set_obj_fn(lprec *lp, LPSREAL *row) +MYBOOL __WINAPI set_obj_fn(lprec *lp, REAL *row) { return( set_obj_fnex(lp, 0, row, NULL) ); } @@ -3025,13 +2873,13 @@ MYBOOL __WINAPI str_set_obj_fn(lprec *lp, char *row_string) { int i; MYBOOL ret = TRUE; - LPSREAL *arow; + REAL *arow; char *p, *newp; allocREAL(lp, &arow, lp->columns + 1, FALSE); p = row_string; for(i = 1; i <= lp->columns; i++) { - arow[i] = (LPSREAL) strtod(p, &newp); + arow[i] = (REAL) strtod(p, &newp); if(p == newp) { report(lp, IMPORTANT, "str_set_obj_fn: Bad string %s\n", p); lp->spx_status = DATAIGNORED; @@ -3079,7 +2927,7 @@ MYBOOL __WINAPI is_add_rowmode(lprec *lp) return(lp->matA->is_roworder); } -MYBOOL __WINAPI set_row(lprec *lp, int rownr, LPSREAL *row) +MYBOOL __WINAPI set_row(lprec *lp, int rownr, REAL *row) { if((rownr < 0) || (rownr > lp->rows)) { report(lp, IMPORTANT, "set_row: Row %d out of range\n", rownr); @@ -3091,7 +2939,7 @@ MYBOOL __WINAPI set_row(lprec *lp, int rownr, LPSREAL *row) return( mat_setrow(lp->matA, rownr, lp->columns, row, NULL, TRUE, TRUE) ); } -MYBOOL __WINAPI set_rowex(lprec *lp, int rownr, int count, LPSREAL *row, int *colno) +MYBOOL __WINAPI set_rowex(lprec *lp, int rownr, int count, REAL *row, int *colno) { if((rownr < 0) || (rownr > lp->rows)) { report(lp, IMPORTANT, "set_rowex: Row %d out of range\n", rownr); @@ -3103,7 +2951,7 @@ MYBOOL __WINAPI set_rowex(lprec *lp, int rownr, int count, LPSREAL *row, int *co return( mat_setrow(lp->matA, rownr, count, row, colno, TRUE, TRUE) ); } -MYBOOL __WINAPI add_constraintex(lprec *lp, int count, LPSREAL *row, int *colno, int constr_type, LPSREAL rh) +MYBOOL __WINAPI add_constraintex(lprec *lp, int count, REAL *row, int *colno, int constr_type, REAL rh) { int n; MYBOOL status = FALSE; @@ -3131,7 +2979,7 @@ MYBOOL __WINAPI add_constraintex(lprec *lp, int count, LPSREAL *row, int *colno, lp->orig_rhs[lp->rows] = rh; /* Insert the non-zero constraint values */ - if(colno == NULL && row != NULL) + if(colno == NULL) n = lp->columns; else n = count; @@ -3157,23 +3005,23 @@ MYBOOL __WINAPI add_constraintex(lprec *lp, int count, LPSREAL *row, int *colno, return( status ); } -MYBOOL __WINAPI add_constraint(lprec *lp, LPSREAL *row, int constr_type, LPSREAL rh) +MYBOOL __WINAPI add_constraint(lprec *lp, REAL *row, int constr_type, REAL rh) { return( add_constraintex(lp, 0, row, NULL, constr_type, rh) ); } -MYBOOL __WINAPI str_add_constraint(lprec *lp, char *row_string, int constr_type, LPSREAL rh) +MYBOOL __WINAPI str_add_constraint(lprec *lp, char *row_string, int constr_type, REAL rh) { int i; char *p, *newp; - LPSREAL *aRow; + REAL *aRow; MYBOOL status = FALSE; allocREAL(lp, &aRow, lp->columns + 1, FALSE); p = row_string; for(i = 1; i <= lp->columns; i++) { - aRow[i] = (LPSREAL) strtod(p, &newp); + aRow[i] = (REAL) strtod(p, &newp); if(p == newp) { report(lp, IMPORTANT, "str_add_constraint: Bad string '%s'\n", p); lp->spx_status = DATAIGNORED; @@ -3209,7 +3057,7 @@ STATIC MYBOOL del_constraintex(lprec *lp, LLrec *rowmap) if(!lp->varmap_locked) { presolve_setOrig(lp, lp->rows, lp->columns); if(lp->names_used) - del_varnameex(lp, lp->row_name, lp->rows, lp->rowname_hashtab, 0, rowmap); + del_varnameex(lp, lp->row_name, lp->rowname_hashtab, 0, rowmap); } #ifdef Paranoia @@ -3229,46 +3077,20 @@ MYBOOL __WINAPI del_constraint(lprec *lp, int rownr) report(lp, IMPORTANT, "del_constraint: Attempt to delete non-existing constraint %d\n", rownr); return(FALSE); } - /* if(lp->matA->is_roworder) { report(lp, IMPORTANT, "del_constraint: Cannot delete constraint while in row entry mode.\n"); return(FALSE); } - */ if(is_constr_type(lp, rownr, EQ) && (lp->equalities > 0)) lp->equalities--; varmap_delete(lp, my_chsign(preparecompact, rownr), -1, NULL); shift_rowdata(lp, my_chsign(preparecompact, rownr), -1, NULL); - -/* - peno 04.10.07 - Fixes a problem with del_constraint. - Constraints names were not shifted and reported variable result was incorrect. - See UnitTest1, UnitTest2 - - min: -2 x3; - - c1: +x2 -x1 <= 10; - c: 0 x3 <= 0; - c2: +x3 +x2 +x1 <= 20; - - 2 <= x3 <= 3; - x1 <= 30; - - // del_constraint(lp, 2); - - // See write_LP and print_solution result - - // To fix, commented if(!lp->varmap_locked) - -*/ - if(!lp->varmap_locked) - { + if(!lp->varmap_locked) { presolve_setOrig(lp, lp->rows, lp->columns); if(lp->names_used) - del_varnameex(lp, lp->row_name, lp->rows, lp->rowname_hashtab, rownr, NULL); + del_varnameex(lp, lp->row_name, lp->rowname_hashtab, rownr, NULL); } #ifdef Paranoia @@ -3279,10 +3101,10 @@ MYBOOL __WINAPI del_constraint(lprec *lp, int rownr) return(TRUE); } -MYBOOL __WINAPI add_lag_con(lprec *lp, LPSREAL *row, int con_type, LPSREAL rhs) +MYBOOL __WINAPI add_lag_con(lprec *lp, REAL *row, int con_type, REAL rhs) { int k; - LPSREAL sign; + REAL sign; if(con_type == LE || con_type == EQ) sign = 1; @@ -3304,18 +3126,18 @@ MYBOOL __WINAPI add_lag_con(lprec *lp, LPSREAL *row, int con_type, LPSREAL rhs) return(TRUE); } -MYBOOL __WINAPI str_add_lag_con(lprec *lp, char *row_string, int con_type, LPSREAL rhs) +MYBOOL __WINAPI str_add_lag_con(lprec *lp, char *row_string, int con_type, REAL rhs) { int i; MYBOOL ret = TRUE; - LPSREAL *a_row; + REAL *a_row; char *p, *new_p; allocREAL(lp, &a_row, lp->columns + 1, FALSE); p = row_string; for(i = 1; i <= lp->columns; i++) { - a_row[i] = (LPSREAL) strtod(p, &new_p); + a_row[i] = (REAL) strtod(p, &new_p); if(p == new_p) { report(lp, IMPORTANT, "str_add_lag_con: Bad string '%s'\n", p); lp->spx_status = DATAIGNORED; @@ -3368,17 +3190,17 @@ void del_splitvars(lprec *lp) } } -MYBOOL __WINAPI set_column(lprec *lp, int colnr, LPSREAL *column) +MYBOOL __WINAPI set_column(lprec *lp, int colnr, REAL *column) { return( mat_setcol(lp->matA, colnr, lp->rows, column, NULL, TRUE, TRUE) ); } -MYBOOL __WINAPI set_columnex(lprec *lp, int colnr, int count, LPSREAL *column, int *rowno) +MYBOOL __WINAPI set_columnex(lprec *lp, int colnr, int count, REAL *column, int *rowno) { return( mat_setcol(lp->matA, colnr, count, column, rowno, TRUE, TRUE) ); } -MYBOOL __WINAPI add_columnex(lprec *lp, int count, LPSREAL *column, int *rowno) +MYBOOL __WINAPI add_columnex(lprec *lp, int count, REAL *column, int *rowno) /* This function adds a data column to the current model; three cases handled: 1: Prepare for column data by setting column = NULL @@ -3400,9 +3222,9 @@ MYBOOL __WINAPI add_columnex(lprec *lp, int count, LPSREAL *column, int *rowno) lp->columns); else #ifdef Paranoia - if(lp->columns != (lp->matA->is_roworder ? lp->matA->rows : lp->matA->columns)) { + if(lp->columns != lp->matA->columns) { report(lp, SEVERE, "add_columnex: Column count mismatch %d vs %d\n", - lp->columns, (lp->matA->is_roworder ? lp->matA->rows : lp->matA->columns)); + lp->columns, lp->matA->columns); } else if(is_BasisReady(lp) && (lp->P1extraDim == 0) && !verify_basis(lp)) report(lp, SEVERE, "add_columnex: Invalid basis detected for column %d\n", @@ -3417,7 +3239,7 @@ MYBOOL __WINAPI add_columnex(lprec *lp, int count, LPSREAL *column, int *rowno) return( status ); } -MYBOOL __WINAPI add_column(lprec *lp, LPSREAL *column) +MYBOOL __WINAPI add_column(lprec *lp, REAL *column) { del_splitvars(lp); return(add_columnex(lp, lp->rows, column, NULL)); @@ -3427,14 +3249,14 @@ MYBOOL __WINAPI str_add_column(lprec *lp, char *col_string) { int i; MYBOOL ret = TRUE; - LPSREAL *aCol; + REAL *aCol; char *p, *newp; allocREAL(lp, &aCol, lp->rows + 1, FALSE); p = col_string; for(i = 0; i <= lp->rows; i++) { - aCol[i] = (LPSREAL) strtod(p, &newp); + aCol[i] = (REAL) strtod(p, &newp); if(p == newp) { report(lp, IMPORTANT, "str_add_column: Bad string '%s'\n", p); lp->spx_status = DATAIGNORED; @@ -3450,7 +3272,7 @@ MYBOOL __WINAPI str_add_column(lprec *lp, char *col_string) return( ret ); } -STATIC MYBOOL del_varnameex(lprec *lp, hashelem **namelist, int items, hashtable *ht, int varnr, LLrec *varmap) +STATIC MYBOOL del_varnameex(lprec *lp, hashelem **namelist, hashtable *ht, int varnr, LLrec *varmap) { int i, n; @@ -3460,10 +3282,9 @@ STATIC MYBOOL del_varnameex(lprec *lp, hashelem **namelist, int items, hashtable else i = varnr; while(i > 0) { - if(namelist[i] != NULL) { - if(namelist[i]->name != NULL) - drophash(namelist[i]->name, namelist, ht); - } + if((namelist[i] != NULL) && + (namelist[i]->name != NULL)) + drophash(namelist[i]->name, namelist, ht); if(varmap != NULL) i = nextInactiveLink(varmap, i); else @@ -3487,8 +3308,6 @@ STATIC MYBOOL del_varnameex(lprec *lp, hashelem **namelist, int items, hashtable i++; if(varmap != NULL) n = nextActiveLink(varmap, i); - else if(n <= items) /* items has been updated for the new count */ - n++; else n = 0; } @@ -3502,7 +3321,7 @@ STATIC MYBOOL del_columnex(lprec *lp, LLrec *colmap) if(!lp->varmap_locked) { presolve_setOrig(lp, lp->rows, lp->columns); if(lp->names_used) - del_varnameex(lp, lp->col_name, lp->columns, lp->colname_hashtab, 0, colmap); + del_varnameex(lp, lp->col_name, lp->colname_hashtab, 0, colmap); } #ifdef Paranoia if(is_BasisReady(lp) && (lp->P1extraDim == 0) && !verify_basis(lp)) @@ -3521,12 +3340,10 @@ MYBOOL __WINAPI del_column(lprec *lp, int colnr) report(lp, IMPORTANT, "del_column: Column %d out of range\n", colnr); return(FALSE); } - /* if(lp->matA->is_roworder) { report(lp, IMPORTANT, "del_column: Cannot delete column while in row entry mode.\n"); return(FALSE); } - */ if((lp->var_is_free != NULL) && (lp->var_is_free[colnr] > 0)) del_column(lp, lp->var_is_free[colnr]); /* delete corresponding split column (is always after this column) */ @@ -3536,7 +3353,7 @@ MYBOOL __WINAPI del_column(lprec *lp, int colnr) if(!lp->varmap_locked) { presolve_setOrig(lp, lp->rows, lp->columns); if(lp->names_used) - del_varnameex(lp, lp->col_name, lp->columns, lp->colname_hashtab, colnr, NULL); + del_varnameex(lp, lp->col_name, lp->colname_hashtab, colnr, NULL); } #ifdef Paranoia if(is_BasisReady(lp) && (lp->P1extraDim == 0) && !verify_basis(lp)) @@ -3573,7 +3390,7 @@ MYBOOL __WINAPI get_bounds_tighter(lprec *lp) return(lp->tighten_on_set); } -MYBOOL __WINAPI set_upbo(lprec *lp, int colnr, LPSREAL value) +MYBOOL __WINAPI set_upbo(lprec *lp, int colnr, REAL value) { if((colnr > lp->columns) || (colnr < 1)) { report(lp, IMPORTANT, "set_upbo: Column %d out of range\n", colnr); @@ -3605,9 +3422,9 @@ MYBOOL __WINAPI set_upbo(lprec *lp, int colnr, LPSREAL value) return(TRUE); } -LPSREAL __WINAPI get_upbo(lprec *lp, int colnr) +REAL __WINAPI get_upbo(lprec *lp, int colnr) { - LPSREAL value; + REAL value; if((colnr > lp->columns) || (colnr < 1)) { report(lp, IMPORTANT, "get_upbo: Column %d out of range\n", colnr); @@ -3619,7 +3436,7 @@ LPSREAL __WINAPI get_upbo(lprec *lp, int colnr) return(value); } -MYBOOL __WINAPI set_lowbo(lprec *lp, int colnr, LPSREAL value) +MYBOOL __WINAPI set_lowbo(lprec *lp, int colnr, REAL value) { if((colnr > lp->columns) || (colnr < 1)) { report(lp, IMPORTANT, "set_lowbo: Column %d out of range\n", colnr); @@ -3651,9 +3468,9 @@ MYBOOL __WINAPI set_lowbo(lprec *lp, int colnr, LPSREAL value) return(TRUE); } -LPSREAL __WINAPI get_lowbo(lprec *lp, int colnr) +REAL __WINAPI get_lowbo(lprec *lp, int colnr) { - LPSREAL value; + REAL value; if((colnr > lp->columns) || (colnr < 1)) { report(lp, IMPORTANT, "get_lowbo: Column %d out of range\n", colnr); @@ -3665,7 +3482,7 @@ LPSREAL __WINAPI get_lowbo(lprec *lp, int colnr) return(value); } -MYBOOL __WINAPI set_bounds(lprec *lp, int colnr, LPSREAL lower, LPSREAL upper) +MYBOOL __WINAPI set_bounds(lprec *lp, int colnr, REAL lower, REAL upper) { if((colnr > lp->columns) || (colnr < 1)) { report(lp, IMPORTANT, "set_bounds: Column %d out of range\n", colnr); @@ -3710,7 +3527,7 @@ MYBOOL __WINAPI set_bounds(lprec *lp, int colnr, LPSREAL lower, LPSREAL upper) return(TRUE); } -MYBOOL get_bounds(lprec *lp, int column, LPSREAL *lower, LPSREAL *upper) +MYBOOL get_bounds(lprec *lp, int column, REAL *lower, REAL *upper) { if((column > lp->columns) || (column < 1)) { report(lp, IMPORTANT, "get_bounds: Column %d out of range", column); @@ -3765,7 +3582,7 @@ MYBOOL __WINAPI is_SOS_var(lprec *lp, int colnr) return((lp->var_type[colnr] & ISSOS) != 0); } -int __WINAPI add_SOS(lprec *lp, char *name, int sostype, int priority, int count, int *sosvars, LPSREAL *weights) +int __WINAPI add_SOS(lprec *lp, char *name, int sostype, int priority, int count, int *sosvars, REAL *weights) { SOSrec *SOS; int k; @@ -3778,7 +3595,7 @@ int __WINAPI add_SOS(lprec *lp, char *name, int sostype, int priority, int count /* Make sure SOSes of order 3 and higher are properly defined */ if(sostype > 2) { int j; - for(k = 0; k < count; k++) { + for(k = 1; k <= count; k++) { j = sosvars[k]; if(!is_int(lp, j) || !is_semicont(lp, j)) { report(lp, IMPORTANT, "add_SOS: SOS3+ members all have to be integer or semi-continuous.\n"); @@ -3889,7 +3706,7 @@ MYBOOL __WINAPI is_negative(lprec *lp, int colnr) (lp->orig_lowbo[colnr] < 0)) ); } -MYBOOL __WINAPI set_var_weights(lprec *lp, LPSREAL *weights) +MYBOOL __WINAPI set_var_weights(lprec *lp, REAL *weights) { if(lp->var_priority != NULL) { FREE(lp->var_priority); @@ -3914,7 +3731,7 @@ MYBOOL __WINAPI set_var_priority(lprec *lp) (lp->var_priority == NULL) && (SOS_count(lp) == 0)) { - LPSREAL *rcost = NULL; + REAL *rcost = NULL; int i, j, *colorder = NULL; allocINT(lp, &colorder, lp->columns+1, FALSE); @@ -3989,7 +3806,7 @@ MYBOOL __WINAPI is_semicont(lprec *lp, int colnr) return((lp->var_type[colnr] & ISSEMI) != 0); } -MYBOOL __WINAPI set_rh(lprec *lp, int rownr, LPSREAL value) +MYBOOL __WINAPI set_rh(lprec *lp, int rownr, REAL value) { if((rownr > lp->rows) || (rownr < 0)) { report(lp, IMPORTANT, "set_rh: Row %d out of range\n", rownr); @@ -4015,9 +3832,9 @@ MYBOOL __WINAPI set_rh(lprec *lp, int rownr, LPSREAL value) return(TRUE); } -LPSREAL __WINAPI get_rh(lprec *lp, int rownr) +REAL __WINAPI get_rh(lprec *lp, int rownr) { - LPSREAL value; + REAL value; if((rownr > lp->rows) || (rownr < 0)) { report(lp, IMPORTANT, "get_rh: Row %d out of range", rownr); @@ -4032,9 +3849,9 @@ LPSREAL __WINAPI get_rh(lprec *lp, int rownr) return(value); } -LPSREAL get_rh_upper(lprec *lp, int rownr) +REAL get_rh_upper(lprec *lp, int rownr) { - LPSREAL value, valueR; + REAL value, valueR; value = lp->orig_rhs[rownr]; if(is_chsign(lp, rownr)) { @@ -4048,9 +3865,9 @@ LPSREAL get_rh_upper(lprec *lp, int rownr) return(value); } -LPSREAL get_rh_lower(lprec *lp, int rownr) +REAL get_rh_lower(lprec *lp, int rownr) { - LPSREAL value, valueR; + REAL value, valueR; value = lp->orig_rhs[rownr]; if(is_chsign(lp, rownr)) @@ -4065,7 +3882,7 @@ LPSREAL get_rh_lower(lprec *lp, int rownr) return(value); } -MYBOOL set_rh_upper(lprec *lp, int rownr, LPSREAL value) +MYBOOL set_rh_upper(lprec *lp, int rownr, REAL value) { if(rownr > lp->rows || rownr < 1) { report(lp, IMPORTANT, "set_rh_upper: Row %d out of range", rownr); @@ -4110,7 +3927,7 @@ MYBOOL set_rh_upper(lprec *lp, int rownr, LPSREAL value) return(TRUE); } -MYBOOL set_rh_lower(lprec *lp, int rownr, LPSREAL value) +MYBOOL set_rh_lower(lprec *lp, int rownr, REAL value) { if(rownr > lp->rows || rownr < 1) { report(lp, IMPORTANT, "set_rh_lower: Row %d out of range", rownr); @@ -4156,7 +3973,7 @@ MYBOOL set_rh_lower(lprec *lp, int rownr, LPSREAL value) return(TRUE); } -MYBOOL __WINAPI set_rh_range(lprec *lp, int rownr, LPSREAL deltavalue) +MYBOOL __WINAPI set_rh_range(lprec *lp, int rownr, REAL deltavalue) { if((rownr > lp->rows) || (rownr < 1)) { report(lp, IMPORTANT, "set_rh_range: Row %d out of range", rownr); @@ -4193,7 +4010,7 @@ MYBOOL __WINAPI set_rh_range(lprec *lp, int rownr, LPSREAL deltavalue) return(TRUE); } -LPSREAL __WINAPI get_rh_range(lprec *lp, int rownr) +REAL __WINAPI get_rh_range(lprec *lp, int rownr) { if((rownr > lp->rows) || (rownr < 0)) { report(lp, IMPORTANT, "get_rh_range: row %d out of range\n", rownr); @@ -4206,10 +4023,10 @@ LPSREAL __WINAPI get_rh_range(lprec *lp, int rownr) return(unscaled_value(lp, lp->orig_upbo[rownr], rownr)); } -void __WINAPI set_rh_vec(lprec *lp, LPSREAL *rh) +void __WINAPI set_rh_vec(lprec *lp, REAL *rh) { int i; - LPSREAL rhi; + REAL rhi; for(i = 1; i <= lp->rows; i++) { rhi = rh[i]; @@ -4225,14 +4042,14 @@ MYBOOL __WINAPI str_set_rh_vec(lprec *lp, char *rh_string) { int i; MYBOOL ret = TRUE; - LPSREAL *newrh; + REAL *newrh; char *p, *newp; allocREAL(lp, &newrh, lp->rows + 1, TRUE); p = rh_string; for(i = 1; i <= lp->rows; i++) { - newrh[i] = (LPSREAL) strtod(p, &newp); + newrh[i] = (REAL) strtod(p, &newp); if(p == newp) { report(lp, IMPORTANT, "str_set_rh_vec: Bad string %s\n", p); lp->spx_status = DATAIGNORED; @@ -4320,12 +4137,7 @@ MYBOOL __WINAPI set_constr_type(lprec *lp, int rownr, int con_type) else lp->row_type[rownr] = con_type; if(oldchsign != is_chsign(lp, rownr)) { - MATrec *mat = lp->matA; - - if(mat->is_roworder) - mat_multcol(mat, rownr, -1, FALSE); - else - mat_multrow(mat, rownr, -1); + mat_multrow(lp->matA, rownr, -1); if(lp->orig_rhs[rownr] != 0) lp->orig_rhs[rownr] *= -1; set_action(&lp->spx_action, ACTION_RECOMPUTE); @@ -4361,10 +4173,10 @@ int __WINAPI get_constr_type(lprec *lp, int rownr) } return( lp->row_type[rownr] ); } -LPSREAL __WINAPI get_constr_value(lprec *lp, int rownr, int count, LPSREAL *primsolution, int *nzindex) +REAL __WINAPI get_constr_value(lprec *lp, int rownr, int count, REAL *primsolution, int *nzindex) { int i; - LPSREAL value = 0.0; + REAL value = 0.0; MATrec *mat = lp->matA; if((rownr < 0) || (rownr > get_Nrows(lp))) @@ -4417,7 +4229,7 @@ STATIC char *get_str_constr_class(lprec *lp, int con_class) switch(con_class) { case ROWCLASS_Unknown: return("Unknown"); case ROWCLASS_Objective: return("Objective"); - case ROWCLASS_GeneralREAL: return("General LPSREAL"); + case ROWCLASS_GeneralREAL: return("General REAL"); case ROWCLASS_GeneralMIP: return("General MIP"); case ROWCLASS_GeneralINT: return("General INT"); case ROWCLASS_GeneralBIN: return("General BIN"); @@ -4447,7 +4259,7 @@ STATIC int get_constr_class(lprec *lp, int rownr) xBIN = 0, xINT = 0, xREAL = 0; int j, elmnr, elmend, nelm; MYBOOL chsign; - LPSREAL a; + REAL a; MATrec *mat = lp->matA; if((rownr < 1) || (rownr > lp->rows)) { @@ -4526,11 +4338,10 @@ STATIC int get_constr_class(lprec *lp, int rownr) return( j ); } -LPSREAL __WINAPI get_mat(lprec *lp, int rownr, int colnr) +REAL __WINAPI get_mat(lprec *lp, int rownr, int colnr) { - LPSREAL value; + REAL value; int elmnr; - int colnr1 = colnr, rownr1 = rownr; if((rownr < 0) || (rownr > lp->rows)) { report(lp, IMPORTANT, "get_mat: Row %d out of range", rownr); @@ -4540,15 +4351,18 @@ LPSREAL __WINAPI get_mat(lprec *lp, int rownr, int colnr) report(lp, IMPORTANT, "get_mat: Column %d out of range", colnr); return(0); } + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "get_mat: Cannot read a matrix value while in row entry mode.\n"); + return(0); + } + if(rownr == 0) { value = lp->orig_obj[colnr]; value = my_chsign(is_chsign(lp, rownr), value); value = unscaled_mat(lp, value, rownr, colnr); } else { - if(lp->matA->is_roworder) - swapINT(&colnr1, &rownr1); - elmnr = mat_findelm(lp->matA, rownr1, colnr1); + elmnr = mat_findelm(lp->matA, rownr, colnr); if(elmnr >= 0) { MATrec *mat = lp->matA; value = my_chsign(is_chsign(lp, rownr), COL_MAT_VALUE(elmnr)); @@ -4560,11 +4374,11 @@ LPSREAL __WINAPI get_mat(lprec *lp, int rownr, int colnr) return(value); } -LPSREAL __WINAPI get_mat_byindex(lprec *lp, int matindex, MYBOOL isrow, MYBOOL adjustsign) +REAL __WINAPI get_mat_byindex(lprec *lp, int matindex, MYBOOL isrow, MYBOOL adjustsign) /* Note that this function does not adjust for sign-changed GT constraints! */ { int *rownr, *colnr; - LPSREAL *value, result; + REAL *value, result; mat_get_data(lp, matindex, isrow, &rownr, &colnr, &value); if(adjustsign) @@ -4577,11 +4391,20 @@ LPSREAL __WINAPI get_mat_byindex(lprec *lp, int matindex, MYBOOL isrow, MYBOOL a return( result ); } -static int mat_getrow(lprec *lp, int rownr, LPSREAL *row, int *colno) +int __WINAPI get_rowex(lprec *lp, int rownr, REAL *row, int *colno) { MYBOOL isnz; int j, countnz = 0; - LPSREAL a; + REAL a; + + if((rownr < 0) || (rownr > lp->rows)) { + report(lp, IMPORTANT, "get_rowex: Row %d out of range\n", rownr); + return( -1 ); + } + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "get_rowex: Cannot return a matrix row while in row entry mode.\n"); + return( -1 ); + } if((rownr == 0) || !mat_validate(lp->matA)) { for(j = 1; j <= lp->columns; j++) { @@ -4598,35 +4421,18 @@ static int mat_getrow(lprec *lp, int rownr, LPSREAL *row, int *colno) } } else { - MYBOOL chsign = FALSE; + MYBOOL chsign; int ie, i; MATrec *mat = lp->matA; - if(colno == NULL) - MEMCLEAR(row, lp->columns+1); - if(mat->is_roworder) { - /* Add the objective function */ - a = get_mat(lp, 0, rownr); - if(colno == NULL) { - row[countnz] = a; - if(a != 0) - countnz++; - } - else if(a != 0) { - row[countnz] = a; - colno[countnz] = 0; - countnz++; - } - } i = mat->row_end[rownr-1]; ie = mat->row_end[rownr]; - if(!lp->matA->is_roworder) - chsign = is_chsign(lp, rownr); + chsign = is_chsign(lp, rownr); + if(colno == NULL) + MEMCLEAR(row, lp->columns+1); for(; i < ie; i++) { j = ROW_MAT_COLNR(i); a = get_mat_byindex(lp, i, TRUE, FALSE); - if(lp->matA->is_roworder) - chsign = is_chsign(lp, j); a = my_chsign(chsign, a); if(colno == NULL) row[j] = a; @@ -4640,27 +4446,39 @@ static int mat_getrow(lprec *lp, int rownr, LPSREAL *row, int *colno) return( countnz ); } -static int mat_getcolumn(lprec *lp, int colnr, LPSREAL *column, int *nzrow) +MYBOOL __WINAPI get_row(lprec *lp, int rownr, REAL *row) +{ + return((MYBOOL) (get_rowex(lp, rownr, row, NULL) >= 0) ); +} + +int __WINAPI get_columnex(lprec *lp, int colnr, REAL *column, int *nzrow) { int n = 0, i, ii, ie, *rownr; - LPSREAL hold, *value; + REAL hold, *value; MATrec *mat = lp->matA; + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "get_columnex: Column %d out of range\n", colnr); + return( -1 ); + } + if(mat->is_roworder) { + report(lp, IMPORTANT, "get_columnex: Cannot return a column while in row entry mode\n"); + return( -1 ); + } + + /* Add the objective function */ if(nzrow == NULL) MEMCLEAR(column, lp->rows + 1); - if(!mat->is_roworder) { - /* Add the objective function */ - hold = get_mat(lp, 0, colnr); - if(nzrow == NULL) { - column[n] = hold; - if(hold != 0) - n++; - } - else if(hold != 0) { - column[n] = hold; - nzrow[n] = 0; + hold = get_mat(lp, 0, colnr); + if(nzrow == NULL) { + column[n] = hold; + if(hold != 0) n++; - } + } + else if(hold != 0) { + column[n] = hold; + nzrow[n] = 0; + n++; } i = lp->matA->col_end[colnr - 1]; @@ -4673,7 +4491,7 @@ static int mat_getcolumn(lprec *lp, int colnr, LPSREAL *column, int *nzrow) i++, rownr += matRowColStep, value += matValueStep) { ii = *rownr; - hold = my_chsign(is_chsign(lp, (mat->is_roworder) ? colnr : ii), *value); + hold = my_chsign(is_chsign(lp, ii), *value); hold = unscaled_mat(lp, hold, ii, colnr); if(nzrow == NULL) column[ii] = hold; @@ -4686,43 +4504,12 @@ static int mat_getcolumn(lprec *lp, int colnr, LPSREAL *column, int *nzrow) return( n ); } -int __WINAPI get_columnex(lprec *lp, int colnr, LPSREAL *column, int *nzrow) -{ - if((colnr > lp->columns) || (colnr < 1)) { - report(lp, IMPORTANT, "get_columnex: Column %d out of range\n", colnr); - return( -1 ); - } - - if(lp->matA->is_roworder) - return(mat_getrow(lp, colnr, column, nzrow)); - else - return(mat_getcolumn(lp, colnr, column, nzrow)); -} - -MYBOOL __WINAPI get_column(lprec *lp, int colnr, LPSREAL *column) +MYBOOL __WINAPI get_column(lprec *lp, int colnr, REAL *column) { return( (MYBOOL) (get_columnex(lp, colnr, column, NULL) >= 0) ); } -int __WINAPI get_rowex(lprec *lp, int rownr, LPSREAL *row, int *colno) -{ - if((rownr < 0) || (rownr > lp->rows)) { - report(lp, IMPORTANT, "get_rowex: Row %d out of range\n", rownr); - return( -1 ); - } - - if(rownr != 0 && lp->matA->is_roworder) - return(mat_getcolumn(lp, rownr, row, colno)); - else - return(mat_getrow(lp, rownr, row, colno)); -} - -MYBOOL __WINAPI get_row(lprec *lp, int rownr, LPSREAL *row) -{ - return((MYBOOL) (get_rowex(lp, rownr, row, NULL) >= 0) ); -} - -STATIC void set_OF_override(lprec *lp, LPSREAL *ofVector) +STATIC void set_OF_override(lprec *lp, REAL *ofVector) /* The purpose of this function is to set, or clear if NULL, the ofVector[0..columns] as the active objective function instead of the one stored in the A-matrix. See also lag_solve().*/ @@ -4730,10 +4517,12 @@ STATIC void set_OF_override(lprec *lp, LPSREAL *ofVector) lp->obj = ofVector; } -MYBOOL modifyOF1(lprec *lp, int index, LPSREAL *ofValue, LPSREAL mult) +MYBOOL modifyOF1(lprec *lp, int index, REAL *ofValue, REAL mult) /* Adjust objective function values for primal/dual phase 1, if appropriate */ { MYBOOL accept = TRUE; +/* static MYBOOL accept; + accept = TRUE; */ /* Primal simplex: Set user variables to zero or BigM-scaled */ if(((lp->simplex_mode & SIMPLEX_Phase1_PRIMAL) != 0) && (abs(lp->P1extraDim) > 0)) { @@ -4783,10 +4572,10 @@ MYBOOL modifyOF1(lprec *lp, int index, LPSREAL *ofValue, LPSREAL mult) return( accept ); } -STATIC void set_OF_p1extra(lprec *lp, LPSREAL p1extra) +STATIC void set_OF_p1extra(lprec *lp, REAL p1extra) { int i; - LPSREAL *value; + REAL *value; if(lp->spx_trace) report(lp, DETAILED, "set_OF_p1extra: Set dual objective offset to %g at iter %.0f.\n", @@ -4806,10 +4595,10 @@ STATIC void unset_OF_p1extra(lprec *lp) FREE(lp->obj); } -LPSREAL __WINAPI get_OF_active(lprec *lp, int varnr, LPSREAL mult) +REAL __WINAPI get_OF_active(lprec *lp, int varnr, REAL mult) { int colnr = varnr - lp->rows; - LPSREAL holdOF = 0; + REAL holdOF = 0; #ifdef Paranoia if((colnr <= 0) || (colnr > lp->columns)) { @@ -4833,7 +4622,7 @@ STATIC MYBOOL is_OF_nz(lprec *lp, int colnr) return( (MYBOOL) (lp->orig_obj[colnr] != 0) ); } -STATIC int singleton_column(lprec *lp, int row_nr, LPSREAL *column, int *nzlist, LPSREAL value, int *maxabs) +STATIC int singleton_column(lprec *lp, int row_nr, REAL *column, int *nzlist, REAL value, int *maxabs) { int nz = 1; @@ -4851,12 +4640,12 @@ STATIC int singleton_column(lprec *lp, int row_nr, LPSREAL *column, int *nzlist, return( nz ); } -STATIC int expand_column(lprec *lp, int col_nr, LPSREAL *column, int *nzlist, LPSREAL mult, int *maxabs) +STATIC int expand_column(lprec *lp, int col_nr, REAL *column, int *nzlist, REAL mult, int *maxabs) { int i, ie, j, maxidx, nzcount; - LPSREAL value, maxval; + REAL value, maxval; MATrec *mat = lp->matA; - LPSREAL *matValue; + REAL *matValue; int *matRownr; /* Retrieve a column from the user data matrix A */ @@ -4931,9 +4720,9 @@ STATIC int expand_column(lprec *lp, int col_nr, LPSREAL *column, int *nzlist, LP /* Retrieve a column vector from the data matrix [1..rows, rows+1..rows+columns]; needs __WINAPI call model since it may be called from BFPs */ -int __WINAPI obtain_column(lprec *lp, int varin, LPSREAL *pcol, int *nzlist, int *maxabs) +int __WINAPI obtain_column(lprec *lp, int varin, REAL *pcol, int *nzlist, int *maxabs) { - LPSREAL value = my_chsign(lp->is_lower[varin], -1); + REAL value = my_chsign(lp->is_lower[varin], -1); if(varin > lp->rows) { varin -= lp->rows; varin = expand_column(lp, varin, pcol, nzlist, value, maxabs); @@ -5093,8 +4882,8 @@ MYBOOL set_callbacks(lprec *lp) lp->put_bb_branchfunc = put_bb_branchfunc; lp->put_logfunc = put_logfunc; lp->put_msgfunc = put_msgfunc; - lp->read_LP = read_LP; - lp->read_MPS = read_MPS; + lp->read_LPhandle = LP_readhandle; + lp->read_MPShandle = MPS_readhandle; lp->read_XLI = read_XLI; lp->read_basis = read_basis; lp->reset_basis = reset_basis; @@ -5300,7 +5089,7 @@ MYBOOL __WINAPI set_BFP(lprec *lp, char *filename) GetProcAddress(lp->hBFP, "bfp_compatible"); if(lp->bfp_compatible == NULL) result = LIB_NOINFO; - else if(lp->bfp_compatible(lp, BFPVERSION, MAJORVERSION, sizeof(LPSREAL))) { + else if(lp->bfp_compatible(lp, BFPVERSION, MAJORVERSION, sizeof(REAL))) { lp->bfp_name = (BFPchar *) GetProcAddress(lp->hBFP, "bfp_name"); @@ -5393,117 +5182,81 @@ MYBOOL __WINAPI set_BFP(lprec *lp, char *filename) /* If the handle is valid, try to get the function addresses. */ if(lp->hBFP != NULL) { - *(void **)(&lp->bfp_compatible) = dlsym(lp->hBFP, "bfp_compatible"); - // lp->bfp_compatible = (BFPbool_lpintintint)dlsym(lp->hBFP, "bfp_compatible"); - if(lp->bfp_compatible == NULL) { + lp->bfp_compatible = (BFPbool_lpintintint *) + dlsym(lp->hBFP, "bfp_compatible"); + if(lp->bfp_compatible == NULL) result = LIB_NOINFO; - } else if(lp->bfp_compatible(lp, BFPVERSION, MAJORVERSION, sizeof(LPSREAL))) { - /* - lp->bfp_name = (BFPchar *) - dlsym(lp->hBFP, "bfp_name"); - lp->bfp_free = (BFP_lp *) - dlsym(lp->hBFP, "bfp_free"); - lp->bfp_resize = (BFPbool_lpint *) - dlsym(lp->hBFP, "bfp_resize"); - lp->bfp_nonzeros = (BFPint_lpbool *) - dlsym(lp->hBFP, "bfp_nonzeros"); - lp->bfp_memallocated = (BFPint_lp *) - dlsym(lp->hBFP, "bfp_memallocated"); - lp->bfp_restart = (BFPbool_lp *) - dlsym(lp->hBFP, "bfp_restart"); - lp->bfp_mustrefactorize = (BFPbool_lp *) - dlsym(lp->hBFP, "bfp_mustrefactorize"); - lp->bfp_preparefactorization = (BFPint_lp *) - dlsym(lp->hBFP, "bfp_preparefactorization"); - lp->bfp_factorize = (BFPint_lpintintboolbool *) - dlsym(lp->hBFP, "bfp_factorize"); - lp->bfp_finishupdate = (BFPbool_lpbool *) - dlsym(lp->hBFP, "bfp_finishupdate"); - lp->bfp_ftran_normal = (BFP_lprealint *) - dlsym(lp->hBFP, "bfp_ftran_normal"); - lp->bfp_ftran_prepare = (BFP_lprealint *) - dlsym(lp->hBFP, "bfp_ftran_prepare"); - lp->bfp_btran_normal = (BFP_lprealint *) - dlsym(lp->hBFP, "bfp_btran_normal"); - lp->bfp_status = (BFPint_lp *) - dlsym(lp->hBFP, "bfp_status"); - lp->bfp_implicitslack = (BFPbool_lp *) - dlsym(lp->hBFP, "bfp_implicitslack"); - lp->bfp_indexbase = (BFPint_lp *) - dlsym(lp->hBFP, "bfp_indexbase"); - lp->bfp_rowoffset = (BFPint_lp *) - dlsym(lp->hBFP, "bfp_rowoffset"); - lp->bfp_pivotmax = (BFPint_lp *) - dlsym(lp->hBFP, "bfp_pivotmax"); - lp->bfp_init = (BFPbool_lpintintchar *) - dlsym(lp->hBFP, "bfp_init"); - lp->bfp_pivotalloc = (BFPbool_lpint *) - dlsym(lp->hBFP, "bfp_pivotalloc"); - lp->bfp_colcount = (BFPint_lp *) - dlsym(lp->hBFP, "bfp_colcount"); - lp->bfp_canresetbasis = (BFPbool_lp *) - dlsym(lp->hBFP, "bfp_canresetbasis"); - lp->bfp_finishfactorization = (BFP_lp *) - dlsym(lp->hBFP, "bfp_finishfactorization"); - lp->bfp_updaterefactstats = (BFP_lp *) - dlsym(lp->hBFP, "bfp_updaterefactstats"); - lp->bfp_prepareupdate = (BFPlreal_lpintintreal *) - dlsym(lp->hBFP, "bfp_prepareupdate"); - lp->bfp_pivotRHS = (BFPreal_lplrealreal *) - dlsym(lp->hBFP, "bfp_pivotRHS"); - lp->bfp_btran_double = (BFP_lprealintrealint *) - dlsym(lp->hBFP, "bfp_btran_double"); - lp->bfp_efficiency = (BFPreal_lp *) - dlsym(lp->hBFP, "bfp_efficiency"); - lp->bfp_pivotvector = (BFPrealp_lp *) - dlsym(lp->hBFP, "bfp_pivotvector"); - lp->bfp_pivotcount = (BFPint_lp *) - dlsym(lp->hBFP, "bfp_pivotcount"); - lp->bfp_refactcount = (BFPint_lpint *) - dlsym(lp->hBFP, "bfp_refactcount"); - lp->bfp_isSetI = (BFPbool_lp *) - dlsym(lp->hBFP, "bfp_isSetI"); - lp->bfp_findredundant = (BFPint_lpintrealcbintint *) - dlsym(lp->hBFP, "bfp_findredundant"); - */ - - MAKE_PEDANTIC_HAPPY(lp->bfp_name) = dlsym(lp->hBFP, "bfp_name"); - MAKE_PEDANTIC_HAPPY(lp->bfp_free) = dlsym(lp->hBFP, "bfp_free"); - MAKE_PEDANTIC_HAPPY(lp->bfp_resize) = dlsym(lp->hBFP, "bfp_resize"); - MAKE_PEDANTIC_HAPPY(lp->bfp_nonzeros) = dlsym(lp->hBFP, "bfp_nonzeros"); - MAKE_PEDANTIC_HAPPY(lp->bfp_memallocated) = dlsym(lp->hBFP, "bfp_memallocated"); - MAKE_PEDANTIC_HAPPY(lp->bfp_restart) = dlsym(lp->hBFP, "bfp_restart"); - MAKE_PEDANTIC_HAPPY(lp->bfp_mustrefactorize) = dlsym(lp->hBFP, "bfp_mustrefactorize"); - MAKE_PEDANTIC_HAPPY(lp->bfp_preparefactorization) = dlsym(lp->hBFP, "bfp_preparefactorization"); - MAKE_PEDANTIC_HAPPY(lp->bfp_factorize) = dlsym(lp->hBFP, "bfp_factorize"); - MAKE_PEDANTIC_HAPPY(lp->bfp_finishupdate) = dlsym(lp->hBFP, "bfp_finishupdate"); - MAKE_PEDANTIC_HAPPY(lp->bfp_ftran_normal) = dlsym(lp->hBFP, "bfp_ftran_normal"); - MAKE_PEDANTIC_HAPPY(lp->bfp_ftran_prepare) = dlsym(lp->hBFP, "bfp_ftran_prepare"); - MAKE_PEDANTIC_HAPPY(lp->bfp_btran_normal) = dlsym(lp->hBFP, "bfp_btran_normal"); - MAKE_PEDANTIC_HAPPY(lp->bfp_status) = dlsym(lp->hBFP, "bfp_status"); - MAKE_PEDANTIC_HAPPY(lp->bfp_implicitslack) = dlsym(lp->hBFP, "bfp_implicitslack"); - MAKE_PEDANTIC_HAPPY(lp->bfp_indexbase) = dlsym(lp->hBFP, "bfp_indexbase"); - MAKE_PEDANTIC_HAPPY(lp->bfp_rowoffset) = dlsym(lp->hBFP, "bfp_rowoffset"); - MAKE_PEDANTIC_HAPPY(lp->bfp_pivotmax) = dlsym(lp->hBFP, "bfp_pivotmax"); - MAKE_PEDANTIC_HAPPY(lp->bfp_init) = dlsym(lp->hBFP, "bfp_init"); - MAKE_PEDANTIC_HAPPY(lp->bfp_pivotalloc) = dlsym(lp->hBFP, "bfp_pivotalloc"); - MAKE_PEDANTIC_HAPPY(lp->bfp_colcount) = dlsym(lp->hBFP, "bfp_colcount"); - MAKE_PEDANTIC_HAPPY(lp->bfp_canresetbasis) = dlsym(lp->hBFP, "bfp_canresetbasis"); - MAKE_PEDANTIC_HAPPY(lp->bfp_finishfactorization) = dlsym(lp->hBFP, "bfp_finishfactorization"); - MAKE_PEDANTIC_HAPPY(lp->bfp_updaterefactstats) = dlsym(lp->hBFP, "bfp_updaterefactstats"); - MAKE_PEDANTIC_HAPPY(lp->bfp_prepareupdate) = dlsym(lp->hBFP, "bfp_prepareupdate"); - MAKE_PEDANTIC_HAPPY(lp->bfp_pivotRHS) = dlsym(lp->hBFP, "bfp_pivotRHS"); - MAKE_PEDANTIC_HAPPY(lp->bfp_btran_double) = dlsym(lp->hBFP, "bfp_btran_double"); - MAKE_PEDANTIC_HAPPY(lp->bfp_efficiency) = dlsym(lp->hBFP, "bfp_efficiency"); - MAKE_PEDANTIC_HAPPY(lp->bfp_pivotvector) = dlsym(lp->hBFP, "bfp_pivotvector"); - MAKE_PEDANTIC_HAPPY(lp->bfp_pivotcount) = dlsym(lp->hBFP, "bfp_pivotcount"); - MAKE_PEDANTIC_HAPPY(lp->bfp_refactcount) = dlsym(lp->hBFP, "bfp_refactcount"); - MAKE_PEDANTIC_HAPPY(lp->bfp_isSetI) = dlsym(lp->hBFP, "bfp_isSetI"); - MAKE_PEDANTIC_HAPPY(lp->bfp_findredundant) = dlsym(lp->hBFP, "bfp_findredundant"); - - } else { - result = LIB_VERINVALID; + else if(lp->bfp_compatible(lp, BFPVERSION, MAJORVERSION, sizeof(REAL))) { + + lp->bfp_name = (BFPchar *) + dlsym(lp->hBFP, "bfp_name"); + lp->bfp_free = (BFP_lp *) + dlsym(lp->hBFP, "bfp_free"); + lp->bfp_resize = (BFPbool_lpint *) + dlsym(lp->hBFP, "bfp_resize"); + lp->bfp_nonzeros = (BFPint_lpbool *) + dlsym(lp->hBFP, "bfp_nonzeros"); + lp->bfp_memallocated = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_memallocated"); + lp->bfp_restart = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_restart"); + lp->bfp_mustrefactorize = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_mustrefactorize"); + lp->bfp_preparefactorization = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_preparefactorization"); + lp->bfp_factorize = (BFPint_lpintintboolbool *) + dlsym(lp->hBFP, "bfp_factorize"); + lp->bfp_finishupdate = (BFPbool_lpbool *) + dlsym(lp->hBFP, "bfp_finishupdate"); + lp->bfp_ftran_normal = (BFP_lprealint *) + dlsym(lp->hBFP, "bfp_ftran_normal"); + lp->bfp_ftran_prepare = (BFP_lprealint *) + dlsym(lp->hBFP, "bfp_ftran_prepare"); + lp->bfp_btran_normal = (BFP_lprealint *) + dlsym(lp->hBFP, "bfp_btran_normal"); + lp->bfp_status = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_status"); + lp->bfp_implicitslack = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_implicitslack"); + lp->bfp_indexbase = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_indexbase"); + lp->bfp_rowoffset = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_rowoffset"); + lp->bfp_pivotmax = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_pivotmax"); + lp->bfp_init = (BFPbool_lpintintchar *) + dlsym(lp->hBFP, "bfp_init"); + lp->bfp_pivotalloc = (BFPbool_lpint *) + dlsym(lp->hBFP, "bfp_pivotalloc"); + lp->bfp_colcount = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_colcount"); + lp->bfp_canresetbasis = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_canresetbasis"); + lp->bfp_finishfactorization = (BFP_lp *) + dlsym(lp->hBFP, "bfp_finishfactorization"); + lp->bfp_updaterefactstats = (BFP_lp *) + dlsym(lp->hBFP, "bfp_updaterefactstats"); + lp->bfp_prepareupdate = (BFPlreal_lpintintreal *) + dlsym(lp->hBFP, "bfp_prepareupdate"); + lp->bfp_pivotRHS = (BFPreal_lplrealreal *) + dlsym(lp->hBFP, "bfp_pivotRHS"); + lp->bfp_btran_double = (BFP_lprealintrealint *) + dlsym(lp->hBFP, "bfp_btran_double"); + lp->bfp_efficiency = (BFPreal_lp *) + dlsym(lp->hBFP, "bfp_efficiency"); + lp->bfp_pivotvector = (BFPrealp_lp *) + dlsym(lp->hBFP, "bfp_pivotvector"); + lp->bfp_pivotcount = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_pivotcount"); + lp->bfp_refactcount = (BFPint_lpint *) + dlsym(lp->hBFP, "bfp_refactcount"); + lp->bfp_isSetI = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_isSetI"); + lp->bfp_findredundant = (BFPint_lpintrealcbintint *) + dlsym(lp->hBFP, "bfp_findredundant"); } + else + result = LIB_VERINVALID; } #endif else @@ -5570,6 +5323,8 @@ MYBOOL __WINAPI set_BFP(lprec *lp, char *filename) return( (MYBOOL) (result == LIB_LOADED)); } +#include +#include /* External language interface routines */ /* DON'T MODIFY */ @@ -5583,10 +5338,10 @@ lprec * __WINAPI read_XLI(char *xliname, char *modelname, char *dataname, char * lp->verbose = verbose; if(!set_XLI(lp, xliname)) { free_lp(&lp); - printf("read_XLI: No valid XLI package selected or available.\n"); + Rprintf("read_XLI: No valid XLI package selected or available.\n"); } else { - if(!lp->xli_readmodel(lp, modelname, (dataname != NULL) && (*dataname != 0) ? dataname : NULL, options, verbose)) + if(!lp->xli_readmodel(lp, modelname, dataname, options, verbose)) free_lp(&lp); } } @@ -5656,7 +5411,7 @@ MYBOOL __WINAPI set_XLI(lprec *lp, char *filename) GetProcAddress(lp->hXLI, "xli_compatible"); if(lp->xli_compatible == NULL) result = LIB_NOINFO; - else if(lp->xli_compatible(lp, XLIVERSION, MAJORVERSION, sizeof(LPSREAL))) { + else if(lp->xli_compatible(lp, XLIVERSION, MAJORVERSION, sizeof(REAL))) { lp->xli_name = (XLIchar *) GetProcAddress(lp->hXLI, "xli_name"); @@ -5689,16 +5444,21 @@ MYBOOL __WINAPI set_XLI(lprec *lp, char *filename) /* If the handle is valid, try to get the function addresses. */ if(lp->hXLI != NULL) { - MAKE_PEDANTIC_HAPPY(lp->xli_compatible) = dlsym(lp->hXLI, "xli_compatible"); - if(lp->xli_compatible == NULL) { + lp->xli_compatible = (XLIbool_lpintintint *) + dlsym(lp->hXLI, "xli_compatible"); + if(lp->xli_compatible == NULL) result = LIB_NOINFO; - } else if(lp->xli_compatible(lp, XLIVERSION, MAJORVERSION, sizeof(LPSREAL))) { - MAKE_PEDANTIC_HAPPY(lp->xli_name) = dlsym(lp->hXLI, "xli_name"); - MAKE_PEDANTIC_HAPPY(lp->xli_readmodel) = dlsym(lp->hXLI, "xli_readmodel"); - MAKE_PEDANTIC_HAPPY(lp->xli_writemodel) = dlsym(lp->hXLI, "xli_writemodel"); - } else { - result = LIB_VERINVALID; + else if(lp->xli_compatible(lp, XLIVERSION, MAJORVERSION, sizeof(REAL))) { + + lp->xli_name = (XLIchar *) + dlsym(lp->hXLI, "xli_name"); + lp->xli_readmodel = (XLIbool_lpcharcharcharint *) + dlsym(lp->hXLI, "xli_readmodel"); + lp->xli_writemodel = (XLIbool_lpcharcharbool *) + dlsym(lp->hXLI, "xli_writemodel"); } + else + result = LIB_VERINVALID; } #endif else @@ -5736,37 +5496,33 @@ MYBOOL __WINAPI set_XLI(lprec *lp, char *filename) } -STATIC int get_basisOF(lprec *lp, int coltarget[], LPSREAL crow[], int colno[]) +STATIC int get_basisOF(lprec *lp, int coltarget[], REAL crow[], int colno[]) /* Fill vector of basic OF values or subtract incoming values from these. This function is called twice during reduced cost updates when the basis does not contain the basic OF vector as the top row. The colno[] array is filled with the count of non-zero values and the index to those. */ { int i, n = lp->rows, nz = 0; - LPSREAL *obj = lp->obj; - register LPSREAL epsvalue = lp->epsvalue; + REAL *obj = lp->obj; + register REAL epsvalue = lp->epsvalue; /* Compute offset over the specified objective indeces (step 2) */ if(coltarget != NULL) { register int ix, m = coltarget[0]; - register LPSREAL value; + register REAL value; for(i = 1, coltarget++; i <= m; i++, coltarget++) { ix = *coltarget; - /* Finalize the computation of the reduced costs, based on the format that - duals are computed as negatives, ref description for step 1 above */ - value = crow[ix]; + value = -crow[ix]; if(ix > n) value += obj[ix - n]; + crow[ix] = value; /* if(value != 0) { */ if(fabs(value) > epsvalue) { nz++; if(colno != NULL) colno[nz] = ix; } - else - value = 0.0; - crow[ix] = value; } } @@ -5776,13 +5532,10 @@ STATIC int get_basisOF(lprec *lp, int coltarget[], LPSREAL crow[], int colno[]) for(i = 1, crow++, basvar++; i <= n; i++, crow++, basvar++) { - /* Load the objective value of the active basic variable; note that we - change the sign of the value to maintain computational compatibility with - the calculation of duals using in-basis storage of the basic OF values */ if(*basvar <= n) *crow = 0; else - *crow = -obj[(*basvar) - n]; + *crow = obj[(*basvar) - n]; if((*crow) != 0) { /* if(fabs(*crow) > epsvalue) { */ nz++; @@ -5831,11 +5584,9 @@ int __WINAPI get_basiscolumn(lprec *lp, int j, int rn[], double bj[]) return( k ); } -MYBOOL __WINAPI get_primal_solution(lprec *lp, LPSREAL *pv) +MYBOOL __WINAPI get_primal_solution(lprec *lp, REAL *pv) { - if(lp->spx_status == OPTIMAL) - ; - else if(!lp->basis_valid) { + if(!lp->basis_valid) { report(lp, CRITICAL, "get_primal_solution: Not a valid basis"); return(FALSE); } @@ -5844,15 +5595,15 @@ MYBOOL __WINAPI get_primal_solution(lprec *lp, LPSREAL *pv) return(TRUE); } -MYBOOL __WINAPI get_ptr_primal_solution(lprec *lp, LPSREAL **pv) +MYBOOL __WINAPI get_ptr_primal_solution(lprec *lp, REAL **pv) { *pv = lp->best_solution; return(TRUE); } -MYBOOL __WINAPI get_dual_solution(lprec *lp, LPSREAL *rc) +MYBOOL __WINAPI get_dual_solution(lprec *lp, REAL *rc) { - LPSREAL *duals; + REAL *duals; MYBOOL ret; if(!lp->basis_valid) { @@ -5867,7 +5618,7 @@ MYBOOL __WINAPI get_dual_solution(lprec *lp, LPSREAL *rc) return(ret); } -MYBOOL __WINAPI get_ptr_dual_solution(lprec *lp, LPSREAL **rc) +MYBOOL __WINAPI get_ptr_dual_solution(lprec *lp, REAL **rc) { MYBOOL ret = lp->basis_valid; @@ -5888,7 +5639,7 @@ MYBOOL __WINAPI get_ptr_dual_solution(lprec *lp, LPSREAL **rc) return(ret); } -MYBOOL __WINAPI get_lambda(lprec *lp, LPSREAL *lambda) +MYBOOL __WINAPI get_lambda(lprec *lp, REAL *lambda) { if(!lp->basis_valid || (get_Lrows(lp) == 0)) { report(lp, CRITICAL, "get_lambda: Not a valid basis"); @@ -5899,7 +5650,7 @@ MYBOOL __WINAPI get_lambda(lprec *lp, LPSREAL *lambda) return(TRUE); } -MYBOOL __WINAPI get_ptr_lambda(lprec *lp, LPSREAL **lambda) +MYBOOL __WINAPI get_ptr_lambda(lprec *lp, REAL **lambda) { *lambda = lp->lambda; return(TRUE); @@ -5924,12 +5675,12 @@ int __WINAPI get_lp_index(lprec *lp, int orig_index) return(orig_index-lp->presolve_undo->orig_rows); } -MYBOOL __WINAPI is_feasible(lprec *lp, LPSREAL *values, LPSREAL threshold) +MYBOOL __WINAPI is_feasible(lprec *lp, REAL *values, REAL threshold) /* Recommend to use threshold = lp->epspivot */ { int i, j, elmnr, ie; - LPSREAL *this_rhs, dist; - LPSREAL *value; + REAL *this_rhs, dist; + REAL *value; int *rownr; MATrec *mat = lp->matA; @@ -5941,7 +5692,7 @@ MYBOOL __WINAPI is_feasible(lprec *lp, LPSREAL *values, LPSREAL threshold) } } - this_rhs = (LPSREAL *) mempool_obtainVector(lp->workarrays, lp->rows+1, sizeof(*this_rhs)); + this_rhs = (REAL *) mempool_obtainVector(lp->workarrays, lp->rows+1, sizeof(*this_rhs)); /* allocREAL(lp, &this_rhs, lp->rows + 1, TRUE); */ for(j = 1; j <= lp->columns; j++) { elmnr = mat->col_end[j - 1]; @@ -5965,13 +5716,13 @@ MYBOOL __WINAPI is_feasible(lprec *lp, LPSREAL *values, LPSREAL threshold) return(TRUE); } -int __WINAPI column_in_lp(lprec *lp, LPSREAL *testcolumn) +int __WINAPI column_in_lp(lprec *lp, REAL *testcolumn) { int i, j, je, colnr = 0; int nz, ident = 1; MATrec *mat = lp->matA; int *matRownr; - LPSREAL value, *matValue; + REAL value, *matValue; for(nz = 0, i = 1; i <= lp->rows; i++) if(fabs(testcolumn[i]) > lp->epsvalue) nz++; @@ -6116,6 +5867,7 @@ char * __WINAPI get_row_name(lprec *lp, int rownr) char * __WINAPI get_origrow_name(lprec *lp, int rownr) { MYBOOL newrow; + static char name[50]; char *ptr; newrow = (MYBOOL) (rownr < 0); @@ -6138,14 +5890,11 @@ char * __WINAPI get_origrow_name(lprec *lp, int rownr) ptr = lp->row_name[rownr]->name; } else { - if(lp->rowcol_name == NULL) - if (!allocCHAR(lp, &lp->rowcol_name, 20, FALSE)) - return(NULL); - ptr = lp->rowcol_name; if(newrow) - sprintf(ptr, ROWNAMEMASK2, rownr); + snprintf(name, sizeof(name), ROWNAMEMASK2, rownr); else - sprintf(ptr, ROWNAMEMASK, rownr); + snprintf(name, sizeof(name), ROWNAMEMASK, rownr); + ptr = name; } return(ptr); } @@ -6186,6 +5935,7 @@ char * __WINAPI get_origcol_name(lprec *lp, int colnr) { MYBOOL newcol; char *ptr; + static char name[50]; newcol = (MYBOOL) (colnr < 0); colnr = abs(colnr); @@ -6206,14 +5956,11 @@ char * __WINAPI get_origcol_name(lprec *lp, int colnr) ptr = lp->col_name[colnr]->name; } else { - if(lp->rowcol_name == NULL) - if (!allocCHAR(lp, &lp->rowcol_name, 20, FALSE)) - return(NULL); - ptr = lp->rowcol_name; if(newcol) - sprintf(ptr, COLNAMEMASK2, colnr); + snprintf((char *) name, sizeof(name), COLNAMEMASK2, colnr); else - sprintf(ptr, COLNAMEMASK, colnr); + snprintf((char *) name, sizeof(name), COLNAMEMASK, colnr); + ptr = name; } return(ptr); } @@ -6253,12 +6000,12 @@ STATIC int GUB_count(lprec *lp) return( lp->GUB->sos_count ); } -STATIC LPSREAL compute_violation(lprec *lp, int row_nr) +STATIC REAL compute_violation(lprec *lp, int row_nr) /* Returns the bound violation of a given basic variable; the return value is negative if it is below is lower bound, it is positive if it is greater than the upper bound, and zero otherwise. */ { - LPSREAL value, test; + REAL value, test; value = lp->rhs[row_nr]; row_nr = lp->var_basic[row_nr]; @@ -6273,10 +6020,10 @@ STATIC LPSREAL compute_violation(lprec *lp, int row_nr) return( test ); } -STATIC LPSREAL feasibilityOffset(lprec *lp, MYBOOL isdual) +STATIC REAL feasibilityOffset(lprec *lp, MYBOOL isdual) { int i, j; - LPSREAL f, Extra; + REAL f, Extra; Extra = 0; if(isdual) { @@ -6328,12 +6075,12 @@ STATIC LPSREAL feasibilityOffset(lprec *lp, MYBOOL isdual) } -STATIC LPSREAL compute_dualslacks(lprec *lp, int target, LPSREAL **dvalues, int **nzdvalues, MYBOOL dosum) +STATIC REAL compute_dualslacks(lprec *lp, int target, REAL **dvalues, int **nzdvalues, MYBOOL dosum) /* Note that this function is similar to the compute_reducedcosts function in lp_price.c */ { int i, varnr, *coltarget, **nzduals, *nzvtemp = NULL; - LPSREAL d, g = 0, **duals, *vtemp = NULL; + REAL d, g = 0, **duals, *vtemp = NULL; MYBOOL localREAL = (MYBOOL) (dvalues == NULL), localINT = (MYBOOL) (nzdvalues == NULL); @@ -6390,14 +6137,14 @@ STATIC LPSREAL compute_dualslacks(lprec *lp, int target, LPSREAL **dvalues, int return( g ); } -STATIC LPSREAL compute_feasibilitygap(lprec *lp, MYBOOL isdual, MYBOOL dosum) +STATIC REAL compute_feasibilitygap(lprec *lp, MYBOOL isdual, MYBOOL dosum) { - LPSREAL f = 0; + REAL f = 0; /* This computes the primal feasibility gap (for use with the dual simplex phase 1) */ if(isdual) { int i; - LPSREAL g; + REAL g; for(i = 1; i <= lp->rows; i++) { if(lp->rhs[i] < 0) @@ -6421,10 +6168,10 @@ STATIC LPSREAL compute_feasibilitygap(lprec *lp, MYBOOL isdual, MYBOOL dosum) } /* Find the smallest fractional value in a given row of the OF/constraint matrix */ -STATIC int row_decimals(lprec *lp, int rownr, MYBOOL intsonly, LPSREAL *intscalar) +STATIC int row_decimals(lprec *lp, int rownr, MYBOOL intsonly, REAL *intscalar) { int basi, i, j, ncols = lp->columns; - LPSREAL f, /* g, */ epsvalue = lp->epsprimal; + REAL f, /* g, */ epsvalue = lp->epsprimal; basi = 0; for(j = 1; j <= ncols; j++) { @@ -6448,11 +6195,11 @@ STATIC int row_decimals(lprec *lp, int rownr, MYBOOL intsonly, LPSREAL *intscala f -= floor (f + epsvalue); } if(i > MAX_FRACSCALE) - /* i = MAX_FRACSCALE */ break; + break; SETMAX(basi, i); } if(j > ncols) - *intscalar = pow(10.0, basi); + *intscalar = pow(10, basi); else { basi = -1; *intscalar = 1; @@ -6460,18 +6207,22 @@ STATIC int row_decimals(lprec *lp, int rownr, MYBOOL intsonly, LPSREAL *intscala return( basi ); } -STATIC int row_intstats(lprec *lp, int rownr, int pivcolnr, int *maxndec, - int *plucount, int *intcount, int *intval, LPSREAL *valGCD, LPSREAL *pivcolval) +STATIC int row_intstats(lprec *lp, int rownr, int pivcolnr, + int *plucount, int *intcount, int *intval, REAL *valGCD, REAL *pivcolval) { - int jb, je, jj, nn = 0, multA, multB, intGCD = 0; - LPSREAL rowval, inthold, intfrac; + int ndec, jb, je, jj, nn = 0, multA, multB, intGCD; + REAL rowval, inthold, intfrac; MATrec *mat = lp->matA; +/* Get rid of a warning by initializing: SEB April 19 2006. */ + intGCD = 0; + /* Do we have a valid matrix? */ if(mat_validate(mat)) { /* Get smallest fractional row value */ - *maxndec = row_decimals(lp, rownr, AUTOMATIC, &intfrac); + ndec = row_decimals(lp, rownr, AUTOMATIC, &intfrac); + ndec = ndec + 0; /* So the compiler thinks we used ndec! -- Buttrey */ /* Get OF row starting and ending positions, as well as the first column index */ if(rownr == 0) { @@ -6521,7 +6272,7 @@ STATIC int row_intstats(lprec *lp, int rownr, int pivcolnr, int *maxndec, if(rowval > 0) (*plucount)++; - /* Check if the parameter value is integer and update the row's GCD */ + /* Check if the parameter value is integer and update the row's GDC */ rowval = fabs(rowval) * intfrac; rowval += rowval*lp->epsmachine; rowval = modf(rowval, &inthold); @@ -6540,183 +6291,63 @@ STATIC int row_intstats(lprec *lp, int rownr, int pivcolnr, int *maxndec, return(nn); } -#if 0 -LPSREAL MIP_stepOF(lprec *lp) +REAL MIP_stepOF(lprec *lp) /* This function tries to find a non-zero minimum improvement if the OF contains all integer variables (logic only applies if we are looking for a single solution, not possibly several equal-valued ones). */ { MYBOOL OFgcd; - int colnr, rownr, n, ib, ie, maxndec, - pluscount, intcount, intval; - LPSREAL value, valOF, divOF, valGCD; + int OFrow, colnr, n, pluscount, intcount, intval; + REAL value, valOF, divOF, valGCD; MATrec *mat = lp->matA; value = 0; if((lp->int_vars > 0) && (lp->solutionlimit == 1) && mat_validate(mat)) { /* Get statistics for integer OF variables and compute base stepsize */ - n = row_intstats(lp, 0, -1, &maxndec, &pluscount, &intcount, &intval, &valGCD, &divOF); - if((n == 0) || (maxndec < 0)) + n = row_intstats(lp, 0, -1, &pluscount, &intcount, &intval, &valGCD, &divOF); + if(n == 0) return( value ); OFgcd = (MYBOOL) (intval > 0); if(OFgcd) value = valGCD; /* Check non-ints in the OF to see if we can get more info */ - if(n - intcount > 0) { - int nrv = 0; - - /* See if we have equality constraints */ - ie = lp->rows; - for(ib = 1; ib <= ie; ib++) { - if(is_constr_type(lp, ib, EQ)) - break; - } - - /* If so, there may be a chance to find an improved stepsize */ - if(ib < ie) - for(colnr = 1; colnr <= lp->columns; colnr++) { - - /* Go directly to the next variable if this is an integer or - there is no row candidate to explore for hidden bounds for - real-valued variables (limit scan to one row!) */ - if(is_int(lp, colnr)) - continue; - nrv++; - /* Scan equality constraints */ - ib = mat->col_end[colnr-1]; - ie = mat->col_end[colnr]; - while(ib < ie) { - if(is_constr_type(lp, (rownr = COL_MAT_ROWNR(ib)), EQ)) { - - /* Get "child" row statistics, but break out if we don't - find enough information, i.e. no integers with coefficients of proper type */ - n = row_intstats(lp, rownr, colnr, &maxndec, &pluscount, &intcount, &intval, &valGCD, &divOF); - if((intval < n - 1) || (maxndec < 0)) { - value = 0; - break; - } - - /* We can update */ - valOF = unscaled_mat(lp, lp->orig_obj[colnr], 0, colnr); - valOF = fabs( valOF * (valGCD / divOF) ); - if(OFgcd) { - SETMIN(value, valOF); - } - else { - OFgcd = TRUE; - value = valOF; - } - } - ib++; - } - - /* No point in continuing scan if we failed in current column */ - if(value == 0) - break; - } + if(n - intcount > 0) + for(colnr = 1; colnr <= lp->columns; colnr++) { + + /* Go directly to the next variable if this is an integer or + there is no row candidate to explore */ + if(is_int(lp, colnr) || + (mat_collength(mat, colnr) != 1) || + (!is_constr_type(lp, (OFrow = COL_MAT_ROWNR(mat->col_end[colnr-1])), EQ))) + continue; - /* Check if we found information for any real-valued variable; - if not, then we must set the iprovement delta to 0 */ - if(nrv == 0) + /* Get "child" row statistics, but break out if we don't + find enough information, i.e. integers with integer coefficients */ + n = row_intstats(lp, OFrow, colnr, &pluscount, &intcount, &intval, &valGCD, &divOF); + if(intval < n - 1) { value = 0; - } - } - return( value ); -} -#else - -LPSREAL MIP_stepOF(lprec *lp) -/* This function tries to find a non-zero minimum improvement - if the OF contains all integer variables (logic only applies if we are - looking for a single solution, not possibly several equal-valued ones). */ -{ - MYBOOL OFgcd; - int colnr, rownr, n, ib, ie, - pluscount, intcount; - int intval, maxndec; - LPSREAL value = 0, valOF, divOF, valGCD; - MATrec *mat = lp->matA; - - if((lp->int_vars > 0) && (lp->solutionlimit == 1) && mat_validate(mat)) { - - /* Get statistics for integer OF variables and compute base stepsize */ - n = row_intstats(lp, 0, 0, &maxndec, &pluscount, &intcount, &intval, &valGCD, &divOF); - if((n == 0) || (maxndec < 0)) - return( value ); - OFgcd = (MYBOOL) (intval > 0); - if(OFgcd) - value = valGCD; - - /* Check non-ints in the OF to see if we can get more info */ - if(n - intcount > 0) { - int nrv = n - intcount; /* Number of real variables in the objective */ - int niv = 0; /* Number of real variables identified as integer */ - int nrows = lp->rows; - - /* See if we have equality constraints */ - for(ib = 1; ib <= nrows; ib++) { - if(is_constr_type(lp, ib, EQ)) - break; + break; } - /* If so, there may be a chance to find an improved stepsize */ - if(ib < nrows) - for(colnr = 1; colnr <= lp->columns; colnr++) { - - /* Go directly to the next variable if this is an integer or - there is no row candidate to explore for hidden bounds for - real-valued variables (limit scan to one row/no recursion) */ - if((lp->orig_obj[colnr] == 0) || is_int(lp, colnr)) - continue; - - /* Scan equality constraints */ - ib = mat->col_end[colnr-1]; - ie = mat->col_end[colnr]; - while(ib < ie) { - if(is_constr_type(lp, (rownr = COL_MAT_ROWNR(ib)), EQ)) { - - /* Get "child" row statistics, but break out if we don't - find enough information, i.e. no integers with coefficients of proper type */ - n = row_intstats(lp, rownr, colnr, &maxndec, &pluscount, &intcount, &intval, &valGCD, &divOF); - if((intval < n - 1) || (maxndec < 0)) { - value = 0; - break; - } - niv++; - - /* We can update */ - valOF = unscaled_mat(lp, lp->orig_obj[colnr], 0, colnr); - valOF = fabs( valOF * (valGCD / divOF) ); - if(OFgcd) { - SETMIN(value, valOF); - } - else { - OFgcd = TRUE; - value = valOF; - } - } - ib++; - } - - /* No point in continuing scan if we failed in current column */ - if(value == 0) - break; + /* We can update */ + valOF = unscaled_mat(lp, lp->orig_obj[colnr], 0, colnr); + valOF = fabs( valOF * (valGCD / divOF) ); + if(OFgcd) { + SETMIN(value, valOF); + } + else { + OFgcd = TRUE; + value = valOF; } - /* Check if we found information for any real-valued variable; - if not, then we must set the improvement delta to 0 */ - if(nrv > niv) - value = 0; } } return( value ); } -#endif - STATIC MYBOOL isPrimalSimplex(lprec *lp) { return((MYBOOL) (((lp->simplex_mode & SIMPLEX_Phase1_PRIMAL) != 0) || @@ -6734,9 +6365,9 @@ STATIC MYBOOL isP1extra(lprec *lp) return((MYBOOL) ((lp->P1extraDim > 0) || (lp->P1extraVal != 0))); } -STATIC MYBOOL feasiblePhase1(lprec *lp, LPSREAL epsvalue) +STATIC MYBOOL feasiblePhase1(lprec *lp, REAL epsvalue) { - LPSREAL gap; + REAL gap; MYBOOL test; gap = fabs(lp->rhs[0] - lp->orig_rhs[0]); @@ -6781,10 +6412,10 @@ STATIC int findBasicFixedvar(lprec *lp, int afternr, MYBOOL slacksonly) return( afternr ); } -STATIC MYBOOL isBasisVarFeasible(lprec *lp, LPSREAL tol, int basis_row) +STATIC MYBOOL isBasisVarFeasible(lprec *lp, REAL tol, int basis_row) { int col; - LPSREAL x; + REAL x; MYBOOL Ok = TRUE; MYBOOL doSC = FALSE; @@ -6798,7 +6429,7 @@ STATIC MYBOOL isBasisVarFeasible(lprec *lp, LPSREAL tol, int basis_row) } return( Ok ); } -STATIC MYBOOL isPrimalFeasible(lprec *lp, LPSREAL tol, int infeasibles[], LPSREAL *feasibilitygap) +STATIC MYBOOL isPrimalFeasible(lprec *lp, REAL tol, int infeasibles[], REAL *feasibilitygap) { int i; MYBOOL feasible = TRUE; @@ -6842,13 +6473,13 @@ STATIC MYBOOL isPrimalFeasible(lprec *lp, LPSREAL tol, int infeasibles[], LPSREA return(feasible); } -STATIC MYBOOL isDualFeasible(lprec *lp, LPSREAL tol, int *boundflipcount, int infeasibles[], LPSREAL *feasibilitygap) +STATIC MYBOOL isDualFeasible(lprec *lp, REAL tol, int *boundflipcount, int infeasibles[], REAL *feasibilitygap) { int i, varnr, n = 0, /* Number of infeasible duals corrected with bound-swaps */ m = 0, target = SCAN_ALLVARS+USE_NONBASICVARS; - LPSREAL f = 0; + REAL f = 0; MYBOOL feasible, islower; @@ -6859,7 +6490,7 @@ STATIC MYBOOL isDualFeasible(lprec *lp, LPSREAL tol, int *boundflipcount, int in having to use dual simplex phase 1. */ if((infeasibles != NULL) || (boundflipcount != NULL)) { int *nzdcol = NULL; - LPSREAL d, *dcol = NULL; + REAL d, *dcol = NULL; f = compute_dualslacks(lp, target, &dcol, &nzdcol, FALSE); if(nzdcol != NULL) @@ -6909,18 +6540,15 @@ STATIC MYBOOL isDualFeasible(lprec *lp, LPSREAL tol, int *boundflipcount, int in varnr = lp->rows + 1; for(i = 1; i <= lp->columns; i++, varnr++) { - if (mat_collength(lp->matA, i) == 0) { - islower = lp->is_lower[varnr]; - if((my_chsign(islower, lp->orig_obj[i]) > 0) && !SOS_is_member(lp->SOS, 0, i)) { - lp->is_lower[varnr] = !islower; - if((islower && my_infinite(lp, lp->upbo[varnr] /* lp->orig_upbo[varnr] */)) || - (!islower && my_infinite(lp, my_lowbo(lp, varnr) /* lp->orig_lowbo[varnr] */))) { - lp->spx_status = UNBOUNDED; - break; - } - /* lp->is_lower[varnr] = !islower; */ - n++; + islower = lp->is_lower[varnr]; + if((my_chsign(islower, lp->orig_obj[i]) > 0) && (mat_collength(lp->matA, i) == 0) && !SOS_is_member(lp->SOS, 0, i)) { + lp->is_lower[varnr] = !islower; + if((islower && my_infinite(lp, lp->upbo[varnr])) || + (!islower && my_infinite(lp, my_lowbo(lp, varnr)))) { + lp->spx_status = UNBOUNDED; + break; } + n++; } } @@ -7089,23 +6717,44 @@ STATIC MYBOOL is_slackbasis(lprec *lp) STATIC MYBOOL verify_basis(lprec *lp) { - int i, ii; + int i, ii; /* , k = 0; */ MYBOOL result = FALSE; +if (buttrey_thing > 0) +{ +buttrey_debugfile = fopen ("h:/temp/egaddeath.txt", "w"); +} for(i = 1; i <= lp->rows; i++) { ii = lp->var_basic[i]; + if (buttrey_thing > 0) { + fprintf (buttrey_debugfile, "i %i, rows %i, ii %i, sum %i, basic[%i] %i\n", + i, lp->rows, ii, lp->sum, ii, lp->is_basic[ii]); + fflush (buttrey_debugfile); + } if((ii < 1) || (ii > lp->sum) || !lp->is_basic[ii]) { - //@FS: unused// k = i; + if (buttrey_thing > 0) { + fprintf (buttrey_debugfile, "lp lib: We're inside.\n"); + fflush (buttrey_debugfile); + } + /* k = i; */ ii = 0; goto Done; } } + if (buttrey_thing > 0) { + fprintf (buttrey_debugfile, "lp lib: We're down here now.\n"); + fflush (buttrey_debugfile); + } ii = lp->rows; for(i = 1; i <= lp->sum; i++) { if(lp->is_basic[i]) ii--; } + if (buttrey_thing > 0) { + fprintf (buttrey_debugfile, "lp lib: About to return.\n"); + fflush (buttrey_debugfile); + } result = (MYBOOL) (ii == 0); Done: @@ -7158,7 +6807,7 @@ int __WINAPI set_basisvar(lprec *lp, int basisPos, int enteringCol) STATIC int perturb_bounds(lprec *lp, BBrec *perturbed, MYBOOL doRows, MYBOOL doCols, MYBOOL includeFIXED) { int i, ii, n = 0; - LPSREAL new_lb, new_ub, *upbo, *lowbo; + REAL new_lb, new_ub, *upbo, *lowbo; if(perturbed == NULL) return( n ); @@ -7213,7 +6862,7 @@ STATIC int perturb_bounds(lprec *lp, BBrec *perturbed, MYBOOL doRows, MYBOOL doC return( n ); } -STATIC MYBOOL impose_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo) +STATIC MYBOOL impose_bounds(lprec *lp, REAL *upbo, REAL *lowbo) /* Explicitly set working bounds to given vectors without pushing or popping */ { MYBOOL ok; @@ -7232,7 +6881,7 @@ STATIC MYBOOL impose_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo) return( ok ); } -STATIC MYBOOL validate_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo) +STATIC MYBOOL validate_bounds(lprec *lp, REAL *upbo, REAL *lowbo) /* Check if all bounds are Explicitly set working bounds to given vectors without pushing or popping */ { MYBOOL ok; @@ -7402,7 +7051,7 @@ STATIC int unload_basis(lprec *lp, MYBOOL restorelast) } -STATIC LPSREAL scaled_floor(lprec *lp, int colnr, LPSREAL value, LPSREAL epsscale) +STATIC REAL scaled_floor(lprec *lp, int colnr, REAL value, REAL epsscale) { value = floor(value); if(value != 0) @@ -7416,7 +7065,7 @@ STATIC LPSREAL scaled_floor(lprec *lp, int colnr, LPSREAL value, LPSREAL epsscal return(value); } -STATIC LPSREAL scaled_ceil(lprec *lp, int colnr, LPSREAL value, LPSREAL epsscale) +STATIC REAL scaled_ceil(lprec *lp, int colnr, REAL value, REAL epsscale) { value = ceil(value); if(value != 0) @@ -7435,7 +7084,7 @@ STATIC LPSREAL scaled_ceil(lprec *lp, int colnr, LPSREAL value, LPSREAL epsscale STATIC MYBOOL is_sc_violated(lprec *lp, int column) { int varno; - LPSREAL tmpreal; + REAL tmpreal; varno = lp->rows+column; tmpreal = unscaled_value(lp, lp->sc_lobound[column], varno); @@ -7447,7 +7096,7 @@ STATIC int find_sc_bbvar(lprec *lp, int *count) { int i, ii, n, bestvar; int firstsc, lastsc; - LPSREAL hold, holdINT, bestval, OFval, randval, scval; + REAL hold, holdINT, bestval, OFval, randval, scval; MYBOOL reversemode, greedymode, randomizemode, pseudocostmode, pseudocostsel; @@ -7591,7 +7240,7 @@ STATIC int find_sos_bbvar(lprec *lp, int *count, MYBOOL intsos) STATIC int find_int_bbvar(lprec *lp, int *count, BBrec *BB, MYBOOL *isfeasible) { int i, ii, n, k, bestvar, depthmax, *nonint = NULL; - LPSREAL hold, holdINT, bestval, OFval, randval, + REAL hold, holdINT, bestval, OFval, randval, *lowbo = BB->lowbo, *upbo = BB->upbo; MYBOOL reversemode, greedymode, depthfirstmode, breadthfirstmode, randomizemode, rcostmode, @@ -7614,7 +7263,7 @@ STATIC int find_int_bbvar(lprec *lp, int *count, BBrec *BB, MYBOOL *isfeasible) depthfirstmode = is_bb_mode(lp, NODE_DEPTHFIRSTMODE); breadthfirstmode = is_bb_mode(lp, NODE_BREADTHFIRSTMODE) && (MYBOOL) (lp->bb_level <= lp->int_vars); - rcostmode = (MYBOOL) /* FALSE */ (BB->lp->solutioncount > 0) && is_bb_mode(lp, NODE_RCOSTFIXING) ; /* 5/2/08 peno disabled NODE_RCOSTFIXING because it results in non-optimal solutions with some models */ /* 15/2/8 peno enabled NODE_RCOSTFIXING again because a fix is found. See lp_simplex.c NODE__RCOSTFIXING fix */ + rcostmode = (MYBOOL) (BB->lp->solutioncount > 0) && is_bb_mode(lp, NODE_RCOSTFIXING); pseudocostmode = is_bb_mode(lp, NODE_PSEUDOCOSTMODE); pseudocostsel = is_bb_rule(lp, NODE_PSEUDOCOSTSELECT) || is_bb_rule(lp, NODE_PSEUDONONINTSELECT) || @@ -7825,7 +7474,7 @@ STATIC int find_int_bbvar(lprec *lp, int *count, BBrec *BB, MYBOOL *isfeasible) STATIC BBPSrec *init_pseudocost(lprec *lp, int pseudotype) { int i; - LPSREAL PSinitUP, PSinitLO; + REAL PSinitUP, PSinitLO; BBPSrec *newitem; MYBOOL isPSCount; @@ -7897,7 +7546,7 @@ STATIC void free_pseudocost(lprec *lp) } } -MYBOOL __WINAPI set_pseudocosts(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int *updatelimit) +MYBOOL __WINAPI set_pseudocosts(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit) { int i; @@ -7914,7 +7563,7 @@ MYBOOL __WINAPI set_pseudocosts(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int return(TRUE); } -MYBOOL __WINAPI get_pseudocosts(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int *updatelimit) +MYBOOL __WINAPI get_pseudocosts(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit) { int i; @@ -7931,7 +7580,7 @@ MYBOOL __WINAPI get_pseudocosts(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int return(TRUE); } -STATIC LPSREAL get_pseudorange(BBPSrec *pc, int mipvar, int varcode) +STATIC REAL get_pseudorange(BBPSrec *pc, int mipvar, int varcode) { if(varcode == BB_SC) return( unscaled_value(pc->lp, pc->lp->sc_lobound[mipvar], pc->lp->rows+mipvar) ); @@ -7939,9 +7588,9 @@ STATIC LPSREAL get_pseudorange(BBPSrec *pc, int mipvar, int varcode) return( 1.0 ); } -STATIC void update_pseudocost(BBPSrec *pc, int mipvar, int varcode, MYBOOL capupper, LPSREAL varsol) +STATIC void update_pseudocost(BBPSrec *pc, int mipvar, int varcode, MYBOOL capupper, REAL varsol) { - LPSREAL OFsol, uplim; + REAL OFsol, uplim; MATitem *PS; MYBOOL nonIntSelect = is_bb_rule(pc->lp, NODE_PSEUDONONINTSELECT); @@ -7956,10 +7605,12 @@ STATIC void update_pseudocost(BBPSrec *pc, int mipvar, int varcode, MYBOOL capup else OFsol = pc->lp->solution[0]; /* The problem's objective function value */ - if(isnan(varsol)) { +#if 0 + if(_isnan(varsol)) { pc->lp->bb_parentOF = OFsol; return; } +#endif /* Point to the applicable (lower or upper) bound and increment attempted update count */ if(capupper) { @@ -8001,7 +7652,7 @@ STATIC void update_pseudocost(BBPSrec *pc, int mipvar, int varcode, MYBOOL capup pc->lp->bb_parentOF = OFsol; } -STATIC LPSREAL get_pseudobranchcost(BBPSrec *pc, int mipvar, MYBOOL dofloor) +STATIC REAL get_pseudobranchcost(BBPSrec *pc, int mipvar, MYBOOL dofloor) { if(dofloor) return( pc->LOcost[mipvar].value ); @@ -8009,14 +7660,16 @@ STATIC LPSREAL get_pseudobranchcost(BBPSrec *pc, int mipvar, MYBOOL dofloor) return( pc->UPcost[mipvar].value ); } -STATIC LPSREAL get_pseudonodecost(BBPSrec *pc, int mipvar, int vartype, LPSREAL varsol) +STATIC REAL get_pseudonodecost(BBPSrec *pc, int mipvar, int vartype, REAL varsol) { - LPSREAL hold, uplim; + REAL hold, uplim; uplim = get_pseudorange(pc, mipvar, vartype); varsol = modf(varsol/uplim, &hold); - if(isnan(varsol)) +#if 0 + if(_isnan(varsol)) varsol = 0; +#endif hold = pc->LOcost[mipvar].value*varsol + pc->UPcost[mipvar].value*(1-varsol); @@ -8024,13 +7677,13 @@ STATIC LPSREAL get_pseudonodecost(BBPSrec *pc, int mipvar, int vartype, LPSREAL return( hold*uplim ); } -STATIC int compute_theta(lprec *lp, int rownr, LREAL *theta, int isupbound, LPSREAL HarrisScalar, MYBOOL primal) +STATIC int compute_theta(lprec *lp, int rownr, LREAL *theta, int isupbound, REAL HarrisScalar, MYBOOL primal) /* The purpose of this routine is to compute the non-basic bound state / value of the leaving variable. Note that the incoming theta is "d" in Chvatal-terminology */ { int colnr = lp->var_basic[rownr]; register LREAL x = lp->rhs[rownr]; - LPSREAL lb = 0, /* Put lower bound here when the fully bounded version is implemented */ + REAL lb = 0, /* Put lower bound here when the fully bounded version is implemented */ ub = lp->upbo[colnr], eps = lp->epsprimal; /* Primal feasibility tolerance */ @@ -8089,12 +7742,12 @@ STATIC int compute_theta(lprec *lp, int rownr, LREAL *theta, int isupbound, LPSR return( colnr ); } -STATIC MYBOOL check_degeneracy(lprec *lp, LPSREAL *pcol, int *degencount) +STATIC MYBOOL check_degeneracy(lprec *lp, REAL *pcol, int *degencount) /* Check if the entering column Pi=Inv(B)*a is likely to produce improvement; (cfr. Istvan Maros: CTOTSM p. 233) */ { int i, ndegen; - LPSREAL *rhs, sdegen, epsmargin = lp->epsprimal; + REAL *rhs, sdegen, epsmargin = lp->epsprimal; sdegen = 0; ndegen = 0; @@ -8118,11 +7771,11 @@ STATIC MYBOOL check_degeneracy(lprec *lp, LPSREAL *pcol, int *degencount) } STATIC MYBOOL performiteration(lprec *lp, int rownr, int varin, LREAL theta, MYBOOL primal, MYBOOL allowminit, - LPSREAL *prow, int *nzprow, LPSREAL *pcol, int *nzpcol, int *boundswaps) + REAL *prow, int *nzprow, REAL *pcol, int *nzpcol, int *boundswaps) { - int varout; - LPSREAL pivot, epsmargin, leavingValue, leavingUB, enteringUB; - MYBOOL leavingToUB = FALSE, enteringFromUB, enteringIsFixed, leavingIsFixed; + static int varout; + static REAL pivot, epsmargin, leavingValue, leavingUB, enteringUB; + static MYBOOL leavingToUB, enteringFromUB, enteringIsFixed, leavingIsFixed; MYBOOL *islower = &(lp->is_lower[varin]); MYBOOL minitNow = FALSE, minitStatus = ITERATE_MAJORMAJOR; LREAL deltatheta = theta; @@ -8158,9 +7811,6 @@ STATIC MYBOOL performiteration(lprec *lp, int rownr, int varin, LREAL theta, MYB leavingUB = lp->upbo[varout]; enteringIsFixed = (MYBOOL) (fabs(enteringUB) < epsmargin); leavingIsFixed = (MYBOOL) (fabs(leavingUB) < epsmargin); -#if defined _PRICE_NOBOUNDFLIP - allowminit &= !ISMASKSET(lp->piv_strategy, PRICE_NOBOUNDFLIP); -#endif #ifdef Paranoia if(enteringUB < 0) report(lp, SEVERE, "performiteration: Negative range for entering variable %d at iter %.0f\n", @@ -8175,7 +7825,7 @@ STATIC MYBOOL performiteration(lprec *lp, int rownr, int varin, LREAL theta, MYB if((boundswaps != NULL) && (boundswaps[0] > 0)) { int i, boundvar; - LPSREAL *hold; + REAL *hold; /* Allocate and initialize accumulation array */ allocREAL(lp, &hold, lp->rows + 1, TRUE); @@ -8331,7 +7981,7 @@ STATIC MYBOOL performiteration(lprec *lp, int rownr, int varin, LREAL theta, MYB } /* performiteration */ -STATIC LPSREAL get_refactfrequency(lprec *lp, MYBOOL final) +STATIC REAL get_refactfrequency(lprec *lp, MYBOOL final) { COUNTER iters; int refacts; @@ -8346,11 +7996,11 @@ STATIC LPSREAL get_refactfrequency(lprec *lp, MYBOOL final) 3) Frequency with added initialization offsets which are diluted in course of the solution process */ if(final) - return( (LPSREAL) (iters) / MAX(1,refacts) ); + return( (REAL) (iters) / MAX(1,refacts) ); else if(lp->bb_totalnodes > 0) - return( (LPSREAL) lp->bfp_pivotmax(lp) ); + return( (REAL) lp->bfp_pivotmax(lp) ); else - return( (LPSREAL) (lp->bfp_pivotmax(lp)+iters) / (1+refacts) ); + return( (REAL) (lp->bfp_pivotmax(lp)+iters) / (1+refacts) ); } #if 0 @@ -8361,20 +8011,6 @@ STATIC LPSREAL get_refactfrequency(lprec *lp, MYBOOL final) else return( (MYBOOL) (lp->upbo[variable]-lp->lowbo[variable] < lp->epsprimal) ); } /* is_fixedvar */ -#else -MYBOOL is_fixedvar(lprec *lp, int varnr) -{ - if(lp->bb_bounds == NULL) { - if(varnr <= lp->rows) - return( (MYBOOL) (lp->orig_upbo[varnr] < lp->epsmachine) ); - else - return( (MYBOOL) (lp->orig_upbo[varnr]-lp->orig_lowbo[varnr] < lp->epsmachine) ); - } - else if((varnr <= lp->rows) || (lp->bb_bounds->UBzerobased == TRUE)) - return( (MYBOOL) (lp->upbo[varnr] < lp->epsvalue) ); - else - return( (MYBOOL) (lp->upbo[varnr]-lp->lowbo[varnr] < lp->epsvalue) ); -} #endif STATIC MYBOOL solution_is_int(lprec *lp, int index, MYBOOL checkfixed) @@ -8545,7 +8181,7 @@ STATIC MYBOOL bb_better(lprec *lp, int target, int mode) 5 -----------++++++ RHS exclusive */ { - LPSREAL epsvalue, offset = lp->epsprimal, + REAL epsvalue, offset = lp->epsprimal, refvalue = lp->infinite, testvalue = lp->solution[0]; MYBOOL ismax = is_maxim(lp), relgap = is_action(mode, OF_TEST_RELGAP), @@ -8573,11 +8209,11 @@ STATIC MYBOOL bb_better(lprec *lp, int target, int mode) break; case OF_INCUMBENT: refvalue = lp->best_solution[0]; break; - case OF_WORKING: refvalue = my_chsign(!ismax, lp->bb_workOF /* unscaled_value(lp, lp->bb_workOF, 0) */ ); + case OF_WORKING: refvalue = my_chsign(!ismax, lp->bb_workOF); if(fcast) testvalue = my_chsign(!ismax, lp->longsteps->obj_last) - epsvalue; else - testvalue = my_chsign(!ismax, lp->rhs[0] /* unscaled_value(lp, lp->rhs[0], 0) */); + testvalue = my_chsign(!ismax, lp->rhs[0]); break; case OF_USERBREAK: refvalue = lp->bb_breakOF; break; @@ -8614,12 +8250,12 @@ STATIC MYBOOL bb_better(lprec *lp, int target, int mode) return( relgap ); } -STATIC void construct_solution(lprec *lp, LPSREAL *target) +STATIC void construct_solution(lprec *lp, REAL *target) { int i, j, basi; - LPSREAL f, epsvalue = lp->epsprimal; - LPSREAL *solution; - LPSREAL *value; + REAL f, epsvalue = lp->epsprimal; + REAL *solution; + REAL *value; int *rownr; MATrec *mat = lp->matA; @@ -8723,35 +8359,25 @@ STATIC void construct_solution(lprec *lp, LPSREAL *target) } /* Do MIP-related tests and computations */ - if((lp->int_vars > 0) && mat_validate(lp->matA) /* && !lp->wasPresolved */) { /* && !lp->wasPresolved uncommented by findings of William H. Patton. The code was never executed when the test was there. The code has effect in an integer model with all integer objective coeff. to cut-off optimization and thus make it faster */ - LPSREAL fixedOF = unscaled_value(lp, lp->orig_rhs[0], 0); + if((lp->int_vars > 0) && mat_validate(lp->matA) && !lp->wasPresolved) { + REAL fixedOF = unscaled_value(lp, lp->orig_rhs[0], 0); /* Check if we have an all-integer OF */ basi = lp->columns; for(j = 1; j <= basi; j++) { - f = fabs(get_mat(lp, 0, j)) + lp->epsint / 2; - if(f > lp->epsint) { /* If coefficient is 0 then it doesn't influence OF, even it variable is not integer */ - if(!is_int(lp, j) || (fmod(f, 1) > lp->epsint)) - break; - } + f = fabs(get_mat(lp, 0, j)) + lp->epsint/2; + f = fmod(f, 1); + if(!is_int(lp, j) || (f > lp->epsint)) + break; } /* If so, we can round up the fractional OF */ if(j > basi) { f = my_chsign(is_maxim(lp), lp->real_solution) + fixedOF; f = floor(f+(1-epsvalue)); - f = my_chsign(is_maxim(lp), f - fixedOF); - if(is_infinite(lp, lp->bb_limitOF)) - lp->bb_limitOF = f; - else if(is_maxim(lp)) { - SETMIN(lp->bb_limitOF, f); - } - else { - SETMAX(lp->bb_limitOF, f); - } + lp->bb_limitOF = my_chsign(is_maxim(lp), f - fixedOF); } } - /* Check that a user limit on the OF is feasible */ if((lp->int_vars > 0) && (my_chsign(is_maxim(lp), my_reldiff(lp->best_solution[0],lp->bb_limitOF)) < -epsvalue)) { @@ -8763,12 +8389,12 @@ STATIC void construct_solution(lprec *lp, LPSREAL *target) } /* construct_solution */ -STATIC int check_solution(lprec *lp, int lastcolumn, LPSREAL *solution, - LPSREAL *upbo, LPSREAL *lowbo, LPSREAL tolerance) +STATIC int check_solution(lprec *lp, int lastcolumn, REAL *solution, + REAL *upbo, REAL *lowbo, REAL tolerance) { /*#define UseMaxValueInCheck*/ MYBOOL isSC; - LPSREAL test, value, hold, diff, maxdiff = 0.0, maxerr = 0.0, *matValue, + REAL test, value, hold, diff, maxdiff = 0.0, maxerr = 0.0, *matValue, #ifdef UseMaxValueInCheck *maxvalue = NULL, #else @@ -8779,10 +8405,10 @@ STATIC int check_solution(lprec *lp, int lastcolumn, LPSREAL *solution, report(lp, NORMAL, " \n"); if(MIP_count(lp) > 0) - report(lp, NORMAL, "%s solution " RESULTVALUEMASK " after %10.0f iter, %9.0f nodes (gap %.1f%%).\n", - my_if(lp->bb_break && !bb_better(lp, OF_DUALLIMIT, OF_TEST_BE) && bb_better(lp, OF_RELAXED, OF_TEST_NE), "Subopt.", "Optimal"), + report(lp, NORMAL, "%sOptimal solution " RESULTVALUEMASK " after %10.0f iter, %9.0f nodes (gap %.1f%%).\n", + my_if(lp->bb_break, "-", "+"), solution[0], (double) lp->total_iter, (double) lp->bb_totalnodes, - 100.0*fabs(my_reldiff(solution[0], lp->bb_limitOF))); + 100.0*fabs(my_reldiff(lp->solution[0], lp->bb_limitOF))); else report(lp, NORMAL, "Optimal solution " RESULTVALUEMASK " after %10.0f iter.\n", solution[0], (double) lp->total_iter); @@ -9040,7 +8666,7 @@ STATIC void transfer_solution(lprec *lp, MYBOOL dofinal) STATIC MYBOOL construct_duals(lprec *lp) { int i, n, *coltarget; - LPSREAL scale0, value, dualOF; + REAL scale0, value, dualOF; if(lp->duals != NULL) free_duals(lp); @@ -9138,7 +8764,7 @@ STATIC MYBOOL construct_sensitivity_duals(lprec *lp) { int k,varnr, ok = TRUE; int *workINT = NULL; - LPSREAL *pcol,a,infinite,epsvalue,from,till,objfromvalue; + REAL *pcol,a,infinite,epsvalue,from,till,objfromvalue; /* one column of the matrix */ FREE(lp->objfromvalue); @@ -9169,13 +8795,14 @@ STATIC MYBOOL construct_sensitivity_duals(lprec *lp) /* Search for the rows(s) which first result in further iterations */ for (k=1; k<=lp->rows; k++) { if (fabs(pcol[k])>epsvalue) { - a = lp->rhs[k]/pcol[k]; + a = unscaled_value(lp, lp->rhs[k]/pcol[k], varnr); if((varnr > lp->rows) && (fabs(lp->solution[varnr]) <= epsvalue) && (a < objfromvalue) && (a >= lp->lowbo[varnr])) objfromvalue = a; if ((a<=0.0) && (pcol[k]<0.0) && (-a=0.0) && (pcol[k]>0.0) && ( aupbo[lp->var_basic[k]] < infinite) { - a = (LPSREAL) ((lp->rhs[k]-lp->upbo[lp->var_basic[k]])/pcol[k]); + a = (REAL) ((lp->rhs[k]-lp->upbo[lp->var_basic[k]])/pcol[k]); + a = unscaled_value(lp, a, varnr); if((varnr > lp->rows) && (fabs(lp->solution[varnr]) <= epsvalue) && (a < objfromvalue) && (a >= lp->lowbo[varnr])) objfromvalue = a; if ((a<=0.0) && (pcol[k]>0.0) && (-adualsfrom[varnr]=lp->solution[varnr]-unscaled_value(lp, from, varnr); + lp->dualsfrom[varnr]=lp->solution[varnr]-from; else lp->dualsfrom[varnr]=-infinite; if (till!=infinite) - lp->dualstill[varnr]=lp->solution[varnr]+unscaled_value(lp, till, varnr); + lp->dualstill[varnr]=lp->solution[varnr]+till; else lp->dualstill[varnr]=infinite; if (varnr > lp->rows) { if (objfromvalue != infinite) { - if ((!sensrejvar) || (lp->upbo[varnr] != 0.0)) { - if (!lp->is_lower[varnr]) - objfromvalue = lp->upbo[varnr] - objfromvalue; - if ((lp->upbo[varnr] < infinite) && (objfromvalue > lp->upbo[varnr])) - objfromvalue = lp->upbo[varnr]; - } + if (!lp->is_lower[varnr]) + objfromvalue = lp->upbo[varnr] - objfromvalue; + if ((lp->upbo[varnr] < infinite) && (objfromvalue > lp->upbo[varnr])) + objfromvalue = lp->upbo[varnr]; objfromvalue += lp->lowbo[varnr]; - objfromvalue = unscaled_value(lp, objfromvalue, varnr); } else objfromvalue = -infinite; @@ -9231,7 +8855,7 @@ STATIC MYBOOL construct_sensitivity_duals(lprec *lp) STATIC MYBOOL construct_sensitivity_obj(lprec *lp) { int i, l, varnr, row_nr, ok = TRUE; - LPSREAL *OrigObj = NULL, *drow = NULL, *prow = NULL, + REAL *OrigObj = NULL, *drow = NULL, *prow = NULL, sign, a, min1, min2, infinite, epsvalue, from, till; /* objective function */ @@ -9276,9 +8900,9 @@ STATIC MYBOOL construct_sensitivity_obj(lprec *lp) a = unscaled_mat(lp, drow[varnr], 0, i); if(is_maxim(lp)) a = -a; - if ((!sensrejvar) && (lp->upbo[varnr] == 0.0)) + if (lp->upbo[varnr] == 0.0) /* ignore, because this case doesn't results in further iterations */ ; - else if(((lp->is_lower[varnr] != 0) == (is_maxim(lp) == FALSE)) && (a > -epsvalue)) + else if((lp->is_lower[varnr] != 0) == (is_maxim(lp) == FALSE)) from = OrigObj[i] - a; /* less than this value gives further iterations */ else till = OrigObj[i] - a; /* bigger than this value gives further iterations */ @@ -9323,13 +8947,13 @@ STATIC MYBOOL construct_sensitivity_obj(lprec *lp) if (is_maxim(lp)) { if (a - lp->lowbo[varnr] < epsvalue) from = -infinite; /* if variable is at lower bound then decrementing objective coefficient will not result in extra iterations because it would only extra decrease the value, but since it is at its lower bound ... */ - else if (((!sensrejvar) || (lp->upbo[varnr] != 0.0)) && (lp->lowbo[varnr] + lp->upbo[varnr] - a < epsvalue)) + else if (lp->lowbo[varnr] + lp->upbo[varnr] - a < epsvalue) till = infinite; /* if variable is at upper bound then incrementing objective coefficient will not result in extra iterations because it would only extra increase the value, but since it is at its upper bound ... */ } else { if (a - lp->lowbo[varnr] < epsvalue) till = infinite; /* if variable is at lower bound then incrementing objective coefficient will not result in extra iterations because it would only extra decrease the value, but since it is at its lower bound ... */ - else if (((!sensrejvar) || (lp->upbo[varnr] != 0.0)) && (lp->lowbo[varnr] + lp->upbo[varnr] - a < epsvalue)) + else if (lp->lowbo[varnr] + lp->upbo[varnr] - a < epsvalue) from = -infinite; /* if variable is at upper bound then decrementing objective coefficient will not result in extra iterations because it would only extra increase the value, but since it is at its upper bound ... */ } } @@ -9357,7 +8981,7 @@ STATIC MYBOOL refactRecent(lprec *lp) return( FALSE ); } -STATIC MYBOOL check_if_less(lprec *lp, LPSREAL x, LPSREAL y, int variable) +STATIC MYBOOL check_if_less(lprec *lp, REAL x, REAL y, int variable) { if(y < x-scaled_value(lp, lp->epsint, variable)) { if(lp->bb_trace) @@ -9420,7 +9044,7 @@ STATIC void initialize_solution(lprec *lp, MYBOOL shiftbounds) { int i, k1, k2, *matRownr, colnr; LREAL theta; - LPSREAL value, *matValue, loB, upB; + REAL value, *matValue, loB, upB; MATrec *mat = lp->matA; /* Set bounding status indicators */ @@ -9532,7 +9156,7 @@ STATIC void initialize_solution(lprec *lp, MYBOOL shiftbounds) } /* Do final pass to get the maximum value */ - i = lps_idamax(lp->rows /* +1 */, lp->rhs, 1); + i = idamaxlpsolve(lp->rows+1, lp->rhs, 1); lp->rhsmax = fabs(lp->rhs[i]); if(shiftbounds == INITSOL_SHIFTZERO) @@ -9569,7 +9193,7 @@ STATIC void recompute_solution(lprec *lp, MYBOOL shiftbounds) STATIC int verify_solution(lprec *lp, MYBOOL reinvert, char *info) { int i, ii, n, *oldmap, *newmap, *refmap = NULL; - LPSREAL *oldrhs, err, errmax; + REAL *oldrhs, err, errmax; allocINT(lp, &oldmap, lp->rows+1, FALSE); allocINT(lp, &newmap, lp->rows+1, FALSE); @@ -9647,7 +9271,7 @@ STATIC int verify_solution(lprec *lp, MYBOOL reinvert, char *info) STATIC int identify_GUB(lprec *lp, MYBOOL mark) { int i, j, jb, je, k, knint, srh; - LPSREAL rh, mv, tv, bv; + REAL rh, mv, tv, bv; MATrec *mat = lp->matA; if((lp->equalities == 0) || !mat_validate(mat)) @@ -9703,7 +9327,7 @@ STATIC int identify_GUB(lprec *lp, MYBOOL mark) STATIC int prepare_GUB(lprec *lp) { int i, j, jb, je, k, *members = NULL; - LPSREAL rh; + REAL rh; char GUBname[16]; MATrec *mat = lp->matA; @@ -9728,7 +9352,7 @@ STATIC int prepare_GUB(lprec *lp) /* Add the GUB */ j = GUB_count(lp) + 1; - sprintf(GUBname, "GUB_%d", i); + snprintf(GUBname, sizeof(GUBname), "GUB_%d", i); add_GUB(lp, GUBname, j, k, members); /* Unmark the GUBs */ @@ -9781,7 +9405,7 @@ STATIC MYBOOL post_MIPOBJ(lprec *lp) int preprocess(lprec *lp) { int i, j, k, ok = TRUE, *new_index = NULL; - LPSREAL hold, *new_column = NULL; + REAL hold, *new_column = NULL; MYBOOL scaled, primal1, primal2; /* do not process if already preprocessed */ @@ -9802,7 +9426,7 @@ int preprocess(lprec *lp) if(doPP) { i = partial_findBlocks(lp, FALSE, FALSE); if(i < 4) - i = (int) (5 * log((LPSREAL) lp->columns / lp->rows)); + i = (int) (5 * log((REAL) lp->columns / lp->rows)); report(lp, NORMAL, "The model is %s to have %d column blocks/stages.\n", (i > 1 ? "estimated" : "set"), i); set_partialprice(lp, i, NULL, FALSE); @@ -9811,7 +9435,7 @@ int preprocess(lprec *lp) if(doPP) { i = partial_findBlocks(lp, FALSE, TRUE); if(i < 4) - i = (int) (5 * log((LPSREAL) lp->rows / lp->columns)); + i = (int) (5 * log((REAL) lp->rows / lp->columns)); report(lp, NORMAL, "The model is %s to have %d row blocks/stages.\n", (i > 1 ? "estimated" : "set"), i); set_partialprice(lp, i, NULL, TRUE); @@ -9834,7 +9458,7 @@ int preprocess(lprec *lp) if(is_piv_mode(lp, PRICE_MULTIPLE) && (primal1 || primal2)) { doPP = is_piv_mode(lp, PRICE_AUTOMULTIPLE); if(doPP) { - i = (int) (2.5*log((LPSREAL) lp->sum)); + i = (int) (2.5*log((REAL) lp->sum)); SETMAX( i, 1); set_multiprice(lp, i); } @@ -9887,7 +9511,7 @@ int preprocess(lprec *lp) if((lp->var_is_free != NULL) && (lp->var_is_free[j] > 0)) del_column(lp, lp->var_is_free[j]); /* Negate the column / flip to the positive range */ - mat_multcol(lp->matA, j, -1, TRUE); + mat_multcol(lp->matA, j, -1); if(lp->var_is_free == NULL) { if(!allocINT(lp, &lp->var_is_free, MAX(lp->columns, lp->columns_alloc) + 1, TRUE)) return(FALSE); @@ -9931,7 +9555,7 @@ int preprocess(lprec *lp) ok = FALSE; break; } - mat_multcol(lp->matA, lp->columns, -1, TRUE); + mat_multcol(lp->matA, lp->columns, -1); if(scaled) lp->scalars[lp->rows+lp->columns] = lp->scalars[i]; lp->scaling_used = (MYBOOL) scaled; @@ -9941,7 +9565,7 @@ int preprocess(lprec *lp) if(lp->names_used && (lp->col_name[j] == NULL)) { char fieldn[50]; - sprintf(fieldn, "__AntiBodyOf(%d)__", j); + snprintf(fieldn, sizeof(fieldn), "__AntiBodyOf(%d)__", j); if(!set_col_name(lp, lp->columns, fieldn)) { /* if (!set_col_name(lp, lp->columns, get_col_name(lp, j))) { */ ok = FALSE; @@ -9993,7 +9617,7 @@ int preprocess(lprec *lp) void postprocess(lprec *lp) { int i,ii,j; - LPSREAL hold; + REAL hold; /* Check if the problem actually was preprocessed */ if(!lp->wasPreprocessed) @@ -10001,12 +9625,12 @@ void postprocess(lprec *lp) /* Must compute duals here in case we have free variables; note that in this case sensitivity analysis is not possible unless done here */ - if((lp->bb_totalnodes == 0) && (lp->var_is_free == NULL)) { - if(is_presolve(lp, PRESOLVE_DUALS)) - construct_duals(lp); - if(is_presolve(lp, PRESOLVE_SENSDUALS)) - if(!construct_sensitivity_duals(lp) || !construct_sensitivity_obj(lp)) - report(lp, IMPORTANT, "postprocess: Unable to allocate working memory for duals.\n"); + if((MIP_count(lp) == 0) && + (is_presolve(lp, PRESOLVE_DUALS) || (lp->var_is_free != NULL))) + construct_duals(lp); + if(is_presolve(lp, PRESOLVE_SENSDUALS)) { + if(!construct_sensitivity_duals(lp) || !construct_sensitivity_obj(lp)) + report(lp, IMPORTANT, "postprocess: Unable to allocate working memory for duals.\n"); } /* Loop over all columns */ @@ -10016,7 +9640,7 @@ void postprocess(lprec *lp) if((lp->var_is_free != NULL) && (lp->var_is_free[j] < 0)) { /* Check if we have the simple case where the UP and LB are negated and switched */ if(-lp->var_is_free[j] == j) { - mat_multcol(lp->matA, j, -1, TRUE); + mat_multcol(lp->matA, j, -1); hold = lp->orig_upbo[i]; lp->orig_upbo[i] = my_flipsign(lp->orig_lowbo[i]); lp->orig_lowbo[i] = my_flipsign(hold); diff --git a/src/lpsolve/headers/run_headers/lp_lib.h b/src/lpSolve/src/lp_lib.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_lib.h rename to src/lpSolve/src/lp_lib.h diff --git a/src/lpsolve/build/lp_solve/lp_matrix.c b/src/lpSolve/src/lp_matrix.c similarity index 83% rename from src/lpsolve/build/lp_solve/lp_matrix.c rename to src/lpSolve/src/lp_matrix.c index 05541573..93d66f34 100644 --- a/src/lpsolve/build/lp_solve/lp_matrix.c +++ b/src/lpSolve/src/lp_matrix.c @@ -33,7 +33,7 @@ ------------------------------------------------------------------------- */ -STATIC MATrec *mat_create(lprec *lp, int rows, int columns, LPSREAL epsvalue) +STATIC MATrec *mat_create(lprec *lp, int rows, int columns, REAL epsvalue) { MATrec *newmat; @@ -94,25 +94,12 @@ STATIC MYBOOL mat_memopt(MATrec *mat, int rowextra, int colextra, int nzextra) int matalloc, colalloc, rowalloc; if((mat == NULL) || -#if 0 (++rowextra < 1) || (++colextra < 1) || (++nzextra < 1)) -#else - (rowextra < 0) || (colextra < 0) || (nzextra < 0)) -#endif return( FALSE ); - mat->rows_alloc = MIN(mat->rows_alloc, mat->rows + rowextra); - mat->columns_alloc = MIN(mat->columns_alloc, mat->columns + colextra); - mat->mat_alloc = MIN(mat->mat_alloc, mat->col_end[mat->columns] + nzextra); -#if 0 - rowalloc = mat->rows_alloc; - colalloc = mat->columns_alloc; - matalloc = mat->mat_alloc; -#else - rowalloc = mat->rows_alloc + 1; - colalloc = mat->columns_alloc + 1; - matalloc = mat->mat_alloc + 1; -#endif + rowalloc = mat->rows_alloc = MIN(mat->rows_alloc, mat->rows + rowextra); + colalloc = mat->columns_alloc = MIN(mat->columns_alloc, mat->columns + colextra); + matalloc = mat->mat_alloc = MIN(mat->mat_alloc, mat->col_end[mat->columns] + nzextra); #if MatrixColAccess==CAM_Record mat->col_mat = (MATitem *) realloc(mat->col_mat, matalloc * sizeof(*(mat->col_mat))); @@ -194,14 +181,14 @@ STATIC MYBOOL inc_mat_space(MATrec *mat, int mindelta) STATIC MYBOOL inc_matrow_space(MATrec *mat, int deltarows) { - int rowsum; + int rowsum; /* , oldrowsalloc; */ MYBOOL status = TRUE; /* Adjust lp row structures */ if(mat->rows+deltarows >= mat->rows_alloc) { /* Update memory allocation and sizes */ - //@FS: unused// oldrowsalloc = mat->rows_alloc; + /* oldrowsalloc = mat->rows_alloc; */ deltarows = DELTA_SIZE(deltarows, mat->rows); SETMAX(deltarows, DELTAROWALLOC); mat->rows_alloc += deltarows; @@ -411,7 +398,7 @@ STATIC int mat_mapreplace(MATrec *mat, LLrec *rowmap, LLrec *colmap, MATrec *mat { lprec *lp = mat->lp; int i, ib, ie, ii, j, jj, jb, je, nz, *colend, *rownr, *rownr2, *indirect = NULL; - LPSREAL *value, *value2; + REAL *value, *value2; /* Check if there is something to insert */ if((mat2 != NULL) && ((mat2->col_tag == NULL) || (mat2->col_tag[0] <= 0) || (mat_nonzeros(mat2) == 0))) @@ -588,7 +575,7 @@ STATIC int mat_zerocompact(MATrec *mat) STATIC int mat_rowcompact(MATrec *mat, MYBOOL dozeros) { int i, ie, ii, j, nn, *colend, *rownr; - LPSREAL *value; + REAL *value; nn = 0; ie = 0; @@ -766,7 +753,7 @@ STATIC int mat_shiftcols(MATrec *mat, int *bbase, int delta, LLrec *varmap) STATIC MATrec *mat_extractmat(MATrec *mat, LLrec *rowmap, LLrec *colmap, MYBOOL negated) { int *rownr, *colnr, xa, na; - LPSREAL *value; + REAL *value; MATrec *newmat = mat_create(mat->lp, mat->rows, mat->columns, mat->epsvalue); /* Initialize */ @@ -788,11 +775,11 @@ STATIC MATrec *mat_extractmat(MATrec *mat, LLrec *rowmap, LLrec *colmap, MYBOOL return( newmat ); } -STATIC MYBOOL mat_setcol(MATrec *mat, int colno, int count, LPSREAL *column, int *rowno, MYBOOL doscale, MYBOOL checkrowmode) +STATIC MYBOOL mat_setcol(MATrec *mat, int colno, int count, REAL *column, int *rowno, MYBOOL doscale, MYBOOL checkrowmode) { int i, jj = 0, elmnr, orignr, newnr, firstrow; MYBOOL *addto = NULL, isA, isNZ; - LPSREAL value, saved = 0; + REAL value, saved = 0; lprec *lp = mat->lp; /* Check if we are in row order mode and should add as row instead; @@ -816,7 +803,7 @@ STATIC MYBOOL mat_setcol(MATrec *mat, int colno, int count, LPSREAL *column, int /* Capture OF definition in column mode */ if(isA && !mat->is_roworder) { - if(isNZ && (count > 0) && (rowno[0] == 0)) { + if(isNZ && (rowno[0] == 0)) { value = column[0]; #ifdef DoMatrixRounding value = roundToPrecision(value, mat->epsvalue); @@ -892,18 +879,10 @@ STATIC MYBOOL mat_setcol(MATrec *mat, int colno, int count, LPSREAL *column, int #ifdef DoMatrixRounding value = roundToPrecision(value, mat->epsvalue); #endif - if(mat->is_roworder) { /* Fix following Ingmar Stein bug report 12.10.2006 */ - if(isA && doscale) - value = scaled_mat(lp, value, colno, rowno[i]); - if(isA) - value = my_chsign(is_chsign(lp, colno), value); - } - else { - if(isA && doscale) - value = scaled_mat(lp, value, rowno[i], colno); - if(isA) - value = my_chsign(is_chsign(lp, rowno[i]), value); - } + if(isA && doscale) + value = scaled_mat(lp, value, rowno[i], colno); + if(isA) + value = my_chsign(is_chsign(lp, rowno[i]), value); SET_MAT_ijA(jj, rowno[i], colno, value); } } @@ -915,18 +894,10 @@ STATIC MYBOOL mat_setcol(MATrec *mat, int colno, int count, LPSREAL *column, int #ifdef DoMatrixRounding value = roundToPrecision(value, mat->epsvalue); #endif - if(mat->is_roworder) { /* Fix following Ingmar Stein bug report 12.10.2006 */ - if(isA && doscale) - value = scaled_mat(lp, value, colno, i); - if(isA) - value = my_chsign(is_chsign(lp, colno), value); - } - else { - if(isA && doscale) - value = scaled_mat(lp, value, i, colno); - if(isA) - value = my_chsign(is_chsign(lp, i), value); - } + if(isA && doscale) + value = scaled_mat(lp, value, i, colno); + if(isA) + value = my_chsign(is_chsign(lp, i), value); SET_MAT_ijA(jj, i, colno, value); jj++; } @@ -946,7 +917,7 @@ STATIC MYBOOL mat_mergemat(MATrec *target, MATrec *source, MYBOOL usecolmap) { lprec *lp = target->lp; int i, ix, iy, n, *colmap = NULL; - LPSREAL *colvalue = NULL; + REAL *colvalue = NULL; if((target->rows < source->rows) || !allocREAL(lp, &colvalue, target->rows+1, FALSE)) return( FALSE ); @@ -988,14 +959,12 @@ STATIC int mat_nz_unused(MATrec *mat) return( mat->mat_alloc - mat->col_end[mat->columns] ); } -#if 0 -STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *colno, MYBOOL doscale, MYBOOL checkrowmode) +STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, REAL *row, int *colno, MYBOOL doscale, MYBOOL checkrowmode) { - lprec *lp = mat->lp; - int delta; - int k, kk, i, ii, j, jj = 0, jj_j, elmnr, orignr, newnr, firstcol, rownr, colnr, matz = 0; - MYBOOL *addto = NULL, isA, isNZ; - LPSREAL value = 0.0, saved = 0; + int kk, i, ii, j, jj = 0, jj_j, elmnr, orignr, newnr, firstcol, rownr, colnr; + MYBOOL *addto = NULL, isA, isNZ; + REAL value, saved = 0; + lprec *lp = mat->lp; /* Check if we are in row order mode and should add as column instead; the matrix will be transposed at a later stage */ @@ -1013,7 +982,7 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c return( FALSE ); if(isNZ && (count > 0)) { if(count > 1) - sortREALByINT(row, (int *) colno, count, 0, TRUE); + sortREALByINT(row, colno, count, 0, TRUE); if((colno[0] < 1) || (colno[count-1] > mat->columns)) return( FALSE ); } @@ -1021,14 +990,14 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c /* Capture OF definition in row mode */ if(isA && mat->is_roworder) { lp->orig_obj[rowno] = 0; - if(isNZ && (count > 0) && (colno[0] == 0)) { + if(isNZ && (colno[0] == 0)) { value = row[0]; - if(doscale) - value = scaled_mat(lp, value, 0, rowno); - value = my_chsign(is_maxim(lp), value); #ifdef DoMatrixRounding value = roundToPrecision(value, mat->epsvalue); #endif + if(doscale) + value = scaled_mat(lp, value, 0, rowno); + value = my_chsign(is_maxim(lp), value); lp->orig_obj[rowno] = value; count--; row++; @@ -1036,115 +1005,43 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c } else if(!isNZ && (row[0] != 0)) { value = saved = row[0]; - if(doscale) - value = scaled_mat(lp, value, 0, rowno); - value = my_chsign(is_maxim(lp), value); #ifdef DoMatrixRounding value = roundToPrecision(value, mat->epsvalue); #endif + if(doscale) + value = scaled_mat(lp, value, 0, rowno); + value = my_chsign(is_maxim(lp), value); lp->orig_obj[rowno] = value; row[0] = 0; } - else { + else lp->orig_obj[rowno] = 0; - value = 0; - } } /* Optionally tally and map the new non-zero values */ - i = mat->row_end[rowno-1]; - ii = mat->row_end[rowno]; // ****** KE 20070106 - was "-1" firstcol = mat->columns + 1; if(isNZ) { - /* See if we can do fast in-place replacements of leading items */ - colnr = 1; /* initialise in case of an empty row */ - while((i < ii) /* && (count > 0) */ && ((colnr = ROW_MAT_COLNR(i)) == *colno) && (count > 0)) { - value = *row; // ****** KE 20080111 - Added line - if(mat->is_roworder) { - if(isA && doscale) - value = scaled_mat(lp, value, colnr, rowno); - if(isA) - value = my_chsign(is_chsign(lp, colnr), value); - } - else { - if(isA && doscale) - value = scaled_mat(lp, value, rowno, colnr); - if(isA) - value = my_chsign(is_chsign(lp, rowno), value); - } -#ifdef DoMatrixRounding - value = roundToPrecision(value, mat->epsvalue); - if(value == 0) - matz++; -#endif - ROW_MAT_VALUE(i) = value; - i++; - count--; - row++; - colno++; - } - if(i >= ii) - colnr = 0; - /* Proceed with remaining entries */ newnr = count; - if(newnr > 0) + if(newnr) firstcol = colno[0]; } else { newnr = 0; - kk = mat->columns; - if(i < ii) - colnr = ROW_MAT_COLNR(i); - else - colnr = 0; - for(k = 1; k <= kk; k++) { - value = row[k]; // ****** KE 20080111 - Added line - if(fabs(value) > mat->epsvalue) { - /* See if we can do fast in-place replacements of leading items */ - if((addto == NULL) && (i < ii) && (colnr == k)) { - if(mat->is_roworder) { - if(isA && doscale) - value = scaled_mat(lp, value, colnr, rowno); - if(isA) - value = my_chsign(is_chsign(lp, colnr), value); - } - else { - if(isA && doscale) - value = scaled_mat(lp, value, rowno, colnr); - if(isA) - value = my_chsign(is_chsign(lp, rowno), value); - } -#ifdef DoMatrixRounding - value = roundToPrecision(value, mat->epsvalue); - if(value == 0) - matz++; -#endif - ROW_MAT_VALUE(i) = value; - i++; - if(i < ii) - colnr = ROW_MAT_COLNR(i); - else - colnr = 0; - } - /* Otherwise update addto-list */ - else { - if(addto == NULL) { - if(!allocMYBOOL(lp, &addto, mat->columns + 1, TRUE)) - return( FALSE ); - firstcol = k; - } - addto[k] = TRUE; - newnr++; - } + if(!allocMYBOOL(lp, &addto, mat->columns + 1, TRUE)) { + return( FALSE ); + } + for(i = mat->columns; i >= 1; i--) { + if(fabs(row[i]) > mat->epsvalue) { + addto[i] = TRUE; + firstcol = i; + newnr++; } } } - if(newnr == 0) - if (FALSE) - return( TRUE ); /* Make sure we have enough matrix space */ - if((newnr > 0) && (mat_nz_unused(mat) <= newnr) && !inc_mat_space(mat, newnr)) { + /* if(!inc_mat_space(mat, newnr)) { */ + if((mat_nz_unused(mat) <= newnr) && !inc_mat_space(mat, newnr)) { newnr = 0; goto Done; } @@ -1152,32 +1049,27 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c /* Pack initial entries if existing row data has a lower column start index than the first index of the new vector */ orignr = mat_nonzeros(mat); - /* delta = newnr - mat_rowlength(mat, rowno);*/ + /* k = newnr - mat_rowlength(mat, rowno); */ kk = 0; if(rowno == 0) ii = 0; else ii = mat->row_end[rowno-1]; - if((orignr == 0) || (ii >= orignr)) - j = firstcol; - else if(isNZ||TRUE) - j = colnr; + j = firstcol /* 1 */; else - j = ROW_MAT_COLNR(ii); /* first column with a value on that row */ - - jj = mat->col_end[firstcol-1]; /* Set the index of the insertion point for the first new value */ + j = ROW_MAT_COLNR(ii); + jj = mat->col_end[firstcol - 1]; if(jj >= orignr) colnr = firstcol; else - colnr = COL_MAT_COLNR(jj); /* first column with a value starting from firstcol */ - - if((j > 0) && (j < colnr)) { - jj = elmnr = mat->col_end[j-1]; + colnr = COL_MAT_COLNR(jj); + if(j < colnr) { + elmnr = mat->col_end[j-1]; + jj = elmnr; for( ; j < colnr; j++) { /* Shift entries in current column */ - k = mat->col_end[j]; - for( ; jj < k; jj++) { + for( ; jj < mat->col_end[j]; jj++) { if(COL_MAT_ROWNR(jj) != rowno) { COL_MAT_COPY(elmnr, jj); elmnr++; @@ -1186,18 +1078,14 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c /* Update next column start index */ mat->col_end[j] = elmnr; } - delta = elmnr - jj; /* The shrinkage count */ - } - else { - delta = 0; - /* Adjust for case where we simply append values - jj is initially the first column item */ - if((mat->col_end[firstcol] == orignr) && 0) - jj = orignr; + jj_j = jj - elmnr; /* The shrinkage count */ } + else + jj_j = 0; /* Make sure we have sufficient space for any additional entries and move existing data down; this ensures that we only have to relocate matrix elements up in the next stage */ - jj_j = MAX(0, newnr + delta); + jj_j = MAX(0, newnr - jj_j); if(jj_j > 0) { if(!inc_mat_space(mat, jj_j)) { FREE(addto); @@ -1210,12 +1098,13 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c } /* Handle case where the matrix was empty before (or we can simply append) */ - if((delta >= 0) && (mat->col_end[firstcol] == orignr) && 0) { + /* if(orignr == 0) { */ + if(mat->col_end[firstcol] == orignr) { if(isNZ) elmnr = count; else elmnr = mat->columns; - jj_j = mat->col_end[firstcol]; + jj_j = mat->col_end[firstcol] /* 0 */; for(newnr = 0; newnr < elmnr; newnr++) { if(isNZ) colnr = colno[newnr]; @@ -1226,20 +1115,18 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c mat->col_end[firstcol] = jj_j; firstcol++; } - if(isNZ || ((addto != NULL) && addto[colnr])) { + if(isNZ || addto[colnr]) { if(isNZ) value = row[newnr]; else value = row[colnr]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif if(isA && doscale) value = scaled_mat(lp, value, rowno, colnr); if(isA) value = my_chsign(is_chsign(lp, rowno), value); -#ifdef DoMatrixRounding - value = roundToPrecision(value, mat->epsvalue); - if(value == 0) - matz++; -#endif SET_MAT_ijA(jj_j, rowno, colnr, value); jj_j++; /* Update last column start position */ @@ -1258,12 +1145,12 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c /* Start from the top of the first non-zero column of the new row */ elmnr = orignr + jj_j; - if(jj <= elmnr) { + if(jj < elmnr) { if(isNZ) newnr = 0; else newnr = firstcol - 1; - j = jj - mat->col_end[firstcol-1]; + j = jj - mat->col_end[firstcol - 1]; colnr = firstcol; while((jj < elmnr) || (newnr < count)) { @@ -1281,10 +1168,7 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c } else { rownr = rowno; - if(!isNZ) /* KE added this conditional on 13.9.2006 */ - colnr = firstcol + 1; - else - colnr = mat->columns + 1; + colnr = mat->columns + 1; } if(isNZ) { @@ -1297,10 +1181,17 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c kk = newnr + 1; /* Test if there is an available new item ... */ +#if 1 /* PENO fix 27.2.2005 */ if((isNZ && (kk > colnr)) || /* If this is not the case */ (!isNZ && ((kk > colnr) || (!addto[kk])))) { /* DELETE if there is an existing value */ if(!isNZ && (kk <= colnr)) +#else + if((isNZ && (kk > colnr)) || /* If this is not the case */ + (!isNZ && !addto[kk])) { + /* DELETE if there is an existing value */ + if(!isNZ) +#endif newnr++; if(rownr == rowno) { kk = jj_j; @@ -1323,15 +1214,13 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c else value = row[newnr+1]; newnr++; - if(isA && doscale) - value = scaled_mat(lp, value, rowno, kk); - if(isA) - value = my_chsign(is_chsign(lp, rowno), value); #ifdef DoMatrixRounding value = roundToPrecision(value, mat->epsvalue); - if(value == 0) - matz++; #endif + if(isA && doscale) + value = scaled_mat(lp, value, rowno, colnr); + if(isA) + value = my_chsign(is_chsign(lp, rowno), value); SET_MAT_ijA(jj_j, rowno, kk, value); /* Adjust if we have inserted an element */ @@ -1367,10 +1256,6 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c firstcol++; } } - - /* Compact in the case that we added zeros and set flag for row index update */ - if(matz > 0) - mat_zerocompact(mat); mat->row_end_valid = FALSE; Done: @@ -1381,250 +1266,11 @@ STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *c } -#else - -STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *colno, MYBOOL doscale, MYBOOL checkrowmode) -{ - lprec *lp = mat->lp; - int delta, delta1; - int k, i, ii, j, jj_j, lendense, - origidx = 0, newidx, orignz, newnz, - rownr, colnr, colnr1; - MYBOOL isA, isNZ; - LPSREAL value = 0.0; - - /* Check if we are in row order mode and should add as column instead; - the matrix will be transposed at a later stage */ - if(checkrowmode && mat->is_roworder) - return( mat_setcol(mat, rowno, count, row, colno, doscale, FALSE) ); - - /* Do initialization and validation */ - if(!mat_validate(mat)) - return( FALSE ); - isA = (MYBOOL) (mat == lp->matA); - if(doscale && isA && !lp->scaling_used) - doscale = FALSE; - isNZ = (MYBOOL) (colno != NULL); - lendense = (mat->is_roworder ? lp->rows : lp->columns); - if((count < 0) || (count > lendense)) - return( FALSE ); - colnr1 = lendense + 1; - - /* Capture OF definition in row mode */ - if(isA && mat->is_roworder) { - lp->orig_obj[rowno] = 0; - if((count > 0) && (colno[0] == 0)) { - value = row[0]; - if(doscale) - value = scaled_mat(lp, value, 0, rowno); - value = my_chsign(is_maxim(lp), value); -#ifdef DoMatrixRounding - value = roundToPrecision(value, mat->epsvalue); -#endif - lp->orig_obj[rowno] = value; - if(isNZ) { - colno++; - row++; - count--; - } - } - else { - lp->orig_obj[rowno] = 0; - value = 0; - } - } - - /* Make local working data copies */ - if(!isNZ) { - LPSREAL *tmprow = NULL; - if(!allocINT(lp, &colno, lendense+1, FALSE)) - return( FALSE ); - newnz = 0; - for(i = 1; i <= lendense; i++) - if((value = row[i]) != 0) { - if((tmprow == NULL) && !allocREAL(lp, &tmprow, lendense-i+1, FALSE)) { - FREE(colno); - return( FALSE ); - } - tmprow[newnz] = value; - colno[newnz++] = i; - } - count = newnz; - row = tmprow; - } - else { - int *tmpcolno = NULL; - if(!allocINT(lp, &tmpcolno, lendense, FALSE)) - return( FALSE ); - newnz = count; - MEMCOPY(tmpcolno, colno, newnz); - colno = tmpcolno; - if(newnz > 1) - sortREALByINT(row, (int *) colno, newnz, 0, TRUE); - if((newnz > 0) && ((colno[0] < 0) || (colno[newnz-1] > lendense))) { - FREE(colno); - newnz = 0; - return( FALSE ); - } - } - - /* Make sure we have enough matrix space */ - i = mat->row_end[rowno-1]; - ii = mat->row_end[rowno]; - delta1 = delta = count - (ii-i); - colnr1 = (newnz > 0 ? colno[0] : lendense+1); - - /* Pack initial entries if existing row data has a lower column - start index than the first index of the new vector */ - orignz = mat_nonzeros(mat); - j = (i >= orignz ? colnr1 : ROW_MAT_COLNR(i)); - - /* Index of the column-top insertion point for the first new value */ - origidx = mat->col_end[colnr1-1]; - colnr = (origidx >= orignz ? colnr1 : COL_MAT_COLNR(origidx)); - - if(j < colnr) { - origidx = newidx = mat->col_end[j-1]; - for( ; j < colnr; j++) { - /* Shift entries in current column */ - jj_j = mat->col_end[j]; - for( ; origidx < jj_j; origidx++) { - if(COL_MAT_ROWNR(origidx) != rowno) { - if(newidx != origidx) { - COL_MAT_COPY(newidx, origidx); - } - newidx++; - } - } - /* Update next column start index */ - mat->col_end[j] = newidx; - } - delta = newidx - origidx; /* The first stage element shrinkage count */ - } - else { - delta = 0; - newidx = origidx; - } - - /* Make sure we have sufficient space for any additional entries and move existing data down; - this ensures that we only have to relocate matrix elements up in the next stage */ - jj_j = MAX(0, (int) newnz + delta); - - j = !((orignz == lendense) && (newnz == orignz) && (delta1 == 0)) && (jj_j > 0) && (orignz > origidx); - - if ((j) && (jj_j > delta1)) - delta1 = jj_j; - - if((delta1 > 0) && (mat_nz_unused(mat) <= delta1) && !inc_mat_space(mat, delta1)) { - newnz = 0; - goto Done; - } - - if(j) { - COL_MAT_MOVE(origidx+jj_j, origidx, orignz-origidx); - origidx += jj_j; - orignz += jj_j; - } - - /* Start from the top of the first non-zero column of the new row */ - newnz = 0; - j = origidx - mat->col_end[colnr1-1]; - k = colnr1; /* Last column for which col_end is valid/updated */ - while((colnr1 <= lendense) || (origidx < orignz)) { - - /* Get the column index of the active update item */ - if(newnz < count) - colnr1 = colno[newnz]; - else - colnr1 = lendense + 1; - - /* Get coordinate of active existing matrix entries */ - if(origidx < orignz) { - rownr = COL_MAT_ROWNR(origidx); - colnr = COL_MAT_COLNR(origidx); - } - else { - if(colnr1 > lendense) - break; - rownr = rowno; - colnr = lendense + 1; - } - - /* Update column start position if we just crossed into a column */ - jj_j = origidx - j; - i = MIN(colnr, colnr1); - for(; k < i; k++) - mat->col_end[k] = jj_j; - - /* Test if there is an available new item ... */ - if(colnr1 > colnr) { /* If this is not the case */ - /* DELETE if there is an existing value */ - if(rownr == rowno) { -ForceDelete: - j++; - delta--; - origidx++; - continue; - } - } - else if((colnr > colnr1) || /* Existing column index > new => INSERT */ - ((colnr == colnr1) && (rownr >= rowno)) ) { /* Same column index, existing row >= target row => INSERT/REPLACE */ - - value = row[newnz]; - newnz++; - if(isA && doscale) - value = scaled_mat(lp, value, rowno, colnr1); - if(isA) - value = my_chsign(is_chsign(lp, rowno), value); -#ifdef DoMatrixRounding - value = roundToPrecision(value, mat->epsvalue); - if(value == 0) { - if((colnr > colnr1) || (rownr > rowno)) - ; - else - goto ForceDelete; - } -#endif - SET_MAT_ijA(jj_j, rowno, colnr1, value); - - /* Adjust if we have inserted an element */ - if((colnr > colnr1) || (rownr > rowno)) { - j--; - origidx--; - jj_j++; - delta++; - } - origidx++; - continue; - } - - /* Shift the matrix element up by the active difference */ - if(jj_j != origidx) { - COL_MAT_COPY(jj_j, origidx); - } - origidx++; - } - - /* Update pending / incomplete column start position */ - jj_j = origidx - j; - for(; k <= lendense; k++) - mat->col_end[k] = jj_j; - mat->row_end_valid = FALSE; - -Done: - if(!isNZ) - FREE(row); - FREE(colno); - return( (MYBOOL) (newnz > 0) ); - -} /* mat_setrow */ -#endif - -STATIC int mat_appendrow(MATrec *mat, int count, LPSREAL *row, int *colno, LPSREAL mult, MYBOOL checkrowmode) +STATIC int mat_appendrow(MATrec *mat, int count, REAL *row, int *colno, REAL mult, MYBOOL checkrowmode) { int i, j, jj = 0, stcol, elmnr, orignr, newnr, firstcol; MYBOOL *addto = NULL, isA, isNZ; - LPSREAL value, saved = 0; + REAL value, saved = 0; lprec *lp = mat->lp; /* Check if we are in row order mode and should add as column instead; @@ -1641,8 +1287,7 @@ STATIC int mat_appendrow(MATrec *mat, int count, LPSREAL *row, int *colno, LPSRE if((colno[0] < 1) || (colno[count-1] > mat->columns)) return( 0 ); } - /* else if((row != NULL) && !mat->is_roworder) */ - else if(!isNZ && (row != NULL) && !mat->is_roworder) + else if(row != NULL) row[0] = 0; /* Capture OF definition in row mode */ @@ -1652,25 +1297,25 @@ STATIC int mat_appendrow(MATrec *mat, int count, LPSREAL *row, int *colno, LPSRE #ifdef DoMatrixRounding value = roundToPrecision(value, mat->epsvalue); #endif - value = scaled_mat(lp, value, 0, lp->columns); + value = scaled_mat(lp, value, 0, mat->columns); value = my_chsign(is_maxim(lp), value); - lp->orig_obj[lp->columns] = value; + lp->orig_obj[mat->columns] = value; count--; row++; colno++; } - else if(!isNZ && (row != NULL) && (row[0] != 0)) { + else if(!isNZ && (row[0] != 0)) { value = saved = row[0]; #ifdef DoMatrixRounding value = roundToPrecision(value, mat->epsvalue); #endif - value = scaled_mat(lp, value, 0, lp->columns); + value = scaled_mat(lp, value, 0, mat->columns); value = my_chsign(is_maxim(lp), value); - lp->orig_obj[lp->columns] = value; + lp->orig_obj[mat->columns] = value; row[0] = 0; } else - lp->orig_obj[lp->columns] = 0; + lp->orig_obj[mat->columns] = 0; } /* Optionally tally and map the new non-zero values */ @@ -1684,16 +1329,14 @@ STATIC int mat_appendrow(MATrec *mat, int count, LPSREAL *row, int *colno, LPSRE } else { newnr = 0; - if(row != NULL) { - if(!allocMYBOOL(lp, &addto, mat->columns + 1, TRUE)) { - return( newnr ); - } - for(i = mat->columns; i >= 1; i--) { - if(fabs(row[i]) > mat->epsvalue) { - addto[i] = TRUE; - firstcol = i; - newnr++; - } + if(!allocMYBOOL(lp, &addto, mat->columns + 1, TRUE)) { + return( newnr ); + } + for(i = mat->columns; i >= 1; i--) { + if(fabs(row[i]) > mat->epsvalue) { + addto[i] = TRUE; + firstcol = i; + newnr++; } } } @@ -1728,11 +1371,8 @@ STATIC int mat_appendrow(MATrec *mat, int count, LPSREAL *row, int *colno, LPSRE value = roundToPrecision(value, mat->epsvalue); #endif value *= mult; - if(isA) { - if(mat->is_roworder) - value = my_chsign(is_chsign(lp, j), value); + if(isA) value = scaled_mat(lp, value, mat->rows, j); - } SET_MAT_ijA(elmnr, mat->rows, j, value); elmnr--; } @@ -1755,10 +1395,10 @@ STATIC int mat_appendrow(MATrec *mat, int count, LPSREAL *row, int *colno, LPSRE } -STATIC int mat_appendcol(MATrec *mat, int count, LPSREAL *column, int *rowno, LPSREAL mult, MYBOOL checkrowmode) +STATIC int mat_appendcol(MATrec *mat, int count, REAL *column, int *rowno, REAL mult, MYBOOL checkrowmode) { int i, row, elmnr, lastnr; - LPSREAL value; + REAL value; MYBOOL isA, isNZ; lprec *lp = mat->lp; @@ -1768,29 +1408,12 @@ STATIC int mat_appendcol(MATrec *mat, int count, LPSREAL *column, int *rowno, LP return( mat_appendrow(mat, count, column, rowno, mult, FALSE) ); /* Make sure we have enough space */ -/* if(!inc_mat_space(mat, mat->rows+1)) return( 0 ); -*/ - if(column == NULL) - i = 0; - else if(rowno != NULL) - i = count; - else { - int nrows = mat->rows; - - elmnr = 0; - for(i = 1; i <= nrows; i++) - if(column[i] != 0) - elmnr++; - i = elmnr; - } - if((mat_nz_unused(mat) <= i) && !inc_mat_space(mat, i)) - return( 0 ); /* Do initialization and validation */ isA = (MYBOOL) (mat == lp->matA); - isNZ = (MYBOOL) (column == NULL || rowno != NULL); + isNZ = (MYBOOL) (rowno != NULL); if(isNZ && (count > 0)) { if(count > 1) sortREALByINT(column, rowno, count, 0, TRUE); @@ -1952,7 +1575,7 @@ STATIC MYBOOL mat_validate(MATrec *mat) return( TRUE ); } -MYBOOL mat_get_data(lprec *lp, int matindex, MYBOOL isrow, int **rownr, int **colnr, LPSREAL **value) +MYBOOL mat_get_data(lprec *lp, int matindex, MYBOOL isrow, int **rownr, int **colnr, REAL **value) { MATrec *mat = lp->matA; @@ -2162,7 +1785,7 @@ int mat_findins(MATrec *mat, int row, int column, int *insertpos, MYBOOL validat return( exitvalue ); } -STATIC LPSREAL mat_getitem(MATrec *mat, int row, int column) +STATIC REAL mat_getitem(MATrec *mat, int row, int column) { int elmnr; @@ -2180,7 +1803,7 @@ STATIC LPSREAL mat_getitem(MATrec *mat, int row, int column) } } -STATIC MYBOOL mat_additem(MATrec *mat, int row, int column, LPSREAL delta) +STATIC MYBOOL mat_additem(MATrec *mat, int row, int column, REAL delta) { int elmnr; @@ -2202,12 +1825,12 @@ STATIC MYBOOL mat_additem(MATrec *mat, int row, int column, LPSREAL delta) } } -STATIC MYBOOL mat_setitem(MATrec *mat, int row, int column, LPSREAL value) +STATIC MYBOOL mat_setitem(MATrec *mat, int row, int column, REAL value) { return( mat_setvalue(mat, row, column, value, FALSE) ); } -STATIC void mat_multrow(MATrec *mat, int row_nr, LPSREAL mult) +STATIC void mat_multrow(MATrec *mat, int row_nr, REAL mult) { int i, k1, k2; @@ -2238,7 +1861,7 @@ STATIC void mat_multrow(MATrec *mat, int row_nr, LPSREAL mult) } } -STATIC void mat_multcol(MATrec *mat, int col_nr, LPSREAL mult, MYBOOL DoObj) +STATIC void mat_multcol(MATrec *mat, int col_nr, REAL mult) { int i, ie; MYBOOL isA; @@ -2258,18 +1881,17 @@ STATIC void mat_multcol(MATrec *mat, int col_nr, LPSREAL mult, MYBOOL DoObj) for(i = mat->col_end[col_nr - 1]; i < ie; i++) COL_MAT_VALUE(i) *= mult; if(isA) { - if(DoObj) - mat->lp->orig_obj[col_nr] *= mult; + mat->lp->orig_obj[col_nr] *= mult; if(get_Lrows(mat->lp) > 0) - mat_multcol(mat->lp->matL, col_nr, mult, DoObj); + mat_multcol(mat->lp->matL, col_nr, mult); } } -STATIC void mat_multadd(MATrec *mat, LPSREAL *lhsvector, int varnr, LPSREAL mult) +STATIC void mat_multadd(MATrec *mat, REAL *lhsvector, int varnr, REAL mult) { int colnr; register int ib, ie, *matRownr; - register LPSREAL *matValue; + register REAL *matValue; /* Handle case of a slack variable */ if(varnr <= mat->lp->rows) { @@ -2300,7 +1922,7 @@ STATIC void mat_multadd(MATrec *mat, LPSREAL *lhsvector, int varnr, LPSREAL mult } -STATIC MYBOOL mat_setvalue(MATrec *mat, int Row, int Column, LPSREAL Value, MYBOOL doscale) +STATIC MYBOOL mat_setvalue(MATrec *mat, int Row, int Column, REAL Value, MYBOOL doscale) { int elmnr, lastelm, i, RowA = Row, ColumnA = Column; MYBOOL isA; @@ -2411,7 +2033,7 @@ STATIC MYBOOL mat_setvalue(MATrec *mat, int Row, int Column, LPSREAL Value, MYBO return(TRUE); } -STATIC MYBOOL mat_appendvalue(MATrec *mat, int Row, LPSREAL Value) +STATIC MYBOOL mat_appendvalue(MATrec *mat, int Row, REAL Value) { int *elmnr, Column = mat->columns; @@ -2491,11 +2113,11 @@ STATIC int mat_findcolumn(MATrec *mat, int matindex) return(j); } -STATIC int mat_expandcolumn(MATrec *mat, int colnr, LPSREAL *column, int *nzlist, MYBOOL signedA) +STATIC int mat_expandcolumn(MATrec *mat, int colnr, REAL *column, int *nzlist, MYBOOL signedA) { MYBOOL isA = (MYBOOL) (mat->lp->matA == mat); int i, ie, j, nzcount = 0; - LPSREAL *matValue; + REAL *matValue; int *matRownr; signedA &= isA; @@ -2532,7 +2154,7 @@ STATIC MYBOOL mat_computemax(MATrec *mat) int *rownr = &COL_MAT_ROWNR(0), *colnr = &COL_MAT_COLNR(0), i = 0, ie = mat->col_end[mat->columns], ez = 0; - LPSREAL *value = &COL_MAT_VALUE(0), epsmachine = mat->lp->epsmachine, absvalue; + REAL *value = &COL_MAT_VALUE(0), epsmachine = mat->lp->epsmachine, absvalue; /* Prepare arrays */ if(!allocREAL(mat->lp, &mat->colmax, mat->columns_alloc+1, AUTOMATIC) || @@ -2598,7 +2220,7 @@ STATIC MYBOOL mat_transpose(MATrec *mat) swapPTR((void **) &mat->col_mat, (void **) &newmat); FREE(newmat); #else /*if MatrixColAccess==CAM_Vector*/ - LPSREAL *newValue = NULL; + REAL *newValue = NULL; int *newRownr = NULL; allocREAL(mat->lp, &newValue, mat->mat_alloc, FALSE); allocINT(mat->lp, &newRownr, mat->mat_alloc, FALSE); @@ -2668,11 +2290,11 @@ STATIC int incrementUndoLadder(DeltaVrec *DV) DV->tracker->columns++; return(DV->activelevel); } -STATIC MYBOOL modifyUndoLadder(DeltaVrec *DV, int itemno, LPSREAL target[], LPSREAL newvalue) +STATIC MYBOOL modifyUndoLadder(DeltaVrec *DV, int itemno, REAL target[], REAL newvalue) { MYBOOL status; int varindex = itemno; - LPSREAL oldvalue = target[itemno]; + REAL oldvalue = target[itemno]; #ifndef UseMilpSlacksRCF /* Check if we should include ranged constraints */ varindex -= DV->lp->rows; @@ -2688,7 +2310,7 @@ STATIC int countsUndoLadder(DeltaVrec *DV) else return( 0 ); } -STATIC int restoreUndoLadder(DeltaVrec *DV, LPSREAL target[]) +STATIC int restoreUndoLadder(DeltaVrec *DV, REAL target[]) { int iD = 0; @@ -2697,7 +2319,7 @@ STATIC int restoreUndoLadder(DeltaVrec *DV, LPSREAL target[]) int iB = mat->col_end[DV->activelevel-1], iE = mat->col_end[DV->activelevel], *matRownr = &COL_MAT_ROWNR(iB); - LPSREAL *matValue = &COL_MAT_VALUE(iB), + REAL *matValue = &COL_MAT_VALUE(iB), oldvalue; /* Restore the values */ @@ -2738,7 +2360,7 @@ STATIC MYBOOL freeUndoLadder(DeltaVrec **DV) return(TRUE); } -STATIC MYBOOL appendUndoPresolve(lprec *lp, MYBOOL isprimal, LPSREAL beta, int colnrDep) +STATIC MYBOOL appendUndoPresolve(lprec *lp, MYBOOL isprimal, REAL beta, int colnrDep) { MATrec *mat; @@ -2773,7 +2395,7 @@ STATIC MYBOOL appendUndoPresolve(lprec *lp, MYBOOL isprimal, LPSREAL beta, int c else return( FALSE ); } -STATIC MYBOOL addUndoPresolve(lprec *lp, MYBOOL isprimal, int colnrElim, LPSREAL alpha, LPSREAL beta, int colnrDep) +STATIC MYBOOL addUndoPresolve(lprec *lp, MYBOOL isprimal, int colnrElim, REAL alpha, REAL beta, int colnrDep) { int ix; DeltaVrec **DV; @@ -2950,7 +2572,7 @@ STATIC MYBOOL addUndoPresolve(lprec *lp, MYBOOL isprimal, int colnrElim, LPSREAL STATIC MYBOOL __WINAPI invert(lprec *lp, MYBOOL shiftbounds, MYBOOL final) { MYBOOL *usedpos, resetbasis; - LPSREAL test; + REAL test; int k, i, j; int singularities, usercolB; @@ -3025,14 +2647,8 @@ STATIC MYBOOL __WINAPI invert(lprec *lp, MYBOOL shiftbounds, MYBOOL final) lp->bfp_finishfactorization(lp); /* Recompute the RHS ( Ref. lp_solve inverse logic and Chvatal p. 121 ) */ -#ifdef DebugInv - blockWriteLREAL(stdout, "RHS-values pre invert", lp->rhs, 0, lp->rows); -#endif recompute_solution(lp, shiftbounds); restartPricer(lp, AUTOMATIC); -#ifdef DebugInv - blockWriteLREAL(stdout, "RHS-values post invert", lp->rhs, 0, lp->rows); -#endif Cleanup: /* Check for numerical instability indicated by frequent refactorizations */ @@ -3049,9 +2665,9 @@ STATIC MYBOOL __WINAPI invert(lprec *lp, MYBOOL shiftbounds, MYBOOL final) } /* invert */ -STATIC MYBOOL fimprove(lprec *lp, LPSREAL *pcol, int *nzidx, LPSREAL roundzero) +STATIC MYBOOL fimprove(lprec *lp, REAL *pcol, int *nzidx, REAL roundzero) { - LPSREAL *errors, sdp; + REAL *errors, sdp; int j; MYBOOL Ok = TRUE; @@ -3081,10 +2697,10 @@ STATIC MYBOOL fimprove(lprec *lp, LPSREAL *pcol, int *nzidx, LPSREAL roundzero) return(Ok); } -STATIC MYBOOL bimprove(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero) +STATIC MYBOOL bimprove(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero) { int j; - LPSREAL *errors, err, maxerr; + REAL *errors, err, maxerr; MYBOOL Ok = TRUE; allocREAL(lp, &errors, lp->sum + 1, FALSE); @@ -3130,7 +2746,7 @@ STATIC MYBOOL bimprove(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundz return(Ok); } -STATIC void ftran(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero) +STATIC void ftran(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero) { #if 0 if(is_action(lp->improve, IMPROVE_SOLUTION) && lp->bfp_pivotcount(lp)) @@ -3140,7 +2756,7 @@ STATIC void ftran(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero) lp->bfp_ftran_normal(lp, rhsvector, nzidx); } -STATIC void btran(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero) +STATIC void btran(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero) { #if 0 if(is_action(lp->improve, IMPROVE_SOLUTION) && lp->bfp_pivotcount(lp)) @@ -3150,7 +2766,7 @@ STATIC void btran(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero) lp->bfp_btran_normal(lp, rhsvector, nzidx); } -STATIC MYBOOL fsolve(lprec *lp, int varin, LPSREAL *pcol, int *nzidx, LPSREAL roundzero, LPSREAL ofscalar, MYBOOL prepareupdate) +STATIC MYBOOL fsolve(lprec *lp, int varin, REAL *pcol, int *nzidx, REAL roundzero, REAL ofscalar, MYBOOL prepareupdate) /* Was setpivcol in versions earlier than 4.0.1.8 - KE */ { MYBOOL ok = TRUE; @@ -3170,7 +2786,7 @@ STATIC MYBOOL fsolve(lprec *lp, int varin, LPSREAL *pcol, int *nzidx, LPSREAL ro } /* fsolve */ -STATIC MYBOOL bsolve(lprec *lp, int row_nr, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero, LPSREAL ofscalar) +STATIC MYBOOL bsolve(lprec *lp, int row_nr, REAL *rhsvector, int *nzidx, REAL roundzero, REAL ofscalar) { MYBOOL ok = TRUE; @@ -3187,8 +2803,8 @@ STATIC MYBOOL bsolve(lprec *lp, int row_nr, LPSREAL *rhsvector, int *nzidx, LPSR /* Vector compression and expansion routines */ -STATIC MYBOOL vec_compress(LPSREAL *densevector, int startpos, int endpos, LPSREAL epsilon, - LPSREAL *nzvector, int *nzindex) +STATIC MYBOOL vec_compress(REAL *densevector, int startpos, int endpos, REAL epsilon, + REAL *nzvector, int *nzindex) { int n; @@ -3211,7 +2827,7 @@ STATIC MYBOOL vec_compress(LPSREAL *densevector, int startpos, int endpos, LPSRE return( TRUE ); } -STATIC MYBOOL vec_expand(LPSREAL *nzvector, int *nzindex, LPSREAL *densevector, int startpos, int endpos) +STATIC MYBOOL vec_expand(REAL *nzvector, int *nzindex, REAL *densevector, int startpos, int endpos) { int i, n; @@ -3241,7 +2857,7 @@ STATIC MYBOOL get_colIndexA(lprec *lp, int varset, int *colindex, MYBOOL append) { int i, varnr, P1extraDim, vb, ve, n, nrows = lp->rows, nsum = lp->sum; MYBOOL omitfixed, omitnonfixed; - LPSREAL v; + REAL v; /* Find what variable range to scan - default is {SCAN_USERVARS} */ /* First determine the starting position; add from the top, going down */ @@ -3294,7 +2910,7 @@ STATIC MYBOOL get_colIndexA(lprec *lp, int varset, int *colindex, MYBOOL append) #endif } - /* Find if the variable is in the scope - default is {Ø} */ + /* Find if the variable is in the scope - default is {?} */ i = lp->is_basic[varnr]; if((varset & USE_BASICVARS) > 0 && (i)) ; @@ -3317,16 +2933,16 @@ STATIC MYBOOL get_colIndexA(lprec *lp, int varset, int *colindex, MYBOOL append) return(TRUE); } -STATIC int prod_Ax(lprec *lp, int *coltarget, LPSREAL *input, int *nzinput, - LPSREAL roundzero, LPSREAL ofscalar, - LPSREAL *output, int *nzoutput, int roundmode) +STATIC int prod_Ax(lprec *lp, int *coltarget, REAL *input, int *nzinput, + REAL roundzero, REAL ofscalar, + REAL *output, int *nzoutput, int roundmode) /* prod_Ax is only used in fimprove; note that it is NOT VALIDATED/verified as of 20030801 - KE */ { - int j, colnr, ib, ie, vb; + int j, colnr, ib, ie, vb; /*, ve; */ MYBOOL localset, localnz = FALSE, isRC; MATrec *mat = lp->matA; - LPSREAL sdp; - LPSREAL *value; + REAL sdp; + REAL *value; int *rownr; /* Find what variable range to scan - default is {SCAN_USERVARS} */ @@ -3352,7 +2968,7 @@ STATIC int prod_Ax(lprec *lp, int *coltarget, LPSREAL *input, int *nzinput, /* Scan the columns */ vb = 1; - //@FS: unused// ve = coltarget[0]; + /* ve = coltarget[0]; */ for(vb = 1; vb <= coltarget[0]; vb++) { colnr = coltarget[vb]; j = lp->is_basic[colnr]; @@ -3385,8 +3001,8 @@ STATIC int prod_Ax(lprec *lp, int *coltarget, LPSREAL *input, int *nzinput, } STATIC int prod_xA(lprec *lp, int *coltarget, - LPSREAL *input, int *nzinput, LPSREAL roundzero, LPSREAL ofscalar, - LPSREAL *output, int *nzoutput, int roundmode) + REAL *input, int *nzinput, REAL roundzero, REAL ofscalar, + REAL *output, int *nzoutput, int roundmode) /* Note that the dot product xa is stored at the active column index of A, i.e. of a. This means that if the basis only contains non-slack variables, output may point to the same vector as input, without overwriting the [0..rows] elements. */ @@ -3397,7 +3013,7 @@ STATIC int prod_xA(lprec *lp, int *coltarget, register REALXP v; int inz, *rowin, countNZ = 0; MATrec *mat = lp->matA; - register LPSREAL *matValue; + register REAL *matValue; register int *matRownr; /* Clean output area (only necessary if we are returning the full vector) */ @@ -3552,14 +3168,14 @@ STATIC int prod_xA(lprec *lp, int *coltarget, /* Special handling of small reduced cost values */ if(!isRC || (my_chsign(lp->is_lower[varnr], v) < 0)) { - SETMAX(vmax, fabs((LPSREAL) v)); + SETMAX(vmax, fabs((REAL) v)); } if(v != 0) { countNZ++; if(nzoutput != NULL) nzoutput[countNZ] = varnr; } - output[varnr] = (LPSREAL) v; + output[varnr] = (REAL) v; } /* Compute reduced cost if this option is active */ @@ -3599,17 +3215,17 @@ STATIC int prod_xA(lprec *lp, int *coltarget, } STATIC MYBOOL prod_xA2(lprec *lp, int *coltarget, - LPSREAL *prow, LPSREAL proundzero, int *nzprow, - LPSREAL *drow, LPSREAL droundzero, int *nzdrow, - LPSREAL ofscalar, int roundmode) + REAL *prow, REAL proundzero, int *nzprow, + REAL *drow, REAL droundzero, int *nzdrow, + REAL ofscalar, int roundmode) { int varnr, colnr, ib, ie, vb, ve, nrows = lp->rows; MYBOOL includeOF, isRC; REALXP dmax, pmax; register REALXP d, p; MATrec *mat = lp->matA; - LPSREAL value; - register LPSREAL *matValue; + REAL value; + register REAL *matValue; register int *matRownr; MYBOOL localset; @@ -3709,8 +3325,8 @@ STATIC MYBOOL prod_xA2(lprec *lp, int *coltarget, } } - SETMAX(pmax, fabs((LPSREAL) p)); - prow[varnr] = (LPSREAL) p; + SETMAX(pmax, fabs((REAL) p)); + prow[varnr] = (REAL) p; if((nzprow != NULL) && (p != 0)) { (*nzprow)++; nzprow[*nzprow] = varnr; @@ -3718,9 +3334,9 @@ STATIC MYBOOL prod_xA2(lprec *lp, int *coltarget, /* Special handling of reduced cost rounding */ if(!isRC || (my_chsign(lp->is_lower[varnr], d) < 0)) { - SETMAX(dmax, fabs((LPSREAL) d)); + SETMAX(dmax, fabs((REAL) d)); } - drow[varnr] = (LPSREAL) d; + drow[varnr] = (REAL) d; if((nzdrow != NULL) && (d != 0)) { (*nzdrow)++; nzdrow[*nzdrow] = varnr; @@ -3773,10 +3389,10 @@ STATIC MYBOOL prod_xA2(lprec *lp, int *coltarget, } STATIC void bsolve_xA2(lprec *lp, int* coltarget, - int row_nr1, LPSREAL *vector1, LPSREAL roundzero1, int *nzvector1, - int row_nr2, LPSREAL *vector2, LPSREAL roundzero2, int *nzvector2, int roundmode) + int row_nr1, REAL *vector1, REAL roundzero1, int *nzvector1, + int row_nr2, REAL *vector2, REAL roundzero2, int *nzvector2, int roundmode) { - LPSREAL ofscalar = 1.0; + REAL ofscalar = 1.0; /* Clear and initialize first vector */ if(nzvector1 == NULL) diff --git a/src/lpsolve/headers/run_headers/lp_matrix.h b/src/lpSolve/src/lp_matrix.h similarity index 99% rename from src/lpsolve/headers/run_headers/lp_matrix.h rename to src/lpSolve/src/lp_matrix.h index 90d8ae45..a1cf9b68 100644 --- a/src/lpsolve/headers/run_headers/lp_matrix.h +++ b/src/lpSolve/src/lp_matrix.h @@ -116,13 +116,13 @@ typedef struct _MATrec int mat_alloc; /* The allocated size for matrix sized structures */ /* Sparse problem matrix storage */ -#if MatrixColAccess==CAM_Record +#if MatrixColAccess==CAM_Record MATitem *col_mat; /* mat_alloc : The sparse data storage */ #else /*MatrixColAccess==CAM_Vector*/ int *col_mat_colnr; int *col_mat_rownr; REAL *col_mat_value; -#endif +#endif int *col_end; /* columns_alloc+1 : col_end[i] is the index of the first element after column i; column[i] is stored in elements col_end[i-1] to col_end[i]-1 */ @@ -231,7 +231,7 @@ STATIC void btran(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero); /* Combined equation solution and matrix product for simplex operations */ STATIC MYBOOL fsolve(lprec *lp, int varin, REAL *pcol, int *nzidx, REAL roundzero, REAL ofscalar, MYBOOL prepareupdate); STATIC MYBOOL bsolve(lprec *lp, int row_nr, REAL *rhsvector, int *nzidx, REAL roundzero, REAL ofscalar); -STATIC void bsolve_xA2(lprec *lp, int* coltarget, +STATIC void bsolve_xA2(lprec *lp, int* coltarget, int row_nr1, REAL *vector1, REAL roundzero1, int *nzvector1, int row_nr2, REAL *vector2, REAL roundzero2, int *nzvector2, int roundmode); diff --git a/src/lpsolve/build/lp_solve/lp_mipbb.c b/src/lpSolve/src/lp_mipbb.c similarity index 90% rename from src/lpsolve/build/lp_solve/lp_mipbb.c rename to src/lpSolve/src/lp_mipbb.c index 1a893f0c..0d05dce0 100644 --- a/src/lpsolve/build/lp_solve/lp_mipbb.c +++ b/src/lpSolve/src/lp_mipbb.c @@ -100,7 +100,7 @@ STATIC BBrec *push_BB(lprec *lp, BBrec *parentBB, int varno, int vartype, int va if((parentBB != NULL) && (parentBB->lastrcf > 0)) { MYBOOL isINT; int k, ii, nfixed = 0, ntighten = 0; - LPSREAL deltaUL; + REAL deltaUL; for(k = 1; k <= lp->nzdrow[0]; k++) { ii = lp->nzdrow[k]; @@ -214,19 +214,15 @@ STATIC BBrec *pop_BB(BBrec *BB) } /* Unwind other variables */ - if(lp->bb_upperchange != NULL) { + restoreUndoLadder(lp->bb_upperchange, BB->upbo); + for(; BB->UBtrack > 0; BB->UBtrack--) { + decrementUndoLadder(lp->bb_upperchange); restoreUndoLadder(lp->bb_upperchange, BB->upbo); - for(; BB->UBtrack > 0; BB->UBtrack--) { - decrementUndoLadder(lp->bb_upperchange); - restoreUndoLadder(lp->bb_upperchange, BB->upbo); - } } - if(lp->bb_lowerchange != NULL) { + restoreUndoLadder(lp->bb_lowerchange, BB->lowbo); + for(; BB->LBtrack > 0; BB->LBtrack--) { + decrementUndoLadder(lp->bb_lowerchange); restoreUndoLadder(lp->bb_lowerchange, BB->lowbo); - for(; BB->LBtrack > 0; BB->LBtrack--) { - decrementUndoLadder(lp->bb_lowerchange); - restoreUndoLadder(lp->bb_lowerchange, BB->lowbo); - } } lp->bb_level--; k = BB->varno - lp->rows; @@ -275,10 +271,10 @@ STATIC BBrec *pop_BB(BBrec *BB) 2. A presolve routine to fix other variables and detect infeasibility THIS IS INACTIVE CODE, PLACEHOLDERS FOR FUTURE DEVELOPMENT!!! */ -STATIC LPSREAL probe_BB(BBrec *BB) +STATIC REAL probe_BB(BBrec *BB) { int i, ii; - LPSREAL coefOF, sum = 0; + REAL coefOF, sum = 0; lprec *lp = BB->lp; /* Loop over all ints to see if the best possible solution @@ -304,7 +300,7 @@ STATIC LPSREAL probe_BB(BBrec *BB) return( sum ); } -STATIC LPSREAL presolve_BB(BBrec *BB) +STATIC REAL presolve_BB(BBrec *BB) { return( 0 ); } @@ -312,7 +308,7 @@ STATIC LPSREAL presolve_BB(BBrec *BB) /* Node and branch management routines */ STATIC MYBOOL initbranches_BB(BBrec *BB) { - LPSREAL new_bound, temp; + REAL new_bound, temp; int k; lprec *lp = BB->lp; @@ -368,9 +364,12 @@ STATIC MYBOOL initbranches_BB(BBrec *BB) /* Otherwise check if we should do automatic branching */ else if(get_var_branch(lp, k) == BRANCH_AUTOMATIC) { new_bound = modf(BB->lastsolution/get_pseudorange(lp->bb_PseudoCost, k, BB->vartype), &temp); - if(isnan(new_bound)) +#if 0 + if(_isnan(new_bound)) new_bound = 0; else if(new_bound < 0) +#endif + if (new_bound < 0) new_bound += 1.0; BB->isfloor = (MYBOOL) (new_bound <= 0.5); @@ -429,8 +428,8 @@ STATIC MYBOOL initbranches_BB(BBrec *BB) STATIC MYBOOL fillbranches_BB(BBrec *BB) { int K, k; - LPSREAL ult_upbo, ult_lowbo; - LPSREAL new_bound, SC_bound, intmargin = BB->lp->epsprimal; + REAL ult_upbo, ult_lowbo; + REAL new_bound, SC_bound, intmargin = BB->lp->epsprimal; lprec *lp = BB->lp; MYBOOL OKstatus = FALSE; @@ -452,7 +451,7 @@ STATIC MYBOOL fillbranches_BB(BBrec *BB) BB->UPbound = lp->infinite; /* Handle SC-variables for the [0-LoBound> range */ - if((SC_bound > 0) && (fabs(BB->lastsolution) < SC_bound-intmargin)) { + if((SC_bound > 0) && (fabs(BB->lastsolution) < SC_bound)) { new_bound = 0; } /* Handle pure integers (non-SOS, non-SC) */ @@ -483,11 +482,11 @@ STATIC MYBOOL fillbranches_BB(BBrec *BB) /* Check if the new bound might conflict and possibly make adjustments */ if(new_bound < BB->lowbo[K]) - new_bound = BB->lowbo[K] - my_avoidtiny(new_bound-BB->lowbo[K], intmargin); + new_bound = BB->lowbo[K] - my_avoidtiny(new_bound-BB->lowbo[K], lp->epsvalue); if(new_bound < BB->lowbo[K]) { #ifdef Paranoia debug_print(lp, - "fillbranches_BB: New upper bound value %g conflicts with old lower bound %g\n", + "New upper bound value %g conflicts with old lower bound %g\n", new_bound, BB->lowbo[K]); #endif BB->nodesleft--; @@ -557,11 +556,11 @@ STATIC MYBOOL fillbranches_BB(BBrec *BB) /* Check if the new bound might conflict and possibly make adjustments */ if(new_bound > BB->upbo[K]) - new_bound = BB->upbo[K] + my_avoidtiny(new_bound-BB->upbo[K], intmargin); + new_bound = BB->upbo[K] + my_avoidtiny(new_bound-BB->upbo[K], lp->epsvalue); if(new_bound > BB->upbo[K]) { #ifdef Paranoia debug_print(lp, - "fillbranches_BB: New lower bound value %g conflicts with old upper bound %g\n", + "New lower bound value %g conflicts with old upper bound %g\n", new_bound, BB->upbo[K]); #endif BB->nodesleft--; @@ -615,7 +614,7 @@ STATIC MYBOOL fillbranches_BB(BBrec *BB) BB->isfloor = !BB->isfloor; /* Header initialization */ BB->isfloor = !BB->isfloor; - while(!OKstatus && /* !userabort(lp, -1) */ lp->spx_status != TIMEOUT && !lp->bb_break && (BB->nodesleft > 0)) + while(!OKstatus && !lp->bb_break && (BB->nodesleft > 0)) OKstatus = nextbranch_BB( BB ); } @@ -762,7 +761,7 @@ STATIC MYBOOL freecuts_BB(lprec *lp) STATIC int solve_LP(lprec *lp, BBrec *BB) { int tilted, restored, status; - LPSREAL testOF, *upbo = BB->upbo, *lowbo = BB->lowbo; + REAL testOF, *upbo = BB->upbo, *lowbo = BB->lowbo; BBrec *perturbed = NULL; if(lp->bb_break) @@ -868,22 +867,10 @@ STATIC int solve_LP(lprec *lp, BBrec *BB) /* Handle the different simplex outcomes */ if(status != OPTIMAL) { - if(lp->bb_level <= 1) - lp->bb_parentOF = lp->infinite; + lp->bb_parentOF = lp->infinite; if((status == USERABORT) || (status == TIMEOUT)) { /* Construct the last feasible solution, if available */ if((lp->solutioncount == 0) && - /* - 30/01/08 added MIP_count test because in following situation thing were wrong: - - The model contains integers - - A break at first is set - - A timeout is set - - The timeout occurs before a first integer solution is found - - When the timeout occurs, the simplex algorithm is in phase 2 and has a feasible (but non-integer) solution, but not optimal yet. - If above situation occurs then a (sub-optimal) solution was returned while no integer - solution isn't found yet at this time - */ - (MIP_count(lp) == 0) && ((lp->simplex_mode & (SIMPLEX_Phase2_PRIMAL | SIMPLEX_Phase2_DUAL)) > 0)) { lp->solutioncount++; construct_solution(lp, NULL); @@ -897,15 +884,6 @@ STATIC int solve_LP(lprec *lp, BBrec *BB) report(lp, NORMAL, "The model %s\n", (status == UNBOUNDED) ? "is UNBOUNDED" : ((status == INFEASIBLE) ? "is INFEASIBLE" : "FAILED")); - else { -#ifdef Paranoia - if((status != FATHOMED) && (status != INFEASIBLE)) - report(lp, SEVERE, "spx_solve: Invalid return code %d during B&B\n", status); -#endif - /* If we fathomed a node due to an inferior OF having been detected, return infeasible */ - if(status == FATHOMED) - lp->spx_status = INFEASIBLE; - } } else { /* ... there is a good solution */ @@ -920,18 +898,12 @@ STATIC int solve_LP(lprec *lp, BBrec *BB) else if((lp->bb_totalnodes == 0) && (MIP_count(lp) > 0)) { if(lp->lag_status != RUNNING) { - report(lp, NORMAL, "\nRelaxed solution " RESULTVALUEMASK " after %10.0f iter is B&B base.\n", - lp->solution[0], (double) lp->total_iter); + report(lp, NORMAL, "\nRelaxed solution " RESULTVALUEMASK " after %10.0f iter is B&B base.\n", + lp->solution[0], (double) lp->total_iter); report(lp, NORMAL, " \n"); } - if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPOPTIMAL)) { - LPSREAL *best_solution = lp->best_solution; - - /* transfer_solution(lp, TRUE); */ - lp->best_solution = lp->solution; + if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPOPTIMAL)) lp->usermessage(lp, lp->msghandle, MSG_LPOPTIMAL); - lp->best_solution = best_solution; - } set_var_priority(lp); } @@ -968,11 +940,11 @@ STATIC BBrec *findself_BB(BBrec *BB) /* Function to determine the opportunity for variable fixing and bound tightening based on a previous best MILP solution and a variable's reduced cost at the current relaxation - inspired by Wolsley */ -STATIC int rcfbound_BB(BBrec *BB, int varno, MYBOOL isINT, LPSREAL *newbound, MYBOOL *isfeasible) +STATIC int rcfbound_BB(BBrec *BB, int varno, MYBOOL isINT, REAL *newbound, MYBOOL *isfeasible) { int i = FR; lprec *lp = BB->lp; - LPSREAL deltaRC, rangeLU, deltaOF, lowbo, upbo; + REAL deltaRC, rangeLU, deltaOF, lowbo, upbo; /* Make sure we only accept non-basic variables */ if(lp->is_basic[varno]) @@ -984,14 +956,7 @@ STATIC int rcfbound_BB(BBrec *BB, int varno, MYBOOL isINT, LPSREAL *newbound, MY rangeLU = upbo - lowbo; if(rangeLU > lp->epsprimal) { -#if 1 /* v5.5 problematic - Gap between current node and the current best bound */ deltaOF = lp->rhs[0] - lp->bb_workOF; -#elif 0 /* v6 less aggressive - Gap between current best bound and the relaxed problem */ - deltaOF = my_chsign(is_maxim(lp), lp->real_solution) - lp->bb_workOF; -#else /* v6 more aggressive - Gap between current node and the relaxed problem */ - deltaOF = my_chsign(is_maxim(lp), lp->real_solution) - lp->rhs[0]; -#endif - deltaRC = my_chsign(!lp->is_lower[varno], lp->drow[varno]); /* Protect against divisions with tiny numbers and stray sign reversals of the reduced cost */ @@ -1041,8 +1006,8 @@ STATIC int rcfbound_BB(BBrec *BB, int varno, MYBOOL isINT, LPSREAL *newbound, MY STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus) { - int countsossc, countnint, k, reasonmsg = MSG_NONE; - LPSREAL varsol; + int countsossc, countnint, k; + REAL varsol; MYBOOL is_better = FALSE, is_equal = FALSE, is_feasible = TRUE; lprec *lp = BB->lp; @@ -1136,7 +1101,7 @@ STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus) /* Check if we have reached the depth limit for any individual variable (protects against infinite recursions of mainly integer variables) */ k = *varno-lp->rows; - if((*varno > 0) && (lp->bb_limitlevel != 0) && (lp->bb_varactive[k] >= abs(lp->bb_limitlevel) /* abs(DEF_BB_LIMITLEVEL) */)) { + if((*varno > 0) && (lp->bb_varactive[k] >= abs(DEF_BB_LIMITLEVEL))) { /* if(!is_action(lp->nomessage, NOMSG_BBLIMIT)) {*/ /* report(lp, IMPORTANT, "findnode_BB: Reached B&B depth limit %d for variable %d; will not dive further.\n\n", @@ -1151,28 +1116,15 @@ STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus) /* Check if the current MIP solution is optimal; equal or better */ if(*varno == 0) { is_better = (MYBOOL) (lp->solutioncount == 0) || bb_better(lp, OF_INCUMBENT | OF_DELTA, OF_TEST_BT); -#if 1 is_better &= bb_better(lp, OF_INCUMBENT | OF_DELTA, OF_TEST_BT | OF_TEST_RELGAP); -#else - /* Check if we can determine clear improvement */ - is_better = (MYBOOL) (lp->solutioncount == 0) || - (MYBOOL) ((lp->bb_deltaOF > 0) && - (my_chsign(is_maxim(lp), lp->solution[0]-lp->best_solution[0]) < 0)); - - /* Apply gap-based improvement testing if the current solution is not clearly better */ - - if(!is_better) { - is_better = bb_better(lp, OF_INCUMBENT | OF_DELTA, OF_TEST_BT); - is_better |= bb_better(lp, OF_INCUMBENT | OF_DELTA, OF_TEST_BT | OF_TEST_RELGAP); - } -#endif is_equal = !is_better; if(is_equal) { if((lp->solutionlimit <= 0) || (lp->solutioncount < lp->solutionlimit)) { lp->solutioncount++; SETMIN(lp->bb_solutionlevel, lp->bb_level); - reasonmsg = MSG_MILPEQUAL; + if((lp->usermessage != NULL) && (lp->msgmask & MSG_MILPEQUAL)) + lp->usermessage(lp, lp->msghandle, MSG_MILPEQUAL); } } @@ -1196,11 +1148,11 @@ STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus) lp->solution[0], (double) lp->total_iter, (double) lp->bb_totalnodes, 100.0*fabs(my_reldiff(lp->solution[0], lp->bb_limitOF))); } - if(MIP_count(lp) > 0) { - if(lp->bb_improvements == 0) - reasonmsg = MSG_MILPFEASIBLE; - else - reasonmsg = MSG_MILPBETTER; + if((lp->usermessage != NULL) && (MIP_count(lp) > 0)) { + if((lp->msgmask & MSG_MILPFEASIBLE) && (lp->bb_improvements == 0)) + lp->usermessage(lp, lp->msghandle, MSG_MILPFEASIBLE); + else if((lp->msgmask & MSG_MILPBETTER) && (lp->msgmask & MSG_MILPBETTER)) + lp->usermessage(lp, lp->msghandle, MSG_MILPBETTER); } lp->bb_status = FEASFOUND; @@ -1242,9 +1194,6 @@ STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus) ) { } } - if((reasonmsg != MSG_NONE) && (lp->msgmask & reasonmsg) && (lp->usermessage != NULL)) - lp->usermessage(lp, lp->msghandle, reasonmsg); - if(lp->print_sol != FALSE) { print_objective(lp); print_solution(lp, 1); @@ -1398,19 +1347,8 @@ STATIC int run_BB(lprec *lp) /* Perform the branch & bound loop */ while(lp->bb_level > 0) { - status = solve_BB(currentBB); -#if 0 - if((lp->bb_level == 1) && (MIP_count(lp) > 0)) { - if(status == RUNNING) - ; - - /* Check if there was an integer solution of an aborted model */ - else if((status == SUBOPTIMAL) && (lp->solutioncount == 1) && - findnode_BB(currentBB, &varno, &vartype, &varcus)) - status = USERABORT; - } -#endif + status = solve_BB(currentBB); if((status == OPTIMAL) && findnode_BB(currentBB, &varno, &vartype, &varcus)) currentBB = push_BB(lp, currentBB, varno, vartype, varcus); @@ -1426,7 +1364,7 @@ STATIC int run_BB(lprec *lp) /* Check if we should adjust status */ if(lp->solutioncount > prevsolutions) { - if((status == PROCBREAK) || (status == USERABORT) || (status == TIMEOUT) || userabort(lp, -1)) + if((status == PROCBREAK) || (status == USERABORT) || (status == TIMEOUT)) status = SUBOPTIMAL; else status = OPTIMAL; diff --git a/src/lpsolve/headers/run_headers/lp_mipbb.h b/src/lpSolve/src/lp_mipbb.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_mipbb.h rename to src/lpSolve/src/lp_mipbb.h diff --git a/src/lpsolve/build/lp_solve/lp_params.c b/src/lpSolve/src/lp_params.c similarity index 90% rename from src/lpsolve/build/lp_solve/lp_params.c rename to src/lpSolve/src/lp_params.c index adc8476d..e698903b 100644 --- a/src/lpsolve/build/lp_solve/lp_params.c +++ b/src/lpSolve/src/lp_params.c @@ -8,14 +8,14 @@ #include "lp_report.h" #include "ini.h" -typedef int (__WINAPI fn_int_get_function)(lprec *lp); -typedef long (__WINAPI fn_long_get_function)(lprec *lp); -typedef MYBOOL (__WINAPI fn_MYBOOL_get_function)(lprec *lp); -typedef LPSREAL (__WINAPI fn_REAL_get_function)(lprec *lp); -typedef void (__WINAPI fn_int_set_function)(lprec *lp, int value); -typedef void (__WINAPI fn_long_set_function)(lprec *lp, long value); -typedef void (__WINAPI fn_MYBOOL_set_function)(lprec *lp, MYBOOL value); -typedef void (__WINAPI fn_REAL_set_function)(lprec *lp, LPSREAL value); +typedef int (__WINAPI int_get_function)(lprec *lp); +typedef long (__WINAPI long_get_function)(lprec *lp); +typedef MYBOOL (__WINAPI MYBOOL_get_function)(lprec *lp); +typedef REAL (__WINAPI REAL_get_function)(lprec *lp); +typedef void (__WINAPI int_set_function)(lprec *lp, int value); +typedef void (__WINAPI long_set_function)(lprec *lp, long value); +typedef void (__WINAPI MYBOOL_set_function)(lprec *lp, MYBOOL value); +typedef void (__WINAPI REAL_set_function)(lprec *lp, REAL value); #define intfunction 1 #define longfunction 2 @@ -25,10 +25,10 @@ typedef void (__WINAPI fn_REAL_set_function)(lprec *lp, LPSREAL value); #define setvalues(values, basemask) values, sizeof(values) / sizeof(*values), basemask #define setNULLvalues NULL, 0, 0 #define setvalue(value) value, #value -#define setintfunction(get_function, set_function) { get_function }, { set_function }, intfunction -#define setlongfunction(get_function, set_function) { (fn_int_get_function *) get_function }, {(fn_int_set_function *) set_function }, longfunction -#define setMYBOOLfunction(get_function, set_function) { (fn_int_get_function *) get_function }, { (fn_int_set_function *) set_function }, MYBOOLfunction -#define setREALfunction(get_function, set_function) {(fn_int_get_function *) get_function }, { (fn_int_set_function *) set_function }, REALfunction +#define setintfunction(get_function, set_function) get_function, set_function, intfunction +#define setlongfunction(get_function, set_function) (int_get_function *) get_function, (int_set_function *) set_function, longfunction +#define setMYBOOLfunction(get_function, set_function) (int_get_function *) get_function, (int_set_function *) set_function, MYBOOLfunction +#define setREALfunction(get_function, set_function) (int_get_function *) get_function, (int_set_function *) set_function, REALfunction #define WRITE_COMMENTED 0 #define WRITE_ACTIVE 1 @@ -41,16 +41,16 @@ struct _values { struct _functions { char *par; /* name of parameter in ini file */ union { - fn_int_get_function *int_get_function; /* set via setintfunction */ - fn_long_get_function *long_get_function; /* set via setlongfunction */ - fn_MYBOOL_get_function *MYBOOL_get_function; /* set via setMYBOOLfunction */ - fn_REAL_get_function *REAL_get_function; /* set via setREALfunction */ + int_get_function *int_get_function; /* set via setintfunction */ + long_get_function *long_get_function; /* set via setlongfunction */ + MYBOOL_get_function *MYBOOL_get_function; /* set via setMYBOOLfunction */ + REAL_get_function *REAL_get_function; /* set via setREALfunction */ } get_function; union { - fn_int_set_function *int_set_function; /* set via setintfunction */ - fn_long_set_function *long_set_function; /* set via setlongfunction */ - fn_MYBOOL_set_function *MYBOOL_set_function; /* set via setMYBOOLfunction */ - fn_REAL_set_function *REAL_set_function; /* set via setREALfunction */ + int_set_function *int_set_function; /* set via setintfunction */ + long_set_function *long_set_function; /* set via setlongfunction */ + MYBOOL_set_function *MYBOOL_set_function; /* set via setMYBOOLfunction */ + REAL_set_function *REAL_set_function; /* set via setREALfunction */ } set_function; int type; /* set via set*function */ struct _values *values; /* set via setvalues to a structure of _values */ @@ -123,22 +123,22 @@ static struct _values improve[] = { setvalue(IMPROVE_BBSIMPLEX) }, }; -static LPSREAL __WINAPI get_mip_gap_abs(lprec *lp) +static REAL __WINAPI get_mip_gap_abs(lprec *lp) { return(get_mip_gap(lp, TRUE)); } -static LPSREAL __WINAPI get_mip_gap_rel(lprec *lp) +static REAL __WINAPI get_mip_gap_rel(lprec *lp) { return(get_mip_gap(lp, FALSE)); } -static void __WINAPI set_mip_gap_abs(lprec *lp, LPSREAL mip_gap) +static void __WINAPI set_mip_gap_abs(lprec *lp, REAL mip_gap) { set_mip_gap(lp, TRUE, mip_gap); } -static void __WINAPI set_mip_gap_rel(lprec *lp, LPSREAL mip_gap) +static void __WINAPI set_mip_gap_rel(lprec *lp, REAL mip_gap) { set_mip_gap(lp, FALSE, mip_gap); } @@ -302,19 +302,19 @@ static struct _functions functions[] = { "PRINT_SOL", setintfunction(get_print_sol, set_print_sol), setvalues(print_sol, ~0), WRITE_COMMENTED }, { "TIMEOUT", setlongfunction(get_timeout, set_timeout), setNULLvalues, WRITE_COMMENTED }, { "TRACE", setMYBOOLfunction(is_trace, set_trace), setNULLvalues, WRITE_COMMENTED }, - { "VERBOSE", setintfunction(get_verbose, set_verbose), setvalues(verbose, ~0), WRITE_COMMENTED }, + { "VERBOSE", setintfunction(get_verbose, set_verbose), setvalues(verbose, ~0), WRITE_COMMENTED } }; static void write_params1(lprec *lp, FILE *fp, char *header, int newline) { int ret = 0, ret2, i, j, k, value, value2, elements, majorversion, minorversion, release, build; unsigned int basemask; - LPSREAL a = 0; + REAL a = 0; char buf[4096], par[20]; ini_writeheader(fp, header, newline); lp_solve_version(&majorversion, &minorversion, &release, &build); - sprintf(buf, "lp_solve version %d.%d settings\n", majorversion, minorversion); + snprintf(buf, sizeof(buf), "lp_solve version %d.%d settings\n", majorversion, minorversion); ini_writecomment(fp, buf); for(i = 0; i < sizeof(functions) / sizeof(*functions); i++) { switch(functions[i].type) { @@ -345,10 +345,10 @@ static void write_params1(lprec *lp, FILE *fp, char *header, int newline) case intfunction: case longfunction: case MYBOOLfunction: - sprintf(buf, "%d", ret); + snprintf(buf, sizeof(buf), "%d", ret); break; case REALfunction: - sprintf(buf, "%g", a); + snprintf(buf, sizeof(buf), "%g", a); break; } } @@ -357,9 +357,9 @@ static void write_params1(lprec *lp, FILE *fp, char *header, int newline) basemask = functions[i].basemask; for(j = 0; j < elements; j++) { value = functions[i].values[j].value; - ret2 = ret; - if(((unsigned int) value) < basemask) - ret2 &= basemask; + ret2 = ret; + if(((unsigned int) value) < basemask) + ret2 &= basemask; if(value == 0) { if(ret2 == 0) { if(*buf) @@ -423,7 +423,7 @@ MYBOOL __WINAPI write_params(lprec *lp, char *filename, char *options) readoptions(options, &header); - k = (int) strlen(filename); + k = strlen(filename); filename0 = (char *) malloc(k + 1 + 1); strcpy(filename0, filename); ptr1 = strrchr(filename0, '.'); @@ -520,7 +520,7 @@ MYBOOL __WINAPI read_params(lprec *lp, char *filename, char *options) hashtable *hashfunctions, *hashparameters; hashelem *hp; int i, j, elements, n, intvalue, state = 0; - LPSREAL REALvalue; + REAL REALvalue; char buf[4096], *header = NULL, *ptr, *ptr1, *ptr2; if((fp = ini_open(filename)) == NULL) diff --git a/src/lpsolve/build/lp_solve/lp_presolve.c b/src/lpSolve/src/lp_presolve.c similarity index 91% rename from src/lpsolve/build/lp_solve/lp_presolve.c rename to src/lpSolve/src/lp_presolve.c index 48f2d84f..c4eab1e4 100644 --- a/src/lpsolve/build/lp_solve/lp_presolve.c +++ b/src/lpSolve/src/lp_presolve.c @@ -45,7 +45,12 @@ #endif -int presolve_rowlength(presolverec *psdata, int rownr) +#define presolve_setstatus(one, two) presolve_setstatusex(one, two, __LINE__, __FILE__) + +INLINE int presolve_nextrow(presolverec *psdata, int colnr, int *previtem); +INLINE int presolve_nextcol(presolverec *psdata, int rownr, int *previtem); + +INLINE int presolve_rowlength(presolverec *psdata, int rownr) { int *items = psdata->rows->next[rownr]; @@ -54,8 +59,7 @@ int presolve_rowlength(presolverec *psdata, int rownr) else return( items[0] ); } - -int presolve_collength(presolverec *psdata, int colnr) +INLINE int presolve_collength(presolverec *psdata, int colnr) { int *items = psdata->cols->next[colnr]; if(items == NULL) @@ -64,8 +68,6 @@ int presolve_collength(presolverec *psdata, int colnr) return( items[0] ); } - -#define presolve_setstatus(one, two) presolve_setstatusex(one, two, __LINE__, __FILE__) STATIC int presolve_setstatusex(presolverec *psdata, int status, int lineno, char *filename) { if((status == INFEASIBLE) || (status == UNBOUNDED)) { @@ -180,7 +182,7 @@ STATIC MYBOOL presolve_fillUndo(lprec *lp, int orig_rows, int orig_cols, MYBOOL STATIC MYBOOL presolve_rebuildUndo(lprec *lp, MYBOOL isprimal) { int ik, ie, ix, j, k, *colnrDep; - LPSREAL hold, *value, *solution, *slacks; + REAL hold, *value, *solution, *slacks; presolveundorec *psdata = lp->presolve_undo; MATrec *mat = NULL; @@ -188,12 +190,16 @@ STATIC MYBOOL presolve_rebuildUndo(lprec *lp, MYBOOL isprimal) if(isprimal) { if(psdata->primalundo != NULL) mat = psdata->primalundo->tracker; + if(mat == NULL) + return( FALSE ); solution = lp->full_solution + lp->presolve_undo->orig_rows; slacks = lp->full_solution; } else { if(psdata->dualundo != NULL) mat = psdata->dualundo->tracker; + if(mat == NULL) + return( FALSE ); solution = lp->full_duals; slacks = lp->full_duals + lp->presolve_undo->orig_rows; } @@ -264,7 +270,7 @@ STATIC void presolve_storeDualUndo(presolverec *psdata, int rownr, int colnr) lprec *lp = psdata->lp; MYBOOL firstdone = FALSE; int ix, iix, item; - LPSREAL Aij = get_mat(lp, rownr, colnr); + REAL Aij = get_mat(lp, rownr, colnr); MATrec *mat = lp->matA; if(presolve_collength(psdata, colnr) == 0) @@ -361,57 +367,51 @@ STATIC MYBOOL presolve_SOScheck(presolverec *psdata) /* Presolve routines for tightening the model */ /* ----------------------------------------------------------------------------- */ -INLINE LPSREAL presolve_roundrhs(lprec *lp, LPSREAL value, MYBOOL isGE) +INLINE REAL presolve_round(lprec *lp, REAL value, MYBOOL isGE) { #ifdef DoPresolveRounding - LPSREAL eps = PRESOLVE_EPSVALUE*1000, - /* LPSREAL eps = PRESOLVE_EPSVALUE*pow(10.0,MAX(0,log10(1+fabs(value)))), */ - testout = my_precision(value, eps); -#if 1 - if(my_chsign(isGE, value-testout) < 0) - value = testout; -#elif 0 - if(my_chsign(isGE, value-testout) < 0) + REAL eps = PRESOLVE_EPSVALUE, +#if 0 + testin = value + my_chsign(isGE, eps/SCALEDINTFIXRANGE), + testout = restoreINT(testin, eps); + if(testout == testin) + value = restoreINT(value, eps); + else value = testout; - else if(value != testout) - value += my_chsign(isGE, (value-testout)/2); - /* value = testout + my_chsign(isGE, (value-testout)/2); */ #else + testout = restoreINT(value, eps); if(testout != value) - value += my_chsign(isGE, eps*1000); /* BASE OPTION */ + value += my_chsign(isGE, eps*1000); // /SCALEDINTFIXRANGE); #endif #endif return( value ); } -INLINE LPSREAL presolve_roundval(lprec *lp, LPSREAL value) +INLINE REAL presolve_precision(lprec *lp, REAL value) { #ifdef DoPresolveRounding - /* value = my_precision(value, PRESOLVE_EPSVALUE*MAX(1,log10(1+fabs(value)))); */ - value = my_precision(value, PRESOLVE_EPSVALUE); /* BASE OPTION */ + value = restoreINT(value, PRESOLVE_EPSVALUE); #endif return( value ); } - -#ifndef R_EMBEDDED_LPSOLVE +#if 0 INLINE MYBOOL presolve_mustupdate(lprec *lp, int colnr) { -#if 0 +!if 0 return( my_infinite(lp, get_lowbo(lp, colnr)) || my_infinite(lp, get_upbo(lp, colnr)) ); -#else +!else return( my_infinite(lp, lp->orig_lowbo[lp->rows+colnr]) || my_infinite(lp, lp->orig_upbo[lp->rows+colnr]) ); -#endif +!endif } #endif - -INLINE LPSREAL presolve_sumplumin(lprec *lp, int item, psrec *ps, MYBOOL doUpper) +INLINE REAL presolve_sumplumin(lprec *lp, int item, psrec *ps, MYBOOL doUpper) { - LPSREAL *plu = (doUpper ? ps->pluupper : ps->plulower), + REAL *plu = (doUpper ? ps->pluupper : ps->plulower), *neg = (doUpper ? ps->negupper : ps->neglower); if(fabs(plu[item]) >= lp->infinite) @@ -422,13 +422,13 @@ INLINE LPSREAL presolve_sumplumin(lprec *lp, int item, psrec *ps, MYBOOL doUpper return( plu[item]+neg[item] ); } -INLINE void presolve_range(lprec *lp, int rownr, psrec *ps, LPSREAL *loValue, LPSREAL *hiValue) +INLINE void presolve_range(lprec *lp, int rownr, psrec *ps, REAL *loValue, REAL *hiValue) { *loValue = presolve_sumplumin(lp, rownr, ps, FALSE); *hiValue = presolve_sumplumin(lp, rownr, ps, TRUE); } -STATIC void presolve_rangeorig(lprec *lp, int rownr, psrec *ps, LPSREAL *loValue, LPSREAL *hiValue, LPSREAL delta) +STATIC void presolve_rangeorig(lprec *lp, int rownr, psrec *ps, REAL *loValue, REAL *hiValue, REAL delta) { delta = my_chsign(is_chsign(lp, rownr), lp->presolve_undo->fixed_rhs[rownr] + delta); *loValue = presolve_sumplumin(lp, rownr, ps, FALSE) + delta; @@ -440,7 +440,7 @@ STATIC MYBOOL presolve_rowfeasible(presolverec *psdata, int rownr, MYBOOL userow lprec *lp = psdata->lp; MYBOOL status = TRUE; int contype, origrownr = rownr; - LPSREAL LHS, RHS, value; + REAL LHS, RHS, value; /* Optionally loop across all active rows in the provided map (debugging) */ if(userowmap) @@ -483,7 +483,7 @@ STATIC MYBOOL presolve_debugmap(presolverec *psdata, char *caption) { lprec *lp = psdata->lp; MATrec *mat = lp->matA; - int colnr, ix, ie, nx, jx, je, *cols, *rows; + int colnr, ix, ie, nx, jx, je, *cols, *rows, n; int nz = mat->col_end[lp->columns] - 1; MYBOOL status = FALSE; @@ -511,7 +511,7 @@ STATIC MYBOOL presolve_debugmap(presolverec *psdata, char *caption) } cols = psdata->rows->next[COL_MAT_ROWNR(*rows)]; ie = cols[0]; - + n = 0; for(ix = 1; ix <= ie; ix++) { nx = cols[ix]; if((nx < 0) || (nx > nz)) { @@ -532,7 +532,7 @@ STATIC MYBOOL presolve_debugmap(presolverec *psdata, char *caption) STATIC MYBOOL presolve_validate(presolverec *psdata, MYBOOL forceupdate) { int i, ie, j, je, k, rownr, *items; - LPSREAL upbound, lobound, value; + REAL upbound, lobound, value; lprec *lp = psdata->lp; MATrec *mat = lp->matA; MYBOOL status = mat->row_end_valid && !forceupdate; @@ -631,7 +631,7 @@ STATIC MYBOOL presolve_validate(presolverec *psdata, MYBOOL forceupdate) STATIC MYBOOL presolve_rowtallies(presolverec *psdata, int rownr, int *plu, int *neg, int *pluneg) { - LPSREAL value; + REAL value; lprec *lp = psdata->lp; MATrec *mat = lp->matA; int ix, jx, ib = 0; @@ -770,8 +770,7 @@ INLINE int presolve_nextrecord(psrec *ps, int recnr, int *previtem) return( status ); } - -int presolve_nextcol(presolverec *psdata, int rownr, int *previtem) +INLINE int presolve_nextcol(presolverec *psdata, int rownr, int *previtem) /* Find the first active (non-eliminated) nonzero column in rownr after prevcol */ { return( presolve_nextrecord(psdata->rows, rownr, previtem) ); @@ -780,7 +779,7 @@ INLINE int presolve_lastcol(presolverec *psdata, int rownr) { return( presolve_nextrecord(psdata->rows, rownr, NULL) ); } -int presolve_nextrow(presolverec *psdata, int colnr, int *previtem) +INLINE int presolve_nextrow(presolverec *psdata, int colnr, int *previtem) /* Find the first active (non-eliminated) nonzero row in colnr after prevrow */ { return( presolve_nextrecord(psdata->cols, colnr, previtem) ); @@ -790,7 +789,7 @@ INLINE int presolve_lastrow(presolverec *psdata, int colnr) return( presolve_nextrecord(psdata->cols, colnr, NULL) ); } -INLINE void presolve_adjustrhs(presolverec *psdata, int rownr, LPSREAL fixdelta, LPSREAL epsvalue) +INLINE void presolve_adjustrhs(presolverec *psdata, int rownr, REAL fixdelta, REAL epsvalue) { lprec *lp = psdata->lp; @@ -799,7 +798,7 @@ INLINE void presolve_adjustrhs(presolverec *psdata, int rownr, LPSREAL fixdelta, #if 1 my_roundzero(lp->orig_rhs[rownr], epsvalue); #else - lp->orig_rhs[rownr] = presolve_roundrhs(lp, lp->orig_rhs[rownr], FALSE); + lp->orig_rhs[rownr] = presolve_round(lp, lp->orig_rhs[rownr], FALSE); #endif lp->presolve_undo->fixed_rhs[rownr] += fixdelta; } @@ -809,7 +808,7 @@ STATIC int presolve_shrink(presolverec *psdata, int *nConRemove, int *nVarRemove SOSgroup *SOS = psdata->lp->SOS; int status = RUNNING, countR = 0, countC = 0, i, ix, n, *list; - LPSREAL fixValue; + REAL fixValue; /* Remove empty rows */ list = psdata->rows->empty; @@ -1042,7 +1041,6 @@ STATIC int presolve_redundantSOS(presolverec *psdata, int *nb, int *nSum) continue; j = SOS->members[k]; SOS_member_delete(lp->SOS, i, j); - /* if(get_upbo(lp, j) - get_lowbo(lp, j) < lp->epsprimal) */ if(is_fixedvar(lp, nrows+j)) continue; if(!presolve_colfix(psdata, j, 0.0, AUTOMATIC, &iBoundTighten)) { @@ -1073,12 +1071,12 @@ STATIC int presolve_redundantSOS(presolverec *psdata, int *nb, int *nSum) return( status ); } -STATIC MYBOOL presolve_fixSOS1(presolverec *psdata, int colnr, LPSREAL fixvalue, int *nr, int *nv) +STATIC MYBOOL presolve_fixSOS1(presolverec *psdata, int colnr, REAL fixvalue, int *nr, int *nv) { lprec *lp = psdata->lp; int i, k, j; SOSrec *SOS; - LPSREAL newvalue; + REAL newvalue; MYBOOL *fixed = NULL, status = FALSE; /* Allocate working member list */ @@ -1183,10 +1181,10 @@ STATIC void presolve_setEQ(presolverec *psdata, int rownr) psdata->dv_upbo[rownr] = lp->infinite; } -STATIC MYBOOL presolve_singletonbounds(presolverec *psdata, int rownr, int colnr, LPSREAL *lobound, LPSREAL *upbound, LPSREAL *aval) +STATIC MYBOOL presolve_singletonbounds(presolverec *psdata, int rownr, int colnr, REAL *lobound, REAL *upbound, REAL *aval) { lprec *lp = psdata->lp; - LPSREAL coeff_a, epsvalue = psdata->epsvalue; + REAL coeff_a, epsvalue = psdata->epsvalue; MYBOOL isneg; /* Compute row singleton variable range */ @@ -1254,10 +1252,10 @@ STATIC MYBOOL presolve_singletonbounds(presolverec *psdata, int rownr, int colnr return( isneg ); } -STATIC MYBOOL presolve_altsingletonvalid(presolverec *psdata, int rownr, int colnr, LPSREAL reflotest, LPSREAL refuptest) +STATIC MYBOOL presolve_altsingletonvalid(presolverec *psdata, int rownr, int colnr, REAL reflotest, REAL refuptest) { lprec *lp = psdata->lp; - LPSREAL coeff_bl, coeff_bu, epsvalue = psdata->epsvalue; + REAL coeff_bl, coeff_bu, epsvalue = psdata->epsvalue; coeff_bl = get_rh_lower(lp, rownr); coeff_bu = get_rh_upper(lp, rownr); @@ -1283,11 +1281,11 @@ STATIC MYBOOL presolve_altsingletonvalid(presolverec *psdata, int rownr, int col } STATIC MYBOOL presolve_multibounds(presolverec *psdata, int rownr, int colnr, - LPSREAL *lobound, LPSREAL *upbound, LPSREAL *aval, MYBOOL *rowbinds) + REAL *lobound, REAL *upbound, REAL *aval, MYBOOL *rowbinds) { lprec *lp = psdata->lp; MYBOOL rowbindsvar = FALSE, status = FALSE; - LPSREAL coeff_a, LHS, RHS, netX, Xupper, Xlower, epsvalue = psdata->epsvalue; + REAL coeff_a, LHS, RHS, netX, Xupper, Xlower, epsvalue = psdata->epsvalue; /* Get variable bounds for netting */ LHS = *lobound; @@ -1307,7 +1305,7 @@ STATIC MYBOOL presolve_multibounds(presolverec *psdata, int rownr, int colnr, LHS -= netX-coeff_a*Xupper; LHS /= coeff_a; if(LHS > Xlower + epsvalue) { - Xlower = presolve_roundrhs(lp, LHS, TRUE); + Xlower = presolve_round(lp, LHS, TRUE); status = TRUE; } else if(LHS > Xlower - epsvalue) @@ -1317,7 +1315,7 @@ STATIC MYBOOL presolve_multibounds(presolverec *psdata, int rownr, int colnr, LHS -= netX-coeff_a*Xlower; LHS /= coeff_a; if(LHS < Xupper - epsvalue) { - Xupper = presolve_roundrhs(lp, LHS, FALSE); + Xupper = presolve_round(lp, LHS, FALSE); status = AUTOMATIC; } else if(LHS < Xupper + epsvalue) @@ -1332,7 +1330,7 @@ STATIC MYBOOL presolve_multibounds(presolverec *psdata, int rownr, int colnr, RHS -= netX-coeff_a*Xupper; RHS /= coeff_a; if(RHS > Xlower + epsvalue) { - Xlower = presolve_roundrhs(lp, RHS, TRUE); + Xlower = presolve_round(lp, RHS, TRUE); status |= TRUE; } else if(RHS > Xlower - epsvalue) @@ -1343,7 +1341,7 @@ STATIC MYBOOL presolve_multibounds(presolverec *psdata, int rownr, int colnr, RHS -= netX-coeff_a*Xlower; RHS /= coeff_a; if(RHS < Xupper - epsvalue) { - Xupper = presolve_roundrhs(lp, RHS, FALSE); + Xupper = presolve_round(lp, RHS, FALSE); status |= AUTOMATIC; } else if(RHS < Xupper + epsvalue) @@ -1376,13 +1374,13 @@ STATIC MYBOOL presolve_testrow(presolverec *psdata, int lastrow) return( TRUE ); } -STATIC MYBOOL presolve_coltighten(presolverec *psdata, int colnr, LPSREAL LOnew, LPSREAL UPnew, int *count) +STATIC MYBOOL presolve_coltighten(presolverec *psdata, int colnr, REAL LOnew, REAL UPnew, int *count) { lprec *lp = psdata->lp; int elmnr, elmend, k, oldcount = 0, newcount = 0, deltainf; - LPSREAL LOold, UPold, Value, margin = psdata->epsvalue; + REAL LOold, UPold, Value, margin = psdata->epsvalue; MATrec *mat = lp->matA; - LPSREAL *value; + REAL *value; int *rownr; /* Attempt correction of marginally equal, but inconsistent input values */ @@ -1503,8 +1501,8 @@ STATIC MYBOOL presolve_coltighten(presolverec *psdata, int colnr, LPSREAL LOnew, /* Now set the new variable bounds, if they are tighter */ if(newcount > oldcount) { - UPnew = presolve_roundval(lp, UPnew); - LOnew = presolve_roundval(lp, LOnew); + UPnew = presolve_precision(lp, UPnew); + LOnew = presolve_precision(lp, LOnew); if(LOnew > UPnew) { if(LOnew-UPnew < margin) { LOnew = UPnew; @@ -1531,7 +1529,7 @@ STATIC int presolve_rowtighten(presolverec *psdata, int rownr, int *tally, MYBOO lprec *lp = psdata->lp; MYBOOL rowbinds; int item = 0, jx, jjx, ix, idxn = 0, *idxbound = NULL, status = RUNNING; - LPSREAL *newbound = NULL, RHlo = get_rh_lower(lp, rownr), RHup = get_rh_upper(lp, rownr), + REAL *newbound = NULL, RHlo = get_rh_lower(lp, rownr), RHup = get_rh_upper(lp, rownr), VARlo, VARup, Aval; MATrec *mat = lp->matA; @@ -1574,13 +1572,13 @@ STATIC int presolve_rowtighten(presolverec *psdata, int rownr, int *tally, MYBOO VARlo = get_lowbo(lp, jx); VARup = get_upbo(lp, jx); - /* while((ix < idxn) && (jx == abs(jjx))) { */ - while((ix < idxn) && (jx == abs((jjx = idxbound[ix])))) { + while((ix < idxn) && (jx == abs(jjx))) { if(jjx < 0) VARlo = newbound[ix]; else VARup = newbound[ix]; ix++; + jjx = idxbound[ix]; } if(!presolve_coltighten(psdata, jx, VARlo, VARup, tally)) { status = presolve_setstatus(psdata, INFEASIBLE); @@ -1594,27 +1592,27 @@ STATIC int presolve_rowtighten(presolverec *psdata, int rownr, int *tally, MYBOO return(status); } -STATIC void set_dv_bounds(presolverec *psdata, int rownr, LPSREAL lowbo, LPSREAL upbo) +STATIC void set_dv_bounds(presolverec *psdata, int rownr, REAL lowbo, REAL upbo) { psdata->dv_lobo[rownr] = lowbo; psdata->dv_upbo[rownr] = upbo; } -STATIC LPSREAL get_dv_lower(presolverec *psdata, int rownr) +STATIC REAL get_dv_lower(presolverec *psdata, int rownr) { return( psdata->dv_lobo[rownr] ); } -STATIC LPSREAL get_dv_upper(presolverec *psdata, int rownr) +STATIC REAL get_dv_upper(presolverec *psdata, int rownr) { return( psdata->dv_upbo[rownr] ); } -STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, LPSREAL newvalue, MYBOOL remove, int *tally) +STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, REAL newvalue, MYBOOL remove, int *tally) { lprec *lp = psdata->lp; int i, ix, ie; MYBOOL isneg, lofinite, upfinite, doupdate = FALSE, chsign = is_chsign(lp, rownr); - LPSREAL lobound, upbound, lovalue, upvalue, + REAL lobound, upbound, lovalue, upvalue, Value, fixvalue, fixprod, mult; MATrec *mat = lp->matA; psrec *ps = psdata->cols; @@ -1698,7 +1696,7 @@ STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, LPSREAL newvalue, if(isneg) { if((ps->negupper[i] < lp->infinite) && lofinite) { ps->negupper[i] += mult*lovalue; - ps->negupper[i] = presolve_roundrhs(lp, ps->negupper[i], FALSE); + ps->negupper[i] = presolve_round(lp, ps->negupper[i], FALSE); } else if(remove && !lofinite) doupdate = TRUE; @@ -1708,7 +1706,7 @@ STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, LPSREAL newvalue, else { if((ps->pluupper[i] < lp->infinite) && upfinite) { ps->pluupper[i] += mult*upvalue; - ps->pluupper[i] = presolve_roundrhs(lp, ps->pluupper[i], FALSE); + ps->pluupper[i] = presolve_round(lp, ps->pluupper[i], FALSE); } else if(remove && !upfinite) doupdate = TRUE; @@ -1720,7 +1718,7 @@ STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, LPSREAL newvalue, if(isneg) { if((ps->neglower[i] > -lp->infinite) && upfinite) { ps->neglower[i] += mult*upvalue; - ps->neglower[i] = presolve_roundrhs(lp, ps->neglower[i], TRUE); + ps->neglower[i] = presolve_round(lp, ps->neglower[i], TRUE); } else if(remove && !upfinite) doupdate = TRUE; @@ -1730,7 +1728,7 @@ STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, LPSREAL newvalue, else { if((ps->plulower[i] > -lp->infinite) && lofinite) { ps->plulower[i] += mult*lovalue; - ps->plulower[i] = presolve_roundrhs(lp, ps->plulower[i], TRUE); + ps->plulower[i] = presolve_round(lp, ps->plulower[i], TRUE); } else if(remove && !lofinite) doupdate = TRUE; @@ -1762,7 +1760,7 @@ STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, LPSREAL newvalue, STATIC int presolve_colsingleton(presolverec *psdata, int i, int j, int *count) { lprec *lp = psdata->lp; - LPSREAL RHlow, RHup, LObound, UPbound, Value; + REAL RHlow, RHup, LObound, UPbound, Value; #ifdef Paranoia if(!isActiveLink(psdata->cols->varmap, j)) @@ -1796,16 +1794,16 @@ STATIC int presolve_colsingleton(presolverec *psdata, int i, int j, int *count) return( presolve_setstatus(psdata, INFEASIBLE) ); } -STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, LPSREAL newvalue, MYBOOL remove, int *tally) +STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, REAL newvalue, MYBOOL remove, int *tally) { lprec *lp = psdata->lp; int i, ix, ie; MYBOOL isneg, lofinite, upfinite, doupdate = FALSE, doOF = TRUE; - LPSREAL lobound, upbound, lovalue, upvalue, + REAL lobound, upbound, lovalue, upvalue, Value, fixvalue, mult; MATrec *mat = lp->matA; psrec *ps = psdata->rows; - LPSREAL *value; + REAL *value; int *rownr; /* Set "fixed" value in case we are deleting a variable */ @@ -1907,7 +1905,7 @@ STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, LPSREAL newvalue, if(isneg) { if((ps->negupper[i] < lp->infinite) && lofinite) { ps->negupper[i] += mult*lovalue; - ps->negupper[i] = presolve_roundrhs(lp, ps->negupper[i], FALSE); + ps->negupper[i] = presolve_round(lp, ps->negupper[i], FALSE); } else if(remove && !lofinite) doupdate = TRUE; @@ -1917,7 +1915,7 @@ STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, LPSREAL newvalue, else { if((ps->pluupper[i] < lp->infinite) && upfinite) { ps->pluupper[i] += mult*upvalue; - ps->pluupper[i] = presolve_roundrhs(lp, ps->pluupper[i], FALSE); + ps->pluupper[i] = presolve_round(lp, ps->pluupper[i], FALSE); } else if(remove && !upfinite) doupdate = TRUE; @@ -1929,7 +1927,7 @@ STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, LPSREAL newvalue, if(isneg) { if((ps->neglower[i] > -lp->infinite) && upfinite) { ps->neglower[i] += mult*upvalue; - ps->neglower[i] = presolve_roundrhs(lp, ps->neglower[i], TRUE); + ps->neglower[i] = presolve_round(lp, ps->neglower[i], TRUE); } else if(remove && !upfinite) doupdate = TRUE; @@ -1939,7 +1937,7 @@ STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, LPSREAL newvalue, else { if((ps->plulower[i] > -lp->infinite) && lofinite) { ps->plulower[i] += mult*lovalue; - ps->plulower[i] = presolve_roundrhs(lp, ps->plulower[i], TRUE); + ps->plulower[i] = presolve_round(lp, ps->plulower[i], TRUE); } else if(remove && !lofinite) doupdate = TRUE; @@ -2012,12 +2010,12 @@ STATIC int presolve_rowfixzero(presolverec *psdata, int rownr, int *nv) } /* Function to find if a variable can be fixed based on considering the dual */ -STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, LPSREAL *fixValue, int *status) +STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, REAL *fixValue, int *status) { lprec *lp = psdata->lp; - MYBOOL hasOF, isDualFREE = TRUE; + MYBOOL hasOF, isMI, isDualFREE = TRUE; int i, ix, ie, *rownr, signOF; - LPSREAL *value, loX, upX, eps = psdata->epsvalue; + REAL *value, loX, upX, eps = psdata->epsvalue; MATrec *mat = lp->matA; /* First check basic variable range */ @@ -2027,7 +2025,7 @@ STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, LPSREAL *fixVa (fabs(upX-loX) < lp->epsvalue) || SOS_is_member_of_type(lp->SOS, colnr, SOSn)) return( FALSE ); - //@FS: unused// isMI = (MYBOOL) (upX <= 0); + isMI = (MYBOOL) (upX <= 0); /* Retrieve OF (standard form assuming maximization) */ ix = mat->col_end[colnr - 1]; @@ -2047,7 +2045,7 @@ STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, LPSREAL *fixVa if(!isActiveLink(psdata->rows->varmap, i)) continue; if(presolve_rowlength(psdata, i) == 1) { - LPSREAL val = my_chsign(is_chsign(lp, i), *value), + REAL val = my_chsign(is_chsign(lp, i), *value), loR = get_rh_lower(lp, i), upR = get_rh_upper(lp, i); if(!presolve_singletonbounds(psdata, i, colnr, &loR, &upR, &val)) { @@ -2055,9 +2053,9 @@ STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, LPSREAL *fixVa return( FALSE ); } if(loR > loX + psdata->epsvalue) - loX = presolve_roundrhs(lp, loR, TRUE); + loX = presolve_round(lp, loR, TRUE); if(upR < upX - psdata->epsvalue) - upX = presolve_roundrhs(lp, upR, FALSE); + upX = presolve_round(lp, upR, FALSE); continue; } else @@ -2106,12 +2104,11 @@ STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, LPSREAL *fixVa return( isDualFREE ); } -#if 0 -STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, LPSREAL *fixvalue) +STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, REAL *fixvalue) { lprec *lp = psdata->lp; int i, ix, item; - LPSREAL loLim, absvalue, epsvalue = psdata->epsvalue; + REAL loLim, absvalue, epsvalue = psdata->epsvalue; MATrec *mat = lp->matA; MYBOOL chsign, canfix = FALSE; @@ -2152,80 +2149,13 @@ STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, LPSREAL *fixva } return( canfix ); } -#else -STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, LPSREAL *fixvalue) -{ - lprec *lp = psdata->lp; - int i, ix, item; - LPSREAL loLim, upLim, range, absvalue, epsvalue = psdata->epsvalue, tolgap; - MATrec *mat = lp->matA; - MYBOOL chsign, status = FALSE; - - if(!is_binary(lp, colnr)) - return( status ); - - /* Loop over all active rows to search for fixing opportunity. The logic is that if a - constraint gets violated by setting a variable at one of its bounds, then it can be - fixed at its opposite bound. */ - item = 0; - - for(ix = presolve_nextrow(psdata, colnr, &item); (ix >= 0); ix = presolve_nextrow(psdata, colnr, &item)) { - i = COL_MAT_ROWNR(ix); - *fixvalue = COL_MAT_VALUE(ix); - absvalue = fabs(*fixvalue); - SETMIN(absvalue, 100); - tolgap = epsvalue*MAX(1, absvalue); - chsign = is_chsign(lp, i); - - /* Get the constraint value limits based on variable bounds, normalized to LE constraint */ - loLim = presolve_sumplumin(lp, i, psdata->rows, FALSE); - upLim = presolve_sumplumin(lp, i, psdata->rows, TRUE); - if(chsign) { - loLim = my_chsign(chsign, loLim); - upLim = my_chsign(chsign, upLim); - swapREAL(&loLim, &upLim); - } - - /* Check the upper constraint bound for possible violation if the value were to be fixed at 1 */ - if(loLim + *fixvalue > lp->orig_rhs[i]+tolgap) { - if(*fixvalue < 0) - presolve_setstatus(psdata, INFEASIBLE); - *fixvalue = 0; - break; - } - - /* Check the lower constraint bound for possible violation if the value were to be fixed at 1 */ - range = get_rh_range(lp, i); - if(!my_infinite(lp, range) && - (upLim + *fixvalue < lp->orig_rhs[i]-range-tolgap)) { - if(*fixvalue > 0) - presolve_setstatus(psdata, INFEASIBLE); - *fixvalue = 0; - break; - } - - /* Check if we have to fix the value at 1 to avoid constraint infeasibility */ - if(psdata->rows->infcount[i] >= 1) - continue; - if(((*fixvalue < 0) && (upLim + *fixvalue >= loLim-tolgap) && (upLim > lp->orig_rhs[i]+tolgap)) || - ((*fixvalue > 0) && (loLim + *fixvalue <= upLim+tolgap) && (loLim < lp->orig_rhs[i]-range-tolgap) && !my_infinite(lp, range))) { - *fixvalue = 1; - break; - } - } - status = (MYBOOL) (ix >= 0); - - /* Returns TRUE if fixing opportunity was identified */ - return( status ); -} -#endif STATIC int presolve_probetighten01(presolverec *psdata, int colnr) { lprec *lp = psdata->lp; MYBOOL chsign; int i, ix, item, n = 0; - LPSREAL upLim, value, absvalue, epsvalue = psdata->epsvalue; + REAL upLim, value, absvalue, epsvalue = psdata->epsvalue; MATrec *mat = lp->matA; #if 0 /* Handled in calling routine */ @@ -2246,7 +2176,7 @@ STATIC int presolve_probetighten01(presolverec *psdata, int colnr) /* Does this constraint qualify for coefficient tightening? */ absvalue = fabs(value); if(upLim - absvalue < lp->orig_rhs[i]-epsvalue*MAX(1, absvalue)) { - LPSREAL delta = lp->orig_rhs[i] - upLim; + REAL delta = lp->orig_rhs[i] - upLim; lp->orig_rhs[i] = upLim; upLim = value - my_chsign(value < 0, delta); COL_MAT_VALUE(ix) = upLim; @@ -2272,7 +2202,7 @@ STATIC int presolve_mergerows(presolverec *psdata, int *nRows, int *nSum) MYBOOL candelete; int status = RUNNING, item1, item2, firstix, RT1, RT2, i, ix, iix, j, jjx, n = 0; - LPSREAL Value1, Value2, bound; + REAL Value1, Value2, bound; MATrec *mat = lp->matA; for(i = lastActiveLink(psdata->rows->varmap); (i > 0) && (status == RUNNING); ) { @@ -2425,7 +2355,7 @@ STATIC MYBOOL presolve_reduceGCD(presolverec *psdata, int *nn, int *nb, int *nsu MYBOOL status = TRUE; int i, jx, je, in = 0, ib = 0; LLONG GCDvalue; - LPSREAL *Avalue, Rvalue, epsvalue = psdata->epsvalue; + REAL *Avalue, Rvalue, epsvalue = psdata->epsvalue; MATrec *mat = lp->matA; for(i = firstActiveLink(psdata->INTmap); i != 0; i = nextActiveLink(psdata->INTmap, i)) { @@ -2479,7 +2409,7 @@ STATIC int presolve_knapsack(presolverec *psdata, int *nn) lprec *lp = psdata->lp; int m, n, i, ix, j, jx, colnr, *rownr = NULL, status = RUNNING; - LPSREAL *colOF = lp->orig_obj, value, *ratio = NULL; + REAL *colOF = lp->orig_obj, value, *ratio = NULL; LLrec *map = psdata->EQmap; MATrec *mat = lp->matA; @@ -2603,7 +2533,7 @@ STATIC MYBOOL presolve_invalideq2(lprec *lp, presolverec *psdata) } /* Callback to obtain the non-zero rows of equality constraints */ -int BFP_CALLMODEL presolve_getcolumnEQ(lprec *lp, int colnr, LPSREAL nzvalues[], int nzrows[], int mapin[]) +int BFP_CALLMODEL presolve_getcolumnEQ(lprec *lp, int colnr, REAL nzvalues[], int nzrows[], int mapin[]) { int i, ib, ie, nn = 0; MATrec *mat = lp->matA; @@ -2677,7 +2607,7 @@ STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int iCoeffChanged = 0, iRowsRemoved = 0, iVarsFixed = 0, nrows = lp->rows, status = RUNNING, *colindex = NULL; MYBOOL freshupdate; - LPSREAL Coeff1, Coeff2, Value1, Value2, lobound, upbound, bound, test, product, + REAL Coeff1, Coeff2, Value1, Value2, lobound, upbound, bound, test, product, *colvalue = NULL, *delvalue = NULL, *colitem; MATrec *mat = lp->matA, *rev = NULL; DeltaVrec *DV = NULL; @@ -2832,7 +2762,7 @@ STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int if(is_int(lp, jjx)) lp->orig_upbo[k] = floor(bound + lp->epsint); else - lp->orig_upbo[k] = presolve_roundrhs(lp, bound, FALSE); + lp->orig_upbo[k] = presolve_round(lp, bound, FALSE); } } else { @@ -2841,7 +2771,7 @@ STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int if(is_int(lp, jjx)) lp->orig_lowbo[k] = ceil(bound - lp->epsint); else - lp->orig_lowbo[k] = presolve_roundrhs(lp, bound, TRUE); + lp->orig_lowbo[k] = presolve_round(lp, bound, TRUE); } } } @@ -2854,7 +2784,7 @@ STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int if(is_int(lp, jjx)) lp->orig_upbo[k] = floor(bound + lp->epsint); else - lp->orig_upbo[k] = presolve_roundrhs(lp, bound, FALSE); + lp->orig_upbo[k] = presolve_round(lp, bound, FALSE); } } else { @@ -2863,7 +2793,7 @@ STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int if(is_int(lp, jjx)) lp->orig_lowbo[k] = ceil(bound - lp->epsint); else - lp->orig_lowbo[k] = presolve_roundrhs(lp, bound, TRUE); + lp->orig_lowbo[k] = presolve_round(lp, bound, TRUE); } } } @@ -3040,7 +2970,7 @@ STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int STATIC MYBOOL presolve_impliedfree(lprec *lp, presolverec *psdata, int colnr) { int i, ix, ie; - LPSREAL Tlower, Tupper; + REAL Tlower, Tupper; MYBOOL status, rowbinds, isfree = FALSE; MATrec *mat = lp->matA; @@ -3067,7 +2997,7 @@ STATIC MYBOOL presolve_impliedcolfix(presolverec *psdata, int rownr, int colnr, MYBOOL signflip, undoadded = FALSE; MATrec *mat = lp->matA; int jx, i, ib, ie = mat->row_end[rownr]; - LPSREAL varLo = 0, varHi = 0, varRange, conRange = 0, matValue = 0, dual, RHS = lp->orig_rhs[rownr], + REAL varLo = 0, varHi = 0, varRange, conRange = 0, matValue = 0, dual, RHS = lp->orig_rhs[rownr], pivot, matAij = mat_getitem(mat, rownr, colnr), *vecOF = lp->orig_obj; /* We cannot have semi-continuous or non-qualifying integers */ @@ -3331,7 +3261,7 @@ STATIC presolverec *presolve_init(lprec *lp) int k, i, ix, ixx, colnr, ncols = lp->columns, nrows = lp->rows; - LPSREAL hold; + REAL hold; MATrec *mat = lp->matA; presolverec *psdata = NULL; @@ -3428,8 +3358,6 @@ STATIC presolverec *presolve_init(lprec *lp) ROW_MAT_VALUE(ix) *= hold; } lp->orig_rhs[i] *= hold; - if(!my_infinite(lp, lp->orig_upbo[i])) - lp->orig_upbo[i] *= hold; /* KE: Fix due to Andy Loto - 20070619 */ } } @@ -3457,7 +3385,7 @@ STATIC int presolve_makefree(presolverec *psdata) { lprec *lp = psdata->lp; int i, ix, j, nn = 0; - LPSREAL Xlower, Xupper, losum, upsum, lorhs, uprhs, freeinf = lp->infinite / 10; + REAL Xlower, Xupper, losum, upsum, lorhs, uprhs, freeinf = lp->infinite / 10; MATrec *mat = lp->matA; LLrec *colLL = NULL; @@ -3621,17 +3549,13 @@ STATIC MYBOOL presolve_finalize(presolverec *psdata) for(n = 1; n <= ke; n++) my_roundzero(lp->orig_rhs[n], lp->epsvalue); - /* Update the SOS sparse mapping */ - if(SOS_count(lp) > 0) - SOS_member_updatemap(lp->SOS); - /* Validate matrix and reconstruct row indexation */ return(mat_validate(lp->matA)); } STATIC MYBOOL presolve_debugdump(lprec *lp, presolverec *psdata, char *filename, MYBOOL doappend) { - FILE *output = stdout; + FILE *output; /* = stdout; */ int size; MYBOOL ok; @@ -3706,7 +3630,7 @@ int CMP_CALLMODEL compAggregate(const UNIONTYPE QSORTrec *current, const UNIONTY int index1 = (int) (current->pvoidint2.intval), index2 = (int) (candidate->pvoidint2.intval); lprec *lp = (lprec *) current->pvoidint2.ptr; - LPSREAL value1 = lp->orig_obj[index1], + REAL value1 = lp->orig_obj[index1], value2 = lp->orig_obj[index2]; /* Smallest objective coefficient (largest contribution to OF) */ @@ -3736,7 +3660,7 @@ STATIC int presolve_rowdominance(presolverec *psdata, int *nCoeffChanged, int *n MATrec *mat = lp->matA; int i, ii, ib, ie, n, jb, je, jx, *coldel = NULL, status = RUNNING, item, iCoeffChanged = 0, iRowRemoved = 0, iVarFixed = 0; - LPSREAL ratio, *rowvalues = NULL; + REAL ratio, *rowvalues = NULL; UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->rows+1, sizeof(*QS)); /* Check if we were able to obtain working memory */ @@ -3915,7 +3839,6 @@ STATIC int presolve_rowdominance(presolverec *psdata, int *nCoeffChanged, int *n return( status ); } -#if 0 STATIC int presolve_coldominance01(presolverec *psdata, int *nConRemoved, int *nVarsFixed, int *nSum) /* The current version of this routine eliminates binary variables that are dominated via set coverage or unit knapsack constraints */ @@ -3925,7 +3848,7 @@ STATIC int presolve_coldominance01(presolverec *psdata, int *nConRemoved, int *n MYBOOL first; int i, ii, ib, ie, n, jb, je, jx, jj, item, item2, *coldel = NULL, status = RUNNING, iVarFixed = 0; - LPSREAL scale, rhsval, *colvalues = NULL; + REAL scale, rhsval, *colvalues = NULL; UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->columns+1, sizeof(*QS)); /* Check if we were able to obtain working memory */ @@ -4043,9 +3966,8 @@ STATIC int presolve_coldominance01(presolverec *psdata, int *nConRemoved, int *n } /* Also make sure we have a compatible RHS (since this version of the dominance logic only applies to "sets") */ - rhsval = scale*lp->orig_rhs[jx] - 1.0; - /* if((rhsval < 0) || (rhsval > 1 + psdata->epsvalue)) */ - if(fabs(rhsval) > psdata->epsvalue) + rhsval = scale*lp->orig_rhs[jx]; + if((rhsval < 0) || (rhsval > 1 + psdata->epsvalue)) break; } @@ -4091,204 +4013,6 @@ STATIC int presolve_coldominance01(presolverec *psdata, int *nConRemoved, int *n return( status ); } -#else - -/* DEVELOPMENT/TEST CODE FOR POSSIBLE REPLACEMENT OF SIMILAR FUNCTION IN lp_presolve.c */ - -#define NATURAL int - -STATIC int presolve_coldominance01(presolverec *psdata, NATURAL *nConRemoved, NATURAL *nVarsFixed, NATURAL *nSum) -/* The current version of this routine eliminates binary variables - that are dominated via set coverage or unit knapsack constraints */ -{ - lprec *lp = psdata->lp; - MATrec *mat = lp->matA; - NATURAL i, ib, ie, jx, item, item2, - n = lp->int_vars, iVarFixed = 0, nrows = lp->rows, - *coldel = NULL; - int jb, jj, ii, - status = RUNNING; - LPSREAL rhsval = 0.0, - *colvalues = NULL, *colobj = NULL; - LLrec *sets = NULL; - UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(n+1, sizeof(*QS)); - - /* Check if we were able to obtain working memory */ - if(QS == NULL) - return( status); - if(n == 0) - goto Finish; - - /* Create list of set coverage and knapsack constraints */ - createLink(nrows, &sets, NULL); - for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) { - if((lp->orig_rhs[i] < 0) || (psdata->rows->negcount[i] > 0)) - continue; - item = 0; - for(jb = presolve_nextcol(psdata, i, &item); jb >= 0; - jb = presolve_nextcol(psdata, i, &item)) { - jx = ROW_MAT_COLNR(jb); - if(!is_binary(lp, jx)) - break; - rhsval = ROW_MAT_VALUE(jb) - 1; - if(fabs(rhsval) > lp->epsvalue) - break; - } - if(jb < 0) - setLink(sets, i); - } - if(countActiveLink(sets) == 0) - goto Finish; - - /* A column dominates another binary variable column with the following criteria: - 1) The relative matrix non-zero entries are identical - 2) The relative objective coefficient is worse than the other; - if the OF coefficients are identical, we can delete an arbitrary variable */ - n = 0; - for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i)) - if(is_binary(lp, i) && !SOS_is_member(lp->SOS, 0, i)) { - /* Make sure the column is member of at least one set */ - item = 0; - for(jb = presolve_nextrow(psdata, i, &item); jb >= 0; - jb = presolve_nextrow(psdata, i, &item)) { - jx = COL_MAT_ROWNR(jb); - if(isActiveLink(sets, jx)) - break; - } - - /* Add to list if set membership test is Ok */ - if(jb >= 0) { - QS[n].int4.intval = i; - item = 0; - ii = presolve_nextrow(psdata, i, &item); - QS[n].int4.intpar1 = COL_MAT_ROWNR(ii); - ii = presolve_collength(psdata, i); - QS[n].int4.intpar2 = ii; - n++; - } - } - if(n <= 1) { - FREE(QS); - return( status ); - } - QS_execute(QS, n, (findCompare_func *) compRedundant, NULL); - - /* Let us start from the top of the list, going forward and looking - for the longest possible dominated column */ - if(!allocREAL(lp, &colvalues, nrows + 1, TRUE) || - !allocREAL(lp, &colobj, n + 1, FALSE) || - !allocINT(lp, &coldel, n + 1, FALSE)) - goto Finish; - - for(ib = 0; ib < n; ib++) { - - /* Get column and check if it was previously eliminated */ - i = QS[ib].int4.intval; - if(!isActiveLink(psdata->cols->varmap, i)) - continue; - - /* Load the non-zero column values */ - item = 0; - for(jb = presolve_nextrow(psdata, i, &item); jb >= 0; - jb = presolve_nextrow(psdata, i, &item)) { - jx = COL_MAT_ROWNR(jb); - colvalues[jx] = COL_MAT_VALUE(jb); - } - - /* Store data for current column */ - coldel[0] = 1; - coldel[1] = i; - colobj[1] = lp->orig_obj[i]; - - /* Loop over all other columns to see if they have equal constraint coefficients */ - for(ie = ib+1; ie < n; ie++) { - - /* Check if this column was previously eliminated */ - ii = QS[ie].int4.intval; - if(!isActiveLink(psdata->cols->varmap, ii)) - continue; - - /* Insist on identical column lengths (sort is decending in column lengths) */ - ii = QS[ib].int4.intpar2 - QS[ie].int4.intpar2; - if(ii != 0) - break; - - /* Also insist on identical starting positions */ - ii = QS[ib].int4.intpar1 - QS[ie].int4.intpar1; - if(ii != 0) - break; - - /* Get column and check if it was previously eliminated */ - ii = QS[ie].int4.intval; - -#ifdef Paranoia - if((QS[ib].int4.intpar1 > QS[ie].int4.intpar1) || - ((QS[ib].int4.intpar1 == QS[ie].int4.intpar1) && (QS[ib].int4.intpar2 < QS[ie].int4.intpar2))) - report(lp, SEVERE, "presolve_coldominance01: Invalid sorted column order\n"); -#endif - - /* Loop over every column member to confirm that the candidate is identical in every row; - we also compute the minimal set order */ - rhsval = lp->infinite; - item = 0; - item2 = 0; - for(jb = presolve_nextrow(psdata, ii, &item), - jj = presolve_nextrow(psdata, i, &item2); jb >= 0; - jb = presolve_nextrow(psdata, ii, &item), - jj = presolve_nextrow(psdata, i, &item2)) { - jx = COL_MAT_ROWNR(jb); - if(jx != COL_MAT_ROWNR(jj)) - break; - if(isActiveLink(sets, jx)) - SETMIN(rhsval, lp->orig_rhs[jx]); - } - - /* "We have contact" */ - if(jb < 0) { - coldel[++coldel[0]] = ii; - colobj[coldel[0]] = lp->orig_obj[ii]; - } - } - - /* Find the dominant columns, fix and delete the others */ - if(coldel[0] > 1) { - qsortex(colobj+1, coldel[0], 0, sizeof(*colobj), FALSE, compareREAL, coldel+1, sizeof(*coldel)); - /* if(rhsval+lp->epsvalue < lp->infinite) { */ - jb = (NATURAL) (rhsval+lp->epsvalue); - /* printf("%f / %d\n", rhsval, jb); */ - for(jb++; jb <= coldel[0]; jb++) { - jx = coldel[jb]; - if(!presolve_colfix(psdata, jx, lp->orig_lowbo[nrows+jx], TRUE, &iVarFixed)) { - status = presolve_setstatus(psdata, INFEASIBLE); - goto Finish; - } - presolve_colremove(psdata, jx, TRUE); - } - /*} */ - } - - /* Clear the non-zero row values ahead of the next row candidate */ - if(ib + 1 < n) { - ie = mat->col_end[i-1]; - ii = mat->col_end[i]; - for(; ie < ii; ie++) - colvalues[COL_MAT_ROWNR(ie)] = 0; - } - } -Finish: - freeLink(&sets); - FREE(QS); - FREE(colvalues); - FREE(coldel); - FREE(colobj); - - (*nVarsFixed) += iVarFixed; - (*nSum) += iVarFixed; - - return( status ); -} - -#endif STATIC int presolve_aggregate(presolverec *psdata, int *nConRemoved, int *nVarsFixed, int *nSum) /* This routine combines compatible or identical columns */ @@ -4298,7 +4022,7 @@ STATIC int presolve_aggregate(presolverec *psdata, int *nConRemoved, int *nVarsF MYBOOL first; int i, ii, ib, ie, ix, n, jb, je, jx, jj, item, item2, *coldel = NULL, status = RUNNING, iVarFixed = 0; - LPSREAL scale, *colvalues = NULL; + REAL scale, *colvalues = NULL; UNIONTYPE QSORTrec *QScand = (UNIONTYPE QSORTrec *) calloc(lp->columns+1, sizeof(*QScand)); /* Check if we were able to obtain working memory */ @@ -4394,7 +4118,7 @@ STATIC int presolve_aggregate(presolverec *psdata, int *nConRemoved, int *nVarsF /* Sort the aggregation list if we have aggregation candidates */ if(coldel[0] > 1) { - LPSREAL of, ofelim, fixvalue; + REAL of, ofelim, fixvalue; MYBOOL isint; UNIONTYPE QSORTrec *QSagg = (UNIONTYPE QSORTrec *) calloc(coldel[0], sizeof(*QSagg)); @@ -4513,7 +4237,7 @@ STATIC int presolve_makesparser(presolverec *psdata, int *nCoeffChanged, int *nC MYBOOL chsign; int i, ii, ib, ix, k, n, jb, je, jl, jjb, jje, jjl, jx, jjx, item, itemEQ, *nzidx = NULL, status = RUNNING, iObjChanged = 0, iCoeffChanged = 0, iConRemove = 0; - LPSREAL test, ratio, value, valueEQ, *valptr; + REAL test, ratio, value, valueEQ, *valptr; LLrec *EQlist = NULL; UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->rows, sizeof(*QS)); @@ -4573,7 +4297,7 @@ STATIC int presolve_makesparser(presolverec *psdata, int *nCoeffChanged, int *nC test = ratio = 0.0; itemEQ = 0; nzidx[0] = 0; - while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && /*(itemEQ > 0) && */ + while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && //(itemEQ > 0) && (fabs(test-ratio) < psdata->epsvalue)) { valueEQ = ROW_MAT_VALUE(jjx); if(valueEQ == 0) @@ -4642,7 +4366,7 @@ STATIC int presolve_makesparser(presolverec *psdata, int *nCoeffChanged, int *nC itemEQ = 0; item = 0; nzidx[0] = 0; - while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && /*(itemEQ > 0) &&*/ + while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && //(itemEQ > 0) && (fabs(test-ratio) < psdata->epsvalue)) { valueEQ = ROW_MAT_VALUE(jjx); if(valueEQ == 0) @@ -4788,7 +4512,7 @@ STATIC int presolve_SOS1(presolverec *psdata, int *nCoeffChanged, int *nConRemov MYBOOL candelete, SOS_GUBactive = FALSE; int iCoeffChanged = 0, iConRemove = 0, iSOS = 0, i,ix,iix, j,jx,jjx, status = RUNNING; - LPSREAL Value1; + REAL Value1; MATrec *mat = lp->matA; for(i = lastActiveLink(psdata->rows->varmap); i > 0; ) { @@ -4811,7 +4535,7 @@ STATIC int presolve_SOS1(presolverec *psdata, int *nCoeffChanged, int *nConRemov /* Define a new SOS instance */ ix = SOS_count(lp) + 1; - sprintf(SOSname, "SOS_%d", ix); + snprintf(SOSname, sizeof(SOSname), "SOS_%d", ix); ix = add_SOS(lp, SOSname, 1, ix, 0, NULL, NULL); if(jx == EQ) SOS_set_GUB(lp->SOS, ix, TRUE); @@ -4851,7 +4575,7 @@ STATIC int presolve_SOS1(presolverec *psdata, int *nCoeffChanged, int *nConRemov STATIC int presolve_boundconflict(presolverec *psdata, int baserowno, int colno) { - LPSREAL Value1, Value2; + REAL Value1, Value2; lprec *lp = psdata->lp; MATrec *mat = lp->matA; int ix, item = 0, @@ -4888,15 +4612,15 @@ STATIC int presolve_boundconflict(presolverec *psdata, int baserowno, int colno) STATIC int presolve_columns(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nBoundTighten, int *nSum) { lprec *lp = psdata->lp; - MYBOOL candelete, isOFNZ, + MYBOOL candelete, isOFNZ, unbounded, probefix = is_presolve(lp, PRESOLVE_PROBEFIX), #if 0 probereduce = is_presolve(lp, PRESOLVE_PROBEREDUCE), #endif colfixdual = is_presolve(lp, PRESOLVE_COLFIXDUAL); int iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0, - status = RUNNING, ix, j, countNZ; - LPSREAL Value1; + status = RUNNING, ix, j, countNZ, item; + REAL Value1; for(j = firstActiveLink(psdata->cols->varmap); (j != 0) && (status == RUNNING); ) { @@ -4910,14 +4634,14 @@ STATIC int presolve_columns(presolverec *psdata, int *nCoeffChanged, int *nConRe countNZ = presolve_collength(psdata, j); isOFNZ = isnz_origobj(lp, j); Value1 = get_lowbo(lp, j); - //@FS: unused// unbounded = is_unbounded(lp, j); + unbounded = is_unbounded(lp, j); /* Clear unnecessary semicont-definitions */ if((lp->sc_vars > 0) && (Value1 == 0) && is_semicont(lp, j)) set_semicont(lp, j, FALSE); candelete = FALSE; - //@FS: unused// item = 0; + item = 0; ix = lp->rows + j; /* Check if the variable is unused */ @@ -5067,7 +4791,7 @@ STATIC int presolve_freeandslacks(presolverec *psdata, int *nCoeffChanged, int * impliedslack = is_presolve(lp, PRESOLVE_IMPLIEDSLK); int iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, status = RUNNING, i, ix, j, countNZ; - LPSREAL coeff_bl, coeff_bu; + REAL coeff_bl, coeff_bu; MATrec *mat = lp->matA; if(impliedfree || impliedslack) @@ -5122,7 +4846,7 @@ STATIC int presolve_freeandslacks(presolverec *psdata, int *nCoeffChanged, int * #endif (countNZ > 1) && !is_constr_type(lp, i, EQ)) { - LPSREAL *target, + REAL *target, ValueA = COL_MAT_VALUE(presolve_lastrow(psdata, j)); #if 0 coeff_bu = get_rh_upper(lp, i); @@ -5145,7 +4869,7 @@ STATIC int presolve_freeandslacks(presolverec *psdata, int *nCoeffChanged, int * } else { *target += ValueA * coeff_bu; - *target = presolve_roundrhs(lp, *target, FALSE); + *target = presolve_round(lp, *target, FALSE); } } } @@ -5169,7 +4893,7 @@ STATIC int presolve_freeandslacks(presolverec *psdata, int *nCoeffChanged, int * } else { *target -= ValueA * coeff_bu; - *target = presolve_roundrhs(lp, *target, FALSE); + *target = presolve_round(lp, *target, FALSE); } } presolve_colfix(psdata, j, coeff_bl, TRUE, &iVarFixed); @@ -5197,7 +4921,7 @@ STATIC int presolve_preparerows(presolverec *psdata, int *nBoundTighten, int *nS MYBOOL impliedfree = is_presolve(lp, PRESOLVE_IMPLIEDFREE), tightenbounds = is_presolve(lp, PRESOLVE_BOUNDS); int iRangeTighten = 0, iBoundTighten = 0, status = RUNNING, i, j; - LPSREAL losum, upsum, lorhs, uprhs, epsvalue = psdata->epsvalue; + REAL losum, upsum, lorhs, uprhs, epsvalue = psdata->epsvalue; MATrec *mat = lp->matA; for(i = lastActiveLink(psdata->rows->varmap); i > 0; i = prevActiveLink(psdata->rows->varmap, i)) { @@ -5230,11 +4954,11 @@ STATIC int presolve_preparerows(presolverec *psdata, int *nBoundTighten, int *nS } if(losum > lorhs+epsvalue) { - set_rh_lower(lp, i, presolve_roundrhs(lp, losum, TRUE)); + set_rh_lower(lp, i, presolve_round(lp, losum, TRUE)); iRangeTighten++; } if(upsum < uprhs-epsvalue) { - set_rh_upper(lp, i, presolve_roundrhs(lp, upsum, FALSE)); + set_rh_upper(lp, i, presolve_round(lp, upsum, FALSE)); iRangeTighten++; } } @@ -5270,7 +4994,7 @@ STATIC int presolve_rows(presolverec *psdata, int *nCoeffChanged, int *nConRemov MYBOOL candelete; int iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0, status = RUNNING, i,ix, j,jx, item; - LPSREAL Value1, Value2, losum, upsum, lorhs, uprhs, epsvalue = psdata->epsvalue; + REAL Value1, Value2, losum, upsum, lorhs, uprhs, epsvalue = psdata->epsvalue; MATrec *mat = lp->matA; for(i = lastActiveLink(psdata->rows->varmap); (i > 0) && (status == RUNNING); ) { @@ -5333,10 +5057,9 @@ STATIC int presolve_rows(presolverec *psdata, int *nCoeffChanged, int *nConRemov MYBOOL isSOS = (MYBOOL) (SOS_is_member(lp->SOS, 0, j) != FALSE), deleteSOS = isSOS && presolve_candeletevar(psdata, j); if((Value1 != 0) && deleteSOS) { - if(!presolve_fixSOS1(psdata, j, Value1, &iConRemove, &iVarFixed)) { + if(!presolve_fixSOS1(psdata, j, Value1, &iConRemove, &iVarFixed)) status = presolve_setstatus(psdata, INFEASIBLE); - } - psdata->forceupdate = TRUE; // @FS #FIXME: Does this belong into the if clause above? If the code was correct before my changed version is also correct! But the intention was misleading. + psdata->forceupdate = TRUE; } else { if(!presolve_colfix(psdata, j, Value1, (MYBOOL) !isSOS, NULL)) @@ -5438,7 +5161,7 @@ STATIC int presolve(lprec *lp) i, j = 0, jx = 0, jjx = 0, k, oSum, iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0, iSOS = 0, iSum = 0, nCoeffChanged = 0, nConRemove = 0, nVarFixed = 0, nBoundTighten = 0, nSOS = 0, nSum = 0; - LPSREAL Value1, Value2, initrhs0 = lp->orig_rhs[0]; + REAL Value1, Value2, initrhs0 = lp->orig_rhs[0]; presolverec *psdata = NULL; MATrec *mat = lp->matA; @@ -5473,7 +5196,8 @@ STATIC int presolve(lprec *lp) /* Produce original model statistics (do hoops to produce correct stats if we have SOS'es) */ i = SOS_count(lp); if(i > 0) { - SOS_member_updatemap(lp->SOS); + if(lp->SOS->memberpos == NULL) + SOS_member_updatemap(lp->SOS); lp->sos_vars = SOS_memberships(lp->SOS, 0); } REPORT_modelinfo(lp, TRUE, "SUBMITTED"); @@ -5491,6 +5215,7 @@ write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */ /*write_mps(lp, "test_in.mps");*/ /* Write to lp-formatted file for debugging */ #endif + /* Update inf norms and check for potential factorization trouble */ mat_computemax(mat /*, FALSE */); #if 0 @@ -5510,7 +5235,7 @@ write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */ ) if((lp->simplex_strategy & SIMPLEX_DYNAMIC) > 0) { clear_action(&lp->algopt, ALGOPT_OBJINBASIS); - report(lp, NORMAL, "Moved objective function out of the basis matrix to enhance factorization accuracy.\n"); + report(lp, NORMAL, "Moved objective function out of the basis matrix to preserve accuracy.\n"); } else if(mat->dynrange > 1.0) report(lp, IMPORTANT, "Warning: Objective/matrix coefficient magnitude differences will cause inaccuracy!\n"); @@ -5533,12 +5258,9 @@ write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */ for(i = 1; i <= SOS_count(lp); i++) { k = SOS_infeasible(lp->SOS, i); if(k > 0) { - presolverec psdata; - - psdata.lp = lp; report(lp, NORMAL, "presolve: Found SOS %d (type %d) to be range-infeasible on variable %d\n", i, SOS_get_type(lp->SOS, i), k); - status = presolve_setstatus(&psdata, INFEASIBLE); + status = presolve_setstatus(psdata, INFEASIBLE); j++; } } @@ -5710,11 +5432,6 @@ write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */ nSOS += iSOS; nSum += iSum; - iSum = iConRemove + iVarFixed + iBoundTighten + iCoeffChanged; - if(iSum > 0) - report(lp, NORMAL, "Presolve O:%d -> Reduced rows:%5d, cols:%5d --- changed bnds:%5d, Ab:%5d.\n", - psdata->outerloops, iConRemove, iVarFixed, iBoundTighten, iCoeffChanged); - /* Do the outermost loop again if we were successful in this presolve sequences */ } while(presolve_statuscheck(psdata, &status) && (psdata->forceupdate || (oSum < nSum)) && @@ -5726,8 +5443,6 @@ write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */ i = presolve_debugcheck(lp, psdata->rows->varmap, psdata->cols->varmap); if(i > 0) report(lp, SEVERE, "presolve: %d internal consistency failure%s\n", i, my_plural_std(i)); - if((SOS_count(lp) > 0) && !presolve_SOScheck(psdata)) - report(lp, SEVERE, "presolve: SOS sparse member mapping problem - part 1\n"); #endif /* Perform bound relaxation to reduce chance of degeneracy. */ if((status == RUNNING) && !is_presolve(lp, PRESOLVE_IMPLIEDFREE)) @@ -5771,25 +5486,24 @@ write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */ else presolve_rangeorig(lp, 0, psdata->rows, &Value1, &Value2, -initrhs0); if((fabs(Value1 - Value2) < psdata->epsvalue) || (fabs(my_reldiff(Value1, Value2)) < psdata->epsvalue)) { + report(lp, NORMAL, "%20s OPTIMAL solution found............... %-g", "", Value1); if((lp->rows == 0) && (lp->columns == 0)) { status = PRESOLVED; - Value1 = my_chsign(is_maxim(lp), Value1); lp->solution[0] = Value1; lp->best_solution[0] = Value1; lp->full_solution[0] = Value1; } - report(lp, NORMAL, "%20s OPTIMAL solution found............... %-g", "", Value1); } else if((status == RUNNING) && (i >= NORMAL)) { char lonum[20], upnum[20]; if(my_infinite(lp, Value1)) - sprintf(lonum, "%13s", "-Inf"); + snprintf(lonum, sizeof(lonum), "%13s", "-Inf"); else - sprintf(lonum, "%+12g", Value1); + snprintf(lonum, sizeof(lonum), "%+12g", Value1); if(my_infinite(lp, Value2)) - sprintf(upnum, "%-13s", "Inf"); + snprintf(upnum, sizeof(upnum), "%-13s", "Inf"); else - sprintf(upnum, "%+-12g", Value2); + snprintf(upnum, sizeof(upnum), "%+-12g", Value2); report(lp, i, "%20s [ %s < Z < %s ]\n", "", lonum, upnum); } @@ -5821,10 +5535,8 @@ write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */ lp->usermessage(lp, lp->msghandle, MSG_PRESOLVE); /* Create master SOS variable list */ - if(SOS_count(lp) > 0) { - /*SOS_member_updatemap(lp->SOS); */ + if(SOS_count(lp) > 0) make_SOSchain(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)); - } /* Finalize model not identified as infeasible or unbounded */ if(status == RUNNING) { diff --git a/src/lpsolve/headers/run_headers/lp_presolve.h b/src/lpSolve/src/lp_presolve.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_presolve.h rename to src/lpSolve/src/lp_presolve.h diff --git a/src/lpsolve/build/lp_solve/lp_price.c b/src/lpSolve/src/lp_price.c similarity index 94% rename from src/lpsolve/build/lp_solve/lp_price.c rename to src/lpSolve/src/lp_price.c index c6dca32e..816a4b03 100644 --- a/src/lpsolve/build/lp_solve/lp_price.c +++ b/src/lpSolve/src/lp_price.c @@ -36,6 +36,7 @@ v1.2.0 24 March 2005 Completed multiple pricing logic. ------------------------------------------------------------------------- */ +INLINE REAL normalizeEdge(lprec *lp, int item, REAL edge, MYBOOL isdual); /* Comparison operators for entering and leaving variables for both the primal and dual simplexes. The functions compare a candidate variable with an incumbent. */ @@ -43,15 +44,17 @@ int CMP_CALLMODEL compareImprovementVar(const pricerec *current, const pricerec { register int result = COMP_PREFERNONE; register lprec *lp = current->lp; - register LPSREAL testvalue, margin = PREC_IMPROVEGAP; - int currentvarno = current->varno, - candidatevarno = candidate->varno; + register REAL testvalue, margin = PREC_IMPROVEGAP; + int currentcolno, currentvarno = current->varno, + candidatecolno, candidatevarno = candidate->varno; MYBOOL isdual = candidate->isdual; if(isdual) { candidatevarno = lp->var_basic[candidatevarno]; currentvarno = lp->var_basic[currentvarno]; } + candidatecolno = candidatevarno - lp->rows; + currentcolno = currentvarno - lp->rows; /* Do pivot-based selection unless Bland's (first index) rule is active */ if(lp->_piv_rule_ != PRICER_FIRSTINDEX) { @@ -97,8 +100,6 @@ int CMP_CALLMODEL compareImprovementVar(const pricerec *current, const pricerec #ifdef UseSortOnColumnLength /* Prevent long columns from entering the basis */ if(result == COMP_PREFERNONE) { - int candidatecolno = candidatevarno - lp->rows; - int currentcolno = currentvarno - lp->rows; if(candidatecolno > 0) testvalue = mat_collength(lp->matA, candidatecolno) + (is_obj_in_basis(lp) && (lp->obj[candidatecolno] != 0) ? 1 : 0); @@ -154,16 +155,18 @@ int CMP_CALLMODEL compareSubstitutionVar(const pricerec *current, const pricerec { register int result = COMP_PREFERNONE; register lprec *lp = current->lp; - register LPSREAL testvalue = candidate->theta, + register REAL testvalue = candidate->theta, margin = current->theta; MYBOOL isdual = candidate->isdual, candbetter; - int currentvarno = current->varno, - candidatevarno = candidate->varno; + int currentcolno, currentvarno = current->varno, + candidatecolno, candidatevarno = candidate->varno; if(!isdual) { candidatevarno = lp->var_basic[candidatevarno]; currentvarno = lp->var_basic[currentvarno]; } + candidatecolno = candidatevarno - lp->rows; + currentcolno = currentvarno - lp->rows; /* Compute the ranking test metric. */ if(isdual) { @@ -194,7 +197,7 @@ int CMP_CALLMODEL compareSubstitutionVar(const pricerec *current, const pricerec /* Resolve a tie */ if(result == COMP_PREFERNONE) { - LPSREAL currentpivot = fabs(current->pivot), + REAL currentpivot = fabs(current->pivot), candidatepivot = fabs(candidate->pivot); /* Handle first index / Bland's rule specially */ @@ -237,20 +240,16 @@ int CMP_CALLMODEL compareSubstitutionVar(const pricerec *current, const pricerec #ifdef UseSortOnColumnLength /* Prevent long columns from entering the basis */ if(result == COMP_PREFERNONE) { - int candidatecolno = candidatevarno - lp->rows; - int currentcolno = currentvarno - lp->rows; - if(candidatecolno > 0) { + if(candidatecolno > 0) testvalue = mat_collength(lp->matA, candidatecolno) + (is_obj_in_basis(lp) && (lp->obj[candidatecolno] != 0) ? 1 : 0); - } else { + else testvalue = 1; - } - if(currentcolno > 0) { + if(currentcolno > 0) testvalue -= mat_collength(lp->matA, currentcolno) + (is_obj_in_basis(lp) && (lp->obj[currentcolno] != 0) ? 1 : 0); - } else { + else testvalue -= 1; - } if(testvalue > 0) result = COMP_PREFERCANDIDATE; else if(testvalue < 0) @@ -294,7 +293,7 @@ int CMP_CALLMODEL compareSubstitutionVar(const pricerec *current, const pricerec } int CMP_CALLMODEL compareBoundFlipVar(const pricerec *current, const pricerec *candidate) { - register LPSREAL testvalue, margin; + register REAL testvalue, margin; register int result = COMP_PREFERNONE; register lprec *lp = current->lp; MYBOOL candbetter; @@ -334,7 +333,7 @@ int CMP_CALLMODEL compareBoundFlipVar(const pricerec *current, const pricerec *c /* Tertiary selection based on priority for large pivot sizes */ if(result == COMP_PREFERNONE) { - LPSREAL currentpivot = fabs(current->pivot), + REAL currentpivot = fabs(current->pivot), candidatepivot = fabs(candidate->pivot); if(candidatepivot > currentpivot+margin) result = COMP_PREFERCANDIDATE; @@ -374,7 +373,7 @@ int CMP_CALLMODEL compareBoundFlipVar(const pricerec *current, const pricerec *c a subject for the comparison functions/operators. */ STATIC MYBOOL validImprovementVar(pricerec *candidate) { - register LPSREAL candidatepivot = fabs(candidate->pivot); + register REAL candidatepivot = fabs(candidate->pivot); #ifdef Paranoia return( (MYBOOL) ((candidate->varno > 0) && (candidatepivot > candidate->lp->epsvalue)) ); @@ -386,7 +385,7 @@ STATIC MYBOOL validImprovementVar(pricerec *candidate) STATIC MYBOOL validSubstitutionVar(pricerec *candidate) { register lprec *lp = candidate->lp; - register LPSREAL theta = (candidate->isdual ? fabs(candidate->theta) : candidate->theta); + register REAL theta = (candidate->isdual ? fabs(candidate->theta) : candidate->theta); #ifdef Paranoia if(candidate->varno <= 0) @@ -660,10 +659,10 @@ STATIC void makePriceLoop(lprec *lp, int *start, int *end, int *delta) } /* Routine to verify accuracy of the current basis factorization */ -STATIC MYBOOL serious_facterror(lprec *lp, LPSREAL *bvector, int maxcols, LPSREAL tolerance) +STATIC MYBOOL serious_facterror(lprec *lp, REAL *bvector, int maxcols, REAL tolerance) { int i, j, ib, ie, nz, nc; - LPSREAL sum, tsum = 0, err = 0; + REAL sum, tsum = 0, err = 0; MATrec *mat = lp->matA; if(bvector == 0) @@ -698,12 +697,12 @@ STATIC MYBOOL serious_facterror(lprec *lp, LPSREAL *bvector, int maxcols, LPSREA } /* Computation of reduced costs */ -STATIC void update_reducedcosts(lprec *lp, MYBOOL isdual, int leave_nr, int enter_nr, LPSREAL *prow, LPSREAL *drow) +STATIC void update_reducedcosts(lprec *lp, MYBOOL isdual, int leave_nr, int enter_nr, REAL *prow, REAL *drow) { /* "Fast" update of the dual reduced cost vector; note that it must be called after the pivot operation and only applies to a major "true" iteration */ int i; - LPSREAL hold; + REAL hold; if(isdual) { hold = -drow[enter_nr]/prow[enter_nr]; @@ -723,11 +722,11 @@ STATIC void update_reducedcosts(lprec *lp, MYBOOL isdual, int leave_nr, int ente STATIC void compute_reducedcosts(lprec *lp, MYBOOL isdual, int row_nr, int *coltarget, MYBOOL dosolve, - LPSREAL *prow, int *nzprow, - LPSREAL *drow, int *nzdrow, + REAL *prow, int *nzprow, + REAL *drow, int *nzdrow, int roundmode) { - LPSREAL epsvalue = lp->epsvalue; /* Any larger value can produce a suboptimal result */ + REAL epsvalue = lp->epsvalue; /* Any larger value can produce a suboptimal result */ roundmode |= MAT_ROUNDRC; if(isdual) { @@ -737,7 +736,7 @@ STATIC void compute_reducedcosts(lprec *lp, MYBOOL isdual, int row_nr, int *colt roundmode); } else { - LPSREAL *bVector; + REAL *bVector; #if 1 /* Legacy mode, that is possibly a little faster */ if((lp->multivars == NULL) && (lp->P1extraDim == 0)) @@ -765,7 +764,7 @@ STATIC void compute_reducedcosts(lprec *lp, MYBOOL isdual, int row_nr, int *colt Both of these cases are associated with numerical stalling, which we could argue should be detected and handled by the stalling monitor routine. */ -STATIC MYBOOL verify_stability(lprec *lp, MYBOOL isprimal, LPSREAL xfeas, LPSREAL sfeas, int nfeas) +STATIC MYBOOL verify_stability(lprec *lp, MYBOOL isprimal, REAL xfeas, REAL sfeas, int nfeas) { MYBOOL testOK = TRUE; return( testOK ); @@ -782,10 +781,10 @@ STATIC MYBOOL verify_stability(lprec *lp, MYBOOL isprimal, LPSREAL xfeas, LPSREA xfeas = fabs(xfeas); /* Maximum (positive) infeasibility */ /* if(xfeas < lp->epspivot) { */ if(xfeas < lp->epssolution) { - LPSREAL f; + REAL f; sfeas = fabs(sfeas); /* Make sum of infeasibilities positive */ xfeas = (sfeas-xfeas)/nfeas; /* Average "residual" feasibility */ - f = 1 + log10((LPSREAL) nfeas); /* Some numerical complexity scalar */ + f = 1 + log10((REAL) nfeas); /* Some numerical complexity scalar */ /* Numerical errors can interact to cause non-convergence, and the idea is to relax the tolerance to account for this and only marginally weakening the (user-specified) tolerance. */ @@ -798,12 +797,12 @@ STATIC MYBOOL verify_stability(lprec *lp, MYBOOL isprimal, LPSREAL xfeas, LPSREA /* Find an entering column for the case that the specified basic variable is fixed or zero - typically used for artificial variable elimination */ -STATIC int find_rowReplacement(lprec *lp, int rownr, LPSREAL *prow, int *nzprow) +STATIC int find_rowReplacement(lprec *lp, int rownr, REAL *prow, int *nzprow) /* The logic in this section generally follows Chvatal: Linear Programming, p. 130 Basically, the function is a specialized coldual(). */ { int i, bestindex; - LPSREAL bestvalue; + REAL bestvalue; /* Solve for "local reduced cost" */ set_action(&lp->piv_strategy, PRICE_FORCEFULL); @@ -832,10 +831,10 @@ STATIC int find_rowReplacement(lprec *lp, int rownr, LPSREAL *prow, int *nzprow) } /* Find the primal simplex entering non-basic column variable */ -STATIC int colprim(lprec *lp, LPSREAL *drow, int *nzdrow, MYBOOL skipupdate, int partialloop, int *candidatecount, MYBOOL updateinfeas, LPSREAL *xviol) +STATIC int colprim(lprec *lp, REAL *drow, int *nzdrow, MYBOOL skipupdate, int partialloop, int *candidatecount, MYBOOL updateinfeas, REAL *xviol) { int i, ix, iy, iz, ninfeas, nloop = 0; - LPSREAL f, sinfeas, xinfeas, epsvalue = lp->epsdual; + REAL f, sinfeas, xinfeas, epsvalue = lp->epsdual; pricerec current, candidate; MYBOOL collectMP = FALSE; int *coltarget = NULL; @@ -959,14 +958,16 @@ STATIC int colprim(lprec *lp, LPSREAL *drow, int *nzdrow, MYBOOL skipupdate, int } /* colprim */ /* Find the primal simplex leaving basic column variable */ -STATIC int rowprim(lprec *lp, int colnr, LREAL *theta, LPSREAL *pcol, int *nzpcol, MYBOOL forceoutEQ, LPSREAL *xviol) +STATIC int rowprim(lprec *lp, int colnr, LREAL *theta, REAL *pcol, int *nzpcol, MYBOOL forceoutEQ, REAL *xviol) { int i, ii, iy, iz, Hpass, k, *nzlist; LREAL f, savef; - LPSREAL Heps, Htheta, Hlimit, epsvalue, epspivot, p = 0.0; + REAL Heps, Htheta, Hlimit, epsvalue, epspivot, p; pricerec current, candidate; MYBOOL isupper = !lp->is_lower[colnr], HarrisTwoPass = FALSE; + p = 0.0; + /* Update local value of pivot setting */ lp->_piv_rule_ = get_piv_rule(lp); if(nzpcol == NULL) @@ -1167,11 +1168,11 @@ STATIC int rowprim(lprec *lp, int colnr, LREAL *theta, LPSREAL *pcol, int *nzpco /* Find the dual simplex leaving basic variable */ -STATIC int rowdual(lprec *lp, LPSREAL *rhvec, MYBOOL forceoutEQ, MYBOOL updateinfeas, LPSREAL *xviol) +STATIC int rowdual(lprec *lp, REAL *rhvec, MYBOOL forceoutEQ, MYBOOL updateinfeas, REAL *xviol) { int k, i, iy, iz, ii, ninfeas; - register LPSREAL rh; - LPSREAL up, lo = 0, + register REAL rh; + REAL up, lo = 0, epsvalue, sinfeas, xinfeas; pricerec current, candidate; MYBOOL collectMP = FALSE; @@ -1287,11 +1288,11 @@ STATIC int rowdual(lprec *lp, LPSREAL *rhvec, MYBOOL forceoutEQ, MYBOOL updatein } /* rowdual */ -STATIC void longdual_testset(lprec *lp, int which, int rownr, LPSREAL *prow, int *nzprow, - LPSREAL *drow, int *nzdrow) +STATIC void longdual_testset(lprec *lp, int which, int rownr, REAL *prow, int *nzprow, + REAL *drow, int *nzdrow) { int i,j; - LPSREAL F = lp->infinite; + REAL F = lp->infinite; if(which == 0) { /* Maros Example-1 - raw data */ j = 1; i = lp->rows+j; lp->upbo[i] = 0; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = 2; drow[i] = -1; j = 2; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -2; drow[i] = 2; @@ -1342,26 +1343,24 @@ STATIC void longdual_testset(lprec *lp, int which, int rownr, LPSREAL *prow, int /* Find the dual simplex entering non-basic variable */ -STATIC int coldual(lprec *lp, int row_nr, LPSREAL *prow, int *nzprow, - LPSREAL *drow, int *nzdrow, +STATIC int coldual(lprec *lp, int row_nr, REAL *prow, int *nzprow, + REAL *drow, int *nzdrow, MYBOOL dualphase1, MYBOOL skipupdate, - int *candidatecount, LPSREAL *xviol) + int *candidatecount, REAL *xviol) { int i, iy, iz, ix, k, nbound; LREAL w, g, quot; - LPSREAL viol, p, epspivot = lp->epspivot; + REAL viol, p, epspivot = lp->epspivot; #ifdef MachinePrecRoundRHS - LPSREAL epsvalue = lp->epsmachine; + REAL epsvalue = lp->epsmachine; #else - LPSREAL epsvalue = lp->epsvalue; + REAL epsvalue = lp->epsvalue; #endif pricerec current, candidate; MYBOOL isbatch = FALSE, /* Requires that lp->longsteps->size > lp->sum */ dolongsteps = (MYBOOL) (lp->longsteps != NULL); /* Initialize */ - if(xviol != NULL) - *xviol = lp->infinite; if(dolongsteps && !dualphase1) dolongsteps = AUTOMATIC; /* Sets Phase1 = TRUE, Phase2 = AUTOMATIC */ current.theta = lp->infinite; @@ -1442,9 +1441,7 @@ STATIC int coldual(lprec *lp, int row_nr, LPSREAL *prow, int *nzprow, for(ix = 1; ix <= iy; ix++) { i = nzprow[ix]; w = prow[i] * g; /* Change sign if upper bound of the leaving variable is violated */ - /* Change sign if the non-basic variable is currently upper-bounded */ - /* w *= 2*lp->is_lower[i] - 1; */ /* fails on AIX!!! */ - w = my_chsign(!lp->is_lower[i], w); + w *= 2*lp->is_lower[i] - 1; /* Change sign if the non-basic variable is currently upper-bounded */ /* Check if the candidate is worth using for anything */ if(w < -epsvalue) { @@ -1540,7 +1537,7 @@ STATIC int coldual(lprec *lp, int row_nr, LPSREAL *prow, int *nzprow, } /* coldual */ -LPSREAL normalizeEdge(lprec *lp, int item, LPSREAL edge, MYBOOL isdual) +INLINE REAL normalizeEdge(lprec *lp, int item, REAL edge, MYBOOL isdual) { #if 1 /* Don't use the pricer "close to home", since this can possibly @@ -1554,18 +1551,18 @@ LPSREAL normalizeEdge(lprec *lp, int item, LPSREAL edge, MYBOOL isdual) } - /* Support routines for block detection and partial pricing */ STATIC int partial_findBlocks(lprec *lp, MYBOOL autodefine, MYBOOL isrow) { int i, jj, n, nb, ne, items; - LPSREAL hold, biggest, *sum = NULL; + REAL hold, biggest, *sum = NULL; MATrec *mat = lp->matA; + partialrec *blockdata; if(!mat_validate(mat)) return( 1 ); - //@FS: unused// blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); items = IF(isrow, lp->rows, lp->columns); allocREAL(lp, &sum, items+1, FALSE); @@ -1644,8 +1641,6 @@ STATIC int partial_findBlocks(lprec *lp, MYBOOL autodefine, MYBOOL isrow) return( n ); } - - STATIC int partial_blockStart(lprec *lp, MYBOOL isrow) { partialrec *blockdata; @@ -1838,7 +1833,7 @@ STATIC int multi_restart(multirec *multi) return( n ); } -STATIC void multi_valueInit(multirec *multi, LPSREAL step_base, LPSREAL obj_base) +STATIC void multi_valueInit(multirec *multi, REAL step_base, REAL obj_base) { multi->step_base = multi->step_last = step_base; multi->obj_base = multi->obj_last = obj_base; @@ -1849,7 +1844,7 @@ STATIC void multi_valueInit(multirec *multi, LPSREAL step_base, LPSREAL obj_base #endif } -STATIC LPSREAL *multi_valueList(multirec *multi) +STATIC REAL *multi_valueList(multirec *multi) { return(multi->valueList); } @@ -1873,7 +1868,7 @@ STATIC int multi_getvar(multirec *multi, int item) STATIC MYBOOL multi_recompute(multirec *multi, int index, MYBOOL isphase2, MYBOOL fullupdate) { int i, n; - LPSREAL lB, uB, Alpha, this_theta, prev_theta; + REAL lB, uB, Alpha, this_theta, prev_theta; lprec *lp = multi->lp; pricerec *thisprice; @@ -1946,7 +1941,7 @@ STATIC MYBOOL multi_recompute(multirec *multi, int index, MYBOOL isphase2, MYBOO n = index; while(n < multi->used) { i = ++multi->freeList[0]; - multi->freeList[i] = (int) (((pricerec *) multi->sortedList[n].pvoidreal.ptr) - multi->items); + multi->freeList[i] = ((pricerec *) multi->sortedList[n].pvoidreal.ptr) - multi->items; n++; } multi->used = index; @@ -1987,11 +1982,13 @@ STATIC MYBOOL multi_removevar(multirec *multi, int varnr) STATIC int multi_enteringvar(multirec *multi, pricerec *current, int priority) { lprec *lp = multi->lp; - int i = 0, bestindex, colnr; - LPSREAL bound, score, bestscore = -lp->infinite; - LPSREAL b1, b2, b3; + int i, bestindex, colnr; + REAL bound, score, bestscore = -lp->infinite; + REAL b1, b2, b3; pricerec *candidate, *bestcand; + i = 0; + /* Check that we have a candidate */ multi->active = bestindex = 0; if((multi == NULL) || (multi->used == 0)) @@ -2032,7 +2029,7 @@ STATIC int multi_enteringvar(multirec *multi, pricerec *current, int priority) score = fabs(candidate->pivot) / multi->maxpivot; score = pow(1.0 + score , b1) * pow(1.0 + log(bound / multi->maxbound + 1), b2) * - pow(1.0 + (LPSREAL) i / multi->used , b3); + pow(1.0 + (REAL) i / multi->used , b3); if(score > bestscore) { bestscore = score; bestindex = i; @@ -2077,7 +2074,7 @@ STATIC int multi_enteringvar(multirec *multi, pricerec *current, int priority) return( multi->active ); } -STATIC LPSREAL multi_enteringtheta(multirec *multi) +STATIC REAL multi_enteringtheta(multirec *multi) { return( multi->step_base ); } diff --git a/src/lpsolve/headers/run_headers/lp_price.h b/src/lpSolve/src/lp_price.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_price.h rename to src/lpSolve/src/lp_price.h diff --git a/src/lpsolve/build/lp_solve/lp_pricePSE.c b/src/lpSolve/src/lp_pricePSE.c similarity index 93% rename from src/lpsolve/build/lp_solve/lp_pricePSE.c rename to src/lpSolve/src/lp_pricePSE.c index 88a6c2d2..20026fd3 100644 --- a/src/lpsolve/build/lp_solve/lp_pricePSE.c +++ b/src/lpSolve/src/lp_pricePSE.c @@ -41,7 +41,7 @@ ---------------------------------------------------------------------------------- */ -MYBOOL applyPricer(lprec *lp) +INLINE MYBOOL applyPricer(lprec *lp) { int rule = get_piv_rule(lp); return( (MYBOOL) ((rule == PRICER_DEVEX) || (rule == PRICER_STEEPESTEDGE)) ); @@ -51,7 +51,7 @@ MYBOOL applyPricer(lprec *lp) STATIC void simplexPricer(lprec *lp, MYBOOL isdual) { if(lp->edgeVector != NULL) - lp->edgeVector[0] = (LPSREAL) isdual; + lp->edgeVector[0] = (REAL) isdual; } @@ -90,9 +90,9 @@ STATIC MYBOOL initPricer(lprec *lp) } -STATIC LPSREAL getPricer(lprec *lp, int item, MYBOOL isdual) +STATIC REAL getPricer(lprec *lp, int item, MYBOOL isdual) { - LPSREAL value = 1.0; + REAL value = 1.0; if(!applyPricer(lp)) return( value ); @@ -136,13 +136,16 @@ STATIC LPSREAL getPricer(lprec *lp, int item, MYBOOL isdual) STATIC MYBOOL restartPricer(lprec *lp, MYBOOL isdual) { - LPSREAL *sEdge = NULL, seNorm, hold; + REAL *sEdge = NULL, seNorm, hold; int i, j, m; MYBOOL isDEVEX, ok = applyPricer(lp); +/* Correction from V6, apparently, via Kjell Eikland and the +** lpSolve mailing list 2014-06-18 2:57 p.m. */ - if(ok && (lp->edgeVector[0] < 0) && (isdual == AUTOMATIC)) + if (ok && (lp->edgeVector[0] < 0) && (isdual == AUTOMATIC)) ok = FALSE; + if(!ok) return( ok ); @@ -227,7 +230,7 @@ STATIC MYBOOL restartPricer(lprec *lp, MYBOOL isdual) } -STATIC MYBOOL formWeights(lprec *lp, int colnr, LPSREAL *pcol, LPSREAL **w) +STATIC MYBOOL formWeights(lprec *lp, int colnr, REAL *pcol, REAL **w) /* This computes Bw = a, where B is the basis and a is a column of A */ { MYBOOL ok = allocREAL(lp, w, lp->rows+1, FALSE); @@ -242,7 +245,7 @@ STATIC MYBOOL formWeights(lprec *lp, int colnr, LPSREAL *pcol, LPSREAL **w) } /* if(pcol != NULL) { - LPSREAL cEdge, hold; + REAL cEdge, hold; int i; cEdge = 0; @@ -258,15 +261,15 @@ STATIC MYBOOL formWeights(lprec *lp, int colnr, LPSREAL *pcol, LPSREAL **w) */ return(ok); } -STATIC void freeWeights(LPSREAL *w) +STATIC void freeWeights(REAL *w) { FREE(w); } -STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSREAL *prow, int *nzprow) +STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, REAL *pcol, REAL *prow, int *nzprow) { - LPSREAL *vEdge = NULL, cEdge, hold, *newEdge, *w = NULL; + REAL *vEdge = NULL, cEdge, hold, *newEdge, *w = NULL; int i, m, n, exitcol, errlevel = DETAILED; MYBOOL forceRefresh = FALSE, isDual, isDEVEX, ok = FALSE; @@ -296,7 +299,7 @@ STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSRE /* Price norms for the dual simplex - the basic columns */ if(isDual) { - LPSREAL rw; + REAL rw; int targetcol; /* Don't need to compute cross-products with DEVEX */ @@ -316,15 +319,9 @@ STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSRE lp->bfp_ftran_normal(lp, vEdge, NULL); } - /* Update the squared steepest edge norms; first store some constants */ + /* Deal with the variable entering the basis to become a new leaving candidate */ cEdge = lp->edgeVector[exitcol]; rw = w[rownr]; - if(fabs(rw) < lp->epspivot) { - forceRefresh = TRUE; - goto Finish2; - } - - /* Deal with the variable entering the basis to become a new leaving candidate */ hold = 1 / rw; lp->edgeVector[colnr] = (hold*hold) * cEdge; @@ -374,7 +371,7 @@ STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSRE /* Price norms for the primal simplex - the non-basic columns */ else { - LPSREAL *vTemp = NULL, *vAlpha = NULL, cAlpha; + REAL *vTemp = NULL, *vAlpha = NULL, cAlpha; int *coltarget; ok = allocREAL(lp, &vTemp, m+1, TRUE) && @@ -418,10 +415,6 @@ STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSRE /* Update the squared steepest edge norms; first store some constants */ cEdge = lp->edgeVector[colnr]; cAlpha = vAlpha[colnr]; - if(fabs(cAlpha) < lp->epspivot) { - forceRefresh = TRUE; - goto Finish1; - } /* Deal with the variable leaving the basis to become a new entry candidate */ hold = 1 / cAlpha; @@ -469,13 +462,11 @@ STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSRE } } -Finish1: FREE(vAlpha); FREE(vTemp); } -Finish2: FREE(vEdge); freeWeights(w); @@ -491,7 +482,7 @@ STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSRE STATIC MYBOOL verifyPricer(lprec *lp) { - LPSREAL value; + REAL value; int i, n; MYBOOL ok = applyPricer(lp); diff --git a/src/lpsolve/headers/run_headers/lp_pricePSE.h b/src/lpSolve/src/lp_pricePSE.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_pricePSE.h rename to src/lpSolve/src/lp_pricePSE.h diff --git a/src/lpsolve/build/lp_solve/lp_report.c b/src/lpSolve/src/lp_report.c similarity index 95% rename from src/lpsolve/build/lp_solve/lp_report.c rename to src/lpSolve/src/lp_report.c index f38fa3b9..4c478228 100644 --- a/src/lpsolve/build/lp_solve/lp_report.c +++ b/src/lpSolve/src/lp_report.c @@ -25,6 +25,10 @@ #include "commonlib.h" #include "lp_report.h" +#include +#include + + #include "mmio.h" #ifdef FORTIFY @@ -46,36 +50,34 @@ char * __VACALL explain(lprec *lp, char *format, ...) va_list ap; va_start(ap, format); - vsnprintf(buff, DEF_STRBUFSIZE, format, ap); + vsnprintf(buff, DEF_STRBUFSIZE, format, ap); + allocCHAR(lp, &(lp->ex_status), (int) strlen(buff), AUTOMATIC); + strcpy(lp->ex_status, buff); va_end(ap); - allocCHAR(lp, &(lp->ex_status), (int) strlen(buff), AUTOMATIC); - strcpy(lp->ex_status, buff); return( lp->ex_status ); } void __VACALL report(lprec *lp, int level, char *format, ...) { - char buff[DEF_STRBUFSIZE+1]; - va_list ap; + static char buff[DEF_STRBUFSIZE+1]; + static va_list ap; if(lp == NULL) { va_start(ap, format); - vfprintf(stderr, format, ap); + REvprintf( format, ap); va_end(ap); } else if(level <= lp->verbose) { + va_start(ap, format); if(lp->writelog != NULL) { - va_start(ap, format); vsnprintf(buff, DEF_STRBUFSIZE, format, ap); - va_end(ap); lp->writelog(lp, lp->loghandle, buff); } if(lp->outstream != NULL) { - va_start(ap, format); vfprintf(lp->outstream, format, ap); - va_end(ap); - if(lp->outstream != stdout) +/* if(lp->outstream != stdout) */ fflush(lp->outstream); } + va_end(ap); } #ifdef xParanoia if(level == CRITICAL) @@ -102,21 +104,19 @@ STATIC void debug_print(lprec *lp, char *format, ...) if(lp->bb_trace) { print_indent(lp); + va_start(ap, format); if (lp == NULL) { - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - fputc('\n', stderr); + REvprintf( format, ap); + /* fputc('\n', stderr); */ } else if(lp->debuginfo != NULL) { char buff[DEF_STRBUFSIZE+1]; - va_start(ap, format); vsnprintf(buff, DEF_STRBUFSIZE, format, ap); - va_end(ap); lp->debuginfo(lp, lp->loghandle, buff); } + va_end(ap); } } /* debug_print */ @@ -133,7 +133,7 @@ STATIC void debug_print_solution(lprec *lp) } } /* debug_print_solution */ -STATIC void debug_print_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo) +STATIC void debug_print_bounds(lprec *lp, REAL *upbo, REAL *lowbo) { int i; @@ -295,7 +295,8 @@ void blockWriteBMAT(FILE *output, const char *label, lprec* lp, int first, int l principally for run difference and debugging purposes */ MYBOOL REPORT_debugdump(lprec *lp, char *filename, MYBOOL livedata) { - FILE *output = stdout; + /* FILE *output = stdout; */ + FILE *output; MYBOOL ok; ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); @@ -356,17 +357,15 @@ void REPORT_objective(lprec *lp) { if(lp->outstream == NULL) return; - if(fabs(lp->best_solution[0]) < 1e-5) - fprintf(lp->outstream, "\nValue of objective function: %g\n", (double)lp->best_solution[0]); - else - fprintf(lp->outstream, "\nValue of objective function: %.8f\n", (double)lp->best_solution[0]); + fprintf(lp->outstream, "\nValue of objective function: %g\n", + (double)lp->best_solution[0]); fflush(lp->outstream); } void REPORT_solution(lprec *lp, int columns) { int i, j, n; - LPSREAL value; + REAL value; presolveundorec *psundo = lp->presolve_undo; MYBOOL NZonly = (MYBOOL) ((lp->print_sol & AUTOMATIC) > 0); @@ -396,7 +395,7 @@ void REPORT_solution(lprec *lp, int columns) void REPORT_constraints(lprec *lp, int columns) { int i, n; - LPSREAL value; + REAL value; MYBOOL NZonly = (MYBOOL) ((lp->print_sol & AUTOMATIC) > 0); if(lp->outstream == NULL) @@ -425,7 +424,7 @@ void REPORT_constraints(lprec *lp, int columns) void REPORT_duals(lprec *lp) { int i; - LPSREAL *duals, *dualsfrom, *dualstill, *objfrom, *objtill, *objfromvalue; + REAL *duals, *dualsfrom, *dualstill, *objfrom, *objtill, *objfromvalue; MYBOOL ret; if(lp->outstream == NULL) @@ -457,8 +456,8 @@ void REPORT_duals(lprec *lp) void REPORT_extended(lprec *lp) { int i, j; - LPSREAL hold; - LPSREAL *duals, *dualsfrom, *dualstill, *objfrom, *objtill; + REAL hold; + REAL *duals, *dualsfrom, *dualstill, *objfrom, *objtill; MYBOOL ret; ret = get_ptr_sensitivity_obj(lp, &objfrom, &objtill); @@ -515,6 +514,11 @@ void REPORT_lp(lprec *lp) if(lp->outstream == NULL) return; + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "REPORT_lp: Cannot print lp while in row entry mode.\n"); + return; + } + fprintf(lp->outstream, "Model name: %s\n", get_lp_name(lp)); fprintf(lp->outstream, " "); @@ -598,7 +602,7 @@ void REPORT_scales(lprec *lp) MYBOOL REPORT_tableau(lprec *lp) { int j, row_nr, *coltarget; - LPSREAL *prow = NULL; + REAL *prow = NULL; FILE *stream = lp->outstream; if(lp->outstream == NULL) @@ -702,9 +706,9 @@ MYBOOL REPORT_mat_mmsave(lprec *lp, char *filename, int *colndx, MYBOOL includeO int n, m, nz, i, j, k, kk; MATrec *mat = lp->matA; MM_typecode matcode; - FILE *output = stdout; + FILE *output; /* = stdout; */ MYBOOL ok; - LPSREAL *acol = NULL; + REAL *acol = NULL; int *nzlist = NULL; /* Open file */ diff --git a/src/lpsolve/headers/run_headers/lp_report.h b/src/lpSolve/src/lp_report.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_report.h rename to src/lpSolve/src/lp_report.h diff --git a/src/lpSolve/src/lp_rlp.c b/src/lpSolve/src/lp_rlp.c new file mode 100644 index 00000000..8682aa3e --- /dev/null +++ b/src/lpSolve/src/lp_rlp.c @@ -0,0 +1,1477 @@ + +/* A Bison parser, made from lp_rlp.y + by GNU Bison version 1.28 */ + +#define YYBISON 1 /* Identify Bison output. */ + +#define VAR 257 +#define CONS 258 +#define INTCONS 259 +#define VARIABLECOLON 260 +#define INF 261 +#define SEC_INT 262 +#define SEC_SEC 263 +#define SEC_SOS 264 +#define SOSDESCR 265 +#define SIGN 266 +#define AR_M_OP 267 +#define RE_OPLE 268 +#define RE_OPGE 269 +#define END_C 270 +#define COMMA 271 +#define COLON 272 +#define MINIMISE 273 +#define MAXIMISE 274 +#define UNDEFINED 275 + + +#include +#include + +#include "lpkit.h" +#include "yacc_read.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +static int HadVar0, HadVar1, HadVar2, HasAR_M_OP, do_add_row, Had_lineair_sum0, HadSign; +static char *Last_var = NULL, *Last_var0 = NULL; +static REAL f, f0, f1; +static int x; +static int state, state0; +static int Sign; +static int isign, isign0; /* internal_sign variable to make sure nothing goes wrong */ + /* with lookahead */ +static int make_neg; /* is true after the relational operator is seen in order */ + /* to remember if lin_term stands before or after re_op */ +static int Within_int_decl = FALSE; /* TRUE when we are within an int declaration */ +static int Within_sec_decl = FALSE; /* TRUE when we are within an sec declaration */ +static int Within_sos_decl = FALSE; /* TRUE when we are within an sos declaration */ +static int Within_sos_decl1; +static short SOStype0; /* SOS type */ +static short SOStype; /* SOS type */ +static int SOSNr; +static int SOSweight = 0; /* SOS weight */ + +static int HadConstraint; +static int HadVar; +static int Had_lineair_sum; + +extern FILE *lp_yyin; + +#define YY_FATAL_ERROR lex_fatal_error + +/* let's please C++ users */ +#ifdef __cplusplus +extern "C" { +#endif + +static int wrap(void) +{ + return(1); +} + +static int __WINAPI lp_input_lp_yyin(void *fpin, char *buf, int max_size) +{ + int result; + + if ( (result = fread( (char*)buf, sizeof(char), max_size, (FILE *) fpin)) < 0) + YY_FATAL_ERROR( "read() in flex scanner failed"); + + return(result); +} + +static read_modeldata_func *lp_input; + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result = lp_input((void *) lp_yyin, buf, max_size); + +#ifdef __cplusplus +}; +#endif + +#define lp_yywrap wrap +#define lp_yyerror read_error + +#include "lp_rlp.h" + +#ifndef YYSTYPE +#define YYSTYPE int +#endif +#include + +#ifndef __cplusplus +#ifndef __STDC__ +#define const +#endif +#endif + + + +#define YYFINAL 120 +#define YYFLAG -32768 +#define YYNTBASE 22 + +#define YYTRANSLATE(x) ((unsigned)(x) <= 275 ? lp_yytranslate[x] : 77) + +static const char lp_yytranslate[] = { 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21 +}; + +#if YYDEBUG != 0 +static const short lp_yyprhs[] = { 0, + 0, 1, 2, 7, 10, 13, 15, 18, 20, 22, + 24, 26, 28, 31, 33, 34, 38, 39, 40, 41, + 50, 52, 53, 54, 60, 62, 64, 66, 67, 71, + 72, 75, 77, 80, 83, 85, 86, 90, 92, 94, + 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, + 117, 119, 122, 124, 126, 128, 129, 133, 134, 140, + 142, 145, 147, 148, 152, 154, 155, 160, 162, 165, + 167, 169, 171, 175, 177, 179, 181, 182, 184, 186, + 189, 193, 196, 199, 202 +}; + +static const short lp_yyrhs[] = { -1, + 0, 24, 25, 28, 54, 0, 20, 26, 0, 19, + 26, 0, 26, 0, 27, 16, 0, 22, 0, 42, + 0, 22, 0, 29, 0, 30, 0, 29, 30, 0, + 32, 0, 0, 6, 31, 32, 0, 0, 0, 0, + 39, 33, 48, 34, 40, 35, 36, 16, 0, 22, + 0, 0, 0, 48, 37, 49, 38, 53, 0, 22, + 0, 40, 0, 42, 0, 0, 7, 41, 53, 0, + 0, 43, 44, 0, 45, 0, 44, 45, 0, 51, + 46, 0, 50, 0, 0, 52, 47, 3, 0, 14, + 0, 15, 0, 51, 50, 0, 7, 0, 5, 0, + 4, 0, 22, 0, 12, 0, 22, 0, 13, 0, + 22, 0, 22, 0, 55, 0, 57, 0, 55, 57, + 0, 8, 0, 9, 0, 10, 0, 0, 56, 58, + 61, 0, 0, 60, 62, 67, 64, 16, 0, 59, + 0, 61, 59, 0, 22, 0, 0, 11, 63, 73, + 0, 22, 0, 0, 14, 5, 65, 66, 0, 22, + 0, 18, 5, 0, 22, 0, 68, 0, 74, 0, + 68, 69, 74, 0, 22, 0, 17, 0, 22, 0, + 0, 22, 0, 22, 0, 3, 70, 0, 6, 71, + 75, 0, 50, 72, 0, 73, 76, 0, 3, 70, + 0, 6, 71, 50, 72, 0 +}; + +#endif + +#if YYDEBUG != 0 +static const short lp_yyrline[] = { 0, + 86, 89, 98, 112, 116, 120, 123, 134, 135, 165, + 166, 169, 170, 174, 175, 182, 184, 190, 197, 221, + 237, 243, 254, 258, 279, 289, 295, 296, 301, 303, + 308, 322, 323, 327, 363, 367, 375, 380, 380, 383, + 385, 396, 396, 399, 404, 411, 415, 421, 438, 440, + 443, 444, 447, 447, 447, 450, 456, 458, 468, 480, + 482, 485, 486, 492, 494, 501, 515, 517, 521, 528, + 529, 532, 533, 538, 539, 542, 567, 586, 608, 622, + 625, 630, 632, 636, 639 +}; +#endif + + +#if YYDEBUG != 0 || defined (YYERROR_VERBOSE) + +static const char * const lp_yytname[] = { "$","error","$undefined.","VAR","CONS", +"INTCONS","VARIABLECOLON","INF","SEC_INT","SEC_SEC","SEC_SOS","SOSDESCR","SIGN", +"AR_M_OP","RE_OPLE","RE_OPGE","END_C","COMMA","COLON","MINIMISE","MAXIMISE", +"UNDEFINED","EMPTY","inputfile","@1","objective_function","real_of","lineair_sum", +"constraints","x_constraints","constraint","@2","real_constraint","@3","@4", +"@5","optionalrange","@6","@7","x_lineair_sum2","x_lineair_sum3","@8","x_lineair_sum", +"@9","x_lineair_sum1","x_lineair_term","x_lineair_term1","@10","RE_OP","cons_term", +"REALCONS","x_SIGN","optional_AR_M_OP","RHS_STORE","int_sec_sos_declarations", +"real_int_sec_sos_decls","SEC_INT_SEC_SOS","int_sec_sos_declaration","@11","xx_int_sec_sos_declaration", +"@12","x_int_sec_sos_declaration","optionalsos","@13","optionalsostype","@14", +"optionalSOSweight","vars","x_vars","optionalcomma","variable","variablecolon", +"sosweight","sosdescr","onevarwithoptionalweight","INTCONSorVARIABLE","x_onevarwithoptionalweight", NULL +}; +#endif + +static const short lp_yyr1[] = { 0, + 22, 24, 23, 25, 25, 25, 26, 27, 27, 28, + 28, 29, 29, 30, 31, 30, 33, 34, 35, 32, + 36, 37, 38, 36, 39, 39, 40, 41, 40, 43, + 42, 44, 44, 45, 46, 47, 46, 48, 48, 49, + 49, 50, 50, 51, 51, 52, 52, 53, 54, 54, + 55, 55, 56, 56, 56, 58, 57, 60, 59, 61, + 61, 62, 63, 62, 64, 65, 64, 66, 66, 67, + 67, 68, 68, 69, 69, 70, 71, 72, 73, 74, + 74, 75, 75, 76, 76 +}; + +static const short lp_yyr2[] = { 0, + 0, 0, 4, 2, 2, 1, 2, 1, 1, 1, + 1, 1, 2, 1, 0, 3, 0, 0, 0, 8, + 1, 0, 0, 5, 1, 1, 1, 0, 3, 0, + 2, 1, 2, 2, 1, 0, 3, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 0, 3, 0, 5, 1, + 2, 1, 0, 3, 1, 0, 4, 1, 2, 1, + 1, 1, 3, 1, 1, 1, 0, 1, 1, 2, + 3, 2, 2, 2, 4 +}; + +static const short lp_yydefact[] = { 2, + 30, 30, 30, 8, 1, 6, 0, 9, 1, 5, + 4, 15, 28, 10, 1, 30, 12, 14, 17, 26, + 27, 7, 45, 44, 1, 32, 1, 30, 1, 53, + 54, 55, 49, 3, 50, 56, 51, 25, 13, 0, + 33, 43, 42, 47, 46, 34, 35, 36, 16, 48, + 29, 52, 58, 38, 39, 18, 0, 60, 1, 58, + 30, 37, 63, 62, 1, 61, 19, 1, 1, 77, + 70, 1, 1, 72, 1, 79, 64, 76, 80, 1, + 0, 65, 0, 75, 74, 0, 21, 0, 22, 1, + 0, 81, 66, 59, 73, 20, 1, 78, 82, 1, + 77, 83, 1, 41, 23, 0, 84, 0, 0, 68, + 67, 1, 40, 1, 69, 24, 85, 0, 0, 0 +}; + +static const short lp_yydefgoto[] = { 4, + 118, 1, 5, 6, 7, 15, 16, 17, 28, 18, + 40, 61, 75, 88, 97, 112, 19, 20, 29, 21, + 9, 25, 26, 46, 57, 56, 105, 47, 27, 48, + 51, 34, 35, 36, 37, 53, 58, 59, 60, 65, + 68, 83, 103, 111, 72, 73, 86, 79, 80, 99, + 77, 74, 92, 102 +}; + +static const short lp_yypact[] = {-32768, + 42, 10, 10,-32768, 2,-32768, 14,-32768, 32,-32768, +-32768,-32768,-32768, -12, 64, 25,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768, 41,-32768, 8, 34,-32768,-32768, +-32768,-32768,-32768,-32768, 64,-32768,-32768,-32768,-32768, 3, +-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768,-32768, 56,-32768, 54, 28, + 73,-32768,-32768,-32768, 13,-32768,-32768,-32768,-32768,-32768, +-32768, 57, 29,-32768, 3,-32768,-32768,-32768,-32768, 72, + 77,-32768, 65,-32768,-32768, 13,-32768, 67,-32768,-32768, + 63,-32768,-32768,-32768,-32768,-32768, 35,-32768,-32768,-32768, +-32768,-32768, 66,-32768,-32768, 72,-32768, 72, 81,-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 87, 88,-32768 +}; + +static const short lp_yypgoto[] = { -5, +-32768,-32768,-32768, 76,-32768,-32768,-32768, 74,-32768, 61, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 30,-32768, 49, +-32768,-32768, 68,-32768,-32768, 19,-32768, -79, -1,-32768, + -15,-32768,-32768,-32768, 69,-32768, 39,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 0, 1, -13, + 23, 20,-32768,-32768 +}; + + +#define YYLAST 109 + + +static const short lp_yytable[] = { 14, + 90, -25, -25, 24, -30, -30, -30, 12, 13, 33, + 38, 42, 43, -30, -30, 69, 54, 55, 70, 24, + 44, 45, 38, 50, -11, -1, 113, -57, 114, 22, + 12, 13, -11, -11, -11, -57, -57, -57, -1, -1, + 13, 104, -71, 23, -71, 84, 23, -1, -1, 8, + 8, 8, 23, 64, -31, -31, -31, -1, 62, 71, + 2, 3, 76, 78, 63, 100, 82, 85, 101, 87, + 81, 30, 31, 32, 76, 42, 43, 10, 11, 13, + 94, 93, 96, 109, 98, 115, 119, 120, 49, 39, + 67, 24, 41, 89, 78, 106, 116, 110, 66, 107, + 117, 108, 91, 52, 0, 95, 50, 0, 98 +}; + +static const short lp_yycheck[] = { 5, + 80, 14, 15, 9, 3, 4, 5, 6, 7, 15, + 16, 4, 5, 12, 13, 3, 14, 15, 6, 25, + 13, 27, 28, 29, 0, 16, 106, 0, 108, 16, + 6, 7, 8, 9, 10, 8, 9, 10, 14, 15, + 7, 7, 14, 12, 16, 17, 12, 14, 15, 1, + 2, 3, 12, 59, 14, 15, 16, 16, 3, 65, + 19, 20, 68, 69, 11, 3, 72, 73, 6, 75, + 14, 8, 9, 10, 80, 4, 5, 2, 3, 7, + 16, 5, 16, 18, 90, 5, 0, 0, 28, 16, + 61, 97, 25, 75, 100, 97, 112, 103, 60, 100, + 114, 101, 80, 35, -1, 86, 112, -1, 114 +}; +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ + +/* This file comes from bison-1.28. */ + +/* Skeleton output parser for bison, + Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser + when the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +#ifndef YYSTACK_USE_ALLOCA +#ifdef alloca +#define YYSTACK_USE_ALLOCA +#else /* alloca not defined */ +#ifdef __GNUC__ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386)) +#define YYSTACK_USE_ALLOCA +#include +#else /* not sparc */ +/* We think this test detects Watcom and Microsoft C. */ +/* This used to test MSDOS, but that is a bad idea + since that symbol is in the user namespace. */ +#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__) +#if 0 /* No need for malloc.h, which pollutes the namespace; + instead, just don't use alloca. */ +#include +#endif +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +/* I don't know what this was needed for, but it pollutes the namespace. + So I turned it off. rms, 2 May 1997. */ +/* #include */ + #pragma alloca +#define YYSTACK_USE_ALLOCA +#else /* not MSDOS, or __TURBOC__, or _AIX */ +#if 0 +#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up, + and on HPUX 10. Eventually we can turn this on. */ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#endif /* __hpux */ +#endif +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc */ +#endif /* not GNU C */ +#endif /* alloca not defined */ +#endif /* YYSTACK_USE_ALLOCA not defined */ + +#ifdef YYSTACK_USE_ALLOCA +#define YYSTACK_ALLOC alloca +#else +#define YYSTACK_ALLOC malloc +#endif + +/* Note: there must be only one dollar sign in this file. + It is replaced by the list of actions, each action + as one case of the switch. */ + +#define lp_yyerrok (lp_yyerrstatus = 0) +#define lp_yyclearin (lp_yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT goto lp_yyacceptlab +#define YYABORT goto lp_yyabortlab +#define YYERROR goto lp_yyerrlab1 +/* Like YYERROR except do call lp_yyerror. + This remains here temporarily to ease the + transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto lp_yyerrlab +#define YYRECOVERING() (!!lp_yyerrstatus) +#define YYBACKUP(token, value) \ +do \ + if (lp_yychar == YYEMPTY && lp_yylen == 1) \ + { lp_yychar = (token), lp_yylval = (value); \ + lp_yychar1 = YYTRANSLATE (lp_yychar); \ + YYPOPSTACK; \ + goto lp_yybackup; \ + } \ + else \ + { lp_yyerror ("syntax error: cannot back up"); YYERROR; } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +#ifndef YYPURE +#define YYLEX lp_yylex() +#endif + +#ifdef YYPURE +#ifdef YYLSP_NEEDED +#ifdef YYLEX_PARAM +#define YYLEX lp_yylex(&lp_yylval, &lp_yylloc, YYLEX_PARAM) +#else +#define YYLEX lp_yylex(&lp_yylval, &lp_yylloc) +#endif +#else /* not YYLSP_NEEDED */ +#ifdef YYLEX_PARAM +#define YYLEX lp_yylex(&lp_yylval, YYLEX_PARAM) +#else +#define YYLEX lp_yylex(&lp_yylval) +#endif +#endif /* not YYLSP_NEEDED */ +#endif + +/* If nonreentrant, generate the variables here */ + +#ifndef YYPURE + +int lp_yychar; /* the lookahead symbol */ +YYSTYPE lp_yylval; /* the semantic value of the */ + /* lookahead symbol */ + +#ifdef YYLSP_NEEDED +YYLTYPE lp_yylloc; /* location data for the lookahead */ + /* symbol */ +#endif + +int lp_yynerrs; /* number of parse errors so far */ +#endif /* not YYPURE */ + +#if YYDEBUG != 0 +int lp_yydebug; /* nonzero means print parse trace */ +/* Since this is uninitialized, it does not stop multiple parsers + from coexisting. */ +#endif + +/* YYINITDEPTH indicates the initial size of the parser's stacks */ + +#ifndef YYINITDEPTH +#define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH is the maximum size the stacks can grow to + (effective only if the built-in stack extension method is used). */ + +#if YYMAXDEPTH == 0 +#undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 10000 +#endif + +/* Define __lp_yy_memcpy. Note that the size argument + should be passed with type unsigned int, because that is what the non-GCC + definitions require. With GCC, __builtin_memcpy takes an arg + of type size_t, but it can handle unsigned int. */ + +#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ +#define __lp_yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT) +#else /* not GNU C or C++ */ +#ifndef __cplusplus + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__lp_yy_memcpy (to, from, count) + char *to; + char *from; + unsigned int count; +{ + register char *f = from; + register char *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#else /* __cplusplus */ + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__lp_yy_memcpy (char *to, char *from, unsigned int count) +{ + register char *t = to; + register char *f = from; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#endif +#endif + + + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into lp_yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +#ifdef __cplusplus +#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#else /* not __cplusplus */ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM +#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +#endif /* not __cplusplus */ +#else /* not YYPARSE_PARAM */ +#define YYPARSE_PARAM_ARG void +#define YYPARSE_PARAM_DECL +#endif /* not YYPARSE_PARAM */ + +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +#ifdef YYPARSE_PARAM +int lp_yyparse (void *); +#else +int lp_yyparse (void); +#endif +#endif + +int +lp_yyparse(YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + register int lp_yystate; + register int lp_yyn; + register short *lp_yyssp; + register YYSTYPE *lp_yyvsp; + int lp_yyerrstatus; /* number of tokens to shift before error messages enabled */ + int lp_yychar1 = 0; /* lookahead token as an internal (translated) token number */ + + short lp_yyssa[YYINITDEPTH]; /* the state stack */ + YYSTYPE lp_yyvsa[YYINITDEPTH]; /* the semantic value stack */ + + short *lp_yyss = lp_yyssa; /* refer to the stacks thru separate pointers */ + YYSTYPE *lp_yyvs = lp_yyvsa; /* to allow lp_yyoverflow to reallocate them elsewhere */ + +#ifdef YYLSP_NEEDED + YYLTYPE lp_yylsa[YYINITDEPTH]; /* the location stack */ + YYLTYPE *lp_yyls = lp_yylsa; + YYLTYPE *lp_yylsp; + +#define YYPOPSTACK (lp_yyvsp--, lp_yyssp--, lp_yylsp--) +#else +#define YYPOPSTACK (lp_yyvsp--, lp_yyssp--) +#endif + + int lp_yystacksize = YYINITDEPTH; + int lp_yyfree_stacks = 0; + +#ifdef YYPURE + int lp_yychar; + YYSTYPE lp_yylval; + int lp_yynerrs; +#ifdef YYLSP_NEEDED + YYLTYPE lp_yylloc; +#endif +#endif + + YYSTYPE lp_yyval = 0; /* the variable used to return */ + /* semantic values from the action */ + /* routines */ + + int lp_yylen; + + + lp_yystate = 0; + lp_yyerrstatus = 0; + lp_yynerrs = 0; + lp_yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + lp_yyssp = lp_yyss - 1; + lp_yyvsp = lp_yyvs; +#ifdef YYLSP_NEEDED + lp_yylsp = lp_yyls; +#endif + +/* Push a new state, which is found in lp_yystate . */ +/* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. */ +lp_yynewstate: + + *++lp_yyssp = lp_yystate; + + if (lp_yyssp >= lp_yyss + lp_yystacksize - 1) + { + /* Give user a chance to reallocate the stack */ + /* Use copies of these so that the &'s don't force the real ones into memory. */ + YYSTYPE *lp_yyvs1 = lp_yyvs; + short *lp_yyss1 = lp_yyss; +#ifdef YYLSP_NEEDED + YYLTYPE *lp_yyls1 = lp_yyls; +#endif + + /* Get the current used size of the three stacks, in elements. */ + int size = lp_yyssp - lp_yyss + 1; + +#ifdef lp_yyoverflow + /* Each stack pointer address is followed by the size of + the data in use in that stack, in bytes. */ +#ifdef YYLSP_NEEDED + /* This used to be a conditional around just the two extra args, + but that might be undefined if lp_yyoverflow is a macro. */ + lp_yyoverflow("parser stack overflow", + &lp_yyss1, size * sizeof (*lp_yyssp), + &lp_yyvs1, size * sizeof (*lp_yyvsp), + &lp_yyls1, size * sizeof (*lp_yylsp), + &lp_yystacksize); +#else + lp_yyoverflow("parser stack overflow", + &lp_yyss1, size * sizeof (*lp_yyssp), + &lp_yyvs1, size * sizeof (*lp_yyvsp), + &lp_yystacksize); +#endif + + lp_yyss = lp_yyss1; lp_yyvs = lp_yyvs1; +#ifdef YYLSP_NEEDED + lp_yyls = lp_yyls1; +#endif +#else /* no lp_yyoverflow */ + /* Extend the stack our own way. */ + if (lp_yystacksize >= YYMAXDEPTH) + { + lp_yyerror("parser stack overflow"); + if (lp_yyfree_stacks) + { + free (lp_yyss); + free (lp_yyvs); +#ifdef YYLSP_NEEDED + free (lp_yyls); +#endif + } + return 2; + } + lp_yystacksize *= 2; + if (lp_yystacksize > YYMAXDEPTH) + lp_yystacksize = YYMAXDEPTH; +#ifndef YYSTACK_USE_ALLOCA + lp_yyfree_stacks = 1; +#endif + lp_yyss = (short *) YYSTACK_ALLOC (lp_yystacksize * sizeof (*lp_yyssp)); + __lp_yy_memcpy ((char *)lp_yyss, (char *)lp_yyss1, + size * (unsigned int) sizeof (*lp_yyssp)); + lp_yyvs = (YYSTYPE *) YYSTACK_ALLOC (lp_yystacksize * sizeof (*lp_yyvsp)); + __lp_yy_memcpy ((char *)lp_yyvs, (char *)lp_yyvs1, + size * (unsigned int) sizeof (*lp_yyvsp)); +#ifdef YYLSP_NEEDED + lp_yyls = (YYLTYPE *) YYSTACK_ALLOC (lp_yystacksize * sizeof (*lp_yylsp)); + __lp_yy_memcpy ((char *)lp_yyls, (char *)lp_yyls1, + size * (unsigned int) sizeof (*lp_yylsp)); +#endif +#endif /* no lp_yyoverflow */ + + lp_yyssp = lp_yyss + size - 1; + lp_yyvsp = lp_yyvs + size - 1; +#ifdef YYLSP_NEEDED + lp_yylsp = lp_yyls + size - 1; +#endif + + + if (lp_yyssp >= lp_yyss + lp_yystacksize - 1) + YYABORT; + } + + + goto lp_yybackup; + lp_yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* lp_yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + lp_yyn = lp_yypact[lp_yystate]; + if (lp_yyn == YYFLAG) + goto lp_yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* lp_yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (lp_yychar == YYEMPTY) + { + lp_yychar = YYLEX; + } + + /* Convert token to internal form (in lp_yychar1) for indexing tables with */ + + if (lp_yychar <= 0) /* This means end of input. */ + { + lp_yychar1 = 0; + lp_yychar = YYEOF; /* Don't call YYLEX any more */ + + } + else + { + lp_yychar1 = YYTRANSLATE(lp_yychar); + + } + + lp_yyn += lp_yychar1; + if (lp_yyn < 0 || lp_yyn > YYLAST || lp_yycheck[lp_yyn] != lp_yychar1) + goto lp_yydefault; + + lp_yyn = lp_yytable[lp_yyn]; + + /* lp_yyn is what to do for this token type in this state. + Negative => reduce, -lp_yyn is rule number. + Positive => shift, lp_yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (lp_yyn < 0) + { + if (lp_yyn == YYFLAG) + goto lp_yyerrlab; + lp_yyn = -lp_yyn; + goto lp_yyreduce; + } + else if (lp_yyn == 0) + goto lp_yyerrlab; + + if (lp_yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + + /* Discard the token being shifted unless it is eof. */ + if (lp_yychar != YYEOF) + lp_yychar = YYEMPTY; + + *++lp_yyvsp = lp_yylval; +#ifdef YYLSP_NEEDED + *++lp_yylsp = lp_yylloc; +#endif + + /* count tokens shifted since error; after three, turn off error status. */ + if (lp_yyerrstatus) lp_yyerrstatus--; + + lp_yystate = lp_yyn; + goto lp_yynewstate; + +/* Do the default action for the current state. */ +lp_yydefault: + + lp_yyn = lp_yydefact[lp_yystate]; + if (lp_yyn == 0) + goto lp_yyerrlab; + +/* Do a reduction. lp_yyn is the number of a rule to reduce with. */ +lp_yyreduce: + lp_yylen = lp_yyr2[lp_yyn]; + if (lp_yylen > 0) + lp_yyval = lp_yyvsp[1-lp_yylen]; /* implement default value of the action */ + + switch (lp_yyn) { + +case 2: +{ + isign = 0; + make_neg = 0; + Sign = 0; + HadConstraint = FALSE; + HadVar = HadVar0 = FALSE; +; + break;} +case 4: +{ + set_obj_dir(TRUE); +; + break;} +case 5: +{ + set_obj_dir(FALSE); +; + break;} +case 7: +{ + add_row(); + HadConstraint = FALSE; + HadVar = HadVar0 = FALSE; + isign = 0; + make_neg = 0; +; + break;} +case 15: +{ + if(!add_constraint_name(Last_var)) + YYABORT; + HadConstraint = TRUE; +; + break;} +case 17: +{ + HadVar1 = HadVar0; + HadVar0 = FALSE; +; + break;} +case 18: +{ + if(!store_re_op((char *) lp_yytext, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + make_neg = 1; + f1 = 0; +; + break;} +case 19: +{ + Had_lineair_sum0 = Had_lineair_sum; + Had_lineair_sum = TRUE; + HadVar2 = HadVar0; + HadVar0 = FALSE; + do_add_row = FALSE; + if(HadConstraint && !HadVar ) { + /* it is a range */ + /* already handled */ + } + else if(!HadConstraint && HadVar) { + /* it is a bound */ + + if(!store_bounds(TRUE)) + YYABORT; + } + else { + /* it is a row restriction */ + if(HadConstraint && HadVar) + store_re_op("", HadConstraint, HadVar, Had_lineair_sum); /* makes sure that data stored in temporary buffers is treated correctly */ + do_add_row = TRUE; + } +; + break;} +case 20: +{ + if((!HadVar) && (!HadConstraint)) { + lp_yyerror("parse error"); + YYABORT; + } + if(do_add_row) + add_row(); + HadConstraint = FALSE; + HadVar = HadVar0 = FALSE; + isign = 0; + make_neg = 0; + null_tmp_store(TRUE); +; + break;} +case 21: +{ + if((!HadVar1) && (Had_lineair_sum0)) + if(!negate_constraint()) + YYABORT; +; + break;} +case 22: +{ + make_neg = 0; + isign = 0; + if(HadConstraint) + HadVar = Had_lineair_sum = FALSE; + HadVar0 = FALSE; + if(!store_re_op((char *) ((*lp_yytext == '<') ? ">" : (*lp_yytext == '>') ? "<" : lp_yytext), HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; +; + break;} +case 23: +{ + f -= f1; +; + break;} +case 24: +{ + if((HadVar1) || (!HadVar2) || (HadVar0)) { + lp_yyerror("parse error"); + YYABORT; + } + + if(HadConstraint && !HadVar ) { + /* it is a range */ + /* already handled */ + if(!negate_constraint()) + YYABORT; + } + else if(!HadConstraint && HadVar) { + /* it is a bound */ + + if(!store_bounds(TRUE)) + YYABORT; + } +; + break;} +case 25: +{ + /* to allow a range */ + /* constraint: < max */ + if(!HadConstraint) { + lp_yyerror("parse error"); + YYABORT; + } + Had_lineair_sum = FALSE; +; + break;} +case 26: +{ + Had_lineair_sum = TRUE; +; + break;} +case 28: +{ + isign = Sign; +; + break;} +case 30: +{ + state = state0 = 0; +; + break;} +case 31: +{ + if (state == 1) { + /* RHS_STORE */ + if ( (isign0 || !make_neg) + && !(isign0 && !make_neg)) /* but not both! */ + f0 = -f0; + if(make_neg) + f1 += f0; + if(!rhs_store(f0, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + } +; + break;} +case 34: +{ + if ((HadSign || state == 1) && (state0 == 1)) { + /* RHS_STORE */ + if ( (isign0 || !make_neg) + && !(isign0 && !make_neg)) /* but not both! */ + f0 = -f0; + if(make_neg) + f1 += f0; + if(!rhs_store(f0, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + } + if (state == 1) { + f0 = f; + isign0 = isign; + } + if (state == 2) { + if((HadSign) || (state0 != 1)) { + isign0 = isign; + f0 = 1.0; + } + if ( (isign0 || make_neg) + && !(isign0 && make_neg)) /* but not both! */ + f0 = -f0; + if(!var_store(Last_var, f0, HadConstraint, HadVar, Had_lineair_sum)) { + lp_yyerror("var_store failed"); + YYABORT; + } + HadConstraint |= HadVar; + HadVar = HadVar0 = TRUE; + } + state0 = state; +; + break;} +case 35: +{ + state = 1; +; + break;} +case 36: +{ + if ((HasAR_M_OP) && (state != 1)) { + lp_yyerror("parse error"); + YYABORT; + } +; + break;} +case 37: +{ + state = 2; +; + break;} +case 41: +{ + isign = Sign; +; + break;} +case 44: +{ + isign = 0; + HadSign = FALSE; +; + break;} +case 45: +{ + isign = Sign; + HadSign = TRUE; +; + break;} +case 46: +{ + HasAR_M_OP = FALSE; +; + break;} +case 47: +{ + HasAR_M_OP = TRUE; +; + break;} +case 48: +{ + if ( (isign || !make_neg) + && !(isign && !make_neg)) /* but not both! */ + f = -f; + if(!rhs_store(f, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + isign = 0; +; + break;} +case 56: +{ + Within_sos_decl1 = Within_sos_decl; +; + break;} +case 58: +{ + if((!Within_int_decl) && (!Within_sec_decl) && (!Within_sos_decl1)) { + lp_yyerror("parse error"); + YYABORT; + } + SOStype = SOStype0; + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, Within_sos_decl1 = (Within_sos_decl1 ? 1 : 0)); +; + break;} +case 59: +{ + if((Within_sos_decl1) && (SOStype == 0)) + { + lp_yyerror("Unsupported SOS type (0)"); + YYABORT; + } +; + break;} +case 63: +{ + FREE(Last_var0); + Last_var0 = strdup(Last_var); +; + break;} +case 65: +{ + if(Within_sos_decl1) { + set_sos_type(SOStype); + set_sos_weight((double) SOSweight, 1); + } +; + break;} +case 66: +{ + if((Within_sos_decl1) && (!SOStype)) + { + set_sos_type(SOStype = (short) (f + .1)); + } + else + { + lp_yyerror("SOS type not expected"); + YYABORT; + } +; + break;} +case 68: +{ + set_sos_weight((double) SOSweight, 1); +; + break;} +case 69: +{ + set_sos_weight(f, 1); +; + break;} +case 76: +{ + if(Within_sos_decl1 == 1) + { + char buf[16]; + + SOSweight++; + snprintf(buf, sizeof(buf), "SOS%d", SOSweight); + storevarandweight(buf); + + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, 2); + Within_sos_decl1 = 2; + SOSNr = 0; + } + + storevarandweight(Last_var); + + if(Within_sos_decl1 == 2) + { + SOSNr++; + set_sos_weight((double) SOSNr, 2); + } +; + break;} +case 77: +{ + if(!Within_sos_decl1) { + lp_yyerror("parse error"); + YYABORT; + } + if(Within_sos_decl1 == 1) { + FREE(Last_var0); + Last_var0 = strdup(Last_var); + } + if(Within_sos_decl1 == 2) + { + storevarandweight(Last_var); + SOSNr++; + set_sos_weight((double) SOSNr, 2); + } +; + break;} +case 78: +{ + if(Within_sos_decl1 == 1) + { + char buf[16]; + + SOSweight++; + snprintf(buf, sizeof(buf), "SOS%d", SOSweight); + storevarandweight(buf); + + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, 2); + Within_sos_decl1 = 2; + SOSNr = 0; + + storevarandweight(Last_var0); + SOSNr++; + } + + set_sos_weight(f, 2); +; + break;} +case 79: +{ /* SOS name */ + if(Within_sos_decl1 == 1) + { + storevarandweight(Last_var0); + set_sos_type(SOStype); + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, 2); + Within_sos_decl1 = 2; + SOSNr = 0; + SOSweight++; + } +; + break;} +} + /* the action file gets copied in in place of this dollarsign */ + + + lp_yyvsp -= lp_yylen; + lp_yyssp -= lp_yylen; +#ifdef YYLSP_NEEDED + lp_yylsp -= lp_yylen; +#endif + + *++lp_yyvsp = lp_yyval; + +#ifdef YYLSP_NEEDED + lp_yylsp++; + if (lp_yylen == 0) + { + lp_yylsp->first_line = lp_yylloc.first_line; + lp_yylsp->first_column = lp_yylloc.first_column; + lp_yylsp->last_line = (lp_yylsp-1)->last_line; + lp_yylsp->last_column = (lp_yylsp-1)->last_column; + lp_yylsp->text = 0; + } + else + { + lp_yylsp->last_line = (lp_yylsp+lp_yylen-1)->last_line; + lp_yylsp->last_column = (lp_yylsp+lp_yylen-1)->last_column; + } +#endif + + /* Now "shift" the result of the reduction. + Determine what state that goes to, + based on the state we popped back to + and the rule number reduced by. */ + + lp_yyn = lp_yyr1[lp_yyn]; + + lp_yystate = lp_yypgoto[lp_yyn - YYNTBASE] + *lp_yyssp; + if (lp_yystate >= 0 && lp_yystate <= YYLAST && lp_yycheck[lp_yystate] == *lp_yyssp) + lp_yystate = lp_yytable[lp_yystate]; + else + lp_yystate = lp_yydefgoto[lp_yyn - YYNTBASE]; + + goto lp_yynewstate; + +lp_yyerrlab: /* here on detecting error */ + + if (! lp_yyerrstatus) + /* If not already recovering from an error, report this error. */ + { + ++lp_yynerrs; + +#ifdef YYERROR_VERBOSE + lp_yyn = lp_yypact[lp_yystate]; + + if (lp_yyn > YYFLAG && lp_yyn < YYLAST) + { + int size = 0; + char *msg; + int x, count; + + count = 0; + /* Start X at -lp_yyn if nec to avoid negative indexes in lp_yycheck. */ + for (x = (lp_yyn < 0 ? -lp_yyn : 0); + x < (sizeof(lp_yytname) / sizeof(char *)); x++) + if (lp_yycheck[x + lp_yyn] == x) + size += strlen(lp_yytname[x]) + 15, count++; + msg = (char *) malloc(size + 15); + if (msg != 0) + { + strcpy(msg, "parse error"); + + if (count < 5) + { + count = 0; + for (x = (lp_yyn < 0 ? -lp_yyn : 0); + x < (sizeof(lp_yytname) / sizeof(char *)); x++) + if (lp_yycheck[x + lp_yyn] == x) + { + strcat(msg, count == 0 ? ", expecting `" : " or `"); + strcat(msg, lp_yytname[x]); + strcat(msg, "'"); + count++; + } + } + lp_yyerror(msg); + free(msg); + } + else + lp_yyerror ("parse error; also virtual memory exceeded"); + } + else +#endif /* YYERROR_VERBOSE */ + lp_yyerror("parse error"); + } + + goto lp_yyerrlab1; +lp_yyerrlab1: /* here on error raised explicitly by an action */ + + if (lp_yyerrstatus == 3) + { + /* if just tried and failed to reuse lookahead token after an error, discard it. */ + + /* return failure if at end of input */ + if (lp_yychar == YYEOF) + YYABORT; + + lp_yychar = YYEMPTY; + } + + /* Else will try to reuse lookahead token + after shifting the error token. */ + + lp_yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto lp_yyerrhandle; + +lp_yyerrdefault: /* current state does not do anything special for the error token. */ + +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + lp_yyn = lp_yydefact[lp_yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ + if (lp_yyn) goto lp_yydefault; +#endif + +lp_yyerrpop: /* pop the current state because it cannot handle the error token */ + + if (lp_yyssp == lp_yyss) YYABORT; + lp_yyvsp--; + lp_yystate = *--lp_yyssp; +#ifdef YYLSP_NEEDED + lp_yylsp--; +#endif + +lp_yyerrhandle: + + lp_yyn = lp_yypact[lp_yystate]; + if (lp_yyn == YYFLAG) + goto lp_yyerrdefault; + + lp_yyn += YYTERROR; + if (lp_yyn < 0 || lp_yyn > YYLAST || lp_yycheck[lp_yyn] != YYTERROR) + goto lp_yyerrdefault; + + lp_yyn = lp_yytable[lp_yyn]; + if (lp_yyn < 0) + { + if (lp_yyn == YYFLAG) + goto lp_yyerrpop; + lp_yyn = -lp_yyn; + goto lp_yyreduce; + } + else if (lp_yyn == 0) + goto lp_yyerrpop; + + if (lp_yyn == YYFINAL) + YYACCEPT; + + + *++lp_yyvsp = lp_yylval; +#ifdef YYLSP_NEEDED + *++lp_yylsp = lp_yylloc; +#endif + + lp_yystate = lp_yyn; + goto lp_yynewstate; + + lp_yyacceptlab: + /* YYACCEPT comes here. */ + if (lp_yyfree_stacks) + { + free (lp_yyss); + free (lp_yyvs); +#ifdef YYLSP_NEEDED + free (lp_yyls); +#endif + } + return 0; + + lp_yyabortlab: + /* YYABORT comes here. */ + if (lp_yyfree_stacks) + { + free (lp_yyss); + free (lp_yyvs); +#ifdef YYLSP_NEEDED + free (lp_yyls); +#endif + } + return 1; +} + + +static void lp_yy_delete_allocated_memory(void) +{ + /* free memory allocated by flex. Otherwise some memory is not freed. + This is a bit tricky. There is not much documentation about this, but a lot of + reports of memory that keeps allocated */ + + /* If you get errors on this function call, just comment it. This will only result + in some memory that is not being freed. */ + +# if defined YY_CURRENT_BUFFER + /* flex defines the macro YY_CURRENT_BUFFER, so you should only get here if lp_rlp.h is + generated by flex */ + /* lex doesn't define this macro and thus should not come here, but lex doesn't has + this memory leak also ...*/ + + lp_yy_delete_buffer(YY_CURRENT_BUFFER); /* comment this line if you have problems with it */ + lp_yy_init = 1; /* make sure that the next time memory is allocated again */ + lp_yy_start = 0; +# endif + + FREE(Last_var); + FREE(Last_var0); +} + +static int parse(void) +{ + return(lp_yyparse()); +} + +lprec *read_lp1(lprec *lp, void *userhandle, read_modeldata_func read_modeldata, int verbose, char *lp_name) +{ + lp_yyin = (FILE *) userhandle; + lp_yyout = NULL; + lp_yylineno = 1; + lp_input = read_modeldata; + return(yacc_read(lp, verbose, lp_name, &lp_yylineno, parse, lp_yy_delete_allocated_memory)); +} + +lprec * __WINAPI read_lp(FILE *filename, int verbose, char *lp_name) +{ + return(read_lp1(NULL, filename, lp_input_lp_yyin, verbose, lp_name)); +} + +lprec * __WINAPI read_lpex(void *userhandle, read_modeldata_func read_modeldata, int verbose, char *lp_name) +{ + return(read_lp1(NULL, userhandle, read_modeldata, verbose, lp_name)); +} + +lprec *read_LP1(lprec *lp, char *filename, int verbose, char *lp_name) +{ + FILE *fpin; + + if((fpin = fopen(filename, "r")) != NULL) { + lp = read_lp1(lp, fpin, lp_input_lp_yyin, verbose, lp_name); + fclose(fpin); + } + else + lp = NULL; + return(lp); +} + +lprec * __WINAPI read_LP(char *filename, int verbose, char *lp_name) +{ + return(read_LP1(NULL, filename, verbose, lp_name)); +} + +MYBOOL __WINAPI LP_readhandle(lprec **lp, FILE *filename, int verbose, char *lp_name) +{ + if(lp != NULL) + *lp = read_lp1(*lp, filename, lp_input_lp_yyin, verbose, lp_name); + + return((lp != NULL) && (*lp != NULL)); +} diff --git a/src/lpsolve/headers/run_headers/lp_rlp.h b/src/lpSolve/src/lp_rlp.h similarity index 99% rename from src/lpsolve/headers/run_headers/lp_rlp.h rename to src/lpSolve/src/lp_rlp.h index ec822615..862c0656 100644 --- a/src/lpsolve/headers/run_headers/lp_rlp.h +++ b/src/lpSolve/src/lp_rlp.h @@ -1158,7 +1158,7 @@ YY_RULE_SETUP * EOB_ACT_END_OF_FILE - end of file */ -static int lp_yy_get_next_buffer() +static int lp_yy_get_next_buffer(void) { register char *dest = lp_yy_current_buffer->lp_yy_ch_buf; register char *source = lp_yytext_ptr; @@ -1290,7 +1290,7 @@ static int lp_yy_get_next_buffer() /* lp_yy_get_previous_state - get the state just before the EOB char was reached */ -static lp_yy_state_type lp_yy_get_previous_state() +static lp_yy_state_type lp_yy_get_previous_state(void) { register lp_yy_state_type lp_yy_current_state; register char *lp_yy_cp; @@ -1398,7 +1398,7 @@ register char *lp_yy_bp; #ifdef __cplusplus static int lp_yyinput() #else -static int input() +static inline int input(void) #endif { int c; diff --git a/src/lpsolve/build/lp_solve/lp_scale.c b/src/lpSolve/src/lp_scale.c similarity index 88% rename from src/lpsolve/build/lp_solve/lp_scale.c rename to src/lpSolve/src/lp_scale.c index 20f94a39..9d05238c 100644 --- a/src/lpsolve/build/lp_solve/lp_scale.c +++ b/src/lpSolve/src/lp_scale.c @@ -31,7 +31,7 @@ /* First define scaling and unscaling primitives */ -LPSREAL scaled_value(lprec *lp, LPSREAL value, int index) +REAL scaled_value(lprec *lp, REAL value, int index) { if(fabs(value) < lp->infinite) { if(lp->scaling_used) { @@ -46,7 +46,7 @@ LPSREAL scaled_value(lprec *lp, LPSREAL value, int index) return(value); } -LPSREAL unscaled_value(lprec *lp, LPSREAL value, int index) +REAL unscaled_value(lprec *lp, REAL value, int index) { if(fabs(value) < lp->infinite) { if(lp->scaling_used) { @@ -61,14 +61,14 @@ LPSREAL unscaled_value(lprec *lp, LPSREAL value, int index) return(value); } -STATIC LPSREAL scaled_mat(lprec *lp, LPSREAL value, int rownr, int colnr) +STATIC REAL scaled_mat(lprec *lp, REAL value, int rownr, int colnr) { if(lp->scaling_used) value *= lp->scalars[rownr] * lp->scalars[lp->rows + colnr]; return( value ); } -STATIC LPSREAL unscaled_mat(lprec *lp, LPSREAL value, int rownr, int colnr) +STATIC REAL unscaled_mat(lprec *lp, REAL value, int rownr, int colnr) { if(lp->scaling_used) value /= lp->scalars[rownr] * lp->scalars[lp->rows + colnr]; @@ -78,13 +78,13 @@ STATIC LPSREAL unscaled_mat(lprec *lp, LPSREAL value, int rownr, int colnr) /* Compute the scale factor by the formulae: FALSE: SUM (log |Aij|) ^ 2 TRUE: SUM (log |Aij| - RowScale[i] - ColScale[j]) ^ 2 */ -LPSREAL CurtisReidMeasure(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *FColScale) +REAL CurtisReidMeasure(lprec *lp, MYBOOL _Advanced, REAL *FRowScale, REAL *FColScale) { int i, nz; - LPSREAL absvalue, logvalue; - register LPSREAL result; + REAL absvalue, logvalue; + register REAL result; MATrec *mat = lp->matA; - LPSREAL *value; + REAL *value; int *rownr, *colnr; /* Do OF part */ @@ -139,22 +139,21 @@ LPSREAL CurtisReidMeasure(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSRE r, c are resulting row and column scalings (RowScale, ColScale) */ -int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *FColScale) +int CurtisReidScales(lprec *lp, MYBOOL _Advanced, REAL *FRowScale, REAL *FColScale) { int i, row, col, ent, nz; - LPSREAL *RowScalem2, *ColScalem2, + REAL *RowScalem2, *ColScalem2, *RowSum, *ColSum, *residual_even, *residual_odd; - LPSREAL sk, qk, ek, + REAL sk, qk, ek, skm1, qkm1, ekm1, - qkqkm1, ekekm1, + qkm2, qkqkm1, ekm2, ekekm1, absvalue, logvalue, StopTolerance; - //@FS: unused// LPSREAL ekm2, qkm2; int *RowCount, *ColCount, colMax; int Result; MATrec *mat = lp->matA; - LPSREAL *value; + REAL *value; int *rownr, *colnr; if(CurtisReidMeasure(lp, _Advanced, FRowScale, FColScale)<0.1*get_nonzeros(lp)) @@ -215,9 +214,9 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F residual = ColSum - ET RowCount-1 RowSum */ StopTolerance= MAX(lp->scalelimit-floor(lp->scalelimit), DEF_SCALINGEPS); - StopTolerance *= (LPSREAL) nz; + StopTolerance *= (REAL) nz; for(row = 0; row <= lp->rows; row++) { - FRowScale[row] = RowSum[row] / (LPSREAL) RowCount[row]; + FRowScale[row] = RowSum[row] / (REAL) RowCount[row]; RowScalem2[row] = FRowScale[row]; } @@ -228,14 +227,14 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F residual_even[col] = ColSum[col]; if(lp->orig_obj[col] != 0) - residual_even[col] -= RowSum[0] / (LPSREAL) RowCount[0]; + residual_even[col] -= RowSum[0] / (REAL) RowCount[0]; i = mat->col_end[col-1]; rownr = &(COL_MAT_ROWNR(i)); ent = mat->col_end[col]; for(; i < ent; i++, rownr += matRowColStep) { - residual_even[col] -= RowSum[*rownr] / (LPSREAL) RowCount[*rownr]; + residual_even[col] -= RowSum[*rownr] / (REAL) RowCount[*rownr]; } } @@ -243,11 +242,11 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F sk = 0; skm1 = 0; for(col = 1; col <= colMax; col++) - sk += (residual_even[col]*residual_even[col]) / (LPSREAL) ColCount[col]; + sk += (residual_even[col]*residual_even[col]) / (REAL) ColCount[col]; Result = 0; - qk=1; qkm1=0; //@FS: unused// qkm2=0; - ek=0; ekm1=0; //@FS: unused// ekm2=0; + qk=1; qkm1=0; qkm2=0; + ek=0; ekm1=0; ekm2=0; while(sk>StopTolerance) { /* Given the values of residual and sk, construct @@ -264,7 +263,7 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F for(row = 0; row <= lp->rows; row++) FRowScale[row]*=(1 + ekekm1 / qkqkm1); for(row = 0; row<=lp->rows; row++) - FRowScale[row]+=(residual_odd[row] / (qkqkm1 * (LPSREAL) RowCount[row]) - + FRowScale[row]+=(residual_odd[row] / (qkqkm1 * (REAL) RowCount[row]) - RowScalem2[row] * ekekm1 / qkqkm1); } } @@ -276,7 +275,7 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F for(col = 1; col <= colMax; col++) FColScale[col] *= (1 + ekekm1 / qkqkm1); for(col = 1; col <= colMax; col++) - FColScale[col] += (residual_even[col] / ((LPSREAL) ColCount[col] * qkqkm1) - + FColScale[col] += (residual_even[col] / ((REAL) ColCount[col] * qkqkm1) - ColScalem2[col] * ekekm1 / qkqkm1); } } @@ -289,13 +288,13 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F for(i = 1; i <= colMax; i++) if(lp->orig_obj[i] != 0) - residual_odd[0] += (residual_even[i] / (LPSREAL) ColCount[i]); + residual_odd[0] += (residual_even[i] / (REAL) ColCount[i]); rownr = &(COL_MAT_ROWNR(0)); colnr = &(COL_MAT_COLNR(0)); for(i = 0; i < nz; i++, rownr += matRowColStep, colnr += matRowColStep) { - residual_odd[*rownr] += (residual_even[*colnr] / (LPSREAL) ColCount[*colnr]); + residual_odd[*rownr] += (residual_even[*colnr] / (REAL) ColCount[*colnr]); } for(row = 0; row <= lp->rows; row++) residual_odd[row] *= (-1 / qk); @@ -304,7 +303,7 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F skm1 = sk; sk = 0; for(row = 0; row <= lp->rows; row++) - sk += (residual_odd[row]*residual_odd[row]) / (LPSREAL) RowCount[row]; + sk += (residual_odd[row]*residual_odd[row]) / (REAL) RowCount[row]; } else { /* odd */ /* residual */ @@ -313,13 +312,13 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F for(i = 1; i <= colMax; i++) if(lp->orig_obj[i] != 0) - residual_even[i] += (residual_odd[0] / (LPSREAL) RowCount[0]); + residual_even[i] += (residual_odd[0] / (REAL) RowCount[0]); rownr = &(COL_MAT_ROWNR(0)); colnr = &(COL_MAT_COLNR(0)); for(i = 0; i < nz; i++, rownr += matRowColStep, colnr += matRowColStep) { - residual_even[*colnr] += (residual_odd[*rownr] / (LPSREAL) RowCount[*rownr]); + residual_even[*colnr] += (residual_odd[*rownr] / (REAL) RowCount[*rownr]); } for(col = 1; col <= colMax; col++) residual_even[col] *= (-1 / qk); @@ -328,15 +327,15 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F skm1 = sk; sk = 0; for(col = 1; col <= colMax; col++) - sk += (residual_even[col]*residual_even[col]) / (LPSREAL) ColCount[col]; + sk += (residual_even[col]*residual_even[col]) / (REAL) ColCount[col]; } /* Compute ek and qk */ - //@FS: unused// ekm2=ekm1; + ekm2=ekm1; ekm1=ek; ek=qk * sk / skm1; - //@FS: unused// qkm2=qkm1; + qkm2=qkm1; qkm1=qk; qk=1-ek; @@ -350,17 +349,17 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F for(row = 0; row<=lp->rows; row++) FRowScale[row]*=(1.0 + ekekm1 / qkm1); for(row = 0; row<=lp->rows; row++) - FRowScale[row]+=(residual_odd[row] / (qkm1 * (LPSREAL) RowCount[row]) - + FRowScale[row]+=(residual_odd[row] / (qkm1 * (REAL) RowCount[row]) - RowScalem2[row] * ekekm1 / qkm1); + } } else { /* pass is odd, compute ColScale */ for(col=1; col<=colMax; col++) FColScale[col]*=(1 + ekekm1 / qkm1); for(col=1; col<=colMax; col++) - FColScale[col]+=(residual_even[col] / ((LPSREAL) ColCount[col] * qkm1) - + FColScale[col]+=(residual_even[col] / ((REAL) ColCount[col] * qkm1) - ColScalem2[col] * ekekm1 / qkm1); } - } /* Do validation, if indicated */ if(FALSE && mat_validate(mat)){ @@ -369,7 +368,7 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F /* CHECK: M RowScale + E ColScale = RowSum */ error = 0; for(row = 0; row <= lp->rows; row++) { - check = (LPSREAL) RowCount[row] * FRowScale[row]; + check = (REAL) RowCount[row] * FRowScale[row]; if(row == 0) { for(i = 1; i <= colMax; i++) { if(lp->orig_obj[i] != 0) @@ -391,7 +390,7 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F /* CHECK: E^T RowScale + N ColScale = ColSum */ error = 0; for(col = 1; col <= colMax; col++) { - check = (LPSREAL) ColCount[col] * FColScale[col]; + check = (REAL) ColCount[col] * FColScale[col]; if(lp->orig_obj[col] != 0) check += FRowScale[0]; @@ -440,9 +439,9 @@ int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *F } -STATIC MYBOOL scaleCR(lprec *lp, LPSREAL *scaledelta) +STATIC MYBOOL scaleCR(lprec *lp, REAL *scaledelta) { - LPSREAL *scalechange = NULL; + REAL *scalechange = NULL; int Result; if(!lp->scaling_used) { @@ -474,7 +473,7 @@ STATIC MYBOOL scaleCR(lprec *lp, LPSREAL *scaledelta) return((MYBOOL) (Result > 0)); } -STATIC MYBOOL transform_for_scale(lprec *lp, LPSREAL *value) +STATIC MYBOOL transform_for_scale(lprec *lp, REAL *value) { MYBOOL Accept = TRUE; *value = fabs(*value); @@ -492,7 +491,7 @@ STATIC MYBOOL transform_for_scale(lprec *lp, LPSREAL *value) return( Accept ); } -STATIC void accumulate_for_scale(lprec *lp, LPSREAL *min, LPSREAL *max, LPSREAL value) +STATIC void accumulate_for_scale(lprec *lp, REAL *min, REAL *max, REAL value) { if(transform_for_scale(lp, &value)) { if(is_scaletype(lp, SCALE_MEAN)) { @@ -503,12 +502,12 @@ STATIC void accumulate_for_scale(lprec *lp, LPSREAL *min, LPSREAL *max, LPSREAL SETMAX(*max, value); SETMIN(*min, value); } - } + } } -STATIC LPSREAL minmax_to_scale(lprec *lp, LPSREAL min, LPSREAL max, int itemcount) +STATIC REAL minmax_to_scale(lprec *lp, REAL min, REAL max, int itemcount) { - LPSREAL scale; + REAL scale; /* Initialize according to transformation / weighting model */ if(is_scalemode(lp, SCALE_LOGARITHMIC)) @@ -553,7 +552,7 @@ STATIC LPSREAL minmax_to_scale(lprec *lp, LPSREAL min, LPSREAL max, int itemcoun return(scale); } -STATIC LPSREAL roundPower2(LPSREAL scale) +STATIC REAL roundPower2(REAL scale) /* Purpose is to round a number to it nearest power of 2; in a system with binary number representation, this avoids rounding errors when scale is used to normalize another value */ @@ -583,7 +582,7 @@ STATIC LPSREAL roundPower2(LPSREAL scale) } -STATIC MYBOOL scale_updatecolumns(lprec *lp, LPSREAL *scalechange, MYBOOL updateonly) +STATIC MYBOOL scale_updatecolumns(lprec *lp, REAL *scalechange, MYBOOL updateonly) { int i, j; @@ -605,7 +604,7 @@ STATIC MYBOOL scale_updatecolumns(lprec *lp, LPSREAL *scalechange, MYBOOL update return( TRUE ); } -STATIC MYBOOL scale_updaterows(lprec *lp, LPSREAL *scalechange, MYBOOL updateonly) +STATIC MYBOOL scale_updaterows(lprec *lp, REAL *scalechange, MYBOOL updateonly) { int i; @@ -628,12 +627,11 @@ STATIC MYBOOL scale_updaterows(lprec *lp, LPSREAL *scalechange, MYBOOL updateonl return( TRUE ); } -STATIC MYBOOL scale_columns(lprec *lp, LPSREAL *scaledelta) +STATIC MYBOOL scale_columns(lprec *lp, REAL *scaledelta) { - int i, j, nz; - //@FS: unused// int colMax; - LPSREAL *scalechange; - LPSREAL *value; + int i,j, colMax, nz; + REAL *scalechange; + REAL *value; int *colnr; MATrec *mat = lp->matA; @@ -646,7 +644,7 @@ STATIC MYBOOL scale_columns(lprec *lp, LPSREAL *scaledelta) else scalechange = &scaledelta[lp->rows]; - //@FS: unused// colMax = lp->columns; + colMax = lp->columns; /* Scale matrix entries (including any Lagrangean constraints) */ for(i = 1; i <= lp->columns; i++) { @@ -678,11 +676,11 @@ STATIC MYBOOL scale_columns(lprec *lp, LPSREAL *scaledelta) return( TRUE ); } -STATIC MYBOOL scale_rows(lprec *lp, LPSREAL *scaledelta) +STATIC MYBOOL scale_rows(lprec *lp, REAL *scaledelta) { int i, j, nz, colMax; - LPSREAL *scalechange; - LPSREAL *value; + REAL *scalechange; + REAL *value; int *rownr; MATrec *mat = lp->matA; @@ -732,14 +730,14 @@ STATIC MYBOOL scale_rows(lprec *lp, LPSREAL *scaledelta) return( TRUE ); } -STATIC LPSREAL scale(lprec *lp, LPSREAL *scaledelta) +STATIC REAL scale(lprec *lp, REAL *scaledelta) { int i, j, nz, row_count, nzOF = 0; - LPSREAL *row_max, *row_min, *scalechange = NULL, absval; - LPSREAL col_max, col_min; + REAL *row_max, *row_min, *scalechange = NULL, absval; + REAL col_max, col_min; MYBOOL rowscaled, colscaled; MATrec *mat = lp->matA; - LPSREAL *value; + REAL *value; int *rownr; if(is_scaletype(lp, SCALE_NONE)) @@ -808,7 +806,7 @@ STATIC LPSREAL scale(lprec *lp, LPSREAL *scaledelta) nz = nzOF; else nz = mat_rowlength(lp->matA, i); - absval = minmax_to_scale(lp, row_min[i], row_max[i], nz); /* nz instead of nzOF KJEI 20/05/2010 */ + absval = minmax_to_scale(lp, row_min[i], row_max[i], nzOF); if(absval == 0) absval = 1; scalechange[i] = absval; @@ -883,7 +881,7 @@ STATIC LPSREAL scale(lprec *lp, LPSREAL *scaledelta) return(1 - sqrt(col_max*col_min)); } -STATIC MYBOOL finalize_scaling(lprec *lp, LPSREAL *scaledelta) +STATIC MYBOOL finalize_scaling(lprec *lp, REAL *scaledelta) { int i; @@ -899,7 +897,7 @@ STATIC MYBOOL finalize_scaling(lprec *lp, LPSREAL *scaledelta) /* Check if we should prevent rounding errors */ if(is_scalemode(lp, SCALE_POWER2)) { - LPSREAL *scalars; + REAL *scalars; if(scaledelta == NULL) scalars = lp->scalars; else @@ -914,10 +912,10 @@ STATIC MYBOOL finalize_scaling(lprec *lp, LPSREAL *scaledelta) } -STATIC LPSREAL auto_scale(lprec *lp) +STATIC REAL auto_scale(lprec *lp) { int n = 1; - LPSREAL scalingmetric = 0, *scalenew = NULL; + REAL scalingmetric = 0, *scalenew = NULL; if(lp->scaling_used && ((((lp->scalemode & SCALE_DYNUPDATE) == 0)) || (lp->bb_level > 0))) @@ -934,7 +932,7 @@ STATIC LPSREAL auto_scale(lprec *lp) scalingmetric = scaleCR(lp, scalenew); } else { - LPSREAL scalinglimit, scalingdelta; + REAL scalinglimit, scalingdelta; int count; /* Integer value of scalelimit holds the maximum number of iterations; default to 1 */ @@ -989,7 +987,7 @@ STATIC void unscale_columns(lprec *lp) { int i, j, nz; MATrec *mat = lp->matA; - LPSREAL *value; + REAL *value; int *rownr, *colnr; if(!lp->columns_scaled) @@ -1029,7 +1027,7 @@ void undoscale(lprec *lp) { int i, j, nz; MATrec *mat = lp->matA; - LPSREAL *value; + REAL *value; int *rownr, *colnr; if(lp->scaling_used) { diff --git a/src/lpsolve/headers/run_headers/lp_scale.h b/src/lpSolve/src/lp_scale.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_scale.h rename to src/lpSolve/src/lp_scale.h diff --git a/src/lpsolve/build/lp_solve/lp_simplex.c b/src/lpSolve/src/lp_simplex.c similarity index 96% rename from src/lpsolve/build/lp_solve/lp_simplex.c rename to src/lpSolve/src/lp_simplex.c index 8d3eeafe..bea1e5ad 100644 --- a/src/lpsolve/build/lp_solve/lp_simplex.c +++ b/src/lpSolve/src/lp_simplex.c @@ -39,7 +39,7 @@ #endif -STATIC void stallMonitor_update(lprec *lp, LPSREAL newOF) +STATIC void stallMonitor_update(lprec *lp, REAL newOF) { int newpos; OBJmonrec *monitor = lp->monitor; @@ -59,7 +59,7 @@ STATIC MYBOOL stallMonitor_creepingObj(lprec *lp) OBJmonrec *monitor = lp->monitor; if(monitor->countstep > 1) { - LPSREAL deltaOF = (monitor->objstep[monitor->currentstep] - + REAL deltaOF = (monitor->objstep[monitor->currentstep] - monitor->objstep[monitor->startstep]) / monitor->countstep; deltaOF /= MAX(1, (monitor->idxstep[monitor->currentstep] - monitor->idxstep[monitor->startstep])); @@ -75,7 +75,7 @@ STATIC MYBOOL stallMonitor_shortSteps(lprec *lp) OBJmonrec *monitor = lp->monitor; if(monitor->countstep == OBJ_STEPS) { - LPSREAL deltaOF = MAX(1, (monitor->idxstep[monitor->currentstep] - + REAL deltaOF = MAX(1, (monitor->idxstep[monitor->currentstep] - monitor->idxstep[monitor->startstep])) / monitor->countstep; deltaOF = pow(deltaOF*OBJ_STEPS, 0.66); return( (MYBOOL) (deltaOF > monitor->limitstall[TRUE]) ); @@ -119,7 +119,7 @@ STATIC MYBOOL stallMonitor_create(lprec *lp, MYBOOL isdual, char *funcname) monitor->limitstall[FALSE] = 0; else monitor->limitstall[FALSE] = MAX(MAX_STALLCOUNT, - (int) pow((LPSREAL) (lp->rows+lp->columns)/2, 0.667)); + (int) pow((REAL) (lp->rows+lp->columns)/2, 0.667)); #if 1 monitor->limitstall[FALSE] *= 2+2; /* Expand degeneracy/stalling tolerance range */ #endif @@ -149,7 +149,7 @@ STATIC MYBOOL stallMonitor_check(lprec *lp, int rownr, int colnr, int lastnr, #else msglevel = DETAILED; #endif - LPSREAL deltaobj = lp->suminfeas; + REAL deltaobj = lp->suminfeas; /* Accept unconditionally if this is the first or second call */ monitor->active = FALSE; @@ -181,34 +181,20 @@ STATIC MYBOOL stallMonitor_check(lprec *lp, int rownr, int colnr, int lastnr, /* Also require that we have a measure of infeasibility-stalling */ if(isStalled) { - LPSREAL testvalue, refvalue = monitor->epsvalue; -#if 1 + REAL testvalue, refvalue = monitor->epsvalue; if(monitor->isdual) refvalue *= 1000*log10(9.0+lp->rows); else refvalue *= 1000*log10(9.0+lp->columns); -#else - refvalue *= 1000*log10(9.0+lp->sum); -#endif testvalue = my_reldiff(monitor->thisinfeas, monitor->previnfeas); isStalled &= (fabs(testvalue) < refvalue); /* Check if we should force "major" pivoting, i.e. no bound flips; this is activated when we see the feasibility deteriorate */ /* if(!isStalled && (testvalue > 0) && (TRUE || is_action(lp->anti_degen, ANTIDEGEN_BOUNDFLIP))) */ -#if !defined _PRICE_NOBOUNDFLIP if(!isStalled && (testvalue > 0) && is_action(lp->anti_degen, ANTIDEGEN_BOUNDFLIP)) acceptance = AUTOMATIC; } -#else - if(!isStalled && (testvalue > 0) && !ISMASKSET(lp->piv_strategy, PRICE_NOBOUNDFLIP)) { - SETMASK(lp->piv_strategy, PRICE_NOBOUNDFLIP); - acceptance = AUTOMATIC; - } - } - else - CLEARMASK(lp->piv_strategy, PRICE_NOBOUNDFLIP); -#endif #if 1 isCreeping = FALSE; @@ -239,7 +225,7 @@ STATIC MYBOOL stallMonitor_check(lprec *lp, int rownr, int colnr, int lastnr, /* Check if we should change pivoting strategy */ else if(isCreeping || /* We have OF creep */ (monitor->Ncycle > monitor->limitstall[monitor->isdual]) || /* KE empirical value */ - ((monitor->Ccycle == rownr) && (monitor->Rcycle == colnr))) { /* Obvious cycling */ + ((monitor->Ccycle == rownr) && (monitor->Rcycle == colnr))) { /* Obvious cycling */ monitor->active = TRUE; @@ -339,7 +325,7 @@ STATIC void stallMonitor_finish(lprec *lp) } -STATIC MYBOOL add_artificial(lprec *lp, int forrownr, LPSREAL *nzarray, int *idxarray) +STATIC MYBOOL add_artificial(lprec *lp, int forrownr, REAL *nzarray, int *idxarray) /* This routine is called for each constraint at the start of primloop and the primal problem is infeasible. Its purpose is to add artificial variables and associated @@ -353,7 +339,7 @@ STATIC MYBOOL add_artificial(lprec *lp, int forrownr, LPSREAL *nzarray, int *idx if(add) { int *rownr = NULL, i, bvar, ii; - LPSREAL *avalue = NULL, rhscoef, acoef; + REAL *avalue = NULL, rhscoef, acoef; MATrec *mat = lp->matA; /* Check the simple case where a slack is basic */ @@ -503,7 +489,7 @@ STATIC int findBasicArtificial(lprec *lp, int before) return(i); } -STATIC void eliminate_artificials(lprec *lp, LPSREAL *prow) +STATIC void eliminate_artificials(lprec *lp, REAL *prow) { int i, j, colnr, rownr, P1extraDim = abs(lp->P1extraDim); @@ -560,14 +546,14 @@ STATIC void clear_artificials(lprec *lp) } -STATIC int primloop(lprec *lp, MYBOOL primalfeasible, LPSREAL primaloffset) +STATIC int primloop(lprec *lp, MYBOOL primalfeasible, REAL primaloffset) { MYBOOL primal = TRUE, bfpfinal = FALSE, changedphase = FALSE, forceoutEQ = AUTOMATIC, primalphase1, pricerCanChange, minit, stallaccept, pendingunbounded; int i, j, k, colnr = 0, rownr = 0, lastnr = 0, candidatecount = 0, minitcount = 0, ok = TRUE; LREAL theta = 0.0; - LPSREAL epsvalue, xviolated = 0.0, cviolated = 0.0, + REAL epsvalue, xviolated, cviolated, *prow = NULL, *pcol = NULL, *drow = lp->drow; int *workINT = NULL, @@ -872,7 +858,7 @@ STATIC int primloop(lprec *lp, MYBOOL primalfeasible, LPSREAL primaloffset) /* Check if we are still primal feasible; the default assumes that this check is not necessary after the relaxed problem has been solved satisfactorily. */ - if((lp->bb_level <= 1) || (lp->improve & IMPROVE_BBSIMPLEX) /* || (lp->bb_rule & NODE_RCOSTFIXING) */) { /* NODE_RCOSTFIXING fix */ + if((lp->bb_level <= 1) || (lp->improve & IMPROVE_BBSIMPLEX)) { set_action(&lp->piv_strategy, PRICE_FORCEFULL); i = rowdual(lp, lp->rhs, FALSE, FALSE, NULL); clear_action(&lp->piv_strategy, PRICE_FORCEFULL); @@ -981,7 +967,7 @@ STATIC int primloop(lprec *lp, MYBOOL primalfeasible, LPSREAL primaloffset) return(ok); } /* primloop */ -STATIC int dualloop(lprec *lp, MYBOOL dualfeasible, int dualinfeasibles[], LPSREAL dualoffset) +STATIC int dualloop(lprec *lp, MYBOOL dualfeasible, int dualinfeasibles[], REAL dualoffset) { MYBOOL primal = FALSE, inP1extra, dualphase1 = FALSE, changedphase = TRUE, pricerCanChange, minit, stallaccept, longsteps, @@ -994,7 +980,7 @@ STATIC int dualloop(lprec *lp, MYBOOL dualfeasible, int dualinfeasibles[], LPSRE ok = TRUE; int *boundswaps = NULL; LREAL theta = 0.0; - LPSREAL xviolated, cviolated, + REAL epsvalue, xviolated, cviolated, *prow = NULL, *pcol = NULL, *drow = lp->drow; int *nzprow = NULL, *workINT = NULL, @@ -1054,7 +1040,7 @@ STATIC int dualloop(lprec *lp, MYBOOL dualfeasible, int dualinfeasibles[], LPSRE /* Do regular dual simplex variable initializations */ lp->spx_status = RUNNING; minit = ITERATE_MAJORMAJOR; - //@FS: unused// epsvalue = lp->epspivot; + epsvalue = lp->epspivot; ok = stallMonitor_create(lp, TRUE, "dualloop"); if(!ok) @@ -1404,7 +1390,7 @@ STATIC int dualloop(lprec *lp, MYBOOL dualfeasible, int dualinfeasibles[], LPSRE /* Check if we are still dual feasible; the default assumes that this check is not necessary after the relaxed problem has been solved satisfactorily. */ colnr = 0; - if((dualoffset != 0) || (lp->bb_level <= 1) || (lp->improve & IMPROVE_BBSIMPLEX) || (lp->bb_rule & NODE_RCOSTFIXING)) { /* NODE_RCOSTFIXING fix */ + if((dualoffset != 0) || (lp->bb_level <= 1) || (lp->improve & IMPROVE_BBSIMPLEX)) { set_action(&lp->piv_strategy, PRICE_FORCEFULL); colnr = colprim(lp, drow, nzdrow, FALSE, 1, &candidatecount, FALSE, NULL); clear_action(&lp->piv_strategy, PRICE_FORCEFULL); @@ -1526,7 +1512,7 @@ STATIC int spx_run(lprec *lp, MYBOOL validInvB) { int i, j, singular_count, lost_feas_count, *infeasibles = NULL, *boundflip_count; MYBOOL primalfeasible, dualfeasible, lost_feas_state, isbb; - LPSREAL primaloffset = 0, dualoffset = 0; + REAL primaloffset = 0, dualoffset = 0; lp->current_iter = 0; lp->current_bswap = 0; @@ -1700,7 +1686,7 @@ lprec *make_lag(lprec *lpserver) int i; lprec *hlp; MYBOOL ret; - LPSREAL *duals; + REAL *duals; /* Create a Lagrangean solver instance */ hlp = make_lp(0, lpserver->columns); @@ -1764,13 +1750,13 @@ STATIC int heuristics(lprec *lp, int mode) return( status ); } -STATIC int lag_solve(lprec *lp, LPSREAL start_bound, int num_iter) +STATIC int lag_solve(lprec *lp, REAL start_bound, int num_iter) { int i, j, citer, nochange, oldpresolve; MYBOOL LagFeas, AnyFeas, Converged, same_basis; - LPSREAL *OrigObj, *ModObj, *SubGrad, *BestFeasSol; - LPSREAL Zub, Zlb, Znow, Zprev, Zbest, rhsmod, hold; - LPSREAL Phi, StepSize = 0.0, SqrsumSubGrad; + REAL *OrigObj, *ModObj, *SubGrad, *BestFeasSol; + REAL Zub, Zlb, Znow, Zprev, Zbest, rhsmod, hold; + REAL Phi, StepSize = 0.0, SqrsumSubGrad; /* Make sure we have something to work with */ if(lp->spx_status != OPTIMAL) { @@ -2049,11 +2035,7 @@ STATIC int spx_solve(lprec *lp) lp->bfp_restart(lp); lp->spx_status = presolve(lp); - if(lp->spx_status == PRESOLVED) { - status = lp->spx_status; - goto Reconstruct; - } - else if(lp->spx_status != RUNNING) + if(lp->spx_status != RUNNING) goto Leave; iprocessed = !lp->wasPreprocessed; @@ -2077,7 +2059,6 @@ STATIC int spx_solve(lprec *lp) postprocess(lp); /* Restore data related to presolve (mainly a placeholder as of v5.1) */ -Reconstruct: if(!postsolve(lp, status)) report(lp, SEVERE, "spx_solve: Failure during postsolve.\n"); @@ -2094,15 +2075,15 @@ STATIC int spx_solve(lprec *lp) if((lp->lag_status != RUNNING) && (lp->invB != NULL)) { int itemp; - LPSREAL test; + REAL test; itemp = lp->bfp_nonzeros(lp, TRUE); test = 100; if(lp->total_iter > 0) - test *= (LPSREAL) lp->total_bswap/lp->total_iter; + test *= (REAL) lp->total_bswap/lp->total_iter; report(lp, NORMAL, "\n "); - report(lp, NORMAL, "MEMO: lp_solve version %d.%d.%d.%d for %d bit OS, with %d bit LPSREAL variables.\n", - MAJORVERSION, MINORVERSION, RELEASE, BUILD, 8*sizeof(void *), 8*sizeof(LPSREAL)); + report(lp, NORMAL, "MEMO: lp_solve version %d.%d.%d.%d for %d bit OS, with %d bit REAL variables.\n", + MAJORVERSION, MINORVERSION, RELEASE, BUILD, 8*sizeof(void *), 8*sizeof(REAL)); report(lp, NORMAL, " In the total iteration count %.0f, %.0f (%.1f%%) were bound flips.\n", (double) lp->total_iter, (double) lp->total_bswap, test); report(lp, NORMAL, " There were %d refactorizations, %d triggered by time and %d by density.\n", @@ -2181,26 +2162,7 @@ int lin_solve(lprec *lp) /* Reset heuristic in preparation for next run (if any) */ lp->bb_heuristicOF = my_chsign(is_maxim(lp), lp->infinite); - /* Check that correct status code is returned */ -/* - peno 26.12.07 - status was not set to SUBOPTIMAL, only lp->spx_status - Bug occured by a change in 5.5.0.10 when && (lp->bb_totalnodes > 0) was added - added status = - See UnitTest3 -*/ -/* - peno 12.01.08 - If an integer solution is found with the same objective value as the relaxed solution then - searching is stopped. This by setting lp->bb_break. However this resulted in a report of SUBOPTIMAL - solution. For this, && !bb_better(lp, OF_DUALLIMIT, OF_TEST_BE) is added in the test. - See UnitTest20 -*/ - if((lp->spx_status == OPTIMAL) && (lp->bb_totalnodes > 0)) { - if((lp->bb_break && !bb_better(lp, OF_DUALLIMIT, OF_TEST_BE)) /* || - ISMASKSET(lp->trace, TRACE_NOBBLIMIT) */) - status = lp->spx_status = SUBOPTIMAL; - } - return( status ); } + + diff --git a/src/lpsolve/headers/run_headers/lp_simplex.h b/src/lpSolve/src/lp_simplex.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_simplex.h rename to src/lpSolve/src/lp_simplex.h diff --git a/src/lpsolve/headers/run_headers/lp_types.h b/src/lpSolve/src/lp_types.h similarity index 98% rename from src/lpsolve/headers/run_headers/lp_types.h rename to src/lpSolve/src/lp_types.h index dd42a44d..c4fde666 100644 --- a/src/lpsolve/headers/run_headers/lp_types.h +++ b/src/lpSolve/src/lp_types.h @@ -214,7 +214,6 @@ #define my_precision(val, eps) restoreINT(val, eps) #endif #define my_reldiff(x, y) (((x) - (y)) / (1.0 + fabs((REAL) (y)))) -#define my_boundstr(x) (fabs(x) < lp->infinite ? sprintf("%g",x) : ((x) < 0 ? "-Inf" : "Inf") ) #ifndef my_boolstr #define my_boolstr(x) (!(x) ? "FALSE" : "TRUE") #endif diff --git a/src/lpsolve/build/lp_solve/lp_utils.c b/src/lpSolve/src/lp_utils.c similarity index 90% rename from src/lpsolve/build/lp_solve/lp_utils.c rename to src/lpSolve/src/lp_utils.c index ff2ba207..54b3b2d4 100644 --- a/src/lpsolve/build/lp_solve/lp_utils.c +++ b/src/lpSolve/src/lp_utils.c @@ -6,12 +6,12 @@ #include "lp_utils.h" #include #include -// #include "lp_bit.h" #ifdef FORTIFY # include "lp_fortify.h" #endif +#include "R.h" /* Miscellaneous utilities as implemented for lp_solve v5.0+ @@ -90,19 +90,19 @@ STATIC MYBOOL allocINT(lprec *lp, int **ptr, int size, MYBOOL clear) else return( TRUE ); } -STATIC MYBOOL allocREAL(lprec *lp, LPSREAL **ptr, int size, MYBOOL clear) +STATIC MYBOOL allocREAL(lprec *lp, REAL **ptr, int size, MYBOOL clear) { if(clear == TRUE) - *ptr = (LPSREAL *) calloc(size, sizeof(**ptr)); + *ptr = (REAL *) calloc(size, sizeof(**ptr)); else if(clear & AUTOMATIC) { - *ptr = (LPSREAL *) realloc(*ptr, size * sizeof(**ptr)); + *ptr = (REAL *) realloc(*ptr, size * sizeof(**ptr)); if(clear & TRUE) MEMCLEAR(*ptr, size); } else - *ptr = (LPSREAL *) malloc(size * sizeof(**ptr)); + *ptr = (REAL *) malloc(size * sizeof(**ptr)); if(((*ptr) == NULL) && (size > 0)) { - lp->report(lp, CRITICAL, "alloc of %d 'LPSREAL' failed\n", size); + lp->report(lp, CRITICAL, "alloc of %d 'REAL' failed\n", size); lp->spx_status = NOMEMORY; return( FALSE ); } @@ -150,6 +150,22 @@ STATIC MYBOOL allocFREE(lprec *lp, void **ptr) #include "lp_utils.h" /* alloc-routines should always be before this line! */ +#if 0 +!if !defined INLINE +void set_biton(MYBOOL *bitarray, int item) +{ + bitarray[item / 8] |= (1 << (item % 8)); +} +void set_bitoff(MYBOOL *bitarray, int item) +{ + bitarray[item / 8] &= ~(1 << (item % 8)); +} +MYBOOL is_biton(MYBOOL *bitarray, int item) +{ + return( (MYBOOL) ((bitarray[item / 8] & (1 << (item % 8))) != 0) ); +} +!endif +#endif int comp_bits(MYBOOL *bitarray1, MYBOOL *bitarray2, int items) { int i, items4, left = 0, right = 0; @@ -217,7 +233,7 @@ STATIC char *mempool_obtainVector(workarraysrec *mempool, int count, int unitsiz char *newmem = NULL; MYBOOL *bnewmem = NULL; int *inewmem = NULL, size, i, ib, ie, memMargin = 0; - LPSREAL *rnewmem = NULL; + REAL *rnewmem = NULL; /* First find the iso-sized window (binary search) */ size = count*unitsize; @@ -271,7 +287,7 @@ STATIC char *mempool_obtainVector(workarraysrec *mempool, int count, int unitsiz allocINT(mempool->lp, &inewmem, count, TRUE); newmem = (char *) inewmem; } - else if(unitsize == sizeof(LPSREAL)) { + else if(unitsize == sizeof(REAL)) { allocREAL(mempool->lp, &rnewmem, count, TRUE); newmem = (char *) rnewmem; } @@ -340,9 +356,9 @@ STATIC MYBOOL mempool_free(workarraysrec **mempool) return( TRUE ); } -LPSREAL *cloneREAL(lprec *lp, LPSREAL *origlist, int size) +REAL *cloneREAL(lprec *lp, REAL *origlist, int size) { - LPSREAL *newlist; + REAL *newlist; size += 1; if(allocREAL(lp, &newlist, size, FALSE)) @@ -376,11 +392,11 @@ STATIC void roundVector(LREAL *myvector, int endpos, LREAL roundzero) *myvector = 0; } -STATIC LPSREAL normalizeVector(LPSREAL *myvector, int endpos) +STATIC REAL normalizeVector(REAL *myvector, int endpos) /* Scale the ingoing vector so that its norm is unit, and return the original length */ { int i; - LPSREAL SSQ; + REAL SSQ; /* Cumulate squares */ SSQ = 0; @@ -407,9 +423,9 @@ STATIC void swapINT(int *item1, int *item2) *item2 = hold; } -STATIC void swapREAL(LPSREAL *item1, LPSREAL *item2) +STATIC void swapREAL(REAL *item1, REAL *item2) { - LPSREAL hold = *item1; + REAL hold = *item1; *item1 = *item2; *item2 = hold; } @@ -423,9 +439,9 @@ STATIC void swapPTR(void **item1, void **item2) } -STATIC LPSREAL restoreINT(LPSREAL valREAL, LPSREAL epsilon) +STATIC REAL restoreINT(REAL valREAL, REAL epsilon) { - LPSREAL valINT, fracREAL, fracABS; + REAL valINT, fracREAL, fracABS; fracREAL = modf(valREAL, &valINT); fracABS = fabs(fracREAL); @@ -440,10 +456,10 @@ STATIC LPSREAL restoreINT(LPSREAL valREAL, LPSREAL epsilon) return(valREAL); } -STATIC LPSREAL roundToPrecision(LPSREAL value, LPSREAL precision) +STATIC REAL roundToPrecision(REAL value, REAL precision) { #if 1 - LPSREAL vmod; + REAL vmod; int vexp2, vexp10; LLONG sign; @@ -458,11 +474,10 @@ STATIC LPSREAL roundToPrecision(LPSREAL value, LPSREAL precision) return( 0 ); else if(value == floor(value)) return( value*sign ); - else if((value < (LPSREAL) MAXINT64) && - (modf((LPSREAL) (value+precision), &vmod) < precision)) { - /* sign *= (LLONG) (value+precision); */ - sign *= (LLONG) (value+0.5); - return( (LPSREAL) sign ); + else if((value < (REAL) MAXINT64) && + (modf((REAL) (value+precision), &vmod) < precision)) { + sign *= (LLONG) (value+precision); + return( (REAL) sign ); } /* Optionally round with base 2 representation for additional precision */ @@ -503,26 +518,24 @@ STATIC int searchFor(int target, int *attributes, int size, int offset, MYBOOL a /* Do binary search logic based on a sorted attribute vector */ newPos = (beginPos + endPos) / 2; match = attributes[newPos]; - if(absolute) { + if(absolute) match = abs(match); - } - while(endPos - beginPos > LINEARSEARCH) { if(match < target) { beginPos = newPos + 1; newPos = (beginPos + endPos) / 2; match = attributes[newPos]; - if(absolute) { + if(absolute) match = abs(match); - } - } else if(match > target) { + } + else if(match > target) { endPos = newPos - 1; newPos = (beginPos + endPos) / 2; match = attributes[newPos]; - if(absolute) { + if(absolute) match = abs(match); - } - } else { + } + else { beginPos = newPos; endPos = newPos; } @@ -531,18 +544,16 @@ STATIC int searchFor(int target, int *attributes, int size, int offset, MYBOOL a /* Do linear (unsorted) search logic */ if(endPos - beginPos <= LINEARSEARCH) { match = attributes[beginPos]; - if(absolute) { + if(absolute) match = abs(match); - } - while((beginPos < endPos) && (match != target)) { - beginPos++; - match = attributes[beginPos]; - if(absolute) - match = abs(match); - } - if(match == target) { - endPos = beginPos; - } + while((beginPos < endPos) && (match != target)) { + beginPos++; + match = attributes[beginPos]; + if(absolute) + match = abs(match); + } + if(match == target) + endPos = beginPos; } /* Return the index if a match was found, or signal failure with a -1 */ @@ -558,7 +569,7 @@ STATIC int searchFor(int target, int *attributes, int size, int offset, MYBOOL a /* Other supporting math routines */ /* ---------------------------------------------------------------------------------- */ -STATIC MYBOOL isINT(lprec *lp, LPSREAL value) +STATIC MYBOOL isINT(lprec *lp, REAL value) { #if 0 return( (MYBOOL) (modf(fabs(value)+lp->epsint, &value) < 2*lp->epsint) ); @@ -566,12 +577,12 @@ STATIC MYBOOL isINT(lprec *lp, LPSREAL value) value = fabs(value)+lp->epsint; return( (MYBOOL) (my_reldiff(value, floor(value)) < 2*lp->epsint) ); #elif 0 - LPSREAL hold; + static REAL hold; value = fabs(value); hold = pow(10, MIN(-2, log10(value+1)+log10(lp->epsint))); return( (MYBOOL) (modf(value+lp->epsint, &value) < 2*hold) ); #elif 0 - value -= (LPSREAL)floor(value); + value -= (REAL)floor(value); return( (MYBOOL) ((value < lp->epsint) || (value > (1 - lp->epsint)) ); #else value += lp->epsint; @@ -584,9 +595,9 @@ STATIC MYBOOL isOrigFixed(lprec *lp, int varno) return( (MYBOOL) (lp->orig_upbo[varno] - lp->orig_lowbo[varno] <= lp->epsmachine) ); } -STATIC void chsign_bounds(LPSREAL *lobound, LPSREAL *upbound) +STATIC void chsign_bounds(REAL *lobound, REAL *upbound) { - LPSREAL temp; + REAL temp; temp = *upbound; if(fabs(*lobound) > 0) *upbound = -(*lobound); @@ -602,15 +613,21 @@ STATIC void chsign_bounds(LPSREAL *lobound, LPSREAL *upbound) /* ---------------------------------------------------------------------------------- */ /* Define randomization routine */ /* ---------------------------------------------------------------------------------- */ -STATIC LPSREAL rand_uniform(lprec *lp, LPSREAL range) +STATIC REAL rand_uniform(lprec *lp, REAL range) { - static MYBOOL randomized = FALSE; /* static ok here for reentrancy/multithreading */ + static MYBOOL randomized = FALSE; if(!randomized) { + GetRNGstate(); +/* Original code: srand((unsigned) time( NULL )); */ randomized = TRUE; - srand((unsigned) time( NULL )); } - range *= (LPSREAL) rand() / (LPSREAL) RAND_MAX; +/* We need to call Put when we're done. So...every time? */ + + range *= (REAL) unif_rand(); + PutRNGstate(); + +/* Original code: range *= (REAL) rand() / (REAL) RAND_MAX; */ return( range ); } @@ -928,12 +945,10 @@ STATIC int compareLink(LLrec *linkmap1, LLrec *linkmap2) int test; test = memcmp(&linkmap1->size, &linkmap2->size, sizeof(int)); - if(test == 0) { + if(test == 0) test = memcmp(&linkmap1->count, &linkmap2->count, sizeof(int)); - } - if(test == 0) { - test = memcmp(linkmap1->map, linkmap2->map, sizeof(int)*(2*linkmap1->size+1)); - } + if(test == 0) + test = memcmp(linkmap1->map, linkmap2->map, sizeof(int)*(2*linkmap1->size+1)); return( test ); } @@ -958,10 +973,10 @@ STATIC MYBOOL verifyLink(LLrec *linkmap, int itemnr, MYBOOL doappend) } /* Packed vector routines */ -STATIC PVrec *createPackedVector(int size, LPSREAL *values, int *workvector) +STATIC PVrec *createPackedVector(int size, REAL *values, int *workvector) { int i, k; - REGISTER LPSREAL ref; + REGISTER REAL ref; PVrec *newPV = NULL; MYBOOL localWV = (MYBOOL) (workvector == NULL); @@ -996,7 +1011,7 @@ STATIC PVrec *createPackedVector(int size, LPSREAL *values, int *workvector) MEMCOPY(newPV->startpos, workvector, k); } newPV->startpos[k] = size + 1; /* Store terminal index + 1 for searching purposes */ - newPV->value = (LPSREAL *) malloc(k*sizeof(*(newPV->value))); + newPV->value = (REAL *) malloc(k*sizeof(*(newPV->value))); /* Fill the values vector before returning */ for(i = 0; i < k; i++) @@ -1005,10 +1020,10 @@ STATIC PVrec *createPackedVector(int size, LPSREAL *values, int *workvector) return( newPV ); } -STATIC MYBOOL unpackPackedVector(PVrec *PV, LPSREAL **target) +STATIC MYBOOL unpackPackedVector(PVrec *PV, REAL **target) { int i, ii, k; - REGISTER LPSREAL ref; + REGISTER REAL ref; /* Test for validity of the target and create it if necessary */ if(target == NULL) @@ -1029,7 +1044,7 @@ STATIC MYBOOL unpackPackedVector(PVrec *PV, LPSREAL **target) return( TRUE ); } -STATIC LPSREAL getvaluePackedVector(PVrec *PV, int index) +STATIC REAL getvaluePackedVector(PVrec *PV, int index) { index = searchFor(index, PV->startpos, PV->count, 0, FALSE); index = abs(index)-1; diff --git a/src/lpsolve/headers/run_headers/lp_utils.h b/src/lpSolve/src/lp_utils.h similarity index 100% rename from src/lpsolve/headers/run_headers/lp_utils.h rename to src/lpSolve/src/lp_utils.h diff --git a/src/lpsolve/build/lp_solve/lp_wlp.c b/src/lpSolve/src/lp_wlp.c similarity index 56% rename from src/lpsolve/build/lp_solve/lp_wlp.c rename to src/lpSolve/src/lp_wlp.c index 00746fbc..da0bda93 100644 --- a/src/lpsolve/build/lp_solve/lp_wlp.c +++ b/src/lpSolve/src/lp_wlp.c @@ -3,7 +3,6 @@ #include #include -#include "commonlib.h" #include "lp_lib.h" #include "lp_scale.h" #include "lp_utils.h" @@ -23,17 +22,15 @@ /* Input and output of lp format model files for lp_solve */ /* ------------------------------------------------------------------------- */ -static int write_data(void *userhandle, write_modeldata_func write_modeldata, char *format, ...) +static void write_data(void *userhandle, write_modeldata_func write_modeldata, char *format, ...) { char buff[DEF_STRBUFSIZE+1]; va_list ap; - int n; va_start(ap, format); vsnprintf(buff, DEF_STRBUFSIZE, format, ap); + write_modeldata(userhandle, buff); va_end(ap); - n = write_modeldata(userhandle, buff); - return(n); } STATIC void write_lpcomment(void *userhandle, write_modeldata_func write_modeldata, char *string, MYBOOL newlinebefore) @@ -41,58 +38,64 @@ STATIC void write_lpcomment(void *userhandle, write_modeldata_func write_modelda write_data(userhandle, write_modeldata, "%s/* %s */\n", (newlinebefore) ? "\n" : "", string); } -STATIC int write_lprow(lprec *lp, int rowno, void *userhandle, write_modeldata_func write_modeldata, int maxlen, int *idx, LPSREAL *val) +STATIC MYBOOL write_lprow(lprec *lp, int rowno, void *userhandle, write_modeldata_func write_modeldata) { - int i, j, nchars, elements; - LPSREAL a; - MYBOOL first = TRUE; - char buf[50]; - - elements = get_rowex(lp, rowno, val, idx); - if(write_modeldata != NULL) { - nchars = 0; - for(i = 0; i < elements; i++) { - j = idx[i]; - if(is_splitvar(lp, j)) + int i, ie, j; + REAL a; + MATrec *mat = lp->matA; + MYBOOL first = TRUE, rowwritten; + + if(rowno == 0) { + i = 1; + ie = lp->columns+1; + } + else { + i = mat->row_end[rowno-1]; + ie = mat->row_end[rowno]; + } + rowwritten = FALSE; + for(; i < ie; i++) { + if(rowno == 0) { + j = i; + a = get_mat(lp, 0, i); + if(a == 0) continue; - a = val[i]; - if(!first) - nchars += write_data(userhandle, write_modeldata, " "); - else - first = FALSE; - sprintf(buf, "%+.12g", (double)a); - if(strcmp(buf, "-1") == 0) - nchars += write_data(userhandle, write_modeldata, "-"); - else if(strcmp(buf, "+1") == 0) - nchars += write_data(userhandle, write_modeldata, "+"); - else - nchars += write_data(userhandle, write_modeldata, "%s ", buf); - nchars += write_data(userhandle, write_modeldata, "%s", get_col_name(lp, j)); - /* Check if we should add a linefeed */ - if((maxlen > 0) && (nchars >= maxlen) && (i < elements-1)) { - write_data(userhandle, write_modeldata, "%s", "\n"); - nchars = 0; - } } + else { + j = ROW_MAT_COLNR(i); + a = ROW_MAT_VALUE(i); + a = my_chsign(is_chsign(lp, rowno), a); + a = unscaled_mat(lp, a, rowno, j); + } + if(is_splitvar(lp, j)) + continue; + if(!first) + write_data(userhandle, write_modeldata, " "); + else + first = FALSE; + if(a == -1) + write_data(userhandle, write_modeldata, "-"); + else if(a == 1) + write_data(userhandle, write_modeldata, "+"); + else + write_data(userhandle, write_modeldata, "%+.12g ", (double)a); + write_data(userhandle, write_modeldata, "%s", get_col_name(lp, j)); + rowwritten = TRUE; } - return(elements); + return(rowwritten); } -#if !defined LP_MAXLINELEN -# define LP_MAXLINELEN 100 -#endif - MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func write_modeldata) { - int i, j, b, - nrows = lp->rows, - ncols = lp->columns, - nchars, maxlen = LP_MAXLINELEN, - *idx; + int i, j, b; MYBOOL ok; - LPSREAL a, *val; + REAL a; char *ptr; + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "LP_writefile: Cannot write to LP file while in row entry mode.\n"); + return(FALSE); + } if(!mat_validate(lp->matA)) { report(lp, IMPORTANT, "LP_writefile: Could not validate the data matrix.\n"); return(FALSE); @@ -114,20 +117,17 @@ MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func wri else write_data(userhandle, write_modeldata, "min: "); - allocREAL(lp, &val, 1 + lp->columns, TRUE); - allocINT(lp, &idx, 1 + lp->columns, TRUE); - - write_lprow(lp, 0, userhandle, write_modeldata, maxlen, idx, val); + write_lprow(lp, 0, userhandle, write_modeldata); a = get_rh(lp, 0); - if(a != 0) + if(a) write_data(userhandle, write_modeldata, " %+.12g", a); write_data(userhandle, write_modeldata, ";\n"); /* Write constraints */ - if(nrows > 0) + if(lp->rows > 0) write_lpcomment(userhandle, write_modeldata, "Constraints", TRUE); - for(j = 1; j <= nrows; j++) { - if(((lp->names_used) && (lp->row_name[j] != NULL)) || (write_lprow(lp, j, userhandle, NULL, maxlen, idx, val) == 1)) + for(j = 1; j <= lp->rows; j++) { + if(lp->names_used && (lp->row_name[j] != NULL)) ptr = get_row_name(lp, j); else ptr = NULL; @@ -148,7 +148,7 @@ MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func wri } #endif - if((!write_lprow(lp, j, userhandle, write_modeldata, maxlen, idx, val)) && (ncols >= 1)) + if((!write_lprow(lp, j, userhandle, write_modeldata)) && (get_Ncolumns(lp) >= 1)) write_data(userhandle, write_modeldata, "0 %s", get_col_name(lp, 1)); if(lp->orig_upbo[j] == 0) @@ -167,13 +167,13 @@ MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func wri #ifdef SingleBoundedRowInLP /* Write the ranged part of the constraint, if specified */ if ((lp->orig_upbo[j]) && (lp->orig_upbo[j] < lp->infinite)) { - if(((lp->names_used) && (lp->row_name[j] != NULL)) || (write_lprow(lp, j, userhandle, NULL, maxlen, idx, val) == 1)) + if(lp->names_used && (lp->row_name[j] != NULL)) ptr = get_row_name(lp, j); else ptr = NULL; if((ptr != NULL) && (*ptr)) write_data(userhandle, write_modeldata, "%s: ", ptr); - if((!write_lprow(lp, j, userhandle, write_modeldata, maxlen, idx, val)) && (get_Ncolumns(lp) >= 1)) + if((!write_lprow(lp, j, userhandle, write_modeldata)) && (get_Ncolumns(lp) >= 1)) write_data(userhandle, write_modeldata, "0 %s", get_col_name(lp, 1)); write_data(userhandle, write_modeldata, " %s %g;\n", (is_chsign(lp, j)) ? "<=" : ">=", @@ -184,57 +184,57 @@ MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func wri /* Write bounds on variables */ ok = FALSE; - for(i = nrows + 1; i <= lp->sum; i++) - if(!is_splitvar(lp, i - nrows)) { + for(i = lp->rows + 1; i <= lp->sum; i++) + if(!is_splitvar(lp, i - lp->rows)) { if(lp->orig_lowbo[i] == lp->orig_upbo[i]) { if(!ok) { - write_lpcomment(userhandle, write_modeldata, "Variable bounds", TRUE); - ok = TRUE; - } - write_data(userhandle, write_modeldata, "%s = %.12g;\n", get_col_name(lp, i - nrows), get_upbo(lp, i - nrows)); + write_lpcomment(userhandle, write_modeldata, "Variable bounds", TRUE); + ok = TRUE; + } + write_data(userhandle, write_modeldata, "%s = %.12g;\n", get_col_name(lp, i - lp->rows), get_upbo(lp, i - lp->rows)); } else { #ifndef SingleBoundedRowInLP if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite)) { if(!ok) { - write_lpcomment(userhandle, write_modeldata, "Variable bounds", TRUE); - ok = TRUE; - } + write_lpcomment(userhandle, write_modeldata, "Variable bounds", TRUE); + ok = TRUE; + } if(lp->orig_lowbo[i] == -lp->infinite) write_data(userhandle, write_modeldata, "-Inf"); else - write_data(userhandle, write_modeldata, "%.12g", get_lowbo(lp, i - nrows)); - write_data(userhandle, write_modeldata, " <= %s <= ", get_col_name(lp, i - nrows)); + write_data(userhandle, write_modeldata, "%.12g", get_lowbo(lp, i - lp->rows)); + write_data(userhandle, write_modeldata, " <= %s <= ", get_col_name(lp, i - lp->rows)); if(lp->orig_lowbo[i] == lp->infinite) write_data(userhandle, write_modeldata, "+Inf"); else - write_data(userhandle, write_modeldata, "%.12g", get_upbo(lp, i - nrows)); + write_data(userhandle, write_modeldata, "%.12g", get_upbo(lp, i - lp->rows)); write_data(userhandle, write_modeldata, ";\n"); - } + } else #endif { if(lp->orig_lowbo[i] != 0) { if(!ok) { - write_lpcomment(userhandle, write_modeldata, "Variable bounds", TRUE); - ok = TRUE; - } - if(lp->orig_lowbo[i] == -lp->infinite) - write_data(userhandle, write_modeldata, "%s >= -Inf;\n", get_col_name(lp, i - nrows)); - else if(lp->orig_lowbo[i] == lp->infinite) - write_data(userhandle, write_modeldata, "%s >= +Inf;\n", get_col_name(lp, i - nrows)); - else + write_lpcomment(userhandle, write_modeldata, "Variable bounds", TRUE); + ok = TRUE; + } + if(lp->orig_lowbo[i] == -lp->infinite) + write_data(userhandle, write_modeldata, "%s >= -Inf;\n", get_col_name(lp, i - lp->rows)); + else if(lp->orig_lowbo[i] == lp->infinite) + write_data(userhandle, write_modeldata, "%s >= +Inf;\n", get_col_name(lp, i - lp->rows)); + else write_data(userhandle, write_modeldata, "%s >= %.12g;\n", - get_col_name(lp, i - nrows), get_lowbo(lp, i - nrows)); - } - if(lp->orig_upbo[i] != lp->infinite) { + get_col_name(lp, i - lp->rows), get_lowbo(lp, i - lp->rows)); + } + if(lp->orig_upbo[i] != lp->infinite) { if(!ok) { - write_lpcomment(userhandle, write_modeldata, "Variable bounds", TRUE); - ok = TRUE; - } + write_lpcomment(userhandle, write_modeldata, "Variable bounds", TRUE); + ok = TRUE; + } write_data(userhandle, write_modeldata, "%s <= %.12g;\n", - get_col_name(lp, i - nrows), get_upbo(lp, i - nrows)); - } + get_col_name(lp, i - lp->rows), get_upbo(lp, i - lp->rows)); + } } } } @@ -243,19 +243,14 @@ MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func wri if(lp->int_vars > 0) { write_lpcomment(userhandle, write_modeldata, "Integer definitions", TRUE); i = 1; - while((i <= ncols) && !is_int(lp, i)) + while(i <= lp->columns && !is_int(lp, i)) i++; - if(i <= ncols) { - nchars = write_data(userhandle, write_modeldata, "int %s", get_col_name(lp, i)); + if(i <= lp->columns) { + write_data(userhandle, write_modeldata, "int %s", get_col_name(lp, i)); i++; - for(; i <= ncols; i++) - if((!is_splitvar(lp, i)) && (is_int(lp, i))) { - if((maxlen!= 0) && (nchars > maxlen)) { - write_data(userhandle, write_modeldata, "%s", "\n"); - nchars = 0; - } + for(; i <= lp->columns; i++) + if((!is_splitvar(lp, i)) && (is_int(lp, i))) write_data(userhandle, write_modeldata, ",%s", get_col_name(lp, i)); - } write_data(userhandle, write_modeldata, ";\n"); } } @@ -264,19 +259,14 @@ MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func wri if(lp->sc_vars > 0) { write_lpcomment(userhandle, write_modeldata, "Semi-continuous variables", TRUE); i = 1; - while((i <= ncols) && !is_semicont(lp, i)) + while(i <= lp->columns && !is_semicont(lp, i)) i++; - if(i <= ncols) { - nchars = write_data(userhandle, write_modeldata, "sec %s", get_col_name(lp, i)); + if(i <= lp->columns) { + write_data(userhandle, write_modeldata, "sec %s", get_col_name(lp, i)); i++; - for(; i <= ncols; i++) - if((!is_splitvar(lp, i)) && (is_semicont(lp, i))) { - if((maxlen != 0) && (nchars > maxlen)) { - write_data(userhandle, write_modeldata, "%s", "\n"); - nchars = 0; - } - nchars += write_data(userhandle, write_modeldata, ",%s", get_col_name(lp, i)); - } + for(; i <= lp->columns; i++) + if((!is_splitvar(lp, i)) && (is_semicont(lp, i))) + write_data(userhandle, write_modeldata, ",%s", get_col_name(lp, i)); write_data(userhandle, write_modeldata, ";\n"); } } @@ -285,37 +275,28 @@ MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func wri if(SOS_count(lp) > 0) { SOSgroup *SOS = lp->SOS; write_lpcomment(userhandle, write_modeldata, "SOS definitions", TRUE); - write_data(userhandle, write_modeldata, "SOS\n"); for(b = 0, i = 0; i < SOS->sos_count; b = SOS->sos_list[i]->priority, i++) { - nchars = write_data(userhandle, write_modeldata, "%s: ", + write_data(userhandle, write_modeldata, "SOS\n%s: ", (SOS->sos_list[i]->name == NULL) || (*SOS->sos_list[i]->name==0) ? "SOS" : SOS->sos_list[i]->name); /* formatnumber12((double) lp->sos_list[i]->priority) */ - for(a = 0.0, j = 1; j <= SOS->sos_list[i]->size; a = SOS->sos_list[i]->weights[j], j++) { - if((maxlen != 0) && (nchars > maxlen)) { - write_data(userhandle, write_modeldata, "%s", "\n"); - nchars = 0; - } + for(a = 0.0, j = 1; j <= SOS->sos_list[i]->size; a = SOS->sos_list[i]->weights[j], j++) if(SOS->sos_list[i]->weights[j] == ++a) - nchars += write_data(userhandle, write_modeldata, "%s%s", + write_data(userhandle, write_modeldata, "%s%s", (j > 1) ? "," : "", get_col_name(lp, SOS->sos_list[i]->members[j])); else - nchars += write_data(userhandle, write_modeldata, "%s%s:%.12g", + write_data(userhandle, write_modeldata, "%s%s:%.12g", (j > 1) ? "," : "", get_col_name(lp, SOS->sos_list[i]->members[j]), - SOS->sos_list[i]->weights[j]); - } + SOS->sos_list[i]->weights[j]); if(SOS->sos_list[i]->priority == ++b) - nchars += write_data(userhandle, write_modeldata, " <= %d;\n", SOS->sos_list[i]->type); + write_data(userhandle, write_modeldata, " <= %d;\n", SOS->sos_list[i]->type); else - nchars += write_data(userhandle, write_modeldata, " <= %d:%d;\n", SOS->sos_list[i]->type, SOS->sos_list[i]->priority); + write_data(userhandle, write_modeldata, " <= %d:%d;\n", SOS->sos_list[i]->type, SOS->sos_list[i]->priority); } } - FREE(val); - FREE(idx); - ok = TRUE; return(ok); @@ -323,26 +304,22 @@ MYBOOL __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func wri static int __WINAPI write_lpdata(void *userhandle, char *buf) { - return(fprintf((FILE *) userhandle, "%s", buf)); + fputs(buf, (FILE *) userhandle); + return(TRUE); } MYBOOL LP_writefile(lprec *lp, char *filename) { - FILE *output = stdout; + FILE *output; /* = stdout; */ MYBOOL ok; - if (filename != NULL) { - ok = (MYBOOL) ((output = fopen(filename, "w")) != NULL); - if(!ok) - return(ok); - } - else - output = lp->outstream; + ok = ((output = fopen(filename, "w")) != NULL); + if(!ok) + return(ok); ok = write_lpex(lp, (void *) output, write_lpdata); - if (filename != NULL) - fclose(output); + fclose(output); return(ok); } @@ -351,8 +328,7 @@ MYBOOL LP_writehandle(lprec *lp, FILE *output) { MYBOOL ok; - if (output != NULL) - set_outputstream(lp, output); + set_outputstream(lp, output); output = lp->outstream; diff --git a/src/lpsolve/headers/include/lp_wlp.h b/src/lpSolve/src/lp_wlp.h similarity index 100% rename from src/lpsolve/headers/include/lp_wlp.h rename to src/lpSolve/src/lp_wlp.h diff --git a/src/lpsolve/headers/run_headers/lpkit.h b/src/lpSolve/src/lpkit.h similarity index 100% rename from src/lpsolve/headers/run_headers/lpkit.h rename to src/lpSolve/src/lpkit.h diff --git a/src/lpSolve/src/lpslink56.c b/src/lpSolve/src/lpslink56.c new file mode 100644 index 00000000..8f9b998f --- /dev/null +++ b/src/lpSolve/src/lpslink56.c @@ -0,0 +1,633 @@ +/* +** lpslink.c: function to link lpsolve with Excel, S-Plus or R. +*/ +/* +** In addition to standard "include" files we need lpkit.h, supplied by +** lpsolve. This gives definitions for (for example) the "lprec" structure. +*/ +#include +#include +#include +#include +#include "lp_lib.h" +#include + +/* +** In R "integers" get passed as longs, whereas in S-Plus they're ints. +** So the user should turn on this flag to compile for R. +*/ + +#ifdef BUILDING_FOR_R +#define LONG_OR_INT int +#else +#define LONG_OR_INT long +#endif + +/* +** The declaration of the link function. +*/ + +void lpslink (LONG_OR_INT *direction, /* 1 for max, 0 for min */ + LONG_OR_INT *x_count, /* Number of x's */ + double *objective, /* Objective function */ + LONG_OR_INT *const_count, /* Number of constraints */ + double *constraints, /* Has extra element on front */ + LONG_OR_INT *int_count, /* Number of integer variables */ + LONG_OR_INT *int_vec, /* Indices of int. variables */ + LONG_OR_INT *bin_count, /* Number of binary variables */ + LONG_OR_INT *bin_vec, /* Indices of bin. variables */ + LONG_OR_INT *num_bin_solns, /* # of all-bin solns to find */ + double *obj_val, /* Objective function value */ + double *solution, /* Result of call */ + LONG_OR_INT *presolve, /* Value of presolve */ + LONG_OR_INT *compute_sens, /* Want sensitivity? */ + double *sens_coef_from, /* Sens. coef. lower limit */ + double *sens_coef_to, /* Sens. coef. upper limit */ + double *duals, /* Dual values */ + double *duals_from, /* Lower limit dual values */ + double *duals_to, /* Lower limit dual values */ + LONG_OR_INT *scale, /* lpsolve scaling parameter */ + LONG_OR_INT *use_dense, /* Use dense constraint matrix?*/ + LONG_OR_INT *dense_col, /* Dense constraint columns */ + double *dense_val, /* Dense constraint values */ + LONG_OR_INT *dense_mat_nrow, /* Dense constraint #entries */ + double *dense_ctr, /* Dense constraint info */ + LONG_OR_INT *use_rw_file, /* See notes below */ + char **rw_file, /* See notes below */ + LONG_OR_INT *status, /* Holds return value */ + LONG_OR_INT *timeout); /* timeout in seconds */ + +/* +** Some globals for calling from VBScript. The caller will call lpslink, +*/ +static long vb_direction; +static long vb_x_count; +static double *vb_objective; +static long vb_const_count; +static double *vb_constraints; +static long vb_int_count; +static long *vb_int_vec; +static double vb_obj_val; +static double *vb_solution; + +/* +**************************** lps_vb_setup ***************************** +** +** +** lps_vb_setup: set up an lp problem (that is, allocate necessary space) +*/ + +long lps_vb_setup (long direction, /* Optimization direction (1 = max) */ + long x_count, /* Number of variables */ + long const_count, /* Number of constraints */ + long int_count) /* Number of integer variables */ +{ +long i; /* iteration variable */ + +/* +** Set globals (which start "vb") from parameters (which do not). +*/ + +vb_direction = direction; +vb_x_count = x_count; +vb_const_count = const_count; +vb_int_count = int_count; + +/* +** Allocate objective (which holds the coefficients in the objective function). +** We need an extra element at the front. If malloc() fails, get out. +*/ +vb_objective = (double *) malloc (1 + sizeof (double) * vb_x_count); +if (vb_objective == (double *) NULL) + return (-1); + +vb_objective[0] = 0.0; + +/* +** Allocate constraints. This, too, gets an extra entry. If the allocation +** fails, exit gracefully. +*/ + +vb_constraints = (double *) malloc (sizeof (double) * + (1 + vb_const_count * (vb_x_count + 2))); + +if (vb_constraints == (double *) NULL) +{ + free (vb_objective); + return (-1); +} + +vb_constraints[0] = 0.0; + +/* +** If any variables are constrained to be integers, allocate one long +** for each such variable. Quit if the allocation fails. If it succeeds, +** put zeros in there. We will insert the proper numbers later. +*/ + +if (vb_int_count > 0) { + vb_int_vec = (long *) malloc (1 + sizeof (long) * vb_int_count); + if (vb_int_vec == (long *) NULL) + { + free (vb_objective); + free (vb_constraints); + return (-1); + } + + for (i = 0L; i <= vb_int_count; i++) /* there's one extra */ + vb_int_vec[i] = 0L; +} + +/* +** Allocate space for the solution. This will hold the optimal values for each +** coefficient. If the allocation fails, quit. +*/ + +vb_solution = (double *) malloc (sizeof (double) * vb_x_count); +if (vb_solution == (double *) NULL) +{ + free (vb_objective); + free (vb_constraints); + if (vb_int_count > 0) + free (vb_int_vec); + return (-1); +} +/* +** Our work here is done. +*/ +return (0); +} /* end lps_vb_setup */ + +/***************************** lps_vb_set_element ********************/ + +long lps_vb_set_element (long type, /* Place to do the setting */ + long row, /* Row in which to set */ + long column, /* Column in which to set */ + double value) /* Value to set */ +{ +/* +** This function allows us to set an element of the lp. If "type" = 1, +** we ignore column and set the "row-th" element of vb_objective to "value." +** If type is 2, set the row, column-th element of constraints (allowing for +** the funny layout) to value; if type = 3, set the rowth variable to integer. +*/ + + if (type == 1) + vb_objective[row] = value; + if (type == 2) { + vb_constraints[(row - 1) * (vb_x_count + 2) + column] = value; + } + if (type == 3 && vb_int_count > 0) + vb_int_vec[row] = floor (value + 0.5); + return (1); +} + +/***************************** lps_vb_get_element ********************/ +double lps_vb_get_element (long type, /* Place to get from */ + long row, /* Row to get from */ + long column) /* Column to get from (unused) */ +{ +/* +** Get an element. If type is 1, get the objective value; if type is 2, +** get the rowth element of the solution. "Column" is currently unused. +*/ + if (type == 1) + return (vb_obj_val); + if (type == 2) + return (vb_solution[row]); + return (0.0); +} + +/* +********************************* lps_vb_cleanup ************************* +** +** lps_vb_cleanup: free all the things allocated in lps_vb_setup. +*/ +long lps_vb_cleanup (long unused) +{ + free (vb_objective); + free (vb_constraints); + free (vb_int_vec); + free (vb_solution); + return (0); +} + +/******************************** lpslink ************************************/ + +void lpslink (LONG_OR_INT *direction, /* 1 for max, 0 for min */ + LONG_OR_INT *x_count, /* Number of x's */ + double *objective, /* Objective function */ + LONG_OR_INT *const_count, /* Number of constraints */ + double *constraints, /* Has extra element on front */ + LONG_OR_INT *int_count, /* Number of integer variables */ + LONG_OR_INT *int_vec, /* Indices of int. variables */ + LONG_OR_INT *bin_count, /* Number of binary variables */ + LONG_OR_INT *bin_vec, /* Indices of bin. variables */ + LONG_OR_INT *num_bin_solns, /* # of all-bin solns to find */ + double *obj_val, /* Objective function value */ + double *solution, /* Result of call */ + LONG_OR_INT *presolve, /* Value of presolve */ + LONG_OR_INT *compute_sens, /* Want sensitivity? */ + double *sens_coef_from, /* Sens. coef. lower limit */ + double *sens_coef_to, /* Sens. coef. upper limit */ + double *duals, /* Dual values */ + double *duals_from, /* Lower limit dual values */ + double *duals_to, /* Lower limit dual values */ + LONG_OR_INT *scale, /* lpsolve scaling parameter */ + LONG_OR_INT *use_dense, /* Use dense const. mat? */ + LONG_OR_INT *dense_col, /* Dense constraint column */ + double *dense_val, /* Dense constraint value */ + LONG_OR_INT *dense_mat_nrow, /* Dense constraint #entries */ + double *dense_ctr, /* Dense constraint info */ + LONG_OR_INT *use_rw_file, /* See notes below */ + char **rw_file, /* See notes below */ + LONG_OR_INT *status, /* Holds return value */ + LONG_OR_INT *timeout) /* timeout in seconds */ +{ +/* +** This is the function called from the outside. +*/ +/* +** "rw file" notes: to get around some sort of a bug in lpSolve, we allow +** callers to set "use_rw_file" to TRUE and pass a file name in rw_file. +** This only makes sense in the case where all the decision variables are +** binary and num_bin_solns > 1. Then instead of just adding constraints to +** lp and re-solving, we write the lp out to the rw_file, delete the lp, and +** read the file back in every time. It's costly, but what can you do? +*/ + +int i = 0, j, /* Iteration variables */ + result; /* Holds result of calls */ + +int dctr_ctr, /* Holds our spot in the dense_ctr matrix */ + dmat_ctr, /* Holds the current row of the dense_mat matrix */ + d_num; /* Number of non-zero entries in this dense constraint */ + +double *const_ptr; /* Points to a constraint */ + +double *last_soln; /* Points to last solution (for multiple soln case) */ +double *new_ptr; /* Point to a bit of "solution" 4 building constraint */ +int soln_ctr; /* Which solution are we on? */ + +double new_rhs; /* RHS value for new constraint. */ +LONG_OR_INT + new_status; /* Status for calls to "solve" after the first. */ + +lprec *lp; /* Structure to hold the lp */ + +FILE *filex_file; /* Points to rw_file once that's been opened */ + +/* +** Make an empty lp with x_count variables. If it fails, return. +*/ +lp = make_lp ((int) 0, *x_count); + +if (lp == (lprec *) NULL) + return; + +set_verbose (lp, 1); /* CRITICAL */ + +/* Set the direction. The default is minimize, but set it anyway. */ +if (*direction == 1) + set_maxim (lp); +else + set_minim (lp); +/* +** "Objective" is a vector. Set the objective function. Return on fail. +*/ +result = set_obj_fn (lp, objective); +if (result == 0) + return; + +set_add_rowmode (lp, TRUE); /* Put problem into "row mode" */ + +/* +** If there are any constraints, see if they're dense or regular. +*/ +if ((int) *const_count > 0) { + if (*use_dense) { +/* +** For dense constraints, dense_ctr holds sets of three entries (# of non- +** zero entries, direction, and rhs). Once we know the number of non-zero +** entries, we get them out of dense_const and use that information to fill +** up a row which we dispatch with add_constraintex. +*/ + dctr_ctr = 0; + dmat_ctr = 0; + for (i = 0; i < (int) *const_count; i++) + { + d_num = (int) dense_ctr[dctr_ctr]; +/* +** The args. to add_constraintex are the lp, the number of non-zero entries, +** a pointer to the values, an int pointer to the column numbers associated with +** the values, the constraint type (<, = >) and the constraint's right side. +*/ + add_constraintex (lp, d_num, + (double *) &(dense_val[dmat_ctr]), + (int *) &(dense_col[dmat_ctr]), + (int) dense_ctr[dctr_ctr + 1], dense_ctr[dctr_ctr + 2]); + dctr_ctr += 3; + dmat_ctr += d_num; + } +/* Maybe replace this with set_row, set_rh_vec, set_constr_type? */ + } + else { +/* +** If we're using regular constaints, point "constr_ptr" at the first one. +*/ + const_ptr = constraints; +/* +** Add constraints, one at a time; then move constr_ptr up. +*/ + + for (i = 0; i < (int) *const_count; i++) + { + add_constraint (lp, const_ptr, + (short) const_ptr[(int) (*x_count) + 1], + const_ptr[(int) (*x_count) + 2]); + const_ptr += (int) *x_count + 2; + } + } /* end "else" (i.e. if these are regular, non-dense constraints.) */ +} /* end "if there are any constraints. */ + +set_add_rowmode (lp, FALSE); /* Take problem out of "row mode" */ + +/* +** Set integer and binary variables. +*/ +if (*int_count > 0) { + for (i = 0; i < (int) *int_count; i++) + set_int (lp, (int) (int_vec[i]), TRUE); +} +if (*bin_count > 0) { + for (i = 0; i < (int) *bin_count; i++) + set_binary (lp, (int) (bin_vec[i]), TRUE); +} + +/* +** Presolve if needed (that is, if we are going to want sensitivity in +** an integer program) then solve the lp. If "status" is non-zero, return. +*/ + +if (*compute_sens > 0) { + if (*int_count > 0) + set_presolve (lp, PRESOLVE_SENSDUALS, get_presolveloops (lp)); + } + + +if (timeout[0] > 0) set_timeout(lp, timeout[0]); + +set_scaling (lp, *scale); +*status = (LONG_OR_INT) solve (lp); + +if ((int) *status != 0) { + delete_lp (lp); + return; +} + +/* Now get the sensitivities, if requested. */ +if (*compute_sens > 0) { + get_sensitivity_obj (lp, sens_coef_from, sens_coef_to); + get_sensitivity_rhs (lp, duals, duals_from, duals_to); +} + +/* +** We've succeeded. Extract the objective function's value and +** the values of the variables. +*/ + +*obj_val = get_objective (lp); + +get_variables (lp, solution); + +/* +** If this is an all-binary problem, and more than one solution has +** been asked for, let's get those other solutions. We do that in +** this way. First, add a constraint requiring that the objective function +** not move below *obj_val, if this is a maximization problem, or above +** it, if this is minimization. We need only do this once. +*/ +soln_ctr = 1; +if (*num_bin_solns > 1) { + add_constraint (lp, objective, + (*direction == 1) ? ROWTYPE_GE : ROWTYPE_LE, *obj_val); + + +/* ================================================== +** ------------------ WHILE LOOP -------------------- +** ================================================== +** Loop until new status isn't 0 or we've produced all solutions asked for. +*/ + new_status = 0; + while (new_status == 0 && soln_ctr < *num_bin_solns) { +/* +** Point to the most recent solution and space in "solution" that we'll +** use to build the new constraint. +*/ + last_soln = &(solution[(soln_ctr - 1) * *x_count]); + new_ptr = last_soln + *x_count; /* Move up *x_count elements */ +/* +** Now we need to add one new constraint. We go through every element of +** the most recent solution. For every element that's a 1 in the solution, +** put in a 1 in the constraint. For every 0, put a -1 in the constraint. +** The rhs is the # of 1's minus 1, and the sign is <. So if there were +** five variables and the last solution was (1, 1, 1, 0, 0), the new +** constraint would say x1 + x2 + x3 -x4 -x5 < 2. +*/ + new_rhs = 0; + new_ptr[0] = 0; + for (j = 0; j < *x_count; j++) { + new_ptr[j + 1] = 2 * last_soln[j] - 1; /* new_ptr is one-based */ + new_rhs += last_soln[j]; + } + + if (*use_rw_file) { + filex_file = fopen (*rw_file, "w"); + write_LP (lp, filex_file); + delete_lp (lp); + fclose (filex_file); + filex_file = fopen (*rw_file, "r"); + lp = read_lp (filex_file, CRITICAL, (char *) NULL); + fclose (filex_file); + } + + result = add_constraint (lp, new_ptr, ROWTYPE_LE, new_rhs - 1); + + set_scaling (lp, *scale); + new_status = (LONG_OR_INT) solve (lp); + + if (new_status != 0) { + *num_bin_solns = soln_ctr; + return; + } + soln_ctr ++; +/* +** Fetch solution into new_ptr, then transfer just *x_count entries. +*/ + result = get_variables (lp, new_ptr); + + } /* end "while" */ + + *num_bin_solns = soln_ctr; + +} /* end "multiple solutions */ + + +/* +** +*/ + +/* +** Free up the memory and return. +*/ + +delete_lp (lp); + +return; + +} /* end "lpslink" */ + +/* +****************************** lp_transbig ************************ +** +** This function handles "big" transportation problem. It takes in +** the number of rows and columns, the cost matrix, and the signs and +** right-hand sides of the constraints and builds everything else on the +** fly. +*/ +void lp_transbig (LONG_OR_INT *direction, /* 1 for max, 0 for min */ + LONG_OR_INT *r_count, /* Number of rows */ + LONG_OR_INT *c_count, /* Number of columns */ + double *costs, /* Objective function */ + LONG_OR_INT *r_signs, /* Signs of row constraints */ + double *r_rhs, /* RHS of row constraints */ + LONG_OR_INT *c_signs, /* Signs of col constraints */ + double *c_rhs, /* RHS of col constraints */ + double *obj_val, /* Objective function value */ + LONG_OR_INT *int_count, /* How many vars are integers?*/ + LONG_OR_INT *integers, /* Which vars. are integer? */ + double *solution, /* Result of call */ + LONG_OR_INT *presolve, /* Value of presolve */ + LONG_OR_INT *compute_sens, /* Want sensitivity? */ + double *sens_coef_from, /* Sens. coef. lower limit */ + double *sens_coef_to, /* Sens. coef. upper limit */ + double *duals, /* Dual values */ + double *duals_from, /* Lower limit dual values */ + double *duals_to, /* Lower limit dual values */ + LONG_OR_INT *status) /* Holds return value */ +{ +long i; /* Iteration variable */ +long result; /* Holds result of calls */ +long this_element; /* Which are we looking at? */ +lprec *lp; /* Structure to hold the lp */ +double *row_vals; /* Holds the values for row-type constraints */ +int *col_inds; /* Holds locations for col-type constraints */ +double *col_vals; /* Holds the values for col-type constraints */ +int *row_inds; /* Holds locations for row-type constraints */ + +long col_ind_ctr, row_ind_ctr; +long rc = *r_count, cc = *c_count; + +/* +** Make an empty lp with r_count x c_count variables. If it fails, return. +*/ +lp = make_lp ((int) 0, *r_count * *c_count); + +if (lp == (lprec *) NULL) + return; + +set_verbose (lp, 1); /* CRITICAL */ + +set_add_rowmode (lp, TRUE); +/* +** "Costs" is already a vector. Set the objective function. Return on fail. +*/ +result = set_obj_fn (lp, costs); +if (result == 0) + return; + +/* Set the direction. The default is minimize, but set it anyway. */ +if (*direction == 1) + set_maxim (lp); +else + set_minim (lp); + +/* +** Add constraints. There are r_count row-type constraints, plus c_count +** col_type constraints. +*/ +row_vals = calloc (cc, sizeof (double)); +col_inds = calloc (cc, sizeof (int)); + +for (row_ind_ctr = 0L; row_ind_ctr < rc; row_ind_ctr++) +{ + for (col_ind_ctr = 0; col_ind_ctr < cc; col_ind_ctr++) { + row_vals[col_ind_ctr] = 1.0; + this_element = 1 + (col_ind_ctr * rc) + row_ind_ctr; + col_inds[col_ind_ctr] = this_element; + } + add_constraintex (lp, cc, row_vals, col_inds, r_signs[row_ind_ctr], r_rhs[row_ind_ctr]); +} + +free (row_vals); +free (col_inds); + +col_vals = calloc (rc, sizeof (double)); +row_inds = calloc (rc, sizeof (int)); + +for (col_ind_ctr = 0L; col_ind_ctr < cc; col_ind_ctr++) +{ + for (row_ind_ctr = 0; row_ind_ctr < rc; row_ind_ctr++) { + col_vals[row_ind_ctr] = 1.0; + this_element = 1 + row_ind_ctr + col_ind_ctr * rc; + row_inds[row_ind_ctr] = this_element; + } + add_constraintex (lp, rc, col_vals, row_inds, c_signs[col_ind_ctr], c_rhs[col_ind_ctr]); +} +free (col_vals); +free (row_inds); + +set_add_rowmode (lp, FALSE); + +/* +** Set integers. +*/ +if (*int_count > 0) + for (i = 0; i < *int_count; i++) + set_int (lp, integers[i], 1); /* Variable in ith element of integers */ + +if (*compute_sens > 0) { + set_presolve (lp, PRESOLVE_SENSDUALS, 10); +} + +*status = (LONG_OR_INT) solve (lp); + +if ((int) *status != 0) { + return; +} + +/* Now get the sensitivities, if requested. */ +if (*compute_sens > 0) { + get_sensitivity_obj (lp, sens_coef_from, sens_coef_to); + get_sensitivity_rhs (lp, duals, duals_from, duals_to); +} + +/* +** We've succeeded. Extract the objective function's value and +** the values of the variables. +*/ + +*obj_val = get_objective (lp); + +get_variables (lp, solution); + +/* +** +*/ + +/* +** Free up the memory and return. +*/ + +delete_lp (lp); +} diff --git a/src/lpsolve/headers/run_headers/lpsolve.h b/src/lpSolve/src/lpsolve.h similarity index 100% rename from src/lpsolve/headers/run_headers/lpsolve.h rename to src/lpSolve/src/lpsolve.h diff --git a/src/lpsolve/build/lp_solve/lusol.c b/src/lpSolve/src/lusol.c similarity index 91% rename from src/lpsolve/build/lp_solve/lusol.c rename to src/lpSolve/src/lusol.c index 5573fe82..7382b45a 100644 --- a/src/lpsolve/build/lp_solve/lusol.c +++ b/src/lpSolve/src/lusol.c @@ -53,16 +53,23 @@ #include "mex.h" #endif -#ifdef FORTIFY -# include "lp_fortify.h" -#endif - /* LUSOL Object creation and destruction */ void *clean_realloc(void *oldptr, int width, int newsize, int oldsize) { newsize *= width; oldsize *= width; + /* this works around valgrind reporting a realloc(3) call with size = 0. + According to https://linux.die.net/man/3/realloc glibc frees the + memory in this case, and (maybe?) returns NULL: + > if size is equal to zero, and ptr is not NULL, then the call + > is equivalent to free(ptr). */ +#ifdef __linux__ + if (oldptr != NULL && newsize == 0) { + free(oldptr); + return NULL; + } +#endif oldptr = LUSOL_REALLOC(oldptr, newsize); if(newsize > oldsize) /* MEMCLEAR(oldptr+oldsize, newsize-oldsize); */ @@ -84,7 +91,7 @@ MYBOOL LUSOL_realloc_a(LUSOLrec *LUSOL, int newsize) if(oldsize > 0) oldsize++; - LUSOL->a = (LPSREAL *) clean_realloc(LUSOL->a, sizeof(*(LUSOL->a)), + LUSOL->a = (REAL *) clean_realloc(LUSOL->a, sizeof(*(LUSOL->a)), newsize, oldsize); LUSOL->indc = (int *) clean_realloc(LUSOL->indc, sizeof(*(LUSOL->indc)), newsize, oldsize); @@ -167,7 +174,7 @@ MYBOOL LUSOL_realloc_r(LUSOLrec *LUSOL, int newsize) if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TRP) #endif { - LUSOL->amaxr = (LPSREAL *) clean_realloc(LUSOL->amaxr, sizeof(*(LUSOL->amaxr)), + LUSOL->amaxr = (REAL *) clean_realloc(LUSOL->amaxr, sizeof(*(LUSOL->amaxr)), newsize, oldsize); if((newsize > 0) && (LUSOL->amaxr == NULL)) return( FALSE ); @@ -203,10 +210,10 @@ MYBOOL LUSOL_realloc_c(LUSOLrec *LUSOL, int newsize) newsize, oldsize); LUSOL->locc = (int *) clean_realloc(LUSOL->locc, sizeof(*(LUSOL->locc)), newsize, oldsize); - LUSOL->w = (LPSREAL *) clean_realloc(LUSOL->w, sizeof(*(LUSOL->w)), + LUSOL->w = (REAL *) clean_realloc(LUSOL->w, sizeof(*(LUSOL->w)), newsize, oldsize); #ifdef LUSOLSafeFastUpdate - LUSOL->vLU6L = (LPSREAL *) clean_realloc(LUSOL->vLU6L, sizeof(*(LUSOL->vLU6L)), + LUSOL->vLU6L = (REAL *) clean_realloc(LUSOL->vLU6L, sizeof(*(LUSOL->vLU6L)), newsize, oldsize); #else LUSOL->vLU6L = LUSOL->w; @@ -219,7 +226,7 @@ MYBOOL LUSOL_realloc_c(LUSOLrec *LUSOL, int newsize) #ifndef ClassicHamaxR if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TCP) { - LUSOL->Ha = (LPSREAL *) clean_realloc(LUSOL->Ha, sizeof(*(LUSOL->Ha)), + LUSOL->Ha = (REAL *) clean_realloc(LUSOL->Ha, sizeof(*(LUSOL->Ha)), newsize, oldsize); LUSOL->Hj = (int *) clean_realloc(LUSOL->Hj, sizeof(*(LUSOL->Hj)), newsize, oldsize); @@ -232,7 +239,7 @@ MYBOOL LUSOL_realloc_c(LUSOLrec *LUSOL, int newsize) #endif #ifndef ClassicdiagU if(LUSOL->luparm[LUSOL_IP_KEEPLU] == FALSE) { - LUSOL->diagU = (LPSREAL *) clean_realloc(LUSOL->diagU, sizeof(*(LUSOL->diagU)), + LUSOL->diagU = (REAL *) clean_realloc(LUSOL->diagU, sizeof(*(LUSOL->diagU)), newsize, oldsize); if((newsize > 0) && (LUSOL->diagU == NULL)) return( FALSE ); @@ -306,7 +313,7 @@ char *LUSOL_pivotLabel(LUSOLrec *LUSOL) void LUSOL_setpivotmodel(LUSOLrec *LUSOL, int pivotmodel, int initlevel) { - LPSREAL newFM, newUM; + REAL newFM, newUM; /* Set pivotmodel if specified */ if(pivotmodel > LUSOL_PIVMOD_NOCHANGE) { @@ -357,9 +364,7 @@ void LUSOL_setpivotmodel(LUSOLrec *LUSOL, int pivotmodel, int initlevel) MYBOOL LUSOL_tightenpivot(LUSOLrec *LUSOL) { -#if 0 - LPSREAL newvalue; -#endif +/* REAL newvalue; */ /* Give up tightening if we are already less than limit and we cannot change strategy */ if(MIN(LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij], @@ -395,7 +400,7 @@ MYBOOL LUSOL_addSingularity(LUSOLrec *LUSOL, int singcol, int *inform) if((NSING > 0) && (NSING >= ASING)) { /* Increase list in "reasonable" steps */ - ASING += (int) (10.0 * (log10((LPSREAL) LUSOL->m)+1.0)); + ASING += (int) (10.0 * (log10((REAL) LUSOL->m)+1.0)); LUSOL->isingular = (int *) LUSOL_REALLOC(LUSOL->isingular, sizeof(*LUSOL->isingular)*(ASING+1)); if(LUSOL->isingular == NULL) { LUSOL->luparm[LUSOL_IP_SINGULARLISTSIZE] = 0; @@ -529,7 +534,7 @@ void LUSOL_clear(LUSOLrec *LUSOL, MYBOOL nzonly) } } -MYBOOL LUSOL_assign(LUSOLrec *LUSOL, int iA[], int jA[], LPSREAL Aij[], int nzcount, MYBOOL istriplet) +MYBOOL LUSOL_assign(LUSOLrec *LUSOL, int iA[], int jA[], REAL Aij[], int nzcount, MYBOOL istriplet) { int k, m, n, ij, kol; @@ -578,7 +583,7 @@ MYBOOL LUSOL_assign(LUSOLrec *LUSOL, int iA[], int jA[], LPSREAL Aij[], int nzco return( TRUE ); } -int LUSOL_loadColumn(LUSOLrec *LUSOL, int iA[], int jA, LPSREAL Aij[], int nzcount, int offset1) +int LUSOL_loadColumn(LUSOLrec *LUSOL, int iA[], int jA, REAL Aij[], int nzcount, int offset1) { int i, ii, nz, k; @@ -622,32 +627,31 @@ void LUSOL_free(LUSOLrec *LUSOL) unload_BLAS(); LUSOL_FREE(LUSOL); } +#include +#include + void LUSOL_report(LUSOLrec *LUSOL, int msglevel, char *format, ...) { va_list ap; + va_start(ap, format); if(LUSOL == NULL) { - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); + REvprintf(format, ap); } else if(msglevel >= 0 /*LUSOL->luparm[2]*/) { if(LUSOL->writelog != NULL) { char buff[255]; - va_start(ap, format); - vsprintf(buff, format, ap); - va_end(ap); + vsnprintf(buff, sizeof(buff), format, ap); LUSOL->writelog(LUSOL, LUSOL->loghandle, buff); } if(LUSOL->outstream != NULL) { - va_start(ap, format); vfprintf(LUSOL->outstream, format, ap); - va_end(ap); fflush(LUSOL->outstream); } } + va_end(ap); } void LUSOL_timer(LUSOLrec *LUSOL, int timerid, char *text) @@ -664,10 +668,10 @@ int LUSOL_factorize(LUSOLrec *LUSOL) return( inform ); } -int LUSOL_ftran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[], MYBOOL prepareupdate) +int LUSOL_ftran(LUSOLrec *LUSOL, REAL b[], int NZidx[], MYBOOL prepareupdate) { int inform; - LPSREAL *vector; + REAL *vector; if(prepareupdate) vector = LUSOL->vLU6L; @@ -678,8 +682,7 @@ int LUSOL_ftran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[], MYBOOL prepareupdate) can create a memory error when the calling program uses a 0-base vector offset back to comply with LUSOL. */ MEMCOPY(vector+1, b+1, LUSOL->n); - if (vector != NULL) - vector[0] = 0; + vector[0] = 0; LU6SOL(LUSOL, LUSOL_SOLVE_Aw_v, vector, b, NZidx, &inform); LUSOL->luparm[LUSOL_IP_FTRANCOUNT]++; @@ -688,7 +691,7 @@ int LUSOL_ftran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[], MYBOOL prepareupdate) } -int LUSOL_btran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[]) +int LUSOL_btran(LUSOLrec *LUSOL, REAL b[], int NZidx[]) { int inform; @@ -696,8 +699,7 @@ int LUSOL_btran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[]) can create a memory error when the calling program uses a 0-base vector offset back to comply with LUSOL. */ MEMCOPY(LUSOL->w+1, b+1, LUSOL->m); - if (LUSOL->w != NULL) - LUSOL->w[0] = 0; + LUSOL->w[0] = 0; LU6SOL(LUSOL, LUSOL_SOLVE_Atv_w, b, LUSOL->w, NZidx, &inform); LUSOL->luparm[LUSOL_IP_BTRANCOUNT]++; @@ -706,10 +708,10 @@ int LUSOL_btran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[]) } -int LUSOL_replaceColumn(LUSOLrec *LUSOL, int jcol, LPSREAL v[]) +int LUSOL_replaceColumn(LUSOLrec *LUSOL, int jcol, REAL v[]) { int inform; - LPSREAL DIAG, VNORM; + REAL DIAG, VNORM; LU8RPC(LUSOL, LUSOL_UPDATE_OLDNONEMPTY, LUSOL_UPDATE_NEWNONEMPTY, jcol, v, NULL, @@ -719,17 +721,17 @@ int LUSOL_replaceColumn(LUSOLrec *LUSOL, int jcol, LPSREAL v[]) return( inform ); } -LPSREAL LUSOL_vecdensity(LUSOLrec *LUSOL, LPSREAL V[]) +REAL LUSOL_vecdensity(LUSOLrec *LUSOL, REAL V[]) { int I, N = 0; for(I = 1; I <= LUSOL->m; I++) if(fabs(V[I]) > 0) N++; - return( (LPSREAL) N / (LPSREAL) LUSOL->m ); + return( (REAL) N / (REAL) LUSOL->m ); } -char relationChar(LPSREAL left, LPSREAL right) +char relationChar(REAL left, REAL right) { if(left > right) return('>'); @@ -741,11 +743,11 @@ char relationChar(LPSREAL left, LPSREAL right) /* Retrieve the core modules ordered by order of dependency */ -#include "lusol2.c" /* Heap management */ -#include "lusol6a.c" /* Singularity checking and equation solving */ -#include "lusol1.c" /* Factorization and core components */ -#include "lusol7a.c" /* Utility routines for updates */ -#include "lusol8a.c" /* Column update */ +#include "lusol2.h" /* Heap management */ +#include "lusol6a.h" /* Singularity checking and equation solving */ +#include "lusol1.h" /* Factorization and core components */ +#include "lusol7a.h" /* Utility routines for updates */ +#include "lusol8a.h" /* Column update */ void LUSOL_dump(FILE *output, LUSOLrec *LUSOL) @@ -782,7 +784,7 @@ LUSOLmat *LUSOL_matcreate(int dim, int nz) newm = (LUSOLmat *) LUSOL_CALLOC(1, sizeof(*newm)); if(newm != NULL) { - newm->a = (LPSREAL *) LUSOL_MALLOC((nz+1)*sizeof(LPSREAL)); + newm->a = (REAL *) LUSOL_MALLOC((nz+1)*sizeof(REAL)); newm->lenx = (int *) LUSOL_MALLOC((dim+1)*sizeof(int)); newm->indx = (int *) LUSOL_MALLOC((dim+1)*sizeof(int)); newm->indr = (int *) LUSOL_MALLOC((nz+1)*sizeof(int)); @@ -805,4 +807,3 @@ void LUSOL_matfree(LUSOLmat **mat) LUSOL_FREE((*mat)->indx); LUSOL_FREE(*mat); } - diff --git a/src/lpsolve/headers/run_headers/lusol.h b/src/lpSolve/src/lusol.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusol.h rename to src/lpSolve/src/lusol.h diff --git a/src/lpsolve/headers/run_headers/lusol1.h b/src/lpSolve/src/lusol1.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusol1.h rename to src/lpSolve/src/lusol1.h diff --git a/src/lpsolve/headers/run_headers/lusol2.h b/src/lpSolve/src/lusol2.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusol2.h rename to src/lpSolve/src/lusol2.h diff --git a/src/lpsolve/headers/run_headers/lusol6a.h b/src/lpSolve/src/lusol6a.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusol6a.h rename to src/lpSolve/src/lusol6a.h diff --git a/src/lpsolve/headers/run_headers/lusol6l0.h b/src/lpSolve/src/lusol6l0.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusol6l0.h rename to src/lpSolve/src/lusol6l0.h diff --git a/src/lpsolve/headers/run_headers/lusol6u.h b/src/lpSolve/src/lusol6u.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusol6u.h rename to src/lpSolve/src/lusol6u.h diff --git a/src/lpsolve/headers/run_headers/lusol7a.h b/src/lpSolve/src/lusol7a.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusol7a.h rename to src/lpSolve/src/lusol7a.h diff --git a/src/lpsolve/headers/run_headers/lusol8a.h b/src/lpSolve/src/lusol8a.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusol8a.h rename to src/lpSolve/src/lusol8a.h diff --git a/src/lpSolve/src/lusolio.c b/src/lpSolve/src/lusolio.c new file mode 100644 index 00000000..e73d0709 --- /dev/null +++ b/src/lpSolve/src/lusolio.c @@ -0,0 +1,299 @@ + +#include +#include +#include "mmio.h" +#include "hbio.h" +#include "lusolio.h" +#include + +/* Utility routines to read matrix files in the Coordinate Text File format*/ + +MYBOOL ctf_read_A(char *filename, int maxm, int maxn, int maxnz, + int *m, int *n, int *nnzero, int *iA, int *jA, REAL *Aij) +{ + FILE *iofile; + int eof; + char buffer[100]; + int k, i, j; + REAL Ak; + MYBOOL filldata; + + *nnzero = 0; + *m = 0; + *n = 0; + + iofile = fopen(filename, "r" ); + if(iofile == NULL) { + Rprintf("A file %s does not exist\n", filename); + return( FALSE ); + } + + filldata = (MYBOOL) !((iA == NULL) && (jA == NULL) && (Aij == NULL)); + eof = TRUE; + for (k = 1; k <= maxnz; k++) { + eof = feof(iofile); + if(eof) + break; + eof = fscanf(iofile, "%d %d %s", &i, &j, buffer); + if(eof == 0 || eof == EOF || i <= 0 || j <= 0 || strlen(buffer) == 0) + break; + Ak = atof(buffer); + (*nnzero)++; + if (filldata) { + iA[k] = i; + jA[k] = j; + Aij[k] = Ak; + } + if (i > *m) *m = i; + if (j > *n) *n = j; + } + fclose( iofile ); + if(!eof) { + Rprintf("Too much data in A file. Increase maxnz\n"); + Rprintf("Current maxnz = %d\n", maxnz); + return( FALSE ); + } + Rprintf("A read successfully\n"); + Rprintf("m = %d n = %d nnzero = %d\n", + *m, *n, *nnzero); + if (*m > maxm || *n > maxn) { + Rprintf("However, matrix dimensions exceed maxm or maxn\n"); + return( FALSE ); + } + return( TRUE ); +} + +MYBOOL ctf_size_A(char *filename, int *m, int *n, int *nnzero) +{ + int maxint = 200000000; + + return( ctf_read_A(filename, maxint, maxint, maxint, + m, n, nnzero, NULL, NULL, NULL) ); +} + +MYBOOL ctf_read_b(char *filename, int m, REAL *b) +{ + FILE *iofile; + int eof; + char buffer[100]; + int i; + + iofile = fopen(filename, "r"); + if(iofile == NULL) { + Rprintf("b file %s does not exist\n", filename); + return( FALSE ); + } + + for (i = 1; i <= m; i++) { + if(feof(iofile)) + goto x350; + eof = fscanf(iofile, "%s", buffer); + if(eof == 0 || eof == EOF) + goto x350; + b[i] = atof(buffer); + } + + fclose( iofile ); + Rprintf("b read successfully\n"); + return( TRUE ); + +x350: + fclose( iofile ); + Rprintf("Not enough data in b file.\n"); + return( FALSE ); +} + + +/* Utility routines to read matrix files in the MatrixMarket format*/ +#define mmf_recsize 255 +MYBOOL mmf_read_A(char *filename, int maxM, int maxN, int maxnz, + int *M, int *N, int *nz, int *iA, int *jA, REAL *Aij) +{ + MM_typecode matcode; + FILE *f; + int i, k, ret_code, ival, jval; + REAL Aval; + MYBOOL status = FALSE, ispat, filldata; + char buf[mmf_recsize]; + + f = fopen(filename, "r"); + if(f == NULL) + return( status ); + + if(mm_read_banner(f, &matcode) != 0) { + Rprintf("Could not process Matrix Market banner.\n"); + goto x900; + } + + /* Screen matrix types since LUSOL only supports a + subset of the Matrix Market data types. */ + if(mm_is_complex(matcode) || mm_is_pattern(matcode)) { + Rprintf("Sorry, this application does not support "); + Rprintf("Market Market type: [%s]\n", mm_typecode_to_str(matcode)); + goto x900; + } + + /* Verify that we have sufficient array storage */ + filldata = (MYBOOL) !((iA == NULL) && (jA == NULL) && (Aij == NULL)); + if(filldata && maxN > 1 && jA == NULL) { + Rprintf("Market Market insufficient array storage specified\n"); + goto x900; + } + + /* Find out size of sparse matrix .... */ + ret_code = mm_read_mtx_crd_size(f, M, N, nz); + if(ret_code != 0 || !filldata || (*M > maxM) || (*N > maxN) || (*nz > maxnz)) { + status = !filldata; + goto x900; + } + + + /* NOTE: when reading in doubles, ANSI C requires the use of the "l" */ + /* specifier as in "%lg", "%lf", "%le", otherwise errors will occur */ + /* (ANSI C X3.159-1989, Sec. 4.9.6.2, p. 136 lines 13-15) */ + + /* Read dense matrix in column order */ + ispat = (MYBOOL) mm_is_pattern(matcode); + k = 1; + if(mm_is_dense(matcode)) { + maxN = MIN(maxN, *N); + for (jval = 1; jval <= maxN; jval++) { + for (i = 1; i <= *M; i++) { + if(fgets(buf, mmf_recsize-1, f) == NULL) + break; + if(sscanf(buf, "%lg\n", &Aval) == 0) + break; + if(Aval != 0) { + if(iA != NULL) + iA[k] = i; + if(jA != NULL) + jA[k] = jval; + + /* Make sure we handle dense vector reading properly */ + if(iA == NULL && jA == NULL) + Aij[i] = Aval; + else + Aij[k] = Aval; + k++; + } + } + } + } + /* Read sparse matrix by coordinate */ + else { + for (i = 1; i <= *nz; i++) { + if(fgets(buf, mmf_recsize-1, f) == NULL) + break; + if(buf[0] == '%') + continue; + if(ispat) { + if(sscanf(buf, "%d %d\n", &ival, &jval) == 0) + continue; + Aij[k] = 1.0; + } + else + if(sscanf(buf, "%d %d %lg\n", &ival, &jval, &Aval) == 0) + continue; + + /* Check if it is a nonzero and we are within column dimension */ + if(Aval != 0 && jval <= maxN) { + Aij[k] = Aval; + if(iA != NULL) + iA[k] = ival; + if(jA != NULL) + jA[k] = jval; + k++; + } + } + } + *nz = k - 1; + + /* Handle case where only the lower triangular parts are given */ + if(!mm_is_general(matcode)) { + if((M != N) || (maxN != maxM) || (2*(*nz) > maxnz)) { + Rprintf("Market Market cannot fill in symmetry data\n"); + goto x900; + } + ispat = mm_is_skew(matcode); + for(i = 1; i <= *nz; i++) { + iA[k] = jA[i]; + jA[k] = iA[i]; + if(ispat) + Aij[k] = -Aij[i]; + else + Aij[k] = Aij[i]; + k++; + } + *nz = k - 1; + } + status = TRUE; + + /* Finish up */ +x900: + fclose( f ); + return( status ); +} + +MYBOOL mmf_size_A(char *filename, int *M, int *N, int *nz) +{ + int maxint = 200000000; + + return( mmf_read_A(filename, maxint, maxint, maxint, + M, N, nz, NULL, NULL, NULL) ); +} + +MYBOOL mmf_read_b(char *filename, int m, REAL *b) +{ + int im, jn, nnzero; + + return( mmf_read_A(filename, m, 1, m, + &im, &jn, &nnzero, NULL, NULL, b)); +} + + +/* Utility routines to read matrix files in Harwell-Boeing format*/ + +MYBOOL hbf_read_A(char *filename, int maxM, int maxN, int maxnz, + int *M, int *N, int *nz, int *iA, int *jA, REAL *Aij) +{ + MYBOOL success; + + success = hbf_size_A(filename, M, N, nz); + if(!success) + return( success ); + + Aij[1] = 0; + success = (MYBOOL) readHB_mat_double(filename, jA, iA-1, Aij-1); + + /* Test if we have a pattern matrix and fill it with all zeros */ + if(Aij[1] == 0) { + int i; + for(i = 1; i <= *nz; i++) + Aij[i] = 1; + } + + /* Expand the column nz counts to triplet format */ + if(success) { + int i, j, ii, k; + k = *nz; + for(j = *N; j > 0; j--) { + ii = jA[j]; + for(i = jA[j-1]; i < ii; i++, k--) + jA[k] = j; + } + } + return( success ); +} + +MYBOOL hbf_size_A(char *filename, int *M, int *N, int *nz) +{ + int Nrhs; + char *Type; + + return( (MYBOOL) readHB_info(filename, M, N, nz, &Type, &Nrhs) ); +} + +MYBOOL hbf_read_b(char *filename, int m, REAL *b) +{ + return( (MYBOOL) readHB_aux_double(filename, 'F', b) ); /* Same format as matrix */ +} diff --git a/src/lpsolve/headers/run_headers/lusolio.h b/src/lpSolve/src/lusolio.h similarity index 100% rename from src/lpsolve/headers/run_headers/lusolio.h rename to src/lpSolve/src/lusolio.h diff --git a/src/lpsolve/build/lp_solve/mmio.c b/src/lpSolve/src/mmio.c similarity index 81% rename from src/lpsolve/build/lp_solve/mmio.c rename to src/lpSolve/src/mmio.c index 2b88358f..e9114ea7 100644 --- a/src/lpsolve/build/lp_solve/mmio.c +++ b/src/lpSolve/src/mmio.c @@ -1,4 +1,4 @@ -/* +/* * Matrix Market I/O library for ANSI C * * See http://math.nist.gov/MatrixMarket for details. @@ -13,8 +13,9 @@ #include #include "mmio.h" +#include +#include -#ifndef R_EMBEDDED_LPSOLVE int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_) { @@ -24,74 +25,73 @@ int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, int i; double *val; int *I, *J; - int x; - + if ((f = fopen(fname, "r")) == NULL) return -1; - - + + if (mm_read_banner(f, &matcode) != 0) { - printf("mm_read_unsymetric: Could not process Matrix Market banner "); - printf(" in file [%s]\n", fname); + Rprintf("mm_read_unsymetric: Could not process Matrix Market banner "); + Rprintf(" in file [%s]\n", fname); return -1; } - - - + + + if ( !(mm_is_real(matcode) && mm_is_matrix(matcode) && mm_is_sparse(matcode))) { - fprintf(stderr, "Sorry, this application does not support "); - fprintf(stderr, "Market Market type: [%s]\n", + REprintf( "Sorry, this application does not support "); + REprintf( "Market Market type: [%s]\n", mm_typecode_to_str(matcode)); return -1; } - + /* find out size of sparse matrix: M, N, nz .... */ - + if (mm_read_mtx_crd_size(f, &M, &N, &nz) !=0) { - fprintf(stderr, "read_unsymmetric_sparse(): could not parse matrix size.\n"); + REprintf( "read_unsymmetric_sparse(): could not parse matrix size.\n"); return -1; } - + *M_ = M; *N_ = N; *nz_ = nz; - + /* reseve memory for matrices */ - + I = (int *) malloc(nz * sizeof(int)); J = (int *) malloc(nz * sizeof(int)); val = (double *) malloc(nz * sizeof(double)); - + *val_ = val; *I_ = I; *J_ = J; - + /* NOTE: when reading in doubles, ANSI C requires the use of the "l" */ /* specifier as in "%lg", "%lf", "%le", otherwise errors will occur */ /* (ANSI C X3.159-1989, Sec. 4.9.6.2, p. 136 lines 13-15) */ - + for (i=0; i= 2) return 0; - + else - do { - num_items_read = fscanf(f, "%d %d %d", M, N, nz); + do { + num_items_read = fscanf(f, "%d %d %d", M, N, nz); if (num_items_read == EOF) return MM_PREMATURE_EOF; } while (num_items_read < 2); @@ -224,22 +224,22 @@ int mm_read_mtx_array_size(FILE *f, int *M, int *N) int num_items_read; /* set return null parameter values, in case we exit with errors */ *M = *N = 0; - + /* now continue scanning until you reach the end-of-comments */ - do + do { - if (fgets(line,MM_MAX_LINE_LENGTH,f) == NULL) + if (fgets(line,MM_MAX_LINE_LENGTH,f) == NULL) return MM_PREMATURE_EOF; }while (line[0] == '%'); /* line[] is either blank or has M,N, nz */ if (sscanf(line, "%d %d", M, N) == 2) return 0; - + else /* we have a blank line */ do - { - num_items_read = fscanf(f, "%d %d", M, N); + { + num_items_read = fscanf(f, "%d %d", M, N); if (num_items_read == EOF) return MM_PREMATURE_EOF; } while (num_items_read != 2); @@ -251,7 +251,7 @@ int mm_write_mtx_array_size(FILE *f, int M, int N) { if (fprintf(f, "%d %d\n", M, N) < 0) return MM_COULD_NOT_WRITE_FILE; - else + else return 0; } @@ -293,7 +293,7 @@ int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], return MM_UNSUPPORTED_TYPE; return 0; - + } int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, @@ -319,7 +319,7 @@ int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, return MM_UNSUPPORTED_TYPE; return 0; - + } @@ -331,7 +331,7 @@ int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, (nz pairs of real/imaginary values) ************************************************************************/ -int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, +int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode) { int ret_code; @@ -346,7 +346,7 @@ int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, if ((ret_code = mm_read_banner(f, matcode)) != 0) return ret_code; - if (!(mm_is_valid(*matcode) && mm_is_sparse(*matcode) && + if (!(mm_is_valid(*matcode) && mm_is_sparse(*matcode) && mm_is_matrix(*matcode))) return MM_UNSUPPORTED_TYPE; @@ -361,21 +361,21 @@ int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, if (mm_is_complex(*matcode)) { *val = (double *) malloc(*nz * 2 * sizeof(double)); - ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, + ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode); if (ret_code != 0) return ret_code; } else if (mm_is_real(*matcode)) { *val = (double *) malloc(*nz * sizeof(double)); - ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, + ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode); if (ret_code != 0) return ret_code; } else if (mm_is_pattern(*matcode)) { - ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, + ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode); if (ret_code != 0) return ret_code; } @@ -400,46 +400,47 @@ int mm_write_banner(FILE *f, MM_typecode matcode) int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode) { - FILE *f; - int i; - - if (strcmp(fname, "stdout") == 0) - f = stdout; - else - if ((f = fopen(fname, "w")) == NULL) - return MM_COULD_NOT_WRITE_FILE; - - /* print banner followed by typecode */ - fprintf(f, "%s ", MatrixMarketBanner); - fprintf(f, "%s\n", mm_typecode_to_str(matcode)); - - /* print matrix sizes and nonzeros */ - fprintf(f, "%d %d %d\n", M, N, nz); - - /* print values */ - if (mm_is_pattern(matcode)) - for (i=0; i +#include +/*#include */ +#include +#include +#include "myblas.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +/* ************************************************************************ */ +/* Initialize BLAS interfacing routines */ +/* ************************************************************************ */ +MYBOOL mustinitBLAS = TRUE; +#if (defined WIN32) || (defined WIN64) + HINSTANCE hBLAS = NULL; +#else + void *hBLAS = NULL; +#endif + + +/* ************************************************************************ */ +/* Function pointers for external BLAS library (C base 0) */ +/* ************************************************************************ */ +BLAS_dscal_func *BLAS_dscal; +BLAS_dcopy_func *BLAS_dcopy; +BLAS_daxpy_func *BLAS_daxpy; +BLAS_dswap_func *BLAS_dswap; +BLAS_ddot_func *BLAS_ddot; +BLAS_idamax_func *BLAS_idamax; +BLAS_idamin_func *BLAS_idamin; +BLAS_dload_func *BLAS_dload; +BLAS_dnormi_func *BLAS_dnormi; + + +/* ************************************************************************ */ +/* Define the BLAS interfacing routines */ +/* ************************************************************************ */ + +void init_BLAS(void) +{ + if(mustinitBLAS) { + load_BLAS(NULL); + mustinitBLAS = FALSE; + } +} + +MYBOOL is_nativeBLAS(void) +{ +#ifdef LoadableBlasLib + return( (MYBOOL) (hBLAS == NULL) ); +#else + return( TRUE ); +#endif +} + +MYBOOL load_BLAS(char *libname) +{ + MYBOOL result = TRUE; + +#ifdef LoadableBlasLib + if(hBLAS != NULL) { + my_FreeLibrary(hBLAS); + } +#endif + + if(libname == NULL) { + if(!mustinitBLAS && is_nativeBLAS()) + return( FALSE ); + BLAS_dscal = my_dscal; + BLAS_dcopy = my_dcopy; + BLAS_daxpy = my_daxpy; + BLAS_dswap = my_dswap; + BLAS_ddot = my_ddot; + BLAS_idamax = my_idamax; + BLAS_idamin = my_idamin; + BLAS_dload = my_dload; + BLAS_dnormi = my_dnormi; + if(mustinitBLAS) + mustinitBLAS = FALSE; + } + else { +#ifdef LoadableBlasLib + #if (defined WIN32) || (defined WIN64) + char *blasname = libname; + #else + /* First standardize UNIX .SO library name format. */ + char blasname[260]; + if(!so_stdname(blasname, libname, 260)) + return( FALSE ); + #endif + /* Get a handle to the Windows DLL module. */ + hBLAS = my_LoadLibrary(blasname); + + /* If the handle is valid, try to get the function addresses. */ + result = (MYBOOL) (hBLAS != NULL); + if(result) { + BLAS_dscal = (BLAS_dscal_func *) my_GetProcAddress(hBLAS, BLAS_prec "scal"); + BLAS_dcopy = (BLAS_dcopy_func *) my_GetProcAddress(hBLAS, BLAS_prec "copy"); + BLAS_daxpy = (BLAS_daxpy_func *) my_GetProcAddress(hBLAS, BLAS_prec "axpy"); + BLAS_dswap = (BLAS_dswap_func *) my_GetProcAddress(hBLAS, BLAS_prec "swap"); + BLAS_ddot = (BLAS_ddot_func *) my_GetProcAddress(hBLAS, BLAS_prec "dot"); + BLAS_idamax = (BLAS_idamax_func *) my_GetProcAddress(hBLAS, "i" BLAS_prec "amax"); + BLAS_idamin = (BLAS_idamin_func *) my_GetProcAddress(hBLAS, "i" BLAS_prec "amin"); +#if 0 + BLAS_dload = (BLAS_dload_func *) my_GetProcAddress(hBLAS, BLAS_prec "load"); + BLAS_dnormi = (BLAS_dnormi_func *) my_GetProcAddress(hBLAS, BLAS_prec "normi"); +#endif + } +#endif + /* Do validation */ + if(!result || + ((BLAS_dscal == NULL) || + (BLAS_dcopy == NULL) || + (BLAS_daxpy == NULL) || + (BLAS_dswap == NULL) || + (BLAS_ddot == NULL) || + (BLAS_idamax == NULL) || + (BLAS_idamin == NULL) || + (BLAS_dload == NULL) || + (BLAS_dnormi == NULL)) + ) { + load_BLAS(NULL); + result = FALSE; + } + } + return( result ); +} +MYBOOL unload_BLAS(void) +{ + return( load_BLAS(NULL) ); +} + + +/* ************************************************************************ */ +/* Now define the unoptimized local BLAS functions */ +/* ************************************************************************ */ +void daxpylpsolve( int n, REAL da, REAL *dx, int incx, REAL *dy, int incy) +{ + dx++; + dy++; + BLAS_daxpy( &n, &da, dx, &incx, dy, &incy); +} +void BLAS_CALLMODEL my_daxpy( int *_n, REAL *_da, REAL *dx, int *_incx, REAL *dy, int *_incy) +{ + +/* constant times a vector plus a vector. + uses unrolled loops for increments equal to one. + jack dongarra, linpack, 3/11/78. + modified 12/3/93, array[1] declarations changed to array[*] */ + + int i, ix, iy; +#ifndef DOFASTMATH + int m, mp1; +#endif + register REAL rda; + REAL da = *_da; + int n = *_n, incx = *_incx, incy = *_incy; + + if (n <= 0) return; + if (da == 0.0) return; + + dx--; + dy--; + ix = 1; + iy = 1; + if (incx < 0) + ix = (-n+1)*incx + 1; + if (incy < 0) + iy = (-n+1)*incy + 1; + rda = da; + +/* CPU intensive loop; option to do pointer arithmetic */ +#if defined DOFASTMATH + { + REAL *xptr, *yptr; + for (i = 1, xptr = dx + ix, yptr = dy + iy; + i <= n; i++, xptr += incx, yptr += incy) + (*yptr) += rda*(*xptr); + } +#else + + if (incx==1 && incy==1) goto x20; + +/* code for unequal increments or equal increments not equal to 1 */ + for (i = 1; i<=n; i++) { + dy[iy]+= rda*dx[ix]; + ix+= incx; + iy+= incy; + } + return; + +/* code for both increments equal to 1 */ + +/* clean-up loop */ +x20: + m = n % 4; + if (m == 0) goto x40; + for (i = 1; i<=m; i++) + dy[i]+= rda*dx[i]; + if(n < 4) return; +x40: + mp1 = m + 1; + for (i = mp1; i<=n; i=i+4) { + dy[i]+= rda*dx[i]; + dy[i + 1]+= rda*dx[i + 1]; + dy[i + 2]+= rda*dx[i + 2]; + dy[i + 3]+= rda*dx[i + 3]; + } +#endif +} + + +/* ************************************************************************ */ +void dcopylpsolve( int n, REAL *dx, int incx, REAL *dy, int incy) +{ + dx++; + dy++; + BLAS_dcopy( &n, dx, &incx, dy, &incy); +} + +void BLAS_CALLMODEL my_dcopy (int *_n, REAL *dx, int *_incx, REAL *dy, int *_incy) +{ + +/* copies a vector, x, to a vector, y. + uses unrolled loops for increments equal to one. + jack dongarra, linpack, 3/11/78. + modified 12/3/93, array[1] declarations changed to array[*] */ + + int i, ix, iy; +#ifndef DOFASTMATH + int m, mp1; +#endif + int n = *_n, incx = *_incx, incy = *_incy; + + if(n<=0) + return; + + dx--; + dy--; + ix = 1; + iy = 1; + if(incx<0) + ix = (-n+1)*incx + 1; + if(incy<0) + iy = (-n+1)*incy + 1; + + +/* CPU intensive loop; option to do pointer arithmetic */ +#if defined DOFASTMATH + { + REAL *xptr, *yptr; + for (i = 1, xptr = dx + ix, yptr = dy + iy; + i <= n; i++, xptr += incx, yptr += incy) + (*yptr) = (*xptr); + } +#else + + if(incx==1 && incy==1) + goto x20; + +/* code for unequal increments or equal increments not equal to 1 */ + + for(i = 1; i<=n; i++) { + dy[iy] = dx[ix]; + ix+= incx; + iy+= incy; + } + return; + +/* code for both increments equal to 1 */ + +/* version with fast machine copy logic (requires memory.h or string.h) */ +x20: + m = n % 7; + if (m == 0) goto x40; + for (i = 1; i<=m; i++) + dy[i] = dx[i]; + if (n < 7) return; + +x40: + mp1 = m + 1; + for (i = mp1; i<=n; i=i+7) { + dy[i] = dx[i]; + dy[i + 1] = dx[i + 1]; + dy[i + 2] = dx[i + 2]; + dy[i + 3] = dx[i + 3]; + dy[i + 4] = dx[i + 4]; + dy[i + 5] = dx[i + 5]; + dy[i + 6] = dx[i + 6]; + } +#endif +} + + +/* ************************************************************************ */ + +void dscallpsolve (int n, REAL da, REAL *dx, int incx) +{ + dx++; + BLAS_dscal (&n, &da, dx, &incx); +} + +void BLAS_CALLMODEL my_dscal (int *_n, REAL *_da, REAL *dx, int *_incx) +{ + +/* Multiply a vector by a constant. + + --Input-- + N number of elements in input vector(s) + DA double precision scale factor + DX double precision vector with N elements + INCX storage spacing between elements of DX + + --Output-- + DX double precision result (unchanged if N.LE.0) + + Replace double precision DX by double precision DA*DX. + For I = 0 to N-1, replace DX(IX+I*INCX) with DA * DX(IX+I*INCX), + where IX = 1 if INCX .GE. 0, else IX = 1+(1-N)*INCX. */ + + int i; +#ifndef DOFASTMATH + int m, mp1, ix; +#endif + register REAL rda; + REAL da = *_da; + int n = *_n, incx = *_incx; + + if (n <= 0) + return; + rda = da; + + dx--; + +/* Optionally do fast pointer arithmetic */ +#if defined DOFASTMATH + { + REAL *xptr; + for (i = 1, xptr = dx + 1; i <= n; i++, xptr += incx) + (*xptr) *= rda; + } +#else + + if (incx == 1) + goto x20; + +/* Code for increment not equal to 1 */ + ix = 1; + if (incx < 0) + ix = (-n+1)*incx + 1; + for(i = 1; i <= n; i++, ix += incx) + dx[ix] *= rda; + return; + +/* Code for increment equal to 1. */ +/* Clean-up loop so remaining vector length is a multiple of 5. */ +x20: + m = n % 5; + if (m == 0) goto x40; + for( i = 1; i <= m; i++) + dx[i] *= rda; + if (n < 5) + return; +x40: + mp1 = m + 1; + for(i = mp1; i <= n; i += 5) { + dx[i] *= rda; + dx[i+1] *= rda; + dx[i+2] *= rda; + dx[i+3] *= rda; + dx[i+4] *= rda; + } +#endif +} + + +/* ************************************************************************ */ + +REAL ddotlpsolve(int n, REAL *dx, int incx, REAL *dy, int incy) +{ + dx++; + dy++; + return( BLAS_ddot (&n, dx, &incx, dy, &incy) ); +} + +REAL BLAS_CALLMODEL my_ddot(int *_n, REAL *dx, int *_incx, REAL *dy, int *_incy) +{ + +/* forms the dot product of two vectors. + uses unrolled loops for increments equal to one. + jack dongarra, linpack, 3/11/78. + modified 12/3/93, array[1] declarations changed to array[*] */ + + register REAL dtemp; + int i, ix, iy; +#ifndef DOFASTMATH + int m, mp1; +#endif + int n = *_n, incx = *_incx, incy = *_incy; + + dtemp = 0.0; + if (n<=0) + return( (REAL) dtemp); + + dx--; + dy--; + ix = 1; + iy = 1; + if (incx<0) + ix = (-n+1)*incx + 1; + if (incy<0) + iy = (-n+1)*incy + 1; + +/* CPU intensive loop; option to do pointer arithmetic */ + +#if defined DOFASTMATH + { + REAL *xptr, *yptr; + for (i = 1, xptr = dx + ix, yptr = dy + iy; + i <= n; i++, xptr += incx, yptr += incy) + dtemp+= (*yptr)*(*xptr); + } +#else + + if (incx==1 && incy==1) goto x20; + +/* code for unequal increments or equal increments not equal to 1 */ + + for (i = 1; i<=n; i++) { + dtemp+= dx[ix]*dy[iy]; + ix+= incx; + iy+= incy; + } + return(dtemp); + +/* code for both increments equal to 1 */ + +/* clean-up loop */ + +x20: + m = n % 5; + if (m == 0) goto x40; + for (i = 1; i<=m; i++) + dtemp+= dx[i]*dy[i]; + if (n < 5) goto x60; + +x40: + mp1 = m + 1; + for (i = mp1; i<=n; i=i+5) + dtemp+= dx[i]*dy[i] + dx[i + 1]*dy[i + 1] + + dx[i + 2]*dy[i + 2] + dx[i + 3]*dy[i + 3] + dx[i + 4]*dy[i + 4]; + +x60: +#endif + return(dtemp); +} + + +/* ************************************************************************ */ + +void dswaplpsolve( int n, REAL *dx, int incx, REAL *dy, int incy ) +{ + dx++; + dy++; + BLAS_dswap( &n, dx, &incx, dy, &incy ); +} + +void BLAS_CALLMODEL my_dswap( int *_n, REAL *dx, int *_incx, REAL *dy, int *_incy ) +{ + int i, ix, iy; +#ifndef DOFASTMATH + int m, mp1, ns; + REAL dtemp2, dtemp3; +#endif + register REAL dtemp1; + int n = *_n, incx = *_incx, incy = *_incy; + + if (n <= 0) return; + + dx--; + dy--; + ix = 1; + iy = 1; + if (incx < 0) + ix = (-n+1)*incx + 1; + if (incy < 0) + iy = (-n+1)*incy + 1; + +/* CPU intensive loop; option to do pointer arithmetic */ +#if defined DOFASTMATH + { + REAL *xptr, *yptr; + for (i = 1, xptr = dx + ix, yptr = dy + iy; + i <= n; i++, xptr += incx, yptr += incy) { + dtemp1 = (*xptr); + (*xptr) = (*yptr); + (*yptr) = dtemp1; + } + } +#else + + if (incx == incy) { + if (incx <= 0) goto x5; + if (incx == 1) goto x20; + goto x60; + } + +/* code for unequal or nonpositive increments. */ +x5: + for (i = 1; i<=n; i++) { + dtemp1 = dx[ix]; + dx[ix] = dy[iy]; + dy[iy] = dtemp1; + ix+= incx; + iy+= incy; + } + return; + +/* code for both increments equal to 1. + clean-up loop so remaining vector length is a multiple of 3. */ +x20: + m = n % 3; + if (m == 0) goto x40; + for (i = 1; i<=m; i++) { + dtemp1 = dx[i]; + dx[i] = dy[i]; + dy[i] = dtemp1; + } + if (n < 3) return; + +x40: + mp1 = m + 1; + for (i = mp1; i<=n; i=i+3) { + dtemp1 = dx[i]; + dtemp2 = dx[i+1]; + dtemp3 = dx[i+2]; + dx[i] = dy[i]; + dx[i+1] = dy[i+1]; + dx[i+2] = dy[i+2]; + dy[i] = dtemp1; + dy[i+1] = dtemp2; + dy[i+2] = dtemp3; + } + return; + +/* code for equal, positive, non-unit increments. */ +x60: + ns = n*incx; + for (i = 1; i<=ns; i=i+incx) { + dtemp1 = dx[i]; + dx[i] = dy[i]; + dy[i] = dtemp1; + } +#endif + +} + + +/* ************************************************************************ */ + +void dload(int n, REAL da, REAL *dx, int incx) +{ + dx++; + BLAS_dload (&n, &da, dx, &incx); +} + +void BLAS_CALLMODEL my_dload (int *_n, REAL *_da, REAL *dx, int *_incx) +{ +/* copies a scalar, a, to a vector, x. + uses unrolled loops when incx equals one. + + To change the precision of this program, run the change + program on dload.f + Alternatively, to make a single precision version append a + comment character to the start of all lines between sequential + precision > double + and + end precision > double + comments and delete the comment character at the start of all + lines between sequential + precision > single + and + end precision > single + comments. To make a double precision version interchange + the append and delete operations in these instructions. */ + + int i, ix, m, mp1; + REAL da = *_da; + int n = *_n, incx = *_incx; + + if (n<=0) return; + dx--; + if (incx==1) goto x20; + +/* code for incx not equal to 1 */ + + ix = 1; + if (incx<0) + ix = (-n+1)*incx + 1; + for (i = 1; i<=n; i++) { + dx[ix] = da; + ix+= incx; + } + return; + +/* code for incx equal to 1 and clean-up loop */ + +x20: + m = n % 7; + if (m == 0) goto x40; + for (i = 1; i<=m; i++) + dx[i] = da; + if (n < 7) return; + +x40: + mp1 = m + 1; + for (i = mp1; i<=n; i=i+7) { + dx[i] = da; + dx[i + 1] = da; + dx[i + 2] = da; + dx[i + 3] = da; + dx[i + 4] = da; + dx[i + 5] = da; + dx[i + 6] = da; + } +} + +/* ************************************************************************ */ +int idamaxlpsolve( int n, REAL *x, int is ) +{ + x++; + return ( BLAS_idamax( &n, x, &is ) ); +} + +int idaminlpsolve( int n, REAL *x, int is ) +{ + x++; + return ( BLAS_idamin( &n, x, &is ) ); +} + +int BLAS_CALLMODEL my_idamax( int *_n, REAL *x, int *_is ) +{ + register REAL xmax, xtest; + int i, imax = 0; +#if !defined DOFASTMATH + int ii; +#endif + int n = *_n, is = *_is; + + if((n < 1) || (is <= 0)) + return(imax); + imax = 1; + if(n == 1) + return(imax); + +#if defined DOFASTMATH + xmax = fabs(*x); + for (i = 2, x += is; i <= n; i++, x += is) { + xtest = fabs(*x); + if(xtest > xmax) { + xmax = xtest; + imax = i; + } + } +#else + x--; + ii = 1; + xmax = fabs(x[ii]); + for(i = 2, ii+ = is; i <= n; i++, ii+ = is) { + xtest = fabs(x[ii]); + if(xtest > xmax) { + xmax = xtest; + imax = i; + } + } +#endif + return(imax); +} + +int BLAS_CALLMODEL my_idamin( int *_n, REAL *x, int *_is ) +{ + register REAL xmin, xtest; + int i, imin = 0; +#if !defined DOFASTMATH + int ii; +#endif + int n = *_n, is = *_is; + + if((n < 1) || (is <= 0)) + return(imin); + imin = 1; + if(n == 1) + return(imin); + +#if defined DOFASTMATH + xmin = fabs(*x); + for (i = 2, x += is; i <= n; i++, x += is) { + xtest = fabs(*x); + if(xtest < xmin) { + xmin = xtest; + imin = i; + } + } +#else + x--; + ii = 1; + xmin = fabs(x[ii]); + for(i = 2, ii+ = is; i <= n; i++, ii+ = is) { + xtest = fabs(x[ii]); + if(xtest < xmin) { + xmin = xtest; + imin = i; + } + } +#endif + return(imin); +} + +/* ************************************************************************ */ +REAL dnormi( int n, REAL *x ) +{ + x++; + return( BLAS_dnormi( &n, x ) ); +} + +REAL BLAS_CALLMODEL my_dnormi( int *_n, REAL *x ) +{ +/* =============================================================== + dnormi returns the infinity-norm of the vector x. + =============================================================== */ + int j; + register REAL hold, absval; + int n = *_n; + + x--; + hold = 0.0; +/* for(j = 1; j <= n; j++) */ + for(j = n; j > 0; j--) { + absval = fabs(x[j]); + hold = MAX( hold, absval ); + } + + return( hold ); +} + + +/* ************************************************************************ */ +/* Subvector and submatrix access routines (Fortran compatibility) */ +/* ************************************************************************ */ + +#ifndef UseMacroVector +int subvec( int item) +{ + return( item-1 ); +} +#endif + +int submat( int nrowb, int row, int col) +{ + return( nrowb*(col-1) + subvec(row) ); +} + +int posmat( int nrowb, int row, int col) +{ + return( submat(nrowb, row, col)+BLAS_BASE ); +} + +/* ************************************************************************ */ +/* Randomization functions */ +/* ************************************************************************ */ + +void randomseed(int seeds[]) +/* Simply create some default seed values */ +{ + seeds[1] = 123456; + seeds[2] = 234567; + seeds[3] = 345678; +} + +void randomdens( int n, REAL *x, REAL r1, REAL r2, REAL densty, int *seeds ) +{ +/* ------------------------------------------------------------------ + random generates a vector x[*] of random numbers + in the range (r1, r2) with (approximate) specified density. + seeds[*] must be initialized before the first call. + ------------------------------------------------------------------ */ + + int i; + REAL *y; + + y = (REAL *) malloc(sizeof(*y) * (n+1)); + ddrand( n, x, 1, seeds ); + ddrand( n, y, 1, seeds ); + + for (i = 1; i<=n; i++) { + if (y[i] < densty) + x[i] = r1 + (r2 - r1) * x[i]; + else + x[i] = 0.0; + } + free(y); +} + + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ + +void ddrand( int n, REAL *x, int incx, int *seeds ) +{ + +/* ------------------------------------------------------------------ + ddrand fills a vector x with uniformly distributed random numbers + in the interval (0, 1) using a method due to Wichman and Hill. + + seeds[1..3] should be set to integer values + between 1 and 30000 before the first entry. + + Integer arithmetic up to 30323 is required. + + Blatantly copied from Wichman and Hill 19-January-1987. + 14-Feb-94. Original version. + 30 Jun 1999. seeds stored in an array. + 30 Jun 1999. This version of ddrand. + ------------------------------------------------------------------ */ + + int ix, xix; + + if (n < 1) return; + + for (ix = 1; ix<=1+(n-1)*incx; ix=ix+incx) { + seeds[1] = 171*(seeds[1] % 177) - 2*(seeds[1]/177); + seeds[2] = 172*(seeds[2] % 176) - 35*(seeds[2]/176); + seeds[3] = 170*(seeds[3] % 178) - 63*(seeds[3]/178); + + if (seeds[1] < 0) seeds[1] = seeds[1] + 30269; + if (seeds[2] < 0) seeds[2] = seeds[2] + 30307; + if (seeds[3] < 0) seeds[3] = seeds[3] + 30323; + + x[ix] = ((REAL) seeds[1])/30269.0 + + ((REAL) seeds[2])/30307.0 + + ((REAL) seeds[3])/30323.0; + xix = (int) x[ix]; + x[ix] = fabs(x[ix] - xix); + } + +} + diff --git a/src/lpsolve/headers/run_headers/myblas.h b/src/lpSolve/src/myblas.h similarity index 100% rename from src/lpsolve/headers/run_headers/myblas.h rename to src/lpSolve/src/myblas.h diff --git a/src/lpSolve/src/sparselib.c b/src/lpSolve/src/sparselib.c new file mode 100644 index 00000000..d30f8d1b --- /dev/null +++ b/src/lpSolve/src/sparselib.c @@ -0,0 +1,967 @@ + +#include +#include +#include +#include +#include "commonlib.h" +#include "myblas.h" +#include "sparselib.h" + +#include "lpkit.h" +#include + +sparseMatrix *createMatrix(int dimLimit, int lenLimit, int initVectors) +{ + int initsize; + sparseMatrix *matrix; + + if(initVectors < 0) + initVectors = 0; + if(initVectors == 0) + initsize = MIN(INITIALSIZE, dimLimit); + else + initsize = MAX(INITIALSIZE, initVectors); + + CALLOC(matrix, 1, sparseMatrix); + matrix->limit = dimLimit; + matrix->limitVector = lenLimit; + resizeMatrix(matrix, initsize); + while(initVectors > 0) { + initVectors--; + appendMatrix(matrix, createVector(lenLimit, 2)); + } + return(matrix); +} + + +void resizeMatrix(sparseMatrix *matrix, int newSize) +{ + int oldSize; + + if(matrix == NULL) + oldSize = 0; + else + oldSize = matrix->size; + while(oldSize>newSize) { + oldSize--; + freeVector(matrix->list[oldSize]); + return; + } +/* This stuff... */ + matrix->list = realloc ((void *) matrix->list, sizeof(sparseVector) * newSize); + if ((void *) matrix->list == NULL) { + report(NULL, CRITICAL, + "realloc of %d bytes failed on new code in sparselib.c!\n"); + } +/* ..replaces this line. SEB April 14 2006 + REALLOC(matrix->list, newSize, sparseVector); +*/ + while(oldSizelist[oldSize] = NULL; + oldSize++; + } + if(newSize>0) + matrix->size = newSize; +} + +int appendMatrix(sparseMatrix *matrix, sparseVector *newVector) +{ + if(matrix->count == matrix->size) + resizeMatrix(matrix, matrix->size + 10); + matrix->list[matrix->count] = newVector; + matrix->count++; + putDiagonalIndex(newVector, matrix->count); + return(matrix->count); +} + + +int NZcountMatrix(sparseMatrix *matrix) +{ + int i, nz; + + nz = 0; + for(i = 0; i < matrix->count; i++) + nz += matrix->list[i]->count; + + return( nz ); +} + + +void freeMatrix(sparseMatrix *matrix) +{ + resizeMatrix(matrix, 0); + FREE(matrix); +} + + +void printMatrix(int n, sparseMatrix *matrix, int modulo, MYBOOL showEmpty) +{ + int i; + for(i = 1; i<=matrix->count; i++) + if(matrix->list[i-1] != NULL) { + if(showEmpty || matrix->list[i-1]->count>0) + printVector(n, matrix->list[i-1], modulo); + } +} + + +sparseVector *createVector(int dimLimit, int initSize) +{ + sparseVector *newitem; + CALLOC(newitem, 1, sparseVector); + newitem->limit = dimLimit; + initSize = resizeVector(newitem, initSize); + return(newitem); +} + + +sparseVector *cloneVector(sparseVector *sparse) +{ + sparseVector *hold; + hold = createVector(sparse->limit, sparse->count); + hold->count = sparse->count; + MEMCOPY(&hold->value[0], &sparse->value[0], (sparse->count+1)); + MEMCOPY(&hold->index[0], &sparse->index[0], (sparse->count+1)); + return(hold); +} + +int redimensionVector(sparseVector *sparse, int newDim) +{ + int olddim, i; + + olddim = sparse->limit; + sparse->limit = newDim; + if(lastIndex(sparse)>newDim) { + i = sparse->count; + while(i>0 && sparse->index[i]>newDim) i--; + sparse->count = i; + resizeVector(sparse, sparse->count); + } + return(olddim); +} + + +int resizeVector(sparseVector *sparse, int newSize) +{ + int oldsize; + + oldsize = sparse->size; +/* + REALLOC(sparse->value, (newSize+1)); + REALLOC(sparse->index, (newSize+1)); +*/ + REALLOC(sparse->value, (newSize+1), REAL); + REALLOC(sparse->index, (newSize+1), int); + + sparse->size = newSize; + return(oldsize); +} + + +void moveVector(sparseVector *sparse, int destPos, int sourcePos, int itemCount) +{ + int i; + + if(itemCount <= 0 || sourcePos == destPos) + return; + +#if defined DOFASTMATH + if(TRUE) { + MEMMOVE(&sparse->value[destPos], &sparse->value[sourcePos], itemCount); + MEMMOVE(&sparse->index[destPos], &sparse->index[sourcePos], itemCount); + } + else { + int *idxPtr1, *idxPtr2; + double *valPtr1, *valPtr2; + + for(i = 1, idxPtr1 = sparse->index+destPos, idxPtr2 = sparse->index+sourcePos, + valPtr1 = sparse->value+destPos, valPtr2 = sparse->value+sourcePos; + i<=itemCount; i++, idxPtr1++, idxPtr2++, valPtr1++, valPtr2++) { + *idxPtr1 = *idxPtr2; + *valPtr1 = *valPtr2; + } + } +#else + for(i = 1; i<=itemCount; i++) { + sparse->value[destPos] = sparse->value[sourcePos]; + sparse->index[destPos] = sparse->index[sourcePos]; + destPos++; + sourcePos++; + } +#endif +} + + +void rotateVector(sparseVector *sparse, int startPos, int chainSize, int stepDelta) +{ +/* int idxHold; */ +/* double valHold; */ + +} + + +void swapVector(sparseVector *sparse1, sparseVector *sparse2) +{ + int n, m, *idx; + REAL *val; + + n = sparse1->count; + sparse1->count = sparse2->count; + sparse2->count = n; + + n = sparse1->size; + sparse1->size = sparse2->size; + sparse2->size = n; + + n = sparse1->limit; + sparse1->limit = sparse2->limit; + sparse2->limit = n; + + idx = sparse1->index; + sparse1->index = sparse2->index; + sparse2->index = idx; + + val = sparse1->value; + sparse1->value = sparse2->value; + sparse2->value = val; + + n = getDiagonalIndex(sparse1); + m = getDiagonalIndex(sparse2); + putDiagonalIndex(sparse1, m); + putDiagonalIndex(sparse2, n); + +} + + +void freeVector(sparseVector *sparse) +{ + if(sparse != NULL) { + FREE(sparse->value); + FREE(sparse->index); + FREE(sparse); + } +} + + +MYBOOL verifyVector(sparseVector *sparse) +{ + int i, n, k1, k2, kd; + int err = 0; + double vd; + + n = sparse->count; + kd = sparse->index[0]; + vd = sparse->value[0]; + if(n <= 1) + return(TRUE); + k1 = 0; + k2 = sparse->index[1]; + if(k2 == kd && sparse->value[1] != vd) + err = 2; + + for(i = 2; i <= n && err == 0; i++) { + k1 = k2; + k2 = sparse->index[i]; + if(k1 >= k2) err = 1; + if(k2 == kd && sparse->value[i] != vd) err = 2; + } + if(err == 0) + return(TRUE); + else if(err == 1) + Rprintf("Invalid sparse vector index order"); + else if(err == 2) + Rprintf("Invalid sparse vector diagonal value"); + return(FALSE); +} + + +int firstIndex(sparseVector *sparse) +{ + return(sparse->index[1]); +} + + +int lastIndex(sparseVector *sparse) +{ + return(sparse->index[sparse->count]); +} + + +int getDiagonalIndex(sparseVector *sparse) +{ + return(sparse->index[0]); +} + + +int putDiagonalIndex(sparseVector *sparse, int index) +{ + int oldindex; + oldindex = sparse->index[0]; + if(index > 0) { + sparse->index[0] = 0; /* Must temporarily set to zero to force vector search in getItem */ + sparse->value[0] = getItem(sparse, index); + } + else + sparse->value[0] = 0; + sparse->index[0] = index; + return(oldindex); +} + + +MYBOOL putDiagonal(sparseVector *sparse, REAL value) +{ + if(sparse->index[0]>0) { + putItem(sparse, sparse->index[0], value); + return(TRUE); + } + else + return(FALSE); +} + + +REAL getDiagonal(sparseVector *sparse) +{ + return(sparse->value[0]); +} + + +REAL getItem(sparseVector *sparse, int targetIndex) +{ + /* First check if we want the diagonal element */ + if(targetIndex == sparse->index[0]) + return(sparse->value[0]); + + /* If not, search for the variable's position in the index list */ + targetIndex = findIndex(targetIndex, sparse->index, sparse->count, BLAS_BASE); + if(targetIndex < 0) + return(0); + else + return(sparse->value[targetIndex]); +} + + +REAL addtoItem(sparseVector *sparse, int targetIndex, REAL value) +{ + int idx; + + if(targetIndex > 0) + idx = findIndex(targetIndex, sparse->index, sparse->count, BLAS_BASE); + else { + idx = -targetIndex; + if(idx > sparse->count) + /* Index error; ignore item */ + return(0.0); + } + + if(idx <=0 ) + value = putItem(sparse, targetIndex, value); + else { + value += sparse->value[idx]; + putItem(sparse, -idx, value); + } + return(value); +} + + +REAL putItem(sparseVector *sparse, int targetIndex, REAL value) +{ + REAL last = 0.0; + int posIndex; + + if(targetIndex < 0) { + posIndex = -targetIndex; + if(posIndex > sparse->count) + return(last); + targetIndex = sparse->index[posIndex]; + } + else + posIndex = findIndex(targetIndex, sparse->index, sparse->count, BLAS_BASE); + + if(fabs(value) < MACHINEPREC) + value = 0; + + if(targetIndex == sparse->index[0]) + sparse->value[0] = value; + + if(posIndex < 0) { + if(value != 0) { + if(sparse->count == sparse->size) + resizeVector(sparse, sparse->size + RESIZEDELTA); + posIndex = -posIndex; + sparse->count++; + if(posIndex < sparse->count) + moveVector(sparse, posIndex+1, posIndex, sparse->count-posIndex); + sparse->value[posIndex] = value; + sparse->index[posIndex] = targetIndex; + } + } + else { + if(value == 0) { + last = sparse->value[posIndex]; + if(sparse->count > posIndex) + moveVector(sparse, posIndex, posIndex+1, sparse->count-posIndex); + sparse->count--; + } + else { + sparse->value[posIndex] = value; + sparse->index[posIndex] = targetIndex; + } + } + +#ifdef DEBUG_SPARSELIB + verifyVector(sparse); +#endif + + return(last); +} + + +void swapItems(sparseVector *sparse, int firstIndex, int secondIndex) +{ + int i,j,ki,kj; + REAL hold; + + if(firstIndex == secondIndex) + return; + if(firstIndex > secondIndex) { + i = firstIndex; + firstIndex = secondIndex; + secondIndex = i; + } + + if(FALSE) { + i = 1; + ki = 0; + while(i <= sparse->count && (ki = sparse->index[i])count && (kj = sparse->index[j])index, sparse->count, BLAS_BASE); + if(i < 0) + i = -i; + j = findIndex(secondIndex, sparse->index, sparse->count, BLAS_BASE); + if(j < 0) + j = -j; + } + + if(i > sparse->count) + ki = 0; + else + ki = sparse->index[i]; + if(j > sparse->count) + kj = 0; + else + kj = sparse->index[j]; + + if(ki == firstIndex && kj == secondIndex) { /* Found both -> swap in place */ + hold = sparse->value[i]; + sparse->value[i] = sparse->value[j]; + sparse->value[j] = hold; + + if(sparse->index[0] == firstIndex) + sparse->value[0] = sparse->value[i]; + else if(sparse->index[0] == secondIndex) + sparse->value[0] = sparse->value[j]; + } + else if(ki == firstIndex) { /* Found first, but not the second -> shift left */ + j--; + if(i < j) { + hold = sparse->value[i]; + moveVector(sparse, i, i+1, j-i); + sparse->value[j] = hold; + } + sparse->index[j] = secondIndex; + + if(sparse->index[0] == firstIndex) + sparse->value[0] = 0; + else if(sparse->index[0] == secondIndex) + sparse->value[0] = sparse->value[j]; + + } + else if(kj == secondIndex) { /* Found second, but not the first -> shift right */ + if(i < j) { + hold = sparse->value[j]; + moveVector(sparse, i+1, i, j-i); + sparse->value[i] = hold; + } + sparse->index[i] = firstIndex; + + if(sparse->index[0] == firstIndex) + sparse->value[0] = sparse->value[i]; + else if(sparse->index[0] == secondIndex) + sparse->value[0] = 0; + } + +#ifdef DEBUG_SPARSELIB + verifyVector(sparse); +#endif + +} + + +void clearVector(sparseVector *sparse, int indexStart, int indexEnd) +{ + int i; + + i = sparse->count; + if(i==0) return; + + if(indexStart<=0) + indexStart=sparse->index[1]; + if(indexEnd<=0) + indexEnd=sparse->index[i]; + + if(indexStart>indexEnd) return; + + if(sparse->index[0]>=indexStart && sparse->index[0]<=indexEnd) { + sparse->value[0] = 0; + } + if(indexStart<=sparse->index[1] && indexEnd>=sparse->index[i]) + sparse->count = 0; + else { + while(i>0 && sparse->index[i]>indexEnd) i--; + indexEnd = i; + while(i>0 && sparse->index[i]>=indexStart) i--; + indexStart = i+1; + if(indexEnd>=indexStart) { + i = sparse->count-indexEnd; + moveVector(sparse, indexStart, indexEnd+1, i); + sparse->count -= indexEnd-indexStart+1; + } + } + +#ifdef DEBUG_SPARSELIB + verifyVector(sparse); +#endif + +} + + +int getVector(sparseVector *sparse, REAL *dense, int indexStart, int indexEnd, MYBOOL doClear) +{ + int i,k; + + i = 1; + while(i<=sparse->count && sparse->index[i]count && (k=sparse->index[i])<=indexEnd) { + while(indexStartvalue[i]; + indexStart++; + i++; + } + + while(indexStart<=indexEnd) { + dense[indexStart] = 0; + indexStart++; + } + + k = sparse->count; + if(doClear) { + sparse->count = 0; + sparse->value[0] = 0; + } + return(k); +} + +void putVector(sparseVector *sparse, REAL *dense, int indexStart, int indexEnd) +{ + int i,n; + + n = sparse->count; + if(indexStart<=0) + indexStart=sparse->index[1]; + if(indexEnd<=0) + indexEnd=sparse->index[n]; + + if(n==0 || sparse->index[n]index[0]; + if(i>=indexStart && i<=indexEnd) + sparse->value[0] = 0; + for(i = indexStart; i<=indexEnd; i++) { + if(dense[i] == 0) continue; + if(sparse->size == sparse->count) + resizeVector(sparse, sparse->size + RESIZEDELTA); + sparse->count++; + sparse->value[sparse->count] = dense[i]; + sparse->index[sparse->count] = i; + if(i == sparse->index[0]) + sparse->value[0] = dense[i]; + } + } + else { + while(indexStart <= indexEnd) { + putItem(sparse, indexStart, dense[indexStart]); + indexStart++; + } + } + +#ifdef DEBUG_SPARSELIB + verifyVector(sparse); +#endif + +} + + +void fillVector(sparseVector *sparse, int count, REAL value) +{ + int i; + + if(sparse->count > 0) + clearVector(sparse, 0, 0); + for(i = 1; i<=count; i++) + putItem(sparse, i, value); +} + + +REAL dotVector(sparseVector *sparse, REAL *dense, int indexStart, int indexEnd) +{ + int i, n; + long REAL sum; + + n = sparse->count; + sum = 0; + + if(n > 0) { + if(indexStart<=0) + indexStart=sparse->index[1]; + if(indexEnd<=0) + indexEnd=sparse->index[n]; + + if(indexStart > 1) { + i = findIndex(indexStart, sparse->index, sparse->count, BLAS_BASE); + if(i < 0) { + i = -i; + if(i > n) + return(sum); + } + } + else + i = 1; + + /* CPU intensive loop; provide alternative evaluation models */ +#if defined DOFASTMATH + { + /* Do fast pointer arithmetic */ + int *indexptr; + REAL *valueptr; +/* for(i = 1, indexptr = sparse->index + 1; + i <= n && (*indexptr) < indexStart; i++, indexptr++); */ + indexptr = sparse->index + i; + for(valueptr = sparse->value + i; + i <= n && (*indexptr) <= indexEnd; i++, indexptr++, valueptr++) + sum += (*valueptr) * dense[(*indexptr)]; + } +#else + { + /* Do traditional indexed access */ + int k; +/* i = 1; */ +/* while(i<=n && sparse->index[i]index[i])<=indexEnd) { + sum += sparse->value[i] * dense[k]; + i++; + } + } +#endif + } + + return(sum); +} + + +void daxpyVector1(sparseVector *sparse, REAL scalar, REAL *dense, int indexStart, int indexEnd) +{ + int i, n; + + if(scalar == 0) return; + + n = sparse->count; + if(indexStart<=0) + indexStart=sparse->index[1]; + if(indexEnd<=0) + indexEnd=sparse->index[n]; + + /* CPU intensive loop; provide alternative evaluation models */ +#if defined DOFASTMATH + { + /* Do fast pointer arithmetic */ + int *indexptr; + REAL *valueptr; + for(i = 1, indexptr = sparse->index + 1; + i <= n && (*indexptr) < indexStart; i++, indexptr++); + for(valueptr = sparse->value + i; + i <= n && (*indexptr) <= indexEnd; i++, indexptr++, valueptr++) + dense[(*indexptr)] += (*valueptr) * scalar; + } +#else + { + /* Do traditional indexed access */ + int k; + for(i = 1; i<= n; i++) { + k = sparse->index[i]; + if(kindexEnd) break; + dense[k] += sparse->value[i] * scalar; + } + } +#endif +} +void daxpyVector2(REAL *dense, REAL scalar, sparseVector *sparse, int indexStart, int indexEnd) +{ + sparseVector *hold; + + hold = createVector(sparse->limit, sparse->count); + putDiagonalIndex(hold, getDiagonalIndex(sparse)); + putVector(hold, dense, indexStart, indexEnd); + daxpyVector3(hold, scalar, sparse, indexStart, indexEnd); + freeVector(hold); +} +void daxpyVector3(sparseVector *sparse1, REAL scalar, sparseVector *sparse2, int indexStart, int indexEnd) +{ + int i1, i2, k, p1, p2, c1, c2; + sparseVector *hold; + + if(sparse1->count == 0) return; + + /* Spool to start positions */ + i1 = 1; + c1 = sparse1->count; + while(i1 <= c1 && sparse1->index[i1] < indexStart) i1++; + if(i1 <= c1) + p1 = sparse1->index[i1]; + else + p1 = indexEnd+1; + + i2 = 1; + c2 = sparse2->count; + while(i2 <= c2 && sparse2->index[i2] < indexStart) i2++; + if(i2 <= c2) + p2 = sparse2->index[i2]; + else + p2 = indexEnd+1; + + /* Create a temporary vector */ + k = c1+c2; + if(k > 0) { + hold = createVector(MAX(sparse1->limit, sparse2->limit), k); + putDiagonalIndex(hold, getDiagonalIndex(sparse2)); + } + else + hold = sparse2; + + /* Loop over all items in both vectors */ + while((i1 <= c1 && p1 <= indexEnd) || + (i2 <= c2 && p2 <= indexEnd)) { + + k = 0; + + /* Add/spool exclusive right-vector items */ + while(i2 <= c2 && p2 < p1) { + if(hold != sparse2) + putItem(hold, p2, sparse2->value[i2]); + i2++; + if(i2 <= c2) + p2 = sparse2->index[i2]; + else + p2 = indexEnd+1; + k++; + } + /* Add equal-indexed items */ + while(i1 <= c1 && i2 <= c2 && p1 == p2) { +/* if(hold != sparse2) */ + putItem(hold, p1, scalar*sparse1->value[i1]+sparse2->value[i2]); +/* else + addtoItem(sparse2, -i2, scalar*sparse1->value[i1]); */ + i1++; + if(i1 <= c1) + p1 = sparse1->index[i1]; + else + p1 = indexEnd+1; + i2++; + if(i2 <= c2) + p2 = sparse2->index[i2]; + else + p2 = indexEnd+1; + k++; + } + /* Add exclusive left-vector items */ + while(i1 <= c1 && p1 < p2) { + putItem(hold, p1, scalar*sparse1->value[i1]); +/* if(hold == sparse2) c2++; */ + i1++; + if(i1 <= c1) + p1 = sparse1->index[i1]; + else + p1 = indexEnd+1; + k++; + } + + if(k == 0) break; + } + +/* if(hold != sparse2) */ + { + swapVector(hold, sparse2); + freeVector(hold); + } + +#ifdef DEBUG_SPARSELIB + verifyVector(sparse2); +#endif + +} + + +void dswapVector1(sparseVector *sparse, REAL *dense, int indexStart, int indexEnd) +{ + int i, d, n; + REAL *x; + + if(indexStart <= 0) + indexStart = 1; + n = lastIndex(sparse); + if(indexEnd <= 0) + indexEnd = n; + CALLOC(x, (MAX(indexEnd,n)+1), REAL); + + getVector(sparse, x, indexStart, n, FALSE); + d = getDiagonalIndex(sparse); + clearVector(sparse, indexStart, n); + for(i = indexStart; i<=indexEnd; i++) { + if(dense[i] != 0) + putItem(sparse, i, dense[i]); + } + for(i = indexEnd+1; i<=n; i++) { + if(x[i] != 0) + putItem(sparse, i, x[i]); + } + MEMCOPY(&dense[indexStart], &x[indexStart], (indexEnd-indexStart+1)); + +#ifdef DEBUG_SPARSELIB + verifyVector(sparse); +#endif + + FREE(x); +} +void dswapVector2(REAL *dense, sparseVector *sparse, int indexStart, int indexEnd) +{ + dswapVector1(sparse, dense, indexStart, indexEnd); +} + + +void dswapVector3(sparseVector *sparse1, sparseVector *sparse2, int indexStart, int indexEnd) +{ + + REAL *dense1, *dense2; + + if(indexStart<=0) + indexStart = 1; + if(indexEnd<=0) + indexEnd = MAX(lastIndex(sparse1), lastIndex(sparse2)); + + if(indexStart <= firstIndex(sparse1) && indexStart <= firstIndex(sparse2) && + indexEnd >= lastIndex(sparse1) && indexEnd >= lastIndex(sparse2)) { + swapVector(sparse1, sparse2); + } + else { + + CALLOC(dense1, (indexEnd+1), REAL); + CALLOC(dense2, (indexEnd+1), REAL); + getVector(sparse1, dense1, indexStart, indexEnd, TRUE); + getVector(sparse2, dense2, indexStart, indexEnd, TRUE); + clearVector(sparse1, indexStart, indexEnd); + clearVector(sparse2, indexStart, indexEnd); + putVector(sparse1, dense2, indexStart, indexEnd); + putVector(sparse2, dense1, indexStart, indexEnd); + FREE(dense1); + FREE(dense2); + } +} + + +int idamaxVector(sparseVector *sparse, int is, REAL *maxValue) +{ + int i, n, imax; + REAL xmax; + + n = sparse->count; + imax = 1; + if(n == 0) + xmax = 0; + else { + xmax = fabs(sparse->value[imax]); + + /* CPU intensive loop; provide alternative evaluation models */ +#if defined DOFASTMATH + { + /* Do fast pointer arithmetic */ + int *indexptr; + REAL *valueptr; + for(i = 1, indexptr = sparse->index + 1; + i <= n && (*indexptr) <= is; i++, indexptr++); + for(valueptr = sparse->value + i; + i <= n; i++, indexptr++, valueptr++) { + if((*valueptr)>xmax) { + xmax = (*valueptr); + imax = (*indexptr); + } + } + } +#else + { + REAL xtest; + /* Do traditional indexed access */ + i = 1; + while(i <= n && sparse->index[i] <= is) i++; + for(; i<=n; i++) { + xtest = fabs(sparse->value[i]); + if(xtest>xmax) { + xmax = xtest; + imax = sparse->index[i]; + } + } + } +#endif + } + if(maxValue != NULL) + (*maxValue) = sparse->index[imax]; + return(imax); +} + + +void printVector(int n, sparseVector *sparse, int modulo ) +{ + int i,j,k; + + if(sparse == NULL) return; + + if (modulo <= 0) modulo = 5; + for (i = 1, j = 1; j<=n; i++, j++) { + if(i<=sparse->count) + k = sparse->index[i]; + else + k = n+1; + while (j < k) { + if(mod(j, modulo) == 1) + Rprintf("\n%2d:%12g", j, 0.0); + else + Rprintf(" %2d:%12g", j, 0.0); + j++; + } + if(k<=n) { + if(mod(j, modulo) == 1) + Rprintf("\n%2d:%12g", k, sparse->value[i]); + else + Rprintf(" %2d:%12g", k, sparse->value[i]); + } + } + if(mod(j, modulo) != 0) Rprintf("\n"); +} + + diff --git a/src/lpsolve/headers/run_headers/sparselib.h b/src/lpSolve/src/sparselib.h similarity index 100% rename from src/lpsolve/headers/run_headers/sparselib.h rename to src/lpSolve/src/sparselib.h diff --git a/src/lpsolve/headers/run_headers/ufortify.h b/src/lpSolve/src/ufortify.h similarity index 100% rename from src/lpsolve/headers/run_headers/ufortify.h rename to src/lpSolve/src/ufortify.h diff --git a/src/lpSolve/src/yacc_read.c b/src/lpSolve/src/yacc_read.c new file mode 100644 index 00000000..05859101 --- /dev/null +++ b/src/lpSolve/src/yacc_read.c @@ -0,0 +1,1114 @@ +/* + ============================================================================ + NAME : yacc_read.c + + PURPOSE : translation of lp-problem and storage in sparse matrix + + SHORT : Subroutines for yacc program to store the input in an intermediate + data-structure. The yacc and lex programs translate the input. First the + problemsize is determined and the date is read into an intermediate + structure, then readinput fills the sparse matrix. + + USAGE : call yyparse(); to start reading the input. call readinput(); to + fill the sparse matrix. + ============================================================================ + Rows : contains the amount of rows + 1. Rows-1 is the amount of constraints + (no bounds) Rows also contains the rownr 0 which is the objective function + + Columns : contains the amount of columns (different variable names found in + the constraints) + + Nonnuls : contains the amount of nonnuls = sum of different entries of all + columns in the constraints and in the objectfunction + + Hash_tab : contains all columnnames on the first level of the structure the + row information is kept under each column structure in a linked list (also + the objective funtion is in this structure) Bound information is also + stored under under the column name + + First_rside : points to a linked list containing all relational operators + and the righthandside values of the constraints the linked list is in + reversed order with respect to the rownumbers + ============================================================================ */ +#include +#include +#include +#include "lpkit.h" +#include "yacc_read.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +#define tol 1.0e-10 +#define coldatastep 100 + +#define HASHSIZE 10007 /* A prime number! */ + +static short Maximise; +static short Ignore_int_decl; +static short int_decl; +static short Ignore_sec_decl; +static short sos_decl; +static int Rows; +static int Columns; +static int Non_zeros; +static short *relat; +static int Verbose; +static hashtable *Hash_tab; +static hashtable *Hash_constraints; +static jmp_buf jump_buf; +static int *lineno; +static int Lin_term_count; +static char *title; + +struct structSOSvars { + char *name; + REAL weight; + struct structSOSvars *next; +}; + +static struct structSOS { + char *name; + short type; + int Nvars; + int weight; + struct structSOSvars *SOSvars, *LastSOSvars; + struct structSOS *next; +} *FirstSOS, *LastSOS; + +static struct _tmp_store_struct +{ + char *name; + int row; + REAL value; + REAL rhs_value; + short relat; +} tmp_store; + +static struct rside /* contains relational operator and rhs value */ +{ + int row; + REAL value; + REAL range_value; + struct rside *next; + short relat; + short range_relat; + char negate; +} *First_rside, *rs; + +struct column +{ + int row; + REAL value; + struct column *next; + struct column *prev; +}; + +static struct structcoldata { + int must_be_int; + int must_be_sec; + REAL upbo; + REAL lowbo; + struct column *firstcol; + struct column *col; +} *coldata; + +static void error(int verbose, const char *string) +{ + if(Verbose >= verbose) + report(NULL, verbose, "%s on line %d\n", string, *lineno); +} + +/* + * error handling routine for yyparse() + */ +void read_error(const char *string) +{ + error(CRITICAL, string); +} + +/* called when lex gets a fatal error */ +void lex_fatal_error(const char *msg) +{ + read_error(msg); + longjmp(jump_buf, 1); +} + +void add_row(void) +{ + Rows++; + rs = NULL; + Lin_term_count = 0; +} + +void check_int_sec_sos_decl(int within_int_decl, int within_sec_decl, int sos_decl0) +{ + Ignore_int_decl = TRUE; + Ignore_sec_decl = TRUE; + sos_decl = 0; + if(within_int_decl) { + Ignore_int_decl = FALSE; + int_decl = (short) within_int_decl; + } + else if(within_sec_decl) { + Ignore_sec_decl = FALSE; + } + else if(sos_decl0) { + sos_decl = (short) sos_decl0; + } +} + +static void add_int_var(char *name, short int_decl) +{ + hashelem *hp; + + if((hp = findhash(name, Hash_tab)) == NULL) { + char buf[256]; + + snprintf(buf, sizeof(buf), "Unknown variable %s declared integer, ignored", name); + error(NORMAL, buf); + } + else if(coldata[hp->index].must_be_int) { + char buf[256]; + + snprintf(buf, sizeof(buf), "Variable %s declared integer more than once, ignored", name); + error(NORMAL, buf); + } + else { + coldata[hp->index].must_be_int = TRUE; + if(int_decl == 2) { + if(coldata[hp->index].lowbo != -DEF_INFINITE * (REAL) 10.0) { + char buf[256]; + + snprintf(buf, sizeof(buf), "Variable %s: lower bound on variable redefined", name); + error(NORMAL, buf); + } + coldata[hp->index].lowbo = 0; + if(coldata[hp->index].upbo < DEF_INFINITE) { + char buf[256]; + + snprintf(buf, sizeof(buf), "Variable %s: upper bound on variable redefined", name); + error(NORMAL, buf); + } + coldata[hp->index].upbo = 1; + } + } +} + +static void add_sec_var(char *name) +{ + hashelem *hp; + + if((hp = findhash(name, Hash_tab)) == NULL) { + char buf[256]; + + snprintf(buf, sizeof(buf), "Unknown variable %s declared semi-continuous, ignored", name); + error(NORMAL, buf); + } + else if(coldata[hp->index].must_be_sec) { + char buf[256]; + + snprintf(buf, sizeof(buf), "Variable %s declared semi-continuous more than once, ignored", name); + error(NORMAL, buf); + } + else + coldata[hp->index].must_be_sec = TRUE; +} + +static int add_sos_name(char *name) +{ + struct structSOS *SOS; + + if(CALLOC(SOS, 1, struct structSOS) == NULL) + return(FALSE); + + if(MALLOC(SOS->name, strlen(name) + 1, char) == NULL) + { + FREE(SOS); + return(FALSE); + } + strcpy(SOS->name, name); + SOS->type = 0; + + if(FirstSOS == NULL) + FirstSOS = SOS; + else + LastSOS->next = SOS; + LastSOS = SOS; + + return(TRUE); +} + +static int add_sos_var(char *name) +{ + struct structSOSvars *SOSvar; + + if(name != NULL) { + if(CALLOC(SOSvar, 1, struct structSOSvars) == NULL) + return(FALSE); + + if(MALLOC(SOSvar->name, strlen(name) + 1, char) == NULL) + { + FREE(SOSvar); + return(FALSE); + } + strcpy(SOSvar->name, name); + + if(LastSOS->SOSvars == NULL) + LastSOS->SOSvars = SOSvar; + else + LastSOS->LastSOSvars->next = SOSvar; + LastSOS->LastSOSvars = SOSvar; + LastSOS->Nvars = LastSOS->Nvars + 1; + } + LastSOS->LastSOSvars->weight = 0; + + return(TRUE); +} + +void storevarandweight(char *name) +{ + if(!Ignore_int_decl) + add_int_var(name, int_decl); + else if(!Ignore_sec_decl) + add_sec_var(name); + else if(sos_decl==1) + add_sos_name(name); + else if(sos_decl==2) + add_sos_var(name); +} + +int set_sos_type(int SOStype) +{ + if(LastSOS != NULL) + LastSOS->type = (short) SOStype; + return(TRUE); +} + +int set_sos_weight(double weight, int sos_decl) +{ + if(LastSOS != NULL) { + if(sos_decl==1) + LastSOS->weight = (int) (weight+.1); + else + LastSOS->LastSOSvars->weight = weight; + } + return(TRUE); +} + +void set_obj_dir(int maximise) +{ + Maximise = (short) maximise; +} + +static int inccoldata(void) +{ + if(Columns == 0) + CALLOC(coldata, coldatastep, struct structcoldata); + else if((Columns%coldatastep) == 0) + REALLOC(coldata, Columns + coldatastep, struct structcoldata); + + if(coldata != NULL) { + coldata[Columns].upbo = (REAL) DEF_INFINITE; + coldata[Columns].lowbo = (REAL) -DEF_INFINITE * (REAL) 10.0; /* temporary. If still this value then 0 will be taken */ + coldata[Columns].col = NULL; + coldata[Columns].firstcol = NULL; + coldata[Columns].must_be_int = FALSE; + coldata[Columns].must_be_sec = FALSE; + } + + return(coldata != NULL); +} + +/* + * initialisation of hashstruct and globals. + */ +static int init_read(int verbose) +{ + int ok = FALSE; + + Verbose = verbose; + set_obj_dir(TRUE); + Rows = 0; + Non_zeros = 0; + Columns = 0; + FirstSOS = LastSOS = NULL; + Lin_term_count = 0; + if (CALLOC(First_rside, 1, struct rside) != NULL) { + rs = First_rside; + rs->value = rs->range_value = 0; + /* first row (nr 0) is always the objective function */ + rs->relat = OF; + rs->range_relat = -1; + Hash_tab = NULL; + Hash_constraints = NULL; + if (((Hash_tab = create_hash_table(HASHSIZE, 0)) == NULL) || + ((Hash_constraints = create_hash_table(HASHSIZE, 0)) == NULL)){ + FREE(First_rside); + FREE(Hash_tab); + FREE(Hash_constraints); + } + else + ok = TRUE; + } + return(ok); +} /* init */ + +/* + * clears the tmp_store variable after all information has been copied + */ +void null_tmp_store(int init_Lin_term_count) +{ + tmp_store.value = 0; + tmp_store.rhs_value = 0; + FREE(tmp_store.name); + if(init_Lin_term_count) + Lin_term_count = 0; +} + +/* + * variable : pointer to text array with name of variable + * row : the rownumber of the constraint + * value : value of matrixelement + * A(row, variable). + * Sign : (global) determines the sign of value. + * store() : stores value in matrix + * A(row, variable). If A(row, variable) already contains data, + * value is added to the existing value. + */ +static int store(char *variable, + int row, + REAL value) +{ + hashelem *h_tab_p; + struct column *col_p; + + if(value == 0) { + char buf[256]; + + snprintf(buf, sizeof(buf), "(store) Warning, variable %s has an effective coefficient of 0, Ignored", variable); + error(NORMAL, buf); + /* return(TRUE); */ + } + + if((h_tab_p = findhash(variable, Hash_tab)) == NULL) { + if (((h_tab_p = puthash(variable, Columns, NULL, Hash_tab)) == NULL) + ) return(FALSE); + inccoldata(); + Columns++; /* counter for calloc of final array */ + if(value) { + if (CALLOC(col_p, 1, struct column) == NULL) + return(FALSE); + Non_zeros++; /* for calloc of final arrays */ + col_p->row = row; + col_p->value = value; + coldata[h_tab_p->index].firstcol = coldata[h_tab_p->index].col = col_p; + } + } + else if((coldata[h_tab_p->index].col == NULL) || (coldata[h_tab_p->index].col->row != row)) { + if(value) { + if (CALLOC(col_p, 1, struct column) == NULL) + return(FALSE); + Non_zeros++; /* for calloc of final arrays */ + if(coldata[h_tab_p->index].col != NULL) + coldata[h_tab_p->index].col->prev = col_p; + else + coldata[h_tab_p->index].firstcol = col_p; + col_p->value = value; + col_p->row = row; + col_p->next = coldata[h_tab_p->index].col; + coldata[h_tab_p->index].col = col_p; + } + } + else if(value) { + coldata[h_tab_p->index].col->value += value; + if(fabs(coldata[h_tab_p->index].col->value) < tol) /* eliminitate rounding errors */ + coldata[h_tab_p->index].col->value = 0; + } + return(TRUE); +} /* store */ + +static int storefirst(void) +{ + struct rside *rp; + + if ((rs != NULL) && (rs->row == tmp_store.row)) + return(TRUE); + + /* make space for the rhs information */ + if (CALLOC(rp, 1, struct rside) == NULL) + return(FALSE); + rp->next = First_rside; + First_rside = rs = rp; + rs->row = /* row */ tmp_store.row; + rs->value = tmp_store.rhs_value; + rs->relat = tmp_store.relat; + rs->range_relat = -1; + + if(tmp_store.value != 0) { + if (!store(tmp_store.name, tmp_store.row, tmp_store.value)) + return(FALSE); + } + else { + char buf[256]; + + snprintf(buf, sizeof(buf), "Warning, variable %s has an effective coefficient of 0, ignored", tmp_store.name); + error(NORMAL, buf); + } + null_tmp_store(FALSE); + return(TRUE); +} + +/* + * store relational operator given in yylex[0] in the rightside list. + * Also checks if it constraint was a bound and if so stores it in the + * boundslist + */ +int store_re_op(char *yytext, int HadConstraint, int HadVar, int Had_lineair_sum) +{ + short tmp_relat; + + switch(yytext[0]) { + + case '=': + tmp_relat = EQ; + break; + + case '>': + tmp_relat = GE; + break; + + case '<': + tmp_relat = LE; + break; + + case 0: + if(rs != NULL) + tmp_relat = rs->relat; + else + tmp_relat = tmp_store.relat; + break; + + default: + { + char buf[256]; + + snprintf(buf, sizeof(buf), "Error: unknown relational operator %s", yytext); + error(CRITICAL, buf); + } + return(FALSE); + break; + } + + if(/* Lin_term_count > 1 */ HadConstraint && HadVar) {/* it is not a bound */ + if(Lin_term_count == 1) + if(!storefirst()) + return(FALSE); + rs->relat = tmp_relat; + } + else if(/* Lin_term_count == 0 */ HadConstraint && !Had_lineair_sum /* HadVar */ /* && (rs != NULL) */) { /* it is a range */ + if(Lin_term_count == 1) + if(!storefirst()) + return(FALSE); + if(rs == NULL) { /* range before row, already reported */ + error(CRITICAL, "Error: range for undefined row"); + return(FALSE); + } + + if(rs->negate) + switch (tmp_relat) { + case LE: + tmp_relat = GE; + break; + case GE: + tmp_relat = LE; + break; + } + + if(rs->range_relat != -1) { + error(CRITICAL, "Error: There was already a range for this row"); + return(FALSE); + } + else if(tmp_relat == rs->relat) { + error(CRITICAL, "Error: relational operator for range is the same as relation operator for equation"); + return(FALSE); + } + else + rs->range_relat = tmp_relat; + } + else /* could be a bound */ + tmp_store.relat = tmp_relat; + + return(TRUE); +} /* store_re_op */ + +int negate_constraint(void) +{ + if(rs != NULL) + rs->negate = TRUE; + + return(TRUE); +} + +/* + * store RHS value in the rightside structure + * if type = true then + */ +int rhs_store(REAL value, int HadConstraint, int HadVar, int Had_lineair_sum) +{ + if(/* Lin_term_count > 1 */ (HadConstraint && HadVar) || (Rows == 0)){ /* not a bound */ + if (Rows == 0) + value = -value; + /* if(Lin_term_count < 2) */ + if(rs == NULL) + tmp_store.rhs_value += value; + else + + if(rs == NULL) { + error(CRITICAL, "Error: No variable specified"); + return(FALSE); + } + else + rs->value += value; + } + else if(/* Lin_term_count == 0 */ HadConstraint && !HadVar) { /* a range */ + if(rs == NULL) /* if range before row, already reported */ + tmp_store.rhs_value += value; + else if(rs->range_relat < 0) /* was a bad range; ignore */; + else { + if(rs->negate) + value = -value; + if(((rs->relat == LE) && (rs->range_relat == GE) && + (rs->value < value)) || + ((rs->relat == GE) && (rs->range_relat == LE) && + (rs->value > value)) || + ((rs->relat == EQ) || (rs->range_relat == EQ))) { + rs->range_relat = -2; + error(CRITICAL, "Error: range restriction conflicts"); + return(FALSE); + } + else + rs->range_value += value; + } + } + else /* a bound */ + tmp_store.rhs_value += value; + return(TRUE); +} /* RHS_store */ + +/* + * store all data in the right place + * count the amount of lineair terms in a constraint + * only store in data-structure if the constraint is not a bound + */ +int var_store(char *var, REAL value, int HadConstraint, int HadVar, int Had_lineair_sum) +{ + int row; + + row = Rows; + + /* also in a bound the same var name can occur more than once. Check for + this. Don't increment Lin_term_count */ + + if(Lin_term_count != 1 || tmp_store.name == NULL || strcmp(tmp_store.name, var) != 0) + Lin_term_count++; + + /* always store objective function with rownr == 0. */ + if(row == 0) + return(store(var, row, value)); + + if(Lin_term_count == 1) { /* don't store yet. could be a bound */ + if(MALLOC(tmp_store.name, strlen(var) + 1, char) != NULL) + strcpy(tmp_store.name, var); + tmp_store.row = row; + tmp_store.value += value; + return(TRUE); + } + + if(Lin_term_count == 2) { /* now you can also store the first variable */ + if(!storefirst()) + return(FALSE); + /* null_tmp_store(FALSE); */ + } + + return(store(var, row, value)); +} /* var_store */ + + + +/* + * store the information in tmp_store because it is a bound + */ +int store_bounds(int warn) +{ + if(tmp_store.value != 0) { + hashelem *h_tab_p; + REAL boundvalue; + + if((h_tab_p = findhash(tmp_store.name, Hash_tab)) == NULL) { + /* a new columnname is found, create an entry in the hashlist */ + if ((h_tab_p = puthash(tmp_store.name, Columns, NULL, Hash_tab)) == NULL) { + error(CRITICAL, "Not enough memory"); + return(FALSE); + } + inccoldata(); + Columns++; /* counter for calloc of final array */ + } + + if(tmp_store.value < 0) { /* divide by negative number, */ + /* relational operator may change */ + if(tmp_store.relat == GE) + tmp_store.relat = LE; + else if(tmp_store.relat == LE) + tmp_store.relat = GE; + } + + boundvalue = tmp_store.rhs_value / tmp_store.value; + +#if FALSE + /* Check sanity of bound; all variables should be positive */ + if( ((tmp_store.relat == EQ) && (boundvalue < 0)) + || ((tmp_store.relat == LE) && (boundvalue < 0))) { /* Error */ + error(CRITICAL, "Error: variables must always be non-negative"); + return(FALSE); + } +#endif + +#if FALSE + if((tmp_store.relat == GE) && (boundvalue <= 0)) /* Warning */ + error(NORMAL, "Warning: useless bound; variables are always >= 0"); +#endif + + /* bound seems to be sane, add it */ + if((tmp_store.relat == GE) || (tmp_store.relat == EQ)) { + if(boundvalue > coldata[h_tab_p->index].lowbo - tol) + coldata[h_tab_p->index].lowbo = boundvalue; + else if(warn) + error(NORMAL, "Ineffective lower bound, ignored"); + } + if((tmp_store.relat == LE) || (tmp_store.relat == EQ)) { + if(boundvalue < coldata[h_tab_p->index].upbo + tol) + coldata[h_tab_p->index].upbo = boundvalue; + else if (warn) + error(NORMAL, "Ineffective upper bound, ignored"); + } + + /* check for empty range */ + if((warn) && (coldata[h_tab_p->index].upbo + tol < coldata[h_tab_p->index].lowbo)) { + error(CRITICAL, "Error: bound contradicts earlier bounds"); + return(FALSE); + } + } + else /* tmp_store.value = 0 ! */ { + char buf[256]; + + if((tmp_store.rhs_value == 0) || + ((tmp_store.rhs_value > 0) && (tmp_store.relat == LE)) || + ((tmp_store.rhs_value < 0) && (tmp_store.relat == GE))) { + snprintf(buf, sizeof(buf), "Variable %s has an effective coefficient of 0 in bound, ignored", + tmp_store.name); + if(warn) + error(NORMAL, buf); + } + else { + snprintf(buf, sizeof(buf), "Error, variable %s has an effective coefficient of 0 in bound", + tmp_store.name); + error(CRITICAL, buf); + return(FALSE); + } + } + + /* null_tmp_store(FALSE); */ + tmp_store.rhs_value = 0; + + return(TRUE); +} /* store_bounds */ + +int set_title(char *name) +{ + title = strdup(name); + return(TRUE); +} + +int add_constraint_name(char *name) +{ + int row; + hashelem *hp; + + if((hp = findhash(name, Hash_constraints)) != NULL) { + row = hp->index; + rs = First_rside; + while ((rs != NULL) && (rs->row != row)) + rs = rs->next; + } + else { + row = Rows; + if (((hp = puthash(name, row, NULL, Hash_constraints)) == NULL) + ) return(FALSE); + rs = NULL; + } + + return(TRUE); +} + +/* + * transport the data from the intermediate structure to the sparse matrix + * and free the intermediate structure + */ +static int readinput(lprec *lp) +{ + int i, count, index; + struct column *cp, *tcp; + hashelem *hp; + struct rside *rp; + MYBOOL *negate = NULL; + REAL *row = NULL, a; + int *rowno = NULL; + + if(lp != NULL) { + if (CALLOC(negate, 1 + Rows, MYBOOL) == NULL) + return(FALSE); + + /* fill names with the rownames */ + hp = Hash_constraints->first; + while(hp != NULL) { + if (!set_row_name(lp, hp->index, hp->name)) + return(FALSE); + hp = hp->nextelem; + } + } + + for(i = Rows;i >= 0;i--) { + rp = First_rside; + if((lp != NULL) && (rp != NULL)) { + negate[i] = rp->negate; + if (rp->negate) { + switch (rp->relat) { + case LE: + rp->relat = GE; + break; + case GE: + rp->relat = LE; + break; + } + switch (rp->range_relat) { + case LE: + rp->range_relat = GE; + break; + case GE: + rp->range_relat = LE; + break; + } + rp->range_value = -rp->range_value; + rp->value = -rp->value; + } + + if((rp->range_relat >= 0) && (rp->value == lp->infinite)) { + rp->value = rp->range_value; + rp->relat = rp->range_relat; + rp->range_relat = -1; + } + else if((rp->range_relat >= 0) && (rp->value == -lp->infinite)) { + rp->value = rp->range_value; + rp->relat = rp->range_relat; + rp->range_relat = -1; + } + if ((rp->range_relat >= 0) && (rp->range_value == rp->value)) { + rp->relat = EQ; + rp->range_relat = EQ; + } + if(i) { + set_constr_type(lp, i, rp->relat); + relat[i] = rp->relat; + } + set_rh(lp, i, rp->value); + if (rp->range_relat >= 0) + /* lp->orig_upbo[i] = my_abs(rp->range_value - rp->value); */ + /* set_uprange(lp, i, my_abs(rp->range_value - rp->value)); */ + set_rh_range(lp, i, rp->range_value - rp->value); + } + if(rp != NULL) { + First_rside = rp->next; + free(rp); /* free memory when data has been read */ + } + else + First_rside = NULL; + } + + while(First_rside != NULL) { + rp = First_rside; + First_rside = rp->next; + free(rp); /* free memory when data has been read */ + } + + /* start reading the Hash_list structure */ + index = 0; + + if((lp != NULL) && + ((MALLOC(row, 1 + Rows, REAL) == NULL) || (MALLOC(rowno, 1 + Rows, int) == NULL))) { + FREE(negate); + FREE(row); + FREE(rowno); + return(FALSE); + } + + /* for(i = 0; i < Hash_tab->size; i++) { + hp = Hash_tab->table[i]; */ + hp = Hash_tab->first; + while(hp != NULL) { + count = 0; + index++; + cp = coldata[hp->index].firstcol; + while(cp != NULL) { + if(lp != NULL) { + rowno[count] = cp->row; + a = cp->value; + if (negate[cp->row]) + a = -a; + row[count++] = a; + } + tcp = cp; + /* cp = cp->next; */ + cp = cp->prev; + free(tcp); /* free memory when data has been read */ + } + + if(lp != NULL) { + add_columnex(lp, count, row, rowno); + /* check for bound */ + if(coldata[hp->index].lowbo == -DEF_INFINITE * 10.0) + /* lp->orig_lowbo[Rows+index] = 0.0; */ + set_lowbo(lp, index, 0); + else + /* lp->orig_lowbo[Rows+index] = coldata[hp->index].lowbo; */ + set_lowbo(lp, index, coldata[hp->index].lowbo); + /* lp->orig_upbo[Rows+index] = coldata[hp->index].upbo; */ + set_upbo(lp, index, coldata[hp->index].upbo); + + /* check if it must be an integer variable */ + if(coldata[hp->index].must_be_int) { + /* lp->must_be_int[Rows + index]=TRUE; */ + set_int(lp, index, TRUE); + } + if(coldata[hp->index].must_be_sec) { + set_semicont(lp, index, TRUE); + } + + /* copy name of column variable */ + if (!set_col_name(lp, index, hp->name)) { + FREE(negate); + FREE(row); + FREE(rowno); + return(FALSE); + } + + /* put matrix values in intermediate row */ + /* cp = hp->col; */ + /* cp = hp->firstcol; */ + } + + /* thp = hp; */ + /* hp = hp->next; */ + /* free(thp->name); */ + /* free(thp); */ /* free memory when data has been read */ + + hp = hp->nextelem; + + } + /* Hash_tab->table[i] = NULL; */ + + FREE(coldata); + + while(FirstSOS != NULL) + { + struct structSOSvars *SOSvars, *SOSvars1; + int *sosvars, n; + REAL *weights; + hashelem *hp; + + LastSOS = FirstSOS; + FirstSOS = FirstSOS->next; + SOSvars = LastSOS->SOSvars; + if(lp != NULL) { + MALLOC(sosvars, LastSOS->Nvars, int); + MALLOC(weights, LastSOS->Nvars, double); + } + else { + sosvars = NULL; + weights = NULL; + } + n = 0; + while(SOSvars != NULL) + { + SOSvars1 = SOSvars; + SOSvars = SOSvars->next; + if((lp != NULL) && ((hp = findhash(SOSvars1->name, lp->colname_hashtab)) != NULL)) { + sosvars[n] = hp->index; + weights[n++] = SOSvars1->weight; + } + FREE(SOSvars1->name); + FREE(SOSvars1); + } + if(lp != NULL) { + add_SOS(lp, LastSOS->name, LastSOS->type, LastSOS->weight, n, sosvars, weights); + FREE(weights); + FREE(sosvars); + } + FREE(LastSOS->name); + FREE(LastSOS); + } + + /* the following should be replaced by a call to the MPS print routine MB */ + +#if 0 + if(Verbose) { + int j; + + printf("\n"); + printf("**********Data read**********\n"); + printf("Rows : %d\n", Rows); + printf("Columns : %d\n", Columns); + printf("Nonnuls : %d\n", Non_zeros); + printf("NAME LPPROB\n"); + printf("ROWS\n"); + for(i = 0; i <= Rows; i++) { + if(relat[i] == LE) + printf(" L "); + else if(relat[i] == EQ) + printf(" E "); + else if(relat[i] == GE) + printf(" G "); + else if(relat[i] == OF) + printf(" N "); + printf("%s\n", get_row_name(lp, i)); + } + + printf("COLUMNS\n"); + j = 0; + for(i = 0; i < Non_zeros; i++) { + if(i == lp->col_end[j]) + j++; + printf(" %-8s %-8s %g\n", get_col_name(lp, j), + get_row_name(lp, lp->mat[i].row_nr), (double)lp->mat[i].value); + } + + printf("RHS\n"); + for(i = 0; i <= Rows; i++) { + printf(" RHS %-8s %g\n", get_row_name(lp, i), + (double)lp->orig_rhs[i]); + } + + printf("RANGES\n"); + for(i = 1; i <= Rows; i++) + if((lp->orig_upbo[i] != lp->infinite) && (lp->orig_upbo[i] != 0)) { + printf(" RGS %-8s %g\n", get_row_name(lp, i), + (double)lp->orig_upbo[i]); + } + else if((lp->orig_lowbo[i] != 0)) { + printf(" RGS %-8s %g\n", get_row_name(lp, i), + (double)-lp->orig_lowbo[i]); + } + + printf("BOUNDS\n"); + for(i = Rows + 1; i <= Rows + Columns; i++) { + if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite) && + (lp->orig_lowbo[i] == lp->orig_upbo[i])) { + printf(" FX BND %-8s %g\n", get_col_name(lp, i - Rows), + (double)lp->orig_upbo[i]); + } + else { + if(lp->orig_upbo[i] < lp->infinite) + printf(" UP BND %-8s %g\n", get_col_name(lp, i - Rows), + (double)lp->orig_upbo[i]); + if(lp->orig_lowbo[i] > 0) + printf(" LO BND %-8s %g\n", get_col_name(lp, i - Rows), + (double)lp->orig_lowbo[i]); + } + } + + printf("ENDATA\n"); + } +#endif + + FREE(row); + FREE(rowno); + FREE(negate); + return(TRUE); +} /* readinput */ + +lprec *yacc_read(lprec *lp, int verbose, char *lp_name, int *_lineno, int (*parse) (void), void (*delete_allocated_memory) (void)) +{ + REAL *orig_upbo; + int stat = -1; + lprec *lp0 = lp; + REAL *trash = 0; + + lineno = _lineno; + title = lp_name; + + if(!init_read(verbose)) + error(CRITICAL, "init_read failed"); + else if (setjmp(jump_buf) == 0) + stat = parse(); + + delete_allocated_memory(); + + Rows--; + + relat = NULL; + if((stat != 0) || (CALLOC(relat, Rows + 1, short) != NULL)) { + if(stat == 0) { + if(lp == NULL) { + lp = make_lp(Rows, 0); + } + else { + int NRows; + + for(NRows = get_Nrows(lp); NRows < Rows; NRows++) + add_constraintex(lp, 0, NULL, NULL, LE, 0); + } + } + else + lp = NULL; + if ((stat != 0) || (lp != NULL)) { + if(lp != NULL) { + set_verbose(lp, Verbose); + } + + if (!readinput(lp)) { + if((lp != NULL) && (lp0 == NULL)) + delete_lp(lp); + lp = NULL; + } + + if(lp != NULL) { + set_lp_name(lp, title); + if(Maximise) + set_maxim(lp); + + if(Rows) { + int row; + + /* gcc 9 warns if we don't assign the value, and use the var :/ */ + trash = MALLOCCPY(orig_upbo, lp->orig_upbo, 1 + Rows, REAL); + (void) trash; + for(row = 1; row <= Rows; row++) + set_constr_type(lp, row, relat[row]); + + memcpy(lp->orig_upbo, orig_upbo, (1 + Rows) * sizeof(*orig_upbo)); /* restore upper bounds (range) */ + FREE(orig_upbo); + } + } + if((title != NULL) && (title != lp_name)) + free(title); + + free_hash_table(Hash_tab); + free_hash_table(Hash_constraints); + } + FREE(relat); + } + null_tmp_store(FALSE); + return(lp); +} diff --git a/src/lpsolve/headers/run_headers/yacc_read.h b/src/lpSolve/src/yacc_read.h similarity index 92% rename from src/lpsolve/headers/run_headers/yacc_read.h rename to src/lpSolve/src/yacc_read.h index 0fa53f4c..51b8dd4d 100644 --- a/src/lpsolve/headers/run_headers/yacc_read.h +++ b/src/lpSolve/src/yacc_read.h @@ -3,7 +3,7 @@ #ifndef __READ_H__ #define __READ_H__ -void lex_fatal_error(char *msg); +void lex_fatal_error(const char *msg); int set_title(char *name); int add_constraint_name(char *name); int store_re_op(char *yytext, int HadConstraint, int HadVar, int Had_lineair_sum); @@ -18,7 +18,7 @@ int negate_constraint(void); void add_row(void); void set_obj_dir(int maximise); -void read_error(char *); +void read_error(const char *); void check_int_sec_sos_decl(int, int, int); lprec *yacc_read(lprec *lp, int verbose, char *lp_name, int *_lineno, int (*parse) (void), void (*delete_allocated_memory) (void)); #endif diff --git a/src/lpsolve/build/lp_solve/lp_BFP1.c b/src/lpsolve/build/lp_solve/lp_BFP1.c deleted file mode 100644 index 46fc57dc..00000000 --- a/src/lpsolve/build/lp_solve/lp_BFP1.c +++ /dev/null @@ -1,206 +0,0 @@ - -/* Routines located in lp_BFP1.cpp; common for all factorization engines */ -/* Cfr. lp_BFP.h for definitions */ -/* ---------------------------------------------------------------------------------- */ -/* Changes: */ -/* 29 May 2004 Corrected calculation of bfp_efficiency(), which required */ -/* modifying the max_Bsize to include slack variables. KE. */ -/* 16 June 2004 Make the symbolic minimum degree ordering routine available */ -/* to BFPs as a routine internal to the library. KE */ -/* 1 July 2004 Change due to change in MDO naming. */ -/* ---------------------------------------------------------------------------------- */ - - -/* MUST MODIFY */ -MYBOOL BFP_CALLMODEL bfp_compatible(lprec *lp, int bfpversion, int lpversion, int sizeofvar) -{ - MYBOOL status = FALSE; - - if((lp != NULL) && (bfpversion == BFPVERSION) && (sizeof(LPSREAL) == sizeofvar)) { -#if 0 - if(lpversion == MAJORVERSION) /* Forces BFP renewal at lp_solve major version changes */ -#endif - status = TRUE; - } - return( status ); -} - -/* DON'T MODIFY */ -int BFP_CALLMODEL bfp_status(lprec *lp) -{ - return(lp->invB->status); -} - -/* DON'T MODIFY */ -int BFP_CALLMODEL bfp_indexbase(lprec *lp) -{ - return( MATINDEXBASE ); -} - -/* DON'T MODIFY */ -int BFP_CALLMODEL bfp_rowoffset(lprec *lp) -{ - if(lp->obj_in_basis) - return( 1 ); - else - return( 0 ); -} - -/* DON'T MODIFY */ -int BFP_CALLMODEL bfp_pivotmax(lprec *lp) -{ - if(lp->max_pivots > 0) - return( lp->max_pivots ); - else - return( DEF_MAXPIVOT ); -} - -/* DON'T MODIFY */ -LPSREAL * BFP_CALLMODEL bfp_pivotvector(lprec *lp) -{ - return( lp->invB->pcol ); -} - -/* DON'T MODIFY */ -LPSREAL BFP_CALLMODEL bfp_efficiency(lprec *lp) -{ - LPSREAL hold; - - hold = lp->bfp_nonzeros(lp, AUTOMATIC); - if(hold == 0) - hold = 1 + lp->rows; - hold = lp->bfp_nonzeros(lp, TRUE)/hold; - - return(hold); -} - -/* DON'T MODIFY */ -int BFP_CALLMODEL bfp_pivotcount(lprec *lp) -{ - return(lp->invB->num_pivots); -} - - -/* DON'T MODIFY */ -int BFP_CALLMODEL bfp_refactcount(lprec *lp, int kind) -{ - if(kind == BFP_STAT_REFACT_TOTAL) - return(lp->invB->num_refact); - else if(kind == BFP_STAT_REFACT_TIMED) - return(lp->invB->num_timed_refact); - else if(kind == BFP_STAT_REFACT_DENSE) - return(lp->invB->num_dense_refact); - else - return( BFP_STAT_ERROR ); -} - -/* DON'T MODIFY */ -MYBOOL BFP_CALLMODEL bfp_mustrefactorize(lprec *lp) -{ - MYBOOL test = lp->is_action(lp->spx_action, ACTION_REINVERT | ACTION_TIMEDREINVERT); - if(!test) { - LPSREAL f; - INVrec *lu = lp->invB; - - if(lu->num_pivots > 0) - f = (timeNow()-lu->time_refactstart) / (LPSREAL) lu->num_pivots; - else - f = 0; - - /* Always refactorize if we are above the set pivot limit */ - if(lu->force_refact || - (lu->num_pivots >= lp->bfp_pivotmax(lp))) - lp->set_action(&lp->spx_action, ACTION_REINVERT); - - /* Check if we should do an optimal time-based refactorization */ - else if(lu->timed_refact && (lu->num_pivots > 1) && - (f > MIN_TIMEPIVOT) && (f > lu->time_refactnext)) { - /* If we have excessive time usage in automatic mode then - treat as untimed case and update optimal time metric, ... */ - if((lu->timed_refact == AUTOMATIC) && - (lu->num_pivots < 0.4*lp->bfp_pivotmax(lp))) - lu->time_refactnext = f; - /* ... otherwise set flag for the optimal time-based refactorization */ - else - lp->set_action(&lp->spx_action, ACTION_TIMEDREINVERT); - } - - /* Otherwise simply update the optimal time metric */ - else - lu->time_refactnext = f; -#if 0 - if(lu->num_pivots % 10 == 0) - lp->report(lp, NORMAL, "bfp pivot %d - start %f - timestat %f", - lu->num_pivots, lu->time_refactstart, f); -#endif - } - - test = lp->is_action(lp->spx_action, ACTION_REINVERT | ACTION_TIMEDREINVERT); - return(test); -} - -/* DON'T MODIFY */ -MYBOOL BFP_CALLMODEL bfp_isSetI(lprec *lp) -{ - return( (MYBOOL) lp->invB->set_Bidentity ); -} - -/* DON'T MODIFY */ -int *bfp_createMDO(lprec *lp, MYBOOL *usedpos, int count, MYBOOL doMDO) -{ - int *mdo, i, j, kk; - - mdo = (int *) malloc((count + 1)*sizeof(*mdo)); -/* allocINT(lp, &mdo, count + 1, FALSE); */ - - /* Fill the mdo[] array with remaining full-pivot basic user variables */ - kk = 0; - for(j = 1; j <= lp->columns; j++) { - i = lp->rows + j; - if(usedpos[i] == TRUE) { - kk++; - mdo[kk] = i; - } - } - mdo[0] = kk; - if(kk == 0) - goto Process; - - /* Calculate the approximate minimum degree column ordering */ - if(doMDO) { - i = lp->getMDO(lp, usedpos, mdo, NULL, FALSE); - if(i != 0) { - lp->report(lp, CRITICAL, "bfp_createMDO: Internal error %d in minimum degree ordering routine", i); - FREE(mdo); - } - } -Process: - return( mdo ); -} -void BFP_CALLMODEL bfp_updaterefactstats(lprec *lp) -{ - INVrec *lu = lp->invB; - - /* Signal that we are refactorizing */ - lu->is_dirty = AUTOMATIC; - - /* Set time of start of current refactorization cycle */ - lu->time_refactstart = timeNow(); - lu->time_refactnext = 0; - lu->user_colcount = 0; - - /* Do the numbers */ - if(lu->force_refact) - lu->num_dense_refact++; - else if(lu->timed_refact && lp->is_action(lp->spx_action, ACTION_TIMEDREINVERT)) - lu->num_timed_refact++; - lu->num_refact++; -} - -int BFP_CALLMODEL bfp_rowextra(lprec *lp) -{ - if(lp->is_obj_in_basis(lp)) - return( 1 ); - else - return( 0 ); -} diff --git a/src/lpsolve/build/lp_solve/lp_BFP2.c b/src/lpsolve/build/lp_solve/lp_BFP2.c deleted file mode 100644 index 0a01ecab..00000000 --- a/src/lpsolve/build/lp_solve/lp_BFP2.c +++ /dev/null @@ -1,177 +0,0 @@ - - -/* Routines located in lp_BFP2.cpp; optional shared for canned implementations */ -/* Cfr. lp_BFP.h for definitions */ -/* ---------------------------------------------------------------------------------- */ - - -/* DON'T MODIFY */ -MYBOOL BFP_CALLMODEL bfp_init(lprec *lp, int size, int delta, char *options) -{ - INVrec *lu; - - lp->invB = (INVrec *) calloc(1, sizeof(*(lp->invB))); - lu = lp->invB; - if((lu == NULL) || - !lp->bfp_resize(lp, size) || - !lp->bfp_restart(lp)) - return( FALSE ); - - /* Store any passed options */ - if(options != NULL) { - size_t len = strlen(options); - lu->opts = (char *) malloc(len + 1); - strcpy(lu->opts, options); - } - - /* Prepare for factorization and undo values reset by bfp_preparefactorization */ - lp->bfp_preparefactorization(lp); - lu->num_refact = 0; - - return( TRUE ); -} - -/* DON'T MODIFY */ -MYBOOL BFP_CALLMODEL bfp_restart(lprec *lp) -{ - INVrec *lu; - - lu = lp->invB; - if(lu == NULL) - return( FALSE ); - - lu->status = BFP_STATUS_SUCCESS; - lu->max_Bsize = 0; /* The largest NZ-count of the B matrix */ - lu->max_colcount = 0; /* The maximum number of user columns in B */ - lu->max_LUsize = 0; /* The largest NZ-count of LU-files generated */ - lu->num_refact = 0; /* The number of times the basis has been factored */ - lu->num_timed_refact = 0; - lu->num_dense_refact = 0; - lu->num_pivots = 0; /* The number of pivots since last factorization */ - lu->pcol = NULL; - lu->set_Bidentity = FALSE; - - return( TRUE ); -} - -/* DON'T MODIFY */ -MYBOOL BFP_CALLMODEL bfp_implicitslack(lprec *lp) -{ - return( FALSE ); -} - -/* DON'T MODIFY */ -int BFP_CALLMODEL bfp_colcount(lprec *lp) -{ - return(lp->invB->user_colcount); -} - - -/* DON'T MODIFY */ -MYBOOL BFP_CALLMODEL bfp_canresetbasis(lprec *lp) -{ - return( FALSE ); -} - - -/* DON'T MODIFY */ -MYBOOL BFP_CALLMODEL bfp_pivotalloc(lprec *lp, int newsize) -{ - /* Does nothing in the default implementation */ - return( TRUE ); -} - - -/* DON'T MODIFY */ -void BFP_CALLMODEL bfp_finishfactorization(lprec *lp) -{ - INVrec *lu; - - lu = lp->invB; - - SETMAX(lu->max_colcount, lp->bfp_colcount(lp)); - SETMAX(lu->max_LUsize, lp->bfp_nonzeros(lp, FALSE)); - - /* Signal that we done factorizing/reinverting */ - lu->is_dirty = FALSE; - lp->clear_action(&lp->spx_action, ACTION_REINVERT | ACTION_TIMEDREINVERT); - lu->force_refact = FALSE; - - /* Store information about the current inverse */ - lu->num_pivots = 0; - -} - - -/* DON'T MODIFY */ -LREAL BFP_CALLMODEL bfp_prepareupdate(lprec *lp, int row_nr, int col_nr, LPSREAL *pcol) -/* Was condensecol() in versions of lp_solve before 4.0.1.8 - KE */ -{ - LREAL pivValue; - INVrec *lu; - - lu = lp->invB; - - /* Store the incoming pivot value for RHS update purposes */ - lu->col_enter = col_nr; /* The index of the new data column */ - lu->col_pos = row_nr; /* The basis column to be replaced */ - lu->col_leave = lp->var_basic[row_nr]; - if(pcol == NULL) - pivValue = 0; - else - pivValue = pcol[row_nr]; - lu->theta_enter = pivValue; - - /* Save reference to the elimination vector */ - lu->pcol = pcol; - - /* Set completion status; but hold if we are reinverting */ - if(lu->is_dirty != AUTOMATIC) - lu->is_dirty = TRUE; - - return( pivValue ); -} - - -/* DON'T MODIFY */ -LPSREAL BFP_CALLMODEL bfp_pivotRHS(lprec *lp, LREAL theta, LPSREAL *pcol) -/* This function is used to adjust the RHS in bound swap operations as - well as handling the updating of the RHS for normal basis changes. - Was rhsmincol(), ie. "rhs minus column" in versions of lp_solve before 4.0.1.8 - KE */ -{ - INVrec *lu; - - lu = lp->invB; - - if(pcol == NULL) - pcol = lu->pcol; - - if(theta != 0) { - register int i, n = lp->rows; - register LREAL roundzero = lp->epsvalue; - register LREAL *rhs = lp->rhs, rhsmax = 0; - - for(i = 0; i <= n; i++, rhs++, pcol++) { - (*rhs) -= theta * (*pcol); - my_roundzero(*rhs, roundzero); - SETMAX(rhsmax, fabs(*rhs)); - } - lp->rhsmax = rhsmax; - } - - if(pcol == lu->pcol) - return( lu->theta_enter ); - else - return( 0.0 ); -} - - -/* DON'T MODIFY */ -void BFP_CALLMODEL bfp_btran_double(lprec *lp, LPSREAL *prow, int *pnzidx, LPSREAL *drow, int *dnzidx) -{ - if(prow != NULL) - lp->bfp_btran_normal(lp, prow, pnzidx); - if(drow != NULL) - lp->bfp_btran_normal(lp, drow, dnzidx); -} - diff --git a/src/lpsolve/build/lp_solve/lp_Hash.c b/src/lpsolve/build/lp_solve/lp_Hash.c deleted file mode 100644 index 5a5276ac..00000000 --- a/src/lpsolve/build/lp_solve/lp_Hash.c +++ /dev/null @@ -1,238 +0,0 @@ - -#include -#include -#include "lp_lib.h" -#include "lp_utils.h" -#include "lp_report.h" -#include "lp_Hash.h" - -#ifdef FORTIFY -# include "lp_fortify.h" -#endif - - -#define HASH_START_SIZE 5000 /* Hash table size for row and column name storage */ -#define NUMHASHPRIMES 45 - -STATIC hashtable *create_hash_table(int size, int base) -{ - int i; - int HashPrimes[ ] = { - 29, 229, 883, 1671, 2791, 4801, 8629, 10007, - 15289, 25303, 34843, 65269, 99709, 129403, 147673, 166669, - 201403, 222163, 242729, 261431, 303491, 320237, 402761, 501131, - 602309, 701507, 800999, 900551, 1000619, 1100837, 1200359, 1300021, - 1400017, 1500007, 1750009, 2000003, 2500009, 3000017, 4000037, 5000011, - 6000011, 7000003, 8000009, 9000011, 9999991}; - hashtable *ht; - - /* Find a good size for the hash table */ - if(size < HASH_START_SIZE) - size = HASH_START_SIZE; - for(i = 0; i < NUMHASHPRIMES-1; i++) - if(HashPrimes[i] > size) - break; - size = HashPrimes[i]; - - /* Then allocate and initialize memory */ - ht = (hashtable *) calloc(1 , sizeof(*ht)); - ht->table = (hashelem **) calloc(size, sizeof(*(ht->table))); - ht->size = size; - ht->base = base; - ht->count = base-1; - - return(ht); -} - -STATIC void free_hash_item(hashelem **hp) -{ - free((*hp)->name); - free(*hp); - *hp = NULL; -} - -STATIC void free_hash_table(hashtable *ht) -{ - hashelem *hp, *thp; - - hp = ht->first; - while(hp != NULL) { - thp = hp; - hp = hp->nextelem; - free_hash_item(&thp); - } - free(ht->table); - free(ht); -} - - -/* make a good hash function for any int size */ -/* inspired by Aho, Sethi and Ullman, Compilers ..., p436 */ -#define HASH_1 sizeof(unsigned int) -#define HASH_2 (sizeof(unsigned int) * 6) -#define HASH_3 (((unsigned int)0xF0) << ((sizeof(unsigned int) - 1) * CHAR_BIT)) - -STATIC int hashval(const char *string, int size) -{ - unsigned int result = 0, tmp; - - for(; *string; string++) { - result = (result << HASH_1) + *string; - if((tmp = result & HASH_3) != 0) { - /* if any of the most significant bits is on */ - result ^= tmp >> HASH_2; /* xor them in in a less significant part */ - result ^= tmp; /* and reset the most significant bits to 0 */ - } - } - return(result % size); -} /* hashval */ - - -STATIC hashelem *findhash(const char *name, hashtable *ht) -{ - hashelem *h_tab_p; - for(h_tab_p = ht->table[hashval(name, ht->size)]; - h_tab_p != NULL; - h_tab_p = h_tab_p->next) - if(strcmp(name, h_tab_p->name) == 0) /* got it! */ - break; - return(h_tab_p); -} /* findhash */ - - -STATIC hashelem *puthash(const char *name, int index, hashelem **list, hashtable *ht) -{ - hashelem *hp = NULL; - int hashindex; - - if(list != NULL) { - hp = list[index]; - if(hp != NULL) - list[index] = NULL; - } - - if((hp = findhash(name, ht)) == NULL) { - - hashindex = hashval(name, ht->size); - hp = (hashelem *) calloc(1, sizeof(*hp)); - allocCHAR(NULL, &hp->name, (int) (strlen(name) + 1), FALSE); - strcpy(hp->name, name); - hp->index = index; - ht->count++; - if(list != NULL) - list[index] = hp; - - hp->next = ht->table[hashindex]; - ht->table[hashindex] = hp; - if(ht->first == NULL) - ht->first = hp; - if(ht->last != NULL) - ht->last->nextelem = hp; - ht->last = hp; - - } - return(hp); -} - -STATIC void drophash(const char *name, hashelem **list, hashtable *ht) { - hashelem *hp, *hp1, *hp2; - int hashindex; - - if((hp = findhash(name, ht)) != NULL) { - hashindex = hashval(name, ht->size); - if((hp1 = ht->table[hashindex]) != NULL) { - hp2 = NULL; - while((hp1 != NULL) && (hp1 != hp)) { - hp2 = hp1; - hp1 = hp1->next; - } - if(hp1 == hp) { - if(hp2 != NULL) - hp2->next = hp->next; - else - ht->table[hashindex] = hp->next; - } - - hp1 = ht->first; - hp2 = NULL; - while((hp1 != NULL) && (hp1 != hp)) { - hp2 = hp1; - hp1 = hp1->nextelem; - } - if(hp1 == hp) { - if(hp2 != NULL) - hp2->nextelem = hp->nextelem; - else { - ht->first = hp->nextelem; - if (ht->first == NULL) - ht->last = NULL; - } - } - if(list != NULL) - list[hp->index] = NULL; - free_hash_item(&hp); - ht->count--; - } - } -} - -STATIC hashtable *copy_hash_table(hashtable *ht, hashelem **list, int newsize) -{ - hashtable *copy; - hashelem *elem, *new_elem; - - if(newsize < ht->size) - newsize = ht->size; - - copy = create_hash_table(newsize, ht->base); - if (copy != NULL) { - elem = ht->first; - while (elem != NULL) { - if((new_elem = puthash(elem->name, elem->index, list, copy)) == NULL) { - free_hash_table(copy); - return(NULL); - } - elem = elem ->nextelem; - } - } - - return(copy); -} - -STATIC int find_row(lprec *lp, char *name, MYBOOL Unconstrained_rows_found) -{ - hashelem *hp; - - if (lp->rowname_hashtab != NULL) - hp = findhash(name, lp->rowname_hashtab); - else - hp = NULL; - - if (hp == NULL) { - if(Unconstrained_rows_found) { /* just ignore them in this case */ - return(-1); - } - else { - return(-1); - } - } - return(hp->index); -} - -STATIC int find_var(lprec *lp, char *name, MYBOOL verbose) -{ - hashelem *hp; - - if (lp->colname_hashtab != NULL) - hp = findhash(name, lp->colname_hashtab); - else - hp = NULL; - - if (hp == NULL) { - if(verbose) - report(lp, SEVERE, "find_var: Unknown variable name '%s'\n", name); - return(-1); - } - return(hp->index); -} - diff --git a/src/lpsolve/build/lp_solve/lp_crash.c b/src/lpsolve/build/lp_solve/lp_crash.c deleted file mode 100644 index 8678983c..00000000 --- a/src/lpsolve/build/lp_solve/lp_crash.c +++ /dev/null @@ -1,863 +0,0 @@ - -/* - ---------------------------------------------------------------------------------- - Crash management routines in lp_solve v5.0+ - ---------------------------------------------------------------------------------- - Author: Kjell Eikland - Contact: kjell.eikland@broadpark.no - License terms: LGPL. - - Requires: lp_lib.h, lp_utils.h, lp_matrix.h - - Release notes: - v1.0.0 1 April 2004 First version. - v1.1.0 20 July 2004 Reworked with flexible matrix storage model. - - ---------------------------------------------------------------------------------- -*/ - -#include - -#include "commonlib.h" -#include "lp_lib.h" -#include "lp_scale.h" -#include "lp_utils.h" -#include "lp_report.h" -#include "lp_matrix.h" -#include "lp_crash.h" - -#ifdef FORTIFY -# include "lp_fortify.h" -#endif - - -MYBOOL crash_basis(lprec *lp) -{ - int i; - MATrec *mat = lp->matA; - MYBOOL ok = TRUE; - - /* Initialize basis indicators */ - if(lp->basis_valid) - lp->var_basic[0] = FALSE; - else - default_basis(lp); - - /* Set initial partial pricing blocks */ - if(lp->rowblocks != NULL) - lp->rowblocks->blocknow = 1; - if(lp->colblocks != NULL) - lp->colblocks->blocknow = ((lp->crashmode == CRASH_NONE) || (lp->colblocks->blockcount == 1) ? 1 : 2); - - /* Construct a basis that is in some measure the "most feasible" */ - if((lp->crashmode == CRASH_MOSTFEASIBLE) && mat_validate(mat)) { - /* The logic here follows Maros */ - LLrec *rowLL = NULL, *colLL = NULL; - int ii, rx, cx, ix, nz; - LPSREAL wx, tx, *rowMAX = NULL, *colMAX = NULL; - int *rowNZ = NULL, *colNZ = NULL, *rowWT = NULL, *colWT = NULL; - LPSREAL *value; - int *rownr, *colnr; - - report(lp, NORMAL, "crash_basis: 'Most feasible' basis crashing selected\n"); - - /* Tally row and column non-zero counts */ - ok = allocINT(lp, &rowNZ, lp->rows+1, TRUE) && - allocINT(lp, &colNZ, lp->columns+1, TRUE) && - allocREAL(lp, &rowMAX, lp->rows+1, FALSE) && - allocREAL(lp, &colMAX, lp->columns+1, FALSE); - if(!ok) - goto Finish; - - nz = mat_nonzeros(mat); - rownr = &COL_MAT_ROWNR(0); - colnr = &COL_MAT_COLNR(0); - value = &COL_MAT_VALUE(0); - for(i = 0; i < nz; - i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) { - rx = *rownr; - cx = *colnr; - wx = fabs(*value); - rowNZ[rx]++; - colNZ[cx]++; - if(i == 0) { - rowMAX[rx] = wx; - colMAX[cx] = wx; - colMAX[0] = wx; - } - else { - SETMAX(rowMAX[rx], wx); - SETMAX(colMAX[cx], wx); - SETMAX(colMAX[0], wx); - } - } - /* Reduce counts for small magnitude to preserve stability */ - rownr = &COL_MAT_ROWNR(0); - colnr = &COL_MAT_COLNR(0); - value = &COL_MAT_VALUE(0); - for(i = 0; i < nz; - i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) { - rx = *rownr; - cx = *colnr; - wx = fabs(*value); -#ifdef CRASH_SIMPLESCALE - if(wx < CRASH_THRESHOLD * colMAX[0]) { - rowNZ[rx]--; - colNZ[cx]--; - } -#else - if(wx < CRASH_THRESHOLD * rowMAX[rx]) - rowNZ[rx]--; - if(wx < CRASH_THRESHOLD * colMAX[cx]) - colNZ[cx]--; -#endif - } - - /* Set up priority tables */ - ok = allocINT(lp, &rowWT, lp->rows+1, TRUE); - createLink(lp->rows, &rowLL, NULL); - ok &= (rowLL != NULL); - if(!ok) - goto Finish; - for(i = 1; i <= lp->rows; i++) { - if(get_constr_type(lp, i)==EQ) - ii = 3; - else if(lp->upbo[i] < lp->infinite) - ii = 2; - else if(fabs(lp->rhs[i]) < lp->infinite) - ii = 1; - else - ii = 0; - rowWT[i] = ii; - if(ii > 0) - appendLink(rowLL, i); - } - ok = allocINT(lp, &colWT, lp->columns+1, TRUE); - createLink(lp->columns, &colLL, NULL); - ok &= (colLL != NULL); - if(!ok) - goto Finish; - for(i = 1; i <= lp->columns; i++) { - ix = lp->rows+i; - if(is_unbounded(lp, i)) - ii = 3; - else if(lp->upbo[ix] >= lp->infinite) - ii = 2; - else if(fabs(lp->upbo[ix]-lp->lowbo[ix]) > lp->epsmachine) - ii = 1; - else - ii = 0; - colWT[i] = ii; - if(ii > 0) - appendLink(colLL, i); - } - - /* Loop over all basis variables */ - for(i = 1; i <= lp->rows; i++) { - - /* Select row */ - rx = 0; - wx = -lp->infinite; - for(ii = firstActiveLink(rowLL); ii > 0; ii = nextActiveLink(rowLL, ii)) { - tx = rowWT[ii] - CRASH_SPACER*rowNZ[ii]; - if(tx > wx) { - rx = ii; - wx = tx; - } - } - if(rx == 0) - break; - removeLink(rowLL, rx); - - /* Select column */ - cx = 0; - wx = -lp->infinite; - for(ii = mat->row_end[rx-1]; ii < mat->row_end[rx]; ii++) { - - /* Update NZ column counts for row selected above */ - tx = fabs(ROW_MAT_VALUE(ii)); - ix = ROW_MAT_COLNR(ii); -#ifdef CRASH_SIMPLESCALE - if(tx >= CRASH_THRESHOLD * colMAX[0]) -#else - if(tx >= CRASH_THRESHOLD * colMAX[ix]) -#endif - colNZ[ix]--; - if(!isActiveLink(colLL, ix) || (tx < CRASH_THRESHOLD * rowMAX[rx])) - continue; - - /* Now do the test for best pivot */ - tx = my_sign(lp->orig_obj[ix]) - my_sign(ROW_MAT_VALUE(ii)); - tx = colWT[ix] + CRASH_WEIGHT*tx - CRASH_SPACER*colNZ[ix]; - if(tx > wx) { - cx = ix; - wx = tx; - } - } - if(cx == 0) - break; - removeLink(colLL, cx); - - /* Update row NZ counts */ - ii = mat->col_end[cx-1]; - rownr = &COL_MAT_ROWNR(ii); - value = &COL_MAT_VALUE(ii); - for(; ii < mat->col_end[cx]; - ii++, rownr += matRowColStep, value += matValueStep) { - wx = fabs(*value); - ix = *rownr; -#ifdef CRASH_SIMPLESCALE - if(wx >= CRASH_THRESHOLD * colMAX[0]) -#else - if(wx >= CRASH_THRESHOLD * rowMAX[ix]) -#endif - rowNZ[ix]--; - } - - /* Set new basis variable */ - set_basisvar(lp, rx, lp->rows+cx); - } - - /* Clean up */ -Finish: - FREE(rowNZ); - FREE(colNZ); - FREE(rowMAX); - FREE(colMAX); - FREE(rowWT); - FREE(colWT); - freeLink(&rowLL); - freeLink(&colLL); - } - - /* Construct a basis that is in some measure the "least degenerate" */ - else if((lp->crashmode == CRASH_LEASTDEGENERATE) && mat_validate(mat)) { - /* The logic here follows Maros */ - LLrec *rowLL = NULL, *colLL = NULL; - int ii, rx, cx, ix, nz, *merit = NULL; - LPSREAL *value, wx, hold, *rhs = NULL, *eta = NULL; - int *rownr, *colnr; - - report(lp, NORMAL, "crash_basis: 'Least degenerate' basis crashing selected\n"); - - /* Create temporary arrays */ - ok = allocINT(lp, &merit, lp->columns + 1, FALSE) && - allocREAL(lp, &eta, lp->rows + 1, FALSE) && - allocREAL(lp, &rhs, lp->rows + 1, FALSE); - createLink(lp->columns, &colLL, NULL); - createLink(lp->rows, &rowLL, NULL); - ok &= (colLL != NULL) && (rowLL != NULL); - if(!ok) - goto FinishLD; - MEMCOPY(rhs, lp->orig_rhs, lp->rows + 1); - for(i = 1; i <= lp->columns; i++) - appendLink(colLL, i); - for(i = 1; i <= lp->rows; i++) - appendLink(rowLL, i); - - /* Loop until we have found enough new bases */ - while(colLL->count > 0) { - - /* Tally non-zeros matching in RHS and each active column */ - nz = mat_nonzeros(mat); - rownr = &COL_MAT_ROWNR(0); - colnr = &COL_MAT_COLNR(0); - ii = 0; - MEMCLEAR(merit, lp->columns + 1); - for(i = 0; i < nz; - i++, rownr += matRowColStep, colnr += matRowColStep) { - rx = *rownr; - cx = *colnr; - if(isActiveLink(colLL, cx) && (rhs[rx] != 0)) { - merit[cx]++; - ii++; - } - } - if(ii == 0) - break; - - /* Find maximal match; break ties with column length */ - i = firstActiveLink(colLL); - cx = i; - for(i = nextActiveLink(colLL, i); i != 0; i = nextActiveLink(colLL, i)) { - if(merit[i] >= merit[cx]) { - if((merit[i] > merit[cx]) || (mat_collength(mat, i) > mat_collength(mat, cx))) - cx = i; - } - } - - /* Determine the best pivot row */ - i = mat->col_end[cx-1]; - nz = mat->col_end[cx]; - rownr = &COL_MAT_ROWNR(i); - value = &COL_MAT_VALUE(i); - rx = 0; - wx = 0; - MEMCLEAR(eta, lp->rows + 1); - for(; i < nz; - i++, rownr += matRowColStep, value += matValueStep) { - ix = *rownr; - hold = *value; - eta[ix] = rhs[ix] / hold; - hold = fabs(hold); - if(isActiveLink(rowLL, ix) && (hold > wx)) { - wx = hold; - rx = ix; - } - } - - /* Set new basis variable */ - if(rx > 0) { - - /* We have to update the rhs vector for the implied transformation - in order to be able to find the new RHS non-zero pattern */ - for(i = 1; i <= lp->rows; i++) - rhs[i] -= wx * eta[i]; - rhs[rx] = wx; - - /* Do the exchange */ - set_basisvar(lp, rx, lp->rows+cx); - removeLink(rowLL, rx); - } - removeLink(colLL, cx); - - } - - /* Clean up */ -FinishLD: - FREE(merit); - FREE(rhs); - freeLink(&rowLL); - freeLink(&colLL); - - } - return( ok ); -} - -#if 0 -MYBOOL __WINAPI guess_basis(lprec *lp, LPSREAL *guessvector, int *basisvector) -{ - MYBOOL status = FALSE; - LPSREAL *values = NULL, *violation = NULL, - *value, error, upB, loB, sortorder = 1.0; - int i, n, *rownr, *colnr; - MATrec *mat = lp->matA; - - if(!mat_validate(lp->matA)) - return( status ); - - /* Create helper arrays */ - if(!allocREAL(lp, &values, lp->sum+1, TRUE) || - !allocREAL(lp, &violation, lp->sum+1, TRUE)) - goto Finish; - - /* Compute values of slack variables for given guess vector */ - i = 0; - n = get_nonzeros(lp); - rownr = &COL_MAT_ROWNR(i); - colnr = &COL_MAT_COLNR(i); - value = &COL_MAT_VALUE(i); - for(; i < n; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) - values[*rownr] += unscaled_mat(lp, my_chsign(is_chsign(lp, *rownr), *value), *rownr, *colnr) * - guessvector[*colnr]; - MEMMOVE(values+lp->rows+1, guessvector+1, lp->columns); - - /* Initialize constraint bound violation measures */ - for(i = 1; i <= lp->rows; i++) { - upB = get_rh_upper(lp, i); - loB = get_rh_lower(lp, i); - error = values[i] - upB; - if(error > lp->epsprimal) - violation[i] = sortorder*error; - else { - error = loB - values[i]; - if(error > lp->epsprimal) - violation[i] = sortorder*error; - else if(is_infinite(lp, loB) && is_infinite(lp, upB)) - ; - else if(is_infinite(lp, upB)) - violation[i] = sortorder*(loB - values[i]); - else if(is_infinite(lp, loB)) - violation[i] = sortorder*(values[i] - upB); - else - violation[i] = - sortorder*MAX(upB - values[i], values[i] - loB); - } - basisvector[i] = i; - } - - /* Initialize user variable bound violation measures */ - for(i = 1; i <= lp->columns; i++) { - n = lp->rows+i; - upB = get_upbo(lp, i); - loB = get_lowbo(lp, i); - error = guessvector[i] - upB; - if(error > lp->epsprimal) - violation[n] = sortorder*error; - else { - error = loB - values[n]; - if(error > lp->epsprimal) - violation[n] = sortorder*error; - else if(is_infinite(lp, loB) && is_infinite(lp, upB)) - ; - else if(is_infinite(lp, upB)) - violation[n] = sortorder*(loB - values[n]); - else if(is_infinite(lp, loB)) - violation[n] = sortorder*(values[n] - upB); - else - violation[n] = - sortorder*MAX(upB - values[n], values[n] - loB); - } - basisvector[n] = n; - } - - /* Sort decending by violation; this means that variables with - the largest violations will be designated as basic */ - sortByREAL(basisvector, violation, lp->sum, 1, FALSE); - - /* Adjust the non-basic indeces for the (proximal) bound state */ - error = lp->epsprimal; - for(i = lp->rows+1, rownr = basisvector+i; i <= lp->sum; i++, rownr++) { - if(*rownr <= lp->rows) { - if(values[*rownr] <= get_rh_lower(lp, *rownr)+error) - *rownr = -(*rownr); - } - else - if(values[i] <= get_lowbo(lp, (*rownr)-lp->rows)+error) - *rownr = -(*rownr); - } - - /* Clean up and return status */ - status = (MYBOOL) (violation[1] == 0); -Finish: - FREE(values); - FREE(violation); - - - return( status ); -} -#endif - -#if 0 -MYBOOL __WINAPI guess_basis(lprec *lp, LPSREAL *guessvector, int *basisvector) -{ - MYBOOL *isnz, status = FALSE; - LPSREAL *values = NULL, *violation = NULL, - eps = lp->epsprimal, - *value, error, upB, loB, sortorder = 1.0; - int i, j, n, *rownr, *colnr, *slkpos, - nrows = lp->rows, ncols = lp->columns; - MATrec *mat = lp->matA; - - if(!mat_validate(mat)) - return( status ); - - /* Create helper arrays */ - if(!allocREAL(lp, &values, lp->sum+1, TRUE) || - !allocREAL(lp, &violation, lp->sum+1, TRUE)) - goto Finish; - - /* Compute values of slack variables for given guess vector */ - i = 0; - n = get_nonzeros(lp); - rownr = &COL_MAT_ROWNR(i); - colnr = &COL_MAT_COLNR(i); - value = &COL_MAT_VALUE(i); - for(; i < n; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) - values[*rownr] += unscaled_mat(lp, my_chsign(is_chsign(lp, *rownr), *value), *rownr, *colnr) * - guessvector[*colnr]; - MEMMOVE(values+nrows+1, guessvector+1, ncols); - - /* Initialize constraint bound violation measures (expressed as positive values) */ - for(i = 1; i <= nrows; i++) { - upB = get_rh_upper(lp, i); - loB = get_rh_lower(lp, i); - error = values[i] - upB; - if(error > eps) - violation[i] = sortorder*error; - else { - error = loB - values[i]; - if(error > eps) - violation[i] = sortorder*error; - else if(my_infinite(lp, loB) && my_infinite(lp, upB)) - ; - else if(my_infinite(lp, upB)) - violation[i] = sortorder*(loB - values[i]); - else if(my_infinite(lp, loB)) - violation[i] = sortorder*(values[i] - upB); - else - violation[i] = -sortorder*MAX(upB - values[i], values[i] - loB); - } - basisvector[i] = i; - } - - /* Initialize user variable bound violation measures (expressed as positive values) */ - for(i = 1; i <= ncols; i++) { - n = nrows+i; - upB = get_upbo(lp, i); - loB = get_lowbo(lp, i); - error = guessvector[i] - upB; - if(error > eps) - violation[n] = sortorder*error; - else { - error = loB - values[n]; - if(error > eps) - violation[n] = sortorder*error; - else if(my_infinite(lp, loB) && my_infinite(lp, upB)) - ; - else if(my_infinite(lp, upB)) - violation[n] = sortorder*(loB - values[n]); - else if(my_infinite(lp, loB)) - violation[n] = sortorder*(values[n] - upB); - else - violation[n] = -sortorder*MAX(upB - values[n], values[n] - loB); - } - basisvector[n] = n; - } - - /* Sort decending by violation; this means that variables with - the largest violations will be designated as basic */ - sortByREAL(basisvector, violation, lp->sum, 1, FALSE); - error = violation[1]; - - /* Adjust the non-basic indeces for the (proximal) bound state */ - for(i = nrows+1, rownr = basisvector+i; i <= lp->sum; i++, rownr++) { - if(*rownr <= nrows) { - if(values[*rownr] <= get_rh_lower(lp, *rownr)+eps) - *rownr = -(*rownr); - } - else - if(values[i] <= get_lowbo(lp, (*rownr)-nrows)+eps) - *rownr = -(*rownr); - } - -#if 1 - /* Let us check for obvious row singularities and try to fix these; - First assemble necessary basis statistics... */ - isnz = (MYBOOL *) values; - MEMCLEAR(isnz, nrows+1); - slkpos = (int *) violation; - MEMCLEAR(slkpos, nrows+1); - for(i = 1; i <= nrows; i++) { - j = abs(basisvector[i]); - if(j <= nrows) { - isnz[j] = TRUE; - slkpos[j] = i; - } - else { - j-= nrows; - j = mat->col_end[j-1]; - isnz[COL_MAT_ROWNR(j)] = TRUE; - /*isnz[COL_MAT_ROWNR(j+1)] = TRUE;*/ - } - } - for(; i <= lp->sum; i++) { - j = abs(basisvector[i]); - if(j <= nrows) - slkpos[j] = i; - } - - /* ...then set the corresponding slacks basic for row rank deficient positions */ - for(j = 1; j <= nrows; j++) { -#ifdef Paranoia - if(slkpos[j] == 0) - report(lp, SEVERE, "guess_basis: Internal error"); -#endif - if(!isnz[j]) { - isnz[j] = TRUE; - i = slkpos[j]; - swapINT(&basisvector[i], &basisvector[j]); - basisvector[j] = abs(basisvector[j]); - } - } -#endif - - /* Clean up and return status */ - status = (MYBOOL) (error <= eps); -Finish: - FREE(values); - FREE(violation); - - return( status ); -} -#endif - -#if 0 -MYBOOL __WINAPI guess_basis(lprec *lp, LPSREAL *guessvector, int *basisvector) -{ - MYBOOL *isnz, status = FALSE; - LPSREAL *values = NULL, *violation = NULL, - eps = lp->epsprimal, - *value, error, upB, loB, sortorder = 1.0; - int i, j, jj, n, *rownr, *colnr, *slkpos, - nrows = lp->rows, ncols = lp->columns; - MATrec *mat = lp->matA; - - if(!mat_validate(mat)) - return( status ); - - /* Create helper arrays */ - if(!allocREAL(lp, &values, lp->sum+1, TRUE) || - !allocREAL(lp, &violation, lp->sum+1, TRUE)) - goto Finish; - - /* Compute values of slack variables for given guess vector */ - i = 0; - n = get_nonzeros(lp); - rownr = &COL_MAT_ROWNR(i); - colnr = &COL_MAT_COLNR(i); - value = &COL_MAT_VALUE(i); - for(; i < n; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) - values[*rownr] += unscaled_mat(lp, my_chsign(is_chsign(lp, *rownr), *value), *rownr, *colnr) * - guessvector[*colnr]; - MEMMOVE(values+nrows+1, guessvector+1, ncols); - - /* Initialize constraint bound violation measures (expressed as positive values) */ - for(i = 1; i <= nrows; i++) { - upB = get_rh_upper(lp, i); - loB = get_rh_lower(lp, i); - error = values[i] - upB; - if(error > -eps) - violation[i] = sortorder*MAX(0,error); - else { - error = loB - values[i]; - if(error > -eps) - violation[i] = sortorder*MAX(0,error); - else if(my_infinite(lp, loB) && my_infinite(lp, upB)) - ; - else if(my_infinite(lp, upB)) - violation[i] = sortorder*(loB - values[i]); - else if(my_infinite(lp, loB)) - violation[i] = sortorder*(values[i] - upB); - else - violation[i] = -sortorder*MAX(upB - values[i], values[i] - loB); - } - basisvector[i] = i; - } - - /* Initialize user variable bound violation measures (expressed as positive values) */ - for(i = 1; i <= ncols; i++) { - n = nrows+i; - upB = get_upbo(lp, i); - loB = get_lowbo(lp, i); - error = guessvector[i] - upB; - if(error > -eps) - violation[n] = sortorder*MAX(0,error); - else { - error = loB - values[n]; - if(error > -eps) - violation[n] = sortorder*MAX(0,error); - else if(my_infinite(lp, loB) && my_infinite(lp, upB)) - ; - else if(my_infinite(lp, upB)) - violation[n] = sortorder*(loB - values[n]); - else if(my_infinite(lp, loB)) - violation[n] = sortorder*(values[n] - upB); - else - violation[n] = -sortorder*MAX(upB - values[n], values[n] - loB); - } - basisvector[n] = n; - } - - /* Sort decending by violation; this means that variables with - the largest violations will be designated as basic */ - sortByREAL(basisvector, violation, lp->sum, 1, FALSE); - error = violation[1]; - - /* Adjust the non-basic indeces for the (proximal) bound state */ - for(i = nrows+1, rownr = basisvector+i; i <= lp->sum; i++, rownr++) { - if(*rownr <= nrows) { - values[*rownr] -= lp->orig_rhs[*rownr]; - if(values[*rownr] <= eps) - *rownr = -(*rownr); - } - else - if(values[i] <= get_lowbo(lp, (*rownr)-nrows)+eps) - *rownr = -(*rownr); - } - - /* Let us check for obvious row singularities and try to fix these; - First assemble necessary basis statistics... */ - isnz = (MYBOOL *) values; - MEMCLEAR(isnz, nrows+1); - slkpos = (int *) violation; - MEMCLEAR(slkpos, nrows+1); - for(i = 1; i <= nrows; i++) { - j = abs(basisvector[i]); - if(j <= nrows) { - isnz[j] = TRUE; - slkpos[j] = i; - } - else { - j-= nrows; - jj = mat->col_end[j-1]; - isnz[COL_MAT_ROWNR(jj)] = TRUE; -/* if(++jj < mat->col_end[j]) - isnz[COL_MAT_ROWNR(jj)] = TRUE; */ - } - } - for(; i <= lp->sum; i++) { - j = abs(basisvector[i]); - if(j <= nrows) - slkpos[j] = i; - } - - /* ...then set the corresponding slacks basic for row rank deficient positions */ - for(j = 1; j <= nrows; j++) { -#ifdef Paranoia - if(slkpos[j] == 0) - report(lp, SEVERE, "guess_basis: Internal error"); -#endif - if(!isnz[j]) { - isnz[j] = TRUE; - i = slkpos[j]; - swapINT(&basisvector[i], &basisvector[j]); - basisvector[j] = abs(basisvector[j]); - } - } - - /* Lastly normalize all basic variables to be coded as lower-bounded */ - for(i = 1; i <= nrows; i++) - basisvector[i] = -abs(basisvector[i]); - - /* Clean up and return status */ - status = (MYBOOL) (error <= eps); -Finish: - FREE(values); - FREE(violation); - - return( status ); -} -#endif - -MYBOOL __WINAPI guess_basis(lprec *lp, LPSREAL *guessvector, int *basisvector) -{ - MYBOOL *isnz = NULL, status = FALSE; - LPSREAL *values = NULL, *violation = NULL, - eps = lp->epsprimal, - *value, error, upB, loB, sortorder = -1.0; - int i, j, jj, n, *rownr, *colnr, *slkpos = NULL, - nrows = lp->rows, ncols = lp->columns, nsum = lp->sum; - int *basisnr; - MATrec *mat = lp->matA; - - if(!mat_validate(mat)) - return( status ); - - /* Create helper arrays, providing for multiple use of the violation array */ - if(!allocREAL(lp, &values, nsum+1, TRUE) || - !allocREAL(lp, &violation, nsum+1, TRUE)) - goto Finish; - - /* Compute the values of the constraints for the given guess vector */ - i = 0; - n = get_nonzeros(lp); - rownr = &COL_MAT_ROWNR(i); - colnr = &COL_MAT_COLNR(i); - value = &COL_MAT_VALUE(i); - for(; i < n; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) - values[*rownr] += unscaled_mat(lp, my_chsign(is_chsign(lp, *rownr), *value), *rownr, *colnr) * - guessvector[*colnr]; - MEMMOVE(values+nrows+1, guessvector+1, ncols); - - /* Initialize bound "violation" or primal non-degeneracy measures, expressed - as the absolute value of the differences from the closest bound. */ - for(i = 1; i <= nsum; i++) { - if(i <= nrows) { - loB = get_rh_lower(lp, i); - upB = get_rh_upper(lp, i); - } - else { - loB = get_lowbo(lp, i-nrows); - upB = get_upbo(lp, i-nrows); - } - - /* Free constraints/variables */ - if(my_infinite(lp, loB) && my_infinite(lp, upB)) - error = 0; - /* Violated constraints/variable bounds */ - else if(values[i]+eps < loB) - error = loB-values[i]; - else if(values[i]-eps > upB) - error = values[i]-upB; - /* Non-violated constraints/variables bounds */ - else if(my_infinite(lp, upB)) - error = MAX(0, values[i]-loB); - else if(my_infinite(lp, loB)) - error = MAX(0, upB-values[i]); - else - error = MIN(upB-values[i], values[i]-loB); /* MAX(upB-values[i], values[i]-loB); */ - if(error != 0) - violation[i] = sortorder*error; - basisvector[i] = i; - } - - /* Sort decending , meaning that variables with the largest - "violations" will be designated basic. Effectively, we are performing a - greedy type algorithm, but start at the "least interesting" end. */ - sortByREAL(basisvector, violation, nsum, 1, FALSE); - error = violation[1]; /* Used for setting the return value */ - - /* Let us check for obvious row singularities and try to fix these. - Note that we reuse the memory allocated to the violation array. - First assemble necessary basis statistics... */ - slkpos = (int *) violation; - n = nrows+1; - MEMCLEAR(slkpos, n); - isnz = (MYBOOL *) (slkpos+n+1); - MEMCLEAR(isnz, n); - for(i = 1; i <= nrows; i++) { - j = abs(basisvector[i]); - if(j <= nrows) { - isnz[j] = TRUE; - slkpos[j] = i; - } - else { - j-= nrows; - jj = mat->col_end[j-1]; - jj = COL_MAT_ROWNR(jj); - isnz[jj] = TRUE; - } - } - for(; i <= nsum; i++) { - j = abs(basisvector[i]); - if(j <= nrows) - slkpos[j] = i; - } - - /* ...then set the corresponding slacks basic for row rank deficient positions */ - for(j = 1; j <= nrows; j++) { - if(slkpos[j] == 0) - report(lp, SEVERE, "guess_basis: Internal error"); - if(!isnz[j]) { - isnz[j] = TRUE; - i = slkpos[j]; - swapINT(&basisvector[i], &basisvector[j]); - basisvector[j] = abs(basisvector[j]); - } - } - - /* Adjust the non-basic indeces for the (proximal) bound state */ - for(i = nrows+1, basisnr = basisvector+i; i <= nsum; i++, basisnr++) { - n = *basisnr; - if(n <= nrows) { - values[n] -= get_rh_lower(lp, n); - if(values[n] <= eps) - *basisnr = -(*basisnr); - } - else - if(values[n]-eps <= get_lowbo(lp, n-nrows)) - *basisnr = -(*basisnr); - } - -/* Lastly normalize all basic variables to be coded as lower-bounded, - or effectively zero-based in the case of free variables. */ - for(i = 1; i <= nrows; i++) - basisvector[i] = -abs(basisvector[i]); - - /* Clean up and return status */ - status = (MYBOOL) (error <= eps); -Finish: - FREE(values); - FREE(violation); - - return( status ); -} diff --git a/src/lpsolve/build/lp_solve/lp_rlp.c b/src/lpsolve/build/lp_solve/lp_rlp.c deleted file mode 100644 index e2ec7268..00000000 --- a/src/lpsolve/build/lp_solve/lp_rlp.c +++ /dev/null @@ -1,2484 +0,0 @@ -/* A Bison parser, made by GNU Bison 2.3. */ - -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* All symbols defined below should begin with lp_yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output. */ -#define YYBISON 1 - -/* Bison version. */ -#define YYBISON_VERSION "2.3" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 1 - -/* Using locations. */ -#define YYLSP_NEEDED 0 - - - -/* Tokens. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum lp_yytokentype { - VAR = 258, - CONS = 259, - INTCONS = 260, - VARIABLECOLON = 261, - INF = 262, - SEC_INT = 263, - SEC_BIN = 264, - SEC_SEC = 265, - SEC_SOS = 266, - SOSDESCR = 267, - SEC_FREE = 268, - TOK_SIGN = 269, - AR_M_OP = 270, - RE_OPEQ = 271, - RE_OPLE = 272, - RE_OPGE = 273, - END_C = 274, - COMMA = 275, - COLON = 276, - MINIMISE = 277, - MAXIMISE = 278, - UNDEFINED = 279 - }; -#endif -/* Tokens. */ -#define VAR 258 -#define CONS 259 -#define INTCONS 260 -#define VARIABLECOLON 261 -#define INF 262 -#define SEC_INT 263 -#define SEC_BIN 264 -#define SEC_SEC 265 -#define SEC_SOS 266 -#define SOSDESCR 267 -#define SEC_FREE 268 -#define TOK_SIGN 269 -#define AR_M_OP 270 -#define RE_OPEQ 271 -#define RE_OPLE 272 -#define RE_OPGE 273 -#define END_C 274 -#define COMMA 275 -#define COLON 276 -#define MINIMISE 277 -#define MAXIMISE 278 -#define UNDEFINED 279 - - - - -/* Copy the first part of user declarations. */ - - -#include -#include -#include - -#define scanner lp_yyscanner -#define PARM lp_yyget_extra(lp_yyscanner) -#define YYSTYPE int -#define YY_EXTRA_TYPE parse_parm * -#define YY_FATAL_ERROR(msg) lex_fatal_error(PARM, lp_yyscanner, msg) -#undef YY_INPUT -#define YY_INPUT(buf,result,max_size) result = lp_input((void *) PARM, buf, max_size); -#define lp_yyerror read_error - -#include "lpkit.h" -#include "yacc_read.h" - -typedef struct parse_vars_s -{ - read_modeldata_func *lp_input; - void *userhandle; - char HadVar, HadVar0, HadVar1, HadVar2, HasAR_M_OP, HadConstraint, Had_lineair_sum, Had_lineair_sum0, do_add_row, HadSign, OP, Sign, isign, isign0, make_neg; - char state, state0; - char Within_int_decl; /* TRUE when we are within an char declaration */ - char Within_bin_decl; /* TRUE when we are within an bin declaration */ - char Within_sec_decl; /* TRUE when we are within a sec declaration */ - char Within_sos_decl; /* TRUE when we are within a sos declaration */ - char Within_sos_decl1; - char Within_free_decl; /* TRUE when we are within a free declaration */ - short SOStype, SOStype0; /* SOS type */ - int SOSNr; - int SOSweight; /* SOS weight */ - char *Last_var, *Last_var0; - LPSREAL f, f0, f1; -} parse_vars; - -#ifdef FORTIFY -# include "lp_fortify.h" -#endif - -/* let's please C++ users */ -#ifdef __cplusplus -extern "C" { -#endif - -#if defined MSDOS || defined __MSDOS__ || defined WINDOWS || defined _WINDOWS || defined WIN32 || defined _WIN32 -#define YY_NO_UNISTD_H - -static int isatty(int f) -{ - return(FALSE); -} - -#if !defined _STDLIB_H -# define _STDLIB_H -#endif -#endif - -static int __WINAPI lp_input_lp_yyin(void *fpin, char *buf, int max_size) -{ - int result; - - result = fread( (char*)buf, sizeof(char), max_size, (FILE *)fpin); - - return(result); -} - -static int __WINAPI lp_input(void *vpp, char *buf, int max_size) -{ - parse_parm *pp = (parse_parm *) vpp; - parse_vars *pv = (parse_vars *) pp->parse_vars; - int result; - - result = pv->lp_input(pv->userhandle, buf, max_size); - if (result < 0) - lex_fatal_error(pp, pp->scanner, "read() in flex scanner failed"); - return(result); -} - -#ifdef __cplusplus -}; -#endif - -#include "lp_rlp.h" - -#undef lp_yylval - - - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif - -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif - -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef int YYSTYPE; -# define lp_yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 -#endif - - - -/* Copy the second part of user declarations. */ - - -/* Line 216 of yacc.c. */ - - -#ifdef short -# undef short -#endif - -#ifdef YYTYPE_UINT8 -typedef YYTYPE_UINT8 lp_yytype_uint8; -#else -typedef unsigned char lp_yytype_uint8; -#endif - -#ifdef YYTYPE_INT8 -typedef YYTYPE_INT8 lp_yytype_int8; -#elif (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -typedef signed char lp_yytype_int8; -#else -typedef short int lp_yytype_int8; -#endif - -#ifdef YYTYPE_UINT16 -typedef YYTYPE_UINT16 lp_yytype_uint16; -#else -typedef unsigned short int lp_yytype_uint16; -#endif - -#ifdef YYTYPE_INT16 -typedef YYTYPE_INT16 lp_yytype_int16; -#else -typedef short int lp_yytype_int16; -#endif - -#ifndef YYSIZE_T -# ifdef __SIZE_TYPE__ -# define YYSIZE_T __SIZE_TYPE__ -# elif defined size_t -# define YYSIZE_T size_t -# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -# include /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# else -# define YYSIZE_T unsigned int -# endif -#endif - -#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) - -#ifndef YY_ -# if YYENABLE_NLS -# if ENABLE_NLS -# include /* INFRINGES ON USER NAME SPACE */ -# define YY_(msgid) dgettext ("bison-runtime", msgid) -# endif -# endif -# ifndef YY_ -# define YY_(msgid) msgid -# endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if ! defined lint || defined __GNUC__ -# define YYUSE(e) ((void) (e)) -#else -# define YYUSE(e) /* empty */ -#endif - -/* Identity function, used to suppress warnings about constant conditions. */ -#ifndef lint -# define YYID(n) (n) -#else -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static int -YYID (int i) -#else -static int -YYID (i) - int i; -#endif -{ - return i; -} -#endif - -#if ! defined lp_yyoverflow || YYERROR_VERBOSE - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# ifdef YYSTACK_USE_ALLOCA -# if YYSTACK_USE_ALLOCA -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# elif defined __BUILTIN_VA_ARG_INCR -# include /* INFRINGES ON USER NAME SPACE */ -# elif defined _AIX -# define YYSTACK_ALLOC __alloca -# elif defined _MSC_VER -# include /* INFRINGES ON USER NAME SPACE */ -# define alloca _alloca -# else -# define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -# include /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 -# endif -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) -# ifndef YYSTACK_ALLOC_MAXIMUM - /* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -# endif -# else -# define YYSTACK_ALLOC YYMALLOC -# define YYSTACK_FREE YYFREE -# ifndef YYSTACK_ALLOC_MAXIMUM -# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -# endif -# if (defined __cplusplus && ! defined _STDLIB_H \ - && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) -# include /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 -# endif -# endif -# ifndef YYMALLOC -# define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# ifndef YYFREE -# define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -void free (void *); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# endif -#endif /* ! defined lp_yyoverflow || YYERROR_VERBOSE */ - - -#if (! defined lp_yyoverflow \ - && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union lp_yyalloc -{ - lp_yytype_int16 lp_yyss; - YYSTYPE lp_yyvs; - }; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (sizeof (union lp_yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (lp_yytype_int16) + sizeof (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T lp_yyi; \ - for (lp_yyi = 0; lp_yyi < (Count); lp_yyi++) \ - (To)[lp_yyi] = (From)[lp_yyi]; \ - } \ - while (YYID (0)) -# endif -# endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T lp_yynewbytes; \ - YYCOPY (&lp_yyptr->Stack, Stack, lp_yysize); \ - Stack = &lp_yyptr->Stack; \ - lp_yynewbytes = lp_yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - lp_yyptr += lp_yynewbytes / sizeof (*lp_yyptr); \ - } \ - while (YYID (0)) - -#endif - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 3 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 115 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 25 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 56 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 89 -/* YYNRULES -- Number of states. */ -#define YYNSTATES 123 - -/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ -#define YYUNDEFTOK 2 -#define YYMAXUTOK 279 - -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? lp_yytranslate[YYX] : YYUNDEFTOK) - -/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ -static const lp_yytype_uint8 lp_yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 -}; - -#if YYDEBUG -/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in - YYRHS. */ -static const lp_yytype_uint8 lp_yyprhs[] = -{ - 0, 0, 3, 4, 5, 10, 13, 16, 18, 21, - 23, 25, 27, 29, 31, 34, 36, 37, 41, 42, - 43, 44, 53, 55, 56, 57, 63, 65, 67, 69, - 70, 74, 75, 78, 80, 83, 86, 88, 89, 93, - 95, 97, 99, 102, 104, 106, 108, 110, 112, 114, - 116, 118, 120, 122, 124, 127, 129, 131, 133, 135, - 137, 138, 142, 143, 149, 151, 154, 156, 157, 161, - 163, 164, 169, 171, 174, 176, 178, 180, 184, 186, - 188, 190, 191, 193, 195, 198, 202, 205, 208, 211 -}; - -/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -static const lp_yytype_int8 lp_yyrhs[] = -{ - 27, 0, -1, -1, -1, 28, 29, 32, 58, -1, - 23, 30, -1, 22, 30, -1, 30, -1, 31, 19, - -1, 26, -1, 46, -1, 26, -1, 33, -1, 34, - -1, 33, 34, -1, 36, -1, -1, 6, 35, 36, - -1, -1, -1, -1, 43, 37, 52, 38, 44, 39, - 40, 19, -1, 26, -1, -1, -1, 52, 41, 53, - 42, 57, -1, 26, -1, 44, -1, 46, -1, -1, - 7, 45, 57, -1, -1, 47, 48, -1, 49, -1, - 48, 49, -1, 55, 50, -1, 54, -1, -1, 56, - 51, 3, -1, 16, -1, 17, -1, 18, -1, 55, - 54, -1, 7, -1, 5, -1, 4, -1, 26, -1, - 14, -1, 26, -1, 15, -1, 26, -1, 26, -1, - 59, -1, 61, -1, 59, 61, -1, 8, -1, 9, - -1, 10, -1, 11, -1, 13, -1, -1, 60, 62, - 65, -1, -1, 64, 66, 71, 68, 19, -1, 63, - -1, 65, 63, -1, 26, -1, -1, 12, 67, 77, - -1, 26, -1, -1, 17, 5, 69, 70, -1, 26, - -1, 21, 5, -1, 26, -1, 72, -1, 78, -1, - 72, 73, 78, -1, 26, -1, 20, -1, 26, -1, - -1, 26, -1, 26, -1, 3, 74, -1, 6, 75, - 79, -1, 54, 76, -1, 77, 80, -1, 3, 74, - -1, 6, 75, 54, 76, -1 -}; - -/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ -static const lp_yytype_uint16 lp_yyrline[] = -{ - 0, 116, 116, 120, 120, 145, 149, 153, 156, 170, - 171, 201, 202, 205, 206, 210, 212, 211, 224, 232, - 242, 223, 288, 298, 311, 297, 342, 355, 364, 366, - 365, 376, 376, 400, 401, 405, 444, 452, 451, 470, - 470, 470, 473, 475, 489, 489, 492, 500, 510, 517, - 526, 547, 548, 551, 552, 555, 555, 555, 555, 555, - 560, 559, 570, 570, 598, 599, 602, 604, 603, 614, - 626, 624, 643, 650, 660, 661, 664, 665, 670, 671, - 674, 703, 724, 749, 770, 772, 777, 779, 784, 786 -}; -#endif - -#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE -/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const lp_yytname[] = -{ - "$end", "error", "$undefined", "VAR", "CONS", "INTCONS", - "VARIABLECOLON", "INF", "SEC_INT", "SEC_BIN", "SEC_SEC", "SEC_SOS", - "SOSDESCR", "SEC_FREE", "TOK_SIGN", "AR_M_OP", "RE_OPEQ", "RE_OPLE", - "RE_OPGE", "END_C", "COMMA", "COLON", "MINIMISE", "MAXIMISE", - "UNDEFINED", "$accept", "EMPTY", "inputfile", "@1", "objective_function", - "real_of", "lineair_sum", "constraints", "x_constraints", "constraint", - "@2", "real_constraint", "@3", "@4", "@5", "optionalrange", "@6", "@7", - "x_lineair_sum2", "x_lineair_sum3", "@8", "x_lineair_sum", "@9", - "x_lineair_sum1", "x_lineair_term", "x_lineair_term1", "@10", "RE_OP", - "cons_term", "REALCONS", "x_SIGN", "optional_AR_M_OP", "RHS_STORE", - "int_bin_sec_sos_free_declarations", "real_int_bin_sec_sos_free_decls", - "SEC_INT_BIN_SEC_SOS_FREE", "int_bin_sec_sos_free_declaration", "@11", - "xx_int_bin_sec_sos_free_declaration", "@12", - "x_int_bin_sec_sos_free_declaration", "optionalsos", "@13", - "optionalsostype", "@14", "optionalSOSweight", "vars", "x_vars", - "optionalcomma", "variable", "variablecolon", "sosweight", "sosdescr", - "onevarwithoptionalweight", "INTCONSorVARIABLE", - "x_onevarwithoptionalweight", 0 -}; -#endif - -# ifdef YYPRINT -/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to - token YYLEX-NUM. */ -static const lp_yytype_uint16 lp_yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, - 275, 276, 277, 278, 279 -}; -# endif - -/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const lp_yytype_uint8 lp_yyr1[] = -{ - 0, 25, 26, 28, 27, 29, 29, 29, 30, 31, - 31, 32, 32, 33, 33, 34, 35, 34, 37, 38, - 39, 36, 40, 41, 42, 40, 43, 43, 44, 45, - 44, 47, 46, 48, 48, 49, 50, 51, 50, 52, - 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, - 57, 58, 58, 59, 59, 60, 60, 60, 60, 60, - 62, 61, 64, 63, 65, 65, 66, 67, 66, 68, - 69, 68, 70, 70, 71, 71, 72, 72, 73, 73, - 74, 75, 76, 77, 78, 78, 79, 79, 80, 80 -}; - -/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -static const lp_yytype_uint8 lp_yyr2[] = -{ - 0, 2, 0, 0, 4, 2, 2, 1, 2, 1, - 1, 1, 1, 1, 2, 1, 0, 3, 0, 0, - 0, 8, 1, 0, 0, 5, 1, 1, 1, 0, - 3, 0, 2, 1, 2, 2, 1, 0, 3, 1, - 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, - 0, 3, 0, 5, 1, 2, 1, 0, 3, 1, - 0, 4, 1, 2, 1, 1, 1, 3, 1, 1, - 1, 0, 1, 1, 2, 3, 2, 2, 2, 4 -}; - -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero - means the default is an error. */ -static const lp_yytype_uint8 lp_yydefact[] = -{ - 3, 0, 31, 1, 31, 31, 9, 2, 7, 0, - 10, 2, 6, 5, 16, 29, 11, 2, 12, 13, - 15, 18, 27, 28, 8, 47, 46, 2, 33, 2, - 31, 2, 55, 56, 57, 58, 59, 51, 4, 52, - 60, 53, 26, 14, 0, 34, 45, 44, 49, 48, - 35, 36, 37, 17, 50, 30, 54, 62, 39, 40, - 41, 19, 0, 64, 2, 61, 31, 38, 67, 66, - 2, 65, 20, 2, 2, 81, 74, 2, 2, 76, - 2, 83, 68, 80, 84, 2, 0, 69, 0, 79, - 78, 0, 22, 0, 23, 2, 0, 85, 70, 63, - 77, 21, 2, 82, 86, 2, 81, 87, 2, 43, - 24, 0, 88, 0, 0, 72, 71, 2, 42, 2, - 73, 25, 89 -}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const lp_yytype_int8 lp_yydefgoto[] = -{ - -1, 6, 1, 2, 7, 8, 9, 17, 18, 19, - 30, 20, 44, 66, 80, 93, 102, 117, 21, 22, - 31, 23, 11, 27, 28, 50, 62, 61, 110, 51, - 29, 52, 55, 38, 39, 40, 41, 57, 63, 64, - 65, 70, 73, 88, 108, 116, 77, 78, 91, 84, - 85, 104, 82, 79, 97, 107 -}; - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -#define YYPACT_NINF -85 -static const lp_yytype_int8 lp_yypact[] = -{ - -85, 13, 46, -85, 2, 2, -85, 32, -85, 7, - -85, 26, -85, -85, -85, -85, 64, 40, 27, -85, - -85, -85, -85, -85, -85, -85, -85, 42, -85, 3, - -2, -85, -85, -85, -85, -85, -85, -85, -85, 40, - -85, -85, -85, -85, 67, -85, -85, -85, -85, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, - -85, -85, 59, -85, 52, 0, 84, -85, -85, -85, - 22, -85, -85, -85, -85, -85, -85, 60, 35, -85, - 67, -85, -85, -85, -85, 82, 87, -85, 74, -85, - -85, 22, -85, 75, -85, -85, 73, -85, -85, -85, - -85, -85, -5, -85, -85, -85, -85, -85, 76, -85, - -85, 82, -85, 82, 91, -85, -85, -85, -85, -85, - -85, -85, -85 -}; - -/* YYPGOTO[NTERM-NUM]. */ -static const lp_yytype_int8 lp_yypgoto[] = -{ - -85, -7, -85, -85, -85, 85, -85, -85, -85, 81, - -85, 72, -85, -85, -85, -85, -85, -85, -85, 34, - -85, 70, -85, -85, 77, -85, -85, 23, -85, -84, - 4, -85, -12, -85, -85, -85, 68, -85, 43, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, 6, - 8, -10, 28, 24, -85, -85 -}; - -/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -76 -static const lp_yytype_int8 lp_yytable[] = -{ - 16, 95, 109, -62, 26, 15, -62, 46, 47, 25, - 37, 42, -62, 3, -2, -2, -2, -62, 48, -62, - 26, -2, 49, 42, 54, 74, 24, 118, 75, 119, - -31, -31, -31, 14, 15, -31, -31, -31, 14, 15, - 25, -31, -31, -2, -2, -2, -31, -31, 32, 33, - 34, 35, -75, 36, -75, 89, 25, 69, -32, -32, - -32, -32, 67, 76, 68, -2, 81, 83, 4, 5, - 87, 90, 10, 92, 10, 10, 105, 86, 81, 106, - -26, -26, -26, 58, 59, 60, 46, 47, 103, 12, - 13, 15, 98, 99, 101, 26, 120, 114, 83, 43, - 72, 115, 53, 94, 45, 121, 111, 56, 71, 122, - 54, 112, 103, 96, 113, 100 -}; - -static const lp_yytype_uint8 lp_yycheck[] = -{ - 7, 85, 7, 3, 11, 7, 6, 4, 5, 14, - 17, 18, 12, 0, 16, 17, 18, 17, 15, 19, - 27, 19, 29, 30, 31, 3, 19, 111, 6, 113, - 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, - 14, 14, 15, 16, 17, 18, 14, 15, 8, 9, - 10, 11, 17, 13, 19, 20, 14, 64, 16, 17, - 18, 19, 3, 70, 12, 19, 73, 74, 22, 23, - 77, 78, 2, 80, 4, 5, 3, 17, 85, 6, - 16, 17, 18, 16, 17, 18, 4, 5, 95, 4, - 5, 7, 5, 19, 19, 102, 5, 21, 105, 18, - 66, 108, 30, 80, 27, 117, 102, 39, 65, 119, - 117, 105, 119, 85, 106, 91 -}; - -/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ -static const lp_yytype_uint8 lp_yystos[] = -{ - 0, 27, 28, 0, 22, 23, 26, 29, 30, 31, - 46, 47, 30, 30, 6, 7, 26, 32, 33, 34, - 36, 43, 44, 46, 19, 14, 26, 48, 49, 55, - 35, 45, 8, 9, 10, 11, 13, 26, 58, 59, - 60, 61, 26, 34, 37, 49, 4, 5, 15, 26, - 50, 54, 56, 36, 26, 57, 61, 62, 16, 17, - 18, 52, 51, 63, 64, 65, 38, 3, 12, 26, - 66, 63, 44, 67, 3, 6, 26, 71, 72, 78, - 39, 26, 77, 26, 74, 75, 17, 26, 68, 20, - 26, 73, 26, 40, 52, 54, 77, 79, 5, 19, - 78, 19, 41, 26, 76, 3, 6, 80, 69, 7, - 53, 55, 74, 75, 21, 26, 70, 42, 54, 54, - 5, 57, 76 -}; - -#define lp_yyerrok (lp_yyerrstatus = 0) -#define lp_yyclearin (lp_yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 - -#define YYACCEPT goto lp_yyacceptlab -#define YYABORT goto lp_yyabortlab -#define YYERROR goto lp_yyerrorlab - - -/* Like YYERROR except do call lp_yyerror. This remains here temporarily - to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ - -#define YYFAIL goto lp_yyerrlab - -#define YYRECOVERING() (!!lp_yyerrstatus) - -#define YYBACKUP(Token, Value) \ -do \ - if (lp_yychar == YYEMPTY && lp_yylen == 1) \ - { \ - lp_yychar = (Token); \ - lp_yylval = (Value); \ - lp_yytoken = YYTRANSLATE (lp_yychar); \ - YYPOPSTACK (1); \ - goto lp_yybackup; \ - } \ - else \ - { \ - lp_yyerror (parm, scanner, YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ -while (YYID (0)) - - -#define YYTERROR 1 -#define YYERRCODE 256 - - -/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - do \ - if (YYID (N)) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ - } \ - while (YYID (0)) -#endif - - -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ - -#ifndef YY_LOCATION_PRINT -# if YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif -#endif - - -/* YYLEX -- calling `lp_yylex' with the right arguments. */ - -#ifdef YYLEX_PARAM -# define YYLEX lp_yylex (&lp_yylval, YYLEX_PARAM) -#else -# define YYLEX lp_yylex (&lp_yylval, scanner) -#endif - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (lp_yydebug) \ - YYFPRINTF Args; \ -} while (YYID (0)) - -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -do { \ - if (lp_yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - lp_yy_symbol_print (stderr, \ - Type, Value, parm, scanner); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (YYID (0)) - - -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -lp_yy_symbol_value_print (FILE *lp_yyoutput, int lp_yytype, YYSTYPE const * const lp_yyvaluep, parse_parm *parm, void *scanner) -#else -static void -lp_yy_symbol_value_print (lp_yyoutput, lp_yytype, lp_yyvaluep, parm, scanner) - FILE *lp_yyoutput; - int lp_yytype; - YYSTYPE const * const lp_yyvaluep; - parse_parm *parm; - void *scanner; -#endif -{ - if (!lp_yyvaluep) - return; - YYUSE (parm); - YYUSE (scanner); -# ifdef YYPRINT - if (lp_yytype < YYNTOKENS) - YYPRINT (lp_yyoutput, lp_yytoknum[lp_yytype], *lp_yyvaluep); -# else - YYUSE (lp_yyoutput); -# endif - switch (lp_yytype) - { - default: - break; - } -} - - -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -lp_yy_symbol_print (FILE *lp_yyoutput, int lp_yytype, YYSTYPE const * const lp_yyvaluep, parse_parm *parm, void *scanner) -#else -static void -lp_yy_symbol_print (lp_yyoutput, lp_yytype, lp_yyvaluep, parm, scanner) - FILE *lp_yyoutput; - int lp_yytype; - YYSTYPE const * const lp_yyvaluep; - parse_parm *parm; - void *scanner; -#endif -{ - if (lp_yytype < YYNTOKENS) - YYFPRINTF (lp_yyoutput, "token %s (", lp_yytname[lp_yytype]); - else - YYFPRINTF (lp_yyoutput, "nterm %s (", lp_yytname[lp_yytype]); - - lp_yy_symbol_value_print (lp_yyoutput, lp_yytype, lp_yyvaluep, parm, scanner); - YYFPRINTF (lp_yyoutput, ")"); -} - -/*------------------------------------------------------------------. -| lp_yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -lp_yy_stack_print (lp_yytype_int16 *bottom, lp_yytype_int16 *top) -#else -static void -lp_yy_stack_print (bottom, top) - lp_yytype_int16 *bottom; - lp_yytype_int16 *top; -#endif -{ - YYFPRINTF (stderr, "Stack now"); - for (; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); - YYFPRINTF (stderr, "\n"); -} - -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (lp_yydebug) \ - lp_yy_stack_print ((Bottom), (Top)); \ -} while (YYID (0)) - - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -lp_yy_reduce_print (YYSTYPE *lp_yyvsp, int lp_yyrule, parse_parm *parm, void *scanner) -#else -static void -lp_yy_reduce_print (lp_yyvsp, lp_yyrule, parm, scanner) - YYSTYPE *lp_yyvsp; - int lp_yyrule; - parse_parm *parm; - void *scanner; -#endif -{ - int lp_yynrhs = lp_yyr2[lp_yyrule]; - int lp_yyi; - unsigned long int lp_yylno = lp_yyrline[lp_yyrule]; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", - lp_yyrule - 1, lp_yylno); - /* The symbols being reduced. */ - for (lp_yyi = 0; lp_yyi < lp_yynrhs; lp_yyi++) - { - fprintf (stderr, " $%d = ", lp_yyi + 1); - lp_yy_symbol_print (stderr, lp_yyrhs[lp_yyprhs[lp_yyrule] + lp_yyi], - &(lp_yyvsp[(lp_yyi + 1) - (lp_yynrhs)]) - , parm, scanner); - fprintf (stderr, "\n"); - } -} - -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (lp_yydebug) \ - lp_yy_reduce_print (lp_yyvsp, Rule, parm, scanner); \ -} while (YYID (0)) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int lp_yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - - - -#if YYERROR_VERBOSE - -# ifndef lp_yystrlen -# if defined __GLIBC__ && defined _STRING_H -# define lp_yystrlen strlen -# else -/* Return the length of YYSTR. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static YYSIZE_T -lp_yystrlen (const char *lp_yystr) -#else -static YYSIZE_T -lp_yystrlen (lp_yystr) - const char *lp_yystr; -#endif -{ - YYSIZE_T lp_yylen; - for (lp_yylen = 0; lp_yystr[lp_yylen]; lp_yylen++) - continue; - return lp_yylen; -} -# endif -# endif - -# ifndef lp_yystpcpy -# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE -# define lp_yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static char * -lp_yystpcpy (char *lp_yydest, const char *lp_yysrc) -#else -static char * -lp_yystpcpy (lp_yydest, lp_yysrc) - char *lp_yydest; - const char *lp_yysrc; -#endif -{ - char *lp_yyd = lp_yydest; - const char *lp_yys = lp_yysrc; - - while ((*lp_yyd++ = *lp_yys++) != '\0') - continue; - - return lp_yyd - 1; -} -# endif -# endif - -# ifndef lp_yytnamerr -/* Copy to YYRES the contents of YYSTR after stripping away unnecessary - quotes and backslashes, so that it's suitable for lp_yyerror. The - heuristic is that double-quoting is unnecessary unless the string - contains an apostrophe, a comma, or backslash (other than - backslash-backslash). YYSTR is taken from lp_yytname. If YYRES is - null, do not copy; instead, return the length of what the result - would have been. */ -static YYSIZE_T -lp_yytnamerr (char *lp_yyres, const char *lp_yystr) -{ - if (*lp_yystr == '"') - { - YYSIZE_T lp_yyn = 0; - char const *lp_yyp = lp_yystr; - - for (;;) - switch (*++lp_yyp) - { - case '\'': - case ',': - goto do_not_strip_quotes; - - case '\\': - if (*++lp_yyp != '\\') - goto do_not_strip_quotes; - /* Fall through. */ - default: - if (lp_yyres) - lp_yyres[lp_yyn] = *lp_yyp; - lp_yyn++; - break; - - case '"': - if (lp_yyres) - lp_yyres[lp_yyn] = '\0'; - return lp_yyn; - } - do_not_strip_quotes: ; - } - - if (! lp_yyres) - return lp_yystrlen (lp_yystr); - - return lp_yystpcpy (lp_yyres, lp_yystr) - lp_yyres; -} -# endif - -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -lp_yysyntax_error (char *lp_yyresult, int lp_yystate, int lp_yychar) -{ - int lp_yyn = lp_yypact[lp_yystate]; - - if (! (YYPACT_NINF < lp_yyn && lp_yyn <= YYLAST)) - return 0; - else - { - int lp_yytype = YYTRANSLATE (lp_yychar); - YYSIZE_T lp_yysize0 = lp_yytnamerr (0, lp_yytname[lp_yytype]); - YYSIZE_T lp_yysize = lp_yysize0; - YYSIZE_T lp_yysize1; - int lp_yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *lp_yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int lp_yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *lp_yyfmt; - char const *lp_yyf; - static char const lp_yyunexpected[] = "syntax error, unexpected %s"; - static char const lp_yyexpecting[] = ", expecting %s"; - static char const lp_yyor[] = " or %s"; - char lp_yyformat[sizeof lp_yyunexpected - + sizeof lp_yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof lp_yyor - 1))]; - char const *lp_yyprefix = lp_yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int lp_yyxbegin = lp_yyn < 0 ? -lp_yyn : 0; - - /* Stay within bounds of both lp_yycheck and lp_yytname. */ - int lp_yychecklim = YYLAST - lp_yyn + 1; - int lp_yyxend = lp_yychecklim < YYNTOKENS ? lp_yychecklim : YYNTOKENS; - int lp_yycount = 1; - - lp_yyarg[0] = lp_yytname[lp_yytype]; - lp_yyfmt = lp_yystpcpy (lp_yyformat, lp_yyunexpected); - - for (lp_yyx = lp_yyxbegin; lp_yyx < lp_yyxend; ++lp_yyx) - if (lp_yycheck[lp_yyx + lp_yyn] == lp_yyx && lp_yyx != YYTERROR) - { - if (lp_yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - lp_yycount = 1; - lp_yysize = lp_yysize0; - lp_yyformat[sizeof lp_yyunexpected - 1] = '\0'; - break; - } - lp_yyarg[lp_yycount++] = lp_yytname[lp_yyx]; - lp_yysize1 = lp_yysize + lp_yytnamerr (0, lp_yytname[lp_yyx]); - lp_yysize_overflow |= (lp_yysize1 < lp_yysize); - lp_yysize = lp_yysize1; - lp_yyfmt = lp_yystpcpy (lp_yyfmt, lp_yyprefix); - lp_yyprefix = lp_yyor; - } - - lp_yyf = YY_(lp_yyformat); - lp_yysize1 = lp_yysize + lp_yystrlen (lp_yyf); - lp_yysize_overflow |= (lp_yysize1 < lp_yysize); - lp_yysize = lp_yysize1; - - if (lp_yysize_overflow) - return YYSIZE_MAXIMUM; - - if (lp_yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *lp_yyp = lp_yyresult; - int lp_yyi = 0; - while ((*lp_yyp = *lp_yyf) != '\0') - { - if (*lp_yyp == '%' && lp_yyf[1] == 's' && lp_yyi < lp_yycount) - { - lp_yyp += lp_yytnamerr (lp_yyp, lp_yyarg[lp_yyi++]); - lp_yyf += 2; - } - else - { - lp_yyp++; - lp_yyf++; - } - } - } - return lp_yysize; - } -} -#endif /* YYERROR_VERBOSE */ - - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -lp_yydestruct (const char *lp_yymsg, int lp_yytype, YYSTYPE *lp_yyvaluep, parse_parm *parm, void *scanner) -#else -static void -lp_yydestruct (lp_yymsg, lp_yytype, lp_yyvaluep, parm, scanner) - const char *lp_yymsg; - int lp_yytype; - YYSTYPE *lp_yyvaluep; - parse_parm *parm; - void *scanner; -#endif -{ - YYUSE (lp_yyvaluep); - YYUSE (parm); - YYUSE (scanner); - - if (!lp_yymsg) - lp_yymsg = "Deleting"; - YY_SYMBOL_PRINT (lp_yymsg, lp_yytype, lp_yyvaluep, lp_yylocationp); - - switch (lp_yytype) - { - - default: - break; - } -} - - -/* Prevent warnings from -Wmissing-prototypes. */ - -#ifdef YYPARSE_PARAM -#if defined __STDC__ || defined __cplusplus -int lp_yyparse (void *YYPARSE_PARAM); -#else -int lp_yyparse (); -#endif -#else /* ! YYPARSE_PARAM */ -#if defined __STDC__ || defined __cplusplus -int lp_yyparse (parse_parm *parm, void *scanner); -#else -int lp_yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ - - - - - - -/*----------. -| lp_yyparse. | -`----------*/ - -#ifdef YYPARSE_PARAM -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -int -lp_yyparse (void *YYPARSE_PARAM) -#else -int -lp_yyparse (YYPARSE_PARAM) - void *YYPARSE_PARAM; -#endif -#else /* ! YYPARSE_PARAM */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -int -lp_yyparse (parse_parm *parm, void *scanner) -#else -int -lp_yyparse (parm, scanner) - parse_parm *parm; - void *scanner; -#endif -#endif -{ - /* The look-ahead symbol. */ -int lp_yychar; - -/* The semantic value of the look-ahead symbol. */ -YYSTYPE lp_yylval; - -/* Number of syntax errors so far. */ -int lp_yynerrs; - - int lp_yystate; - int lp_yyn; - int lp_yyresult; - /* Number of tokens to shift before error messages enabled. */ - int lp_yyerrstatus; - /* Look-ahead token as an internal (translated) token number. */ - int lp_yytoken = 0; -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char lp_yymsgbuf[128]; - char *lp_yymsg = lp_yymsgbuf; - YYSIZE_T lp_yymsg_alloc = sizeof lp_yymsgbuf; -#endif - - /* Three stacks and their tools: - `lp_yyss': related to states, - `lp_yyvs': related to semantic values, - `lp_yyls': related to locations. - - Refer to the stacks thru separate pointers, to allow lp_yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - lp_yytype_int16 lp_yyssa[YYINITDEPTH]; - lp_yytype_int16 *lp_yyss = lp_yyssa; - lp_yytype_int16 *lp_yyssp; - - /* The semantic value stack. */ - YYSTYPE lp_yyvsa[YYINITDEPTH]; - YYSTYPE *lp_yyvs = lp_yyvsa; - YYSTYPE *lp_yyvsp; - - - -#define YYPOPSTACK(N) (lp_yyvsp -= (N), lp_yyssp -= (N)) - - YYSIZE_T lp_yystacksize = YYINITDEPTH; - - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE lp_yyval; - - - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int lp_yylen = 0; - - YYDPRINTF ((stderr, "Starting parse\n")); - - lp_yystate = 0; - lp_yyerrstatus = 0; - lp_yynerrs = 0; - lp_yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - - lp_yyssp = lp_yyss; - lp_yyvsp = lp_yyvs; - - goto lp_yysetstate; - -/*------------------------------------------------------------. -| lp_yynewstate -- Push a new state, which is found in lp_yystate. | -`------------------------------------------------------------*/ - lp_yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - lp_yyssp++; - - lp_yysetstate: - *lp_yyssp = lp_yystate; - - if (lp_yyss + lp_yystacksize - 1 <= lp_yyssp) - { - /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T lp_yysize = lp_yyssp - lp_yyss + 1; - -#ifdef lp_yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *lp_yyvs1 = lp_yyvs; - lp_yytype_int16 *lp_yyss1 = lp_yyss; - - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if lp_yyoverflow is a macro. */ - lp_yyoverflow (YY_("memory exhausted"), - &lp_yyss1, lp_yysize * sizeof (*lp_yyssp), - &lp_yyvs1, lp_yysize * sizeof (*lp_yyvsp), - - &lp_yystacksize); - - lp_yyss = lp_yyss1; - lp_yyvs = lp_yyvs1; - } -#else /* no lp_yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto lp_yyexhaustedlab; -# else - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= lp_yystacksize) - goto lp_yyexhaustedlab; - lp_yystacksize *= 2; - if (YYMAXDEPTH < lp_yystacksize) - lp_yystacksize = YYMAXDEPTH; - - { - lp_yytype_int16 *lp_yyss1 = lp_yyss; - union lp_yyalloc *lp_yyptr = - (union lp_yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (lp_yystacksize)); - if (! lp_yyptr) - goto lp_yyexhaustedlab; - YYSTACK_RELOCATE (lp_yyss); - YYSTACK_RELOCATE (lp_yyvs); - -# undef YYSTACK_RELOCATE - if (lp_yyss1 != lp_yyssa) - YYSTACK_FREE (lp_yyss1); - } -# endif -#endif /* no lp_yyoverflow */ - - lp_yyssp = lp_yyss + lp_yysize - 1; - lp_yyvsp = lp_yyvs + lp_yysize - 1; - - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) lp_yystacksize)); - - if (lp_yyss + lp_yystacksize - 1 <= lp_yyssp) - YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", lp_yystate)); - - goto lp_yybackup; - -/*-----------. -| lp_yybackup. | -`-----------*/ -lp_yybackup: - - /* Do appropriate processing given the current state. Read a - look-ahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to look-ahead token. */ - lp_yyn = lp_yypact[lp_yystate]; - if (lp_yyn == YYPACT_NINF) - goto lp_yydefault; - - /* Not known => get a look-ahead token if don't already have one. */ - - /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ - if (lp_yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); - lp_yychar = YYLEX; - } - - if (lp_yychar <= YYEOF) - { - lp_yychar = lp_yytoken = YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else - { - lp_yytoken = YYTRANSLATE (lp_yychar); - YY_SYMBOL_PRINT ("Next token is", lp_yytoken, &lp_yylval, &lp_yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - lp_yyn += lp_yytoken; - if (lp_yyn < 0 || YYLAST < lp_yyn || lp_yycheck[lp_yyn] != lp_yytoken) - goto lp_yydefault; - lp_yyn = lp_yytable[lp_yyn]; - if (lp_yyn <= 0) - { - if (lp_yyn == 0 || lp_yyn == YYTABLE_NINF) - goto lp_yyerrlab; - lp_yyn = -lp_yyn; - goto lp_yyreduce; - } - - if (lp_yyn == YYFINAL) - YYACCEPT; - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (lp_yyerrstatus) - lp_yyerrstatus--; - - /* Shift the look-ahead token. */ - YY_SYMBOL_PRINT ("Shifting", lp_yytoken, &lp_yylval, &lp_yylloc); - - /* Discard the shifted token unless it is eof. */ - if (lp_yychar != YYEOF) - lp_yychar = YYEMPTY; - - lp_yystate = lp_yyn; - *++lp_yyvsp = lp_yylval; - - goto lp_yynewstate; - - -/*-----------------------------------------------------------. -| lp_yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -lp_yydefault: - lp_yyn = lp_yydefact[lp_yystate]; - if (lp_yyn == 0) - goto lp_yyerrlab; - goto lp_yyreduce; - - -/*-----------------------------. -| lp_yyreduce -- Do a reduction. | -`-----------------------------*/ -lp_yyreduce: - /* lp_yyn is the number of a rule to reduce with. */ - lp_yylen = lp_yyr2[lp_yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - `$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - lp_yyval = lp_yyvsp[1-lp_yylen]; - - - YY_REDUCE_PRINT (lp_yyn); - switch (lp_yyn) - { - case 3: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->isign = 0; - pv->make_neg = 0; - pv->Sign = 0; - pv->HadConstraint = FALSE; - pv->HadVar = pv->HadVar0 = FALSE; -} - break; - - case 5: - - { - set_obj_dir(PARM, TRUE); -} - break; - - case 6: - - { - set_obj_dir(PARM, FALSE); -} - break; - - case 8: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - add_row(pp); - pv->HadConstraint = FALSE; - pv->HadVar = pv->HadVar0 = FALSE; - pv->isign = 0; - pv->make_neg = 0; -} - break; - - case 16: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if(!add_constraint_name(pp, pv->Last_var)) - YYABORT; - pv->HadConstraint = TRUE; -} - break; - - case 18: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->HadVar1 = pv->HadVar0; - pv->HadVar0 = FALSE; -} - break; - - case 19: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if(!store_re_op(pp, pv->OP, (int) pv->HadConstraint, (int) pv->HadVar, (int) pv->Had_lineair_sum)) - YYABORT; - pv->make_neg = 1; - pv->f1 = 0; -} - break; - - case 20: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->Had_lineair_sum0 = pv->Had_lineair_sum; - pv->Had_lineair_sum = TRUE; - pv->HadVar2 = pv->HadVar0; - pv->HadVar0 = FALSE; - pv->do_add_row = FALSE; - if(pv->HadConstraint && !pv->HadVar ) { - /* it is a range */ - /* already handled */ - } - else if(!pv->HadConstraint && pv->HadVar) { - /* it is a bound */ - - if(!store_bounds(pp, TRUE)) - YYABORT; - } - else { - /* it is a row restriction */ - if(pv->HadConstraint && pv->HadVar) - store_re_op(pp, '\0', (int) pv->HadConstraint, (int) pv->HadVar, (int) pv->Had_lineair_sum); /* makes sure that data stored in temporary buffers is treated correctly */ - pv->do_add_row = TRUE; - } -} - break; - - case 21: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if((!pv->HadVar) && (!pv->HadConstraint)) { - lp_yyerror(pp, pp->scanner, "parse error"); - YYABORT; - } - if(pv->do_add_row) - add_row(pp); - pv->HadConstraint = FALSE; - pv->HadVar = pv->HadVar0 = FALSE; - pv->isign = 0; - pv->make_neg = 0; - null_tmp_store(pp, TRUE); -} - break; - - case 22: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if((!pv->HadVar1) && (pv->Had_lineair_sum0)) - if(!negate_constraint(pp)) - YYABORT; -} - break; - - case 23: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->make_neg = 0; - pv->isign = 0; - if(pv->HadConstraint) - pv->HadVar = pv->Had_lineair_sum = FALSE; - pv->HadVar0 = FALSE; - if(!store_re_op(pp, (char) ((pv->OP == '<') ? '>' : (pv->OP == '>') ? '<' : pv->OP), (int) pv->HadConstraint, (int) pv->HadVar, (int) pv->Had_lineair_sum)) - YYABORT; -} - break; - - case 24: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->f -= pv->f1; -} - break; - - case 25: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if((pv->HadVar1) || (!pv->HadVar2) || (pv->HadVar0)) { - lp_yyerror(pp, pp->scanner, "parse error"); - YYABORT; - } - - if(pv->HadConstraint && !pv->HadVar ) { - /* it is a range */ - /* already handled */ - if(!negate_constraint(pp)) - YYABORT; - } - else if(!pv->HadConstraint && pv->HadVar) { - /* it is a bound */ - - if(!store_bounds(pp, TRUE)) - YYABORT; - } -} - break; - - case 26: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - /* to allow a range */ - /* constraint: < max */ - if(!pv->HadConstraint) { - lp_yyerror(pp, pp->scanner, "parse error"); - YYABORT; - } - pv->Had_lineair_sum = FALSE; -} - break; - - case 27: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->Had_lineair_sum = TRUE; -} - break; - - case 29: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->isign = pv->Sign; -} - break; - - case 31: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->state = pv->state0 = 0; -} - break; - - case 32: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if (pv->state == 1) { - /* RHS_STORE */ - if ( (pv->isign0 || !pv->make_neg) - && !(pv->isign0 && !pv->make_neg)) /* but not both! */ - pv->f0 = -pv->f0; - if(pv->make_neg) - pv->f1 += pv->f0; - if(!rhs_store(pp, pv->f0, (int) pv->HadConstraint, (int) pv->HadVar, (int) pv->Had_lineair_sum)) - YYABORT; - } -} - break; - - case 35: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if ((pv->HadSign || pv->state == 1) && (pv->state0 == 1)) { - /* RHS_STORE */ - if ( (pv->isign0 || !pv->make_neg) - && !(pv->isign0 && !pv->make_neg)) /* but not both! */ - pv->f0 = -pv->f0; - if(pv->make_neg) - pv->f1 += pv->f0; - if(!rhs_store(pp, pv->f0, (int) pv->HadConstraint, (int) pv->HadVar, (int) pv->Had_lineair_sum)) - YYABORT; - } - if (pv->state == 1) { - pv->f0 = pv->f; - pv->isign0 = pv->isign; - } - if (pv->state == 2) { - if((pv->HadSign) || (pv->state0 != 1)) { - pv->isign0 = pv->isign; - pv->f0 = 1.0; - } - if ( (pv->isign0 || pv->make_neg) - && !(pv->isign0 && pv->make_neg)) /* but not both! */ - pv->f0 = -pv->f0; - if(!var_store(pp, pv->Last_var, pv->f0, (int) pv->HadConstraint, (int) pv->HadVar, (int) pv->Had_lineair_sum)) { - lp_yyerror(pp, pp->scanner, "var_store failed"); - YYABORT; - } - pv->HadConstraint |= pv->HadVar; - pv->HadVar = pv->HadVar0 = TRUE; - } - pv->state0 = pv->state; -} - break; - - case 36: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->state = 1; -} - break; - - case 37: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if ((pv->HasAR_M_OP) && (pv->state != 1)) { - lp_yyerror(pp, pp->scanner, "parse error"); - YYABORT; - } -} - break; - - case 38: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->state = 2; -} - break; - - case 43: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->isign = pv->Sign; -} - break; - - case 46: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->isign = 0; - pv->HadSign = FALSE; -} - break; - - case 47: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->isign = pv->Sign; - pv->HadSign = TRUE; -} - break; - - case 48: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->HasAR_M_OP = FALSE; -} - break; - - case 49: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->HasAR_M_OP = TRUE; -} - break; - - case 50: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if ( (pv->isign || !pv->make_neg) - && !(pv->isign && !pv->make_neg)) /* but not both! */ - pv->f = -pv->f; - if(!rhs_store(pp, pv->f, (int) pv->HadConstraint, (int) pv->HadVar, (int) pv->Had_lineair_sum)) - YYABORT; - pv->isign = 0; -} - break; - - case 60: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pv->Within_sos_decl1 = pv->Within_sos_decl; -} - break; - - case 62: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if((!pv->Within_int_decl) && (!pv->Within_sec_decl) && (!pv->Within_sos_decl1) && (!pv->Within_free_decl)) { - lp_yyerror(pp, pp->scanner, "parse error"); - YYABORT; - } - pv->SOStype = pv->SOStype0; - check_int_sec_sos_free_decl(pp, (int) pv->Within_int_decl, (int) pv->Within_sec_decl, (int) (pv->Within_sos_decl1 = (pv->Within_sos_decl1 ? 1 : 0)), (int) pv->Within_free_decl); -} - break; - - case 63: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if((pv->Within_sos_decl1) && (pv->SOStype == 0)) - { - lp_yyerror(pp, pp->scanner, "Unsupported SOS type (0)"); - YYABORT; - } -} - break; - - case 67: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - FREE(pv->Last_var0); - pv->Last_var0 = strdup(pv->Last_var); -} - break; - - case 69: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if(pv->Within_sos_decl1) { - set_sos_type(pp, pv->SOStype); - set_sos_weight(pp, (double) pv->SOSweight, 1); - } -} - break; - - case 70: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if((pv->Within_sos_decl1) && (!pv->SOStype)) - { - set_sos_type(pp, pv->SOStype = (short) (pv->f + .1)); - } - else - { - lp_yyerror(pp, pp->scanner, "SOS type not expected"); - YYABORT; - } -} - break; - - case 72: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - set_sos_weight(pp, (double) pv->SOSweight, 1); -} - break; - - case 73: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - set_sos_weight(pp, pv->f, 1); -} - break; - - case 80: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if(pv->Within_sos_decl1 == 1) - { - char buf[16]; - - pv->SOSweight++; - sprintf(buf, "SOS%d", pv->SOSweight); - storevarandweight(pp, buf); - - check_int_sec_sos_free_decl(pp, (int) pv->Within_int_decl, (int) pv->Within_sec_decl, 2, (int) pv->Within_free_decl); - pv->Within_sos_decl1 = 2; - pv->SOSNr = 0; - } - - storevarandweight(pp, pv->Last_var); - - if(pv->Within_sos_decl1 == 2) - { - pv->SOSNr++; - set_sos_weight(pp, (double) pv->SOSNr, 2); - } -} - break; - - case 81: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if(!pv->Within_sos_decl1) { - lp_yyerror(pp, pp->scanner, "parse error"); - YYABORT; - } - if(pv->Within_sos_decl1 == 1) { - FREE(pv->Last_var0); - pv->Last_var0 = strdup(pv->Last_var); - } - if(pv->Within_sos_decl1 == 2) - { - storevarandweight(pp, pv->Last_var); - pv->SOSNr++; - set_sos_weight(pp, (double) pv->SOSNr, 2); - } -} - break; - - case 82: - - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if(pv->Within_sos_decl1 == 1) - { - char buf[16]; - - pv->SOSweight++; - sprintf(buf, "SOS%d", pv->SOSweight); - storevarandweight(pp, buf); - - check_int_sec_sos_free_decl(pp, (int) pv->Within_int_decl, (int) pv->Within_sec_decl, 2, (int) pv->Within_free_decl); - pv->Within_sos_decl1 = 2; - pv->SOSNr = 0; - - storevarandweight(pp, pv->Last_var0); - pv->SOSNr++; - } - - set_sos_weight(pp, pv->f, 2); -} - break; - - case 83: - - { /* SOS name */ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - if(pv->Within_sos_decl1 == 1) - { - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - storevarandweight(pp, pv->Last_var0); - set_sos_type(pp, pv->SOStype); - check_int_sec_sos_free_decl(pp, (int) pv->Within_int_decl, (int) pv->Within_sec_decl, 2, (int) pv->Within_free_decl); - pv->Within_sos_decl1 = 2; - pv->SOSNr = 0; - pv->SOSweight++; - } -} - break; - - -/* Line 1267 of yacc.c. */ - - default: break; - } - YY_SYMBOL_PRINT ("-> $$ =", lp_yyr1[lp_yyn], &lp_yyval, &lp_yyloc); - - YYPOPSTACK (lp_yylen); - lp_yylen = 0; - YY_STACK_PRINT (lp_yyss, lp_yyssp); - - *++lp_yyvsp = lp_yyval; - - - /* Now `shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - - lp_yyn = lp_yyr1[lp_yyn]; - - lp_yystate = lp_yypgoto[lp_yyn - YYNTOKENS] + *lp_yyssp; - if (0 <= lp_yystate && lp_yystate <= YYLAST && lp_yycheck[lp_yystate] == *lp_yyssp) - lp_yystate = lp_yytable[lp_yystate]; - else - lp_yystate = lp_yydefgoto[lp_yyn - YYNTOKENS]; - - goto lp_yynewstate; - - -/*------------------------------------. -| lp_yyerrlab -- here on detecting error | -`------------------------------------*/ -lp_yyerrlab: - /* If not already recovering from an error, report this error. */ - if (!lp_yyerrstatus) - { - ++lp_yynerrs; -#if ! YYERROR_VERBOSE - lp_yyerror (parm, scanner, YY_("syntax error")); -#else - { - YYSIZE_T lp_yysize = lp_yysyntax_error (0, lp_yystate, lp_yychar); - if (lp_yymsg_alloc < lp_yysize && lp_yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T lp_yyalloc = 2 * lp_yysize; - if (! (lp_yysize <= lp_yyalloc && lp_yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - lp_yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (lp_yymsg != lp_yymsgbuf) - YYSTACK_FREE (lp_yymsg); - lp_yymsg = (char *) YYSTACK_ALLOC (lp_yyalloc); - if (lp_yymsg) - lp_yymsg_alloc = lp_yyalloc; - else - { - lp_yymsg = lp_yymsgbuf; - lp_yymsg_alloc = sizeof lp_yymsgbuf; - } - } - - if (0 < lp_yysize && lp_yysize <= lp_yymsg_alloc) - { - (void) lp_yysyntax_error (lp_yymsg, lp_yystate, lp_yychar); - lp_yyerror (parm, scanner, lp_yymsg); - } - else - { - lp_yyerror (parm, scanner, YY_("syntax error")); - if (lp_yysize != 0) - goto lp_yyexhaustedlab; - } - } -#endif - } - - - - if (lp_yyerrstatus == 3) - { - /* If just tried and failed to reuse look-ahead token after an - error, discard it. */ - - if (lp_yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (lp_yychar == YYEOF) - YYABORT; - } - else - { - lp_yydestruct ("Error: discarding", - lp_yytoken, &lp_yylval, parm, scanner); - lp_yychar = YYEMPTY; - } - } - - /* Else will try to reuse look-ahead token after shifting the error - token. */ - goto lp_yyerrlab1; - - -/*---------------------------------------------------. -| lp_yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -lp_yyerrorlab: - - /* Pacify compilers like GCC when the user code never invokes - YYERROR and the label lp_yyerrorlab therefore never appears in user - code. */ - if (/*CONSTCOND*/ 0) - goto lp_yyerrorlab; - - /* Do not reclaim the symbols of the rule which action triggered - this YYERROR. */ - YYPOPSTACK (lp_yylen); - lp_yylen = 0; - YY_STACK_PRINT (lp_yyss, lp_yyssp); - lp_yystate = *lp_yyssp; - goto lp_yyerrlab1; - - -/*-------------------------------------------------------------. -| lp_yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -lp_yyerrlab1: - lp_yyerrstatus = 3; /* Each real token shifted decrements this. */ - - for (;;) - { - lp_yyn = lp_yypact[lp_yystate]; - if (lp_yyn != YYPACT_NINF) - { - lp_yyn += YYTERROR; - if (0 <= lp_yyn && lp_yyn <= YYLAST && lp_yycheck[lp_yyn] == YYTERROR) - { - lp_yyn = lp_yytable[lp_yyn]; - if (0 < lp_yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (lp_yyssp == lp_yyss) - YYABORT; - - - lp_yydestruct ("Error: popping", - lp_yystos[lp_yystate], lp_yyvsp, parm, scanner); - YYPOPSTACK (1); - lp_yystate = *lp_yyssp; - YY_STACK_PRINT (lp_yyss, lp_yyssp); - } - - if (lp_yyn == YYFINAL) - YYACCEPT; - - *++lp_yyvsp = lp_yylval; - - - /* Shift the error token. */ - YY_SYMBOL_PRINT ("Shifting", lp_yystos[lp_yyn], lp_yyvsp, lp_yylsp); - - lp_yystate = lp_yyn; - goto lp_yynewstate; - - -/*-------------------------------------. -| lp_yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -lp_yyacceptlab: - lp_yyresult = 0; - goto lp_yyreturn; - -/*-----------------------------------. -| lp_yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -lp_yyabortlab: - lp_yyresult = 1; - goto lp_yyreturn; - -#ifndef lp_yyoverflow -/*-------------------------------------------------. -| lp_yyexhaustedlab -- memory exhaustion comes here. | -`-------------------------------------------------*/ -lp_yyexhaustedlab: - lp_yyerror (parm, scanner, YY_("memory exhausted")); - lp_yyresult = 2; - /* Fall through. */ -#endif - -lp_yyreturn: - if (lp_yychar != YYEOF && lp_yychar != YYEMPTY) - lp_yydestruct ("Cleanup: discarding lookahead", - lp_yytoken, &lp_yylval, parm, scanner); - /* Do not reclaim the symbols of the rule which action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK (lp_yylen); - YY_STACK_PRINT (lp_yyss, lp_yyssp); - while (lp_yyssp != lp_yyss) - { - lp_yydestruct ("Cleanup: popping", - lp_yystos[*lp_yyssp], lp_yyvsp, parm, scanner); - YYPOPSTACK (1); - } -#ifndef lp_yyoverflow - if (lp_yyss != lp_yyssa) - YYSTACK_FREE (lp_yyss); -#endif -#if YYERROR_VERBOSE - if (lp_yymsg != lp_yymsgbuf) - YYSTACK_FREE (lp_yymsg); -#endif - /* Make sure YYID is used. */ - return YYID (lp_yyresult); -} - - - - - -static void lp_yy_delete_allocated_memory(parse_parm *pp) -{ - parse_vars *pv = (parse_vars *) pp->parse_vars; - /* free memory allocated by flex. Otherwise some memory is not freed. - This is a bit tricky. There is not much documentation about this, but a lot of - reports of memory that keeps allocated */ - - /* If you get errors on this function call, just comment it. This will only result - in some memory that is not being freed. */ - -# if defined YY_CURRENT_BUFFER - /* flex defines the macro YY_CURRENT_BUFFER, so you should only get here if lp_rlp.h is - generated by flex */ - /* lex doesn't define this macro and thus should not come here, but lex doesn't has - this memory leak also ...*/ - -# if 0 - /* older versions of flex */ - lp_yy_delete_buffer(YY_CURRENT_BUFFER); /* comment this line if you have problems with it */ - lp_yy_init = 1; /* make sure that the next time memory is allocated again */ - lp_yy_start = 0; -# else - /* As of version 2.5.9 Flex */ - lp_yylex_destroy(pp->scanner); /* comment this line if you have problems with it */ -# endif -# endif - - FREE(pv->Last_var); - FREE(pv->Last_var0); -} - -static int parse(parse_parm *pp) -{ - return(lp_yyparse(pp, pp->scanner)); -} - -lprec *read_lp1(lprec *lp, void *userhandle, read_modeldata_func read_modeldata, int verbose, char *lp_name) -{ - parse_vars *pv; - lprec *lp1 = NULL; - - CALLOC(pv, 1, parse_vars); - if (pv != NULL) { - parse_parm pp; - - memset(&pp, 0, sizeof(pp)); - pp.parse_vars = (void *) pv; - - lp_yylex_init(&pp.scanner); - lp_yyset_extra(&pp, pp.scanner); - - lp_yyset_in((FILE *) userhandle, pp.scanner); - lp_yyset_out(NULL, pp.scanner); - pv->lp_input = read_modeldata; - pv->userhandle = userhandle; - lp1 = yacc_read(lp, verbose, lp_name, parse, &pp, lp_yy_delete_allocated_memory); - FREE(pv); - } - return(lp1); -} - -lprec * __WINAPI read_lp(FILE *filename, int verbose, char *lp_name) -{ - return(read_lp1(NULL, filename, lp_input_lp_yyin, verbose, lp_name)); -} - -lprec * __WINAPI read_lpex(void *userhandle, read_modeldata_func read_modeldata, int verbose, char *lp_name) -{ - return(read_lp1(NULL, userhandle, read_modeldata, verbose, lp_name)); -} - -lprec *read_LP1(lprec *lp, char *filename, int verbose, char *lp_name) -{ - FILE *fpin; - - if((fpin = fopen(filename, "r")) != NULL) { - lp = read_lp1(lp, fpin, lp_input_lp_yyin, verbose, lp_name); - fclose(fpin); - } - else - lp = NULL; - return(lp); -} - -lprec * __WINAPI read_LP(char *filename, int verbose, char *lp_name) -{ - return(read_LP1(NULL, filename, verbose, lp_name)); -} - -MYBOOL __WINAPI LP_readhandle(lprec **lp, FILE *filename, int verbose, char *lp_name) -{ - if(lp != NULL) - *lp = read_lp1(*lp, filename, lp_input_lp_yyin, verbose, lp_name); - - return((lp != NULL) && (*lp != NULL)); -} - diff --git a/src/lpsolve/build/lp_solve/lusol1.c b/src/lpsolve/build/lp_solve/lusol1.c deleted file mode 100644 index e0998e76..00000000 --- a/src/lpsolve/build/lp_solve/lusol1.c +++ /dev/null @@ -1,3725 +0,0 @@ - -/* ================================================================== - lu1DCP factors a dense m x n matrix A by Gaussian elimination, - using Complete Pivoting (row and column interchanges) for stability. - This version also uses column interchanges if all elements in a - pivot column are smaller than (or equal to) "small". Such columns - are changed to zero and permuted to the right-hand end. - As in LINPACK's dgefa, ipvt(!) keeps track of pivot rows. - Rows of U are interchanged, but we don't have to physically - permute rows of L. In contrast, column interchanges are applied - directly to the columns of both L and U, and to the column - permutation vector iq(*). - ------------------------------------------------------------------ - On entry: - a Array holding the matrix A to be factored. - lda The leading dimension of the array a. - m The number of rows in A. - n The number of columns in A. - small A drop tolerance. Must be zero or positive. - - On exit: - a An upper triangular matrix and the multipliers - which were used to obtain it. - The factorization can be written A = L*U where - L is a product of permutation and unit lower - triangular matrices and U is upper triangular. - nsing Number of singularities detected. - ipvt Records the pivot rows. - iq A vector to which column interchanges are applied. - ------------------------------------------------------------------ - 01 May 2002: First dense Complete Pivoting, derived from lu1DPP. - 07 May 2002: Another break needed at end of first loop. - 07 May 2002: Current version of lu1DCP. - ================================================================== */ -void LU1DCP(LUSOLrec *LUSOL, LPSREAL DA[], int LDA, int M, int N, LPSREAL SMALL, - int *NSING, int IPVT[], int IX[]) -{ - - int I, J, K, KP1, L, LAST, LENCOL, IMAX, JMAX, JLAST, JNEW; - LPSREAL AIJMAX, AJMAX; - register LPSREAL T; -#ifdef LUSOLFastDenseIndex - register LPSREAL *DA1, *DA2; - int IDA1, IDA2; -#else - register int IDA1, IDA2; -#endif - - *NSING = 0; - LENCOL = M+1; - LAST = N; -/* ----------------------------------------------------------------- - Start of elimination loop. - ----------------------------------------------------------------- */ - for(K = 1; K <= N; K++) { - KP1 = K+1; - LENCOL--; -/* Find the biggest aij in row imax and column jmax. */ - AIJMAX = ZERO; - IMAX = K; - JMAX = K; - JLAST = LAST; - for(J = K; J <= JLAST; J++) { -x10: - L = lps_idamax(LENCOL,DA+DAPOS(K,J)-LUSOL_ARRAYOFFSET,1)+K-1; - AJMAX = fabs(DA[DAPOS(L,J)]); - if(AJMAX<=SMALL) { -/* ======================================================== - Do column interchange, changing old column to zero. - Reduce "last" and try again with same j. - ======================================================== */ - (*NSING)++; - JNEW = IX[LAST]; - IX[LAST] = IX[J]; - IX[J] = JNEW; -#ifdef LUSOLFastDenseIndex - DA1 = DA+DAPOS(0,LAST); - DA2 = DA+DAPOS(0,J); - for(I = 1; I <= K-1; I++) { - DA1++; - DA2++; - T = *DA1; - *DA1 = *DA2; - *DA2 = T; -#else - for(I = 1; I <= K-1; I++) { - IDA1 = DAPOS(I,LAST); - IDA2 = DAPOS(I,J); - T = DA[IDA1]; - DA[IDA1] = DA[IDA2]; - DA[IDA2] = T; -#endif - } -#ifdef LUSOLFastDenseIndex - for(I = K; I <= M; I++) { - DA1++; - DA2++; - T = *DA1; - *DA1 = ZERO; - *DA2 = T; -#else - for(I = K; I <= M; I++) { - IDA1 = DAPOS(I,LAST); - IDA2 = DAPOS(I,J); - T = DA[IDA1]; - DA[IDA1] = ZERO; - DA[IDA2] = T; -#endif - } - LAST--; - if(J<=LAST) - goto x10; - break; - } -/* Check if this column has biggest aij so far. */ - if(AIJMAX=LAST) - break; - } - IPVT[K] = IMAX; - if(JMAX!=K) { -/* ========================================================== - Do column interchange (k and jmax). - ========================================================== */ - JNEW = IX[JMAX]; - IX[JMAX] = IX[K]; - IX[K] = JNEW; -#ifdef LUSOLFastDenseIndex - DA1 = DA+DAPOS(0,JMAX); - DA2 = DA+DAPOS(0,K); - for(I = 1; I <= M; I++) { - DA1++; - DA2++; - T = *DA1; - *DA1 = *DA2; - *DA2 = T; -#else - for(I = 1; I <= M; I++) { - IDA1 = DAPOS(I,JMAX); - IDA2 = DAPOS(I,K); - T = DA[IDA1]; - DA[IDA1] = DA[IDA2]; - DA[IDA2] = T; -#endif - } - } - if(M>K) { -/* =========================================================== - Do row interchange if necessary. - =========================================================== */ - if(IMAX!=K) { - IDA1 = DAPOS(IMAX,K); - IDA2 = DAPOS(K,K); - T = DA[IDA1]; - DA[IDA1] = DA[IDA2]; - DA[IDA2] = T; - } -/* =========================================================== - Compute multipliers. - Do row elimination with column indexing. - =========================================================== */ - T = -ONE/DA[DAPOS(K,K)]; - lps_dscal(M-K,T,DA+DAPOS(KP1,K)-LUSOL_ARRAYOFFSET,1); - for(J = KP1; J <= LAST; J++) { - IDA1 = DAPOS(IMAX,J); - T = DA[IDA1]; - if(IMAX!=K) { - IDA2 = DAPOS(K,J); - DA[IDA1] = DA[IDA2]; - DA[IDA2] = T; - } - lps_daxpy(M-K,T,DA+DAPOS(KP1,K)-LUSOL_ARRAYOFFSET,1, - DA+DAPOS(KP1,J)-LUSOL_ARRAYOFFSET,1); - } - } - else - break; - if(K>=LAST) - break; - } -/* Set ipvt(*) for singular rows. */ - for(K = LAST+1; K <= M; K++) - IPVT[K] = K; - -} - -/* ================================================================== - lu1DPP factors a dense m x n matrix A by Gaussian elimination, - using row interchanges for stability, as in dgefa from LINPACK. - This version also uses column interchanges if all elements in a - pivot column are smaller than (or equal to) "small". Such columns - are changed to zero and permuted to the right-hand end. - As in LINPACK, ipvt(*) keeps track of pivot rows. - Rows of U are interchanged, but we don't have to physically - permute rows of L. In contrast, column interchanges are applied - directly to the columns of both L and U, and to the column - permutation vector iq(*). - ------------------------------------------------------------------ - On entry: - a Array holding the matrix A to be factored. - lda The leading dimension of the array a. - m The number of rows in A. - n The number of columns in A. - small A drop tolerance. Must be zero or positive. - - On exit: - a An upper triangular matrix and the multipliers - which were used to obtain it. - The factorization can be written A = L*U where - L is a product of permutation and unit lower - triangular matrices and U is upper triangular. - nsing Number of singularities detected. - ipvt Records the pivot rows. - iq A vector to which column interchanges are applied. - ------------------------------------------------------------------ - 02 May 1989: First version derived from dgefa - in LINPACK (version dated 08/14/78). - 05 Feb 1994: Generalized to treat rectangular matrices - and use column interchanges when necessary. - ipvt is retained, but column permutations are applied - directly to iq(*). - 21 Dec 1994: Bug found via example from Steve Dirkse. - Loop 100 added to set ipvt(*) for singular rows. - ================================================================== */ -void LU1DPP(LUSOLrec *LUSOL, LPSREAL DA[], int LDA, int M, int N, LPSREAL SMALL, - int *NSING, int IPVT[], int IX[]) -{ - int I, J, K, KP1, L, LAST, LENCOL; - register LPSREAL T; -#ifdef LUSOLFastDenseIndex - register LPSREAL *DA1, *DA2; - int IDA1, IDA2; -#else - register int IDA1, IDA2; -#endif - - *NSING = 0; - K = 1; - LAST = N; -/* ------------------------------------------------------------------ - Start of elimination loop. - ------------------------------------------------------------------ */ -x10: - KP1 = K+1; - LENCOL = (M-K)+1; -/* Find l, the pivot row. */ - L = (lps_idamax(LENCOL,DA+DAPOS(K,K)-LUSOL_ARRAYOFFSET,1)+K)-1; - IPVT[K] = L; - if(fabs(DA[DAPOS(L,K)])<=SMALL) { -/* =============================================================== - Do column interchange, changing old pivot column to zero. - Reduce "last" and try again with same k. - =============================================================== */ - (*NSING)++; - J = IX[LAST]; - IX[LAST] = IX[K]; - IX[K] = J; -#ifdef LUSOLFastDenseIndex - DA1 = DA+DAPOS(0,LAST); - DA2 = DA+DAPOS(0,K); - for(I = 1; I <= K-1; I++) { - DA1++; - DA2++; - T = *DA1; - *DA1 = *DA2; - *DA2 = T; -#else - for(I = 1; I <= K-1; I++) { - IDA1 = DAPOS(I,LAST); - IDA2 = DAPOS(I,K); - T = DA[IDA1]; - DA[IDA1] = DA[IDA2]; - DA[IDA2] = T; -#endif - } -#ifdef LUSOLFastDenseIndex - for(I = K; I <= M; I++) { - DA1++; - DA2++; - T = *DA1; - *DA1 = ZERO; - *DA2 = T; -#else - for(I = K; I <= M; I++) { - IDA1 = DAPOS(I,LAST); - IDA2 = DAPOS(I,K); - T = DA[IDA1]; - DA[IDA1] = ZERO; - DA[IDA2] = T; -#endif - } - LAST = LAST-1; - if(K<=LAST) - goto x10; - } - else if(M>K) { -/* =============================================================== - Do row interchange if necessary. - =============================================================== */ - if(L!=K) { - IDA1 = DAPOS(L,K); - IDA2 = DAPOS(K,K); - T = DA[IDA1]; - DA[IDA1] = DA[IDA2]; - DA[IDA2] = T; - } -/* =============================================================== - Compute multipliers. - Do row elimination with column indexing. - =============================================================== */ - T = -ONE/DA[DAPOS(K,K)]; - lps_dscal(M-K,T,DA+DAPOS(KP1,K)-LUSOL_ARRAYOFFSET,1); - for(J = KP1; J <= LAST; J++) { - IDA1 = DAPOS(L,J); - T = DA[IDA1]; - if(L!=K) { - IDA2 = DAPOS(K,J); - DA[IDA1] = DA[IDA2]; - DA[IDA2] = T; - } - lps_daxpy(M-K,T,DA+DAPOS(KP1,K)-LUSOL_ARRAYOFFSET,1, - DA+DAPOS(KP1,J)-LUSOL_ARRAYOFFSET,1); - } - K++; - if(K<=LAST) - goto x10; - } -/* Set ipvt(*) for singular rows. */ - for(K = LAST+1; K <= M; K++) - IPVT[K] = K; - -} - - -/* ================================================================== - lu1pq1 constructs a permutation iperm from the array len. - ------------------------------------------------------------------ - On entry: - len(i) holds the number of nonzeros in the i-th row (say) - of an m by n matrix. - num(*) can be anything (workspace). - - On exit: - iperm contains a list of row numbers in the order - rows of length 0, rows of length 1,..., rows of length n. - loc(nz) points to the first row containing nz nonzeros, - nz = 1, n. - inv(i) points to the position of row i within iperm(*). - ================================================================== */ -void LU1PQ1(LUSOLrec *LUSOL, int M, int N, int LEN[], - int IPERM[], int LOC[], int INV[], int NUM[]) -{ - int NZEROS, NZ, I, L; - -/* Count the number of rows of each length. */ - NZEROS = 0; - for(NZ = 1; NZ <= N; NZ++) { - NUM[NZ] = 0; - LOC[NZ] = 0; - } - for(I = 1; I <= M; I++) { - NZ = LEN[I]; - if(NZ==0) - NZEROS++; - else - NUM[NZ]++; - } -/* Set starting locations for each length. */ - L = NZEROS+1; - for(NZ = 1; NZ <= N; NZ++) { - LOC[NZ] = L; - L += NUM[NZ]; - NUM[NZ] = 0; - } -/* Form the list. */ - NZEROS = 0; - for(I = 1; I <= M; I++) { - NZ = LEN[I]; - if(NZ==0) { - NZEROS++; - IPERM[NZEROS] = I; - } - else { - L = LOC[NZ]+NUM[NZ]; - IPERM[L] = I; - NUM[NZ]++; - } - } -/* Define the inverse of iperm. */ - for(L = 1; L <= M; L++) { - I = IPERM[L]; - INV[I] = L; - } -} - -/* ================================================================== - lu1pq2 frees the space occupied by the pivot row, - and updates the column permutation iq. - Also used to free the pivot column and update the row perm ip. - ------------------------------------------------------------------ - nzpiv (input) is the length of the pivot row (or column). - nzchng (output) is the net change in total nonzeros. - ------------------------------------------------------------------ - 14 Apr 1989 First version. - ================================================================== */ -void LU1PQ2(LUSOLrec *LUSOL, int NZPIV, int *NZCHNG, - int IND[], int LENOLD[], int LENNEW[], int IXLOC[], int IX[], int IXINV[]) -{ - int LR, J, NZ, NZNEW, L, NEXT, LNEW, JNEW; - - *NZCHNG = 0; - for(LR = 1; LR <= NZPIV; LR++) { - J = IND[LR]; - IND[LR] = 0; - NZ = LENOLD[LR]; - NZNEW = LENNEW[J]; - if(NZ!=NZNEW) { - L = IXINV[J]; - *NZCHNG = (*NZCHNG+NZNEW)-NZ; -/* l above is the position of column j in iq (so j = iq(l)). */ - if(NZNZNEW) - goto x120; - } - IX[LNEW] = J; - IXINV[J] = LNEW; - } - } -} - -/* ================================================================== - lu1pq3 looks at the permutation iperm(*) and moves any entries - to the end whose corresponding length len(*) is zero. - ------------------------------------------------------------------ - 09 Feb 1994: Added work array iw(*) to improve efficiency. - ================================================================== */ -void LU1PQ3(LUSOLrec *LUSOL, int MN, int LEN[], int IPERM[], int IW[], int *NRANK) -{ - int NZEROS, K, I; - - *NRANK = 0; - NZEROS = 0; - for(K = 1; K <= MN; K++) { - I = IPERM[K]; - if(LEN[I]==0) { - NZEROS++; - IW[NZEROS] = I; - } - else { - (*NRANK)++; - IPERM[*NRANK] = I; - } - } - for(K = 1; K <= NZEROS; K++) - IPERM[(*NRANK)+K] = IW[K]; -} - -/* ================================================================== - lu1rec - ------------------------------------------------------------------ - On exit: - ltop is the length of useful entries in ind(*), a(*). - ind(ltop+1) is "i" such that len(i), loc(i) belong to the last - item in ind(*), a(*). - ------------------------------------------------------------------ - 00 Jun 1983: Original version of lu1rec followed John Reid's - compression routine in LA05. It recovered - space in ind(*) and optionally a(*) - by eliminating entries with ind(l) = 0. - The elements of ind(*) could not be negative. - If len(i) was positive, entry i contained - that many elements, starting at loc(i). - Otherwise, entry i was eliminated. - 23 Mar 2001: Realised we could have len(i) = 0 in rare cases! - (Mostly during TCP when the pivot row contains - a column of length 1 that couldn't be a pivot.) - Revised storage scheme to - keep entries with ind(l) > 0, - squeeze out entries with -n <= ind(l) <= 0, - and to allow len(i) = 0. - Empty items are moved to the end of the compressed - ind(*) and/or a(*) arrays are given one empty space. - Items with len(i) < 0 are still eliminated. - 27 Mar 2001: Decided to use only ind(l) > 0 and = 0 in lu1fad. - Still have to keep entries with len(i) = 0. - ================================================================== */ -void LU1REC(LUSOLrec *LUSOL, int N, MYBOOL REALS, int *LTOP, - int IND[], int LEN[], int LOC[]) -{ - int NEMPTY, I, LENI, L, LEND, K, KLAST, ILAST, LPRINT; - - NEMPTY = 0; - for(I = 1; I <= N; I++) { - LENI = LEN[I]; - if(LENI>0) { - L = (LOC[I]+LENI)-1; - LEN[I] = IND[L]; - IND[L] = -(N+I); - } - else if(LENI==0) - NEMPTY++; - } - K = 0; -/* Previous k */ - KLAST = 0; -/* Last entry moved. */ - ILAST = 0; - LEND = *LTOP; - for(L = 1; L <= LEND; L++) { - I = IND[L]; - if(I>0) { - K++; - IND[K] = I; - if(REALS) - LUSOL->a[K] = LUSOL->a[L]; - } - else if(I<-N) { -/* This is the end of entry i. */ - I = -(N+I); - ILAST = I; - K++; - IND[K] = LEN[I]; - if(REALS) - LUSOL->a[K] = LUSOL->a[L]; - LOC[I] = KLAST+1; - LEN[I] = K-KLAST; - KLAST = K; - } - } -/* Move any empty items to the end, adding 1 free entry for each. */ - if(NEMPTY>0) { - for(I = 1; I <= N; I++) { - if(LEN[I]==0) { - K++; - LOC[I] = K; - IND[K] = 0; - ILAST = I; - } - } - } - LPRINT = LUSOL->luparm[LUSOL_IP_PRINTLEVEL]; - if(LPRINT>=LUSOL_MSG_PIVOT) - LUSOL_report(LUSOL, 0, "lu1rec. File compressed from %d to %d\n", - *LTOP,K,REALS,NEMPTY); -/* ncp */ - LUSOL->luparm[LUSOL_IP_COMPRESSIONS_LU]++; -/* Return ilast in ind(ltop + 1). */ - *LTOP = K; - IND[(*LTOP)+1] = ILAST; -} - -/* ================================================================== - lu1slk sets w(j) > 0 if column j is a unit vector. - ------------------------------------------------------------------ - 21 Nov 2000: First version. lu1fad needs it for TCP. - Note that w(*) is nominally an integer array, - but the only spare space is the double array w(*). - ================================================================== */ -void LU1SLK(LUSOLrec *LUSOL) -{ - int J, LC1, LQ, LQ1, LQ2; - - for(J = 1; J <= LUSOL->n; J++) { - LUSOL->w[J] = 0; - } - LQ1 = (LUSOL->iqloc ? LUSOL->iqloc[1] : LUSOL->n+1); -/* LQ1 = LUSOL->iqloc[1]; This is the original version; correction above by Yin Zhang */ - LQ2 = LUSOL->n; - if(LUSOL->m>1) - LQ2 = LUSOL->iqloc[2]-1; - for(LQ = LQ1; LQ <= LQ2; LQ++) { - J = LUSOL->iq[LQ]; - LC1 = LUSOL->locc[J]; - if(fabs(LUSOL->a[LC1])==1) { - LUSOL->w[J] = 1; - } - } -} - -/* ================================================================== - lu1gau does most of the work for each step of Gaussian elimination. - A multiple of the pivot column is added to each other column j - in the pivot row. The column list is fully updated. - The row list is updated if there is room, but some fill-ins may - remain, as indicated by ifill and jfill. - ------------------------------------------------------------------ - Input: - ilast is the row at the end of the row list. - jlast is the column at the end of the column list. - lfirst is the first column to be processed. - lu + 1 is the corresponding element of U in au(*). - nfill keeps track of pending fill-in. - a(*) contains the nonzeros for each column j. - indc(*) contains the row indices for each column j. - al(*) contains the new column of L. A multiple of it is - used to modify each column. - mark(*) has been set to -1, -2, -3, ... in the rows - corresponding to nonzero 1, 2, 3, ... of the col of L. - au(*) contains the new row of U. Each nonzero gives the - required multiple of the column of L. - - Workspace: - markl(*) marks the nonzeros of L actually used. - (A different mark, namely j, is used for each column.) - - Output: - ilast New last row in the row list. - jlast New last column in the column list. - lfirst = 0 if all columns were completed, - > 0 otherwise. - lu returns the position of the last nonzero of U - actually used, in case we come back in again. - nfill keeps track of the total extra space needed in the - row file. - ifill(ll) counts pending fill-in for rows involved in the new - column of L. - jfill(lu) marks the first pending fill-in stored in columns - involved in the new row of U. - ------------------------------------------------------------------ - 16 Apr 1989: First version of lu1gau. - 23 Apr 1989: lfirst, lu, nfill are now input and output - to allow re-entry if elimination is interrupted. - 23 Mar 2001: Introduced ilast, jlast. - 27 Mar 2001: Allow fill-in "in situ" if there is already room - up to but NOT INCLUDING the end of the - row or column file. - Seems safe way to avoid overwriting empty rows/cols - at the end. (May not be needed though, now that we - have ilast and jlast.) - ================================================================== */ -void LU1GAU(LUSOLrec *LUSOL, int MELIM, int NSPARE, - LPSREAL SMALL, int LPIVC1, int LPIVC2, int *LFIRST, int LPIVR2, - int LFREE, int MINFRE, int ILAST, int *JLAST, int *LROW, int *LCOL, - int *LU, int *NFILL, - int MARK[], LPSREAL AL[], int MARKL[], LPSREAL AU[], int IFILL[], int JFILL[]) -{ - MYBOOL ATEND; - int LR, J, LENJ, NFREE, LC1, LC2, NDONE, NDROP, L, I, LL, K, - LR1, LAST, LREP, L1, L2, LC, LENI; - register LPSREAL UJ; - LPSREAL AIJ; - - for(LR = *LFIRST; LR <= LPIVR2; LR++) { - J = LUSOL->indr[LR]; - LENJ = LUSOL->lenc[J]; - NFREE = LFREE - *LCOL; - if(NFREElocc[J]; - LC2 = (LC1+LENJ)-1; - ATEND = (MYBOOL) (J==*JLAST); - NDONE = 0; - if(LENJ==0) - goto x500; - NDROP = 0; - for(L = LC1; L <= LC2; L++) { - I = LUSOL->indc[L]; - LL = -MARK[I]; - if(LL>0) { - NDONE++; - MARKL[LL] = J; - LUSOL->a[L] += AL[LL]*UJ; - if(fabs(LUSOL->a[L])<=SMALL) { - NDROP++; - } - } - } -/* --------------------------------------------------------------- - Remove any negligible modified nonzeros from both - the column file and the row file. - --------------------------------------------------------------- */ - if(NDROP==0) - goto x500; - K = LC1; - for(L = LC1; L <= LC2; L++) { - I = LUSOL->indc[L]; - if(fabs(LUSOL->a[L])<=SMALL) - goto x460; - LUSOL->a[K] = LUSOL->a[L]; - LUSOL->indc[K] = I; - K++; - continue; -/* Delete the nonzero from the row file. */ -x460: - LENJ--; - LUSOL->lenr[I]--; - LR1 = LUSOL->locr[I]; - LAST = LR1+LUSOL->lenr[I]; - for(LREP = LR1; LREP <= LAST; LREP++) { - if(LUSOL->indr[LREP]==J) - break; - } - LUSOL->indr[LREP] = LUSOL->indr[LAST]; - LUSOL->indr[LAST] = 0; - if(I==ILAST) - (*LROW)--; - } -/* Free the deleted elements from the column file. */ -#ifdef LUSOLFastClear - MEMCLEAR(LUSOL->indc+K, LC2-K+1); -#else - for(L = K; L <= LC2; L++) - LUSOL->indc[L] = ZERO; -#endif - if(ATEND) - *LCOL = K-1; -/* --------------------------------------------------------------- - Deal with the fill-in in column j. - --------------------------------------------------------------- */ -x500: - if(NDONE==MELIM) - goto x590; -/* See if column j already has room for the fill-in. */ - if(ATEND) - goto x540; - LAST = (LC1+LENJ)-1; - L1 = LAST+1; - L2 = (LAST+MELIM)-NDONE; -/* 27 Mar 2001: Be sure it's not at or past end of the col file. */ - if(L2>=*LCOL) - goto x520; - for(L = L1; L <= L2; L++) { - if(LUSOL->indc[L]!=0) - goto x520; - } - goto x540; -/* We must move column j to the end of the column file. - First, leave some spare room at the end of the - current last column. */ -x520: -#if 1 - L1 = (*LCOL)+1; - L2 = (*LCOL)+NSPARE; - *LCOL = L2; - for(L = L1; L <= L2; L++) { -#else - for(L = (*LCOL)+1; L <= (*LCOL)+NSPARE; L++) { - *LCOL = L; /* ****** ERROR ???? */ -#endif -/* Spare space is free. */ - LUSOL->indc[L] = 0; - } - ATEND = TRUE; - *JLAST = J; - L1 = LC1; - L2 = *LCOL; - LC1 = L2+1; - LUSOL->locc[J] = LC1; - for(L = L1; L <= LAST; L++) { - L2++; - LUSOL->a[L2] = LUSOL->a[L]; - LUSOL->indc[L2] = LUSOL->indc[L]; -/* Free space. */ - LUSOL->indc[L] = 0; - } - *LCOL = L2; -/* --------------------------------------------------------------- - Inner loop for the fill-in in column j. - This is usually not very expensive. - --------------------------------------------------------------- */ -x540: - LAST = (LC1+LENJ)-1; - LL = 0; - for(LC = LPIVC1; LC <= LPIVC2; LC++) { - LL++; - if(MARKL[LL]==J) - continue; - AIJ = AL[LL]*UJ; - if(fabs(AIJ)<=SMALL) - continue; - LENJ++; - LAST++; - LUSOL->a[LAST] = AIJ; - I = LUSOL->indc[LC]; - LUSOL->indc[LAST] = I; - LENI = LUSOL->lenr[I]; -/* Add 1 fill-in to row i if there is already room. - 27 Mar 2001: Be sure it's not at or past the } - of the row file. */ - L = LUSOL->locr[I]+LENI; - if(L>=*LROW) - goto x550; - if(LUSOL->indr[L]>0) - goto x550; - LUSOL->indr[L] = J; - LUSOL->lenr[I] = LENI+1; - continue; -/* Row i does not have room for the fill-in. - Increment ifill(ll) to count how often this has - happened to row i. Also, add m to the row index - indc(last) in column j to mark it as a fill-in that is - still pending. - If this is the first pending fill-in for row i, - nfill includes the current length of row i - (since the whole row has to be moved later). - If this is the first pending fill-in for column j, - jfill(lu) records the current length of column j - (to shorten the search for pending fill-ins later). */ -x550: - if(IFILL[LL]==0) - (*NFILL) += LENI+NSPARE; - if(JFILL[*LU]==0) - JFILL[*LU] = LENJ; - (*NFILL)++; - IFILL[LL]++; - LUSOL->indc[LAST] = LUSOL->m+I; - } - if(ATEND) - *LCOL = LAST; -/* End loop for column j. Store its final length. */ -x590: - LUSOL->lenc[J] = LENJ; - } -/* Successful completion. */ - *LFIRST = 0; - return; -/* Interruption. We have to come back in after the - column file is compressed. Give lfirst a new value. - lu and nfill will retain their current values. */ -x900: - *LFIRST = LR; -} - -/* ================================================================== - lu1mar uses a Markowitz criterion to select a pivot element - for the next stage of a sparse LU factorization, - subject to a Threshold Partial Pivoting stability criterion (TPP) - that bounds the elements of L. - ------------------------------------------------------------------ - gamma is "gamma" in the tie-breaking rule TB4 in the LUSOL paper. - ------------------------------------------------------------------ - Search cols of length nz = 1, then rows of length nz = 1, - then cols of length nz = 2, then rows of length nz = 2, etc. - ------------------------------------------------------------------ - 00 Jan 1986 Version documented in LUSOL paper: - Gill, Murray, Saunders and Wright (1987), - Maintaining LU factors of a general sparse matrix, - Linear algebra and its applications 88/89, 239-270. - 02 Feb 1989 Following Suhl and Aittoniemi (1987), the largest - element in each column is now kept at the start of - the column, i.e. in position locc(j) of a and indc. - This should speed up the Markowitz searches. - 26 Apr 1989 Both columns and rows searched during spars1 phase. - Only columns searched during spars2 phase. - maxtie replaced by maxcol and maxrow. - 05 Nov 1993 Initializing "mbest = m * n" wasn't big enough when - m = 10, n = 3, and last column had 7 nonzeros. - 09 Feb 1994 Realised that "mbest = maxmn * maxmn" might overflow. - Changed to "mbest = maxmn * 1000". - 27 Apr 2000 On large example from Todd Munson, - that allowed "if (mbest .le. nz1**2) go to 900" - to exit before any pivot had been found. - Introduced kbest = mbest / nz1. - Most pivots can be rejected with no integer multiply. - TRUE merit is evaluated only if it's as good as the - best so far (or better). There should be no danger - of integer overflow unless A is incredibly - large and dense. - 10 Sep 2000 TCP, aijtol added for Threshold Complete Pivoting. - ================================================================== */ -void LU1MAR(LUSOLrec *LUSOL, int MAXMN, MYBOOL TCP, LPSREAL AIJTOL, LPSREAL LTOL, - int MAXCOL, int MAXROW, int *IBEST, int *JBEST, int *MBEST) -{ - int KBEST, NCOL, NROW, NZ1, NZ, LQ1, LQ2, LQ, J, LC1, LC2, LC, I, LEN1, MERIT, LP1, - LP2, LP, LR1, LR2, LR; - LPSREAL ABEST, LBEST, AMAX, AIJ, CMAX; - - ABEST = ZERO; - LBEST = ZERO; - *IBEST = 0; - *MBEST = -1; - KBEST = MAXMN+1; - NCOL = 0; - NROW = 0; - NZ1 = 0; - for(NZ = 1; NZ <= MAXMN; NZ++) { -/* nz1 = nz - 1 - if (mbest .le. nz1**2) go to 900 */ - if(KBEST<=NZ1) - goto x900; - if(*IBEST>0) { - if(NCOL>=MAXCOL) - goto x200; - } - if(NZ>LUSOL->m) - goto x200; -/* --------------------------------------------------------------- - Search the set of columns of length nz. - --------------------------------------------------------------- */ - LQ1 = LUSOL->iqloc[NZ]; - LQ2 = LUSOL->n; - if(NZm) - LQ2 = LUSOL->iqloc[NZ+1]-1; - for(LQ = LQ1; LQ <= LQ2; LQ++) { - NCOL = NCOL+1; - J = LUSOL->iq[LQ]; - LC1 = LUSOL->locc[J]; - LC2 = LC1+NZ1; - AMAX = fabs(LUSOL->a[LC1]); -/* Test all aijs in this column. - amax is the largest element (the first in the column). - cmax is the largest multiplier if aij becomes pivot. */ - if(TCP) { -/* Nothing in whole column */ - if(AMAXindc[LC]; - LEN1 = LUSOL->lenr[I]-1; -/* merit = nz1 * len1 - if (merit > mbest) continue; */ - if(LEN1>KBEST) - continue; -/* aij has a promising merit. - Apply the stability test. - We require aij to be sufficiently large compared to - all other nonzeros in column j. This is equivalent - to requiring cmax to be bounded by Ltol. */ - if(LC==LC1) { -/* This is the maximum element, amax. - Find the biggest element in the rest of the column - and hence get cmax. We know cmax .le. 1, but - we still want it exactly in order to break ties. - 27 Apr 2002: Settle for cmax = 1. */ - AIJ = AMAX; - CMAX = ONE; -/* cmax = zero - for (l = lc1 + 1; l <= lc2; l++) - cmax = max( cmax, abs( a(l) ) ); - cmax = cmax / amax; */ - } - else { -/* aij is not the biggest element, so cmax .ge. 1. - Bail out if cmax will be too big. */ - AIJ = fabs(LUSOL->a[LC]); -/* Absolute test for Complete Pivoting */ - if(TCP) { - if(AIJparmlu[LUSOL_RP_GAMMA] && - CMAX<=LUSOL->parmlu[LUSOL_RP_GAMMA]) { - if(ABEST>=AIJ) - continue; - } - else { - if(LBEST<=CMAX) - continue; - } - } -/* aij is the best pivot so far. */ - *IBEST = I; - *JBEST = J; - KBEST = LEN1; - *MBEST = MERIT; - ABEST = AIJ; - LBEST = CMAX; - if(NZ==1) - goto x900; - } -/* Finished with that column. */ - if(*IBEST>0) { - if(NCOL>=MAXCOL) - goto x200; - } - } -/* --------------------------------------------------------------- - Search the set of rows of length nz. - --------------------------------------------------------------- */ -x200: -/* if (mbest .le. nz*nz1) go to 900 */ - if(KBEST<=NZ) - goto x900; - if(*IBEST>0) { - if(NROW>=MAXROW) - goto x290; - } - if(NZ>LUSOL->n) - goto x290; - LP1 = LUSOL->iploc[NZ]; - LP2 = LUSOL->m; - if(NZn) - LP2 = LUSOL->iploc[NZ+1]-1; - for(LP = LP1; LP <= LP2; LP++) { - NROW++; - I = LUSOL->ip[LP]; - LR1 = LUSOL->locr[I]; - LR2 = LR1+NZ1; - for(LR = LR1; LR <= LR2; LR++) { - J = LUSOL->indr[LR]; - LEN1 = LUSOL->lenc[J]-1; -/* merit = nz1 * len1 - if (merit .gt. mbest) continue */ - if(LEN1>KBEST) - continue; -/* aij has a promising merit. - Find where aij is in column j. */ - LC1 = LUSOL->locc[J]; - LC2 = LC1+LEN1; - AMAX = fabs(LUSOL->a[LC1]); - for(LC = LC1; LC <= LC2; LC++) { - if(LUSOL->indc[LC]==I) - break; - } -/* Apply the same stability test as above. */ - AIJ = fabs(LUSOL->a[LC]); -/* Absolute test for Complete Pivoting */ - if(TCP) { - if(AIJparmlu[LUSOL_RP_GAMMA] && - CMAX<=LUSOL->parmlu[LUSOL_RP_GAMMA]) { - if(ABEST>=AIJ) - continue; - } - else { - if(LBEST<=CMAX) - continue; - } - } -/* aij is the best pivot so far. */ - *IBEST = I; - *JBEST = J; - *MBEST = MERIT; - KBEST = LEN1; - ABEST = AIJ; - LBEST = CMAX; - if(NZ==1) - goto x900; - } -/* Finished with that row. */ - if(*IBEST>0) { - if(NROW>=MAXROW) - goto x290; - } - } -/* See if it's time to quit. */ -x290: - if(*IBEST>0) { - if(NROW>=MAXROW && NCOL>=MAXCOL) - goto x900; - } -/* Press on with next nz. */ - NZ1 = NZ; - if(*IBEST>0) - KBEST = *MBEST/NZ1; - } -x900: -; -} - -/* ================================================================== - lu1mCP uses a Markowitz criterion to select a pivot element - for the next stage of a sparse LU factorization, - subject to a Threshold Complete Pivoting stability criterion (TCP) - that bounds the elements of L and U. - ------------------------------------------------------------------ - gamma is "gamma" in the tie-breaking rule TB4 in the LUSOL paper. - ------------------------------------------------------------------ - 09 May 2002: First version of lu1mCP. - It searches columns only, using the heap that - holds the largest element in each column. - 09 May 2002: Current version of lu1mCP. - ================================================================== */ -void LU1MCP(LUSOLrec *LUSOL, LPSREAL AIJTOL, int *IBEST, int *JBEST, int *MBEST, - int HLEN, LPSREAL HA[], int HJ[]) -{ - int J, KHEAP, LC, LC1, LC2, LENJ, MAXCOL, NCOL, NZ1, I, LEN1, MERIT; - LPSREAL ABEST, AIJ, AMAX, CMAX, LBEST; - -/* ------------------------------------------------------------------ - Search up to maxcol columns stored at the top of the heap. - The very top column helps initialize mbest. - ------------------------------------------------------------------ */ - ABEST = ZERO; - LBEST = ZERO; - *IBEST = 0; -/* Column at the top of the heap */ - *JBEST = HJ[1]; - LENJ = LUSOL->lenc[*JBEST]; -/* Bigger than any possible merit */ - *MBEST = LENJ*HLEN; -/* ??? Big question */ - MAXCOL = 40; -/* No. of columns searched */ - NCOL = 0; - for(KHEAP = 1; KHEAP <= HLEN; KHEAP++) { - AMAX = HA[KHEAP]; - if(AMAXlenc[J]; - NZ1 = LENJ-1; - LC1 = LUSOL->locc[J]; - LC2 = LC1+NZ1; -/* -- amax = abs( a(lc1) ) - Test all aijs in this column. - amax is the largest element (the first in the column). - cmax is the largest multiplier if aij becomes pivot. */ - for(LC = LC1; LC <= LC2; LC++) { - I = LUSOL->indc[LC]; - LEN1 = LUSOL->lenr[I]-1; - MERIT = NZ1*LEN1; - if(MERIT>*MBEST) - continue; -/* aij has a promising merit. */ - if(LC==LC1) { -/* This is the maximum element, amax. - Find the biggest element in the rest of the column - and hence get cmax. We know cmax .le. 1, but - we still want it exactly in order to break ties. - 27 Apr 2002: Settle for cmax = 1. */ - AIJ = AMAX; - CMAX = ONE; -/* cmax = ZERO; - for(l = lc1 + 1; l <= lc2; l++) - cmax = max( cmax, abs( a(l) ) ) - cmax = cmax / amax; */ - } - else { -/* aij is not the biggest element, so cmax .ge. 1. - Bail out if cmax will be too big. */ - AIJ = fabs(LUSOL->a[LC]); - if(AIJparmlu[LUSOL_RP_GAMMA] && - CMAX<=LUSOL->parmlu[LUSOL_RP_GAMMA]) { - if(ABEST>=AIJ) - continue; - } - else { - if(LBEST<=CMAX) - continue; - } - } -/* aij is the best pivot so far. */ - *IBEST = I; - *JBEST = J; - *MBEST = MERIT; - ABEST = AIJ; - LBEST = CMAX; -/* Col or row of length 1 */ - if(MERIT==0) - goto x900; - } - if(NCOL>=MAXCOL) - goto x900; - } -x900: -; -} - -/* ================================================================== - lu1mRP uses a Markowitz criterion to select a pivot element - for the next stage of a sparse LU factorization, - subject to a Threshold Rook Pivoting stability criterion (TRP) - that bounds the elements of L and U. - ------------------------------------------------------------------ - 11 Jun 2002: First version of lu1mRP derived from lu1mar. - 11 Jun 2002: Current version of lu1mRP. - ================================================================== */ -void LU1MRP(LUSOLrec *LUSOL, int MAXMN, LPSREAL LTOL, int MAXCOL, int MAXROW, - int *IBEST, int *JBEST, int *MBEST, LPSREAL AMAXR[]) -{ - int I, J, KBEST, LC, LC1, LC2, LEN1, LP, LP1, LP2, LQ, LQ1, - LQ2, LR, LR1, LR2, MERIT, NCOL, NROW, NZ, NZ1; - LPSREAL ABEST, AIJ, AMAX, ATOLI, ATOLJ; - -/* ------------------------------------------------------------------ - Search cols of length nz = 1, then rows of length nz = 1, - then cols of length nz = 2, then rows of length nz = 2, etc. - ------------------------------------------------------------------ */ - ABEST = ZERO; - *IBEST = 0; - KBEST = MAXMN+1; - *MBEST = -1; - NCOL = 0; - NROW = 0; - NZ1 = 0; - for(NZ = 1; NZ <= MAXMN; NZ++) { -/* nz1 = nz - 1 - if (mbest .le. nz1**2) go to 900 */ - if(KBEST<=NZ1) - goto x900; - if(*IBEST>0) { - if(NCOL>=MAXCOL) - goto x200; - } - if(NZ>LUSOL->m) - goto x200; -/* --------------------------------------------------------------- - Search the set of columns of length nz. - --------------------------------------------------------------- */ - LQ1 = LUSOL->iqloc[NZ]; - LQ2 = LUSOL->n; - if(NZm) - LQ2 = LUSOL->iqloc[NZ+1]-1; - for(LQ = LQ1; LQ <= LQ2; LQ++) { - NCOL = NCOL+1; - J = LUSOL->iq[LQ]; - LC1 = LUSOL->locc[J]; - LC2 = LC1+NZ1; - AMAX = fabs(LUSOL->a[LC1]); -/* Min size of pivots in col j */ - ATOLJ = AMAX/LTOL; -/* Test all aijs in this column. */ - for(LC = LC1; LC <= LC2; LC++) { - I = LUSOL->indc[LC]; - LEN1 = LUSOL->lenr[I]-1; -/* merit = nz1 * len1 - if (merit .gt. mbest) continue; */ - if(LEN1>KBEST) - continue; -/* aij has a promising merit. - Apply the Threshold Rook Pivoting stability test. - First we require aij to be sufficiently large - compared to other nonzeros in column j. - Then we require aij to be sufficiently large - compared to other nonzeros in row i. */ - AIJ = fabs(LUSOL->a[LC]); - if(AIJ=AIJ) - continue; - } -/* aij is the best pivot so far. */ - *IBEST = I; - *JBEST = J; - KBEST = LEN1; - *MBEST = MERIT; - ABEST = AIJ; - if(NZ==1) - goto x900; - } -/* Finished with that column. */ - if(*IBEST>0) { - if(NCOL>=MAXCOL) - goto x200; - } - } -/* --------------------------------------------------------------- - Search the set of rows of length nz. - --------------------------------------------------------------- */ -x200: -/* if (mbest .le. nz*nz1) go to 900 */ - if(KBEST<=NZ) - goto x900; - if(*IBEST>0) { - if(NROW>=MAXROW) - goto x290; - } - if(NZ>LUSOL->n) - goto x290; - LP1 = LUSOL->iploc[NZ]; - LP2 = LUSOL->m; - if(NZn) - LP2 = LUSOL->iploc[NZ+1]-1; - for(LP = LP1; LP <= LP2; LP++) { - NROW = NROW+1; - I = LUSOL->ip[LP]; - LR1 = LUSOL->locr[I]; - LR2 = LR1+NZ1; -/* Min size of pivots in row i */ - ATOLI = AMAXR[I]/LTOL; - for(LR = LR1; LR <= LR2; LR++) { - J = LUSOL->indr[LR]; - LEN1 = LUSOL->lenc[J]-1; -/* merit = nz1 * len1 - if (merit .gt. mbest) continue; */ - if(LEN1>KBEST) - continue; -/* aij has a promising merit. - Find where aij is in column j. */ - LC1 = LUSOL->locc[J]; - LC2 = LC1+LEN1; - AMAX = fabs(LUSOL->a[LC1]); - for(LC = LC1; LC <= LC2; LC++) { - if(LUSOL->indc[LC]==I) - break; - } -/* Apply the Threshold Rook Pivoting stability test. - First we require aij to be sufficiently large - compared to other nonzeros in row i. - Then we require aij to be sufficiently large - compared to other nonzeros in column j. */ - AIJ = fabs(LUSOL->a[LC]); - if(AIJ=AIJ) - continue; - } -/* aij is the best pivot so far. */ - *IBEST = I; - *JBEST = J; - KBEST = LEN1; - *MBEST = MERIT; - ABEST = AIJ; - if(NZ==1) - goto x900; - } -/* Finished with that row. */ - if(*IBEST>0) { - if(NROW>=MAXROW) - goto x290; - } - } -/* See if it's time to quit. */ -x290: - if(*IBEST>0) { - if(NROW>=MAXROW && NCOL>=MAXCOL) - goto x900; - } -/* Press on with next nz. */ - NZ1 = NZ; - if(*IBEST>0) - KBEST = *MBEST/NZ1; - } -x900: -; -} - -/* ================================================================== - lu1mSP is intended for symmetric matrices that are either - definite or quasi-definite. - lu1mSP uses a Markowitz criterion to select a pivot element for - the next stage of a sparse LU factorization of a symmetric matrix, - subject to a Threshold Symmetric Pivoting stability criterion - (TSP) restricted to diagonal elements to preserve symmetry. - This bounds the elements of L and U and should have rank-revealing - properties analogous to Threshold Rook Pivoting for unsymmetric - matrices. - ------------------------------------------------------------------ - 14 Dec 2002: First version of lu1mSP derived from lu1mRP. - There is no safeguard to ensure that A is symmetric. - 14 Dec 2002: Current version of lu1mSP. - ================================================================== */ -void LU1MSP(LUSOLrec *LUSOL, int MAXMN, LPSREAL LTOL, int MAXCOL, - int *IBEST, int *JBEST, int *MBEST) -{ - int I, J, KBEST, LC, LC1, LC2, LQ, LQ1, LQ2, MERIT, NCOL, NZ, NZ1; - LPSREAL ABEST, AIJ, AMAX, ATOLJ; - -/* ------------------------------------------------------------------ - Search cols of length nz = 1, then cols of length nz = 2, etc. - ------------------------------------------------------------------ */ - ABEST = ZERO; - *IBEST = 0; - *MBEST = -1; - KBEST = MAXMN+1; - NCOL = 0; - NZ1 = 0; - for(NZ = 1; NZ <= MAXMN; NZ++) { -/* nz1 = nz - 1 - if (mbest .le. nz1**2) go to 900 */ - if(KBEST<=NZ1) - goto x900; - if(*IBEST>0) { - if(NCOL>=MAXCOL) - goto x200; - } - if(NZ>LUSOL->m) - goto x200; -/* --------------------------------------------------------------- - Search the set of columns of length nz. - --------------------------------------------------------------- */ - LQ1 = LUSOL->iqloc[NZ]; - LQ2 = LUSOL->n; - if(NZm) - LQ2 = LUSOL->iqloc[NZ+1]-1; - for(LQ = LQ1; LQ <= LQ2; LQ++) { - NCOL++; - J = LUSOL->iq[LQ]; - LC1 = LUSOL->locc[J]; - LC2 = LC1+NZ1; - AMAX = fabs(LUSOL->a[LC1]); -/* Min size of pivots in col j */ - ATOLJ = AMAX/LTOL; -/* Test all aijs in this column. - Ignore everything except the diagonal. */ - for(LC = LC1; LC <= LC2; LC++) { - I = LUSOL->indc[LC]; -/* Skip off-diagonals. */ - if(I!=J) - continue; -/* merit = nz1 * nz1 - if (merit .gt. mbest) continue; */ - if(NZ1>KBEST) - continue; -/* aij has a promising merit. - Apply the Threshold Partial Pivoting stability test - (which is equivalent to Threshold Rook Pivoting for - symmetric matrices). - We require aij to be sufficiently large - compared to other nonzeros in column j. */ - AIJ = fabs(LUSOL->a[LC]); - if(AIJ=AIJ) - continue; - } -/* aij is the best pivot so far. */ - *IBEST = I; - *JBEST = J; - KBEST = NZ1; - *MBEST = MERIT; - ABEST = AIJ; - if(NZ==1) - goto x900; - } -/* Finished with that column. */ - if(*IBEST>0) { - if(NCOL>=MAXCOL) - goto x200; - } - } -/* See if it's time to quit. */ -x200: - if(*IBEST>0) { - if(NCOL>=MAXCOL) - goto x900; - } -/* Press on with next nz. */ - NZ1 = NZ; - if(*IBEST>0) - KBEST = *MBEST/NZ1; - } -x900: -; -} - -/* ================================================================== - lu1mxc moves the largest element in each of columns iq(k1:k2) - to the top of its column. - If k1 > k2, nothing happens. - ------------------------------------------------------------------ - 06 May 2002: (and earlier) - All columns k1:k2 must have one or more elements. - 07 May 2002: Allow for empty columns. The heap routines need to - find 0.0 as the "largest element". - 29 Nov 2005: Bug fix - avoiding overwriting the next column when - the current column is empty (i.e. LENJ==0) - Yin Zhang - ================================================================== */ -void LU1MXC(LUSOLrec *LUSOL, int K1, int K2, int IX[]) -{ - int I, J, K, L, LC, LENJ; - LPSREAL AMAX; - - for(K = K1; K <= K2; K++) { - J = IX[K]; - LC = LUSOL->locc[J]; - LENJ = LUSOL->lenc[J]; - if(LENJ==0) -/* LUSOL->a[LC] = ZERO; Removal suggested by Yin Zhang to avoid overwriting next column when current is empty */ - ; - else { - L = lps_idamax(LUSOL->lenc[J], LUSOL->a + LC - LUSOL_ARRAYOFFSET,1) + LC - 1; - if(L>LC) { - AMAX = LUSOL->a[L]; - LUSOL->a[L] = LUSOL->a[LC]; - LUSOL->a[LC] = AMAX; - I = LUSOL->indc[L]; - LUSOL->indc[L] = LUSOL->indc[LC]; - LUSOL->indc[LC] = I; - } - } - } -} - -/* ================================================================== - lu1mxr finds the largest element in each of row ip(k1:k2) - and stores it in Amaxr(*). The nonzeros are stored column-wise - in (a,indc,lenc,locc) and their structure is row-wise - in ( indr,lenr,locr). - If k1 > k2, nothing happens. - ------------------------------------------------------------------ - 11 Jun 2002: First version of lu1mxr. - Allow for empty columns. - ================================================================== */ -void LU1MXR(LUSOLrec *LUSOL, int K1, int K2, int IX[], LPSREAL AMAXR[]) -{ -#define FastMXR -#ifdef FastMXR - static int I, *J, *IC, K, LC, LC1, LC2, LR, LR1, LR2; - static LPSREAL AMAX; -#else - int I, J, K, LC, LC1, LC2, LR, LR1, LR2; - LPSREAL AMAX; -#endif - - for(K = K1; K <= K2; K++) { - AMAX = ZERO; - I = IX[K]; -/* Find largest element in row i. */ - LR1 = LUSOL->locr[I]; - LR2 = (LR1+LUSOL->lenr[I])-1; -#ifdef FastMXR - for(LR = LR1, J = LUSOL->indr + LR1; - LR <= LR2; LR++, J++) { -/* Find where aij is in column j. */ - LC1 = LUSOL->locc[*J]; - LC2 = LC1+LUSOL->lenc[*J]; - for(LC = LC1, IC = LUSOL->indc + LC1; - LC < LC2; LC++, IC++) { - if(*IC==I) - break; - } - SETMAX(AMAX,fabs(LUSOL->a[LC])); - } -#else - for(LR = LR1; LR <= LR2; LR++) { - J = LUSOL->indr[LR]; -/* Find where aij is in column j. */ - LC1 = LUSOL->locc[J]; - LC2 = (LC1+LUSOL->lenc[J])-1; - for(LC = LC1; LC <= LC2; LC++) { - if(LUSOL->indc[LC]==I) - break; - } - SETMAX(AMAX,fabs(LUSOL->a[LC])); - } -#endif - AMAXR[I] = AMAX; - } -} - - -/* ================================================================== - lu1ful computes a dense (full) LU factorization of the - mleft by nleft matrix that remains to be factored at the - beginning of the nrowu-th pass through the main loop of lu1fad. - ------------------------------------------------------------------ - 02 May 1989: First version. - 05 Feb 1994: Column interchanges added to lu1DPP. - 08 Feb 1994: ipinv reconstructed, since lu1pq3 may alter ip. - ================================================================== */ -void LU1FUL(LUSOLrec *LUSOL, int LEND, int LU1, MYBOOL TPP, - int MLEFT, int NLEFT, int NRANK, int NROWU, - int *LENL, int *LENU, int *NSING, - MYBOOL KEEPLU, LPSREAL SMALL, LPSREAL D[], int IPVT[]) -{ - int L, I, J, IPBASE, LDBASE, LQ, LC1, LC2, LC, LD, LKK, LKN, LU, K, L1, - L2, IBEST, JBEST, LA, LL, NROWD, NCOLD; - LPSREAL AI, AJ; - -/* ------------------------------------------------------------------ - If lu1pq3 moved any empty rows, reset ipinv = inverse of ip. - ------------------------------------------------------------------ */ - if(NRANKm) { - for(L = 1; L <= LUSOL->m; L++) { - I = LUSOL->ip[L]; - LUSOL->ipinv[I] = L; - } - } -/* ------------------------------------------------------------------ - Copy the remaining matrix into the dense matrix D. - ------------------------------------------------------------------ */ -#ifdef LUSOLFastClear - MEMCLEAR((D+1), LEND); -#else -/* dload(LEND, ZERO, D, 1); */ - for(J = 1; J <= LEND; J++) - D[J] = ZERO; -#endif - - IPBASE = NROWU-1; - LDBASE = 1-NROWU; - for(LQ = NROWU; LQ <= LUSOL->n; LQ++) { - J = LUSOL->iq[LQ]; - LC1 = LUSOL->locc[J]; - LC2 = (LC1+LUSOL->lenc[J])-1; - for(LC = LC1; LC <= LC2; LC++) { - I = LUSOL->indc[LC]; - LD = LDBASE+LUSOL->ipinv[I]; - D[LD] = LUSOL->a[LC]; - } - LDBASE += MLEFT; - } -/* ------------------------------------------------------------------ - Call our favorite dense LU factorizer. - ------------------------------------------------------------------ */ - if(TPP) - LU1DPP(LUSOL, D,MLEFT,MLEFT,NLEFT,SMALL,NSING,IPVT,LUSOL->iq+NROWU-LUSOL_ARRAYOFFSET); - else - LU1DCP(LUSOL, D,MLEFT,MLEFT,NLEFT,SMALL,NSING,IPVT,LUSOL->iq+NROWU-LUSOL_ARRAYOFFSET); - -/* ------------------------------------------------------------------ - Move D to the beginning of A, - and pack L and U at the top of a, indc, indr. - In the process, apply the row permutation to ip. - lkk points to the diagonal of U. - ------------------------------------------------------------------ */ -#ifdef LUSOLFastCopy - MEMCOPY(LUSOL->a+1,D+1,LEND); -#else - lps_dcopy(LEND,D,1,LUSOL->a,1); -#endif -#ifdef ClassicdiagU - LUSOL->diagU = LUSOL->a + (LUSOL->lena-LUSOL->n); -#endif - LKK = 1; - LKN = (LEND-MLEFT)+1; - LU = LU1; - for(K = 1; K <= MIN(MLEFT,NLEFT); K++) { - L1 = IPBASE+K; - L2 = IPBASE+IPVT[K]; - if(L1!=L2) { - I = LUSOL->ip[L1]; - LUSOL->ip[L1] = LUSOL->ip[L2]; - LUSOL->ip[L2] = I; - } - IBEST = LUSOL->ip[L1]; - JBEST = LUSOL->iq[L1]; - if(KEEPLU) { -/* =========================================================== - Pack the next column of L. - =========================================================== */ - LA = LKK; - LL = LU; - NROWD = 1; - for(I = K+1; I <= MLEFT; I++) { - LA++; - AI = LUSOL->a[LA]; - if(fabs(AI)>SMALL) { - NROWD = NROWD+1; - LL--; - LUSOL->a[LL] = AI; - LUSOL->indc[LL] = LUSOL->ip[IPBASE+I]; - LUSOL->indr[LL] = IBEST; - } - } -/* =========================================================== - Pack the next row of U. - We go backwards through the row of D - so the diagonal ends up at the front of the row of U. - Beware -- the diagonal may be zero. - =========================================================== */ - LA = LKN+MLEFT; - LU = LL; - NCOLD = 0; - for(J = NLEFT; J >= K; J--) { - LA = LA-MLEFT; - AJ = LUSOL->a[LA]; - if(fabs(AJ)>SMALL || J==K) { - NCOLD++; - LU--; - LUSOL->a[LU] = AJ; - LUSOL->indr[LU] = LUSOL->iq[IPBASE+J]; - } - } - LUSOL->lenr[IBEST] = -NCOLD; - LUSOL->lenc[JBEST] = -NROWD; - *LENL = ((*LENL)+NROWD)-1; - *LENU = (*LENU)+NCOLD; - LKN++; - } - else { -/* =========================================================== - Store just the diagonal of U, in natural order. - =========================================================== */ - LUSOL->diagU[JBEST] = LUSOL->a[LKK]; - } - LKK += MLEFT+1; - } -} - - -/* ================================================================== - lu1or1 organizes the elements of an m by n matrix A as - follows. On entry, the parallel arrays a, indc, indr, - contain nelem entries of the form aij, i, j, - in any order. nelem must be positive. - Entries not larger than the input parameter small are treated as - zero and removed from a, indc, indr. The remaining entries are - defined to be nonzero. numnz returns the number of such nonzeros - and Amax returns the magnitude of the largest nonzero. - The arrays lenc, lenr return the number of nonzeros in each - column and row of A. - inform = 0 on exit, except inform = 1 if any of the indices in - indc, indr imply that the element aij lies outside the m by n - dimensions of A. - ------------------------------------------------------------------ - xx Feb 1985: Original version. - 17 Oct 2000: a, indc, indr now have size lena to allow nelem = 0. - ================================================================== */ -void LU1OR1(LUSOLrec *LUSOL, LPSREAL SMALL, - LPSREAL *AMAX, int *NUMNZ, int *LERR, int *INFORM) -{ - int I, J, L, LDUMMY; - -#ifdef LUSOLFastClear - MEMCLEAR((LUSOL->lenr+1), LUSOL->m); - MEMCLEAR((LUSOL->lenc+1), LUSOL->n); -#else - for(I = 1; I <= LUSOL->m; I++) - LUSOL->lenr[I] = ZERO; - for(I = 1; I <= LUSOL->n; I++) - LUSOL->lenc[I] = ZERO; -#endif - - *AMAX = 0; - *NUMNZ = LUSOL->nelem; - L = LUSOL->nelem+1; - for(LDUMMY = 1; LDUMMY <= LUSOL->nelem; LDUMMY++) { - L--; - if(fabs(LUSOL->a[L])>SMALL) { - I = LUSOL->indc[L]; - J = LUSOL->indr[L]; - SETMAX(*AMAX,fabs(LUSOL->a[L])); - if(I<1 || I>LUSOL->m) - goto x910; - if(J<1 || J>LUSOL->n) - goto x910; - LUSOL->lenr[I]++; - LUSOL->lenc[J]++; - } - else { -/* Replace a negligible element by last element. Since - we are going backwards, we know the last element is ok. */ - LUSOL->a[L] = LUSOL->a[*NUMNZ]; - LUSOL->indc[L] = LUSOL->indc[*NUMNZ]; - LUSOL->indr[L] = LUSOL->indr[*NUMNZ]; - (*NUMNZ)--; - } - } - *LERR = 0; - *INFORM = LUSOL_INFORM_LUSUCCESS; - return; - -x910: - *LERR = L; - *INFORM = LUSOL_INFORM_LUSINGULAR; -} - -/* ================================================================== - lu1or2 sorts a list of matrix elements a(i,j) into column - order, given numa entries a(i,j), i, j in the parallel - arrays a, inum, jnum respectively. The matrix is assumed - to have n columns and an arbitrary number of rows. - On entry, len(*) must contain the length of each column. - On exit, a(*) and inum(*) are sorted, jnum(*) = 0, and - loc(j) points to the start of column j. - lu1or2 is derived from mc20ad, a routine in the Harwell - Subroutine Library, author J. K. Reid. - ------------------------------------------------------------------ - xx Feb 1985: Original version. - 17 Oct 2000: a, inum, jnum now have size lena to allow nelem = 0. - ================================================================== */ -void LU1OR2(LUSOLrec *LUSOL) -{ - LPSREAL ACE, ACEP; - int L, J, I, JCE, ICE, ICEP, JCEP, JA, JB; - -/* Set loc(j) to point to the beginning of column j. */ - L = 1; - for(J = 1; J <= LUSOL->n; J++) { - LUSOL->locc[J] = L; - L += LUSOL->lenc[J]; - } -/* Sort the elements into column order. - The algorithm is an in-place sort and is of order numa. */ - for(I = 1; I <= LUSOL->nelem; I++) { -/* Establish the current entry. */ - JCE = LUSOL->indr[I]; - if(JCE==0) - continue; - ACE = LUSOL->a[I]; - ICE = LUSOL->indc[I]; - LUSOL->indr[I] = 0; -/* Chain from current entry. */ - for(J = 1; J <= LUSOL->nelem; J++) { -/* The current entry is not in the correct position. - Determine where to store it. */ - L = LUSOL->locc[JCE]; - LUSOL->locc[JCE]++; -/* Save the contents of that location. */ - ACEP = LUSOL->a[L]; - ICEP = LUSOL->indc[L]; - JCEP = LUSOL->indr[L]; -/* Store current entry. */ - LUSOL->a[L] = ACE; - LUSOL->indc[L] = ICE; - LUSOL->indr[L] = 0; -/* If next current entry needs to be processed, - copy it into current entry. */ - if(JCEP==0) - break; - ACE = ACEP; - ICE = ICEP; - JCE = JCEP; - } - } -/* Reset loc(j) to point to the start of column j. */ - JA = 1; - for(J = 1; J <= LUSOL->n; J++) { - JB = LUSOL->locc[J]; - LUSOL->locc[J] = JA; - JA = JB; - } -} - -/* ================================================================== - lu1or3 looks for duplicate elements in an m by n matrix A - defined by the column list indc, lenc, locc. - iw is used as a work vector of length m. - ------------------------------------------------------------------ - xx Feb 1985: Original version. - 17 Oct 2000: indc, indr now have size lena to allow nelem = 0. - ================================================================== */ -void LU1OR3(LUSOLrec *LUSOL, int *LERR, int *INFORM) -{ - int I, J, L1, L2, L; - -#ifdef LUSOLFastClear - MEMCLEAR((LUSOL->ip+1), LUSOL->m); -#else - for(I = 1; I <= LUSOL->m; I++) - LUSOL->ip[I] = ZERO; -#endif - - for(J = 1; J <= LUSOL->n; J++) { - if(LUSOL->lenc[J]>0) { - L1 = LUSOL->locc[J]; - L2 = (L1+LUSOL->lenc[J])-1; - for(L = L1; L <= L2; L++) { - I = LUSOL->indc[L]; - if(LUSOL->ip[I]==J) - goto x910; - LUSOL->ip[I] = J; - } - } - } - *INFORM = LUSOL_INFORM_LUSUCCESS; - return; -x910: - *LERR = L; - *INFORM = LUSOL_INFORM_LUSINGULAR; -} - -/* ================================================================== - lu1or4 constructs a row list indr, locr - from a corresponding column list indc, locc, - given the lengths of both columns and rows in lenc, lenr. - ------------------------------------------------------------------ - xx Feb 1985: Original version. - 17 Oct 2000: indc, indr now have size lena to allow nelem = 0. - ================================================================== */ -void LU1OR4(LUSOLrec *LUSOL) -{ - int L, I, L2, J, JDUMMY, L1, LR; - -/* Initialize locr(i) to point just beyond where the - last component of row i will be stored. */ - L = 1; - for(I = 1; I <= LUSOL->m; I++) { - L += LUSOL->lenr[I]; - LUSOL->locr[I] = L; - } -/* By processing the columns backwards and decreasing locr(i) - each time it is accessed, it will end up pointing to the - beginning of row i as required. */ - L2 = LUSOL->nelem; - J = LUSOL->n+1; - for(JDUMMY = 1; JDUMMY <= LUSOL->n; JDUMMY++) { - J = J-1; - if(LUSOL->lenc[J]>0) { - L1 = LUSOL->locc[J]; - for(L = L1; L <= L2; L++) { - I = LUSOL->indc[L]; - LR = LUSOL->locr[I]-1; - LUSOL->locr[I] = LR; - LUSOL->indr[LR] = J; - } - L2 = L1-1; - } - } -} - -/* ================================================================== - lu1pen deals with pending fill-in in the row file. - ------------------------------------------------------------------ - ifill(ll) says if a row involved in the new column of L - has to be updated. If positive, it is the total - length of the final updated row. - jfill(lu) says if a column involved in the new row of U - contains any pending fill-ins. If positive, it points - to the first fill-in in the column that has yet to be - added to the row file. - ------------------------------------------------------------------ - 16 Apr 1989: First version of lu1pen. - 23 Mar 2001: ilast used and updated. - ================================================================== */ -void LU1PEN(LUSOLrec *LUSOL, int NSPARE, int *ILAST, - int LPIVC1, int LPIVC2, int LPIVR1, int LPIVR2, - int *LROW, int IFILL[], int JFILL[]) -{ - int LL, LC, L, I, LR1, LR2, LR, LU, J, LC1, LC2, LAST; - - LL = 0; - for(LC = LPIVC1; LC <= LPIVC2; LC++) { - LL++; - if(IFILL[LL]==0) - continue; -/* Another row has pending fill. - First, add some spare space at the } - of the current last row. */ -#if 1 - LC1 = (*LROW)+1; - LC2 = (*LROW)+NSPARE; - *LROW = LC2; - for(L = LC1; L <= LC2; L++) { -#else - for(L = (*LROW)+1; L <= (*LROW)+NSPARE; L++) { - *LROW = L; /* ******* ERROR ???? */ -#endif - LUSOL->indr[L] = 0; - } -/* Now move row i to the end of the row file. */ - I = LUSOL->indc[LC]; - *ILAST = I; - LR1 = LUSOL->locr[I]; - LR2 = (LR1+LUSOL->lenr[I])-1; - LUSOL->locr[I] = (*LROW)+1; - for(LR = LR1; LR <= LR2; LR++) { - (*LROW)++; - LUSOL->indr[*LROW] = LUSOL->indr[LR]; - LUSOL->indr[LR] = 0; - } - (*LROW) += IFILL[LL]; - } -/* Scan all columns of D and insert the pending fill-in - into the row file. */ - LU = 1; - for(LR = LPIVR1; LR <= LPIVR2; LR++) { - LU++; - if(JFILL[LU]==0) - continue; - J = LUSOL->indr[LR]; - LC1 = (LUSOL->locc[J]+JFILL[LU])-1; - LC2 = (LUSOL->locc[J]+LUSOL->lenc[J])-1; - for(LC = LC1; LC <= LC2; LC++) { - I = LUSOL->indc[LC]-LUSOL->m; - if(I>0) { - LUSOL->indc[LC] = I; - LAST = LUSOL->locr[I]+LUSOL->lenr[I]; - LUSOL->indr[LAST] = J; - LUSOL->lenr[I]++; - } - } - } -} - - -/* ================================================================== - lu1fad is a driver for the numerical phase of lu1fac. - At each stage it computes a column of L and a row of U, - using a Markowitz criterion to select the pivot element, - subject to a stability criterion that bounds the elements of L. - ------------------------------------------------------------------ - Local variables - --------------- - lcol is the length of the column file. It points to the last - nonzero in the column list. - lrow is the analogous quantity for the row file. - lfile is the file length (lcol or lrow) after the most recent - compression of the column list or row list. - nrowd and ncold are the number of rows and columns in the - matrix defined by the pivot column and row. They are the - dimensions of the submatrix D being altered at this stage. - melim and nelim are the number of rows and columns in the - same matrix D, excluding the pivot column and row. - mleft and nleft are the number of rows and columns - still left to be factored. - nzchng is the increase in nonzeros in the matrix that remains - to be factored after the current elimination - (usually negative). - nzleft is the number of nonzeros still left to be factored. - nspare is the space we leave at the end of the last row or - column whenever a row or column is being moved to the } - of its file. nspare = 1 or 2 might help reduce the - number of file compressions when storage is tight. - The row and column ordering permutes A into the form - ------------------------ - \ | - \ U1 | - \ | - -------------------- - |\ - | \ - | \ - P A Q = | \ - | \ - | -------------- - | | | - | | | - | L1 | A2 | - | | | - | | | - -------------------- - where the block A2 is factored as A2 = L2 U2. - The phases of the factorization are as follows. - Utri is true when U1 is being determined. - Any column of length 1 is accepted immediately (if TPP). - Ltri is true when L1 is being determined. - lu1mar exits as soon as an acceptable pivot is found - in a row of length 1. - spars1 is true while the density of the (modified) A2 is less - than the parameter dens1 = parmlu(7) = 0.3 say. - lu1mar searches maxcol columns and maxrow rows, - where maxcol = luparm(3), maxrow = maxcol - 1. - lu1mxc is used to keep the biggest element at the top - of all remaining columns. - spars2 is true while the density of the modified A2 is less - than the parameter dens2 = parmlu(8) = 0.6 say. - lu1mar searches maxcol columns and no rows. - lu1mxc could fix up only the first maxcol cols (with TPP). - 22 Sep 2000: For simplicity, lu1mxc fixes all - modified cols. - dense is true once the density of A2 reaches dens2. - lu1mar searches only 1 column (the shortest). - lu1mxc could fix up only the first column (with TPP). - ------------------------------------------------------------------ - 00 Jan 1986 Version documented in LUSOL paper: - Gill, Murray, Saunders and Wright (1987), - Maintaining LU factors of a general sparse matrix, - Linear algebra and its applications 88/89, 239-270. - 02 Feb 1989 Following Suhl and Aittoniemi (1987), the largest - element in each column is now kept at the start of - the column, i.e. in position locc(j) of a and indc. - This should speed up the Markowitz searches. - To save time on highly triangular matrices, we wait - until there are no further columns of length 1 - before setting and maintaining that property. - 12 Apr 1989 ipinv and iqinv added (inverses of ip and iq) - to save searching ip and iq for rows and columns - altered in each elimination step. (Used in lu1pq2) - 19 Apr 1989 Code segmented to reduce its size. - lu1gau does most of the Gaussian elimination work. - lu1mar does just the Markowitz search. - lu1mxc moves biggest elements to top of columns. - lu1pen deals with pending fill-in in the row list. - lu1pq2 updates the row and column permutations. - 26 Apr 1989 maxtie replaced by maxcol, maxrow in the Markowitz - search. maxcol, maxrow change as density increases. - 25 Oct 1993 keepLU implemented. - 07 Feb 1994 Exit main loop early to finish off with a dense LU. - densLU tells lu1fad whether to do it. - 21 Dec 1994 Bug fixed. nrank was wrong after the call to lu1ful. - 12 Nov 1999 A parallel version of dcopy gave trouble in lu1ful - during left-shift of dense matrix D within a(*). - Fixed this unexpected problem here in lu1fad - by making sure the first and second D don't overlap. - 13 Sep 2000 TCP (Threshold Complete Pivoting) implemented. - lu2max added - (finds aijmax from biggest elems in each col). - Utri, Ltri and Spars1 phases apply. - No switch to Dense CP yet. (Only TPP switches.) - 14 Sep 2000 imax needed to remember row containing aijmax. - 22 Sep 2000 For simplicity, lu1mxc always fixes all modified cols. - (TPP spars2 used to fix just the first maxcol cols.) - 08 Nov 2000: Speed up search for aijmax. - Don't need to search all columns if the elimination - didn't alter the col containing the current aijmax. - 21 Nov 2000: lu1slk implemented for Utri phase with TCP - to guard against deceptive triangular matrices. - (Utri used to have aijtol >= 0.9999 to include - slacks, but this allows other 1s to be accepted.) - Utri now accepts slacks, but applies normal aijtol - test to other pivots. - 28 Nov 2000: TCP with empty cols must call lu1mxc and lu2max - with ( lq1, n, ... ), not just ( 1, n, ... ). - 23 Mar 2001: lu1fad bug with TCP. - A col of length 1 might not be accepted as a pivot. - Later it appears in a pivot row and temporarily - has length 0 (when pivot row is removed - but before the column is filled in). If it is the - last column in storage, the preceding col also thinks - it is "last". Trouble arises when the preceding col - needs fill-in -- it overlaps the real "last" column. - (Very rarely, same trouble might have happened if - the drop tolerance caused columns to have length 0.) - Introduced ilast to record the last row in row file, - jlast to record the last col in col file. - lu1rec returns ilast = indr(lrow + 1) - or jlast = indc(lcol + 1). - (Should be an output parameter, but didn't want to - alter lu1rec's parameter list.) - lu1rec also treats empty rows or cols safely. - (Doesn't eliminate them!) - 26 Apr 2002: Heap routines added for TCP. - lu2max no longer needed. - imax, jmax used only for printing. - 01 May 2002: lu1DCP implemented (dense complete pivoting). - Both TPP and TCP now switch to dense LU - when density exceeds dens2. - 06 May 2002: In dense mode, store diag(U) in natural order. - 09 May 2002: lu1mCP implemented (Markowitz TCP via heap). - 11 Jun 2002: lu1mRP implemented (Markowitz TRP). - 28 Jun 2002: Fixed call to lu1mxr. - 14 Dec 2002: lu1mSP implemented (Markowitz TSP). - 15 Dec 2002: Both TPP and TSP can grab cols of length 1 - during Utri. - ================================================================== */ -void LU1FAD(LUSOLrec *LUSOL, -#ifdef ClassicHamaxR - int LENA2, int LENH, LPSREAL HA[], int HJ[], int HK[], LPSREAL AMAXR[], -#endif - int *INFORM, int *LENL, int *LENU, int *MINLEN, - int *MERSUM, int *NUTRI, int *NLTRI, - int *NDENS1, int *NDENS2, int *NRANK, - LPSREAL *LMAX, LPSREAL *UMAX, LPSREAL *DUMAX, LPSREAL *DUMIN, LPSREAL *AKMAX) -{ - MYBOOL UTRI, LTRI, SPARS1, SPARS2, DENSE, DENSLU, KEEPLU, TCP, TPP, TRP,TSP; - int HLEN, HOPS, H, LPIV, LPRINT, MAXCOL, MAXROW, ILAST, JLAST, LFILE, LROW, LCOL, - MINMN, MAXMN, NZLEFT, NSPARE, LU1, KK, J, LC, MLEFT, NLEFT, NROWU, - LQ1, LQ2, JBEST, LQ, I, IBEST, MBEST, LEND, NFREE, LD, NCOLD, NROWD, - MELIM, NELIM, JMAX, IMAX, LL1, LSAVE, LFREE, LIMIT, MINFRE, LPIVR, LPIVR1, LPIVR2, - L, LPIVC, LPIVC1, LPIVC2, KBEST, LU, LR, LENJ, LC1, LAST, LL, LS, - LENI, LR1, LFIRST, NFILL, NZCHNG, K, MRANK, NSING; - LPSREAL LIJ, LTOL, SMALL, USPACE, DENS1, DENS2, AIJMAX, AIJTOL, AMAX, ABEST, DIAG, V; -#ifdef ClassicHamaxR - int LDIAGU; -#else - int LENA2 = LUSOL->lena; -#endif - -#ifdef UseTimer - int eltime, mktime, ntime; - timer ( "start", 3 ); - ntime = LUSOL->n / 4; -#endif - -#ifdef ForceInitialization - AIJMAX = 0; - AIJTOL = 0; - HLEN = 0; - JBEST = 0; - IBEST = 0; - MBEST = 0; - LEND = 0; - LD = 0; -#endif - - LPRINT = LUSOL->luparm[LUSOL_IP_PRINTLEVEL]; - MAXCOL = LUSOL->luparm[LUSOL_IP_MARKOWITZ_MAXCOL]; - LPIV = LUSOL->luparm[LUSOL_IP_PIVOTTYPE]; - KEEPLU = (MYBOOL) (LUSOL->luparm[LUSOL_IP_KEEPLU]!=FALSE); -/* Threshold Partial Pivoting (normal). */ - TPP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TPP); -/* Threshold Rook Pivoting */ - TRP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TRP); -/* Threshold Complete Pivoting. */ - TCP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TCP); -/* Threshold Symmetric Pivoting. */ - TSP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TSP); - DENSLU = FALSE; - MAXROW = MAXCOL-1; -/* Assume row m is last in the row file. */ - ILAST = LUSOL->m; -/* Assume col n is last in the col file. */ - JLAST = LUSOL->n; - LFILE = LUSOL->nelem; - LROW = LUSOL->nelem; - LCOL = LUSOL->nelem; - MINMN = MIN(LUSOL->m,LUSOL->n); - MAXMN = MAX(LUSOL->m,LUSOL->n); - NZLEFT = LUSOL->nelem; - NSPARE = 1; - - if(KEEPLU) - LU1 = LENA2+1; - else { -/* Store only the diagonals of U in the top of memory. */ -#ifdef ClassicdiagU - LDIAGU = LENA2-LUSOL->n; - LU1 = LDIAGU+1; - LUSOL->diagU = LUSOL->a+LDIAGU; -#else - LU1 = LENA2+1; -#endif - } - - LTOL = LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - USPACE = LUSOL->parmlu[LUSOL_RP_COMPSPACE_U]; - DENS1 = LUSOL->parmlu[LUSOL_RP_MARKOWITZ_CONLY]; - DENS2 = LUSOL->parmlu[LUSOL_RP_MARKOWITZ_DENSE]; - UTRI = TRUE; - LTRI = FALSE; - SPARS1 = FALSE; - SPARS2 = FALSE; - DENSE = FALSE; -/* Check parameters. */ - SETMAX(LTOL,1.0001E+0); - SETMIN(DENS1,DENS2); -/* Initialize output parameters. - lenL, lenU, minlen, mersum, nUtri, nLtri, ndens1, ndens2, nrank - are already initialized by lu1fac. */ - *LMAX = ZERO; - *UMAX = ZERO; - *DUMAX = ZERO; - *DUMIN = LUSOL_BIGNUM; - if(LUSOL->nelem==0) - *DUMIN = ZERO; - *AKMAX = ZERO; - HOPS = 0; -/* More initialization. - Don't worry yet about lu1mxc. */ - if(TPP || TSP) { - AIJMAX = ZERO; - AIJTOL = ZERO; - HLEN = 1; -/* TRP or TCP */ - } - else { -/* Move biggest element to top of each column. - Set w(*) to mark slack columns (unit vectors). */ - LU1MXC(LUSOL, 1,LUSOL->n,LUSOL->iq); - LU1SLK(LUSOL); - } - if(TRP) -/* Find biggest element in each row. */ -#ifdef ClassicHamaxR - LU1MXR(LUSOL, 1,LUSOL->m,LUSOL->ip,AMAXR); -#else - LU1MXR(LUSOL, 1,LUSOL->m,LUSOL->ip,LUSOL->amaxr); -#endif - - if(TCP) { -/* Set Ha(1:Hlen) = biggest element in each column, - Hj(1:Hlen) = corresponding column indices. */ - HLEN = 0; - for(KK = 1; KK <= LUSOL->n; KK++) { - HLEN++; - J = LUSOL->iq[KK]; - LC = LUSOL->locc[J]; -#ifdef ClassicHamaxR - HA[HLEN] = fabs(LUSOL->a[LC]); - HJ[HLEN] = J; - HK[J] = HLEN; -#else - LUSOL->Ha[HLEN] = fabs(LUSOL->a[LC]); - LUSOL->Hj[HLEN] = J; - LUSOL->Hk[J] = HLEN; -#endif - } -/* Build the heap, creating new Ha, Hj and setting Hk(1:Hlen). */ -#ifdef ClassicHamaxR - HBUILD(HA,HJ,HK,HLEN,&HOPS); -#else - HBUILD(LUSOL->Ha,LUSOL->Hj,LUSOL->Hk,HLEN,&HOPS); -#endif - } -/* ------------------------------------------------------------------ - Start of main loop. - ------------------------------------------------------------------ */ - MLEFT = LUSOL->m+1; - NLEFT = LUSOL->n+1; - for(NROWU = 1; NROWU <= MINMN; NROWU++) { -#ifdef UseTimer - mktime = (nrowu / ntime) + 4; - eltime = (nrowu / ntime) + 9; -#endif - MLEFT--; - NLEFT--; -/* Bail out if there are no nonzero rows left. */ - if(LUSOL->iploc[1]>LUSOL->m) - goto x900; -/* For TCP, the largest Aij is at the top of the heap. */ - if(TCP) { -/* - Marvelously easy */ -#ifdef ClassicHamaxR - AIJMAX = HA[1]; -#else - AIJMAX = LUSOL->Ha[1]; -#endif - SETMAX(*AKMAX,AIJMAX); - AIJTOL = AIJMAX/LTOL; - } -/* =============================================================== - Find a suitable pivot element. - =============================================================== */ - if(UTRI) { -/* ------------------------------------------------------------ - So far all columns have had length 1. - We are still looking for the (backward) triangular part of A - that forms the first rows and columns of U. - ------------------------------------------------------------ */ - LQ1 = LUSOL->iqloc[1]; - LQ2 = LUSOL->n; - if(LUSOL->m>1) - LQ2 = LUSOL->iqloc[2]-1; -/* There are more cols of length 1. */ - if(LQ1<=LQ2) { - if(TPP || TSP) { -/* Grab the first one. */ - JBEST = LUSOL->iq[LQ1]; -/* Scan all columns of length 1 ... TRP or TCP */ - } - else { - JBEST = 0; - for(LQ = LQ1; LQ <= LQ2; LQ++) { - J = LUSOL->iq[LQ]; -/* Accept a slack */ - if(LUSOL->w[J]>ZERO) { - JBEST = J; - goto x250; - } - LC = LUSOL->locc[J]; - AMAX = fabs(LUSOL->a[LC]); - if(TRP) { - I = LUSOL->indc[LC]; -#ifdef ClassicHamaxR - AIJTOL = AMAXR[I]/LTOL; -#else - AIJTOL = LUSOL->amaxr[I]/LTOL; -#endif - } - if(AMAX>=AIJTOL) { - JBEST = J; - goto x250; - } - } - } -x250: - if(JBEST>0) { - LC = LUSOL->locc[JBEST]; - IBEST = LUSOL->indc[LC]; - MBEST = 0; - goto x300; - } - } -/* This is the end of the U triangle. - We will not return to this part of the code. - TPP and TSP call lu1mxc for the first time - (to move biggest element to top of each column). */ - if(LPRINT>=LUSOL_MSG_PIVOT) - LUSOL_report(LUSOL, 0, "Utri ended. spars1 = TRUE\n"); - UTRI = FALSE; - LTRI = TRUE; - SPARS1 = TRUE; - *NUTRI = NROWU-1; - if(TPP || TSP) - LU1MXC(LUSOL, LQ1,LUSOL->n,LUSOL->iq); - } - if(SPARS1) { -/* ------------------------------------------------------------ - Perform a Markowitz search. - Search cols of length 1, then rows of length 1, - then cols of length 2, then rows of length 2, etc. - ------------------------------------------------------------ */ -#ifdef UseTimer - timer ( "start", mktime ); -#endif -/* 12 Jun 2002: Next line disables lu1mCP below - if (TPP) then */ - if(TPP || TCP) { - LU1MAR(LUSOL, MAXMN,TCP,AIJTOL,LTOL,MAXCOL,MAXROW,&IBEST,&JBEST,&MBEST); - } - else if(TRP) { -#ifdef ClassicHamaxR - LU1MRP(LUSOL, MAXMN,LTOL,MAXCOL,MAXROW,&IBEST,&JBEST,&MBEST,AMAXR); -#else - LU1MRP(LUSOL, MAXMN,LTOL,MAXCOL,MAXROW,&IBEST,&JBEST,&MBEST,LUSOL->amaxr); -#endif -/* else if (TCP) { - lu1mCP( m , n , lena , aijtol, - ibest, jbest , mbest , - a , indc , indr , - lenc , lenr , locc , - Hlen , Ha , Hj ) */ - } - else if(TSP) { - LU1MSP(LUSOL, MAXMN,LTOL,MAXCOL,&IBEST,&JBEST,&MBEST); - if(IBEST==0) - goto x990; - } -#ifdef UseTimer - timer ( "finish", mktime ); -#endif - if(LTRI) { -/* So far all rows have had length 1. - We are still looking for the (forward) triangle of A - that forms the first rows and columns of L. */ - if(MBEST>0) { - LTRI = FALSE; - *NLTRI = NROWU-1-*NUTRI; - if(LPRINT>=LUSOL_MSG_PIVOT) - LUSOL_report(LUSOL, 0, "Ltri ended.\n"); - } - } - else { -/* See if what's left is as dense as dens1. */ - if(NZLEFT>=(DENS1*MLEFT)*NLEFT) { - SPARS1 = FALSE; - SPARS2 = TRUE; - *NDENS1 = NLEFT; - MAXROW = 0; - if(LPRINT>=LUSOL_MSG_PIVOT) - LUSOL_report(LUSOL, 0, "spars1 ended. spars2 = TRUE\n"); - } - } - } - else if(SPARS2 || DENSE) { -/* ------------------------------------------------------------ - Perform a restricted Markowitz search, - looking at only the first maxcol columns. (maxrow = 0.) - ------------------------------------------------------------ */ -#ifdef UseTimer - timer ( "start", mktime ); -#endif -/* 12 Jun 2002: Next line disables lu1mCP below - if (TPP) then */ - if(TPP || TCP) { - LU1MAR(LUSOL, MAXMN,TCP,AIJTOL,LTOL,MAXCOL,MAXROW,&IBEST,&JBEST,&MBEST); - } - else if(TRP) { -#ifdef ClassicHamaxR - LU1MRP(LUSOL, MAXMN,LTOL,MAXCOL,MAXROW,&IBEST,&JBEST,&MBEST,AMAXR); -#else - LU1MRP(LUSOL, MAXMN,LTOL,MAXCOL,MAXROW,&IBEST,&JBEST,&MBEST,LUSOL->amaxr); -#endif -/* else if (TCP) { - lu1mCP( m , n , lena , aijtol, - ibest, jbest , mbest , - a , indc , indr , - lenc , lenr , locc , - Hlen , Ha , Hj ) */ - } - else if(TSP) { - LU1MSP(LUSOL, MAXMN,LTOL,MAXCOL,&IBEST,&JBEST,&MBEST); - if(IBEST==0) - goto x985; - } -#ifdef UseTimer - timer ( "finish", mktime ); -#endif -/* See if what's left is as dense as dens2. */ - if(SPARS2) { - if(NZLEFT>=(DENS2*MLEFT)*NLEFT) { - SPARS2 = FALSE; - DENSE = TRUE; - *NDENS2 = NLEFT; - MAXCOL = 1; - if(LPRINT>=LUSOL_MSG_PIVOT) - LUSOL_report(LUSOL, 0, "spars2 ended. dense = TRUE\n"); - } - } - } -/* --------------------------------------------------------------- - See if we can finish quickly. - --------------------------------------------------------------- */ - if(DENSE) { - LEND = MLEFT*NLEFT; - NFREE = LU1-1; - if(NFREE>=2*LEND) { -/* There is room to treat the remaining matrix as - a dense matrix D. - We may have to compress the column file first. - 12 Nov 1999: D used to be put at the - beginning of free storage (lD = lcol + 1). - Now put it at the end (lD = lu1 - lenD) - so the left-shift in lu1ful will not - involve overlapping storage - (fatal with parallel dcopy). - */ - DENSLU = TRUE; - *NDENS2 = NLEFT; - LD = LU1-LEND; - if(LCOL>=LD) { - LU1REC(LUSOL, LUSOL->n,TRUE,&LCOL, - LUSOL->indc,LUSOL->lenc,LUSOL->locc); - LFILE = LCOL; - JLAST = LUSOL->indc[LCOL+1]; - } - goto x900; - } - } -/* =============================================================== - The best aij has been found. - The pivot row ibest and the pivot column jbest - Define a dense matrix D of size nrowd by ncold. - =============================================================== */ -x300: - NCOLD = LUSOL->lenr[IBEST]; - NROWD = LUSOL->lenc[JBEST]; - MELIM = NROWD-1; - NELIM = NCOLD-1; - (*MERSUM) += MBEST; - (*LENL) += MELIM; - (*LENU) += NCOLD; - if(LPRINT>=LUSOL_MSG_PIVOT) { - if(NROWU==1) - LUSOL_report(LUSOL, 0, "lu1fad debug:\n"); - if(TPP || TRP || TSP) { - LUSOL_report(LUSOL, 0, "nrowu:%7d i,jbest:%7d,%7d nrowd,ncold:%6d,%6d\n", - NROWU, IBEST,JBEST, NROWD,NCOLD); -/* TCP */ - } - else { -#ifdef ClassicHamaxR - JMAX = HJ[1]; -#else - JMAX = LUSOL->Hj[1]; -#endif - IMAX = LUSOL->indc[LUSOL->locc[JMAX]]; - LUSOL_report(LUSOL, 0, "nrowu:%7d i,jbest:%7d,%7d nrowd,ncold:%6d,%6d i,jmax:%7d,%7d aijmax:%g\n", - NROWU, IBEST,JBEST, NROWD,NCOLD, IMAX,JMAX, AIJMAX); - } - } -/* =============================================================== - Allocate storage for the next column of L and next row of U. - Initially the top of a, indc, indr are used as follows: - ncold melim ncold melim - a |...........|...........|ujbest..ujn|li1......lim| - indc |...........| lenr(i) | lenc(j) | markl(i) | - indr |...........| iqloc(i) | jfill(j) | ifill(i) | - ^ ^ ^ ^ ^ - lfree lsave lu1 ll1 oldlu1 - Later the correct indices are inserted: - indc | | | |i1........im| - indr | | |jbest....jn|ibest..ibest| - =============================================================== */ - if(!KEEPLU) { -/* Always point to the top spot. - Only the current column of L and row of U will - take up space, overwriting the previous ones. */ -#ifdef ClassicHamaxR - LU1 = LDIAGU+1; -#else - LU1 = LENA2+1; -#endif - } - /* Update (left-shift) pointers to make room for the new data */ - LL1 = LU1-MELIM; - LU1 = LL1-NCOLD; - LSAVE = LU1-NROWD; - LFREE = LSAVE-NCOLD; - - /* Check if we need to allocate more memory, and allocate if necessary */ -#if 0 /* Proposal by Michael A. Saunders (logic based on Markowitz' rule) */ - L = NROWD*NCOLD; - - /* Try to avoid future expansions by anticipating further updates - KE extension */ - if(LUSOL->luparm[LUSOL_IP_UPDATELIMIT] > 0) -#if 1 - L *= (int) (log(LUSOL->luparm[LUSOL_IP_UPDATELIMIT]-LUSOL->luparm[LUSOL_IP_UPDATECOUNT]+2.0) + 1); -#else - L *= (LUSOL->luparm[LUSOL_IP_UPDATELIMIT]-LUSOL->luparm[LUSOL_IP_UPDATECOUNT]) / 2 + 1; -#endif - -#else /* Version by Kjell Eikland (from luparm[LUSOL_IP_MINIMUMLENA] and safety margin) */ - L = (KEEPLU ? MAX(LROW, LCOL) + 2*(LUSOL->m+LUSOL->n) : 0); - L *= LUSOL_MULT_nz_a; - SETMAX(L, NROWD*NCOLD); -#endif - - /* Do the memory expansion */ - if((L > LFREE-LCOL) && LUSOL_expand_a(LUSOL, &L, &LFREE)) { - LL1 += L; - LU1 += L; - LSAVE += L; -#ifdef ClassicdiagU - LUSOL->diagU += L; -#endif -#ifdef ClassicHamaxR - HA += L; - HJ += L; - HK += L; - AMAXR += L; -#endif - } - LIMIT = (int) (USPACE*LFILE)+LUSOL->m+LUSOL->n+1000; - -/* Make sure the column file has room. - Also force a compression if its length exceeds a certain limit. */ -#ifdef StaticMemAlloc - MINFRE = NCOLD+MELIM; -#else - MINFRE = NROWD*NCOLD; -#endif - NFREE = LFREE-LCOL; - if(NFREELIMIT) { - LU1REC(LUSOL, LUSOL->n,TRUE,&LCOL, - LUSOL->indc,LUSOL->lenc,LUSOL->locc); - LFILE = LCOL; - JLAST = LUSOL->indc[LCOL+1]; - NFREE = LFREE-LCOL; - if(NFREELIMIT) { - LU1REC(LUSOL, LUSOL->m,FALSE,&LROW, - LUSOL->indr,LUSOL->lenr,LUSOL->locr); - LFILE = LROW; - ILAST = LUSOL->indr[LROW+1]; - NFREE = LFREE-LROW; - if(NFREElocr[IBEST]; - LPIVR1 = LPIVR+1; - LPIVR2 = LPIVR+NELIM; - for(L = LPIVR; L <= LPIVR2; L++) { - if(LUSOL->indr[L]==JBEST) - break; - } - - LUSOL->indr[L] = LUSOL->indr[LPIVR]; - LUSOL->indr[LPIVR] = JBEST; - LPIVC = LUSOL->locc[JBEST]; - LPIVC1 = LPIVC+1; - LPIVC2 = LPIVC+MELIM; - for(L = LPIVC; L <= LPIVC2; L++) { - if(LUSOL->indc[L]==IBEST) - break; - } - LUSOL->indc[L] = LUSOL->indc[LPIVC]; - LUSOL->indc[LPIVC] = IBEST; - ABEST = LUSOL->a[L]; - LUSOL->a[L] = LUSOL->a[LPIVC]; - LUSOL->a[LPIVC] = ABEST; - if(!KEEPLU) -/* Store just the diagonal of U, in natural order. - !! a[ldiagU + nrowu] = abest ! This was in pivot order. */ - LUSOL->diagU[JBEST] = ABEST; - -/* ============================================================== - Delete pivot col from heap. - Hk tells us where it is in the heap. - ============================================================== */ - if(TCP) { -#ifdef ClassicHamaxR - KBEST = HK[JBEST]; - HDELETE(HA,HJ,HK,&HLEN,KBEST,&H); -#else - KBEST = LUSOL->Hk[JBEST]; - HDELETE(LUSOL->Ha,LUSOL->Hj,LUSOL->Hk,&HLEN,KBEST,&H); -#endif - HOPS += H; - } -/* =============================================================== - Delete the pivot row from the column file - and store it as the next row of U. - set indr(lu) = 0 to initialize jfill ptrs on columns of D, - indc(lu) = lenj to save the original column lengths. - =============================================================== */ - LUSOL->a[LU1] = ABEST; - LUSOL->indr[LU1] = JBEST; - LUSOL->indc[LU1] = NROWD; - LU = LU1; - DIAG = fabs(ABEST); - SETMAX(*UMAX,DIAG); - SETMAX(*DUMAX,DIAG); - SETMIN(*DUMIN,DIAG); - for(LR = LPIVR1; LR <= LPIVR2; LR++) { - LU++; - J = LUSOL->indr[LR]; - LENJ = LUSOL->lenc[J]; - LUSOL->lenc[J] = LENJ-1; - LC1 = LUSOL->locc[J]; - LAST = LC1+LUSOL->lenc[J]; - for(L = LC1; L <= LAST; L++) { - if(LUSOL->indc[L]==IBEST) - break; - } - LUSOL->a[LU] = LUSOL->a[L]; - LUSOL->indr[LU] = 0; - LUSOL->indc[LU] = LENJ; - SETMAX(*UMAX,fabs(LUSOL->a[LU])); - LUSOL->a[L] = LUSOL->a[LAST]; - LUSOL->indc[L] = LUSOL->indc[LAST]; -/* Free entry */ - LUSOL->indc[LAST] = 0; -/* ??? if (j .eq. jlast) lcol = lcol - 1 */ - } -/* =============================================================== - Delete the pivot column from the row file - and store the nonzeros of the next column of L. - Set indc(ll) = 0 to initialize markl(*) markers, - indr(ll) = 0 to initialize ifill(*) row fill-in cntrs, - indc(ls) = leni to save the original row lengths, - indr(ls) = iqloc(i) to save parts of iqloc(*), - iqloc(i) = lsave - ls to point to the nonzeros of L - = -1, -2, -3, ... in mark(*). - =============================================================== */ - LUSOL->indc[LSAVE] = NCOLD; - if(MELIM==0) - goto x700; - LL = LL1-1; - LS = LSAVE; - ABEST = ONE/ABEST; - for(LC = LPIVC1; LC <= LPIVC2; LC++) { - LL++; - LS++; - I = LUSOL->indc[LC]; - LENI = LUSOL->lenr[I]; - LUSOL->lenr[I] = LENI-1; - LR1 = LUSOL->locr[I]; - LAST = LR1+LUSOL->lenr[I]; - for(L = LR1; L <= LAST; L++) { - if(LUSOL->indr[L]==JBEST) - break; - } - LUSOL->indr[L] = LUSOL->indr[LAST]; -/* Free entry */ - LUSOL->indr[LAST] = 0; - LUSOL->a[LL] = -LUSOL->a[LC]*ABEST; - LIJ = fabs(LUSOL->a[LL]); - SETMAX(*LMAX,LIJ); - LUSOL->indc[LL] = 0; - LUSOL->indr[LL] = 0; - LUSOL->indc[LS] = LENI; - LUSOL->indr[LS] = LUSOL->iqloc[I]; - LUSOL->iqloc[I] = LSAVE-LS; - } -/* =============================================================== - Do the Gaussian elimination. - This involves adding a multiple of the pivot column - to all other columns in the pivot row. - Sometimes more than one call to lu1gau is needed to allow - compression of the column file. - lfirst says which column the elimination should start with. - minfre is a bound on the storage needed for any one column. - lu points to off-diagonals of u. - nfill keeps track of pending fill-in in the row file. - =============================================================== */ - if(NELIM==0) - goto x700; - LFIRST = LPIVR1; - MINFRE = MLEFT+NSPARE; - LU = 1; - NFILL = 0; - -x400: -#ifdef UseTimer - timer ( "start", eltime ); -#endif - LU1GAU(LUSOL, MELIM,NSPARE,SMALL,LPIVC1,LPIVC2,&LFIRST,LPIVR2, - LFREE,MINFRE,ILAST,&JLAST,&LROW,&LCOL,&LU,&NFILL, - LUSOL->iqloc, LUSOL->a+LL1-LUSOL_ARRAYOFFSET, - LUSOL->indc+LL1-LUSOL_ARRAYOFFSET, LUSOL->a+LU1-LUSOL_ARRAYOFFSET, - LUSOL->indr+LL1-LUSOL_ARRAYOFFSET, LUSOL->indr+LU1-LUSOL_ARRAYOFFSET); -#ifdef UseTimer - timer ( "finish", eltime ); -#endif - if(LFIRST>0) { -/* The elimination was interrupted. - Compress the column file and try again. - lfirst, lu and nfill have appropriate new values. */ - LU1REC(LUSOL, LUSOL->n,TRUE,&LCOL, - LUSOL->indc,LUSOL->lenc,LUSOL->locc); - LFILE = LCOL; - JLAST = LUSOL->indc[LCOL+1]; - LPIVC = LUSOL->locc[JBEST]; - LPIVC1 = LPIVC+1; - LPIVC2 = LPIVC+MELIM; - NFREE = LFREE-LCOL; - if(NFREE0) { -/* Compress the row file if necessary. - lu1gau has set nfill to be the number of pending fill-ins - plus the current length of any rows that need to be moved. */ - MINFRE = NFILL; - NFREE = LFREE-LROW; - if(NFREEm,FALSE,&LROW, - LUSOL->indr,LUSOL->lenr,LUSOL->locr); - LFILE = LROW; - ILAST = LUSOL->indr[LROW+1]; - LPIVR = LUSOL->locr[IBEST]; - LPIVR1 = LPIVR+1; - LPIVR2 = LPIVR+NELIM; - NFREE = LFREE-LROW; - if(NFREEindr+LL1-LUSOL_ARRAYOFFSET,LUSOL->indr+LU1-LUSOL_ARRAYOFFSET); - } -/* =============================================================== - Restore the saved values of iqloc. - Insert the correct indices for the col of L and the row of U. - =============================================================== */ -x700: - LUSOL->lenr[IBEST] = 0; - LUSOL->lenc[JBEST] = 0; - LL = LL1-1; - LS = LSAVE; - for(LC = LPIVC1; LC <= LPIVC2; LC++) { - LL++; - LS++; - I = LUSOL->indc[LC]; - LUSOL->iqloc[I] = LUSOL->indr[LS]; - LUSOL->indc[LL] = I; - LUSOL->indr[LL] = IBEST; - } - LU = LU1-1; - for(LR = LPIVR; LR <= LPIVR2; LR++) { - LU++; - LUSOL->indr[LU] = LUSOL->indr[LR]; - } -/* =============================================================== - Free the space occupied by the pivot row - and update the column permutation. - Then free the space occupied by the pivot column - and update the row permutation. - nzchng is found in both calls to lu1pq2, but we use it only - after the second. - =============================================================== */ - LU1PQ2(LUSOL, NCOLD, &NZCHNG, - LUSOL->indr+LPIVR-LUSOL_ARRAYOFFSET, - LUSOL->indc+LU1-LUSOL_ARRAYOFFSET, LUSOL->lenc, - LUSOL->iqloc, LUSOL->iq, LUSOL->iqinv); - LU1PQ2(LUSOL, NROWD, &NZCHNG, - LUSOL->indc+LPIVC-LUSOL_ARRAYOFFSET, - LUSOL->indc+LSAVE-LUSOL_ARRAYOFFSET, LUSOL->lenr, - LUSOL->iploc, LUSOL->ip, LUSOL->ipinv); - NZLEFT += NZCHNG; - -/* =============================================================== - lu1mxr resets Amaxr(i) in each modified row i. - lu1mxc moves the largest aij to the top of each modified col j. - 28 Jun 2002: Note that cols of L have an implicit diag of 1.0, - so lu1mxr is called with ll1, not ll1+1, whereas - lu1mxc is called with lu1+1. - =============================================================== */ - if(UTRI && TPP) { -/* Relax -- we're not keeping big elements at the top yet. */ - } - else { - if(TRP && MELIM>0) -#ifdef ClassicHamaxR - LU1MXR(LUSOL, LL1,LL,LUSOL->indc,AMAXR); -#else - LU1MXR(LUSOL, LL1,LL,LUSOL->indc,LUSOL->amaxr); -#endif - - if(NELIM>0) { - LU1MXC(LUSOL, LU1+1,LU,LUSOL->indr); -/* Update modified columns in heap */ - if(TCP) { - for(KK = LU1+1; KK <= LU; KK++) { - J = LUSOL->indr[KK]; -#ifdef ClassicHamaxR - K = HK[J]; -#else - K = LUSOL->Hk[J]; -#endif -/* Biggest aij in column j */ - V = fabs(LUSOL->a[LUSOL->locc[J]]); -#ifdef ClassicHamaxR - HCHANGE(HA,HJ,HK,HLEN,K,V,J,&H); -#else - HCHANGE(LUSOL->Ha,LUSOL->Hj,LUSOL->Hk,HLEN,K,V,J,&H); -#endif - HOPS += H; - } - } - } - } -/* =============================================================== - Negate lengths of pivot row and column so they will be - eliminated during compressions. - =============================================================== */ - LUSOL->lenr[IBEST] = -NCOLD; - LUSOL->lenc[JBEST] = -NROWD; - -/* Test for fatal bug: row or column lists overwriting L and U. */ - if(LROW>LSAVE || LCOL>LSAVE) - goto x980; - -/* Reset the file lengths if pivot row or col was at the end. */ - if(IBEST==ILAST) - LROW = LUSOL->locr[IBEST]; - - if(JBEST==JLAST) - LCOL = LUSOL->locc[JBEST]; - - } -/* ------------------------------------------------------------------ - End of main loop. - ------------------------------------------------------------------ - ------------------------------------------------------------------ - Normal exit. - Move empty rows and cols to the end of ip, iq. - Then finish with a dense LU if necessary. - ------------------------------------------------------------------ */ -x900: - *INFORM = LUSOL_INFORM_LUSUCCESS; - LU1PQ3(LUSOL, LUSOL->m,LUSOL->lenr,LUSOL->ip,LUSOL->ipinv,&MRANK); - LU1PQ3(LUSOL, LUSOL->n,LUSOL->lenc,LUSOL->iq,LUSOL->iqinv,NRANK); - SETMIN(*NRANK, MRANK); - if(DENSLU) { -#ifdef UseTimer - timer ( "start", 17 ); -#endif - LU1FUL(LUSOL, LEND,LU1,TPP,MLEFT,NLEFT,*NRANK,NROWU,LENL,LENU, - &NSING,KEEPLU,SMALL,LUSOL->a+LD-LUSOL_ARRAYOFFSET,LUSOL->locr); -/* *** 21 Dec 1994: Bug in next line. - *** nrank = nrank - nsing */ - *NRANK = MINMN-NSING; -#ifdef UseTimer - timer ( "finish", 17 ); -#endif - } - *MINLEN = (*LENL)+(*LENU)+2*(LUSOL->m+LUSOL->n); - goto x990; -/* Not enough space free after a compress. - Set minlen to an estimate of the necessary value of lena. */ -x970: - *INFORM = LUSOL_INFORM_ANEEDMEM; - *MINLEN = LENA2+LFILE+2*(LUSOL->m+LUSOL->n); - goto x990; -/* Fatal error. This will never happen! - (Famous last words.) */ -x980: - *INFORM = LUSOL_INFORM_FATALERR; - goto x990; -/* Fatal error with TSP. Diagonal pivot not found. */ -x985: - *INFORM = LUSOL_INFORM_NOPIVOT; -/* Exit. */ -x990: -#ifdef UseTimer - timer ( "finish", 3 ); -#endif -; -} - - -/* ================================================================== - lu1fac computes a factorization A = L*U, where A is a sparse - matrix with m rows and n columns, P*L*P' is lower triangular - and P*U*Q is upper triangular for certain permutations P, Q - (which are returned in the arrays ip, iq). - Stability is ensured by limiting the size of the elements of L. - The nonzeros of A are input via the parallel arrays a, indc, indr, - which should contain nelem entries of the form aij, i, j - in any order. There should be no duplicate pairs i, j. - - ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - + Beware !!! The row indices i must be in indc, + - + and the column indices j must be in indr. + - + (Not the other way round!) + - ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - - It does not matter if some of the entries in a(*) are zero. - Entries satisfying abs( a(i) ) .le. parmlu(3) are ignored. - Other parameters in luparm and parmlu are described below. - The matrix A may be singular. On exit, nsing = luparm(11) gives - the number of apparent singularities. This is the number of - "small" diagonals of the permuted factor U, as judged by - the input tolerances Utol1 = parmlu(4) and Utol2 = parmlu(5). - The diagonal element diagj associated with column j of A is - "small" if - abs( diagj ) .le. Utol1 - or - abs( diagj ) .le. Utol2 * max( uj ), - where max( uj ) is the maximum element in the j-th column of U. - The position of such elements is returned in w(*). In general, - w(j) = + max( uj ), but if column j is a singularity, - w(j) = - max( uj ). Thus, w(j) .le. 0 if column j appears to be - dependent on the other columns of A. - NOTE: lu1fac (like certain other sparse LU packages) does not - treat dense columns efficiently. This means it will be slow - on "arrow matrices" of the form - A = (x a) - ( x b) - ( x c) - ( x d) - (x x x x e) - if the numerical values in the dense column allow it to be - chosen LATE in the pivot order. - With TPP (Threshold Partial Pivoting), the dense column is - likely to be chosen late. - With TCP (Threshold Complete Pivoting), if any of a,b,c,d - is significantly larger than other elements of A, it will - be chosen as the first pivot and the dense column will be - eliminated, giving reasonably sparse factors. - However, if element e is so big that TCP chooses it, the factors - will become dense. (It's hard to win on these examples!) - ------------------------------------------------------------------ - - Notes on the array names - ------------------------ - During the LU factorization, the sparsity pattern of the matrix - being factored is stored twice: in a column list and a row list. - The column list is ( a, indc, locc, lenc ) - where - a(*) holds the nonzeros, - indc(*) holds the indices for the column list, - locc(j) points to the start of column j in a(*) and indc(*), - lenc(j) is the number of nonzeros in column j. - The row list is ( indr, locr, lenr ) - where - indr(*) holds the indices for the row list, - locr(i) points to the start of row i in indr(*), - lenr(i) is the number of nonzeros in row i. - At all stages of the LU factorization, ip contains a complete - row permutation. At the start of stage k, ip(1), ..., ip(k-1) - are the first k-1 rows of the final row permutation P. - The remaining rows are stored in an ordered list - ( ip, iploc, ipinv ) - where - iploc(nz) points to the start in ip(*) of the set of rows - that currently contain nz nonzeros, - ipinv(i) points to the position of row i in ip(*). - For example, - iploc(1) = k (and this is where rows of length 1 {), - iploc(2) = k+p if there are p rows of length 1 - (and this is where rows of length 2 {). - Similarly for iq, iqloc, iqinv. - --------------------------------------------------------------------- - INPUT PARAMETERS - m (not altered) is the number of rows in A. - n (not altered) is the number of columns in A. - nelem (not altered) is the number of matrix entries given in - the arrays a, indc, indr. - lena (not altered) is the dimension of a, indc, indr. - This should be significantly larger than nelem. - Typically one should have - lena > max( 2*nelem, 10*m, 10*n, 10000 ) - but some applications may need more. - On machines with virtual memory it is safe to have - lena "far bigger than necessary", since not all of the - arrays will be used. - a (overwritten) contains entries Aij in a(1:nelem). - indc (overwritten) contains the indices i in indc(1:nelem). - indr (overwritten) contains the indices j in indr(1:nelem). - luparm input parameters: Typical value - luparm( 1) = nout File number for printed messages. 6 - luparm( 2) = lprint Print level. 0 - < 0 suppresses output. - = 0 gives error messages. - >= 10 gives statistics about the LU factors. - >= 50 gives debug output from lu1fac - (the pivot row and column and the - no. of rows and columns involved at - each elimination step). - luparm( 3) = maxcol lu1fac: maximum number of columns 5 - searched allowed in a Markowitz-type - search for the next pivot element. - For some of the factorization, the - number of rows searched is - maxrow = maxcol - 1. - luparm( 6) = 0 => TPP: Threshold Partial Pivoting. 0 - = 1 => TRP: Threshold Rook Pivoting. - = 2 => TCP: Threshold Complete Pivoting. - = 3 => TSP: Threshold Symmetric Pivoting. - = 4 => TDP: Threshold Diagonal Pivoting. - (TDP not yet implemented). - TRP and TCP are more expensive than TPP but - more stable and better at revealing rank. - Take care with setting parmlu(1), especially - with TCP. - NOTE: TSP and TDP are for symmetric matrices - that are either definite or quasi-definite. - TSP is effectively TRP for symmetric matrices. - TDP is effectively TCP for symmetric matrices. - luparm( 8) = keepLU lu1fac: keepLU = 1 means the numerical 1 - factors will be computed if possible. - keepLU = 0 means L and U will be discarded - but other information such as the row and - column permutations will be returned. - The latter option requires less storage. - parmlu input parameters: Typical value - parmlu( 1) = Ltol1 Max Lij allowed during Factor. - TPP 10.0 or 100.0 - TRP 4.0 or 10.0 - TCP 5.0 or 10.0 - TSP 4.0 or 10.0 - With TRP and TCP (Rook and Complete Pivoting), - values less than 25.0 may be expensive - on badly scaled data. However, - values less than 10.0 may be needed - to obtain a reliable rank-revealing - factorization. - parmlu( 2) = Ltol2 Max Lij allowed during Updates. 10.0 - during updates. - parmlu( 3) = small Absolute tolerance for eps**0.8 = 3.0d-13 - treating reals as zero. - parmlu( 4) = Utol1 Absolute tol for flagging eps**0.67= 3.7d-11 - small diagonals of U. - parmlu( 5) = Utol2 Relative tol for flagging eps**0.67= 3.7d-11 - small diagonals of U. - (eps = machine precision) - parmlu( 6) = Uspace Factor limiting waste space in U. 3.0 - In lu1fac, the row or column lists - are compressed if their length - exceeds Uspace times the length of - either file after the last compression. - parmlu( 7) = dens1 The density at which the Markowitz 0.3 - pivot strategy should search maxcol - columns and no rows. - (Use 0.3 unless you are experimenting - with the pivot strategy.) - parmlu( 8) = dens2 the density at which the Markowitz 0.5 - strategy should search only 1 column, - or (if storage is available) - the density at which all remaining - rows and columns will be processed - by a dense LU code. - For example, if dens2 = 0.1 and lena is - large enough, a dense LU will be used - once more than 10 per cent of the - remaining matrix is nonzero. - - OUTPUT PARAMETERS - a, indc, indr contain the nonzero entries in the LU factors of A. - If keepLU = 1, they are in a form suitable for use - by other parts of the LUSOL package, such as lu6sol. - U is stored by rows at the start of a, indr. - L is stored by cols at the end of a, indc. - If keepLU = 0, only the diagonals of U are stored, at the - end of a. - ip, iq are the row and column permutations defining the - pivot order. For example, row ip(1) and column iq(1) - defines the first diagonal of U. - lenc(1:numl0) contains the number of entries in nontrivial - columns of L (in pivot order). - lenr(1:m) contains the number of entries in each row of U - (in original order). - locc(1:n) = 0 (ready for the LU update routines). - locr(1:m) points to the beginning of the rows of U in a, indr. - iploc, iqloc, ipinv, iqinv are undefined. - w indicates singularity as described above. - inform = 0 if the LU factors were obtained successfully. - = 1 if U appears to be singular, as judged by lu6chk. - = 3 if some index pair indc(l), indr(l) lies outside - the matrix dimensions 1:m , 1:n. - = 4 if some index pair indc(l), indr(l) duplicates - another such pair. - = 7 if the arrays a, indc, indr were not large enough. - Their length "lena" should be increase to at least - the value "minlen" given in luparm(13). - = 8 if there was some other fatal error. (Shouldn't happen!) - = 9 if no diagonal pivot could be found with TSP or TDP. - The matrix must not be sufficiently definite - or quasi-definite. - luparm output parameters: - luparm(10) = inform Return code from last call to any LU routine. - luparm(11) = nsing No. of singularities marked in the - output array w(*). - luparm(12) = jsing Column index of last singularity. - luparm(13) = minlen Minimum recommended value for lena. - luparm(14) = maxlen ? - luparm(15) = nupdat No. of updates performed by the lu8 routines. - luparm(16) = nrank No. of nonempty rows of U. - luparm(17) = ndens1 No. of columns remaining when the density of - the matrix being factorized reached dens1. - luparm(18) = ndens2 No. of columns remaining when the density of - the matrix being factorized reached dens2. - luparm(19) = jumin The column index associated with DUmin. - luparm(20) = numL0 No. of columns in initial L. - luparm(21) = lenL0 Size of initial L (no. of nonzeros). - luparm(22) = lenU0 Size of initial U. - luparm(23) = lenL Size of current L. - luparm(24) = lenU Size of current U. - luparm(25) = lrow Length of row file. - luparm(26) = ncp No. of compressions of LU data structures. - luparm(27) = mersum lu1fac: sum of Markowitz merit counts. - luparm(28) = nUtri lu1fac: triangular rows in U. - luparm(29) = nLtri lu1fac: triangular rows in L. - luparm(30) = - parmlu output parameters: - parmlu(10) = Amax Maximum element in A. - parmlu(11) = Lmax Maximum multiplier in current L. - parmlu(12) = Umax Maximum element in current U. - parmlu(13) = DUmax Maximum diagonal in U. - parmlu(14) = DUmin Minimum diagonal in U. - parmlu(15) = Akmax Maximum element generated at any stage - during TCP factorization. - parmlu(16) = growth TPP: Umax/Amax TRP, TCP, TSP: Akmax/Amax - parmlu(17) = - parmlu(18) = - parmlu(19) = - parmlu(20) = resid lu6sol: residual after solve with U or U'. - ... - parmlu(30) = - ------------------------------------------------------------------ - 00 Jun 1983 Original version. - 00 Jul 1987 nrank saved in luparm(16). - 12 Apr 1989 ipinv, iqinv added as workspace. - 26 Apr 1989 maxtie replaced by maxcol in Markowitz search. - 16 Mar 1992 jumin saved in luparm(19). - 10 Jun 1992 lu1fad has to move empty rows and cols to the bottom - (via lu1pq3) before doing the dense LU. - 12 Jun 1992 Deleted dense LU (lu1ful, lu1vlu). - 25 Oct 1993 keepLU implemented. - 07 Feb 1994 Added new dense LU (lu1ful, lu1den). - 21 Dec 1994 Bugs fixed in lu1fad (nrank) and lu1ful (ipvt). - 08 Aug 1995 Use ip instead of w as parameter to lu1or3 (for F90). - 13 Sep 2000 TPP and TCP options implemented. - 17 Oct 2000 Fixed troubles due to A = empty matrix (Todd Munson). - 01 Dec 2000 Save Lmax, Umax, etc. after both lu1fad and lu6chk. - lu1fad sets them when keepLU = false. - lu6chk sets them otherwise, and includes items - from the dense LU. - 11 Mar 2001 lu6chk now looks at diag(U) when keepLU = false. - 26 Apr 2002 New TCP implementation using heap routines to - store largest element in each column. - New workspace arrays Ha, Hj, Hk required. - For compatibility, borrow space from a, indc, indr - rather than adding new input parameters. - 01 May 2002 lu1den changed to lu1DPP (dense partial pivoting). - lu1DCP implemented (dense complete pivoting). - Both TPP and TCP now switch to dense mode and end. - ================================================================== */ -void LU1FAC(LUSOLrec *LUSOL, int *INFORM) -{ - MYBOOL KEEPLU, TPP; - int LPIV, NELEM0, LPRINT, MINLEN, NUML0, LENL, LENU, LROW, MERSUM, - NUTRI, NLTRI, NDENS1, NDENS2, NRANK, NSING, JSING, JUMIN, NUMNZ, LERR, - LU, LL, LM, LTOPL, K, I, LENUK, J, LENLK, IDUMMY, LLSAVE, NMOVE, L2, L, NCP, NBUMP; -#ifdef ClassicHamaxR - int LENH, LENA2, LOCH, LMAXR; -#endif - - LPSREAL LMAX, LTOL, SMALL, AMAX, UMAX, DUMAX, DUMIN, AKMAX, DM, DN, DELEM, DENSTY, - AGRWTH, UGRWTH, GROWTH, CONDU, DINCR, AVGMER; - -/* Free row-based version of L0 (regenerated by LUSOL_btran). */ - if(LUSOL->L0 != NULL) - LUSOL_matfree(&(LUSOL->L0)); - -/* Grab relevant input parameters. */ - NELEM0 = LUSOL->nelem; - LPRINT = LUSOL->luparm[LUSOL_IP_PRINTLEVEL]; - LPIV = LUSOL->luparm[LUSOL_IP_PIVOTTYPE]; - KEEPLU = (MYBOOL) (LUSOL->luparm[LUSOL_IP_KEEPLU]!=FALSE); -/* Limit on size of Lij */ - LTOL = LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij]; -/* Drop tolerance */ - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - TPP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TPP); -/* Initialize output parameters. */ - *INFORM = LUSOL_INFORM_LUSUCCESS; - LERR = 0; - MINLEN = LUSOL->nelem + 2*(LUSOL->m+LUSOL->n); - NUML0 = 0; - LENL = 0; - LENU = 0; - LROW = 0; - MERSUM = 0; - NUTRI = LUSOL->m; - NLTRI = 0; - NDENS1 = 0; - NDENS2 = 0; - NRANK = 0; - NSING = 0; - JSING = 0; - JUMIN = 0; - AMAX = ZERO; - LMAX = ZERO; - UMAX = ZERO; - DUMAX = ZERO; - DUMIN = ZERO; - AKMAX = ZERO; - -/* Float version of dimensions. */ - DM = LUSOL->m; - DN = LUSOL->n; - DELEM = LUSOL->nelem; - -/* Initialize workspace parameters. */ - LUSOL->luparm[LUSOL_IP_COMPRESSIONS_LU] = 0; - if(LUSOL->lena < MINLEN) { - if(!LUSOL_realloc_a(LUSOL, MINLEN)) - goto x970; - } - -/* ------------------------------------------------------------------ - Organize the aij's in a, indc, indr. - lu1or1 deletes small entries, tests for illegal i,j's, - and counts the nonzeros in each row and column. - lu1or2 reorders the elements of A by columns. - lu1or3 uses the column list to test for duplicate entries - (same indices i,j). - lu1or4 constructs a row list from the column list. - ------------------------------------------------------------------ */ - LU1OR1(LUSOL, SMALL,&AMAX,&NUMNZ,&LERR,INFORM); - if(LPRINT>=LUSOL_MSG_STATISTICS) { - DENSTY = (100*DELEM)/(DM*DN); - LUSOL_report(LUSOL, 0, "m:%6d %c n:%6d nzcount:%9d Amax:%g Density:%g\n", - LUSOL->m, relationChar(LUSOL->m, LUSOL->n), LUSOL->n, - LUSOL->nelem, AMAX, DENSTY); - } - if(*INFORM!=LUSOL_INFORM_LUSUCCESS) - goto x930; - LUSOL->nelem = NUMNZ; - LU1OR2(LUSOL); - LU1OR3(LUSOL, &LERR,INFORM); - if(*INFORM!=LUSOL_INFORM_LUSUCCESS) - goto x940; - LU1OR4(LUSOL); -/* ------------------------------------------------------------------ - Set up lists of rows and columns with equal numbers of nonzeros, - using indc(*) as workspace. - ------------------------------------------------------------------ */ - LU1PQ1(LUSOL, LUSOL->m,LUSOL->n,LUSOL->lenr, - LUSOL->ip,LUSOL->iploc,LUSOL->ipinv, - LUSOL->indc+LUSOL->nelem); /* LUSOL_ARRAYOFFSET implied */ - LU1PQ1(LUSOL, LUSOL->n,LUSOL->m,LUSOL->lenc, - LUSOL->iq,LUSOL->iqloc,LUSOL->iqinv, - LUSOL->indc+LUSOL->nelem); /* LUSOL_ARRAYOFFSET implied */ -/* ------------------------------------------------------------------ - For TCP, Ha, Hj, Hk are allocated separately, similarly amaxr - for TRP. Then compute the factorization A = L*U. - ------------------------------------------------------------------ */ -#ifdef ClassicHamaxR - MYBOOL TRP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TRP); - MYBOOL TSP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TSP); - MYBOOL TCP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TCP); - if(TPP || TSP) { - LENH = 1; - LENA2 = LUSOL->lena; - LOCH = LUSOL->lena; - LMAXR = 1; - } - else if(TRP) { - LENH = 1; /* Dummy */ - LENA2 = LUSOL->lena-LUSOL->m; /* Reduced length of a */ - LOCH = LUSOL->lena; /* Dummy */ - LMAXR = LENA2+1; /* Start of Amaxr in a */ - } - else if(TCP) { - LENH = LUSOL->n; /* Length of heap */ - LENA2 = LUSOL->lena-LENH; /* Reduced length of a, indc, indr */ - LOCH = LENA2+1; /* Start of Ha, Hj, Hk in a, indc, indr */ - LMAXR = 1; /* Dummy */ - } - LU1FAD(LUSOL, - LENA2,LENH, - LUSOL->a+LOCH-LUSOL_ARRAYOFFSET, - LUSOL->indc+LOCH-LUSOL_ARRAYOFFSET, - LUSOL->indr+LOCH-LUSOL_ARRAYOFFSET, - LUSOL->a+LMAXR-LUSOL_ARRAYOFFSET, - INFORM,&LENL,&LENU, - &MINLEN,&MERSUM,&NUTRI,&NLTRI,&NDENS1,&NDENS2, - &NRANK,&LMAX,&UMAX,&DUMAX,&DUMIN,&AKMAX); -#else - LU1FAD(LUSOL, - INFORM,&LENL,&LENU, - &MINLEN,&MERSUM,&NUTRI,&NLTRI,&NDENS1,&NDENS2, - &NRANK,&LMAX,&UMAX,&DUMAX,&DUMIN,&AKMAX); -#endif - LUSOL->luparm[LUSOL_IP_RANK_U] = NRANK; - LUSOL->luparm[LUSOL_IP_NONZEROS_L] = LENL; - if(*INFORM==LUSOL_INFORM_ANEEDMEM) - goto x970; - if(*INFORM==LUSOL_INFORM_NOPIVOT) - goto x985; - if(*INFORM>LUSOL_INFORM_LUSUCCESS) - goto x980; - if(KEEPLU) { -/* --------------------------------------------------------------- - The LU factors are at the top of a, indc, indr, - with the columns of L and the rows of U in the order - ( free ) ... ( u3 ) ( l3 ) ( u2 ) ( l2 ) ( u1 ) ( l1 ). - Starting with ( l1 ) and ( u1 ), move the rows of U to the - left and the columns of L to the right, giving - ( u1 ) ( u2 ) ( u3 ) ... ( free ) ... ( l3 ) ( l2 ) ( l1 ). - Also, set numl0 = the number of nonempty columns of U. - --------------------------------------------------------------- */ - LU = 0; - LL = LUSOL->lena+1; -#ifdef ClassicHamaxR - LM = LENA2+1; -#else - LM = LL; -#endif - LTOPL = LL-LENL-LENU; - LROW = LENU; - for(K = 1; K <= NRANK; K++) { - I = LUSOL->ip[K]; - LENUK = -LUSOL->lenr[I]; - LUSOL->lenr[I] = LENUK; - J = LUSOL->iq[K]; - LENLK = -LUSOL->lenc[J]-1; - if(LENLK>0) { - NUML0++; - LUSOL->iqloc[NUML0] = LENLK; - } - if(LU+LENUKa[LL] = LUSOL->a[LM]; - LUSOL->indc[LL] = LUSOL->indc[LM]; - LUSOL->indr[LL] = LUSOL->indr[LM]; - } - } - else { -/* ========================================================= - There is no room for ( uk ) yet. We have to - right-shift the whole of the remaining LU file. - Note that ( lk ) ends up in the correct place. - ========================================================= */ - LLSAVE = LL-LENLK; - NMOVE = LM-LTOPL; - for(IDUMMY = 1; IDUMMY <= NMOVE; IDUMMY++) { - LL--; - LM--; - LUSOL->a[LL] = LUSOL->a[LM]; - LUSOL->indc[LL] = LUSOL->indc[LM]; - LUSOL->indr[LL] = LUSOL->indr[LM]; - } - LTOPL = LL; - LL = LLSAVE; - LM = LL; - } -/* ====================================================== - Left-shift ( uk ). - ====================================================== */ - LUSOL->locr[I] = LU+1; - L2 = LM-1; - LM = LM-LENUK; - for(L = LM; L <= L2; L++) { - LU = LU+1; - LUSOL->a[LU] = LUSOL->a[L]; - LUSOL->indr[LU] = LUSOL->indr[L]; - } - } -/* --------------------------------------------------------------- - Save the lengths of the nonempty columns of L, - and initialize locc(j) for the LU update routines. - --------------------------------------------------------------- */ - for(K = 1; K <= NUML0; K++) { - LUSOL->lenc[K] = LUSOL->iqloc[K]; - } - for(J = 1; J <= LUSOL->n; J++) { - LUSOL->locc[J] = 0; - } -/* --------------------------------------------------------------- - Test for singularity. - lu6chk sets nsing, jsing, jumin, Lmax, Umax, DUmax, DUmin - (including entries from the dense LU). - inform = 1 if there are singularities (nsing gt 0). - --------------------------------------------------------------- */ - LU6CHK(LUSOL, 1,LUSOL->lena,INFORM); - NSING = LUSOL->luparm[LUSOL_IP_SINGULARITIES]; - JSING = LUSOL->luparm[LUSOL_IP_SINGULARINDEX]; - JUMIN = LUSOL->luparm[LUSOL_IP_COLINDEX_DUMIN]; - LMAX = LUSOL->parmlu[LUSOL_RP_MAXMULT_L]; - UMAX = LUSOL->parmlu[LUSOL_RP_MAXELEM_U]; - DUMAX = LUSOL->parmlu[LUSOL_RP_MAXELEM_DIAGU]; - DUMIN = LUSOL->parmlu[LUSOL_RP_MINELEM_DIAGU]; - } - else { -/* --------------------------------------------------------------- - keepLU = 0. L and U were not kept, just the diagonals of U. - lu1fac will probably be called again soon with keepLU = .true. - 11 Mar 2001: lu6chk revised. We can call it with keepLU = 0, - but we want to keep Lmax, Umax from lu1fad. - 05 May 2002: Allow for TCP with new lu1DCP. Diag(U) starts - below lena2, not lena. Need lena2 in next line. - --------------------------------------------------------------- */ -#ifdef ClassicHamaxR - LU6CHK(LUSOL, 1,LENA2,INFORM); -#else - LU6CHK(LUSOL, 1,LUSOL->lena,INFORM); -#endif - NSING = LUSOL->luparm[LUSOL_IP_SINGULARITIES]; - JSING = LUSOL->luparm[LUSOL_IP_SINGULARINDEX]; - JUMIN = LUSOL->luparm[LUSOL_IP_COLINDEX_DUMIN]; - DUMAX = LUSOL->parmlu[LUSOL_RP_MAXELEM_DIAGU]; - DUMIN = LUSOL->parmlu[LUSOL_RP_MINELEM_DIAGU]; - } - goto x990; -/* ------------ - Error exits. - ------------ */ -x930: - *INFORM = LUSOL_INFORM_ADIMERR; - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu1fac error...\nentry a[%d] has an illegal row (%d) or column (%d) index\n", - LERR,LUSOL->indc[LERR],LUSOL->indr[LERR]); - goto x990; -x940: - *INFORM = LUSOL_INFORM_ADUPLICATE; - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu1fac error...\nentry a[%d] is a duplicate with indeces indc=%d, indr=%d\n", - LERR,LUSOL->indc[LERR],LUSOL->indr[LERR]); - goto x990; -x970: - *INFORM = LUSOL_INFORM_ANEEDMEM; - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu1fac error...\ninsufficient storage; increase lena from %d to at least %d\n", - LUSOL->lena, MINLEN); - goto x990; -x980: - *INFORM = LUSOL_INFORM_FATALERR; - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu1fac error...\nfatal bug (sorry --- this should never happen)\n"); - goto x990; -x985: - *INFORM = LUSOL_INFORM_NOPIVOT; - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu1fac error...\nTSP used but diagonal pivot could not be found\n"); - -/* Finalize and store output parameters. */ -x990: - LUSOL->nelem = NELEM0; - LUSOL->luparm[LUSOL_IP_SINGULARITIES] = NSING; - LUSOL->luparm[LUSOL_IP_SINGULARINDEX] = JSING; - LUSOL->luparm[LUSOL_IP_MINIMUMLENA] = MINLEN; - LUSOL->luparm[LUSOL_IP_UPDATECOUNT] = 0; - LUSOL->luparm[LUSOL_IP_RANK_U] = NRANK; - LUSOL->luparm[LUSOL_IP_COLCOUNT_DENSE1] = NDENS1; - LUSOL->luparm[LUSOL_IP_COLCOUNT_DENSE2] = NDENS2; - LUSOL->luparm[LUSOL_IP_COLINDEX_DUMIN] = JUMIN; - LUSOL->luparm[LUSOL_IP_COLCOUNT_L0] = NUML0; - LUSOL->luparm[LUSOL_IP_ROWCOUNT_L0] = 0; - LUSOL->luparm[LUSOL_IP_NONZEROS_L0] = LENL; - LUSOL->luparm[LUSOL_IP_NONZEROS_U0] = LENU; - LUSOL->luparm[LUSOL_IP_NONZEROS_L] = LENL; - LUSOL->luparm[LUSOL_IP_NONZEROS_U] = LENU; - LUSOL->luparm[LUSOL_IP_NONZEROS_ROW] = LROW; - LUSOL->luparm[LUSOL_IP_MARKOWITZ_MERIT] = MERSUM; - LUSOL->luparm[LUSOL_IP_TRIANGROWS_U] = NUTRI; - LUSOL->luparm[LUSOL_IP_TRIANGROWS_L] = NLTRI; - LUSOL->parmlu[LUSOL_RP_MAXELEM_A] = AMAX; - LUSOL->parmlu[LUSOL_RP_MAXMULT_L] = LMAX; - LUSOL->parmlu[LUSOL_RP_MAXELEM_U] = UMAX; - LUSOL->parmlu[LUSOL_RP_MAXELEM_DIAGU] = DUMAX; - LUSOL->parmlu[LUSOL_RP_MINELEM_DIAGU] = DUMIN; - LUSOL->parmlu[LUSOL_RP_MAXELEM_TCP] = AKMAX; - AGRWTH = AKMAX/(AMAX+LUSOL_SMALLNUM); - UGRWTH = UMAX/(AMAX+LUSOL_SMALLNUM); - if(TPP) - GROWTH = UGRWTH; -/* TRP or TCP or TSP */ - else - GROWTH = AGRWTH; - LUSOL->parmlu[LUSOL_RP_GROWTHRATE] = GROWTH; - - LUSOL->luparm[LUSOL_IP_FTRANCOUNT] = 0; - LUSOL->luparm[LUSOL_IP_BTRANCOUNT] = 0; - -/* ------------------------------------------------------------------ - Set overall status variable. - ------------------------------------------------------------------ */ - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; - if(*INFORM == LUSOL_INFORM_NOMEMLEFT) - LUSOL_report(LUSOL, 0, "lu1fac error...\ninsufficient memory available\n"); - -/* ------------------------------------------------------------------ - Print statistics for the LU factors. - ------------------------------------------------------------------ */ - NCP = LUSOL->luparm[LUSOL_IP_COMPRESSIONS_LU]; - CONDU = DUMAX/MAX(DUMIN,LUSOL_SMALLNUM); - DINCR = (LENL+LENU)-LUSOL->nelem; - DINCR = (DINCR*100)/MAX(DELEM,ONE); - AVGMER = MERSUM; - AVGMER = AVGMER/DM; - NBUMP = LUSOL->m-NUTRI-NLTRI; - if(LPRINT>=LUSOL_MSG_STATISTICS) { - if(TPP) { - LUSOL_report(LUSOL, 0, "Merit %g %d %d %d %g %d %d %g %g %d %d %d\n", - AVGMER,LENL,LENL+LENU,NCP,DINCR,NUTRI,LENU, - LTOL,UMAX,UGRWTH,NLTRI,NDENS1,LMAX); - } - else { - LUSOL_report(LUSOL, 0, "Merit %s %g %d %d %d %g %d %d %g %g %d %d %d %g %g\n", - LUSOL_pivotLabel(LUSOL), - AVGMER,LENL,LENL+LENU,NCP,DINCR,NUTRI,LENU, - LTOL,UMAX,UGRWTH,NLTRI,NDENS1,LMAX,AKMAX,AGRWTH); - } - LUSOL_report(LUSOL, 0, "bump%9d dense2%7d DUmax%g DUmin%g conDU%g\n", - NBUMP,NDENS2,DUMAX,DUMIN,CONDU); - } -} - - diff --git a/src/lpsolve/build/lp_solve/lusol2.c b/src/lpsolve/build/lp_solve/lusol2.c deleted file mode 100644 index 49fb8a21..00000000 --- a/src/lpsolve/build/lp_solve/lusol2.c +++ /dev/null @@ -1,204 +0,0 @@ - -/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - File lusol2 LUSOL heap management routines - Hbuild Hchange Hdelete Hdown Hinsert Hup - Heap-management routines for LUSOL's lu1fac. - May be useful for other applications. - ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - For LUSOL, the heap structure involves three arrays of length N. - N is the current number of entries in the heap. - Ha(1:N) contains the values that the heap is partially sorting. - For LUSOL they are double precision values -- the largest - element in each remaining column of the updated matrix. - The biggest entry is in Ha(1), the top of the heap. - Hj(1:N) contains column numbers j. - Ha(k) is the biggest entry in column j = Hj(k). - Hk(1:N) contains indices within the heap. It is the - inverse of Hj(1:N), so k = Hk(j) <=> j = Hj(k). - Column j is entry k in the heap. - hops is the number of heap operations, - i.e., the number of times an entry is moved - (the number of "hops" up or down the heap). - Together, Hj and Hk let us find values inside the heap - whenever we want to change one of the values in Ha. - For other applications, Ha may need to be some other data type, - like the keys that sort routines operate on. - ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 11 Feb 2002: MATLAB version derived from "Algorithms" by - R. Sedgewick - 03 Mar 2002: F77 version derived from MATLAB version. - 07 May 2002: Safeguard input parameters k, N, Nk. - We don't want them to be output! - 07 May 2002: Current version of lusol2.f. - ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ - -/* ================================================================== - Hdown updates heap by moving down tree from node k. - ------------------------------------------------------------------ - 01 May 2002: Need Nk for length of Hk. - 05 May 2002: Change input paramter k to kk to stop k being output. - 05 May 2002: Current version of Hdown. - ================================================================== */ -void HDOWN(LPSREAL HA[], int HJ[], int HK[], int N, int K, int *HOPS) -{ - int J, JJ, JV, N2; - LPSREAL V; - - *HOPS = 0; - V = HA[K]; - JV = HJ[K]; - N2 = N/2; -/* while 1 - break */ -x100: - if(K>N2) - goto x200; - (*HOPS)++; - J = K+K; - if(J=HA[J]) - goto x200; - HA[K] = HA[J]; - JJ = HJ[J]; - HJ[K] = JJ; - HK[JJ] = K; - K = J; - goto x100; -/* end while */ -x200: - HA[K] = V; - HJ[K] = JV; - HK[JV] = K; -} - -/* ================================================================== - Hup updates heap by moving up tree from node k. - ------------------------------------------------------------------ - 01 May 2002: Need Nk for length of Hk. - 05 May 2002: Change input paramter k to kk to stop k being output. - 05 May 2002: Current version of Hup. - ================================================================== */ -void HUP(LPSREAL HA[], int HJ[], int HK[], int K, int *HOPS) -{ - int J, JV, K2; - LPSREAL V; - - *HOPS = 0; - V = HA[K]; - JV = HJ[K]; -/* while 1 - break */ -x100: - if(K<2) - goto x200; - K2 = K/2; -/* break */ - if(V n - nrank may occur. - If keepLU = 0, - Lmax and Umax are already set by lu1fac. - The diagonals of U are in the top of A. - Only Utol1 is used in the singularity test to set w(*). - inform = 0 if A appears to have full column rank (nsing = 0). - inform = 1 otherwise (nsing .gt. 0). - ------------------------------------------------------------------ - 00 Jul 1987: Early version. - 09 May 1988: f77 version. - 11 Mar 2001: Allow for keepLU = 0. - 17 Nov 2001: Briefer output for singular factors. - 05 May 2002: Comma needed in format 1100 (via Kenneth Holmstrom). - 06 May 2002: With keepLU = 0, diags of U are in natural order. - They were not being extracted correctly. - 23 Apr 2004: TRP can judge singularity better by comparing - all diagonals to DUmax. - 27 Jun 2004: (PEG) Allow write only if nout .gt. 0. - ================================================================== */ -#ifdef UseOld_LU6CHK_20040510 -void LU6CHK(LUSOLrec *LUSOL, int MODE, int LENA2, int *INFORM) -{ - MYBOOL KEEPLU; - int I, J, JUMIN, K, L, L1, L2, LENL, LPRINT, NDEFIC, NRANK; - LPSREAL AIJ, DIAG, DUMAX, DUMIN, LMAX, UMAX, UTOL1, UTOL2; - - LPRINT = LUSOL->luparm[LUSOL_IP_PRINTLEVEL]; - KEEPLU = (MYBOOL) (LUSOL->luparm[LUSOL_IP_KEEPLU]!=0); - NRANK = LUSOL->luparm[LUSOL_IP_RANK_U]; - LENL = LUSOL->luparm[LUSOL_IP_NONZEROS_L]; - UTOL1 = LUSOL->parmlu[LUSOL_RP_SMALLDIAG_U]; - UTOL2 = LUSOL->parmlu[LUSOL_RP_EPSDIAG_U]; - *INFORM = LUSOL_INFORM_LUSUCCESS; - LMAX = ZERO; - UMAX = ZERO; - LUSOL->luparm[LUSOL_IP_SINGULARITIES] = 0; - LUSOL->luparm[LUSOL_IP_SINGULARINDEX] = 0; - JUMIN = 0; - DUMAX = ZERO; - DUMIN = LUSOL_BIGNUM; - -#ifdef LUSOLFastClear - MEMCLEAR(LUSOL->w+1, LUSOL->n); -#else - for(I = 1; I <= LUSOL->n; I++) - LUSOL->w[I] = ZERO; -#endif - - if(KEEPLU) { -/* -------------------------------------------------------------- - Find Lmax. - -------------------------------------------------------------- */ - for(L = (LENA2+1)-LENL; L <= LENA2; L++) { - SETMAX(LMAX,fabs(LUSOL->a[L])); - } -/* -------------------------------------------------------------- - Find Umax and set w(j) = maximum element in j-th column of U. - -------------------------------------------------------------- */ - for(K = 1; K <= NRANK; K++) { - I = LUSOL->ip[K]; - L1 = LUSOL->locr[I]; - L2 = (L1+LUSOL->lenr[I])-1; - for(L = L1; L <= L2; L++) { - J = LUSOL->indr[L]; - AIJ = fabs(LUSOL->a[L]); - SETMAX(LUSOL->w[J],AIJ); - SETMAX(UMAX,AIJ); - } - } -/* -------------------------------------------------------------- - Negate w(j) if the corresponding diagonal of U is - too small in absolute terms or relative to the other elements - in the same column of U. - Also find DUmax and DUmin, the extreme diagonals of U. - -------------------------------------------------------------- */ - for(K = 1; K <= LUSOL->n; K++) { - J = LUSOL->iq[K]; - if(K>NRANK) - DIAG = ZERO; - else { - I = LUSOL->ip[K]; - L1 = LUSOL->locr[I]; - DIAG = fabs(LUSOL->a[L1]); - SETMAX(DUMAX,DIAG); - if(DUMIN>DIAG) { - DUMIN = DIAG; - JUMIN = J; - } - } - if((DIAG<=UTOL1) || (DIAG<=UTOL2*LUSOL->w[J])) { - LUSOL_addSingularity(LUSOL, J, INFORM); - LUSOL->w[J] = -LUSOL->w[J]; - } - } - LUSOL->parmlu[LUSOL_RP_MAXMULT_L] = LMAX; - LUSOL->parmlu[LUSOL_RP_MAXELEM_U] = UMAX; - } - else { -/* -------------------------------------------------------------- - keepLU = 0. - Only diag(U) is stored. Set w(*) accordingly. - -------------------------------------------------------------- */ - for(K = 1; K <= LUSOL->n; K++) { - J = LUSOL->iq[K]; - if(K>NRANK) - DIAG = ZERO; - else { -/* ! diag = abs( diagU(k) ) ! 06 May 2002: Diags are in natural order */ - DIAG = fabs(LUSOL->diagU[J]); - LUSOL->w[J] = DIAG; - SETMAX(DUMAX,DIAG); - if(DUMIN>DIAG) { - DUMIN = DIAG; - JUMIN = J; - } - } - if(DIAG<=UTOL1) { - LUSOL_addSingularity(LUSOL, J, INFORM); - LUSOL->w[J] = -LUSOL->w[J]; - } - } - } -/* ----------------------------------------------------------------- - Set output parameters. - ----------------------------------------------------------------- */ - if(JUMIN==0) - DUMIN = ZERO; - LUSOL->luparm[LUSOL_IP_COLINDEX_DUMIN] = JUMIN; - LUSOL->parmlu[LUSOL_RP_MAXELEM_DIAGU] = DUMAX; - LUSOL->parmlu[LUSOL_RP_MINELEM_DIAGU] = DUMIN; -/* The matrix has been judged singular. */ - if(LUSOL->luparm[LUSOL_IP_SINGULARITIES]>0) { - *INFORM = LUSOL_INFORM_LUSINGULAR; - NDEFIC = LUSOL->n-NRANK; - if(LPRINT>=LUSOL_MSG_SINGULARITY) { - LUSOL_report(LUSOL, 0, "Singular(m%cn) rank:%9d n-rank:%8d nsing:%9d\n", - relationChar(LUSOL->m, LUSOL->n),NRANK,NDEFIC, - LUSOL->luparm[LUSOL_IP_SINGULARITIES]); - } - } -/* Exit. */ - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; -} -#else -void LU6CHK(LUSOLrec *LUSOL, int MODE, int LENA2, int *INFORM) -{ - MYBOOL KEEPLU, TRP; - int I, J, JUMIN, K, L, L1, L2, LENL, LDIAGU, LPRINT, NDEFIC, NRANK; - LPSREAL AIJ, DIAG, DUMAX, DUMIN, LMAX, UMAX, UTOL1, UTOL2; - - LPRINT = LUSOL->luparm[LUSOL_IP_PRINTLEVEL]; - KEEPLU = (MYBOOL) (LUSOL->luparm[LUSOL_IP_KEEPLU] != 0); - TRP = (MYBOOL) (LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TRP); - NRANK = LUSOL->luparm[LUSOL_IP_RANK_U]; - LENL = LUSOL->luparm[LUSOL_IP_NONZEROS_L]; - UTOL1 = LUSOL->parmlu[LUSOL_RP_SMALLDIAG_U]; - UTOL2 = LUSOL->parmlu[LUSOL_RP_EPSDIAG_U]; - *INFORM = LUSOL_INFORM_LUSUCCESS; - LMAX = ZERO; - UMAX = ZERO; - LUSOL->luparm[LUSOL_IP_SINGULARITIES] = 0; - LUSOL->luparm[LUSOL_IP_SINGULARINDEX] = 0; - JUMIN = 0; - DUMAX = ZERO; - DUMIN = LUSOL_BIGNUM; - -#ifdef LUSOLFastClear - MEMCLEAR(LUSOL->w+1, LUSOL->n); -#else - for(I = 1; I <= LUSOL->n; I++) - LUSOL->w[I] = ZERO; -#endif - - if(KEEPLU) { -/* -------------------------------------------------------------- - Find Lmax. - -------------------------------------------------------------- */ - for(L = (LENA2+1)-LENL; L <= LENA2; L++) { - SETMAX(LMAX,fabs(LUSOL->a[L])); - } -/* -------------------------------------------------------------- - Find Umax and set w(j) = maximum element in j-th column of U. - -------------------------------------------------------------- */ - for(K = 1; K <= NRANK; K++) { - I = LUSOL->ip[K]; - L1 = LUSOL->locr[I]; - L2 = (L1+LUSOL->lenr[I])-1; - for(L = L1; L <= L2; L++) { - J = LUSOL->indr[L]; - AIJ = fabs(LUSOL->a[L]); - SETMAX(LUSOL->w[J],AIJ); - SETMAX(UMAX,AIJ); - } - } - LUSOL->parmlu[LUSOL_RP_MAXMULT_L] = LMAX; - LUSOL->parmlu[LUSOL_RP_MAXELEM_U] = UMAX; -/* -------------------------------------------------------------- - Find DUmax and DUmin, the extreme diagonals of U. - -------------------------------------------------------------- */ - for(K = 1; K <= NRANK; K++) { - J = LUSOL->iq[K]; - I = LUSOL->ip[K]; - L1 = LUSOL->locr[I]; - DIAG = fabs(LUSOL->a[L1]); - SETMAX( DUMAX, DIAG ); - if(DUMIN > DIAG) { - DUMIN = DIAG; - JUMIN = J; - } - } - } - else { -/* -------------------------------------------------------------- - keepLU = 0. - Only diag(U) is stored. Set w(*) accordingly. - Find DUmax and DUmin, the extreme diagonals of U. - -------------------------------------------------------------- */ - LDIAGU = LENA2 - LUSOL->n; - for(K = 1; K <= NRANK; K++) { - J = LUSOL->iq[K]; - DIAG = fabs( LUSOL->a[LDIAGU + J] ); /* are in natural order */ - LUSOL->w[J] = DIAG; - SETMAX( DUMAX, DIAG ); - if(DUMIN > DIAG) { - DUMIN = DIAG; - JUMIN = J; - } - } - } -/* -------------------------------------------------------------- - Negate w(j) if the corresponding diagonal of U is - too small in absolute terms or relative to the other elements - in the same column of U. - - 23 Apr 2004: TRP ensures that diags are NOT small relative to - other elements in their own column. - Much better, we can compare all diags to DUmax. - -------------------------------------------------------------- */ - if((MODE == 1) && TRP) { - SETMAX( UTOL1, UTOL2*DUMAX ); - } - - if(KEEPLU) { - for(K = 1; K <= LUSOL->n; K++) { - J = LUSOL->iq[K]; - if(K>NRANK) - DIAG = ZERO; - else { - I = LUSOL->ip[K]; - L1 = LUSOL->locr[I]; - DIAG = fabs(LUSOL->a[L1]); - } - if((DIAG<=UTOL1) || (DIAG<=UTOL2*LUSOL->w[J])) { - LUSOL_addSingularity(LUSOL, J, INFORM); - LUSOL->w[J] = -LUSOL->w[J]; - } - } - } - else { /* keepLU = FALSE */ - for(K = 1; K <= LUSOL->n; K++) { - J = LUSOL->iq[K]; - DIAG = LUSOL->w[J]; - if(DIAG<=UTOL1) { - LUSOL_addSingularity(LUSOL, J, INFORM); - LUSOL->w[J] = -LUSOL->w[J]; - } - } - } -/* ----------------------------------------------------------------- - Set output parameters. - ----------------------------------------------------------------- */ - if(JUMIN==0) - DUMIN = ZERO; - LUSOL->luparm[LUSOL_IP_COLINDEX_DUMIN] = JUMIN; - LUSOL->parmlu[LUSOL_RP_MAXELEM_DIAGU] = DUMAX; - LUSOL->parmlu[LUSOL_RP_MINELEM_DIAGU] = DUMIN; -/* The matrix has been judged singular. */ - if(LUSOL->luparm[LUSOL_IP_SINGULARITIES]>0) { - *INFORM = LUSOL_INFORM_LUSINGULAR; - NDEFIC = LUSOL->n-NRANK; - if((LUSOL->outstream!=NULL) && (LPRINT>=LUSOL_MSG_SINGULARITY)) { - LUSOL_report(LUSOL, 0, "Singular(m%cn) rank:%9d n-rank:%8d nsing:%9d\n", - relationChar(LUSOL->m, LUSOL->n),NRANK,NDEFIC, - LUSOL->luparm[LUSOL_IP_SINGULARITIES]); - } - } -/* Exit. */ - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; -} -#endif - - -/* ------------------------------------------------------------------ - Include routines for row-based L0. - 20 Apr 2005 Current version - KE. - ------------------------------------------------------------------ */ -#include "lusol6l0.c" - - -/* ------------------------------------------------------------------ - lu6L solves L v = v(input). - ------------------------------------------------------------------ - 15 Dec 2002: First version derived from lu6sol. - 15 Dec 2002: Current version. - ------------------------------------------------------------------ */ -void LU6L(LUSOLrec *LUSOL, int *INFORM, LPSREAL V[], int NZidx[]) -{ - int JPIV, K, L, L1, LEN, LENL, LENL0, NUML, NUML0; - LPSREAL SMALL; - register LPSREAL VPIV; -#ifdef LUSOLFastSolve - LPSREAL *aptr; - int *iptr, *jptr; -#else - int I, J; -#endif - - NUML0 = LUSOL->luparm[LUSOL_IP_COLCOUNT_L0]; - LENL0 = LUSOL->luparm[LUSOL_IP_NONZEROS_L0]; - LENL = LUSOL->luparm[LUSOL_IP_NONZEROS_L]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - *INFORM = LUSOL_INFORM_LUSUCCESS; - L1 = LUSOL->lena+1; - for(K = 1; K <= NUML0; K++) { - LEN = LUSOL->lenc[K]; - L = L1; - L1 -= LEN; - JPIV = LUSOL->indr[L1]; - VPIV = V[JPIV]; - if(fabs(VPIV)>SMALL) { -/* ***** This loop could be coded specially. */ -#ifdef LUSOLFastSolve - L--; - for(aptr = LUSOL->a+L, iptr = LUSOL->indc+L; - LEN > 0; LEN--, aptr--, iptr--) - V[*iptr] += (*aptr) * VPIV; -#else - for(; LEN > 0; LEN--) { - L--; - I = LUSOL->indc[L]; - V[I] += LUSOL->a[L]*VPIV; - } -#endif - } -#ifdef SetSmallToZero - else - V[JPIV] = 0; -#endif - } - L = (LUSOL->lena-LENL0)+1; - NUML = LENL-LENL0; -/* ***** This loop could be coded specially. */ -#ifdef LUSOLFastSolve - L--; - for(aptr = LUSOL->a+L, jptr = LUSOL->indr+L, iptr = LUSOL->indc+L; - NUML > 0; NUML--, aptr--, jptr--, iptr--) { - if(fabs(V[*jptr])>SMALL) - V[*iptr] += (*aptr) * V[*jptr]; -#ifdef SetSmallToZero - else - V[*jptr] = 0; -#endif - } -#else - for(; NUML > 0; NUML--) { - L--; - J = LUSOL->indr[L]; - if(fabs(V[J])>SMALL) { - I = LUSOL->indc[L]; - V[I] += LUSOL->a[L]*V[J]; - } -#ifdef SetSmallToZero - else - V[J] = 0; -#endif - } -#endif -/* Exit. */ - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; -} - -/* ================================================================== - lu6LD assumes lu1fac has computed factors A = LU of a - symmetric definite or quasi-definite matrix A, - using Threshold Symmetric Pivoting (TSP), luparm(6) = 3, - or Threshold Diagonal Pivoting (TDP), luparm(6) = 4. - It also assumes that no updates have been performed. - In such cases, U = D L', where D = diag(U). - lu6LDL returns v as follows: - - mode - 1 v solves L D v = v(input). - 2 v solves L|D|v = v(input). - ------------------------------------------------------------------ - 15 Dec 2002: First version of lu6LD. - 15 Dec 2002: Current version. - ================================================================== */ -void LU6LD(LUSOLrec *LUSOL, int *INFORM, int MODE, LPSREAL V[], int NZidx[]) -{ - int IPIV, K, L, L1, LEN, NUML0; - LPSREAL DIAG, SMALL; - register LPSREAL VPIV; -#ifdef LUSOLFastSolve - LPSREAL *aptr; - int *jptr; -#else - int J; -#endif - -/* Solve L D v(new) = v or L|D|v(new) = v, depending on mode. - The code for L is the same as in lu6L, - but when a nonzero entry of v arises, we divide by - the corresponding entry of D or |D|. */ - NUML0 = LUSOL->luparm[LUSOL_IP_COLCOUNT_L0]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - *INFORM = LUSOL_INFORM_LUSUCCESS; - L1 = LUSOL->lena+1; - for(K = 1; K <= NUML0; K++) { - LEN = LUSOL->lenc[K]; - L = L1; - L1 -= LEN; - IPIV = LUSOL->indr[L1]; - VPIV = V[IPIV]; - if(fabs(VPIV)>SMALL) { -/* ***** This loop could be coded specially. */ -#ifdef LUSOLFastSolve - L--; - for(aptr = LUSOL->a+L, jptr = LUSOL->indc+L; - LEN > 0; LEN--, aptr--, jptr--) - V[*jptr] += (*aptr)*VPIV; -#else - for(; LEN > 0; LEN--) { - L--; - J = LUSOL->indc[L]; - V[J] += LUSOL->a[L]*VPIV; - } -#endif -/* Find diag = U(ipiv,ipiv) and divide by diag or |diag|. */ - L = LUSOL->locr[IPIV]; - DIAG = LUSOL->a[L]; - if(MODE==2) - DIAG = fabs(DIAG); - V[IPIV] = VPIV/DIAG; - } -#ifdef SetSmallToZero - else - V[IPIV] = 0; -#endif - } -} - - -/* ================================================================== - lu6Lt solves L'v = v(input). - ------------------------------------------------------------------ - 15 Dec 2002: First version derived from lu6sol. - 15 Dec 2002: Current version. - ================================================================== */ -void LU6LT(LUSOLrec *LUSOL, int *INFORM, LPSREAL V[], int NZidx[]) -{ -#ifdef DoTraceL0 - LPSREAL TEMP; -#endif - int K, L, L1, L2, LEN, LENL, LENL0, NUML0; - LPSREAL SMALL; - register REALXP SUM; - register LPSREAL HOLD; -#if (defined LUSOLFastSolve) && !(defined DoTraceL0) - LPSREAL *aptr; - int *iptr, *jptr; -#else - int I, J; -#endif - - NUML0 = LUSOL->luparm[LUSOL_IP_COLCOUNT_L0]; - LENL0 = LUSOL->luparm[LUSOL_IP_NONZEROS_L0]; - LENL = LUSOL->luparm[LUSOL_IP_NONZEROS_L]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - *INFORM = LUSOL_INFORM_LUSUCCESS; - L1 = (LUSOL->lena-LENL)+1; - L2 = LUSOL->lena-LENL0; - -/* ***** This loop could be coded specially. */ -#if (defined LUSOLFastSolve) && !(defined DoTraceL0) - for(L = L1, aptr = LUSOL->a+L1, iptr = LUSOL->indr+L1, jptr = LUSOL->indc+L1; - L <= L2; L++, aptr++, iptr++, jptr++) { - HOLD = V[*jptr]; - if(fabs(HOLD)>SMALL) - V[*iptr] += (*aptr)*HOLD; -#ifdef SetSmallToZero - else - V[*jptr] = 0; -#endif - } -#else - for(L = L1; L <= L2; L++) { - J = LUSOL->indc[L]; - HOLD = V[J]; - if(fabs(HOLD)>SMALL) { - I = LUSOL->indr[L]; - V[I] += LUSOL->a[L]*HOLD; - } -#ifdef SetSmallToZero - else - V[J] = 0; -#endif - } -#endif - - /* Do row-based L0 version, if available */ - if((LUSOL->L0 != NULL) || - ((LUSOL->luparm[LUSOL_IP_BTRANCOUNT] == 0) && LU1L0(LUSOL, &(LUSOL->L0), INFORM))) { - LU6L0T_v(LUSOL, LUSOL->L0, V, NZidx, INFORM); - } - - /* Alternatively, do the standard column-based L0 version */ - else { - /* Perform loop over columns */ - for(K = NUML0; K >= 1; K--) { - SUM = ZERO; - LEN = LUSOL->lenc[K]; - L1 = L2+1; - L2 += LEN; -/* ***** This loop could be coded specially. */ -#if (defined LUSOLFastSolve) && !(defined DoTraceL0) - for(L = L1, aptr = LUSOL->a+L1, jptr = LUSOL->indc+L1; - L <= L2; L++, aptr++, jptr++) - SUM += (*aptr) * V[*jptr]; -#else - for(L = L1; L <= L2; L++) { - J = LUSOL->indc[L]; -#ifndef DoTraceL0 - SUM += LUSOL->a[L]*V[J]; -#else - TEMP = V[LUSOL->indr[L1]] + SUM; - SUM += LUSOL->a[L]*V[J]; - printf("V[%3d] = V[%3d] + L[%d,%d]*V[%3d]\n", LUSOL->indr[L1], LUSOL->indr[L1], J,LUSOL->indr[L1], J); - printf("%6g = %6g + %6g*%6g\n", V[LUSOL->indr[L1]] + SUM, TEMP, LUSOL->a[L], V[J]); -#endif - } -#endif - V[LUSOL->indr[L1]] += (LPSREAL) SUM; - } - } - -/* Exit. */ - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; -} - -void print_L0(LUSOLrec *LUSOL) -{ - int I, J, K, L, L1, L2, LEN, LENL0, NUML0; - LPSREAL *denseL0 = (LPSREAL*) calloc(LUSOL->m+1, (LUSOL->n+1)*sizeof(*denseL0)); - - NUML0 = LUSOL->luparm[LUSOL_IP_COLCOUNT_L0]; - LENL0 = LUSOL->luparm[LUSOL_IP_NONZEROS_L0]; - - L2 = LUSOL->lena-LENL0; - for(K = NUML0; K >= 1; K--) { - LEN = LUSOL->lenc[K]; - L1 = L2+1; - L2 += LEN; - for(L = L1; L <= L2; L++) { - I = LUSOL->indc[L]; - I = LUSOL->ipinv[I]; /* Undo row mapping */ - J = LUSOL->indr[L]; - denseL0[(LUSOL->n+1)*(J-1) + I] = LUSOL->a[L]; - } - } - - for(I = 1; I <= LUSOL->n; I++) { - for(J = 1; J <= LUSOL->m; J++) - fprintf(stdout, "%10g", denseL0[(LUSOL->n+1)*(J-1) + I]); - fprintf(stdout, "\n"); - } - LUSOL_FREE(denseL0); -} - - -/* ------------------------------------------------------------------ - Include routines for column-based U. - 5 Feb 2006 Current version - KE. - ------------------------------------------------------------------ */ -#include "lusol6u.c" - - -/* ================================================================== - lu6U solves U w = v. v is not altered. - ------------------------------------------------------------------ - 15 Dec 2002: First version derived from lu6sol. - 15 Dec 2002: Current version. - ================================================================== */ -void LU6U(LUSOLrec *LUSOL, int *INFORM, LPSREAL V[], LPSREAL W[], int NZidx[]) -{ - /* Do column-based U version, if available */ - if((LUSOL->U != NULL) || - ((LUSOL->luparm[LUSOL_IP_FTRANCOUNT] == 0) && LU1U0(LUSOL, &(LUSOL->U), INFORM))) { - LU6U0_v(LUSOL, LUSOL->U, V, W, NZidx, INFORM); - } - /* Alternatively, do the standard column-based L0 version */ - else { - int I, J, K, KLAST, L, L1, L2, L3, NRANK, NRANK1; - LPSREAL SMALL; - register REALXP T; -#ifdef LUSOLFastSolve - LPSREAL *aptr; - int *jptr; -#endif - NRANK = LUSOL->luparm[LUSOL_IP_RANK_U]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - *INFORM = LUSOL_INFORM_LUSUCCESS; - NRANK1 = NRANK+1; -/* Find the first nonzero in v(1:nrank), counting backwards. */ - for(KLAST = NRANK; KLAST >= 1; KLAST--) { - I = LUSOL->ip[KLAST]; - if(fabs(V[I])>SMALL) - break; - } - L = LUSOL->n; -#ifdef LUSOLFastSolve - for(K = KLAST+1, jptr = LUSOL->iq+K; K <= L; K++, jptr++) - W[*jptr] = ZERO; -#else - for(K = KLAST+1; K <= L; K++) { - J = LUSOL->iq[K]; - W[J] = ZERO; - } -#endif -/* Do the back-substitution, using rows 1:klast of U. */ - for(K = KLAST; K >= 1; K--) { - I = LUSOL->ip[K]; - T = V[I]; - L1 = LUSOL->locr[I]; - L2 = L1+1; - L3 = (L1+LUSOL->lenr[I])-1; -/* ***** This loop could be coded specially. */ -#ifdef LUSOLFastSolve - for(L = L2, aptr = LUSOL->a+L2, jptr = LUSOL->indr+L2; - L <= L3; L++, aptr++, jptr++) - T -= (*aptr) * W[*jptr]; -#else - for(L = L2; L <= L3; L++) { - J = LUSOL->indr[L]; - T -= LUSOL->a[L]*W[J]; - } -#endif - J = LUSOL->iq[K]; - if(fabs((LPSREAL) T)<=SMALL) - T = ZERO; - else - T /= LUSOL->a[L1]; - W[J] = (LPSREAL) T; - } -/* Compute residual for overdetermined systems. */ - T = ZERO; - for(K = NRANK1; K <= LUSOL->m; K++) { - I = LUSOL->ip[K]; - T += fabs(V[I]); - } -/* Exit. */ - if(T>ZERO) - *INFORM = LUSOL_INFORM_LUSINGULAR; - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; - LUSOL->parmlu[LUSOL_RP_RESIDUAL_U] = (LPSREAL) T; - } -} - -/* ================================================================== - lu6Ut solves U'v = w. w is destroyed. - ------------------------------------------------------------------ - 15 Dec 2002: First version derived from lu6sol. - 15 Dec 2002: Current version. - ================================================================== */ -void LU6UT(LUSOLrec *LUSOL, int *INFORM, LPSREAL V[], LPSREAL W[], int NZidx[]) -{ - int I, J, K, L, L1, L2, NRANK, NRANK1, - *ip = LUSOL->ip + 1, *iq = LUSOL->iq + 1; - LPSREAL SMALL; - register LPSREAL T; -#ifdef LUSOLFastSolve - LPSREAL *aptr; - int *jptr; -#endif - - NRANK = LUSOL->luparm[LUSOL_IP_RANK_U]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - *INFORM = LUSOL_INFORM_LUSUCCESS; - NRANK1 = NRANK+1; - L = LUSOL->m; -#ifdef LUSOLFastSolve - for(K = NRANK1, jptr = LUSOL->ip+K; K <= L; K++, jptr++) - V[*jptr] = ZERO; -#else - for(K = NRANK1; K <= L; K++) { - I = LUSOL->ip[K]; - V[I] = ZERO; - } -#endif -/* Do the forward-substitution, skipping columns of U(transpose) - when the associated element of w(*) is negligible. */ -#if 0 - for(K = 1; K <= NRANK; K++) { - I = LUSOL->ip[K]; - J = LUSOL->iq[K]; -#else - for(K = 1; K <= NRANK; K++, ip++, iq++) { - I = *ip; - J = *iq; -#endif - T = W[J]; - if(fabs(T)<=SMALL) { - V[I] = ZERO; - continue; - } - L1 = LUSOL->locr[I]; - T /= LUSOL->a[L1]; - V[I] = T; - L2 = (L1+LUSOL->lenr[I])-1; - L1++; -/* ***** This loop could be coded specially. */ -#ifdef LUSOLFastSolve - for(L = L1, aptr = LUSOL->a+L1, jptr = LUSOL->indr+L1; - L <= L2; L++, aptr++, jptr++) - W[*jptr] -= T * (*aptr); -#else - for(L = L1; L <= L2; L++) { - J = LUSOL->indr[L]; - W[J] -= T*LUSOL->a[L]; - } -#endif - } -/* Compute residual for overdetermined systems. */ - T = ZERO; - for(K = NRANK1; K <= LUSOL->n; K++) { - J = LUSOL->iq[K]; - T += fabs(W[J]); - } -/* Exit. */ - if(T>ZERO) - *INFORM = LUSOL_INFORM_LUSINGULAR; - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; - LUSOL->parmlu[LUSOL_RP_RESIDUAL_U] = T; -} - -/* ================================================================== - lu6sol uses the factorization A = L U as follows: - ------------------------------------------------------------------ - mode - 1 v solves L v = v(input). w is not touched. - 2 v solves L'v = v(input). w is not touched. - 3 w solves U w = v. v is not altered. - 4 v solves U'v = w. w is destroyed. - 5 w solves A w = v. v is altered as in 1. - 6 v solves A'v = w. w is destroyed. - - If mode = 3,4,5,6, v and w must not be the same arrays. - If lu1fac has just been used to factorize a symmetric matrix A - (which must be definite or quasi-definite), the factors A = L U - may be regarded as A = LDL', where D = diag(U). In such cases, - - mode - 7 v solves A v = L D L'v = v(input). w is not touched. - 8 v solves L |D| L'v = v(input). w is not touched. - - ip(*), iq(*) hold row and column numbers in pivotal order. - lenc(k) is the length of the k-th column of initial L. - lenr(i) is the length of the i-th row of U. - locc(*) is not used. - locr(i) is the start of the i-th row of U. - - U is assumed to be in upper-trapezoidal form (nrank by n). - The first entry for each row is the diagonal element - (according to the permutations ip, iq). It is stored at - location locr(i) in a(*), indr(*). - - On exit, inform = 0 except as follows. - if(mode = 3,4,5,6 and if U (and hence A) is singular,) - inform = 1 if there is a nonzero residual in solving the system - involving U. parmlu(20) returns the norm of the residual. - ------------------------------------------------------------------ - July 1987: Early version. - 09 May 1988: f77 version. - 27 Apr 2000: Abolished the dreaded "computed go to". - But hard to change other "go to"s to "if then else". - 15 Dec 2002: lu6L, lu6Lt, lu6U, lu6Ut added to modularize lu6sol. - ================================================================== */ -void LU6SOL(LUSOLrec *LUSOL, int MODE, LPSREAL V[], LPSREAL W[], int NZidx[], int *INFORM) -{ - if(MODE==LUSOL_SOLVE_Lv_v) { /* Solve L v(new) = v. */ - LU6L(LUSOL, INFORM,V, NZidx); - } - else if(MODE==LUSOL_SOLVE_Ltv_v) { /* Solve L'v(new) = v. */ - LU6LT(LUSOL, INFORM,V, NZidx); - } - else if(MODE==LUSOL_SOLVE_Uw_v) { /* Solve U w = v. */ - LU6U(LUSOL, INFORM,V,W, NZidx); - } - else if(MODE==LUSOL_SOLVE_Utv_w) { /* Solve U'v = w. */ - LU6UT(LUSOL, INFORM,V,W, NZidx); - } - else if(MODE==LUSOL_SOLVE_Aw_v) { /* Solve A w = v (i.e. FTRAN) */ - LU6L(LUSOL, INFORM,V, NZidx); /* via L v(new) = v */ - LU6U(LUSOL, INFORM,V,W, NULL); /* ... and U w = v(new). */ - } - else if(MODE==LUSOL_SOLVE_Atv_w) { /* Solve A'v = w (i.e. BTRAN) */ - LU6UT(LUSOL, INFORM,V,W, NZidx); /* via U'v = w */ - LU6LT(LUSOL, INFORM,V, NULL); /* ... and L'v(new) = v. */ - } - else if(MODE==LUSOL_SOLVE_Av_v) { /* Solve LDv(bar) = v */ - LU6LD(LUSOL, INFORM,1,V, NZidx); /* and L'v(new) = v(bar). */ - LU6LT(LUSOL, INFORM,V, NULL); - } - else if(MODE==LUSOL_SOLVE_LDLtv_v) { /* Solve L|D|v(bar) = v */ - LU6LD(LUSOL, INFORM,2,V, NZidx); /* and L'v(new) = v(bar). */ - LU6LT(LUSOL, INFORM,V, NULL); - } -} - diff --git a/src/lpsolve/build/lp_solve/lusol6l0.c b/src/lpsolve/build/lp_solve/lusol6l0.c deleted file mode 100644 index 1381af13..00000000 --- a/src/lpsolve/build/lp_solve/lusol6l0.c +++ /dev/null @@ -1,163 +0,0 @@ - -/* Create a row-based version of L0. - This makes it possible to solve L0'x=h (btran) faster for sparse h, - since we only run down the columns of L0' (rows of LO) for which - the corresponding entry in h is non-zero. */ -MYBOOL LU1L0(LUSOLrec *LUSOL, LUSOLmat **mat, int *inform) -{ - MYBOOL status = FALSE; - int K, L, LL, L1, L2, LENL0, NUML0, I; - int *lsumr; - - /* Assume success */ - *inform = LUSOL_INFORM_LUSUCCESS; - - /* Check if there is anything worth doing */ - if(mat == NULL) - return( status ); - if(*mat != NULL) - LUSOL_matfree(mat); - NUML0 = LUSOL->luparm[LUSOL_IP_COLCOUNT_L0]; - LENL0 = LUSOL->luparm[LUSOL_IP_NONZEROS_L0]; - if((NUML0 == 0) || (LENL0 == 0) || - (LUSOL->luparm[LUSOL_IP_ACCELERATION] == LUSOL_BASEORDER) || - ((LUSOL->luparm[LUSOL_IP_ACCELERATION] & LUSOL_ACCELERATE_L0) == 0)) - return( status ); - - /* Allocate temporary array */ - lsumr = (int *) LUSOL_CALLOC((LUSOL->m+1), sizeof(*lsumr)); - if(lsumr == NULL) { - *inform = LUSOL_INFORM_NOMEMLEFT; - return( status ); - } - - /* Compute non-zero counts by permuted row index (order is unimportant) */ - K = 0; - L2 = LUSOL->lena; - L1 = L2-LENL0+1; - for(L = L1; L <= L2; L++) { - I = LUSOL->indc[L]; - lsumr[I]++; - if(lsumr[I] == 1) - K++; - } - LUSOL->luparm[LUSOL_IP_ROWCOUNT_L0] = K; - - /* Check if we should apply "smarts" before proceeding to the row matrix creation */ - if((LUSOL->luparm[LUSOL_IP_ACCELERATION] & LUSOL_AUTOORDER) && - ((LPSREAL) LUSOL->luparm[LUSOL_IP_ROWCOUNT_L0] / -#if 0 - LUSOL->luparm[LUSOL_IP_COLCOUNT_L0] -#else - LUSOL->m -#endif - > LUSOL->parmlu[LUSOL_RP_SMARTRATIO])) - goto Finish; - - /* We are Ok to create the new matrix object */ - *mat = LUSOL_matcreate(LUSOL->m, LENL0); - if(*mat == NULL) { - *inform = LUSOL_INFORM_NOMEMLEFT; - goto Finish; - } - - /* Cumulate row counts to get vector offsets; first row is leftmost - (stick with Fortran array offset for consistency) */ - (*mat)->lenx[0] = 1; - for(K = 1; K <= LUSOL->m; K++) { - (*mat)->lenx[K] = (*mat)->lenx[K-1] + lsumr[K]; - lsumr[K] = (*mat)->lenx[K-1]; - } - - /* Map the matrix into row order by permuted index; - Note: The first permuted row is located leftmost in the array. - The column order is irrelevant, since the indeces will - refer to constant / resolved values of V[] during solve. */ - L2 = LUSOL->lena; - L1 = L2-LENL0+1; - for(L = L1; L <= L2; L++) { - I = LUSOL->indc[L]; - LL = lsumr[I]++; - (*mat)->a[LL] = LUSOL->a[L]; - (*mat)->indr[LL] = LUSOL->indr[L]; - (*mat)->indc[LL] = I; - } - - /* Pack row starting positions, and set mapper from original index to packed */ - I = 0; - for(L = 1; L <= LUSOL->m; L++) { - K = LUSOL->ip[L]; - if((*mat)->lenx[K] > (*mat)->lenx[K-1]) { - I++; - (*mat)->indx[I] = K; - } - } - - /* Confirm that everything went well */ - status = TRUE; - - /* Clean up */ -Finish: - FREE(lsumr); - return( status ); -} - -/* Solve L0' v = v based on row-based version of L0, constructed by LU1L0 */ -void LU6L0T_v(LUSOLrec *LUSOL, LUSOLmat *mat, LPSREAL V[], int NZidx[], int *INFORM) -{ -#ifdef DoTraceL0 - LPSREAL TEMP; -#endif - int LEN, K, KK, L, L1, NUML0; - LPSREAL SMALL; - register LPSREAL VPIV; -#if (defined LUSOLFastSolve) && !(defined DoTraceL0) - LPSREAL *aptr; - int *jptr; -#else - int J; -#endif - - NUML0 = LUSOL->luparm[LUSOL_IP_ROWCOUNT_L0]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - - /* Loop over the nz columns of L0' - from the end, going forward. */ - for(K = NUML0; K > 0; K--) { - KK = mat->indx[K]; - L = mat->lenx[KK]; - L1 = mat->lenx[KK-1]; - LEN = L - L1; - if(LEN == 0) - continue; - /* Get value of the corresponding active entry of V[] */ - VPIV = V[KK]; - /* Only process the column of L0' if the value of V[] is non-zero */ - if(fabs(VPIV)>SMALL) { -/* ***** This loop could be coded specially. */ -#if (defined LUSOLFastSolve) && !(defined DoTraceL0) - L--; - for(aptr = mat->a+L, jptr = mat->indr+L; - LEN > 0; LEN--, aptr--, jptr--) - V[*jptr] += VPIV * (*aptr); -#else - for(; LEN > 0; LEN--) { - L--; - J = mat->indr[L]; -#ifndef DoTraceL0 - V[J] += VPIV * mat->a[L]; -#else - TEMP = V[J]; - V[J] += VPIV * mat->a[L]; - printf("V[%3d] = V[%3d] + L[%d,%d]*V[%3d]\n", J, J, KK,J, KK); - printf("%6g = %6g + %6g*%6g\n", V[J], TEMP, mat->a[L], VPIV); -#endif - } -#endif - } -#ifdef SetSmallToZero - else - V[KK] = 0; -#endif - } - -} diff --git a/src/lpsolve/build/lp_solve/lusol6u.c b/src/lpsolve/build/lp_solve/lusol6u.c deleted file mode 100644 index 2244aefc..00000000 --- a/src/lpsolve/build/lp_solve/lusol6u.c +++ /dev/null @@ -1,176 +0,0 @@ - -/* Create a column-based version of U. - This makes it possible to solve Ux=h (ftran) faster for sparse h, - since we only run down the rows of U (columns of U') for which - the corresponding entry in h is non-zero. */ -MYBOOL LU1U0(LUSOLrec *LUSOL, LUSOLmat **mat, int *inform) -{ - MYBOOL status = FALSE; - int K, L, LL, LENU, NUMU, J; - int *lsumc; - - /* Assume success */ - *inform = LUSOL_INFORM_LUSUCCESS; - - /* Check if there is anything worth doing */ - if(mat == NULL) - return( status ); - if(*mat != NULL) - LUSOL_matfree(mat); - NUMU = LUSOL->luparm[LUSOL_IP_RANK_U]; - LENU = LUSOL->luparm[LUSOL_IP_NONZEROS_U]; - if((NUMU == 0) || (LENU == 0) || - (LUSOL->luparm[LUSOL_IP_ACCELERATION] == LUSOL_BASEORDER) || - ((LUSOL->luparm[LUSOL_IP_ACCELERATION] & LUSOL_ACCELERATE_U) == 0)) - return( status ); - - /* Allocate temporary array */ - lsumc = (int *) LUSOL_CALLOC((LUSOL->n+1), sizeof(*lsumc)); - if(lsumc == NULL) { - *inform = LUSOL_INFORM_NOMEMLEFT; - return( status ); - } - - /* Compute non-zero counts by permuted column index (order is unimportant) */ - for(L = 1; L <= LENU; L++) { - J = LUSOL->indr[L]; - lsumc[J]++; - } - - /* Check if we should apply "smarts" before proceeding to the column matrix creation */ - if((LUSOL->luparm[LUSOL_IP_ACCELERATION] & LUSOL_AUTOORDER) && - ((LPSREAL) sqrt((LPSREAL) NUMU/LENU) > LUSOL->parmlu[LUSOL_RP_SMARTRATIO])) - goto Finish; - - /* We are Ok to create the new matrix object */ - *mat = LUSOL_matcreate(LUSOL->n, LENU); - if(*mat == NULL) { - *inform = LUSOL_INFORM_NOMEMLEFT; - goto Finish; - } - - /* Cumulate row counts to get vector offsets; first column is leftmost - (stick with Fortran array offset for consistency) */ - (*mat)->lenx[0] = 1; - for(K = 1; K <= LUSOL->n; K++) { - (*mat)->lenx[K] = (*mat)->lenx[K-1] + lsumc[K]; - lsumc[K] = (*mat)->lenx[K-1]; - } - - /* Map the matrix into column order by permuted index; - Note: The first permuted column is located leftmost in the array. - The row order is irrelevant, since the indeces will - refer to constant / resolved values of V[] during solve. */ - for(L = 1; L <= LENU; L++) { - J = LUSOL->indr[L]; - LL = lsumc[J]++; - (*mat)->a[LL] = LUSOL->a[L]; - (*mat)->indr[LL] = J; - (*mat)->indc[LL] = LUSOL->indc[L]; - } - - /* Pack column starting positions, and set mapper from original index to packed */ - J = 0; - for(L = 1; L <= LUSOL->n; L++) { - K = LUSOL->iq[L]; -#if 1 /* Deactivate to produce a full-rank version (implicit unit diagonals) */ - if((*mat)->lenx[K] > (*mat)->lenx[K-1]) -#endif - { - J++; - (*mat)->indx[J] = K; - } - } - - /* Confirm that everything went well */ - status = TRUE; - - /* Clean up */ -Finish: - FREE(lsumc); - return( status ); -} - -/* Solve U w = v based on column-based version of U, constructed by LU1U0 */ -void LU6U0_v(LUSOLrec *LUSOL, LUSOLmat *mat, LPSREAL V[], LPSREAL W[], int NZidx[], int *INFORM) -{ -#ifdef DoTraceU0 - LPSREAL TEMP; -#endif - int LEN, I, K, L, L1, NRANK, NRANK1, KLAST; - LPSREAL SMALL; - register LPSREAL T; -#if (defined xxLUSOLFastSolve) && !(defined DoTraceU0) - LPSREAL *aptr; - int *jptr; -#else - int J; -#endif - - NRANK = LUSOL->luparm[LUSOL_IP_RANK_U]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - *INFORM = LUSOL_INFORM_LUSUCCESS; - NRANK1 = NRANK+1; -/* Find the first nonzero in v(1:nrank), counting backwards. */ - for(KLAST = NRANK; KLAST >= 1; KLAST--) { - I = LUSOL->ip[KLAST]; - if(fabs(V[I])>SMALL) - break; - } - L = LUSOL->n; -#ifdef xxLUSOLFastSolve - for(K = KLAST+1, jptr = LUSOL->iq+K; K <= L; K++, jptr++) - W[*jptr] = ZERO; -#else - for(K = KLAST+1; K <= L; K++) { - J = LUSOL->iq[K]; - W[J] = ZERO; - } -#endif - /* Loop over the nz columns of U - from the right, going left. */ - for(K = NRANK; K > 0; K--) { - I = mat->indx[K]; - L = mat->lenx[I]; - L1 = mat->lenx[I-1]; - LEN = L - L1; - T = V[I]; - if(fabs(T)<=SMALL) { - W[K] = ZERO; - continue; - } - T /= mat->a[L1]; /* Should it be L or L1 ? */ - W[K] = T; - LEN--; -/* ***** This loop could be coded specially. */ -#ifdef xxLUSOLFastSolve - L--; - for(aptr = mat->a+L, jptr = mat->indc+L; - LEN > 0; LEN--, aptr--, jptr--) - V[*jptr] -= T * (*aptr); -#else - for(; LEN > 0; LEN--) { - L--; - J = mat->indc[L]; -#ifndef DoTraceL0 - V[J] -= T * mat->a[L]; -#else - TEMP = V[J]; - V[J] += T * mat->a[L]; - printf("V[%3d] = V[%3d] + L[%d,%d]*V[%3d]\n", J, J, I,J, I); - printf("%6g = %6g + %6g*%6g\n", V[J], TEMP, mat->a[L], T); -#endif - } -#endif - } -/* Compute residual for overdetermined systems. */ - T = ZERO; - for(K = NRANK1; K <= LUSOL->m; K++) { - I = LUSOL->ip[K]; - T += fabs(V[I]); - } -/* Exit. */ - if(T>ZERO) - *INFORM = LUSOL_INFORM_LUSINGULAR; - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; - LUSOL->parmlu[LUSOL_RP_RESIDUAL_U] = (LPSREAL) T; -} diff --git a/src/lpsolve/build/lp_solve/lusol7a.c b/src/lpsolve/build/lp_solve/lusol7a.c deleted file mode 100644 index 128362f8..00000000 --- a/src/lpsolve/build/lp_solve/lusol7a.c +++ /dev/null @@ -1,704 +0,0 @@ - -/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - File lusol7a - lu7add lu7cyc lu7elm lu7for lu7rnk lu7zap - Utilities for LUSOL's update routines. - lu7for is the most important -- the forward sweep. - 01 May 2002: Derived from LUSOL's original lu7a.f file. - 01 May 2002: Current version of lusol7a.f. - ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ - -/* ================================================================== - lu7add inserts the first nrank elements of the vector v(*) - as column jadd of U. We assume that U does not yet have any - entries in this column. - Elements no larger than parmlu(3) are treated as zero. - klast will be set so that the last row to be affected - (in pivotal order) is row ip(klast). - ------------------------------------------------------------------ - 09 May 1988: First f77 version. - ================================================================== */ -void LU7ADD(LUSOLrec *LUSOL, int JADD, LPSREAL V[], int LENL, int *LENU, - int *LROW, int NRANK, int *INFORM, int *KLAST, LPSREAL *VNORM) -{ - LPSREAL SMALL; - int K, I, LENI, MINFRE, NFREE, LR1, LR2, L; -#ifndef LUSOLFastMove - int J; -#endif - - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - *VNORM = ZERO; - *KLAST = 0; - for(K = 1; K <= NRANK; K++) { - I = LUSOL->ip[K]; - if(fabs(V[I])<=SMALL) - continue; - *KLAST = K; - (*VNORM) += fabs(V[I]); - LENI = LUSOL->lenr[I]; -/* Compress row file if necessary. */ - MINFRE = LENI+1; - NFREE = LUSOL->lena - LENL - *LROW; - if(NFREEm, TRUE,LROW,LUSOL->indr,LUSOL->lenr,LUSOL->locr); - NFREE = LUSOL->lena - LENL - *LROW; - if(NFREElocr[I] = (*LROW) + 1; - LR1 = LUSOL->locr[I]; - LR2 = (LR1+LENI)-1; - if(LR2==*LROW) - goto x150; - if(LUSOL->indr[LR2+1]==0) - goto x180; - LUSOL->locr[I] = (*LROW) + 1; -#ifdef LUSOLFastMove - L = LR2-LR1+1; - if(L > 0) { - LR2 = (*LROW)+1; - MEMMOVE(LUSOL->a+LR2, LUSOL->a+LR1, L); - MEMMOVE(LUSOL->indr+LR2, LUSOL->indr+LR1, L); - MEMCLEAR(LUSOL->indr+LR1, L); - *LROW += L; - } -#else - for(L = LR1; L <= LR2; L++) { - (*LROW)++; - LUSOL->a[*LROW] = LUSOL->a[L]; - J = LUSOL->indr[L]; - LUSOL->indr[L] = 0; - LUSOL->indr[*LROW] = J; - } -#endif -x150: - LR2 = *LROW; - (*LROW)++; -/* Add the element of v. */ -x180: - LR2++; - LUSOL->a[LR2] = V[I]; - LUSOL->indr[LR2] = JADD; - LUSOL->lenr[I] = LENI+1; - (*LENU)++; - } -/* Normal exit. */ - *INFORM = LUSOL_INFORM_LUSUCCESS; - goto x990; -/* Not enough storage. */ -x970: - *INFORM = LUSOL_INFORM_ANEEDMEM; -x990: -; -} - -/* ================================================================== - lu7cyc performs a cyclic permutation on the row or column ordering - stored in ip, moving entry kfirst down to klast. - If kfirst .ge. klast, lu7cyc should not be called. - Sometimes klast = 0 and nothing should happen. - ------------------------------------------------------------------ - 09 May 1988: First f77 version. - ================================================================== */ -void LU7CYC(LUSOLrec *LUSOL, int KFIRST, int KLAST, int IX[]) -{ - if(KFIRST 0, y has just become column jelm of the matrix A. - lu7elm should not be called unless m is greater than nrank. - inform = 0 if y contained no subdiagonal nonzeros to eliminate. - inform = 1 if y contained at least one nontrivial subdiagonal. - inform = 7 if there is insufficient storage. - ------------------------------------------------------------------ - 09 May 1988: First f77 version. - No longer calls lu7for at end. lu8rpc, lu8mod do so. - ================================================================== */ -void LU7ELM(LUSOLrec *LUSOL, int JELM, LPSREAL V[], int *LENL, - int *LROW, int NRANK, int *INFORM, LPSREAL *DIAG) -{ - LPSREAL VI, VMAX, SMALL; - int NRANK1, MINFRE, NFREE, KMAX, L, K, I, LMAX, IMAX, L1, L2; - -#ifdef ForceInitialization - LMAX = 0; -#endif - - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - NRANK1 = NRANK+1; - *DIAG = ZERO; -/* Compress row file if necessary. */ - MINFRE = LUSOL->m-NRANK; - NFREE = LUSOL->lena-(*LENL)-(*LROW); - if(NFREE>=MINFRE) - goto x100; - LU1REC(LUSOL, LUSOL->m,TRUE,LROW,LUSOL->indr,LUSOL->lenr,LUSOL->locr); - NFREE = LUSOL->lena-(*LENL)-(*LROW); - if(NFREElena-(*LENL))+1; - for(K = NRANK1; K <= LUSOL->m; K++) { - I = LUSOL->ip[K]; - VI = fabs(V[I]); - if(VI<=SMALL) - continue; - L--; - LUSOL->a[L] = V[I]; - LUSOL->indc[L] = I; - if(VMAX>=VI) - continue; - VMAX = VI; - KMAX = K; - LMAX = L; - } - if(KMAX==0) - goto x900; -/* ------------------------------------------------------------------ - Remove vmax by overwriting it with the last packed v(i). - Then set the multipliers in L for the other elements. - ------------------------------------------------------------------ */ - IMAX = LUSOL->ip[KMAX]; - VMAX = LUSOL->a[LMAX]; - LUSOL->a[LMAX] = LUSOL->a[L]; - LUSOL->indc[LMAX] = LUSOL->indc[L]; - L1 = L+1; - L2 = LUSOL->lena-(*LENL); - *LENL = ((*LENL)+L2)-L; - for(L = L1; L <= L2; L++) { - LUSOL->a[L] /= -VMAX; - LUSOL->indr[L] = IMAX; - } -/* Move the row containing vmax to pivotal position nrank + 1. */ - LUSOL->ip[KMAX] = LUSOL->ip[NRANK1]; - LUSOL->ip[NRANK1] = IMAX; - *DIAG = VMAX; -/* ------------------------------------------------------------------ - If jelm is positive, insert vmax into a new row of U. - This is now the only subdiagonal element. - ------------------------------------------------------------------ */ - if(JELM>0) { - (*LROW)++; - LUSOL->locr[IMAX] = *LROW; - LUSOL->lenr[IMAX] = 1; - LUSOL->a[*LROW] = VMAX; - LUSOL->indr[*LROW] = JELM; - } - *INFORM = LUSOL_INFORM_LUSINGULAR; - goto x990; -/* No elements to eliminate. */ -x900: - *INFORM = LUSOL_INFORM_LUSUCCESS; - goto x990; -/* Not enough storage. */ -x970: - *INFORM = LUSOL_INFORM_ANEEDMEM; -x990: -; -} - -/* ================================================================== - lu7for (forward sweep) updates the LU factorization A = L*U - when row iw = ip(klast) of U is eliminated by a forward - sweep of stabilized row operations, leaving ip * U * iq upper - triangular. - The row permutation ip is updated to preserve stability and/or - sparsity. The column permutation iq is not altered. - kfirst is such that row ip(kfirst) is the first row involved - in eliminating row iw. (Hence, kfirst marks the first nonzero - in row iw in pivotal order.) If kfirst is unknown it may be - input as 1. - klast is such that row ip(klast) is the row being eliminated. - klast is not altered. - lu7for should be called only if kfirst .le. klast. - If kfirst = klast, there are no nonzeros to eliminate, but the - diagonal element of row ip(klast) may need to be moved to the - front of the row. - ------------------------------------------------------------------ - On entry, locc(*) must be zero. - - On exit: - inform = 0 if row iw has a nonzero diagonal (could be small). - inform = 1 if row iw has no diagonal. - inform = 7 if there is not enough storage to finish the update. - - On a successful exit (inform le 1), locc(*) will again be zero. - ------------------------------------------------------------------ - Jan 1985: Final f66 version. - 09 May 1988: First f77 version. - ================================================================== */ -void LU7FOR(LUSOLrec *LUSOL, int KFIRST, int KLAST, int *LENL, int *LENU, - int *LROW, int *INFORM, LPSREAL *DIAG) -{ - MYBOOL SWAPPD; - int KBEGIN, IW, LENW, LW1, LW2, JFIRST, MINFRE, NFREE, L, J, KSTART, KSTOP, K, - LFIRST, IV, LENV, LV1, JLAST, LV2, LV3, LV, JV, LW, LDIAG, LIMIT; - LPSREAL AMULT, LTOL, USPACE, SMALL, VJ, WJ; - - LTOL = LUSOL->parmlu[LUSOL_RP_UPDATEMAX_Lij]; - SMALL = LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE]; - USPACE = LUSOL->parmlu[LUSOL_RP_COMPSPACE_U]; - KBEGIN = KFIRST; - SWAPPD = FALSE; - -/* We come back here from below if a row interchange is performed. */ -x100: - IW = LUSOL->ip[KLAST]; - LENW = LUSOL->lenr[IW]; - if(LENW==0) - goto x910; - LW1 = LUSOL->locr[IW]; - LW2 = (LW1+LENW)-1; - JFIRST = LUSOL->iq[KBEGIN]; - if(KBEGIN>=KLAST) - goto x700; -/* Make sure there is room at the end of the row file - in case row iw is moved there and fills in completely. */ - MINFRE = LUSOL->n+1; - NFREE = LUSOL->lena-(*LENL)-(*LROW); - if(NFREEm,TRUE,LROW,LUSOL->indr,LUSOL->lenr,LUSOL->locr); - LW1 = LUSOL->locr[IW]; - LW2 = (LW1+LENW)-1; - NFREE = LUSOL->lena-(*LENL)-(*LROW); - if(NFREEindr[L]; - LUSOL->locc[J] = L; - } -/* ================================================================== - Main elimination loop. - ================================================================== */ - KSTART = KBEGIN; - KSTOP = MIN(KLAST,LUSOL->n); - for(K = KSTART; K <= KSTOP; K++) { - JFIRST = LUSOL->iq[K]; - LFIRST = LUSOL->locc[JFIRST]; - if(LFIRST==0) - goto x490; -/* Row iw has its first element in column jfirst. */ - WJ = LUSOL->a[LFIRST]; - if(K==KLAST) - goto x490; -/* --------------------------------------------------------------- - We are about to use the first element of row iv - to eliminate the first element of row iw. - However, we may wish to interchange the rows instead, - to preserve stability and/or sparsity. - --------------------------------------------------------------- */ - IV = LUSOL->ip[K]; - LENV = LUSOL->lenr[IV]; - LV1 = LUSOL->locr[IV]; - VJ = ZERO; - if(LENV==0) - goto x150; - if(LUSOL->indr[LV1]!=JFIRST) - goto x150; - VJ = LUSOL->a[LV1]; - if(SWAPPD) - goto x200; - if(LTOL*fabs(WJ)ip[KLAST] = IV; - LUSOL->ip[K] = IW; - KBEGIN = K; - SWAPPD = TRUE; - goto x600; -/* --------------------------------------------------------------- - Delete the eliminated element from row iw - by overwriting it with the last element. - --------------------------------------------------------------- */ -x200: - LUSOL->a[LFIRST] = LUSOL->a[LW2]; - JLAST = LUSOL->indr[LW2]; - LUSOL->indr[LFIRST] = JLAST; - LUSOL->indr[LW2] = 0; - LUSOL->locc[JLAST] = LFIRST; - LUSOL->locc[JFIRST] = 0; - LENW--; - (*LENU)--; - if(*LROW==LW2) - (*LROW)--; - LW2 = LW2-1; -/* --------------------------------------------------------------- - Form the multiplier and store it in the L file. - --------------------------------------------------------------- */ - if(fabs(WJ)<=SMALL) - goto x490; - AMULT = -WJ/VJ; - L = LUSOL->lena-(*LENL); - LUSOL->a[L] = AMULT; - LUSOL->indr[L] = IV; - LUSOL->indc[L] = IW; - (*LENL)++; -/* --------------------------------------------------------------- - Add the appropriate multiple of row iv to row iw. - We use two different inner loops. The first one is for the - case where row iw is not at the end of storage. - --------------------------------------------------------------- */ - if(LENV==1) - goto x490; - LV2 = LV1+1; - LV3 = (LV1+LENV)-1; - if(LW2==*LROW) - goto x400; -/* ............................................................... - This inner loop will be interrupted only if - fill-in occurs enough to bump into the next row. - ............................................................... */ - for(LV = LV2; LV <= LV3; LV++) { - JV = LUSOL->indr[LV]; - LW = LUSOL->locc[JV]; - if(LW>0) { -/* No fill-in. */ - LUSOL->a[LW] += AMULT*LUSOL->a[LV]; - if(fabs(LUSOL->a[LW])<=SMALL) { -/* Delete small computed element. */ - LUSOL->a[LW] = LUSOL->a[LW2]; - J = LUSOL->indr[LW2]; - LUSOL->indr[LW] = J; - LUSOL->indr[LW2] = 0; - LUSOL->locc[J] = LW; - LUSOL->locc[JV] = 0; - (*LENU)--; - LENW--; - LW2--; - } - } - else { -/* Row iw doesn't have an element in column jv yet - so there is a fill-in. */ - if(LUSOL->indr[LW2+1]!=0) - goto x360; - (*LENU)++; - LENW++; - LW2++; - LUSOL->a[LW2] = AMULT*LUSOL->a[LV]; - LUSOL->indr[LW2] = JV; - LUSOL->locc[JV] = LW2; - } - } - goto x490; -/* Fill-in interrupted the previous loop. - Move row iw to the end of the row file. */ -x360: - LV2 = LV; - LUSOL->locr[IW] = (*LROW)+1; - -#ifdef LUSOLFastMove - L = LW2-LW1+1; - if(L > 0) { - int loci, *locp; - for(loci = LW1, locp = LUSOL->indr+LW1; - loci <= LW2; loci++, locp++) { - (*LROW)++; - LUSOL->locc[*locp] = *LROW; - } - LW2 = (*LROW)-L+1; - MEMMOVE(LUSOL->a+LW2, LUSOL->a+LW1, L); - MEMMOVE(LUSOL->indr+LW2, LUSOL->indr+LW1, L); - MEMCLEAR(LUSOL->indr+LW1, L); - } -#else - for(L = LW1; L <= LW2; L++) { - (*LROW)++; - LUSOL->a[*LROW] = LUSOL->a[L]; - J = LUSOL->indr[L]; - LUSOL->indr[L] = 0; - LUSOL->indr[*LROW] = J; - LUSOL->locc[J] = *LROW; - } -#endif - LW1 = LUSOL->locr[IW]; - LW2 = *LROW; -/* ............................................................... - Inner loop with row iw at the end of storage. - ............................................................... */ -x400: - for(LV = LV2; LV <= LV3; LV++) { - JV = LUSOL->indr[LV]; - LW = LUSOL->locc[JV]; - if(LW>0) { -/* No fill-in. */ - LUSOL->a[LW] += AMULT*LUSOL->a[LV]; - if(fabs(LUSOL->a[LW])<=SMALL) { -/* Delete small computed element. */ - LUSOL->a[LW] = LUSOL->a[LW2]; - J = LUSOL->indr[LW2]; - LUSOL->indr[LW] = J; - LUSOL->indr[LW2] = 0; - LUSOL->locc[J] = LW; - LUSOL->locc[JV] = 0; - (*LENU)--; - LENW--; - LW2--; - } - } - else { -/* Row iw doesn't have an element in column jv yet - so there is a fill-in. */ - (*LENU)++; - LENW++; - LW2++; - LUSOL->a[LW2] = AMULT*LUSOL->a[LV]; - LUSOL->indr[LW2] = JV; - LUSOL->locc[JV] = LW2; - } - } - *LROW = LW2; -/* The k-th element of row iw has been processed. - Reset swappd before looking at the next element. */ -x490: - SWAPPD = FALSE; - } -/* ================================================================== - End of main elimination loop. - ================================================================== - - Cancel markers on row iw. */ -x600: - LUSOL->lenr[IW] = LENW; - if(LENW==0) - goto x910; - for(L = LW1; L <= LW2; L++) { - J = LUSOL->indr[L]; - LUSOL->locc[J] = 0; - } -/* Move the diagonal element to the front of row iw. - At this stage, lenw gt 0 and klast le n. */ -x700: - for(L = LW1; L <= LW2; L++) { - LDIAG = L; - if(LUSOL->indr[L]==JFIRST) - goto x730; - } - goto x910; - -x730: - *DIAG = LUSOL->a[LDIAG]; - LUSOL->a[LDIAG] = LUSOL->a[LW1]; - LUSOL->a[LW1] = *DIAG; - LUSOL->indr[LDIAG] = LUSOL->indr[LW1]; - LUSOL->indr[LW1] = JFIRST; -/* If an interchange is needed, repeat from the beginning with the - new row iw, knowing that the opposite interchange cannot occur. */ - if(SWAPPD) - goto x100; - *INFORM = LUSOL_INFORM_LUSUCCESS; - goto x950; -/* Singular. */ -x910: - *DIAG = ZERO; - *INFORM = LUSOL_INFORM_LUSINGULAR; -/* Force a compression if the file for U is much longer than the - no. of nonzeros in U (i.e. if lrow is much bigger than lenU). - This should prevent memory fragmentation when there is far more - memory than necessary (i.e. when lena is huge). */ -x950: - LIMIT = (int) (USPACE*(*LENU))+LUSOL->m+LUSOL->n+1000; - if(*LROW>LIMIT) - LU1REC(LUSOL, LUSOL->m,TRUE,LROW,LUSOL->indr,LUSOL->lenr,LUSOL->locr); - goto x990; -/* Not enough storage. */ -x970: - *INFORM = LUSOL_INFORM_ANEEDMEM; -/* Exit. */ -x990: -; -} - -/* ================================================================== - lu7rnk (check rank) assumes U is currently nrank by n - and determines if row nrank contains an acceptable pivot. - If not, the row is deleted and nrank is decreased by 1. - jsing is an input parameter (not altered). If jsing is positive, - column jsing has already been judged dependent. A substitute - (if any) must be some other column. - ------------------------------------------------------------------ - -- Jul 1987: First version. - 09 May 1988: First f77 version. - ================================================================== */ -void LU7RNK(LUSOLrec *LUSOL, int JSING, int *LENU, - int *LROW, int *NRANK, int *INFORM, LPSREAL *DIAG) -{ - LPSREAL UTOL1, UMAX; - int IW, LENW, L1, L2, LMAX, L, JMAX, KMAX; - -#ifdef ForceInitialization - L1 = 0; - L2 = 0; -#endif - - UTOL1 = LUSOL->parmlu[LUSOL_RP_SMALLDIAG_U]; - *DIAG = ZERO; -/* Find Umax, the largest element in row nrank. */ - IW = LUSOL->ip[*NRANK]; - LENW = LUSOL->lenr[IW]; - if(LENW==0) - goto x400; - L1 = LUSOL->locr[IW]; - L2 = (L1+LENW)-1; - UMAX = ZERO; - LMAX = L1; - for(L = L1; L <= L2; L++) { - if(UMAXa[L])) { - UMAX = fabs(LUSOL->a[L]); - LMAX = L; - } - } -/* Find which column that guy is in (in pivotal order). - Interchange him with column nrank, then move him to be - the new diagonal at the front of row nrank. */ - *DIAG = LUSOL->a[LMAX]; - JMAX = LUSOL->indr[LMAX]; - for(KMAX = *NRANK; KMAX <= LUSOL->n; KMAX++) { - if(LUSOL->iq[KMAX]==JMAX) - break; - } - LUSOL->iq[KMAX] = LUSOL->iq[*NRANK]; - LUSOL->iq[*NRANK] = JMAX; - LUSOL->a[LMAX] = LUSOL->a[L1]; - LUSOL->a[L1] = *DIAG; - LUSOL->indr[LMAX] = LUSOL->indr[L1]; - LUSOL->indr[L1] = JMAX; -/* See if the new diagonal is big enough. */ - if(UMAX<=UTOL1) - goto x400; - if(JMAX==JSING) - goto x400; -/* ------------------------------------------------------------------ - The rank stays the same. - ------------------------------------------------------------------ */ - *INFORM = LUSOL_INFORM_LUSUCCESS; - return; -/* ------------------------------------------------------------------ - The rank decreases by one. - ------------------------------------------------------------------ */ -x400: - *INFORM = LUSOL_INFORM_RANKLOSS; - (*NRANK)--; - if(LENW>0) { -/* Delete row nrank from U. */ - LENU = LENU-LENW; - LUSOL->lenr[IW] = 0; - for(L = L1; L <= L2; L++) { - LUSOL->indr[L] = 0; - } - if(L2==*LROW) { -/* This row was at the end of the data structure. - We have to reset lrow. - Preceding rows might already have been deleted, so we - have to be prepared to go all the way back to 1. */ - for(L = 1; L <= L2; L++) { - if(LUSOL->indr[*LROW]>0) - goto x900; - (*LROW)--; - } - } - } -x900: -; -} - -/* ================================================================== - lu7zap eliminates all nonzeros in column jzap of U. - It also sets kzap to the position of jzap in pivotal order. - Thus, on exit we have iq(kzap) = jzap. - ------------------------------------------------------------------ - -- Jul 1987: nrank added. - 10 May 1988: First f77 version. - ================================================================== */ -void LU7ZAP(LUSOLrec *LUSOL, int JZAP, int *KZAP, int *LENU, int *LROW, - int NRANK) -{ - int K, I, LENI, LR1, LR2, L; - - for(K = 1; K <= NRANK; K++) { - I = LUSOL->ip[K]; - LENI = LUSOL->lenr[I]; - if(LENI==0) - goto x90; - LR1 = LUSOL->locr[I]; - LR2 = (LR1+LENI)-1; - for(L = LR1; L <= LR2; L++) { - if(LUSOL->indr[L]==JZAP) - goto x60; - } - goto x90; -/* Delete the old element. */ -x60: - LUSOL->a[L] = LUSOL->a[LR2]; - LUSOL->indr[L] = LUSOL->indr[LR2]; - LUSOL->indr[LR2] = 0; - LUSOL->lenr[I] = LENI-1; - (*LENU)--; -/* Stop if we know there are no more rows containing jzap. */ -x90: - *KZAP = K; - if(LUSOL->iq[K]==JZAP) - goto x800; - } -/* nrank must be smaller than n because we haven't found kzap yet. */ - L = LUSOL->n; - for(K = NRANK+1; K <= L; K++) { - *KZAP = K; - if(LUSOL->iq[K]==JZAP) - break; - } -/* See if we zapped the last element in the file. */ -x800: - if(*LROW>0) { - if(LUSOL->indr[*LROW]==0) - (*LROW)--; - } - -} - diff --git a/src/lpsolve/build/lp_solve/lusol8a.c b/src/lpsolve/build/lp_solve/lusol8a.c deleted file mode 100644 index 2bb8005c..00000000 --- a/src/lpsolve/build/lp_solve/lusol8a.c +++ /dev/null @@ -1,279 +0,0 @@ - -/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - File lusol8a - lu8rpc - Sparse LU update: Replace Column - LUSOL's sparse implementation of the Bartels-Golub update. - - 01 May 2002: Derived from LUSOL's original lu8a.f file. - 01 May 2002: Current version of lusol8a.f. - ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ - -/* ================================================================== - lu8rpc updates the LU factorization A = L*U when column jrep - is replaced by some vector a(new). - lu8rpc is an implementation of the Bartels-Golub update, - designed for the case where A is rectangular and/or singular. - L is a product of stabilized eliminations (m x m, nonsingular). - P U Q is upper trapezoidal (m x n, rank nrank). - - If mode1 = 0, the old column is taken to be zero - (so it does not have to be removed from U). - If mode1 = 1, the old column need not have been zero. - If mode2 = 0, the new column is taken to be zero. - v(*) is not used or altered. - If mode2 = 1, v(*) must contain the new column a(new). - On exit, v(*) will satisfy L*v = a(new). - If mode2 = 2, v(*) must satisfy L*v = a(new). - - The array w(*) is not used or altered. - On entry, all elements of locc are assumed to be zero. - On a successful exit (inform != 7), this will again be true. - On exit: - - inform = -1 if the rank of U decreased by 1. - inform = 0 if the rank of U stayed the same. - inform = 1 if the rank of U increased by 1. - inform = 2 if the update seemed to be unstable - (diag much bigger than vnorm). - inform = 7 if the update was not completed (lack of storage). - inform = 8 if jrep is not between 1 and n. - ------------------------------------------------------------------ - -- Jan 1985: Original F66 version. - -- Jul 1987: Modified to maintain U in trapezoidal form. - 10 May 1988: First f77 version. - 16 Oct 2000: Added test for instability (inform = 2). - ================================================================== */ -void LU8RPC(LUSOLrec *LUSOL, int MODE1, int MODE2, - int JREP, LPSREAL V[], LPSREAL W[], - int *INFORM, LPSREAL *DIAG, LPSREAL *VNORM) -{ - MYBOOL SINGLR; - int LPRINT, NRANK, LENL, LENU, LROW, NRANK0, KREP, KLAST, IW, L1, J1, JSING; - LPSREAL UTOL1, UTOL2; - - LPRINT = LUSOL->luparm[LUSOL_IP_PRINTLEVEL]; - NRANK = LUSOL->luparm[LUSOL_IP_RANK_U]; - LENL = LUSOL->luparm[LUSOL_IP_NONZEROS_L]; - LENU = LUSOL->luparm[LUSOL_IP_NONZEROS_U]; - LROW = LUSOL->luparm[LUSOL_IP_NONZEROS_ROW]; - UTOL1 = LUSOL->parmlu[LUSOL_RP_SMALLDIAG_U]; - UTOL2 = LUSOL->parmlu[LUSOL_RP_EPSDIAG_U]; - NRANK0 = NRANK; - *DIAG = ZERO; - *VNORM = ZERO; - if(JREP<1) - goto x980; - if(JREP>LUSOL->n) - goto x980; - -/* ------------------------------------------------------------------ - If mode1 = 0, there are no elements to be removed from U - but we still have to set krep (using a backward loop). - Otherwise, use lu7zap to remove column jrep from U - and set krep at the same time. - ------------------------------------------------------------------ */ - if(MODE1==LUSOL_UPDATE_OLDEMPTY) { - KREP = LUSOL->n+1; -x10: - KREP--; - if(LUSOL->iq[KREP]!=JREP) - goto x10; - } - else - LU7ZAP(LUSOL, JREP,&KREP,&LENU,&LROW,NRANK); - -/* ------------------------------------------------------------------ - Insert a new column of u and find klast. - ------------------------------------------------------------------ */ - if(MODE2==LUSOL_UPDATE_NEWEMPTY) { - KLAST = 0; - } - else { - if(MODE2==LUSOL_UPDATE_NEWNONEMPTY) { -/* Transform v = a(new) to satisfy L*v = a(new). */ - LU6SOL(LUSOL, LUSOL_SOLVE_Lv_v, V,W, NULL, INFORM); - } - else if(V==NULL) -/* Otherwise, the V vector is taken to satisfy this already, or stored earlier. */ - V=LUSOL->vLU6L; - - -/* Insert into U any nonzeros in the top of v. - row ip(klast) will contain the last nonzero in pivotal order. - Note that klast will be in the range ( 0, nrank ). */ - LU7ADD(LUSOL, JREP,V,LENL,&LENU,&LROW,NRANK,INFORM,&KLAST,VNORM); - if(*INFORM==LUSOL_INFORM_ANEEDMEM) - goto x970; - } -/* ------------------------------------------------------------------ - In general, the new column causes U to look like this: - krep n krep n - ....a......... ..........a... - . a . . a . - . a . . a . - .a . . a . - P U Q = a . or . a . - b. . . a . - b . . . a . - b . . . a . - b ...... ..a... nrank - c c - c c - c c m - klast points to the last nonzero "a" or "b". - klast = 0 means all "a" and "b" entries are zero. - ------------------------------------------------------------------ */ - if(MODE2==LUSOL_UPDATE_NEWEMPTY) { - if(KREP>NRANK) - goto x900; - } - else if(NRANKm) { -/* Eliminate any "c"s (in either case). - Row nrank + 1 may end up containing one nonzero. */ - LU7ELM(LUSOL, JREP,V,&LENL,&LROW,NRANK,INFORM,DIAG); - if(*INFORM==LUSOL_INFORM_ANEEDMEM) - goto x970; - if(*INFORM==LUSOL_INFORM_LUSINGULAR) { -/* The nonzero is apparently significant. - Increase nrank by 1 and make klast point to the bottom. */ - NRANK++; - KLAST = NRANK; - } - } - if(NRANKn) { -/* The column rank is low. - In the first case, we want the new column to end up in - position nrank, so the trapezoidal columns will have a chance - later on (in lu7rnk) to pivot in that position. - Otherwise the new column is not part of the triangle. We - swap it into position nrank so we can judge it for singularity. - lu7rnk might choose some other trapezoidal column later. */ - if(KREPiq[KREP] = LUSOL->iq[NRANK]; - LUSOL->iq[NRANK] = JREP; - KREP = NRANK; - } - } -/* ------------------------------------------------------------------ - If krep .lt. klast, there are some "b"s to eliminate: - krep - ....a......... - . a . - . a . - .a . - P U Q = a . krep - b. . - b . . - b . . - b ...... nrank - If krep .eq. klast, there are no "b"s, but the last "a" still - has to be moved to the front of row krep (by lu7for). - ------------------------------------------------------------------ */ - if(KREP<=KLAST) { -/* Perform a cyclic permutation on the current pivotal order, - and eliminate the resulting row spike. krep becomes klast. - The final diagonal (if any) will be correctly positioned at - the front of the new krep-th row. nrank stays the same. */ - LU7CYC(LUSOL, KREP,KLAST,LUSOL->ip); - LU7CYC(LUSOL, KREP,KLAST,LUSOL->iq); - LU7FOR(LUSOL, KREP,KLAST,&LENL,&LENU,&LROW,INFORM,DIAG); - if(*INFORM==LUSOL_INFORM_ANEEDMEM) - goto x970; - KREP = KLAST; -/* Test for instability (diag much bigger than vnorm). */ - SINGLR = (MYBOOL) ((*VNORM)ip[KREP]; - SINGLR = (MYBOOL) (LUSOL->lenr[IW]==0); - if(!SINGLR) { - L1 = LUSOL->locr[IW]; - J1 = LUSOL->indr[L1]; - SINGLR = (MYBOOL) (J1!=JREP); - if(!SINGLR) { - *DIAG = LUSOL->a[L1]; - SINGLR = (MYBOOL) (fabs(*DIAG)<=UTOL1 || fabs(*DIAG)<=UTOL2*(*VNORM)); - } - } - if(SINGLR && (KREPip); - LU7CYC(LUSOL, KREP,LUSOL->n,LUSOL->iq); - LU7FOR(LUSOL, KREP,NRANK,&LENL,&LENU,&LROW,INFORM,DIAG); - if(*INFORM==LUSOL_INFORM_ANEEDMEM) - goto x970; - } -/* Find the best column to be in position nrank. - If singlr, it can't be the new column, jrep. - If nothing satisfactory exists, nrank will be decreased. */ - if(SINGLR || (NRANKn)) { - JSING = 0; - if(SINGLR) - JSING = JREP; - LU7RNK(LUSOL, JSING,&LENU,&LROW,&NRANK,INFORM,DIAG); - } - -/* ------------------------------------------------------------------ - Update indeces of optional row-based version of L0. - ------------------------------------------------------------------ */ -#if 0 - if(LUSOL->L0 != NULL) - LU1L0UPD(LUSOL, INFORM); -#endif - -/* ------------------------------------------------------------------ - Set inform for exit. - ------------------------------------------------------------------ */ -x900: - if(NRANK==NRANK0) - *INFORM = LUSOL_INFORM_LUSUCCESS; - else if(NRANKn) { - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu8rpc warning...\nSingularity after replacing column. jrep=%8d diag=%g\n", - JREP,DIAG); - } - } - else - *INFORM = LUSOL_INFORM_LUSINGULAR; - goto x990; -/* Instability. */ -x920: - *INFORM = LUSOL_INFORM_LUUNSTABLE; - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu8rpc warning...\nInstability after replacing column. jrep=%8d diag=%g\n", - JREP,DIAG); - goto x990; -/* Not enough storage. */ -x970: - *INFORM = LUSOL_INFORM_ANEEDMEM; - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu8rpc error...\nInsufficient memory. lena=%8d\n", - LUSOL->lena); - goto x990; -/* jrep is out of range. */ -x980: - *INFORM = LUSOL_INFORM_FATALERR; - if(LPRINT>=LUSOL_MSG_SINGULARITY) - LUSOL_report(LUSOL, 0, "lu8rpc error...\njrep is out of range. m=%8d n=%8d jrep=%8d\n", - LUSOL->m,LUSOL->n,JREP); -/* Exit. */ -x990: - LUSOL->luparm[LUSOL_IP_UPDATECOUNT]++; - LUSOL->luparm[LUSOL_IP_RANK_U] = NRANK; - LUSOL->luparm[LUSOL_IP_NONZEROS_L] = LENL; - LUSOL->luparm[LUSOL_IP_NONZEROS_U] = LENU; - LUSOL->luparm[LUSOL_IP_NONZEROS_ROW] = LROW; - LUSOL->luparm[LUSOL_IP_INFORM] = *INFORM; -} diff --git a/src/lpsolve/build/lp_solve/myblas.c b/src/lpsolve/build/lp_solve/myblas.c deleted file mode 100644 index cad7b873..00000000 --- a/src/lpsolve/build/lp_solve/myblas.c +++ /dev/null @@ -1,102 +0,0 @@ - -#include -#include -/*#include */ -#include -#include -#include "myblas.h" -#define STRICT_R_HEADERS -#include "R_ext/BLAS.h" - -#ifdef FORTIFY -# include "lp_fortify.h" -#endif - -/* ************************************************************************ */ -/* Initialize BLAS interfacing routines */ -/* ************************************************************************ */ - -MYBOOL mustinitBLAS = TRUE; -#ifdef WIN32 - HINSTANCE hBLAS = NULL; -#else - void *hBLAS = NULL; -#endif - - -/* ************************************************************************ */ -/* Define the BLAS interfacing routines */ -/* ************************************************************************ */ - -void init_BLAS(void) -{ - if(mustinitBLAS) { - load_BLAS(NULL); - mustinitBLAS = FALSE; - } -} - -MYBOOL is_nativeBLAS(void) -{ -#ifdef LoadableBlasLib - return( (MYBOOL) (hBLAS == NULL) ); -#else - return( TRUE ); -#endif -} - -MYBOOL load_BLAS(char *libname) -{ - MYBOOL result = TRUE; - return( result ); -} - -MYBOOL unload_BLAS(void) -{ - return( load_BLAS(NULL) ); -} - - -/* ************************************************************************ */ -/* Now define the unoptimized local BLAS functions */ -/* ************************************************************************ */ - -void lps_daxpy( int n, LPSREAL da, LPSREAL *dx, int incx, LPSREAL *dy, int incy) -{ - dx++; - dy++; - F77_CALL(daxpy) ( &n, &da, dx, &incx, dy, &incy); -} - - -/* ************************************************************************ */ - -void lps_dcopy( int n, LPSREAL *dx, int incx, LPSREAL *dy, int incy) -{ - dx++; - dy++; - F77_CALL(dcopy) ( &n, dx, &incx, dy, &incy); -} - - -/* ************************************************************************ */ - -void lps_dscal (int n, LPSREAL da, LPSREAL *dx, int incx) -{ - dx++; - F77_CALL(dscal) (&n, &da, dx, &incx); -} - - -/* ************************************************************************ */ - -int lps_idamax( int n, LPSREAL *x, int is ) -{ - x++; - return ( F77_CALL(idamax)( &n, x, &is ) ); -} - - - - - diff --git a/src/lpsolve/build/lp_solve/yacc_read.c b/src/lpsolve/build/lp_solve/yacc_read.c deleted file mode 100644 index b8198f50..00000000 --- a/src/lpsolve/build/lp_solve/yacc_read.c +++ /dev/null @@ -1,1289 +0,0 @@ -/* - ============================================================================ - NAME : yacc_read.c - - PURPOSE : translation of lp-problem and storage in sparse matrix - - SHORT : Subroutines for yacc program to store the input in an intermediate - data-structure. The yacc and lex programs translate the input. First the - problemsize is determined and the date is read into an intermediate - structure, then readinput fills the sparse matrix. - - USAGE : call yyparse(); to start reading the input. call readinput(); to - fill the sparse matrix. - ============================================================================ - Rows : contains the amount of rows + 1. Rows-1 is the amount of constraints - (no bounds) Rows also contains the rownr 0 which is the objective function - - Columns : contains the amount of columns (different variable names found in - the constraints) - - Nonnuls : contains the amount of nonnuls = sum of different entries of all - columns in the constraints and in the objectfunction - - Hash_tab : contains all columnnames on the first level of the structure the - row information is kept under each column structure in a linked list (also - the objective funtion is in this structure) Bound information is also - stored under under the column name - - First_rside : points to a linked list containing all relational operators - and the righthandside values of the constraints the linked list is in - reversed order with respect to the rownumbers - ============================================================================ */ -#include -#include -#include -#include "lpkit.h" -#include "yacc_read.h" - -#ifdef FORTIFY -# include "lp_fortify.h" -#endif - -#define tol 1.0e-10 -#define coldatastep 100 - -#define HASHSIZE 10007 /* A prime number! */ - -struct structSOSvars { - char *name; - int col; - LPSREAL weight; - struct structSOSvars *next; -}; - -struct structSOS { - char *name; - short type; - int Nvars; - int weight; - struct structSOSvars *SOSvars, *LastSOSvars; - struct structSOS *next; -}; - -struct SOSrow { - int col; - LPSREAL value; - struct SOSrow *next; -}; - -struct SOSrowdata { - short type; - char *name; - struct SOSrow *SOSrow; -}; - -struct rside /* contains relational operator and rhs value */ -{ - int row; - LPSREAL value; - LPSREAL range_value; - struct rside *next; - short relat; - short range_relat; - char negate; - short SOStype; -}; - -struct column -{ - int row; - LPSREAL value; - struct column *next; - struct column *prev; -}; - -struct structcoldata { - int must_be_int; - int must_be_sec; - int must_be_free; - LPSREAL upbo; - LPSREAL lowbo; - struct column *firstcol; - struct column *col; -}; - -static void error(parse_parm *pp, int verbose, char *string) -{ - if(pp == NULL) - report(NULL, CRITICAL, string); - else if(pp->Verbose >= verbose) - report(NULL, verbose, "%s on line %d\n", string, pp->lineno); -} - -/* - * error handling routine for yyparse() - */ -void read_error(parse_parm *pp, void *scanner, char *string) -{ - error(pp, CRITICAL, string); -} - -/* called when lex gets a fatal error */ -void lex_fatal_error(parse_parm *pp, void *scanner, char *msg) -{ - read_error(pp, scanner, msg); - longjmp(pp->jump_buf, 1); -} - -void add_row(parse_parm *pp) -{ - pp->Rows++; - pp->rs = NULL; - pp->Lin_term_count = 0; -} - -void add_sos_row(parse_parm *pp, short SOStype) -{ - if (pp->rs != NULL) - pp->rs->SOStype = SOStype; - pp->Rows++; - pp->rs = NULL; - pp->Lin_term_count = 0; -} - -void check_int_sec_sos_free_decl(parse_parm *pp, int within_int_decl, int within_sec_decl, int sos_decl0, int within_free_decl) -{ - pp->Ignore_int_decl = TRUE; - pp->Ignore_sec_decl = TRUE; - pp->Ignore_free_decl = TRUE; - pp->sos_decl = 0; - if(within_int_decl) { - pp->Ignore_int_decl = FALSE; - pp->int_decl = (char) within_int_decl; - if(within_sec_decl) - pp->Ignore_sec_decl = FALSE; - } - else if(within_sec_decl) { - pp->Ignore_sec_decl = FALSE; - } - else if(sos_decl0) { - pp->sos_decl = (char) sos_decl0; - } - else if(within_free_decl) { - pp->Ignore_free_decl = FALSE; - } -} - -static void add_int_var(parse_parm *pp, char *name, short int_decl) -{ - hashelem *hp; - - if((hp = findhash(name, pp->Hash_tab)) == NULL) { - char buf[256]; - - sprintf(buf, "Unknown variable %s declared integer, ignored", name); - error(pp, NORMAL, buf); - } - else if(pp->coldata[hp->index].must_be_int) { - char buf[256]; - - sprintf(buf, "Variable %s declared integer more than once, ignored", name); - error(pp, NORMAL, buf); - } - else { - pp->coldata[hp->index].must_be_int = TRUE; - if(int_decl == 2) { - if(pp->coldata[hp->index].lowbo != -DEF_INFINITE * (LPSREAL) 10.0) { - char buf[256]; - - sprintf(buf, "Variable %s: lower bound on variable redefined", name); - error(pp, NORMAL, buf); - } - pp->coldata[hp->index].lowbo = 0; - if(pp->coldata[hp->index].upbo < DEF_INFINITE) { - char buf[256]; - - sprintf(buf, "Variable %s: upper bound on variable redefined", name); - error(pp, NORMAL, buf); - } - pp->coldata[hp->index].upbo = 1; - } - else if(int_decl == 3) { - if(pp->coldata[hp->index].upbo == DEF_INFINITE * (LPSREAL) 10.0) - pp->coldata[hp->index].upbo = 1.0; - } - } -} - -static void add_sec_var(parse_parm *pp, char *name) -{ - hashelem *hp; - - if((hp = findhash(name, pp->Hash_tab)) == NULL) { - char buf[256]; - - sprintf(buf, "Unknown variable %s declared semi-continuous, ignored", name); - error(pp, NORMAL, buf); - } - else if(pp->coldata[hp->index].must_be_sec) { - char buf[256]; - - sprintf(buf, "Variable %s declared semi-continuous more than once, ignored", name); - error(pp, NORMAL, buf); - } - else - pp->coldata[hp->index].must_be_sec = TRUE; -} - -int set_sec_threshold(parse_parm *pp, char *name, LPSREAL threshold) -{ - hashelem *hp; - - if((hp = findhash(name, pp->Hash_tab)) == NULL) { - char buf[256]; - - sprintf(buf, "Unknown variable %s declared semi-continuous, ignored", name); - error(pp, NORMAL, buf); - return(FALSE); - } - - if ((pp->coldata[hp->index].lowbo > 0.0) && (threshold > 0.0)) { - char buf[256]; - - pp->coldata[hp->index].must_be_sec = FALSE; - sprintf(buf, "Variable %s declared semi-continuous, but it has a non-negative lower bound (%f), ignored", name, pp->coldata[hp->index].lowbo); - error(pp, NORMAL, buf); - } - if (threshold > pp->coldata[hp->index].lowbo) - pp->coldata[hp->index].lowbo = threshold; - - return(pp->coldata[hp->index].must_be_sec); -} - -static void add_free_var(parse_parm *pp, char *name) -{ - hashelem *hp; - - if((hp = findhash(name, pp->Hash_tab)) == NULL) { - char buf[256]; - - sprintf(buf, "Unknown variable %s declared free, ignored", name); - error(pp, NORMAL, buf); - } - else if(pp->coldata[hp->index].must_be_free) { - char buf[256]; - - sprintf(buf, "Variable %s declared free more than once, ignored", name); - error(pp, NORMAL, buf); - } - else - pp->coldata[hp->index].must_be_free = TRUE; -} - -static int add_sos_name(parse_parm *pp, char *name) -{ - struct structSOS *SOS; - - if(CALLOC(SOS, 1, struct structSOS) == NULL) - return(FALSE); - - if(MALLOC(SOS->name, strlen(name) + 1, char) == NULL) - { - FREE(SOS); - return(FALSE); - } - strcpy(SOS->name, name); - SOS->type = 0; - - if(pp->FirstSOS == NULL) - pp->FirstSOS = SOS; - else - pp->LastSOS->next = SOS; - pp->LastSOS = SOS; - - return(TRUE); -} - -static int add_sos_var(parse_parm *pp, char *name) -{ - struct structSOSvars *SOSvar; - - if(name != NULL) { - if(CALLOC(SOSvar, 1, struct structSOSvars) == NULL) - return(FALSE); - - if(MALLOC(SOSvar->name, strlen(name) + 1, char) == NULL) - { - FREE(SOSvar); - return(FALSE); - } - strcpy(SOSvar->name, name); - - if(pp->LastSOS->SOSvars == NULL) - pp->LastSOS->SOSvars = SOSvar; - else - pp->LastSOS->LastSOSvars->next = SOSvar; - pp->LastSOS->LastSOSvars = SOSvar; - pp->LastSOS->Nvars = pp->LastSOS->Nvars + 1; - } - pp->LastSOS->LastSOSvars->weight = 0; - - return(TRUE); -} - -void storevarandweight(parse_parm *pp, char *name) -{ - if(!pp->Ignore_int_decl) { - add_int_var(pp, name, pp->int_decl); - if(!pp->Ignore_sec_decl) - add_sec_var(pp, name); - } - else if(!pp->Ignore_sec_decl) - add_sec_var(pp, name); - else if(pp->sos_decl==1) - add_sos_name(pp, name); - else if(pp->sos_decl==2) - add_sos_var(pp, name); - else if(!pp->Ignore_free_decl) - add_free_var(pp, name); -} - -int set_sos_type(parse_parm *pp, int SOStype) -{ - if(pp->LastSOS != NULL) - pp->LastSOS->type = (short) SOStype; - return(TRUE); -} - -int set_sos_weight(parse_parm *pp, double weight, int sos_decl) -{ - if(pp->LastSOS != NULL) { - if(sos_decl==1) - pp->LastSOS->weight = (int) (weight+.1); - else - pp->LastSOS->LastSOSvars->weight = weight; - } - return(TRUE); -} - -static int inccoldata(parse_parm *pp) -{ - long Columns = pp->Columns; - - if(Columns == 0) - CALLOC(pp->coldata, coldatastep, struct structcoldata); - else if((Columns%coldatastep) == 0) - REALLOC(pp->coldata, Columns + coldatastep, struct structcoldata); - - if(pp->coldata != NULL) { - pp->coldata[Columns].upbo = (LPSREAL) DEF_INFINITE * (LPSREAL) 10.0; - pp->coldata[Columns].lowbo = (LPSREAL) -DEF_INFINITE * (LPSREAL) 10.0; /* temporary. If still this value then 0 will be taken */ - pp->coldata[Columns].col = NULL; - pp->coldata[Columns].firstcol = NULL; - pp->coldata[Columns].must_be_int = FALSE; - pp->coldata[Columns].must_be_sec = FALSE; - pp->coldata[Columns].must_be_free = FALSE; - } - - return(pp->coldata != NULL); -} - -/* - * initialisation of hashstruct and globals. - */ -static int init_read(parse_parm *pp, int verbose) -{ - int ok = FALSE; - - pp->Verbose = verbose; - set_obj_dir(pp, TRUE); - pp->Rows = 0; - pp->Non_zeros = 0; - pp->Columns = 0; - pp->FirstSOS = pp->LastSOS = NULL; - pp->Lin_term_count = 0; - if (CALLOC(pp->First_rside, 1, struct rside) != NULL) { - pp->rs = pp->First_rside; - pp->rs->value = pp->rs->range_value = 0; - /* first row (nr 0) is always the objective function */ - pp->rs->relat = OF; - pp->rs->range_relat = -1; - pp->rs->SOStype = 0; - pp->Hash_tab = NULL; - pp->Hash_constraints = NULL; - if (((pp->Hash_tab = create_hash_table(HASHSIZE, 0)) == NULL) || - ((pp->Hash_constraints = create_hash_table(HASHSIZE, 0)) == NULL)){ - FREE(pp->First_rside); - FREE(pp->Hash_tab); - FREE(pp->Hash_constraints); - } - else - ok = TRUE; - } - return(ok); -} /* init */ - -/* - * clears the tmp_store variable after all information has been copied - */ -void null_tmp_store(parse_parm *pp, int init_Lin_term_count) -{ - pp->tmp_store.value = 0; - pp->tmp_store.rhs_value = 0; - FREE(pp->tmp_store.name); - if(init_Lin_term_count) - pp->Lin_term_count = 0; -} - -/* - * variable : pointer to text array with name of variable - * row : the rownumber of the constraint - * value : value of matrixelement - * A(row, variable). - * Sign : (global) determines the sign of value. - * store() : stores value in matrix - * A(row, variable). If A(row, variable) already contains data, - * value is added to the existing value. - */ -static int store(parse_parm *pp, char *variable, - int row, - LPSREAL value) -{ - hashelem *h_tab_p; - struct column *col_p; - - if(value == 0) { - char buf[256]; - - sprintf(buf, "(store) Warning, variable %s has an effective coefficient of 0, Ignored", variable); - error(pp, NORMAL, buf); - /* return(TRUE); */ - } - - if((h_tab_p = findhash(variable, pp->Hash_tab)) == NULL) { - if (((h_tab_p = puthash(variable, pp->Columns, NULL, pp->Hash_tab)) == NULL) - ) return(FALSE); - inccoldata(pp); - pp->Columns++; /* counter for calloc of final array */ - if(value) { - if (CALLOC(col_p, 1, struct column) == NULL) - return(FALSE); - pp->Non_zeros++; /* for calloc of final arrays */ - col_p->row = row; - col_p->value = value; - pp->coldata[h_tab_p->index].firstcol = pp->coldata[h_tab_p->index].col = col_p; - } - } - else if((pp->coldata[h_tab_p->index].col == NULL) || (pp->coldata[h_tab_p->index].col->row != row)) { - if(value) { - if (CALLOC(col_p, 1, struct column) == NULL) - return(FALSE); - pp->Non_zeros++; /* for calloc of final arrays */ - if(pp->coldata[h_tab_p->index].col != NULL) - pp->coldata[h_tab_p->index].col->prev = col_p; - else - pp->coldata[h_tab_p->index].firstcol = col_p; - col_p->value = value; - col_p->row = row; - col_p->next = pp->coldata[h_tab_p->index].col; - pp->coldata[h_tab_p->index].col = col_p; - } - } - else if(value) { - pp->coldata[h_tab_p->index].col->value += value; - if(fabs(pp->coldata[h_tab_p->index].col->value) < tol) /* eliminitate rounding errors */ - pp->coldata[h_tab_p->index].col->value = 0; - } - return(TRUE); -} /* store */ - -static int storefirst(parse_parm *pp) -{ - struct rside *rp; - - if ((pp->rs != NULL) && (pp->rs->row == pp->tmp_store.row)) - return(TRUE); - - /* make space for the rhs information */ - if (CALLOC(rp, 1, struct rside) == NULL) - return(FALSE); - rp->next = pp->First_rside; - pp->First_rside = pp->rs = rp; - pp->rs->row = /* row */ pp->tmp_store.row; - pp->rs->value = pp->tmp_store.rhs_value; - pp->rs->relat = pp->tmp_store.relat; - pp->rs->range_relat = -1; - pp->rs->SOStype = 0; - - if(pp->tmp_store.name != NULL) { - if(pp->tmp_store.value != 0) { - if (!store(pp, pp->tmp_store.name, pp->tmp_store.row, pp->tmp_store.value)) - return(FALSE); - } - else { - char buf[256]; - - sprintf(buf, "Warning, variable %s has an effective coefficient of 0, ignored", pp->tmp_store.name); - error(pp, NORMAL, buf); - } - } - null_tmp_store(pp, FALSE); - return(TRUE); -} - -/* - * store relational operator given in yylex[0] in the rightside list. - * Also checks if it constraint was a bound and if so stores it in the - * boundslist - */ -int store_re_op(parse_parm *pp, char OP, int HadConstraint, int HadVar, int Had_lineair_sum) -{ - short tmp_relat; - - switch(OP) { - - case '=': - tmp_relat = EQ; - break; - - case '>': - tmp_relat = GE; - break; - - case '<': - tmp_relat = LE; - break; - - case 0: - if(pp->rs != NULL) - tmp_relat = pp->rs->relat; - else - tmp_relat = pp->tmp_store.relat; - break; - - default: - { - char buf[256]; - - sprintf(buf, "Error: unknown relational operator %c", OP); - error(pp, CRITICAL, buf); - } - return(FALSE); - break; - } - - if(/* pp->Lin_term_count > 1 */ HadConstraint && HadVar) {/* it is not a bound */ - if(pp->Lin_term_count <= 1) - if(!storefirst(pp)) - return(FALSE); - pp->rs->relat = tmp_relat; - } - else if(/* pp->Lin_term_count == 0 */ HadConstraint && !Had_lineair_sum /* HadVar */ /* && (pp->rs != NULL) */) { /* it is a range */ - if(pp->Lin_term_count == 1) - if(!storefirst(pp)) - return(FALSE); - if(pp->rs == NULL) { /* range before row, already reported */ - error(pp, CRITICAL, "Error: range for undefined row"); - return(FALSE); - } - - if(pp->rs->negate) - switch (tmp_relat) { - case LE: - tmp_relat = GE; - break; - case GE: - tmp_relat = LE; - break; - } - - if(pp->rs->range_relat != -1) { - error(pp, CRITICAL, "Error: There was already a range for this row"); - return(FALSE); - } - else if(tmp_relat == pp->rs->relat) { - error(pp, CRITICAL, "Error: relational operator for range is the same as relation operator for equation"); - return(FALSE); - } - else - pp->rs->range_relat = tmp_relat; - } - else /* could be a bound */ - pp->tmp_store.relat = tmp_relat; - - return(TRUE); -} /* store_re_op */ - -int negate_constraint(parse_parm *pp) -{ - if(pp->rs != NULL) - pp->rs->negate = TRUE; - - return(TRUE); -} - -/* - * store RHS value in the rightside structure - * if type = true then - */ -int rhs_store(parse_parm *pp, LPSREAL value, int HadConstraint, int HadVar, int Had_lineair_sum) -{ - if(/* pp->Lin_term_count > 1 */ (HadConstraint && HadVar) || (pp->Rows == 0)){ /* not a bound */ - if (pp->Rows == 0) - value = -value; - /* if(pp->Lin_term_count < 2) */ - if(pp->rs == NULL) - pp->tmp_store.rhs_value += value; - else - - if(pp->rs == NULL) { - error(pp, CRITICAL, "Error: No variable specified"); - return(FALSE); - } - else - pp->rs->value += value; - } - else if(/* pp->Lin_term_count == 0 */ HadConstraint && !HadVar) { /* a range */ - if(pp->rs == NULL) /* if range before row, already reported */ - pp->tmp_store.rhs_value += value; - else if(pp->rs->range_relat < 0) /* was a bad range; ignore */; - else { - if(pp->rs->negate) - value = -value; - if(((pp->rs->relat == LE) && (pp->rs->range_relat == GE) && - (pp->rs->value < value)) || - ((pp->rs->relat == GE) && (pp->rs->range_relat == LE) && - (pp->rs->value > value)) || - ((pp->rs->relat == EQ) || (pp->rs->range_relat == EQ))) { - pp->rs->range_relat = -2; - error(pp, CRITICAL, "Error: range restriction conflicts"); - return(FALSE); - } - else - pp->rs->range_value += value; - } - } - else /* a bound */ - pp->tmp_store.rhs_value += value; - return(TRUE); -} /* RHS_store */ - -/* - * store all data in the right place - * count the amount of lineair terms in a constraint - * only store in data-structure if the constraint is not a bound - */ -int var_store(parse_parm *pp, char *var, LPSREAL value, int HadConstraint, int HadVar, int Had_lineair_sum) -{ - int row; - - row = pp->Rows; - - /* also in a bound the same var name can occur more than once. Check for - this. Don't increment Lin_term_count */ - - if(pp->Lin_term_count != 1 || pp->tmp_store.name == NULL || strcmp(pp->tmp_store.name, var) != 0) - pp->Lin_term_count++; - - /* always store objective function with rownr == 0. */ - if(row == 0) - return(store(pp, var, row, value)); - - if(pp->Lin_term_count == 1) { /* don't store yet. could be a bound */ - if(MALLOC(pp->tmp_store.name, strlen(var) + 1, char) != NULL) - strcpy(pp->tmp_store.name, var); - pp->tmp_store.row = row; - pp->tmp_store.value += value; - return(TRUE); - } - - if(pp->Lin_term_count == 2) { /* now you can also store the first variable */ - if(!storefirst(pp)) - return(FALSE); - /* null_tmp_store(pp, FALSE); */ - } - - return(store(pp, var, row, value)); -} /* var_store */ - - - -/* - * store the information in tmp_store because it is a bound - */ -int store_bounds(parse_parm *pp, int warn) -{ - if(pp->tmp_store.value != 0) { - hashelem *h_tab_p; - LPSREAL boundvalue; - - if((h_tab_p = findhash(pp->tmp_store.name, pp->Hash_tab)) == NULL) { - /* a new columnname is found, create an entry in the hashlist */ - if ((h_tab_p = puthash(pp->tmp_store.name, pp->Columns, NULL, pp->Hash_tab)) == NULL) { - error(pp, CRITICAL, "Not enough memory"); - return(FALSE); - } - inccoldata(pp); - pp->Columns++; /* counter for calloc of final array */ - } - - if(pp->tmp_store.value < 0) { /* divide by negative number, */ - /* relational operator may change */ - if(pp->tmp_store.relat == GE) - pp->tmp_store.relat = LE; - else if(pp->tmp_store.relat == LE) - pp->tmp_store.relat = GE; - } - - boundvalue = pp->tmp_store.rhs_value / pp->tmp_store.value; - -#if FALSE - /* Check sanity of bound; all variables should be positive */ - if( ((pp->tmp_store.relat == EQ) && (boundvalue < 0)) - || ((pp->tmp_store.relat == LE) && (boundvalue < 0))) { /* Error */ - error(pp, CRITICAL, "Error: variables must always be non-negative"); - return(FALSE); - } -#endif - -#if FALSE - if((pp->tmp_store.relat == GE) && (boundvalue <= 0)) /* Warning */ - error(pp, NORMAL, "Warning: useless bound; variables are always >= 0"); -#endif - - /* bound seems to be sane, add it */ - if((pp->tmp_store.relat == GE) || (pp->tmp_store.relat == EQ)) { - if(boundvalue > pp->coldata[h_tab_p->index].lowbo - tol) - pp->coldata[h_tab_p->index].lowbo = boundvalue; - else if(warn) - error(pp, NORMAL, "Ineffective lower bound, ignored"); - } - if((pp->tmp_store.relat == LE) || (pp->tmp_store.relat == EQ)) { - if(boundvalue < pp->coldata[h_tab_p->index].upbo + tol) - pp->coldata[h_tab_p->index].upbo = boundvalue; - else if (warn) - error(pp, NORMAL, "Ineffective upper bound, ignored"); - } - - /* check for empty range */ - if((warn) && (pp->coldata[h_tab_p->index].upbo + tol < pp->coldata[h_tab_p->index].lowbo)) { - error(pp, CRITICAL, "Error: bound contradicts earlier bounds"); - return(FALSE); - } - } - else /* pp->tmp_store.value = 0 ! */ { - char buf[256]; - - if((pp->tmp_store.rhs_value == 0) || - ((pp->tmp_store.rhs_value > 0) && (pp->tmp_store.relat == LE)) || - ((pp->tmp_store.rhs_value < 0) && (pp->tmp_store.relat == GE))) { - sprintf(buf, "Variable %s has an effective coefficient of 0 in bound, ignored", - pp->tmp_store.name); - if(warn) - error(pp, NORMAL, buf); - } - else { - sprintf(buf, "Error, variable %s has an effective coefficient of 0 in bound", - pp->tmp_store.name); - error(pp, CRITICAL, buf); - return(FALSE); - } - } - - /* null_tmp_store(pp, FALSE); */ - pp->tmp_store.rhs_value = 0; - - return(TRUE); -} /* store_bounds */ - -int set_title(parse_parm *pp, char *name) -{ - pp->title = strdup(name); - return(TRUE); -} - -int add_constraint_name(parse_parm *pp, char *name) -{ - int row; - hashelem *hp; - - if((hp = findhash(name, pp->Hash_constraints)) != NULL) { - row = hp->index; - pp->rs = pp->First_rside; - while ((pp->rs != NULL) && (pp->rs->row != row)) - pp->rs = pp->rs->next; - } - else { - row = pp->Rows; - if (((hp = puthash(name, row, NULL, pp->Hash_constraints)) == NULL) - ) return(FALSE); - if(row) - pp->rs = NULL; - } - - return(TRUE); -} - -/* - * transport the data from the intermediate structure to the sparse matrix - * and free the intermediate structure - */ -static int readinput(parse_parm *pp, lprec *lp) -{ - int i, i1, count, index, col; - struct column *cp, *tcp; - hashelem *hp; - struct rside *rp; - signed char *negateAndSOS = NULL; - LPSREAL *row = NULL, a; - int *rowno = NULL; - MYBOOL SOSinMatrix = FALSE; - struct SOSrowdata *SOSrowdata = NULL; - struct SOSrow *SOSrow, *SOSrow1; - - if(lp != NULL) { - if (CALLOC(negateAndSOS, 1 + pp->Rows, signed char) == NULL) - return(FALSE); - - rp = pp->First_rside; - for(i = pp->Rows; (i >= 0) && (rp != NULL); i--) { - if(rp->SOStype == 0) - negateAndSOS[i] = (rp->negate ? -1 : 0); - else - negateAndSOS[i] = (signed char) rp->SOStype; - - rp = rp->next; - } - - /* fill names with the rownames */ - hp = pp->Hash_constraints->first; - while(hp != NULL) { - if (/* (negateAndSOS[hp->index] <= 0) && */ (!set_row_name(lp, hp->index, hp->name))) - return(FALSE); - hp = hp->nextelem; - } - } - - for(i = pp->Rows; i >= 0; i--) { - rp = pp->First_rside; - if((lp != NULL) && (rp != NULL)) { - if(rp->SOStype == 0) { - if (rp->negate) { - switch (rp->relat) { - case LE: - rp->relat = GE; - break; - case GE: - rp->relat = LE; - break; - } - switch (rp->range_relat) { - case LE: - rp->range_relat = GE; - break; - case GE: - rp->range_relat = LE; - break; - } - rp->range_value = -rp->range_value; - rp->value = -rp->value; - } - - if((rp->range_relat >= 0) && (rp->value == lp->infinite)) { - rp->value = rp->range_value; - rp->relat = rp->range_relat; - rp->range_relat = -1; - } - else if((rp->range_relat >= 0) && (rp->value == -lp->infinite)) { - rp->value = rp->range_value; - rp->relat = rp->range_relat; - rp->range_relat = -1; - } - if ((rp->range_relat >= 0) && (rp->range_value == rp->value)) { - rp->relat = EQ; - rp->range_relat = EQ; - } - if(i) { - set_constr_type(lp, i, rp->relat); - pp->relat[i] = rp->relat; - } - set_rh(lp, i, rp->value); - if (rp->range_relat >= 0) - set_rh_range(lp, i, rp->range_value - rp->value); - } - else { - SOSinMatrix = TRUE; - if(i) - pp->relat[i] = rp->relat; - } - } - if(rp != NULL) { - pp->First_rside = rp->next; - free(rp); /* free memory when data has been read */ - } - else - pp->First_rside = NULL; - } - - while(pp->First_rside != NULL) { - rp = pp->First_rside; - pp->First_rside = rp->next; - free(rp); /* free memory when data has been read */ - } - - /* start reading the Hash_list structure */ - index = 0; - - if((SOSinMatrix) && (CALLOC(SOSrowdata, 1 + pp->Rows, struct SOSrowdata) == NULL)) { - FREE(negateAndSOS); - FREE(row); - FREE(rowno); - return(FALSE); - } - - if((lp != NULL) && - ((MALLOC(row, 1 + pp->Rows, LPSREAL) == NULL) || (MALLOC(rowno, 1 + pp->Rows, int) == NULL))) { - FREE(SOSrowdata); - FREE(negateAndSOS); - FREE(row); - FREE(rowno); - return(FALSE); - } - - /* for(i = 0; i < pp->Hash_tab->size; i++) { - hp = pp->Hash_tab->table[i]; */ - hp = pp->Hash_tab->first; - while(hp != NULL) { - count = 0; - index++; - cp = pp->coldata[hp->index].firstcol; - col = hp->index + 1; - while(cp != NULL) { - if(lp != NULL) { - if (negateAndSOS[cp->row] <= 0) { - rowno[count] = cp->row; - a = cp->value; - if (negateAndSOS[cp->row]) - a = -a; - row[count++] = a; - } - else { - if (MALLOC(SOSrow, 1, struct SOSrow) == NULL) { - FREE(SOSrowdata); - FREE(negateAndSOS); - FREE(row); - FREE(rowno); - return(FALSE); - } - if(SOSrowdata[cp->row].SOSrow == NULL) - SOSrowdata[cp->row].name = strdup(get_row_name(lp, cp->row)); - SOSrow->next = SOSrowdata[cp->row].SOSrow; - SOSrowdata[cp->row].SOSrow = SOSrow; - SOSrowdata[cp->row].type = negateAndSOS[cp->row]; - SOSrow->col = col; - SOSrow->value = cp->value; - } - } - tcp = cp; - /* cp = cp->next; */ - cp = cp->prev; - free(tcp); /* free memory when data has been read */ - } - - if(lp != NULL) { - add_columnex(lp, count, row, rowno); - /* check for bound */ - if(pp->coldata[hp->index].lowbo == -DEF_INFINITE * 10.0) - /* lp->orig_lowbo[pp->Rows+index] = 0.0; */ - set_lowbo(lp, index, 0); - else - /* lp->orig_lowbo[pp->Rows+index] = pp->coldata[hp->index].lowbo; */ - set_lowbo(lp, index, pp->coldata[hp->index].lowbo); - /* lp->orig_upbo[pp->Rows+index] = pp->coldata[hp->index].upbo; */ - if(pp->coldata[hp->index].upbo >= DEF_INFINITE) - set_upbo(lp, index, DEF_INFINITE); - else - set_upbo(lp, index, pp->coldata[hp->index].upbo); - - /* check if it must be an integer variable */ - if(pp->coldata[hp->index].must_be_int) { - /* lp->must_be_int[pp->Rows + index]=TRUE; */ - set_int(lp, index, TRUE); - } - if(pp->coldata[hp->index].must_be_sec) { - set_semicont(lp, index, TRUE); - } - if(pp->coldata[hp->index].must_be_free) { - set_unbounded(lp, index); - } - - /* copy name of column variable */ - if (!set_col_name(lp, index, hp->name)) { - FREE(SOSrowdata); - FREE(negateAndSOS); - FREE(row); - FREE(rowno); - return(FALSE); - } - - /* put matrix values in intermediate row */ - /* cp = hp->col; */ - /* cp = hp->firstcol; */ - } - - /* thp = hp; */ - /* hp = hp->next; */ - /* free(thp->name); */ - /* free(thp); */ /* free memory when data has been read */ - - hp = hp->nextelem; - - } - /* pp->Hash_tab->table[i] = NULL; */ - - FREE(pp->coldata); - - if(SOSrowdata != NULL) { - struct structSOS *structSOS; - struct structSOSvars *SOSvars, *SOSvars1; - int SOSweight = 0; - - for(i = 1; i <= pp->Rows; i++) { - SOSrow = SOSrowdata[i].SOSrow; - if(SOSrow != NULL) { - if(MALLOC(structSOS, 1, struct structSOS) == NULL) { - FREE(SOSrowdata); - FREE(negateAndSOS); - FREE(row); - FREE(rowno); - return(FALSE); - } - structSOS->Nvars = 0; - structSOS->type = SOSrowdata[i].type; - structSOS->weight = ++SOSweight; - structSOS->name = strdup(SOSrowdata[i].name); - structSOS->LastSOSvars = NULL; - structSOS->next = pp->FirstSOS; - pp->FirstSOS = structSOS; - SOSvars = NULL; - while(SOSrow != NULL) { - SOSvars1 = SOSvars; - MALLOC(SOSvars, 1, struct structSOSvars); - SOSvars->next = SOSvars1; - SOSvars->col = SOSrow->col; - SOSvars->weight = SOSrow->value; - SOSvars->name = NULL; - structSOS->Nvars++; - SOSrow1 = SOSrow->next; - FREE(SOSrow); - SOSrow = SOSrow1; - } - structSOS->SOSvars = SOSvars; - } - } - FREE(SOSrowdata); - } - - while(pp->FirstSOS != NULL) - { - struct structSOSvars *SOSvars, *SOSvars1; - int *sosvars, n, col; - LPSREAL *weights; - hashelem *hp; - - pp->LastSOS = pp->FirstSOS; - pp->FirstSOS = pp->FirstSOS->next; - SOSvars = pp->LastSOS->SOSvars; - if(lp != NULL) { - MALLOC(sosvars, pp->LastSOS->Nvars, int); - MALLOC(weights, pp->LastSOS->Nvars, double); - } - else { - sosvars = NULL; - weights = NULL; - } - n = 0; - while(SOSvars != NULL) - { - SOSvars1 = SOSvars; - SOSvars = SOSvars->next; - if(lp != NULL) { - col = SOSvars1->col; - if(col == 0) - if((hp = findhash(SOSvars1->name, lp->colname_hashtab)) != NULL) - col = hp->index; - if (col) { - sosvars[n] = col; - weights[n++] = SOSvars1->weight; - } - } - FREE(SOSvars1->name); - FREE(SOSvars1); - } - if(lp != NULL) { - add_SOS(lp, pp->LastSOS->name, pp->LastSOS->type, pp->LastSOS->weight, n, sosvars, weights); - FREE(weights); - FREE(sosvars); - } - FREE(pp->LastSOS->name); - FREE(pp->LastSOS); - } - - if(negateAndSOS != NULL) { - for(i1 = 0, i = 1; i <= pp->Rows; i++) - if(negateAndSOS[i] <= 0) - pp->relat[++i1] = pp->relat[i]; - -#if 01 - for(i = pp->Rows; i > 0; i--) - if(negateAndSOS[i] > 0) { - del_constraint(lp, i); - pp->Rows--; - } -#endif - } - - /* the following should be replaced by a call to the MPS print routine MB */ - -#if 0 - if(pp->Verbose) { - int j; - - printf("\n"); - printf("**********Data read**********\n"); - printf("Rows : %d\n", pp->Rows); - printf("Columns : %d\n", pp->Columns); - printf("Nonnuls : %d\n", pp->Non_zeros); - printf("NAME LPPROB\n"); - printf("ROWS\n"); - for(i = 0; i <= pp->Rows; i++) { - if(pp->relat[i] == LE) - printf(" L "); - else if(pp->relat[i] == EQ) - printf(" E "); - else if(pp->relat[i] == GE) - printf(" G "); - else if(pp->relat[i] == OF) - printf(" N "); - printf("%s\n", get_row_name(lp, i)); - } - - printf("COLUMNS\n"); - j = 0; - for(i = 0; i < pp->Non_zeros; i++) { - if(i == lp->col_end[j]) - j++; - printf(" %-8s %-8s %g\n", get_col_name(lp, j), - get_row_name(lp, lp->mat[i].row_nr), (double)lp->mat[i].value); - } - - printf("RHS\n"); - for(i = 0; i <= pp->Rows; i++) { - printf(" RHS %-8s %g\n", get_row_name(lp, i), - (double)lp->orig_rhs[i]); - } - - printf("RANGES\n"); - for(i = 1; i <= pp->Rows; i++) - if((lp->orig_upbo[i] != lp->infinite) && (lp->orig_upbo[i] != 0)) { - printf(" RGS %-8s %g\n", get_row_name(lp, i), - (double)lp->orig_upbo[i]); - } - else if((lp->orig_lowbo[i] != 0)) { - printf(" RGS %-8s %g\n", get_row_name(lp, i), - (double)-lp->orig_lowbo[i]); - } - - printf("BOUNDS\n"); - for(i = pp->Rows + 1; i <= pp->Rows + pp->Columns; i++) { - if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite) && - (lp->orig_lowbo[i] == lp->orig_upbo[i])) { - printf(" FX BND %-8s %g\n", get_col_name(lp, i - pp->Rows), - (double)lp->orig_upbo[i]); - } - else { - if(lp->orig_upbo[i] < lp->infinite) - printf(" UP BND %-8s %g\n", get_col_name(lp, i - pp->Rows), - (double)lp->orig_upbo[i]); - if(lp->orig_lowbo[i] > 0) - printf(" LO BND %-8s %g\n", get_col_name(lp, i - pp->Rows), - (double)lp->orig_lowbo[i]); - } - } - - printf("ENDATA\n"); - } -#endif - - FREE(row); - FREE(rowno); - FREE(negateAndSOS); - return(TRUE); -} /* readinput */ - -lprec *yacc_read(lprec *lp, int verbose, char *lp_name, int (*parse) (parse_parm *pp), parse_parm *pp, void (*delete_allocated_memory) (parse_parm *pp)) -{ - LPSREAL *orig_upbo; - int stat = -1; - lprec *lp0 = lp; - - pp->title = lp_name; - - if(!init_read(pp, verbose)) - error(pp, CRITICAL, "init_read failed"); - else if (setjmp(pp->jump_buf) == 0) - stat = parse(pp); - - delete_allocated_memory(pp); - - pp->Rows--; - - pp->relat = NULL; - if((stat != 0) || (CALLOC(pp->relat, pp->Rows + 1, short) != NULL)) { - if(stat == 0) { - if(lp == NULL) { - lp = make_lp(pp->Rows, 0); - } - else { - int NRows; - - for(NRows = get_Nrows(lp); NRows < pp->Rows; NRows++) - add_constraintex(lp, 0, NULL, NULL, LE, 0); - } - } - else - lp = NULL; - if ((stat != 0) || (lp != NULL)) { - if(lp != NULL) { - set_verbose(lp, pp->Verbose); - } - - if (!readinput(pp, lp)) { - if((lp != NULL) && (lp0 == NULL)) - delete_lp(lp); - lp = NULL; - } - - if(lp != NULL) { - set_lp_name(lp, pp->title); - if(pp->Maximise) - set_maxim(lp); - - if(pp->Rows) { - int row; - - // @FS: replaced // MALLOCCPY(orig_upbo, lp->orig_upbo, 1 + pp->Rows, LPSREAL); - MALLOC(orig_upbo, 1 + pp->Rows, LPSREAL); - if ( orig_upbo != NULL ) { - memcpy(orig_upbo, lp->orig_upbo, (size_t)((1 + pp->Rows) * sizeof(*lp->orig_upbo))); - } - - for(row = 1; row <= pp->Rows; row++) - set_constr_type(lp, row, pp->relat[row]); - - memcpy(lp->orig_upbo, orig_upbo, (1 + pp->Rows) * sizeof(*orig_upbo)); /* restore upper bounds (range) */ - FREE(orig_upbo); - } - } - if((pp->title != NULL) && (pp->title != lp_name)) - free(pp->title); - - free_hash_table(pp->Hash_tab); - free_hash_table(pp->Hash_constraints); - } - FREE(pp->relat); - } - null_tmp_store(pp, FALSE); - return(lp); -} diff --git a/src/lpsolve/headers/include/RlpSolve.h b/src/lpsolve/headers/include/RlpSolve.h deleted file mode 100644 index 69fd8624..00000000 --- a/src/lpsolve/headers/include/RlpSolve.h +++ /dev/null @@ -1,15 +0,0 @@ -#include "lp_lib.h" -// #include "R.h" -// #include "Rinternals.h" -#define STRICT_R_HEADERS // To disable some R code else I get an compile error -#include -// #include "R_ext/Rdynload.h" -// #include "R_ext/Utils.h" - -void R_init_lpSolveAPI(DllInfo *info); -lprec* lprecPointerFromSEXP(SEXP Slprec); -int __WINAPI RlpSolveAbortFunction(lprec *lp, void *userhandle); -void __WINAPI RlpSolveLogFunction(lprec *lp, void *userhandle, char *buf); -void RlpsHS(lprec *lp, unsigned char status); - - diff --git a/src/lpsolve/headers/include/RlpSolveLink.h b/src/lpsolve/headers/include/RlpSolveLink.h deleted file mode 100644 index 5751bb7f..00000000 --- a/src/lpsolve/headers/include/RlpSolveLink.h +++ /dev/null @@ -1,294 +0,0 @@ -/************************************************************************************ - * The lp_solve API -************************************************************************************/ - -/******************************* - * Create/destroy model -*******************************/ - -SEXP RlpSolve_make_lp(SEXP Srows, SEXP Scolumns); -SEXP RlpSolve_copy_lp(SEXP Slp); -/*read_lp*/ -SEXP RlpSolve_read_LP(SEXP Sfilename, SEXP Sverbose); -/*read_mps*/ -/*read_freemps*/ -SEXP RlpSolve_read_MPS(SEXP Sfilename, SEXP Soptions); -SEXP RlpSolve_read_freeMPS(SEXP Sfilename, SEXP Soptions); -/*SEXP RlpSolve_read_XLI(SEXP Sxliname, SEXP Smodelname, SEXP Sdataname, - SEXP Soptions);*/ -SEXP RlpSolve_delete_lp(SEXP Slp); -/*free_lp*/ - -/******************************* - * Build model -*******************************/ - -/*add_column*/ -SEXP RlpSolve_add_columnex(SEXP Slp, SEXP Scolumn, SEXP Srowno); -/*str_add_column*/ -SEXP RlpSolve_set_columnex(SEXP Slp, SEXP Scol_no, SEXP Scolumn, SEXP Srowno); -/*get_column*/ -SEXP RlpSolve_get_columnex(SEXP Slp, SEXP Scol_nr); -/*add_constraint*/ -SEXP RlpSolve_add_constraintex(SEXP Slp, SEXP Srow, SEXP Scolno, - SEXP Sconstr_type, SEXP Srh); -/*str_add_constraint*/ -/*set_row*/ -SEXP RlpSolve_set_rowex(SEXP Slp, SEXP Srow_no, SEXP Srow, SEXP Scolno); -SEXP RlpSolve_add_lag_con(SEXP Slp, SEXP Srow, SEXP Sconstr_type, SEXP Srh); -/*str_add_lag_con*/ -SEXP RlpSolve_add_SOS(SEXP Slp, SEXP Sname, SEXP Ssostype, SEXP Spriority, - SEXP Ssosvars, SEXP Sweights); -SEXP RlpSolve_is_SOS_var(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_del_column(SEXP Slp, SEXP Scolumn); -SEXP RlpSolve_del_constraint(SEXP Slp, SEXP Sdel_row); -/*get_row*/ -SEXP RlpSolve_get_rowex(SEXP Slp, SEXP Srow_nr); -SEXP RlpSolve_get_nameindex(SEXP Slp, SEXP Snames, SEXP Sisrow); -SEXP RlpSolve_is_infinite(SEXP Slp, SEXP Svalues); -SEXP RlpSolve_is_negative(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_resize_lp(SEXP Slp, SEXP Srows, SEXP Scolumns); -SEXP RlpSolve_set_add_rowmode(SEXP Slp, SEXP Sturnon); -SEXP RlpSolve_is_add_rowmode(SEXP Slp); -SEXP RlpSolve_set_binary(SEXP Slp, SEXP Scolumns, SEXP Smust_be_bin); -SEXP RlpSolve_is_binary(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_set_bounds(SEXP Slp, SEXP Scolumns, SEXP Slower, SEXP Supper); -SEXP RlpSolve_set_bounds_tighter(SEXP Slp, SEXP Stighten); -SEXP RlpSolve_get_bounds_tighter(SEXP Slp); -SEXP RlpSolve_set_col_names(SEXP Slp, SEXP Scolumns, SEXP Snames); -SEXP RlpSolve_get_col_names(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_get_origcol_names(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_set_constr_type(SEXP Slp, SEXP Srows, SEXP Scon_types); -SEXP RlpSolve_get_constr_type(SEXP Slp, SEXP Srows); -SEXP RlpSolve_is_constr_type(SEXP Slp, SEXP Srows, SEXP Smasks); -SEXP RlpSolve_set_unbounded(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_is_unbounded(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_set_infinite(SEXP Slp, SEXP Sinfinite); -SEXP RlpSolve_get_infinite(SEXP Slp); -SEXP RlpSolve_set_int(SEXP Slp, SEXP Scolumns, SEXP Smust_be_int); -SEXP RlpSolve_is_int(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_set_lowbo(SEXP Slp, SEXP Scolumns, SEXP Svalues); -SEXP RlpSolve_get_lowbo(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_set_lp_name(SEXP Slp, SEXP Slpname); -SEXP RlpSolve_get_lp_name(SEXP Slp); -SEXP RlpSolve_set_mat(SEXP Slp, SEXP Srow, SEXP Scolumn, SEXP Svalue); -SEXP RlpSolve_get_mat(SEXP Slp, SEXP Srow, SEXP Scolumn); -SEXP RlpSolve_set_obj_bound(SEXP Slp, SEXP Sobj_bound); -SEXP RlpSolve_get_obj_bound(SEXP Slp); -/*set_obj_fn*/ -SEXP RlpSolve_set_obj_fnex(SEXP Slp, SEXP Srow, SEXP Scolno); -/*str_set_obj_fn*/ -/*set_obj*/ -SEXP RlpSolve_set_rh(SEXP Slp, SEXP Srow, SEXP Svalue); -SEXP RlpSolve_get_rh(SEXP Slp, SEXP Srow); -SEXP RlpSolve_set_rh_range(SEXP Slp, SEXP Srows, SEXP Sdeltavalue); -SEXP RlpSolve_get_rh_range(SEXP Slp, SEXP Srows); -SEXP RlpSolve_set_rh_vec(SEXP Slp, SEXP Srh); -/*str_set_rh_vec*/ -SEXP RlpSolve_set_row_names(SEXP Slp, SEXP Srows, SEXP Snew_names); -SEXP RlpSolve_get_row_names(SEXP Slp, SEXP Srows); -SEXP RlpSolve_get_origrow_names(SEXP Slp, SEXP Srows); -SEXP RlpSolve_set_semicont(SEXP Slp, SEXP Scolumns, SEXP Ssc); -SEXP RlpSolve_is_semicont(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_set_upbo(SEXP Slp, SEXP Scolumns, SEXP Svalues); -SEXP RlpSolve_get_upbo(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_set_var_branch(SEXP Slp, SEXP Scolumns, SEXP Sbranch_mode); -SEXP RlpSolve_get_var_branch(SEXP Slp, SEXP Scolumns); -SEXP RlpSolve_set_var_weights(SEXP Slp, SEXP Sweights); - - -/******************************* - * Solver settings -*******************************/ - -SEXP RlpSolve_default_basis(SEXP Slp); -/*read_basis*/ -SEXP RlpSolve_reset_basis(SEXP Slp); -/*write_basis*/ -SEXP RlpSolve_guess_basis(SEXP SLP, SEXP Sguessvector); -/*read_params*/ -/*write_params*/ -SEXP RlpSolve_reset_params(SEXP Slp); -SEXP RlpSolve_set_anti_degen(SEXP Slp, SEXP Santi_degen); -SEXP RlpSolve_is_anti_degen(SEXP Slp, SEXP Stestmasks); -SEXP RlpSolve_set_basis(SEXP Slp, SEXP Sbascolumn, SEXP Snonbasic); -SEXP RlpSolve_get_basis(SEXP Slp, SEXP Snonbasic); -SEXP RlpSolve_set_basiscrash(SEXP Slp, SEXP Smode); -SEXP RlpSolve_get_basiscrash(SEXP Slp); -SEXP RlpSolve_set_bb_depthlimit(SEXP Slp, SEXP Sbb_maxlevel); -SEXP RlpSolve_get_bb_depthlimit(SEXP Slp); -SEXP RlpSolve_set_bb_floorfirst(SEXP Slp, SEXP Sbb_floorfirst); -SEXP RlpSolve_get_bb_floorfirst(SEXP Slp); -SEXP RlpSolve_set_bb_rule(SEXP Slp, SEXP Sbb_rule); -SEXP RlpSolve_get_bb_rule(SEXP Slp); -/*set_BFP*/ -/*has_BFP*/ -/*is_nativeBFP*/ -SEXP RlpSolve_set_break_at_first(SEXP Slp, SEXP Sbreak_at_first); -SEXP RlpSolve_is_break_at_first(SEXP Slp); -SEXP RlpSolve_set_break_at_value(SEXP Slp, SEXP Sbreak_at_value); -SEXP RlpSolve_get_break_at_value(SEXP Slp); -SEXP RlpSolve_set_epsb(SEXP Slp, SEXP Sepsb); -SEXP RlpSolve_get_epsb(SEXP Slp); -SEXP RlpSolve_set_epsd(SEXP Slp, SEXP Sepsd); -SEXP RlpSolve_get_epsd(SEXP Slp); -SEXP RlpSolve_set_epsel(SEXP Slp, SEXP Sepsel); -SEXP RlpSolve_get_epsel(SEXP Slp); -SEXP RlpSolve_set_epsint(SEXP Slp, SEXP Sepsint); -SEXP RlpSolve_get_epsint(SEXP Slp); -SEXP RlpSolve_set_epsperturb(SEXP Slp, SEXP Sepsperturb); -SEXP RlpSolve_get_epsperturb(SEXP Slp); -SEXP RlpSolve_set_epspivot(SEXP Slp, SEXP Sepspivot); -SEXP RlpSolve_get_epspivot(SEXP Slp); -SEXP RlpSolve_set_epslevel(SEXP Slp, SEXP Sepslevel); -SEXP RlpSolve_set_improve(SEXP Slp, SEXP Simprove); -SEXP RlpSolve_get_improve(SEXP Slp); -SEXP RlpSolve_set_maxim(SEXP Slp); -SEXP RlpSolve_is_maxim(SEXP Slp); -SEXP RlpSolve_set_maxpivot(SEXP Slp, SEXP Smax_num_inv); -SEXP RlpSolve_get_maxpivot(SEXP Slp); -SEXP RlpSolve_set_minim(SEXP Slp); -SEXP RlpSolve_set_mip_gap(SEXP Slp, SEXP Sabsolute, SEXP Smip_gap); -SEXP RlpSolve_get_mip_gap(SEXP Slp, SEXP Sabsolute); -SEXP RlpSolve_set_negrange(SEXP Slp, SEXP Snegrange); -SEXP RlpSolve_get_negrange(SEXP Slp); -SEXP RlpSolve_set_obj_in_basis(SEXP Slp, SEXP Sobj_in_basis); -SEXP RlpSolve_is_obj_in_basis(SEXP Slp); -SEXP RlpSolve_set_pivoting(SEXP Slp, SEXP Spivoting); -SEXP RlpSolve_get_pivoting(SEXP Slp); -SEXP RlpSolve_is_piv_mode(SEXP Slp, SEXP Stestmasks); -SEXP RlpSolve_is_piv_rule(SEXP Slp, SEXP Srules); -SEXP RlpSolve_set_preferdual(SEXP Slp, SEXP Sdodual); -SEXP RlpSolve_set_presolve(SEXP Slp, SEXP Sdo_presolve, SEXP Smaxloops); -SEXP RlpSolve_get_presolve(SEXP Slp); -SEXP RlpSolve_get_presolveloops(SEXP Slp); -SEXP RlpSolve_is_presolve(SEXP Slp, SEXP Stestmasks); -SEXP RlpSolve_set_scalelimit(SEXP Slp, SEXP Sscalelimit); -SEXP RlpSolve_get_scalelimit(SEXP Slp); -SEXP RlpSolve_set_scaling(SEXP Slp, SEXP Sscalemode); -SEXP RlpSolve_get_scaling(SEXP Slp); -SEXP RlpSolve_is_integerscaling(SEXP Slp); -SEXP RlpSolve_is_scalemode(SEXP Slp, SEXP Stestmasks); -SEXP RlpSolve_is_scaletype(SEXP Slp, SEXP Sscaletype); -SEXP RlpSolve_set_sense(SEXP Slp, SEXP Smaximize); -SEXP RlpSolve_set_simplextype(SEXP Slp, SEXP Ssimplextype); -SEXP RlpSolve_get_simplextype(SEXP Slp); -SEXP RlpSolve_set_solutionlimit(SEXP Slp, SEXP Slimit); -SEXP RlpSolve_get_solutionlimit(SEXP Slp); -SEXP RlpSolve_set_timeout(SEXP Slp, SEXP Ssectimeout); -SEXP RlpSolve_get_timeout(SEXP Slp); -SEXP RlpSolve_set_use_names(SEXP Slp, SEXP Sisrow, SEXP Suse_names); -SEXP RlpSolve_is_use_names(SEXP Slp, SEXP Sisrow); -SEXP RlpSolve_unscale(SEXP Slp); - - -/******************************* - * Solve -*******************************/ - -SEXP RlpSolve_solve(SEXP Slp); -/*SEXP RlpSolve_lag_solve(SEXP Slp, SEXP Sstart_bound, SEXP Snum_iter);*/ - - -/******************************* - * Solution -*******************************/ - -SEXP RlpSolve_get_constraints(SEXP Slp); -/*get_ptr_constraints*/ -/*get_constr_value*/ -SEXP RlpSolve_get_objective(SEXP Slp); -SEXP RlpSolve_get_primal_solution(SEXP Slp); -/*get_ptr_primal_solution*/ -SEXP RlpSolve_get_var_primalresult(SEXP Slp); -SEXP RlpSolve_get_sensitivity_obj(SEXP Slp); -/*get_ptr_sensitivity_obj*/ -SEXP RlpSolve_get_sensitivity_objex(SEXP Slp); -/*get_ptr_sensitivity_objex*/ -SEXP RlpSolve_get_sensitivity_rhs(SEXP Slp); -/*get_ptr_sensitivity_rhs*/ -SEXP RlpSolve_get_dual_solution(SEXP Slp); -/*get_ptr_dual_solution*/ -/*get_var_dualresult*/ -SEXP RlpSolve_get_solutioncount(SEXP Slp); -SEXP RlpSolve_get_total_iter(SEXP Slp); -SEXP RlpSolve_get_total_nodes(SEXP Slp); -SEXP RlpSolve_get_variables(SEXP Slp); -/*get_ptr_variables*/ -/*get_working_objective*/ -/*is_feasible*/ - - -/******************************* - * Debug/print settings -*******************************/ - -/*set_debug*/ -/*is_debug*/ -/*set_lag_trace*/ -/*is_lag_trace*/ -/*set_outputstream*/ -/*set_outputfile*/ -/*set_print_sol*/ -/*get_print_sol*/ -/*set_trace*/ -/*is_trace*/ -SEXP RlpSolve_set_verbose(SEXP Slp, SEXP Sverbose); -SEXP RlpSolve_get_verbose(SEXP Slp); - - -/******************************* - * Debug/print -*******************************/ - -/*print_constraints*/ -/*print_debugdump*/ -/*print_duals*/ -/*print_lp*/ -/*print_objective*/ -/*print_scales*/ -/*print_solution*/ -/*print_str*/ -/*SEXP RlpSolve_print_tableau(SEXP Slp);*/ - - -/******************************* - * Write model to file -*******************************/ - -SEXP RlpSolve_write_lp(SEXP Slp, SEXP Sfilename); -/*write_LP*/ -/*write_lpex*/ -SEXP RlpSolve_write_mps(SEXP Slp, SEXP Sfilename); -SEXP RlpSolve_write_freemps(SEXP Slp, SEXP Sfilename); -/*write_MPS*/ -/*write_freeMPS*/ -/*MPS_writefileex*/ -/*write_XLI*/ -/*set_XLI*/ -/*has_XLI*/ -/*is_nativeXLI*/ - -/******************************* - * Miscellaneous routines -*******************************/ - -/*column_in_lp*/ -SEXP RlpSolve_dualize_lp(SEXP Slp); -SEXP RlpSolve_get_lp_index(SEXP Slp, SEXP Sorig_indices); -/*SEXP RlpSolve_get_Lrows(SEXP Slp);*/ -SEXP RlpSolve_get_Ncolumns(SEXP Slp); -SEXP RlpSolve_get_nonzeros(SEXP Slp); -SEXP RlpSolve_get_Norig_columns(SEXP Slp); -SEXP RlpSolve_get_Norig_rows(SEXP Slp); -SEXP RlpSolve_get_Nrows(SEXP Slp); -SEXP RlpSolve_get_orig_index(SEXP Slp, SEXP Slp_indices); -SEXP RlpSolve_get_status(SEXP Slp); -SEXP RlpSolve_get_statustext(SEXP Slp, SEXP Sstatuscode); -SEXP RlpSolve_lp_solve_version(); -/*set_basisvar*/ -SEXP RlpSolve_time_elapsed(SEXP Slp); - - - - diff --git a/src/lpsolve/headers/include/colamd.h b/src/lpsolve/headers/include/colamd.h deleted file mode 100644 index e8539922..00000000 --- a/src/lpsolve/headers/include/colamd.h +++ /dev/null @@ -1,286 +0,0 @@ -/* ========================================================================== */ -/* === colamd/symamd prototypes and definitions ============================= */ -/* ========================================================================== */ - -/* - You must include this file (colamd.h) in any routine that uses colamd, - symamd, or the related macros and definitions. - - Authors: - - The authors of the code itself are Stefan I. Larimore and Timothy A. - Davis (davis@cise.ufl.edu), University of Florida. The algorithm was - developed in collaboration with John Gilbert, Xerox PARC, and Esmond - Ng, Oak Ridge National Laboratory. - - Date: - - May 4, 2001. Version 2.1. - - Acknowledgements: - - This work was supported by the National Science Foundation, under - grants DMS-9504974 and DMS-9803599. - - Notice: - - Copyright (c) 1998-2001 by the University of Florida. - All Rights Reserved. - - THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY - EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. - - Permission is hereby granted to use or copy this program for any - purpose, provided the above notices are retained on all copies. - User documentation of any code that uses this code must cite the - Authors, the Copyright, and "Used by permission." If this code is - accessible from within Matlab, then typing "help colamd" and "help - symamd" must cite the Authors. Permission to modify the code and to - distribute modified code is granted, provided the above notices are - retained, and a notice that the code was modified is included with the - above copyright notice. You must also retain the Availability - information below, of the original version. - - This software is provided free of charge. - - Availability: - - The colamd/symamd library is available at - - http://www.cise.ufl.edu/research/sparse/colamd - - This is the http://www.cise.ufl.edu/research/sparse/colamd/colamd.h - file. It is required by the colamd.c, colamdmex.c, and symamdmex.c - files, and by any C code that calls the routines whose prototypes are - listed below, or that uses the colamd/symamd definitions listed below. - -*/ - -#ifndef COLAMD_H -#define COLAMD_H - -/* ========================================================================== */ -/* === Include files ======================================================== */ -/* ========================================================================== */ - -#include - -/* ========================================================================== */ -/* === Knob and statistics definitions ====================================== */ -/* ========================================================================== */ - -/* size of the knobs [ ] array. Only knobs [0..1] are currently used. */ -#define COLAMD_KNOBS 20 - -/* number of output statistics. Only stats [0..6] are currently used. */ -#define COLAMD_STATS 20 - -/* knobs [0] and stats [0]: dense row knob and output statistic. */ -#define COLAMD_DENSE_ROW 0 - -/* knobs [1] and stats [1]: dense column knob and output statistic. */ -#define COLAMD_DENSE_COL 1 - -/* stats [2]: memory defragmentation count output statistic */ -#define COLAMD_DEFRAG_COUNT 2 - -/* stats [3]: colamd status: zero OK, > 0 warning or notice, < 0 error */ -#define COLAMD_STATUS 3 - -/* stats [4..6]: error info, or info on jumbled columns */ -#define COLAMD_INFO1 4 -#define COLAMD_INFO2 5 -#define COLAMD_INFO3 6 - -/* error codes returned in stats [3]: */ -#define COLAMD_OK (0) -#define COLAMD_OK_BUT_JUMBLED (1) -#define COLAMD_ERROR_A_not_present (-1) -#define COLAMD_ERROR_p_not_present (-2) -#define COLAMD_ERROR_nrow_negative (-3) -#define COLAMD_ERROR_ncol_negative (-4) -#define COLAMD_ERROR_nnz_negative (-5) -#define COLAMD_ERROR_p0_nonzero (-6) -#define COLAMD_ERROR_A_too_small (-7) -#define COLAMD_ERROR_col_length_negative (-8) -#define COLAMD_ERROR_row_index_out_of_bounds (-9) -#define COLAMD_ERROR_out_of_memory (-10) -#define COLAMD_ERROR_internal_error (-999) - - - -/* ========================================================================== */ -/* === Row and Column structures ============================================ */ -/* ========================================================================== */ - -/* User code that makes use of the colamd/symamd routines need not directly */ -/* reference these structures. They are used only for the COLAMD_RECOMMENDED */ -/* macro. */ - -typedef struct Colamd_Col_struct -{ - int start ; /* index for A of first row in this column, or DEAD */ - /* if column is dead */ - int length ; /* number of rows in this column */ - union - { - int thickness ; /* number of original columns represented by this */ - /* col, if the column is alive */ - int parent ; /* parent in parent tree super-column structure, if */ - /* the column is dead */ - } shared1 ; - union - { - int score ; /* the score used to maintain heap, if col is alive */ - int order ; /* pivot ordering of this column, if col is dead */ - } shared2 ; - union - { - int headhash ; /* head of a hash bucket, if col is at the head of */ - /* a degree list */ - int hash ; /* hash value, if col is not in a degree list */ - int prev ; /* previous column in degree list, if col is in a */ - /* degree list (but not at the head of a degree list) */ - } shared3 ; - union - { - int degree_next ; /* next column, if col is in a degree list */ - int hash_next ; /* next column, if col is in a hash list */ - } shared4 ; - -} Colamd_Col ; - -typedef struct Colamd_Row_struct -{ - int start ; /* index for A of first col in this row */ - int length ; /* number of principal columns in this row */ - union - { - int degree ; /* number of principal & non-principal columns in row */ - int p ; /* used as a row pointer in init_rows_cols () */ - } shared1 ; - union - { - int mark ; /* for computing set differences and marking dead rows*/ - int first_column ;/* first column in row (used in garbage collection) */ - } shared2 ; - -} Colamd_Row ; - -/* ========================================================================== */ -/* === Colamd recommended memory size ======================================= */ -/* ========================================================================== */ - -/* - The recommended length Alen of the array A passed to colamd is given by - the COLAMD_RECOMMENDED (nnz, n_row, n_col) macro. It returns -1 if any - argument is negative. 2*nnz space is required for the row and column - indices of the matrix. COLAMD_C (n_col) + COLAMD_R (n_row) space is - required for the Col and Row arrays, respectively, which are internal to - colamd. An additional n_col space is the minimal amount of "elbow room", - and nnz/5 more space is recommended for run time efficiency. - - This macro is not needed when using symamd. -*/ - -#define COLAMD_C(n_col) (((n_col) + 1) * sizeof (Colamd_Col) / sizeof (int)) -#define COLAMD_R(n_row) (((n_row) + 1) * sizeof (Colamd_Row) / sizeof (int)) - -#define COLAMD_RECOMMENDED(nnz, n_row, n_col) \ -( \ -((nnz) < 0 || (n_row) < 0 || (n_col) < 0) \ -? \ - (-1) \ -: \ - (int) (2 * (nnz) + COLAMD_C (n_col) + COLAMD_R (n_row) + (n_col) + ((nnz) / 5)) \ -) - -/* ========================================================================== */ -/* === Prototypes of user-callable routines ================================= */ -/* ========================================================================== */ - -/* -#ifdef __cplusplus - #define __EXTERN_C extern "C" -#else - #define __EXTERN_C -#endif -*/ - -#ifndef __BORLANDC__ - - #ifdef __cplusplus - #define __EXTERN_C extern "C" - #else - #define __EXTERN_C - #endif - -#else /* Otherwise set up for the Borland compiler */ - - #define __EXTERN_C extern "C" - -#endif - -#ifdef __cplusplus -__EXTERN_C { -#endif - - - -int colamd_recommended /* returns recommended value of Alen, */ - /* or (-1) if input arguments are erroneous */ -( - int nnz, /* nonzeros in A */ - int n_row, /* number of rows in A */ - int n_col /* number of columns in A */ -) ; - -void colamd_set_defaults /* sets default parameters */ -( /* knobs argument is modified on output */ - double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ -) ; - -int colamd /* returns (1) if successful, (0) otherwise*/ -( /* A and p arguments are modified on output */ - int n_row, /* number of rows in A */ - int n_col, /* number of columns in A */ - int Alen, /* size of the array A */ - int A [], /* row indices of A, of size Alen */ - int p [], /* column pointers of A, of size n_col+1 */ - double knobs [COLAMD_KNOBS],/* parameter settings for colamd */ - int stats [COLAMD_STATS] /* colamd output statistics and error codes */ -) ; - -int symamd /* return (1) if OK, (0) otherwise */ -( - int n, /* number of rows and columns of A */ - int A [], /* row indices of A */ - int p [], /* column pointers of A */ - int perm [], /* output permutation, size n_col+1 */ - double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ - int stats [COLAMD_STATS], /* output statistics and error codes */ - void * (*allocate) (size_t, size_t), - /* pointer to calloc (ANSI C) or */ - /* mxCalloc (for Matlab mexFunction) */ - void (*release) (void *) - /* pointer to free (ANSI C) or */ - /* mxFree (for Matlab mexFunction) */ -) ; - -void colamd_report -( - int stats [COLAMD_STATS] -) ; - -void symamd_report -( - int stats [COLAMD_STATS] -) ; - -#endif /* COLAMD_H */ - - -#ifdef __cplusplus -} -#endif - diff --git a/src/lpsolve/headers/include/commonlib.h b/src/lpsolve/headers/include/commonlib.h deleted file mode 100644 index 9c2b607d..00000000 --- a/src/lpsolve/headers/include/commonlib.h +++ /dev/null @@ -1,334 +0,0 @@ -#ifndef HEADER_commonlib -#define HEADER_commonlib - -#include -#include -#ifdef WIN32 - #include -#endif - -/* static char SpaceChars[3] = {" " "\7"}; */ -/* static char NumChars[14] = {"0123456789-+."}; */ - -#define BIGNUMBER 1.0e+30 -#define TINYNUMBER 1.0e-04 -#define MACHINEPREC 2.22e-16 -#define MATHPREC 1.0e-16 -#define ERRLIMIT 1.0e-06 - -#ifndef LINEARSEARCH - #define LINEARSEARCH 5 -#endif - -#if 0 - #define INTEGERTIME -#endif - -/* ************************************************************************ */ -/* Define loadable library function headers */ -/* ************************************************************************ */ -#if (defined WIN32) || (defined WIN64) - #define my_LoadLibrary(name) LoadLibrary(name) - #define my_GetProcAddress(handle, name) GetProcAddress(handle, name) - #define my_FreeLibrary(handle) FreeLibrary(handle); \ - handle = NULL -#else - #define my_LoadLibrary(name) dlopen(name, RTLD_LAZY) - #define my_GetProcAddress(handle, name) dlsym(handle, name) - #define my_FreeLibrary(handle) dlclose(handle); \ - handle = NULL -#endif - - -/* ************************************************************************ */ -/* Define sizes of standard number types */ -/* ************************************************************************ */ -#ifndef LLONG - #if defined __BORLANDC__ - #define LLONG __int64 - #elif !defined _MSC_VER || _MSC_VER >= 1310 - #define LLONG long long - #else - #define LLONG __int64 - #endif -#endif - -#ifndef MYBOOL - #if 0 - #define MYBOOL unsigned int - #else - #define MYBOOL unsigned char - #endif -#endif - -#ifndef LPSREAL - #define LPSREAL double -#endif -#ifndef BLAS_prec - #define BLAS_prec "d" /* The BLAS precision prefix must correspond to the LPSREAL type */ -#endif - -#ifndef REALXP - #if 1 - #define REALXP long double /* Set local accumulation variable as long double */ - #else - #define REALXP LPSREAL /* Set local accumulation as default precision */ - #endif -#endif - -#ifndef my_boolstr - #define my_boolstr(x) (!(x) ? "FALSE" : "TRUE") -#endif - -#ifndef NULL - #define NULL 0 -#endif - -#ifndef FALSE - #define FALSE 0 - #define TRUE 1 -#endif - -#ifndef DEF_STRBUFSIZE - #define DEF_STRBUFSIZE 512 -#endif -#ifndef MAXINT32 - #define MAXINT32 2147483647 -#endif -#ifndef MAXUINT32 - #define MAXUINT32 4294967295 -#endif - -#ifndef MAXINT64 - #if defined _LONGLONG || defined __LONG_LONG_MAX__ || defined LLONG_MAX - #define MAXINT64 9223372036854775807ll - #else - #define MAXINT64 9223372036854775807l - #endif -#endif -#ifndef MAXUINT64 - #if defined _LONGLONG || defined __LONG_LONG_MAX__ || defined LLONG_MAX - #define MAXUINT64 18446744073709551615ll - #else - #define MAXUINT64 18446744073709551615l - #endif -#endif - -#ifndef DOFASTMATH - #define DOFASTMATH -#endif - - -#ifndef CALLOC -#define CALLOC(ptr, nr)\ - if(!((void *) ptr = calloc((size_t)(nr), sizeof(*ptr))) && nr) {\ - printf("calloc of %d bytes failed on line %d of file %s\n",\ - (size_t) nr * sizeof(*ptr), __LINE__, __FILE__);\ - } -#endif - -#ifndef MALLOC -#define MALLOC(ptr, nr)\ - if(!((void *) ptr = malloc((size_t)((size_t) (nr) * sizeof(*ptr)))) && nr) {\ - printf("malloc of %d bytes failed on line %d of file %s\n",\ - (size_t) nr * sizeof(*ptr), __LINE__, __FILE__);\ - } -#endif - -#ifndef REALLOC -#define REALLOC(ptr, nr)\ - if(!((void *) ptr = realloc(ptr, (size_t)((size_t) (nr) * sizeof(*ptr)))) && nr) {\ - printf("realloc of %d bytes failed on line %d of file %s\n",\ - (size_t) nr * sizeof(*ptr), __LINE__, __FILE__);\ - } -#endif - -#ifndef FREE -#define FREE(ptr)\ - if((void *) ptr != NULL) {\ - free(ptr);\ - ptr = NULL; \ - } -#endif - -#ifndef MEMCOPY -#define MEMCOPY(nptr, optr, nr)\ - memcpy((nptr), (optr), (size_t)((size_t)(nr) * sizeof(*(optr)))) -#endif - -#ifndef MEMMOVE -#define MEMMOVE(nptr, optr, nr)\ - memmove((nptr), (optr), (size_t)((size_t)(nr) * sizeof(*(optr)))) -#endif - -#ifndef MEMALLOCCOPY -#define MEMALLOCCOPY(nptr, optr, nr)\ - {MALLOC(nptr, (size_t)(nr));\ - MEMCOPY(nptr, optr, (size_t)(nr));} -#endif - -#ifndef STRALLOCCOPY -#define STRALLOCCOPY(nstr, ostr)\ - {nstr = (char *) malloc((size_t) (strlen(ostr) + 1));\ - strcpy(nstr, ostr);} -#endif - -#ifndef MEMCLEAR -/*#define useMMX*/ -#ifdef useMMX - #define MEMCLEAR(ptr, nr)\ - mem_set((ptr), '\0', (size_t)((size_t)(nr) * sizeof(*(ptr)))) -#else - #define MEMCLEAR(ptr, nr)\ - memset((ptr), '\0', (size_t)((size_t)(nr) * sizeof(*(ptr)))) -#endif -#endif - - -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#define MAX(x, y) ((x) > (y) ? (x) : (y)) -#define SETMIN(x, y) if((x) > (y)) x = y -#define SETMAX(x, y) if((x) < (y)) x = y -#define LIMIT(lo, x, hi) ((x < (lo) ? lo : ((x) > hi ? hi : x))) -#define BETWEEN(x, a, b) (MYBOOL) (((x)-(a)) * ((x)-(b)) <= 0) -#define IF(t, x, y) ((t) ? (x) : (y)) -#define SIGN(x) ((x) < 0 ? -1 : 1) - -#define DELTA_SIZE(newSize, oldSize) ((int) ((newSize) * MIN(1.33, pow(1.5, fabs((double)newSize)/((oldSize+newSize)+1))))) - -#ifndef CMP_CALLMODEL -#if (defined WIN32) || (defined WIN64) - #define CMP_CALLMODEL _cdecl -#else - #define CMP_CALLMODEL -#endif -#endif - -typedef int (CMP_CALLMODEL findCompare_func)(const void *current, const void *candidate); -#define CMP_COMPARE(current, candidate) ( current < candidate ? -1 : (current > candidate ? 1 : 0) ) -#define CMP_ATTRIBUTES(item) (((char *) attributes)+(item)*recsize) -#define CMP_TAGS(item) (((char *) tags)+(item)*tagsize) - -#ifndef UNIONTYPE - #ifdef __cplusplus - #define UNIONTYPE - #else - #define UNIONTYPE union - #endif -#endif - -/* This defines a 16 byte sort record (in both 32 and 64 bit OS-es) */ -typedef struct _QSORTrec1 -{ - void *ptr; - void *ptr2; -} QSORTrec1; -typedef struct _QSORTrec2 -{ - void *ptr; - double realval; -} QSORTrec2; -typedef struct _QSORTrec3 -{ - void *ptr; - int intval; - int intpar1; -} QSORTrec3; -typedef struct _QSORTrec4 -{ - LPSREAL realval; - int intval; - int intpar1; -} QSORTrec4; -typedef struct _QSORTrec5 -{ - double realval; - long int longval; -} QSORTrec5; -typedef struct _QSORTrec6 -{ - double realval; - double realpar1; -} QSORTrec6; -typedef struct _QSORTrec7 -{ - int intval; - int intpar1; - int intpar2; - int intpar3; -} QSORTrec7; -union QSORTrec -{ - QSORTrec1 pvoid2; - QSORTrec2 pvoidreal; - QSORTrec3 pvoidint2; - QSORTrec4 realint2; - QSORTrec5 reallong; - QSORTrec6 real2; - QSORTrec7 int4; -}; - - -#ifdef __cplusplus - extern "C" { -#endif - -int intpow(int base, int exponent); -int mod(int n, int d); - -void strtoup(char *s); -void strtolo(char *s); -void strcpyup(char *t, char *s); -void strcpylo(char *t, char *s); - -MYBOOL so_stdname(char *stdname, char *descname, int buflen); -int gcd(LLONG a, LLONG b, int *c, int *d); - -int findIndex(int target, int *attributes, int count, int offset); -int findIndexEx(void *target, void *attributes, int count, int offset, int recsize, findCompare_func findCompare, MYBOOL ascending); - -void qsortex_swap(void *attributes, int l, int r, int recsize, - void *tags, int tagsize, char *save, char *savetag); - -int qsortex(void *attributes, int count, int offset, int recsize, MYBOOL descending, findCompare_func findCompare, void *tags, int tagsize); - -int CMP_CALLMODEL compareCHAR(const void *current, const void *candidate); -int CMP_CALLMODEL compareINT(const void *current, const void *candidate); -int CMP_CALLMODEL compareREAL(const void *current, const void *candidate); -void hpsort(void *attributes, int count, int offset, int recsize, MYBOOL descending, findCompare_func findCompare); -void hpsortex(void *attributes, int count, int offset, int recsize, MYBOOL descending, findCompare_func findCompare, int *tags); - -void QS_swap(UNIONTYPE QSORTrec a[], int i, int j); -int QS_addfirst(UNIONTYPE QSORTrec a[], void *mydata); -int QS_append(UNIONTYPE QSORTrec a[], int ipos, void *mydata); -void QS_replace(UNIONTYPE QSORTrec a[], int ipos, void *mydata); -void QS_insert(UNIONTYPE QSORTrec a[], int ipos, void *mydata, int epos); -void QS_delete(UNIONTYPE QSORTrec a[], int ipos, int epos); -MYBOOL QS_execute(UNIONTYPE QSORTrec a[], int count, findCompare_func findCompare, int *nswaps); - -int sortByREAL(int *item, LPSREAL *weight, int size, int offset, MYBOOL unique); -int sortByINT(int *item, int *weight, int size, int offset, MYBOOL unique); -LPSREAL sortREALByINT(LPSREAL *item, int *weight, int size, int offset, MYBOOL unique); - -double timeNow(void); - -void blockWriteBOOL(FILE *output, char *label, MYBOOL *myvector, int first, int last, MYBOOL asRaw); -void blockWriteINT(FILE *output, char *label, int *myvector, int first, int last); -void blockWriteREAL(FILE *output, char *label, LPSREAL *myvector, int first, int last); - -void printvec( int n, LPSREAL *x, int modulo ); -void printmatSQ( int size, int n, LPSREAL *X, int modulo ); -void printmatUT( int size, int n, LPSREAL *U, int modulo ); - -unsigned int catchFPU(unsigned int mask); - -#if defined _MSC_VER -int fileCount( char *filemask ); -MYBOOL fileSearchPath( char *envvar, char *searchfile, char *foundpath ); -#endif - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_commonlib */ diff --git a/src/lpsolve/headers/include/ini.h b/src/lpsolve/headers/include/ini.h deleted file mode 100644 index 3bef0d7b..00000000 --- a/src/lpsolve/headers/include/ini.h +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#ifdef __cplusplus -__EXTERN_C { -#endif - -extern FILE *ini_create(char *filename); -extern FILE *ini_open(char *filename); -extern void ini_writecomment(FILE *fp, char *comment); -extern void ini_writeheader(FILE *fp, char *header, int addnewline); -extern void ini_writedata(FILE *fp, char *name, char *data); -extern int ini_readdata(FILE *fp, char *data, int szdata, int withcomment); -extern void ini_close(FILE *fp); - -#ifdef __cplusplus -} -#endif diff --git a/src/lpsolve/headers/include/lp_BFP.h b/src/lpsolve/headers/include/lp_BFP.h deleted file mode 100644 index e546db83..00000000 --- a/src/lpsolve/headers/include/lp_BFP.h +++ /dev/null @@ -1,82 +0,0 @@ - -/* ---------------------------------------------------------------------------------- */ -/* lp_solve v5+ headers for basis inversion / factorization libraries */ -/* ---------------------------------------------------------------------------------- */ -#define BFP_STATUS_RANKLOSS -1 -#define BFP_STATUS_SUCCESS 0 -#define BFP_STATUS_SINGULAR 1 -#define BFP_STATUS_UNSTABLE 2 -#define BFP_STATUS_NOPIVOT 3 -#define BFP_STATUS_DIMERROR 4 -#define BFP_STATUS_DUPLICATE 5 -#define BFP_STATUS_NOMEMORY 6 -#define BFP_STATUS_ERROR 7 /* Unspecified, command-related error */ -#define BFP_STATUS_FATAL 8 - -#define BFP_STAT_ERROR -1 -#define BFP_STAT_REFACT_TOTAL 0 -#define BFP_STAT_REFACT_TIMED 1 -#define BFP_STAT_REFACT_DENSE 2 - -#ifndef BFP_CALLMODEL - #ifdef WIN32 - #define BFP_CALLMODEL __stdcall /* "Standard" call model */ - #else - #define BFP_CALLMODEL - #endif -#endif - -#ifdef RoleIsExternalInvEngine - #define __BFP_EXPORT_TYPE __EXPORT_TYPE -#else - #define __BFP_EXPORT_TYPE -#endif - - -/* Routines with UNIQUE implementations for each inversion engine */ -/* ---------------------------------------------------------------------------------- */ -char __BFP_EXPORT_TYPE *(BFP_CALLMODEL bfp_name)(void); -void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_free)(lprec *lp); -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_resize)(lprec *lp, int newsize); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_nonzeros)(lprec *lp, MYBOOL maximum); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_memallocated)(lprec *lp); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_preparefactorization)(lprec *lp); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_factorize)(lprec *lp, int uservars, int Bsize, MYBOOL *usedpos, MYBOOL final); -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_finishupdate)(lprec *lp, MYBOOL changesign); -void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_ftran_normal)(lprec *lp, LPSREAL *pcol, int *nzidx); -void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_ftran_prepare)(lprec *lp, LPSREAL *pcol, int *nzidx); -void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_btran_normal)(lprec *lp, LPSREAL *prow, int *nzidx); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_status)(lprec *lp); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_findredundant)(lprec *lp, int items, getcolumnex_func cb, int *maprow, int*mapcol); - - -/* Routines SHARED for all inverse implementations; located in lp_BFP1.c */ -/* ---------------------------------------------------------------------------------- */ -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_compatible)(lprec *lp, int bfpversion, int lpversion, int sizeofvar); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_indexbase)(lprec *lp); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_rowoffset)(lprec *lp); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_pivotmax)(lprec *lp); -LPSREAL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_efficiency)(lprec *lp); -LPSREAL __BFP_EXPORT_TYPE *(BFP_CALLMODEL bfp_pivotvector)(lprec *lp); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_pivotcount)(lprec *lp); -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_mustrefactorize)(lprec *lp); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_refactcount)(lprec *lp, int kind); -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_isSetI)(lprec *lp); -int *bfp_createMDO(lprec *lp, MYBOOL *usedpos, int count, MYBOOL doMDO); -void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_updaterefactstats)(lprec *lp); -int BFP_CALLMODEL bfp_rowextra(lprec *lp); - -/* Routines with OPTIONAL SHARED code; template routines suitable for canned */ -/* inverse engines are located in lp_BFP2.c */ -/* ---------------------------------------------------------------------------------- */ -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_init)(lprec *lp, int size, int deltasize, char *options); -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_restart)(lprec *lp); -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_implicitslack)(lprec *lp); -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_pivotalloc)(lprec *lp, int newsize); -int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_colcount)(lprec *lp); -MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_canresetbasis)(lprec *lp); -void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_finishfactorization)(lprec *lp); -LREAL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_prepareupdate)(lprec *lp, int row_nr, int col_nr, LPSREAL *pcol); -LPSREAL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_pivotRHS)(lprec *lp, LREAL theta, LPSREAL *pcol); -void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_btran_double)(lprec *lp, LPSREAL *prow, int *pnzidx, LPSREAL *drow, int *dnzidx); - diff --git a/src/lpsolve/headers/include/lp_Hash.h b/src/lpsolve/headers/include/lp_Hash.h deleted file mode 100644 index b286960c..00000000 --- a/src/lpsolve/headers/include/lp_Hash.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef HEADER_lp_hash -#define HEADER_lp_hash - -/* For row and column name hash tables */ - -typedef struct _hashelem -{ - char *name; - int index; - struct _hashelem *next; - struct _hashelem *nextelem; -} hashelem; - -typedef struct /* _hashtable */ -{ - hashelem **table; - int size; - int base; - int count; - struct _hashelem *first; - struct _hashelem *last; -} hashtable; - -#ifdef __cplusplus -extern "C" { -#endif - -STATIC hashtable *create_hash_table(int size, int base); -STATIC void free_hash_table(hashtable *ht); -STATIC hashelem *findhash(const char *name, hashtable *ht); -STATIC hashelem *puthash(const char *name, int index, hashelem **list, hashtable *ht); -STATIC void drophash(const char *name, hashelem **list, hashtable *ht); -STATIC void free_hash_item(hashelem **hp); -STATIC hashtable *copy_hash_table(hashtable *ht, hashelem **list, int newsize); -STATIC int find_var(lprec *lp, char *name, MYBOOL verbose); -STATIC int find_row(lprec *lp, char *name, MYBOOL Unconstrained_rows_found); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_hash */ - diff --git a/src/lpsolve/headers/include/lp_LUSOL.h b/src/lpsolve/headers/include/lp_LUSOL.h deleted file mode 100644 index 46db4d46..00000000 --- a/src/lpsolve/headers/include/lp_LUSOL.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef HEADER_lp_LUSOL -#define HEADER_lp_LUSOL - -/* Include libraries for this inverse system */ -#include "lp_types.h" -#include "lusol.h" - -/* LUSOL defines */ -#ifdef WIN32 -# define LUSOL_UseBLAS -#endif -/*#define MAPSINGULARCOLUMN*/ -#define MATINDEXBASE LUSOL_ARRAYOFFSET /* Inversion engine index start for arrays */ -#define LU_START_SIZE 10000 /* Start size of LU; realloc'ed if needed */ -#define DEF_MAXPIVOT 250 /* Maximum number of pivots before refactorization */ -#define MAX_DELTAFILLIN 2.0 /* Do refactorizations based on sparsity considerations */ -#define TIGHTENAFTER 10 /* Tighten LU pivot criteria only after this number of singularities */ - -/* typedef */ struct _INVrec -{ - int status; /* Last operation status code */ - int dimcount; /* The actual number of LU rows/columns */ - int dimalloc; /* The allocated LU rows/columns size */ - int user_colcount; /* The number of user LU columns */ - LUSOLrec *LUSOL; - int col_enter; /* The full index of the entering column */ - int col_leave; /* The full index of the leaving column */ - int col_pos; /* The B column to be changed at the next update using data in value[.]*/ - LPSREAL *value; - LPSREAL *pcol; /* Reference to the elimination vector */ - LPSREAL theta_enter; /* Value of the entering column theta */ - - int max_Bsize; /* The largest B matrix of user variables */ - int max_colcount; /* The maximum number of user columns in LU */ - int max_LUsize; /* The largest NZ-count of LU-files generated */ - int num_refact; /* Number of times the basis was refactored */ - int num_timed_refact; - int num_dense_refact; - double time_refactstart; /* Time since start of last refactorization-pivots cyle */ - double time_refactnext; /* Time estimated to next refactorization */ - int num_pivots; /* Number of pivots since last refactorization */ - int num_singular; /* The total number of singular updates */ - char *opts; - MYBOOL is_dirty; /* Specifies if a column is incompletely processed */ - MYBOOL force_refact; /* Force refactorization at the next opportunity */ - MYBOOL timed_refact; /* Set if timer-driven refactorization should be active */ - MYBOOL set_Bidentity; /* Force B to be the identity matrix at the next refactorization */ -} /* INVrec */; - - -#ifdef __cplusplus -/* namespace LUSOL */ -extern "C" { -#endif - -/* Put function headers here */ -#include "lp_BFP.h" - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_LUSOL */ diff --git a/src/lpsolve/headers/include/lp_MPS.h b/src/lpsolve/headers/include/lp_MPS.h deleted file mode 100644 index 0d970269..00000000 --- a/src/lpsolve/headers/include/lp_MPS.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef HEADER_lp_MPS -#define HEADER_lp_MPS - -#include "lp_types.h" - -/* For MPS file reading and writing */ -#define ROWNAMEMASK "R%d" -#define ROWNAMEMASK2 "r%d" -#define COLNAMEMASK "C%d" -#define COLNAMEMASK2 "c%d" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Read an MPS file */ -MYBOOL MPS_readfile(lprec **newlp, char *filename, int typeMPS, int verbose); -MYBOOL __WINAPI MPS_readhandle(lprec **newlp, FILE *filehandle, int typeMPS, int verbose); - -/* Write a MPS file to output */ -MYBOOL MPS_writefile(lprec *lp, int typeMPS, char *filename); -MYBOOL MPS_writehandle(lprec *lp, int typeMPS, FILE *output); - -/* Read and write BAS files */ -MYBOOL MPS_readBAS(lprec *lp, int typeMPS, char *filename, char *info); -MYBOOL MPS_writeBAS(lprec *lp, int typeMPS, char *filename); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_MPS */ - diff --git a/src/lpsolve/headers/include/lp_SOS.h b/src/lpsolve/headers/include/lp_SOS.h deleted file mode 100644 index 6f995c08..00000000 --- a/src/lpsolve/headers/include/lp_SOS.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef HEADER_lp_SOS -#define HEADER_lp_SOS - -/* Specially Ordered Sets (SOS) prototypes and settings */ -/* ------------------------------------------------------------------------- */ - -#include "lp_types.h" -#include "lp_utils.h" -#include "lp_matrix.h" - - -/* SOS constraint defines */ -/* ------------------------------------------------------------------------- */ -#define SOS1 1 -#define SOS2 2 -#define SOS3 -1 -#define SOSn MAXINT32 -#define SOS_START_SIZE 10 /* Start size of SOS_list array; realloced if needed */ - -/* Define SOS_is_feasible() return values */ -/* ------------------------------------------------------------------------- */ -#define SOS3_INCOMPLETE -2 -#define SOS_INCOMPLETE -1 -#define SOS_COMPLETE 0 -#define SOS_INFEASIBLE 1 -#define SOS_INTERNALERROR 2 - - -typedef struct _SOSgroup SOSgroup; - -typedef struct _SOSrec -{ - SOSgroup *parent; - int tagorder; - char *name; - int type; - MYBOOL isGUB; - int size; - int priority; - int *members; - LPSREAL *weights; - int *membersSorted; - int *membersMapped; -} SOSrec; - -/* typedef */ struct _SOSgroup -{ - lprec *lp; /* Pointer to owner */ - SOSrec **sos_list; /* Array of pointers to SOS lists */ - int sos_alloc; /* Size allocated to specially ordered sets (SOS1, SOS2...) */ - int sos_count; /* Number of specially ordered sets (SOS1, SOS2...) */ - int maxorder; /* The highest-order SOS in the group */ - int sos1_count; /* Number of the lowest order SOS in the group */ - int *membership; /* Array of variable-sorted indeces to SOSes that the variable is member of */ - int *memberpos; /* Starting positions of the each column's membership list */ -} /* SOSgroup */; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* SOS storage structure */ -STATIC SOSgroup *create_SOSgroup(lprec *lp); -STATIC void resize_SOSgroup(SOSgroup *group); -STATIC int append_SOSgroup(SOSgroup *group, SOSrec *SOS); -STATIC int clean_SOSgroup(SOSgroup *group, MYBOOL forceupdatemap); -STATIC void free_SOSgroup(SOSgroup **group); - -STATIC SOSrec *create_SOSrec(SOSgroup *group, char *name, int type, int priority, int size, int *variables, LPSREAL *weights); -STATIC MYBOOL delete_SOSrec(SOSgroup *group, int sosindex); -STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, LPSREAL *weights); -STATIC void free_SOSrec(SOSrec *SOS); - -/* SOS utilities */ -STATIC int make_SOSchain(lprec *lp, MYBOOL forceresort); -STATIC int SOS_member_updatemap(SOSgroup *group); -STATIC MYBOOL SOS_member_sortlist(SOSgroup *group, int sosindex); -STATIC MYBOOL SOS_shift_col(SOSgroup *group, int sosindex, int column, int delta, LLrec *usedmap, MYBOOL forceresort); -int SOS_member_delete(SOSgroup *group, int sosindex, int member); -int SOS_get_type(SOSgroup *group, int sosindex); -int SOS_infeasible(SOSgroup *group, int sosindex); -int SOS_member_index(SOSgroup *group, int sosindex, int member); -int SOS_member_count(SOSgroup *group, int sosindex); -int SOS_memberships(SOSgroup *group, int column); -int *SOS_get_candidates(SOSgroup *group, int sosindex, int column, MYBOOL excludetarget, LPSREAL *upbound, LPSREAL *lobound); -int SOS_is_member(SOSgroup *group, int sosindex, int column); -MYBOOL SOS_is_member_of_type(SOSgroup *group, int column, int sostype); -MYBOOL SOS_set_GUB(SOSgroup *group, int sosindex, MYBOOL state); -MYBOOL SOS_is_GUB(SOSgroup *group, int sosindex); -MYBOOL SOS_is_marked(SOSgroup *group, int sosindex, int column); -MYBOOL SOS_is_active(SOSgroup *group, int sosindex, int column); -MYBOOL SOS_is_full(SOSgroup *group, int sosindex, int column, MYBOOL activeonly); -MYBOOL SOS_can_activate(SOSgroup *group, int sosindex, int column); -MYBOOL SOS_set_marked(SOSgroup *group, int sosindex, int column, MYBOOL asactive); -MYBOOL SOS_unmark(SOSgroup *group, int sosindex, int column); -int SOS_fix_unmarked(SOSgroup *group, int sosindex, int variable, LPSREAL *bound, LPSREAL value, - MYBOOL isupper, int *diffcount, DeltaVrec *changelog); -int SOS_fix_list(SOSgroup *group, int sosindex, int variable, LPSREAL *bound, - int *varlist, MYBOOL isleft, DeltaVrec *changelog); -int SOS_is_satisfied(SOSgroup *group, int sosindex, LPSREAL *solution); -MYBOOL SOS_is_feasible(SOSgroup *group, int sosindex, LPSREAL *solution); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_SOS */ diff --git a/src/lpsolve/headers/include/lp_bit.h b/src/lpsolve/headers/include/lp_bit.h deleted file mode 100644 index 4d33eeea..00000000 --- a/src/lpsolve/headers/include/lp_bit.h +++ /dev/null @@ -1,17 +0,0 @@ -#include "lp_types.h" - -#if defined INLINE -# define MYINLINE INLINE -#else -# define MYINLINE static -#endif - -MYINLINE void set_biton(MYBOOL *bitarray, int item); -MYINLINE MYBOOL is_biton(MYBOOL *bitarray, int item); - -/* -MYINLINE void set_bitoff(MYBOOL *bitarray, int item) -{ - bitarray[item / 8] &= ~(1 << (item % 8)); -} -*/ diff --git a/src/lpsolve/headers/include/lp_crash.h b/src/lpsolve/headers/include/lp_crash.h deleted file mode 100644 index eb1f84de..00000000 --- a/src/lpsolve/headers/include/lp_crash.h +++ /dev/null @@ -1,27 +0,0 @@ - -#ifndef HEADER_lp_crash -#define HEADER_lp_crash - - -#include "lp_types.h" - -#define CRASH_SIMPLESCALE /* Specify if we should use a simple absolute scaling threshold */ - -#define CRASH_THRESHOLD 0.167 -#define CRASH_SPACER 10 -#define CRASH_WEIGHT 0.500 - - - -#ifdef __cplusplus -__EXTERN_C { -#endif - -STATIC MYBOOL crash_basis(lprec *lp); - -#ifdef __cplusplus -} -#endif - -#endif /* HEADER_lp_crash */ - diff --git a/src/lpsolve/headers/include/lp_lib.h b/src/lpsolve/headers/include/lp_lib.h deleted file mode 100644 index 22eb9ae8..00000000 --- a/src/lpsolve/headers/include/lp_lib.h +++ /dev/null @@ -1,2297 +0,0 @@ - -#ifndef HEADER_lp_lib -#define HEADER_lp_lib - -/* -------------------------------------------------------------------------- - - This is the main library header file for the lp_solve v5.0 release - - Starting at version 3.0, LP_Solve is released under the LGPL license. - For full information, see the enclosed file LGPL.txt. - - Original developer: Michel Berkelaar - michel@ics.ele.tue.nl - Most changes 1.5-2.0: Jeroen Dirks - jeroend@tor.numetrix.com - Changes 3.2-4.0: Kjell Eikland - kjell.eikland@broadpark.no - (Simplex code, SOS, SC, code optimization) - Peter Notebaert - lpsolve@peno.be - (Sensitivity analysis, documentation) - Changes 5.0+: Kjell Eikland - kjell.eikland@broadpark.no - (BFP, XLI, simplex, B&B, code modularization) - Peter Notebaert - lpsolve@peno.be - (Sensitivity analysis, New lp parser, LINDO (XLI) - parser, VB/.NET interface, documentation) - - Release notes: - - Version 4.0 enhances version 3.2 in terms of internal program/simplex - architecture, call level interfaces, data layout, features and contains - several bug fixes. There is now complete support for semi-continuous - variables and SOS constructions. In the process, a complete API - was added. The MPS parser has been amended to support this. - Sensitivity analysis and variouse bug fixes was provided by Peter - Notebaert in 4.0 sub-releases. Peter also wrote a complete - documentation of the API and contributed a VB interface, both of which - significantly enhanced the accessibility of lp_solve. - - Version 5.0 is a major rewrite and code cleanup. The main additions that - drove forward this cleanup were the modular inversion logic with optimal - column ordering, addition of primal phase 1 and dual phase 2 logic for - full flexibility in the selection of primal and dual simplex modes, - DEVEX and steepest edge pivot selection, along with dynamic cycling - detection and prevention. This cleanup made it possible to harmonize the - internal rounding principles, contributing to increased numerical stability. - - Version 5.1 rearranges the matrix storage model by enabling both legacy - element record-based storage and split vector storage. In addition the - lprec structure is optimized and additional routines are added, mainly for - sparse vector additions and enhanced XLI functionality. Support for XML- - based models was added on the basis of the LPFML schema via xli_LPFML. - - Version 5.2 removes the objective function from the constraint matrix, - adds a number of presolve options and speed them up. Degeneracy handling - is significantly improved. Support for XLI_ZIMPL was added. - Multiple and partial pricing has been enhanced and activated. - - -------------------------------------------------------------------------- */ -/* Define user program feature option switches */ -/* ------------------------------------------------------------------------- */ - -# if defined _WIN32 && !defined __GNUC__ -# define isnan _isnan -# endif -#if defined NOISNAN -# define isnan(x) FALSE -#endif - -// https://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html -#define MAKE_PEDANTIC_HAPPY(fptr) *(void **)(&fptr) - -#define SETMASK(variable, mask) variable |= mask -#define CLEARMASK(variable, mask) variable &= ~(mask) -#define TOGGLEMASK(variable, mask) variable ^= mask -#define ISMASKSET(variable, mask) (MYBOOL) (((variable) & (mask)) != 0) - -/* Utility/system settings */ -/* ------------------------------------------------------------------------- */ -/*#define INTEGERTIME */ /* Set use of lower-resolution timer */ - - -/* New v5.0+ simplex/optimization features and settings */ -/* ------------------------------------------------------------------------- */ -/*#define NoRowScaleOF */ /* Optionally skip row-scaling of the OF */ -#define DoMatrixRounding /* Round A matrix elements to precision */ -#define DoBorderRounding /* Round RHS, bounds and ranges to precision */ -#define Phase1EliminateRedundant /* Remove rows of redundant artificials */ -#define FixViolatedOptimal -#define ImproveSolutionPrecision /* Round optimal solution values */ -/*#define IncreasePivotOnReducedAccuracy */ /* Increase epspivot on instability */ -/*#define FixInaccurateDualMinit */ /* Reinvert on inaccuracy in dual minits */ -/*#define EnforcePositiveTheta */ /* Ensure that the theta range is valid */ -#define ResetMinitOnReinvert -/*#define UsePrimalReducedCostUpdate */ /* Not tested */ -/*#define UseDualReducedCostUpdate */ /* Seems Ok, but slower than expected */ -/*#ifdef UseLegacyExtrad */ /* Use v3.2- style Extrad method */ -#define UseMilpExpandedRCF /* Non-ints in reduced cost bound tightening */ -/*#define UseMilpSlacksRCF */ /* Slacks in reduced cost bound tightening (degen - prone); requires !SlackInitMinusInf */ -#define LegacySlackDefinition /* Slack as the "value of the constraint" */ - - -/* Development features (change at own risk) */ -/* ------------------------------------------------------------------------- */ -/*#define MIPboundWithOF */ /* Enable to detect OF constraint for use during B&B */ -/*#define SlackInitMinusInf */ /* Slacks have 0 LB if this is not defined */ -#define FULLYBOUNDEDSIMPLEX FALSE /* WARNING: Activate at your own risk! */ - - -/* Specify use of the basic linear algebra subroutine library */ -/* ------------------------------------------------------------------------- */ -#define libBLAS 2 /* 0: No, 1: Internal, 2: External */ -#define libnameBLAS "myBLAS" - - -/* Active inverse logic (default is optimized original etaPFI) */ -/* ------------------------------------------------------------------------- */ -#if !defined LoadInverseLib -# define LoadInverseLib TRUE /* Enable alternate inverse libraries */ -#endif -/*#define ExcludeNativeInverse */ /* Disable INVERSE_ACTIVE inverse engine */ - -#define DEF_OBJINBASIS TRUE /* Additional rows inserted at the top (1 => OF) */ - -#define INVERSE_NONE -1 -#define INVERSE_LEGACY 0 -#define INVERSE_ETAPFI 1 -#define INVERSE_LUMOD 2 -#define INVERSE_LUSOL 3 -#define INVERSE_GLPKLU 4 - -#ifndef RoleIsExternalInvEngine /* Defined in inverse DLL drivers */ - #ifdef ExcludeNativeInverse - #define INVERSE_ACTIVE INVERSE_NONE /* Disable native engine */ - #else - #define INVERSE_ACTIVE INVERSE_LEGACY /* User or DLL-selected */ - #endif -#endif - - -/* Active external language interface logic (default is none) */ -/* ------------------------------------------------------------------------- */ -#if !defined LoadLanguageLib -# define LoadLanguageLib TRUE /* Enable alternate language libraries */ -#endif -#define ExcludeNativeLanguage /* Disable LANGUAGE_ACTIVE XLI */ - -#define LANGUAGE_NONE -1 -#define LANGUAGE_LEGACYLP 0 -#define LANGUAGE_CPLEXLP 1 -#define LANGUAGE_MPSX 2 -#define LANGUAGE_LPFML 3 -#define LANGUAGE_MATHPROG 4 -#define LANGUAGE_AMPL 5 -#define LANGUAGE_GAMS 6 -#define LANGUAGE_ZIMPL 7 -#define LANGUAGE_S 8 -#define LANGUAGE_R 9 -#define LANGUAGE_MATLAB 10 -#define LANGUAGE_OMATRIX 11 -#define LANGUAGE_SCILAB 12 -#define LANGUAGE_OCTAVE 13 -#define LANGUAGE_EMPS 14 - -#ifndef RoleIsExternalLanguageEngine /* Defined in XLI driver libraries */ - #ifdef ExcludeNativeLanguage - #define LANGUAGE_ACTIVE LANGUAGE_NONE /* Disable native engine */ - #else - #define LANGUAGE_ACTIVE LANGUAGE_CPLEXLP /* User or DLL-selected */ - #endif -#endif - - -/* Default parameters and tolerances */ -/* ------------------------------------------------------------------------- */ -#define OriginalPARAM 0 -#define ProductionPARAM 1 -#define ChvatalPARAM 2 -#define LoosePARAM 3 -#if 1 - #define ActivePARAM ProductionPARAM -#else - #define ActivePARAM LoosePARAM -#endif - - -/* Miscellaneous settings */ -/* ------------------------------------------------------------------------- */ -#ifndef Paranoia - #ifdef _DEBUG - #define Paranoia - #endif -#endif - - -/* Program version data */ -/* ------------------------------------------------------------------------- */ -#define MAJORVERSION 5 -#define MINORVERSION 5 -#define RELEASE 2 -#define BUILD 0 -#define BFPVERSION 12 /* Checked against bfp_compatible() */ -#define XLIVERSION 12 /* Checked against xli_compatible() */ -/* Note that both BFPVERSION and XLIVERSION typically have to be incremented - in the case that the lprec structure changes. */ - - -/* Include/header files */ -/* ------------------------------------------------------------------------- */ -#include -#include -#include -#include -#include - -#include "lp_types.h" -#include "lp_utils.h" - -#if (LoadInverseLib == TRUE) || (LoadLanguageLib == TRUE) - #ifdef WIN32 - #include - #else - #include - #endif -#endif - -#ifndef BFP_CALLMODEL - #ifdef WIN32 - #define BFP_CALLMODEL __stdcall /* "Standard" call model */ - #else - #define BFP_CALLMODEL - #endif -#endif -#ifndef XLI_CALLMODEL - #define XLI_CALLMODEL BFP_CALLMODEL -#endif - -#define REGISTER register /* Speed up certain operations */ - - -/* Definition of program constrants */ -/* ------------------------------------------------------------------------- */ -#define SIMPLEX_UNDEFINED 0 -#define SIMPLEX_Phase1_PRIMAL 1 -#define SIMPLEX_Phase1_DUAL 2 -#define SIMPLEX_Phase2_PRIMAL 4 -#define SIMPLEX_Phase2_DUAL 8 -#define SIMPLEX_DYNAMIC 16 -#define SIMPLEX_AUTODUALIZE 32 - -#define SIMPLEX_PRIMAL_PRIMAL (SIMPLEX_Phase1_PRIMAL + SIMPLEX_Phase2_PRIMAL) -#define SIMPLEX_DUAL_PRIMAL (SIMPLEX_Phase1_DUAL + SIMPLEX_Phase2_PRIMAL) -#define SIMPLEX_PRIMAL_DUAL (SIMPLEX_Phase1_PRIMAL + SIMPLEX_Phase2_DUAL) -#define SIMPLEX_DUAL_DUAL (SIMPLEX_Phase1_DUAL + SIMPLEX_Phase2_DUAL) -#define SIMPLEX_DEFAULT (SIMPLEX_DUAL_PRIMAL) - -/* Variable codes (internal) */ -#define ISREAL 0 -#define ISINTEGER 1 -#define ISSEMI 2 -#define ISSOS 4 -#define ISSOSTEMPINT 8 -#define ISGUB 16 - -/* Presolve defines */ -#define PRESOLVE_NONE 0 -#define PRESOLVE_ROWS 1 -#define PRESOLVE_COLS 2 -#define PRESOLVE_LINDEP 4 -#define PRESOLVE_AGGREGATE 8 /* Not implemented */ -#define PRESOLVE_SPARSER 16 /* Not implemented */ -#define PRESOLVE_SOS 32 -#define PRESOLVE_REDUCEMIP 64 -#define PRESOLVE_KNAPSACK 128 /* Implementation not tested completely */ -#define PRESOLVE_ELIMEQ2 256 -#define PRESOLVE_IMPLIEDFREE 512 -#define PRESOLVE_REDUCEGCD 1024 -#define PRESOLVE_PROBEFIX 2048 -#define PRESOLVE_PROBEREDUCE 4096 -#define PRESOLVE_ROWDOMINATE 8192 -#define PRESOLVE_COLDOMINATE 16384 /* Reduced functionality, should be expanded */ -#define PRESOLVE_MERGEROWS 32768 -#define PRESOLVE_IMPLIEDSLK 65536 -#define PRESOLVE_COLFIXDUAL 131072 -#define PRESOLVE_BOUNDS 262144 -#define PRESOLVE_LASTMASKMODE (PRESOLVE_DUALS - 1) -#define PRESOLVE_DUALS 524288 -#define PRESOLVE_SENSDUALS 1048576 - -/* Basis crash options */ -#define CRASH_NONE 0 -#define CRASH_NONBASICBOUNDS 1 -#define CRASH_MOSTFEASIBLE 2 -#define CRASH_LEASTDEGENERATE 3 - -/* Solution recomputation options (internal) */ -#define INITSOL_SHIFTZERO 0 -#define INITSOL_USEZERO 1 -#define INITSOL_ORIGINAL 2 - -/* Strategy codes to avoid or recover from degenerate pivots, - infeasibility or numeric errors via randomized bound relaxation */ -#define ANTIDEGEN_NONE 0 -#define ANTIDEGEN_FIXEDVARS 1 -#define ANTIDEGEN_COLUMNCHECK 2 -#define ANTIDEGEN_STALLING 4 -#define ANTIDEGEN_NUMFAILURE 8 -#define ANTIDEGEN_LOSTFEAS 16 -#define ANTIDEGEN_INFEASIBLE 32 -#define ANTIDEGEN_DYNAMIC 64 -#define ANTIDEGEN_DURINGBB 128 -#define ANTIDEGEN_RHSPERTURB 256 -#define ANTIDEGEN_BOUNDFLIP 512 -#define ANTIDEGEN_DEFAULT (ANTIDEGEN_FIXEDVARS | ANTIDEGEN_STALLING /* | ANTIDEGEN_INFEASIBLE */) - -/* REPORT defines */ -#define NEUTRAL 0 -#define CRITICAL 1 -#define SEVERE 2 -#define IMPORTANT 3 -#define NORMAL 4 -#define DETAILED 5 -#define FULL 6 - -/* MESSAGE defines */ -#define MSG_NONE 0 -#define MSG_PRESOLVE 1 -#define MSG_ITERATION 2 -#define MSG_INVERT 4 -#define MSG_LPFEASIBLE 8 -#define MSG_LPOPTIMAL 16 -#define MSG_LPEQUAL 32 -#define MSG_LPBETTER 64 -#define MSG_MILPFEASIBLE 128 -#define MSG_MILPEQUAL 256 -#define MSG_MILPBETTER 512 -#define MSG_MILPSTRATEGY 1024 -#define MSG_MILPOPTIMAL 2048 -#define MSG_PERFORMANCE 4096 -#define MSG_INITPSEUDOCOST 8192 - -/* MPS file types */ -#define MPSFIXED 1 -#define MPSFREE 2 -#define MPSIBM 4 -#define MPSNEGOBJCONST 8 - -#define MPS_FREE (MPSFREE<<2) -#define MPS_IBM (MPSIBM<<2) -#define MPS_NEGOBJCONST (MPSNEGOBJCONST<<2) - -/* MPS defines (internal) */ -#define MPSUNDEF -4 -#define MPSNAME -3 -#define MPSOBJSENSE -2 -#define MPSOBJNAME -1 -#define MPSROWS 0 -#define MPSCOLUMNS 1 -#define MPSRHS 2 -#define MPSBOUNDS 3 -#define MPSRANGES 4 -#define MPSSOS 5 - -#define MPSVARMASK "%-8s" -#define MPSVALUEMASK "%12g" - -/* Constraint type codes (internal) */ -#define ROWTYPE_EMPTY 0 -#define ROWTYPE_LE 1 -#define ROWTYPE_GE 2 -#define ROWTYPE_EQ 3 -#define ROWTYPE_CONSTRAINT ROWTYPE_EQ /* This is the mask for modes */ -#define ROWTYPE_OF 4 -#define ROWTYPE_INACTIVE 8 -#define ROWTYPE_RELAX 16 -#define ROWTYPE_GUB 32 -#define ROWTYPE_OFMAX (ROWTYPE_OF + ROWTYPE_GE) -#define ROWTYPE_OFMIN (ROWTYPE_OF + ROWTYPE_LE) -#define ROWTYPE_CHSIGN ROWTYPE_GE - -/* Public constraint codes */ -#define FR ROWTYPE_EMPTY -#define LE ROWTYPE_LE -#define GE ROWTYPE_GE -#define EQ ROWTYPE_EQ -#define OF ROWTYPE_OF - -/* MIP constraint classes */ -#define ROWCLASS_Unknown 0 /* Undefined/unknown */ -#define ROWCLASS_Objective 1 /* The objective function */ -#define ROWCLASS_GeneralREAL 2 /* General real-values constraint */ -#define ROWCLASS_GeneralMIP 3 /* General mixed integer/binary and real valued constraint */ -#define ROWCLASS_GeneralINT 4 /* General integer-only constraint */ -#define ROWCLASS_GeneralBIN 5 /* General binary-only constraint */ -#define ROWCLASS_KnapsackINT 6 /* Sum of positive integer times integer variables <= positive integer */ -#define ROWCLASS_KnapsackBIN 7 /* Sum of positive integer times binary variables <= positive integer */ -#define ROWCLASS_SetPacking 8 /* Sum of binary variables >= 1 */ -#define ROWCLASS_SetCover 9 /* Sum of binary variables <= 1 */ -#define ROWCLASS_GUB 10 /* Sum of binary variables = 1 */ -#define ROWCLASS_MAX ROWCLASS_GUB - -/* Column subsets (internal) */ -#define SCAN_USERVARS 1 -#define SCAN_SLACKVARS 2 -#define SCAN_ARTIFICIALVARS 4 -#define SCAN_PARTIALBLOCK 8 -#define USE_BASICVARS 16 -#define USE_NONBASICVARS 32 -#define SCAN_NORMALVARS (SCAN_USERVARS + SCAN_ARTIFICIALVARS) -#define SCAN_ALLVARS (SCAN_SLACKVARS + SCAN_USERVARS + SCAN_ARTIFICIALVARS) -#define USE_ALLVARS (USE_BASICVARS + USE_NONBASICVARS) -#define OMIT_FIXED 64 -#define OMIT_NONFIXED 128 - -/* Improvement defines */ -#define IMPROVE_NONE 0 -#define IMPROVE_SOLUTION 1 -#define IMPROVE_DUALFEAS 2 -#define IMPROVE_THETAGAP 4 -#define IMPROVE_BBSIMPLEX 8 -#define IMPROVE_DEFAULT (IMPROVE_DUALFEAS + IMPROVE_THETAGAP) -#define IMPROVE_INVERSE (IMPROVE_SOLUTION + IMPROVE_THETAGAP) - -/* Scaling types */ -#define SCALE_NONE 0 -#define SCALE_EXTREME 1 -#define SCALE_RANGE 2 -#define SCALE_MEAN 3 -#define SCALE_GEOMETRIC 4 -#define SCALE_FUTURE1 5 -#define SCALE_FUTURE2 6 -#define SCALE_CURTISREID 7 /* Override to Curtis-Reid "optimal" scaling */ - -/* Alternative scaling weights */ -#define SCALE_LINEAR 0 -#define SCALE_QUADRATIC 8 -#define SCALE_LOGARITHMIC 16 -#define SCALE_USERWEIGHT 31 -#define SCALE_MAXTYPE (SCALE_QUADRATIC-1) - -/* Scaling modes */ -#define SCALE_POWER2 32 /* As is or rounded to power of 2 */ -#define SCALE_EQUILIBRATE 64 /* Make sure that no scaled number is above 1 */ -#define SCALE_INTEGERS 128 /* Apply to integer columns/variables */ -#define SCALE_DYNUPDATE 256 /* Apply incrementally every solve() */ -#define SCALE_ROWSONLY 512 /* Override any scaling to only scale the rows */ -#define SCALE_COLSONLY 1024 /* Override any scaling to only scale the rows */ - -/* Standard defines for typical scaling models (no Lagrangeans) */ -#define SCALEMODEL_EQUILIBRATED (SCALE_LINEAR+SCALE_EXTREME+SCALE_INTEGERS) -#define SCALEMODEL_GEOMETRIC (SCALE_LINEAR+SCALE_GEOMETRIC+SCALE_INTEGERS) -#define SCALEMODEL_ARITHMETIC (SCALE_LINEAR+SCALE_MEAN+SCALE_INTEGERS) -#define SCALEMODEL_DYNAMIC (SCALEMODEL_GEOMETRIC+SCALE_EQUILIBRATE) -#define SCALEMODEL_CURTISREID (SCALE_CURTISREID+SCALE_INTEGERS+SCALE_POWER2) - -/* Iteration status and strategies (internal) */ -#define ITERATE_MAJORMAJOR 0 -#define ITERATE_MINORMAJOR 1 -#define ITERATE_MINORRETRY 2 - -/* Pricing methods */ -#define PRICER_FIRSTINDEX 0 -#define PRICER_DANTZIG 1 -#define PRICER_DEVEX 2 -#define PRICER_STEEPESTEDGE 3 -#define PRICER_LASTOPTION PRICER_STEEPESTEDGE - -/* Additional settings for pricers (internal) */ -#define PRICER_RANDFACT 0.1 -#define DEVEX_RESTARTLIMIT 1.0e+09 /* Reset the norms if any value exceeds this limit */ -#define DEVEX_MINVALUE 0.000 /* Minimum weight [0..1] for entering variable, consider 0.01 */ - -/* Pricing strategies */ -#define PRICE_PRIMALFALLBACK 4 /* In case of Steepest Edge, fall back to DEVEX in primal */ -#define PRICE_MULTIPLE 8 /* Enable multiple pricing (primal simplex) */ -#define PRICE_PARTIAL 16 /* Enable partial pricing */ -#define PRICE_ADAPTIVE 32 /* Temporarily use alternative strategy if cycling is detected */ -#define PRICE_HYBRID 64 /* NOT IMPLEMENTED */ -#define PRICE_RANDOMIZE 128 /* Adds a small randomization effect to the selected pricer */ -#define PRICE_AUTOPARTIAL 256 /* Detect and use data on the block structure of the model (primal) */ -#define PRICE_AUTOMULTIPLE 512 /* Automatically select multiple pricing (primal simplex) */ -#define PRICE_LOOPLEFT 1024 /* Scan entering/leaving columns left rather than right */ -#define PRICE_LOOPALTERNATE 2048 /* Scan entering/leaving columns alternatingly left/right */ -#define PRICE_HARRISTWOPASS 4096 /* Use Harris' primal pivot logic rather than the default */ -#define PRICE_FORCEFULL 8192 /* Non-user option to force full pricing */ -#define PRICE_TRUENORMINIT 16384 /* Use true norms for Devex and Steepest Edge initializations */ - -/*#define _PRICE_NOBOUNDFLIP*/ -#if defined _PRICE_NOBOUNDFLIP -#define PRICE_NOBOUNDFLIP 65536 /* Disallow automatic bound-flip during pivot */ -#endif - -#define PRICE_STRATEGYMASK (PRICE_PRIMALFALLBACK + \ - PRICE_MULTIPLE + PRICE_PARTIAL + \ - PRICE_ADAPTIVE + PRICE_HYBRID + \ - PRICE_RANDOMIZE + PRICE_AUTOPARTIAL + PRICE_AUTOMULTIPLE + \ - PRICE_LOOPLEFT + PRICE_LOOPALTERNATE + \ - PRICE_HARRISTWOPASS + \ - PRICE_FORCEFULL + PRICE_TRUENORMINIT) - -/* B&B active variable codes (internal) */ -#define BB_REAL 0 -#define BB_INT 1 -#define BB_SC 2 -#define BB_SOS 3 -#define BB_GUB 4 - -/* B&B strategies */ -#define NODE_FIRSTSELECT 0 -#define NODE_GAPSELECT 1 -#define NODE_RANGESELECT 2 -#define NODE_FRACTIONSELECT 3 -#define NODE_PSEUDOCOSTSELECT 4 -#define NODE_PSEUDONONINTSELECT 5 /* Kjell Eikland #1 - Minimize B&B depth */ -#define NODE_PSEUDOFEASSELECT (NODE_PSEUDONONINTSELECT+NODE_WEIGHTREVERSEMODE) -#define NODE_PSEUDORATIOSELECT 6 /* Kjell Eikland #2 - Minimize a "cost/benefit" ratio */ -#define NODE_USERSELECT 7 -#define NODE_STRATEGYMASK (NODE_WEIGHTREVERSEMODE-1) /* Mask for B&B strategies */ -#define NODE_WEIGHTREVERSEMODE 8 -#define NODE_BRANCHREVERSEMODE 16 -#define NODE_GREEDYMODE 32 -#define NODE_PSEUDOCOSTMODE 64 -#define NODE_DEPTHFIRSTMODE 128 -#define NODE_RANDOMIZEMODE 256 -#define NODE_GUBMODE 512 -#define NODE_DYNAMICMODE 1024 -#define NODE_RESTARTMODE 2048 -#define NODE_BREADTHFIRSTMODE 4096 -#define NODE_AUTOORDER 8192 -#define NODE_RCOSTFIXING 16384 -#define NODE_STRONGINIT 32768 - -#define BRANCH_CEILING 0 -#define BRANCH_FLOOR 1 -#define BRANCH_AUTOMATIC 2 -#define BRANCH_DEFAULT 3 - -/* Action constants for simplex and B&B (internal) */ -#define ACTION_NONE 0 -#define ACTION_ACTIVE 1 -#define ACTION_REBASE 2 -#define ACTION_RECOMPUTE 4 -#define ACTION_REPRICE 8 -#define ACTION_REINVERT 16 -#define ACTION_TIMEDREINVERT 32 -#define ACTION_ITERATE 64 -#define ACTION_RESTART 255 - -/* Solver status values */ -#define UNKNOWNERROR -5 -#define DATAIGNORED -4 -#define NOBFP -3 -#define NOMEMORY -2 -#define NOTRUN -1 -#define OPTIMAL 0 -#define SUBOPTIMAL 1 -#define INFEASIBLE 2 -#define UNBOUNDED 3 -#define DEGENERATE 4 -#define NUMFAILURE 5 -#define USERABORT 6 -#define TIMEOUT 7 -#define RUNNING 8 -#define PRESOLVED 9 - -/* Branch & Bound and Lagrangean extra status values (internal) */ -#define PROCFAIL 10 -#define PROCBREAK 11 -#define FEASFOUND 12 -#define NOFEASFOUND 13 -#define FATHOMED 14 - -/* Status values internal to the solver (internal) */ -#define SWITCH_TO_PRIMAL 20 -#define SWITCH_TO_DUAL 21 -#define SINGULAR_BASIS 22 -#define LOSTFEAS 23 -#define MATRIXERROR 24 - -/* Objective testing options for "bb_better" (internal) */ -#define OF_RELAXED 0 -#define OF_INCUMBENT 1 -#define OF_WORKING 2 -#define OF_USERBREAK 3 -#define OF_HEURISTIC 4 -#define OF_DUALLIMIT 5 -#define OF_DELTA 8 /* Mode */ -#define OF_PROJECTED 16 /* Mode - future, not active */ - -#define OF_TEST_BT 1 -#define OF_TEST_BE 2 -#define OF_TEST_NE 3 -#define OF_TEST_WE 4 -#define OF_TEST_WT 5 -#define OF_TEST_RELGAP 8 /* Mode */ - - -/* Name list and sparse matrix storage parameters (internal) */ -#define MAT_START_SIZE 10000 -#define DELTACOLALLOC 100 -#define DELTAROWALLOC 100 -#define RESIZEFACTOR 4 /* Fractional increase in selected memory allocations */ - -/* Default solver parameters and tolerances (internal) */ -#define DEF_PARTIALBLOCKS 10 /* The default number of blocks for partial pricing */ -#define DEF_MAXRELAX 7 /* Maximum number of non-BB relaxations in MILP */ -#define DEF_MAXPIVOTRETRY 10 /* Maximum number of times to retry a div-0 situation */ -#define DEF_MAXSINGULARITIES 10 /* Maximum number of singularities in refactorization */ -#define MAX_MINITUPDATES 60 /* Maximum number of bound swaps between refactorizations - without recomputing the whole vector - contain errors */ -#define MIN_REFACTFREQUENCY 5 /* Refactorization frequency indicating an inherent - numerical instability of the basis */ -#define LAG_SINGULARLIMIT 5 /* Number of times the objective does not change - before it is assumed that the Lagrangean constraints - are non-binding, and therefore impossible to converge; - upper iteration limit is divided by this threshold */ -#define MIN_TIMEPIVOT 5.0e-02 /* Minimum time per pivot for reinversion optimization - purposes; use active monitoring only if a pivot - takes more than MINTIMEPIVOT seconds. 5.0e-2 is - roughly suitable for a 1GHz system. */ -#define MAX_STALLCOUNT 12 /* The absolute upper limit to the number of stalling or - cycling iterations before switching rule */ -#define MAX_RULESWITCH 5 /* The maximum number of times to try an alternate pricing rule - to recover from stalling; set negative for no limit. */ -#define DEF_TIMEDREFACT AUTOMATIC /* Default for timed refactorization in BFPs; - can be FALSE, TRUE or AUTOMATIC (dynamic) */ - -#define DEF_SCALINGLIMIT 5 /* The default maximum number of scaling iterations */ - -#define DEF_NEGRANGE -1.0e+06 /* Downward limit for expanded variable range before the - variable is split into positive and negative components */ -#define DEF_BB_LIMITLEVEL -50 /* Relative B&B limit to protect against very deep, - memory-consuming trees */ - -#define MAX_FRACSCALE 6 /* The maximum decimal scan range for simulated integers */ -#define RANDSCALE 100 /* Randomization scaling range */ -#define DOUBLEROUND 0.0e-02 /* Extra rounding scalar used in btran/ftran calculations; the - rationale for 0.0 is that prod_xA() uses rounding as well */ -#define DEF_EPSMACHINE 2.22e-16 /* Machine relative precision (doubles) */ -#define MIN_STABLEPIVOT 5.0 /* Minimum pivot magnitude assumed to be numerically stable */ - - -/* Precision macros */ -/* -------------------------------------------------------------------------------------- */ -#define PREC_REDUCEDCOST lp->epsvalue -#define PREC_IMPROVEGAP lp->epsdual -#define PREC_SUBSTFEASGAP lp->epsprimal -#if 1 - #define PREC_BASICSOLUTION lp->epsvalue /* Zero-rounding of RHS/basic solution vector */ -#else - #define PREC_BASICSOLUTION lp->epsmachine /* Zero-rounding of RHS/basic solution vector */ -#endif -#define LIMIT_ABS_REL 10.0 /* Limit for testing using relative metric */ - - -/* Parameters constants for short-cut setting of tolerances */ -/* -------------------------------------------------------------------------------------- */ -#define EPS_TIGHT 0 -#define EPS_MEDIUM 1 -#define EPS_LOOSE 2 -#define EPS_BAGGY 3 -#define EPS_DEFAULT EPS_TIGHT - - -#if ActivePARAM==ProductionPARAM /* PARAMETER SET FOR PRODUCTION */ -/* -------------------------------------------------------------------------------------- */ -#define DEF_INFINITE 1.0e+30 /* Limit for dynamic range */ -#define DEF_EPSVALUE 1.0e-12 /* High accuracy and feasibility preserving tolerance */ -#define DEF_EPSPRIMAL 1.0e-10 /* For rounding primal/RHS values to 0 */ -#define DEF_EPSDUAL 1.0e-09 /* For rounding reduced costs to 0 */ -#define DEF_EPSPIVOT 2.0e-07 /* Pivot reject threshold */ -#define DEF_PERTURB 1.0e-05 /* Perturbation scalar for degenerate problems; - must at least be RANDSCALE greater than EPSPRIMAL */ -#define DEF_EPSSOLUTION 1.0e-05 /* Margin of error for solution bounds */ -#define DEF_EPSINT 1.0e-07 /* Accuracy for considering a float value as integer */ - -#elif ActivePARAM==OriginalPARAM /* PARAMETER SET FOR LEGACY VERSIONS */ -/* -------------------------------------------------------------------------------------- */ -#define DEF_INFINITE 1.0e+24 /* Limit for dynamic range */ -#define DEF_EPSVALUE 1.0e-08 /* High accuracy and feasibility preserving tolerance */ -#define DEF_EPSPRIMAL 5.01e-07 /* For rounding primal/RHS values to 0, infeasibility */ -#define DEF_EPSDUAL 1.0e-06 /* For rounding reduced costs to 0 */ -#define DEF_EPSPIVOT 1.0e-04 /* Pivot reject threshold */ -#define DEF_PERTURB 1.0e-05 /* Perturbation scalar for degenerate problems; - must at least be RANDSCALE greater than EPSPRIMAL */ -#define DEF_EPSSOLUTION 1.0e-02 /* Margin of error for solution bounds */ -#define DEF_EPSINT 1.0e-03 /* Accuracy for considering a float value as integer */ - -#elif ActivePARAM==ChvatalPARAM /* PARAMETER SET EXAMPLES FROM Vacek Chvatal */ -/* -------------------------------------------------------------------------------------- */ -#define DEF_INFINITE 1.0e+30 /* Limit for dynamic range */ -#define DEF_EPSVALUE 1.0e-10 /* High accuracy and feasibility preserving tolerance */ -#define DEF_EPSPRIMAL 10e-07 /* For rounding primal/RHS values to 0 */ -#define DEF_EPSDUAL 10e-05 /* For rounding reduced costs to 0 */ -#define DEF_EPSPIVOT 10e-05 /* Pivot reject threshold */ -#define DEF_PERTURB 10e-03 /* Perturbation scalar for degenerate problems; - must at least be RANDSCALE greater than EPSPRIMAL */ -#define DEF_EPSSOLUTION 1.0e-05 /* Margin of error for solution bounds */ -#define DEF_EPSINT 5.0e-03 /* Accuracy for considering a float value as integer */ - -#elif ActivePARAM==LoosePARAM /* PARAMETER SET FOR LOOSE TOLERANCES */ -/* -------------------------------------------------------------------------------------- */ -#define DEF_INFINITE 1.0e+30 /* Limit for dynamic range */ -#define DEF_EPSVALUE 1.0e-10 /* High accuracy and feasibility preserving tolerance */ -#define DEF_EPSPRIMAL 5.01e-08 /* For rounding primal/RHS values to 0 */ -#define DEF_EPSDUAL 1.0e-07 /* For rounding reduced costs to 0 */ -#define DEF_EPSPIVOT 1.0e-05 /* Pivot reject threshold */ -#define DEF_PERTURB 1.0e-05 /* Perturbation scalar for degenerate problems; - must at least be RANDSCALE greater than EPSPRIMAL */ -#define DEF_EPSSOLUTION 1.0e-05 /* Margin of error for solution bounds */ -#define DEF_EPSINT 1.0e-04 /* Accuracy for considering a float value as integer */ - -#endif - - -#define DEF_MIP_GAP 1.0e-11 /* The default absolute and relative MIP gap */ -#define SCALEDINTFIXRANGE 1.6 /* Epsilon range multiplier < 2 for collapsing bounds to fix */ - -#define MIN_SCALAR 1.0e-10 /* Smallest allowed scaling adjustment */ -#define MAX_SCALAR 1.0e+10 /* Largest allowed scaling adjustment */ -#define DEF_SCALINGEPS 1.0e-02 /* Relative scaling convergence criterion for auto_scale */ - -#define DEF_LAGACCEPT 1.0e-03 /* Default Lagrangean convergence acceptance criterion */ -#define DEF_LAGCONTRACT 0.90 /* The contraction parameter for Lagrangean iterations */ -#define DEF_LAGMAXITERATIONS 100 /* The maximum number of Lagrangean iterations */ - -#define DEF_PSEUDOCOSTUPDATES 7 /* The default number of times pseudo-costs are recalculated; - experiments indicate that costs tend to stabilize */ -#define DEF_PSEUDOCOSTRESTART 0.15 /* The fraction of price updates required for B&B restart - when the mode is NODE_RESTARTMODE */ -#define DEF_MAXPRESOLVELOOPS 0 /* Upper limit to the number of loops during presolve, - <= 0 for no limit. */ - - -/* Hashing prototypes and function headers */ -/* ------------------------------------------------------------------------- */ -#include "lp_Hash.h" - - -/* Sparse matrix prototypes */ -/* ------------------------------------------------------------------------- */ -#include "lp_matrix.h" - - -/* Basis storage (mainly for B&B) */ -typedef struct _basisrec -{ - int level; - int *var_basic; - MYBOOL *is_basic; - MYBOOL *is_lower; - int pivots; - struct _basisrec *previous; -} basisrec; - -/* Presolve undo data storage */ -typedef struct _presolveundorec -{ - lprec *lp; - int orig_rows; - int orig_columns; - int orig_sum; - int *var_to_orig; /* sum_alloc+1 : Mapping of variables from solution to - best_solution to account for removed variables and - rows during presolve; a non-positive value indicates - that the constraint or variable was removed */ - int *orig_to_var; /* sum_alloc+1 : Mapping from original variable index to - current / working index number */ - LPSREAL *fixed_rhs; /* rows_alloc+1 : Storage of values of presolved fixed colums */ - LPSREAL *fixed_obj; /* columns_alloc+1: Storage of values of presolved fixed rows */ - DeltaVrec *deletedA; /* A matrix of eliminated data from matA */ - DeltaVrec *primalundo; /* Affine translation vectors for eliminated primal variables */ - DeltaVrec *dualundo; /* Affine translation vectors for eliminated dual variables */ - MYBOOL OFcolsdeleted; -} presolveundorec; - -/* Pseudo-cost arrays used during B&B */ -typedef struct _BBPSrec -{ - lprec *lp; - int pseodotype; - int updatelimit; - int updatesfinished; - LPSREAL restartlimit; - MATitem *UPcost; - MATitem *LOcost; - struct _BBPSrec *secondary; -} BBPSrec; - -#include "lp_mipbb.h" - - -/* Partial pricing block data */ -typedef struct _partialrec { - lprec *lp; - int blockcount; /* ## The number of logical blocks or stages in the model */ - int blocknow; /* The currently active block */ - int *blockend; /* Array of column indeces giving the start of each block */ - int *blockpos; /* Array of column indeces giving the start scan position */ - MYBOOL isrow; -} partialrec; - - -/* Specially Ordered Sets (SOS) prototypes and settings */ -/* ------------------------------------------------------------------------- */ -/* SOS storage structure (LINEARSEARCH is typically in the 0-10 range) */ -#ifndef LINEARSEARCH -#define LINEARSEARCH 0 -#endif - -#include "lp_SOS.h" - - -/* Prototypes for user call-back functions */ -/* ------------------------------------------------------------------------- */ -typedef int (__WINAPI lphandle_intfunc)(lprec *lp, void *userhandle); -typedef void (__WINAPI lphandlestr_func)(lprec *lp, void *userhandle, char *buf); -typedef void (__WINAPI lphandleint_func)(lprec *lp, void *userhandle, int message); -typedef int (__WINAPI lphandleint_intfunc)(lprec *lp, void *userhandle, int message); - - -/* API typedef definitions */ -/* ------------------------------------------------------------------------- */ -typedef MYBOOL (__WINAPI add_column_func)(lprec *lp, LPSREAL *column); -typedef MYBOOL (__WINAPI add_columnex_func)(lprec *lp, int count, LPSREAL *column, int *rowno); -typedef MYBOOL (__WINAPI add_constraint_func)(lprec *lp, LPSREAL *row, int constr_type, LPSREAL rh); -typedef MYBOOL (__WINAPI add_constraintex_func)(lprec *lp, int count, LPSREAL *row, int *colno, int constr_type, LPSREAL rh); -typedef MYBOOL (__WINAPI add_lag_con_func)(lprec *lp, LPSREAL *row, int con_type, LPSREAL rhs); -typedef int (__WINAPI add_SOS_func)(lprec *lp, char *name, int sostype, int priority, int count, int *sosvars, LPSREAL *weights); -typedef int (__WINAPI column_in_lp_func)(lprec *lp, LPSREAL *column); -typedef lprec * (__WINAPI copy_lp_func)(lprec *lp); -typedef void (__WINAPI default_basis_func)(lprec *lp); -typedef MYBOOL (__WINAPI del_column_func)(lprec *lp, int colnr); -typedef MYBOOL (__WINAPI del_constraint_func)(lprec *lp, int rownr); -typedef void (__WINAPI delete_lp_func)(lprec *lp); -typedef MYBOOL (__WINAPI dualize_lp_func)(lprec *lp); -typedef void (__WINAPI free_lp_func)(lprec **plp); -typedef int (__WINAPI get_anti_degen_func)(lprec *lp); -typedef MYBOOL (__WINAPI get_basis_func)(lprec *lp, int *bascolumn, MYBOOL nonbasic); -typedef int (__WINAPI get_basiscrash_func)(lprec *lp); -typedef int (__WINAPI get_bb_depthlimit_func)(lprec *lp); -typedef int (__WINAPI get_bb_floorfirst_func)(lprec *lp); -typedef int (__WINAPI get_bb_rule_func)(lprec *lp); -typedef MYBOOL (__WINAPI get_bounds_tighter_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_break_at_value_func)(lprec *lp); -typedef char * (__WINAPI get_col_name_func)(lprec *lp, int colnr); -typedef MYBOOL (__WINAPI get_column_func)(lprec *lp, int colnr, LPSREAL *column); -typedef int (__WINAPI get_columnex_func)(lprec *lp, int colnr, LPSREAL *column, int *nzrow); -typedef int (__WINAPI get_constr_type_func)(lprec *lp, int rownr); -typedef LPSREAL (__WINAPI get_constr_value_func)(lprec *lp, int rownr, int count, LPSREAL *primsolution, int *nzindex); -typedef MYBOOL (__WINAPI get_constraints_func)(lprec *lp, LPSREAL *constr); -typedef MYBOOL (__WINAPI get_dual_solution_func)(lprec *lp, LPSREAL *rc); -typedef LPSREAL (__WINAPI get_epsb_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_epsd_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_epsel_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_epsint_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_epsperturb_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_epspivot_func)(lprec *lp); -typedef int (__WINAPI get_improve_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_infinite_func)(lprec *lp); -typedef MYBOOL (__WINAPI get_lambda_func)(lprec *lp, LPSREAL *lambda); -typedef LPSREAL (__WINAPI get_lowbo_func)(lprec *lp, int colnr); -typedef int (__WINAPI get_lp_index_func)(lprec *lp, int orig_index); -typedef char * (__WINAPI get_lp_name_func)(lprec *lp); -typedef int (__WINAPI get_Lrows_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_mat_func)(lprec *lp, int rownr, int colnr); -typedef LPSREAL (__WINAPI get_mat_byindex_func)(lprec *lp, int matindex, MYBOOL isrow, MYBOOL adjustsign); -typedef int (__WINAPI get_max_level_func)(lprec *lp); -typedef int (__WINAPI get_maxpivot_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_mip_gap_func)(lprec *lp, MYBOOL absolute); -typedef int (__WINAPI get_multiprice_func)(lprec *lp, MYBOOL getabssize); -typedef MYBOOL (__WINAPI is_use_names_func)(lprec *lp, MYBOOL isrow); -typedef void (__WINAPI set_use_names_func)(lprec *lp, MYBOOL isrow, MYBOOL use_names); -typedef int (__WINAPI get_nameindex_func)(lprec *lp, char *varname, MYBOOL isrow); -typedef int (__WINAPI get_Ncolumns_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_negrange_func)(lprec *lp); -typedef int (__WINAPI get_nz_func)(lprec *lp); -typedef int (__WINAPI get_Norig_columns_func)(lprec *lp); -typedef int (__WINAPI get_Norig_rows_func)(lprec *lp); -typedef int (__WINAPI get_Nrows_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_obj_bound_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_objective_func)(lprec *lp); -typedef int (__WINAPI get_orig_index_func)(lprec *lp, int lp_index); -typedef char * (__WINAPI get_origcol_name_func)(lprec *lp, int colnr); -typedef char * (__WINAPI get_origrow_name_func)(lprec *lp, int rownr); -typedef void (__WINAPI get_partialprice_func)(lprec *lp, int *blockcount, int *blockstart, MYBOOL isrow); -typedef int (__WINAPI get_pivoting_func)(lprec *lp); -typedef int (__WINAPI get_presolve_func)(lprec *lp); -typedef int (__WINAPI get_presolveloops_func)(lprec *lp); -typedef MYBOOL (__WINAPI get_primal_solution_func)(lprec *lp, LPSREAL *pv); -typedef int (__WINAPI get_print_sol_func)(lprec *lp); -typedef MYBOOL (__WINAPI get_pseudocosts_func)(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int *updatelimit); -typedef MYBOOL (__WINAPI get_ptr_constraints_func)(lprec *lp, LPSREAL **constr); -typedef MYBOOL (__WINAPI get_ptr_dual_solution_func)(lprec *lp, LPSREAL **rc); -typedef MYBOOL (__WINAPI get_ptr_lambda_func)(lprec *lp, LPSREAL **lambda); -typedef MYBOOL (__WINAPI get_ptr_primal_solution_func)(lprec *lp, LPSREAL **pv); -typedef MYBOOL (__WINAPI get_ptr_sensitivity_obj_func)(lprec *lp, LPSREAL **objfrom, LPSREAL **objtill); -typedef MYBOOL (__WINAPI get_ptr_sensitivity_objex_func)(lprec *lp, LPSREAL **objfrom, LPSREAL **objtill, LPSREAL **objfromvalue, LPSREAL **objtillvalue); -typedef MYBOOL (__WINAPI get_ptr_sensitivity_rhs_func)(lprec *lp, LPSREAL **duals, LPSREAL **dualsfrom, LPSREAL **dualstill); -typedef MYBOOL (__WINAPI get_ptr_variables_func)(lprec *lp, LPSREAL **var); -typedef LPSREAL (__WINAPI get_rh_func)(lprec *lp, int rownr); -typedef LPSREAL (__WINAPI get_rh_range_func)(lprec *lp, int rownr); -typedef int (__WINAPI get_rowex_func)(lprec *lp, int rownr, LPSREAL *row, int *colno); -typedef MYBOOL (__WINAPI get_row_func)(lprec *lp, int rownr, LPSREAL *row); -typedef char * (__WINAPI get_row_name_func)(lprec *lp, int rownr); -typedef LPSREAL (__WINAPI get_scalelimit_func)(lprec *lp); -typedef int (__WINAPI get_scaling_func)(lprec *lp); -typedef MYBOOL (__WINAPI get_sensitivity_obj_func)(lprec *lp, LPSREAL *objfrom, LPSREAL *objtill); -typedef MYBOOL (__WINAPI get_sensitivity_objex_func)(lprec *lp, LPSREAL *objfrom, LPSREAL *objtill, LPSREAL *objfromvalue, LPSREAL *objtillvalue); -typedef MYBOOL (__WINAPI get_sensitivity_rhs_func)(lprec *lp, LPSREAL *duals, LPSREAL *dualsfrom, LPSREAL *dualstill); -typedef int (__WINAPI get_simplextype_func)(lprec *lp); -typedef int (__WINAPI get_solutioncount_func)(lprec *lp); -typedef int (__WINAPI get_solutionlimit_func)(lprec *lp); -typedef int (__WINAPI get_status_func)(lprec *lp); -typedef char * (__WINAPI get_statustext_func)(lprec *lp, int statuscode); -typedef long (__WINAPI get_timeout_func)(lprec *lp); -typedef COUNTER (__WINAPI get_total_iter_func)(lprec *lp); -typedef COUNTER (__WINAPI get_total_nodes_func)(lprec *lp); -typedef LPSREAL (__WINAPI get_upbo_func)(lprec *lp, int colnr); -typedef int (__WINAPI get_var_branch_func)(lprec *lp, int colnr); -typedef LPSREAL (__WINAPI get_var_dualresult_func)(lprec *lp, int index); -typedef LPSREAL (__WINAPI get_var_primalresult_func)(lprec *lp, int index); -typedef int (__WINAPI get_var_priority_func)(lprec *lp, int colnr); -typedef MYBOOL (__WINAPI get_variables_func)(lprec *lp, LPSREAL *var); -typedef int (__WINAPI get_verbose_func)(lprec *lp); -typedef MYBOOL (__WINAPI guess_basis_func)(lprec *lp, LPSREAL *guessvector, int *basisvector); -typedef LPSREAL (__WINAPI get_working_objective_func)(lprec *lp); -typedef MYBOOL (__WINAPI has_BFP_func)(lprec *lp); -typedef MYBOOL (__WINAPI has_XLI_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_add_rowmode_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_anti_degen_func)(lprec *lp, int testmask); -typedef MYBOOL (__WINAPI is_binary_func)(lprec *lp, int colnr); -typedef MYBOOL (__WINAPI is_break_at_first_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_constr_type_func)(lprec *lp, int rownr, int mask); -typedef MYBOOL (__WINAPI is_debug_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_feasible_func)(lprec *lp, LPSREAL *values, LPSREAL threshold); -typedef MYBOOL (__WINAPI is_unbounded_func)(lprec *lp, int colnr); -typedef MYBOOL (__WINAPI is_infinite_func)(lprec *lp, LPSREAL value); -typedef MYBOOL (__WINAPI is_int_func)(lprec *lp, int column); -typedef MYBOOL (__WINAPI is_integerscaling_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_lag_trace_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_maxim_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_nativeBFP_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_nativeXLI_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_negative_func)(lprec *lp, int colnr); -typedef MYBOOL (__WINAPI is_obj_in_basis_func)(lprec *lp); -typedef MYBOOL (__WINAPI is_piv_mode_func)(lprec *lp, int testmask); -typedef MYBOOL (__WINAPI is_piv_rule_func)(lprec *lp, int rule); -typedef MYBOOL (__WINAPI is_presolve_func)(lprec *lp, int testmask); -typedef MYBOOL (__WINAPI is_scalemode_func)(lprec *lp, int testmask); -typedef MYBOOL (__WINAPI is_scaletype_func)(lprec *lp, int scaletype); -typedef MYBOOL (__WINAPI is_semicont_func)(lprec *lp, int colnr); -typedef MYBOOL (__WINAPI is_SOS_var_func)(lprec *lp, int colnr); -typedef MYBOOL (__WINAPI is_trace_func)(lprec *lp); -typedef void (__WINAPI lp_solve_version_func)(int *majorversion, int *minorversion, int *release, int *build); -typedef lprec * (__WINAPI make_lp_func)(int rows, int columns); -typedef void (__WINAPI print_constraints_func)(lprec *lp, int columns); -typedef MYBOOL (__WINAPI print_debugdump_func)(lprec *lp, char *filename); -typedef void (__WINAPI print_duals_func)(lprec *lp); -typedef void (__WINAPI print_lp_func)(lprec *lp); -typedef void (__WINAPI print_objective_func)(lprec *lp); -typedef void (__WINAPI print_scales_func)(lprec *lp); -typedef void (__WINAPI print_solution_func)(lprec *lp, int columns); -typedef void (__WINAPI print_str_func)(lprec *lp, char *str); -typedef void (__WINAPI print_tableau_func)(lprec *lp); -typedef void (__WINAPI put_abortfunc_func)(lprec *lp, lphandle_intfunc newctrlc, void *ctrlchandle); -typedef void (__WINAPI put_bb_nodefunc_func)(lprec *lp, lphandleint_intfunc newnode, void *bbnodehandle); -typedef void (__WINAPI put_bb_branchfunc_func)(lprec *lp, lphandleint_intfunc newbranch, void *bbbranchhandle); -typedef void (__WINAPI put_logfunc_func)(lprec *lp, lphandlestr_func newlog, void *loghandle); -typedef void (__WINAPI put_msgfunc_func)(lprec *lp, lphandleint_func newmsg, void *msghandle, int mask); -typedef lprec * (__WINAPI read_LP_func)(char *filename, int verbose, char *lp_name); -typedef lprec * (__WINAPI read_MPS_func)(char *filename, int options); -typedef lprec * (__WINAPI read_XLI_func)(char *xliname, char *modelname, char *dataname, char *options, int verbose); -typedef MYBOOL (__WINAPI read_basis_func)(lprec *lp, char *filename, char *info); -typedef void (__WINAPI reset_basis_func)(lprec *lp); -typedef MYBOOL (__WINAPI read_params_func)(lprec *lp, char *filename, char *options); -typedef void (__WINAPI reset_params_func)(lprec *lp); -typedef MYBOOL (__WINAPI resize_lp_func)(lprec *lp, int rows, int columns); -typedef MYBOOL (__WINAPI set_add_rowmode_func)(lprec *lp, MYBOOL turnon); -typedef void (__WINAPI set_anti_degen_func)(lprec *lp, int anti_degen); -typedef int (__WINAPI set_basisvar_func)(lprec *lp, int basisPos, int enteringCol); -typedef MYBOOL (__WINAPI set_basis_func)(lprec *lp, int *bascolumn, MYBOOL nonbasic); -typedef void (__WINAPI set_basiscrash_func)(lprec *lp, int mode); -typedef void (__WINAPI set_bb_depthlimit_func)(lprec *lp, int bb_maxlevel); -typedef void (__WINAPI set_bb_floorfirst_func)(lprec *lp, int bb_floorfirst); -typedef void (__WINAPI set_bb_rule_func)(lprec *lp, int bb_rule); -typedef MYBOOL (__WINAPI set_BFP_func)(lprec *lp, char *filename); -typedef MYBOOL (__WINAPI set_binary_func)(lprec *lp, int colnr, MYBOOL must_be_bin); -typedef MYBOOL (__WINAPI set_bounds_func)(lprec *lp, int colnr, LPSREAL lower, LPSREAL upper); -typedef void (__WINAPI set_bounds_tighter_func)(lprec *lp, MYBOOL tighten); -typedef void (__WINAPI set_break_at_first_func)(lprec *lp, MYBOOL break_at_first); -typedef void (__WINAPI set_break_at_value_func)(lprec *lp, LPSREAL break_at_value); -typedef MYBOOL (__WINAPI set_column_func)(lprec *lp, int colnr, LPSREAL *column); -typedef MYBOOL (__WINAPI set_columnex_func)(lprec *lp, int colnr, int count, LPSREAL *column, int *rowno); -typedef MYBOOL (__WINAPI set_col_name_func)(lprec *lp, int colnr, char *new_name); -typedef MYBOOL (__WINAPI set_constr_type_func)(lprec *lp, int rownr, int con_type); -typedef void (__WINAPI set_debug_func)(lprec *lp, MYBOOL debug); -typedef void (__WINAPI set_epsb_func)(lprec *lp, LPSREAL epsb); -typedef void (__WINAPI set_epsd_func)(lprec *lp, LPSREAL epsd); -typedef void (__WINAPI set_epsel_func)(lprec *lp, LPSREAL epsel); -typedef void (__WINAPI set_epsint_func)(lprec *lp, LPSREAL epsint); -typedef MYBOOL (__WINAPI set_epslevel_func)(lprec *lp, int epslevel); -typedef void (__WINAPI set_epsperturb_func)(lprec *lp, LPSREAL epsperturb); -typedef void (__WINAPI set_epspivot_func)(lprec *lp, LPSREAL epspivot); -typedef MYBOOL (__WINAPI set_unbounded_func)(lprec *lp, int colnr); -typedef void (__WINAPI set_improve_func)(lprec *lp, int improve); -typedef void (__WINAPI set_infinite_func)(lprec *lp, LPSREAL infinite); -typedef MYBOOL (__WINAPI set_int_func)(lprec *lp, int colnr, MYBOOL must_be_int); -typedef void (__WINAPI set_lag_trace_func)(lprec *lp, MYBOOL lag_trace); -typedef MYBOOL (__WINAPI set_lowbo_func)(lprec *lp, int colnr, LPSREAL value); -typedef MYBOOL (__WINAPI set_lp_name_func)(lprec *lp, char *lpname); -typedef MYBOOL (__WINAPI set_mat_func)(lprec *lp, int row, int column, LPSREAL value); -typedef void (__WINAPI set_maxim_func)(lprec *lp); -typedef void (__WINAPI set_maxpivot_func)(lprec *lp, int max_num_inv); -typedef void (__WINAPI set_minim_func)(lprec *lp); -typedef void (__WINAPI set_mip_gap_func)(lprec *lp, MYBOOL absolute, LPSREAL mip_gap); -typedef MYBOOL (__WINAPI set_multiprice_func)(lprec *lp, int multiblockdiv); -typedef void (__WINAPI set_negrange_func)(lprec *lp, LPSREAL negrange); -typedef MYBOOL (__WINAPI set_obj_func)(lprec *lp, int colnr, LPSREAL value); -typedef void (__WINAPI set_obj_bound_func)(lprec *lp, LPSREAL obj_bound); -typedef MYBOOL (__WINAPI set_obj_fn_func)(lprec *lp, LPSREAL *row); -typedef MYBOOL (__WINAPI set_obj_fnex_func)(lprec *lp, int count, LPSREAL *row, int *colno); -typedef void (__WINAPI set_obj_in_basis_func)(lprec *lp, MYBOOL obj_in_basis); -typedef MYBOOL (__WINAPI set_outputfile_func)(lprec *lp, char *filename); -typedef void (__WINAPI set_outputstream_func)(lprec *lp, FILE *stream); -typedef MYBOOL (__WINAPI set_partialprice_func)(lprec *lp, int blockcount, int *blockstart, MYBOOL isrow); -typedef void (__WINAPI set_pivoting_func)(lprec *lp, int piv_rule); -typedef void (__WINAPI set_preferdual_func)(lprec *lp, MYBOOL dodual); -typedef void (__WINAPI set_presolve_func)(lprec *lp, int presolvemode, int maxloops); -typedef void (__WINAPI set_print_sol_func)(lprec *lp, int print_sol); -typedef MYBOOL (__WINAPI set_pseudocosts_func)(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int *updatelimit); -typedef MYBOOL (__WINAPI set_rh_func)(lprec *lp, int rownr, LPSREAL value); -typedef MYBOOL (__WINAPI set_rh_range_func)(lprec *lp, int rownr, LPSREAL deltavalue); -typedef void (__WINAPI set_rh_vec_func)(lprec *lp, LPSREAL *rh); -typedef MYBOOL (__WINAPI set_row_func)(lprec *lp, int rownr, LPSREAL *row); -typedef MYBOOL (__WINAPI set_rowex_func)(lprec *lp, int rownr, int count, LPSREAL *row, int *colno); -typedef MYBOOL (__WINAPI set_row_name_func)(lprec *lp, int rownr, char *new_name); -typedef void (__WINAPI set_scalelimit_func)(lprec *lp, LPSREAL scalelimit); -typedef void (__WINAPI set_scaling_func)(lprec *lp, int scalemode); -typedef MYBOOL (__WINAPI set_semicont_func)(lprec *lp, int colnr, MYBOOL must_be_sc); -typedef void (__WINAPI set_sense_func)(lprec *lp, MYBOOL maximize); -typedef void (__WINAPI set_simplextype_func)(lprec *lp, int simplextype); -typedef void (__WINAPI set_solutionlimit_func)(lprec *lp, int limit); -typedef void (__WINAPI set_timeout_func)(lprec *lp, long sectimeout); -typedef void (__WINAPI set_trace_func)(lprec *lp, MYBOOL trace); -typedef MYBOOL (__WINAPI set_upbo_func)(lprec *lp, int colnr, LPSREAL value); -typedef MYBOOL (__WINAPI set_var_branch_func)(lprec *lp, int colnr, int branch_mode); -typedef MYBOOL (__WINAPI set_var_weights_func)(lprec *lp, LPSREAL *weights); -typedef void (__WINAPI set_verbose_func)(lprec *lp, int verbose); -typedef MYBOOL (__WINAPI set_XLI_func)(lprec *lp, char *filename); -typedef int (__WINAPI solve_func)(lprec *lp); -typedef MYBOOL (__WINAPI str_add_column_func)(lprec *lp, char *col_string); -typedef MYBOOL (__WINAPI str_add_constraint_func)(lprec *lp, char *row_string ,int constr_type, LPSREAL rh); -typedef MYBOOL (__WINAPI str_add_lag_con_func)(lprec *lp, char *row_string, int con_type, LPSREAL rhs); -typedef MYBOOL (__WINAPI str_set_obj_fn_func)(lprec *lp, char *row_string); -typedef MYBOOL (__WINAPI str_set_rh_vec_func)(lprec *lp, char *rh_string); -typedef LPSREAL (__WINAPI time_elapsed_func)(lprec *lp); -typedef void (__WINAPI unscale_func)(lprec *lp); -typedef MYBOOL (__WINAPI write_lp_func)(lprec *lp, char *filename); -typedef MYBOOL (__WINAPI write_LP_func)(lprec *lp, FILE *output); -typedef MYBOOL (__WINAPI write_mps_func)(lprec *lp, char *filename); -typedef MYBOOL (__WINAPI write_MPS_func)(lprec *lp, FILE *output); -typedef MYBOOL (__WINAPI write_freemps_func)(lprec *lp, char *filename); -typedef MYBOOL (__WINAPI write_freeMPS_func)(lprec *lp, FILE *output); -typedef MYBOOL (__WINAPI write_XLI_func)(lprec *lp, char *filename, char *options, MYBOOL results); -typedef MYBOOL (__WINAPI write_basis_func)(lprec *lp, char *filename); -typedef MYBOOL (__WINAPI write_params_func)(lprec *lp, char *filename, char *options); - - -/* Prototypes for callbacks from basis inverse/factorization libraries */ -/* ------------------------------------------------------------------------- */ -typedef MYBOOL (__WINAPI userabortfunc)(lprec *lp, int level); -typedef void (__VACALL reportfunc)(lprec *lp, int level, char *format, ...); -typedef char * (__VACALL explainfunc)(lprec *lp, char *format, ...); -typedef int (__WINAPI getvectorfunc)(lprec *lp, int varin, LPSREAL *pcol, int *nzlist, int *maxabs); -typedef int (__WINAPI getpackedfunc)(lprec *lp, int j, int rn[], double bj[]); -typedef LPSREAL (__WINAPI get_OF_activefunc)(lprec *lp, int varnr, LPSREAL mult); -typedef int (__WINAPI getMDOfunc)(lprec *lp, MYBOOL *usedpos, int *colorder, int *size, MYBOOL symmetric); -typedef MYBOOL (__WINAPI invertfunc)(lprec *lp, MYBOOL shiftbounds, MYBOOL final); -typedef void (__WINAPI set_actionfunc)(int *actionvar, int actionmask); -typedef MYBOOL (__WINAPI is_actionfunc)(int actionvar, int testmask); -typedef void (__WINAPI clear_actionfunc)(int *actionvar, int actionmask); - - -/* Prototypes for basis inverse/factorization libraries */ -/* ------------------------------------------------------------------------- */ -typedef char *(BFP_CALLMODEL BFPchar)(void); -typedef void (BFP_CALLMODEL BFP_lp)(lprec *lp); -typedef void (BFP_CALLMODEL BFP_lpint)(lprec *lp, int newsize); -typedef int (BFP_CALLMODEL BFPint_lp)(lprec *lp); -typedef int (BFP_CALLMODEL BFPint_lpint)(lprec *lp, int kind); -typedef LPSREAL (BFP_CALLMODEL BFPreal_lp)(lprec *lp); -typedef LPSREAL *(BFP_CALLMODEL BFPrealp_lp)(lprec *lp); -typedef void (BFP_CALLMODEL BFP_lpbool)(lprec *lp, MYBOOL maximum); -typedef int (BFP_CALLMODEL BFPint_lpbool)(lprec *lp, MYBOOL maximum); -typedef int (BFP_CALLMODEL BFPint_lpintintboolbool)(lprec *lp, int uservars, int Bsize, MYBOOL *usedpos, MYBOOL final); -typedef void (BFP_CALLMODEL BFP_lprealint)(lprec *lp, LPSREAL *pcol, int *nzidx); -typedef void (BFP_CALLMODEL BFP_lprealintrealint)(lprec *lp, LPSREAL *prow, int *pnzidx, LPSREAL *drow, int *dnzidx); -typedef MYBOOL (BFP_CALLMODEL BFPbool_lp)(lprec *lp); -typedef MYBOOL (BFP_CALLMODEL BFPbool_lpbool)(lprec *lp, MYBOOL changesign); -typedef MYBOOL (BFP_CALLMODEL BFPbool_lpint)(lprec *lp, int size); -typedef MYBOOL (BFP_CALLMODEL BFPbool_lpintintchar)(lprec *lp, int size, int deltasize, char *options); -typedef MYBOOL (BFP_CALLMODEL BFPbool_lpintintint)(lprec *lp, int size, int deltasize, int sizeofvar); -typedef LREAL (BFP_CALLMODEL BFPlreal_lpintintreal)(lprec *lp, int row_nr, int col_nr, LPSREAL *pcol); -typedef LPSREAL (BFP_CALLMODEL BFPreal_lplrealreal)(lprec *lp, LREAL theta, LPSREAL *pcol); - -typedef int (BFP_CALLMODEL getcolumnex_func)(lprec *lp, int colnr, LPSREAL *nzvalues, int *nzrows, int *mapin); -typedef int (BFP_CALLMODEL BFPint_lpintrealcbintint)(lprec *lp, int items, getcolumnex_func cb, int *maprow, int*mapcol); - -/* Prototypes for external language libraries */ -/* ------------------------------------------------------------------------- */ -typedef char *(XLI_CALLMODEL XLIchar)(void); -typedef MYBOOL (XLI_CALLMODEL XLIbool_lpintintint)(lprec* lp, int size, int deltasize, int sizevar); -typedef MYBOOL (XLI_CALLMODEL XLIbool_lpcharcharcharint)(lprec *lp, char *modelname, char *dataname, char *options, int verbose); -typedef MYBOOL (XLI_CALLMODEL XLIbool_lpcharcharbool)(lprec *lp, char *filename, char *options, MYBOOL results); - - -/* Main lp_solve prototypes and function definitions */ -/* ------------------------------------------------------------------------- */ -struct _lprec -{ - /* Full list of exported functions made available in a quasi object-oriented fashion */ - add_column_func *add_column; - add_columnex_func *add_columnex; - add_constraint_func *add_constraint; - add_constraintex_func *add_constraintex; - add_lag_con_func *add_lag_con; - add_SOS_func *add_SOS; - column_in_lp_func *column_in_lp; - copy_lp_func *copy_lp; - default_basis_func *default_basis; - del_column_func *del_column; - del_constraint_func *del_constraint; - delete_lp_func *delete_lp; - dualize_lp_func *dualize_lp; - free_lp_func *free_lp; - get_anti_degen_func *get_anti_degen; - get_basis_func *get_basis; - get_basiscrash_func *get_basiscrash; - get_bb_depthlimit_func *get_bb_depthlimit; - get_bb_floorfirst_func *get_bb_floorfirst; - get_bb_rule_func *get_bb_rule; - get_bounds_tighter_func *get_bounds_tighter; - get_break_at_value_func *get_break_at_value; - get_col_name_func *get_col_name; - get_columnex_func *get_columnex; - get_constr_type_func *get_constr_type; - get_constr_value_func *get_constr_value; - get_constraints_func *get_constraints; - get_dual_solution_func *get_dual_solution; - get_epsb_func *get_epsb; - get_epsd_func *get_epsd; - get_epsel_func *get_epsel; - get_epsint_func *get_epsint; - get_epsperturb_func *get_epsperturb; - get_epspivot_func *get_epspivot; - get_improve_func *get_improve; - get_infinite_func *get_infinite; - get_lambda_func *get_lambda; - get_lowbo_func *get_lowbo; - get_lp_index_func *get_lp_index; - get_lp_name_func *get_lp_name; - get_Lrows_func *get_Lrows; - get_mat_func *get_mat; - get_mat_byindex_func *get_mat_byindex; - get_max_level_func *get_max_level; - get_maxpivot_func *get_maxpivot; - get_mip_gap_func *get_mip_gap; - get_multiprice_func *get_multiprice; - get_nameindex_func *get_nameindex; - get_Ncolumns_func *get_Ncolumns; - get_negrange_func *get_negrange; - get_nz_func *get_nonzeros; - get_Norig_columns_func *get_Norig_columns; - get_Norig_rows_func *get_Norig_rows; - get_Nrows_func *get_Nrows; - get_obj_bound_func *get_obj_bound; - get_objective_func *get_objective; - get_orig_index_func *get_orig_index; - get_origcol_name_func *get_origcol_name; - get_origrow_name_func *get_origrow_name; - get_partialprice_func *get_partialprice; - get_pivoting_func *get_pivoting; - get_presolve_func *get_presolve; - get_presolveloops_func *get_presolveloops; - get_primal_solution_func *get_primal_solution; - get_print_sol_func *get_print_sol; - get_pseudocosts_func *get_pseudocosts; - get_ptr_constraints_func *get_ptr_constraints; - get_ptr_dual_solution_func *get_ptr_dual_solution; - get_ptr_lambda_func *get_ptr_lambda; - get_ptr_primal_solution_func *get_ptr_primal_solution; - get_ptr_sensitivity_obj_func *get_ptr_sensitivity_obj; - get_ptr_sensitivity_objex_func *get_ptr_sensitivity_objex; - get_ptr_sensitivity_rhs_func *get_ptr_sensitivity_rhs; - get_ptr_variables_func *get_ptr_variables; - get_rh_func *get_rh; - get_rh_range_func *get_rh_range; - get_row_func *get_row; - get_rowex_func *get_rowex; - get_row_name_func *get_row_name; - get_scalelimit_func *get_scalelimit; - get_scaling_func *get_scaling; - get_sensitivity_obj_func *get_sensitivity_obj; - get_sensitivity_objex_func *get_sensitivity_objex; - get_sensitivity_rhs_func *get_sensitivity_rhs; - get_simplextype_func *get_simplextype; - get_solutioncount_func *get_solutioncount; - get_solutionlimit_func *get_solutionlimit; - get_status_func *get_status; - get_statustext_func *get_statustext; - get_timeout_func *get_timeout; - get_total_iter_func *get_total_iter; - get_total_nodes_func *get_total_nodes; - get_upbo_func *get_upbo; - get_var_branch_func *get_var_branch; - get_var_dualresult_func *get_var_dualresult; - get_var_primalresult_func *get_var_primalresult; - get_var_priority_func *get_var_priority; - get_variables_func *get_variables; - get_verbose_func *get_verbose; - get_working_objective_func *get_working_objective; - has_BFP_func *has_BFP; - has_XLI_func *has_XLI; - is_add_rowmode_func *is_add_rowmode; - is_anti_degen_func *is_anti_degen; - is_binary_func *is_binary; - is_break_at_first_func *is_break_at_first; - is_constr_type_func *is_constr_type; - is_debug_func *is_debug; - is_feasible_func *is_feasible; - is_infinite_func *is_infinite; - is_int_func *is_int; - is_integerscaling_func *is_integerscaling; - is_lag_trace_func *is_lag_trace; - is_maxim_func *is_maxim; - is_nativeBFP_func *is_nativeBFP; - is_nativeXLI_func *is_nativeXLI; - is_negative_func *is_negative; - is_obj_in_basis_func *is_obj_in_basis; - is_piv_mode_func *is_piv_mode; - is_piv_rule_func *is_piv_rule; - is_presolve_func *is_presolve; - is_scalemode_func *is_scalemode; - is_scaletype_func *is_scaletype; - is_semicont_func *is_semicont; - is_SOS_var_func *is_SOS_var; - is_trace_func *is_trace; - is_unbounded_func *is_unbounded; - is_use_names_func *is_use_names; - lp_solve_version_func *lp_solve_version; - make_lp_func *make_lp; - print_constraints_func *print_constraints; - print_debugdump_func *print_debugdump; - print_duals_func *print_duals; - print_lp_func *print_lp; - print_objective_func *print_objective; - print_scales_func *print_scales; - print_solution_func *print_solution; - print_str_func *print_str; - print_tableau_func *print_tableau; - put_abortfunc_func *put_abortfunc; - put_bb_nodefunc_func *put_bb_nodefunc; - put_bb_branchfunc_func *put_bb_branchfunc; - put_logfunc_func *put_logfunc; - put_msgfunc_func *put_msgfunc; - read_LP_func *read_LP; - read_MPS_func *read_MPS; - read_XLI_func *read_XLI; - read_params_func *read_params; - read_basis_func *read_basis; - reset_basis_func *reset_basis; - reset_params_func *reset_params; - resize_lp_func *resize_lp; - set_add_rowmode_func *set_add_rowmode; - set_anti_degen_func *set_anti_degen; - set_basisvar_func *set_basisvar; - set_basis_func *set_basis; - set_basiscrash_func *set_basiscrash; - set_bb_depthlimit_func *set_bb_depthlimit; - set_bb_floorfirst_func *set_bb_floorfirst; - set_bb_rule_func *set_bb_rule; - set_BFP_func *set_BFP; - set_binary_func *set_binary; - set_bounds_func *set_bounds; - set_bounds_tighter_func *set_bounds_tighter; - set_break_at_first_func *set_break_at_first; - set_break_at_value_func *set_break_at_value; - set_column_func *set_column; - set_columnex_func *set_columnex; - set_col_name_func *set_col_name; - set_constr_type_func *set_constr_type; - set_debug_func *set_debug; - set_epsb_func *set_epsb; - set_epsd_func *set_epsd; - set_epsel_func *set_epsel; - set_epsint_func *set_epsint; - set_epslevel_func *set_epslevel; - set_epsperturb_func *set_epsperturb; - set_epspivot_func *set_epspivot; - set_unbounded_func *set_unbounded; - set_improve_func *set_improve; - set_infinite_func *set_infinite; - set_int_func *set_int; - set_lag_trace_func *set_lag_trace; - set_lowbo_func *set_lowbo; - set_lp_name_func *set_lp_name; - set_mat_func *set_mat; - set_maxim_func *set_maxim; - set_maxpivot_func *set_maxpivot; - set_minim_func *set_minim; - set_mip_gap_func *set_mip_gap; - set_multiprice_func *set_multiprice; - set_negrange_func *set_negrange; - set_obj_bound_func *set_obj_bound; - set_obj_fn_func *set_obj_fn; - set_obj_fnex_func *set_obj_fnex; - set_obj_func *set_obj; - set_obj_in_basis_func *set_obj_in_basis; - set_outputfile_func *set_outputfile; - set_outputstream_func *set_outputstream; - set_partialprice_func *set_partialprice; - set_pivoting_func *set_pivoting; - set_preferdual_func *set_preferdual; - set_presolve_func *set_presolve; - set_print_sol_func *set_print_sol; - set_pseudocosts_func *set_pseudocosts; - set_rh_func *set_rh; - set_rh_range_func *set_rh_range; - set_rh_vec_func *set_rh_vec; - set_row_func *set_row; - set_rowex_func *set_rowex; - set_row_name_func *set_row_name; - set_scalelimit_func *set_scalelimit; - set_scaling_func *set_scaling; - set_semicont_func *set_semicont; - set_sense_func *set_sense; - set_simplextype_func *set_simplextype; - set_solutionlimit_func *set_solutionlimit; - set_timeout_func *set_timeout; - set_trace_func *set_trace; - set_upbo_func *set_upbo; - set_use_names_func *set_use_names; - set_var_branch_func *set_var_branch; - set_var_weights_func *set_var_weights; - set_verbose_func *set_verbose; - set_XLI_func *set_XLI; - solve_func *solve; - str_add_column_func *str_add_column; - str_add_constraint_func *str_add_constraint; - str_add_lag_con_func *str_add_lag_con; - str_set_obj_fn_func *str_set_obj_fn; - str_set_rh_vec_func *str_set_rh_vec; - time_elapsed_func *time_elapsed; - unscale_func *unscale; - write_lp_func *write_lp; - write_LP_func *write_LP; - write_mps_func *write_mps; - write_MPS_func *write_MPS; - write_freemps_func *write_freemps; - write_freeMPS_func *write_freeMPS; - write_XLI_func *write_XLI; - write_basis_func *write_basis; - write_params_func *write_params; - - /* Spacer */ - int *alignmentspacer; - - /* Problem description */ - char *lp_name; /* The name of the model */ - - /* Problem sizes */ - int sum; /* The total number of variables, including slacks */ - int rows; - int columns; - int equalities; /* No of non-Lagrangean equality constraints in the problem */ - int boundedvars; /* Count of bounded variables */ - int INTfuture1; - - /* Memory allocation sizes */ - int sum_alloc; /* The allocated memory for row+column-sized data */ - int rows_alloc; /* The allocated memory for row-sized data */ - int columns_alloc; /* The allocated memory for column-sized data */ - - /* Model status and solver result variables */ - MYBOOL source_is_file; /* The base model was read from a file */ - MYBOOL model_is_pure; /* The model has been built entirely from row and column additions */ - MYBOOL model_is_valid; /* Has this lp pased the 'test' */ - MYBOOL tighten_on_set; /* Specify if bounds will be tightened or overriden at bound setting */ - MYBOOL names_used; /* Flag to indicate if names for rows and columns are used */ - MYBOOL use_row_names; /* Flag to indicate if names for rows are used */ - MYBOOL use_col_names; /* Flag to indicate if names for columns are used */ - - MYBOOL lag_trace; /* Print information on Lagrange progression */ - MYBOOL spx_trace; /* Print information on simplex progression */ - MYBOOL bb_trace; /* TRUE to print extra debug information */ - MYBOOL streamowned; /* TRUE if the handle should be closed at delete_lp() */ - MYBOOL obj_in_basis; /* TRUE if the objective function is in the basis matrix */ - - int spx_status; /* Simplex solver feasibility/mode code */ - int lag_status; /* Extra status variable for lag_solve */ - int solutioncount; /* number of equal-valued solutions found (up to solutionlimit) */ - int solutionlimit; /* upper number of equal-valued solutions kept track of */ - - LPSREAL real_solution; /* Optimal non-MIP solution base */ - LPSREAL *solution; /* sum_alloc+1 : Solution array of the next to optimal LP, - Index 0 : Objective function value, - Indeces 1..rows : Slack variable values, - Indeced rows+1..sum : Variable values */ - LPSREAL *best_solution; /* sum_alloc+1 : Solution array of optimal 'Integer' LP, - structured as the solution array above */ - LPSREAL *full_solution; /* sum_alloc+1 : Final solution array expanded for deleted variables */ - LPSREAL *edgeVector; /* Array of reduced cost scaling norms (DEVEX and Steepest Edge) */ - - LPSREAL *drow; /* sum+1: Reduced costs of the last simplex */ - int *nzdrow; /* sum+1: Indeces of non-zero reduced costs of the last simplex */ - LPSREAL *duals; /* rows_alloc+1 : The dual variables of the last LP */ - LPSREAL *full_duals; /* sum_alloc+1: Final duals array expanded for deleted variables */ - LPSREAL *dualsfrom; /* sum_alloc+1 :The sensitivity on dual variables/reduced costs - of the last LP */ - LPSREAL *dualstill; /* sum_alloc+1 :The sensitivity on dual variables/reduced costs - of the last LP */ - LPSREAL *objfrom; /* columns_alloc+1 :The sensitivity on objective function - of the last LP */ - LPSREAL *objtill; /* columns_alloc+1 :The sensitivity on objective function - of the last LP */ - LPSREAL *objfromvalue; /* columns_alloc+1 :The value of the variables when objective value - is at its from value of the last LP */ - LPSREAL *orig_obj; /* Unused pointer - Placeholder for OF not part of B */ - LPSREAL *obj; /* Special vector used to temporarily change the OF vector */ - - COUNTER current_iter; /* Number of iterations in the current/last simplex */ - COUNTER total_iter; /* Number of iterations over all B&B steps */ - COUNTER current_bswap; /* Number of bound swaps in the current/last simplex */ - COUNTER total_bswap; /* Number of bount swaps over all B&B steps */ - int solvecount; /* The number of solve() performed in this model */ - int max_pivots; /* Number of pivots between refactorizations of the basis */ - - /* Various execution parameters */ - int simplex_strategy; /* Set desired combination of primal and dual simplex algorithms */ - int simplex_mode; /* Specifies the current simplex mode during solve; see simplex_strategy */ - int verbose; /* Set amount of run-time messages and results */ - int print_sol; /* TRUE to print optimal solution; AUTOMATIC skips zeros */ - FILE *outstream; /* Output stream, initialized to STDOUT */ - - /* Main Branch and Bound settings */ - MYBOOL *bb_varbranch; /* Determines branching strategy at the individual variable level; - the setting here overrides the bb_floorfirst setting */ - int piv_strategy; /* Strategy for selecting row and column entering/leaving */ - int _piv_rule_; /* Internal working rule-part of piv_strategy above */ - int bb_rule; /* Rule for selecting B&B variables */ - MYBOOL bb_floorfirst; /* Set BRANCH_FLOOR for B&B to set variables to floor bound first; - conversely with BRANCH_CEILING, the ceiling value is set first */ - MYBOOL bb_breakfirst; /* TRUE to stop at first feasible solution */ - MYBOOL _piv_left_; /* Internal variable indicating active pricing loop order */ - MYBOOL BOOLfuture1; - - LPSREAL scalelimit; /* Relative convergence criterion for iterated scaling */ - int scalemode; /* OR-ed codes for data scaling */ - int improve; /* Set to non-zero for iterative improvement */ - int anti_degen; /* Anti-degen strategy (or none) TRUE to avoid cycling */ - int do_presolve; /* PRESOLVE_ parameters for LP presolving */ - int presolveloops; /* Maximum number of presolve loops */ - - int perturb_count; /* The number of bound relaxation retries performed */ - - /* Row and column names storage variables */ - hashelem **row_name; /* rows_alloc+1 */ - hashelem **col_name; /* columns_alloc+1 */ - hashtable *rowname_hashtab; /* hash table to store row names */ - hashtable *colname_hashtab; /* hash table to store column names */ - - /* Optionally specify continuous rows/column blocks for partial pricing */ - partialrec *rowblocks; - partialrec *colblocks; - - /* Row and column type codes */ - MYBOOL *var_type; /* sum_alloc+1 : TRUE if variable must be integer */ - - /* Data for multiple pricing */ - multirec *multivars; - int multiblockdiv; /* The divisor used to set or augment pricing block */ - - /* Variable (column) parameters */ - int fixedvars; /* The current number of basic fixed variables in the model */ - int int_vars; /* Number of variables required to be integer */ - - int sc_vars; /* Number of semi-continuous variables */ - LPSREAL *sc_lobound; /* sum_columns+1 : TRUE if variable is semi-continuous; - value replaced by conventional lower bound during solve */ - int *var_is_free; /* columns+1: Index of twin variable if variable is free */ - int *var_priority; /* columns: Priority-mapping of variables */ - - SOSgroup *GUB; /* Pointer to record containing GUBs */ - - int sos_vars; /* Number of variables in the sos_priority list */ - int sos_ints; /* Number of integers in SOS'es above */ - SOSgroup *SOS; /* Pointer to record containing all SOS'es */ - int *sos_priority; /* Priority-sorted list of variables (no duplicates) */ - - /* Optionally specify list of active rows/columns used in multiple pricing */ - LPSREAL *bsolveVal; /* rows+1: bsolved solution vector for reduced costs */ - int *bsolveIdx; /* rows+1: Non-zero indeces of bsolveVal */ - - /* RHS storage */ - LPSREAL *orig_rhs; /* rows_alloc+1 : The RHS after scaling and sign - changing, but before 'Bound transformation' */ - LREAL *rhs; /* rows_alloc+1 : The RHS of the current simplex tableau */ - - /* Row (constraint) parameters */ - int *row_type; /* rows_alloc+1 : Row/constraint type coding */ - - /* Optionally specify data for dual long-step */ - multirec *longsteps; - - /* Original and working row and variable bounds */ - LPSREAL *orig_upbo; /* sum_alloc+1 : Bound before transformations */ - LPSREAL *upbo; /* " " : Upper bound after transformation and B&B work */ - LPSREAL *orig_lowbo; /* " " */ - LPSREAL *lowbo; /* " " : Lower bound after transformation and B&B work */ - - /* User data and basis factorization matrices (ETA or LU, product form) */ - MATrec *matA; - INVrec *invB; - - /* Basis and bounds */ - BBrec *bb_bounds; /* The linked list of B&B bounds */ - BBrec *rootbounds; /* The bounds at the lowest B&B level */ - basisrec *bb_basis; /* The linked list of B&B bases */ - basisrec *rootbasis; - OBJmonrec *monitor; /* Objective monitoring record for stalling/degeneracy handling */ - - /* Scaling parameters */ - LPSREAL *scalars; /* sum_alloc+1:0..Rows the scaling of the rows, - Rows+1..Sum the scaling of the columns */ - MYBOOL scaling_used; /* TRUE if scaling is used */ - MYBOOL columns_scaled; /* TRUE if the columns are scaled too */ - MYBOOL varmap_locked; /* Determines whether the var_to_orig and orig_to_var are fixed */ - - /* Variable state information */ - MYBOOL basis_valid; /* TRUE is the basis is still valid */ - int crashmode; /* Basis crashing mode (or none) */ - int *var_basic; /* rows_alloc+1: The list of columns in the basis */ - LPSREAL *val_nonbasic; /* Array to store current values of non-basic variables */ - MYBOOL *is_basic; /* sum_alloc+1: TRUE if the column is in the basis */ - MYBOOL *is_lower; /* " " : TRUE if the variable is at its - lower bound (or in the basis), FALSE otherwise */ - - /* Simplex basis indicators */ - int *rejectpivot; /* List of unacceptable pivot choices due to division-by-zero */ - BBPSrec *bb_PseudoCost; /* Data structure for costing of node branchings */ - int bb_PseudoUpdates; /* Maximum number of updates for pseudo-costs */ - int bb_strongbranches; /* The number of strong B&B branches performed */ - int is_strongbranch; /* Are we currently in a strong branch mode? */ - int bb_improvements; /* The number of discrete B&B objective improvement steps */ - - /* Solver working variables */ - LPSREAL rhsmax; /* The maximum |value| of the rhs vector at any iteration */ - LPSREAL suminfeas; /* The working sum of primal and dual infeasibilities */ - LPSREAL bigM; /* Original objective weighting in primal phase 1 */ - LPSREAL P1extraVal; /* Phase 1 OF/RHS offset for feasibility */ - int P1extraDim; /* Phase 1 additional columns/rows for feasibility */ - int spx_action; /* ACTION_ variables for the simplex routine */ - MYBOOL spx_perturbed; /* The variable bounds were relaxed/perturbed into this simplex */ - MYBOOL bb_break; /* Solver working variable; signals break of the B&B */ - MYBOOL wasPreprocessed; /* The solve preprocessing was performed */ - MYBOOL wasPresolved; /* The solve presolver was invoked */ - int INTfuture2; - - /* Lagragean solver storage and parameters */ - MATrec *matL; - LPSREAL *lag_rhs; /* Array of Lagrangean rhs vector */ - int *lag_con_type; /* Array of GT, LT or EQ */ - LPSREAL *lambda; /* Lambda values (Lagrangean multipliers) */ - LPSREAL lag_bound; /* The Lagrangian lower OF bound */ - LPSREAL lag_accept; /* The Lagrangian convergence criterion */ - - /* Solver thresholds */ - LPSREAL infinite; /* Limit for dynamic range */ - LPSREAL negrange; /* Limit for negative variable range */ - LPSREAL epsmachine; /* Default machine accuracy */ - LPSREAL epsvalue; /* Input data precision / rounding of data values to 0 */ - LPSREAL epsprimal; /* For rounding RHS values to 0/infeasibility */ - LPSREAL epsdual; /* For rounding reduced costs to zero */ - LPSREAL epspivot; /* Pivot reject tolerance */ - LPSREAL epsperturb; /* Perturbation scalar */ - LPSREAL epssolution; /* The solution tolerance for final validation */ - - /* Branch & Bound working parameters */ - int bb_status; /* Indicator that the last solvelp() gave an improved B&B solution */ - int bb_level; /* Solver B&B working variable (recursion depth) */ - int bb_maxlevel; /* The deepest B&B level of the last solution */ - int bb_limitlevel; /* The maximum B&B level allowed */ - COUNTER bb_totalnodes; /* Total number of nodes processed in B&B */ - int bb_solutionlevel; /* The B&B level of the last / best solution */ - int bb_cutpoolsize; /* Size of the B&B cut pool */ - int bb_cutpoolused; /* Currently used cut pool */ - int bb_constraintOF; /* General purpose B&B parameter (typically for testing) */ - int *bb_cuttype; /* The type of the currently used cuts */ - int *bb_varactive; /* The B&B state of the variable; 0 means inactive */ - DeltaVrec *bb_upperchange; /* Changes to upper bounds during the B&B phase */ - DeltaVrec *bb_lowerchange; /* Changes to lower bounds during the B&B phase */ - - LPSREAL bb_deltaOF; /* Minimum OF step value; computed at beginning of solve() */ - - LPSREAL bb_breakOF; /* User-settable value for the objective function deemed - to be sufficiently good in an integer problem */ - LPSREAL bb_limitOF; /* "Dual" bound / limit to final optimal MIP solution */ - LPSREAL bb_heuristicOF; /* Set initial "at least better than" guess for objective function - (can significantly speed up B&B iterations) */ - LPSREAL bb_parentOF; /* The OF value of the previous BB simplex */ - LPSREAL bb_workOF; /* The unadjusted OF value for the current best solution */ - - /* Internal work arrays allocated as required */ - presolveundorec *presolve_undo; - workarraysrec *workarrays; - - /* MIP parameters */ - LPSREAL epsint; /* Margin of error in determining if a float value is integer */ - LPSREAL mip_absgap; /* Absolute MIP gap */ - LPSREAL mip_relgap; /* Relative MIP gap */ - - /* Time/timer variables and extended status text */ - double timecreate; - double timestart; - double timeheuristic; - double timepresolved; - double timeend; - long sectimeout; - - /* Extended status message text set via explain() */ - char *ex_status; - - /* Refactorization engine interface routines (for dynamic DLL/SO BFPs) */ -#if LoadInverseLib == TRUE - #ifdef WIN32 - HINSTANCE hBFP; - #else - void *hBFP; - #endif -#endif - BFPchar *bfp_name; - BFPbool_lpintintint *bfp_compatible; - BFPbool_lpintintchar *bfp_init; - BFP_lp *bfp_free; - BFPbool_lpint *bfp_resize; - BFPint_lp *bfp_memallocated; - BFPbool_lp *bfp_restart; - BFPbool_lp *bfp_mustrefactorize; - BFPint_lp *bfp_preparefactorization; - BFPint_lpintintboolbool *bfp_factorize; - BFP_lp *bfp_finishfactorization; - BFP_lp *bfp_updaterefactstats; - BFPlreal_lpintintreal *bfp_prepareupdate; - BFPreal_lplrealreal *bfp_pivotRHS; - BFPbool_lpbool *bfp_finishupdate; - BFP_lprealint *bfp_ftran_prepare; - BFP_lprealint *bfp_ftran_normal; - BFP_lprealint *bfp_btran_normal; - BFP_lprealintrealint *bfp_btran_double; - BFPint_lp *bfp_status; - BFPint_lpbool *bfp_nonzeros; - BFPbool_lp *bfp_implicitslack; - BFPint_lp *bfp_indexbase; - BFPint_lp *bfp_rowoffset; - BFPint_lp *bfp_pivotmax; - BFPbool_lpint *bfp_pivotalloc; - BFPint_lp *bfp_colcount; - BFPbool_lp *bfp_canresetbasis; - BFPreal_lp *bfp_efficiency; - BFPrealp_lp *bfp_pivotvector; - BFPint_lp *bfp_pivotcount; - BFPint_lpint *bfp_refactcount; - BFPbool_lp *bfp_isSetI; - BFPint_lpintrealcbintint *bfp_findredundant; - - /* External language interface routines (for dynamic DLL/SO XLIs) */ -#if LoadLanguageLib == TRUE - #ifdef WIN32 - HINSTANCE hXLI; - #else - void *hXLI; - #endif -#endif - XLIchar *xli_name; - XLIbool_lpintintint *xli_compatible; - XLIbool_lpcharcharcharint *xli_readmodel; - XLIbool_lpcharcharbool *xli_writemodel; - - /* Miscellaneous internal functions made available externally */ - userabortfunc *userabort; - reportfunc *report; - explainfunc *explain; - getvectorfunc *get_lpcolumn; - getpackedfunc *get_basiscolumn; - get_OF_activefunc *get_OF_active; - getMDOfunc *getMDO; - invertfunc *invert; - set_actionfunc *set_action; - is_actionfunc *is_action; - clear_actionfunc *clear_action; - - /* User program interface callbacks */ - lphandle_intfunc *ctrlc; - void *ctrlchandle; /* User-specified "owner process ID" */ - lphandlestr_func *writelog; - void *loghandle; /* User-specified "owner process ID" */ - lphandlestr_func *debuginfo; - lphandleint_func *usermessage; - int msgmask; - void *msghandle; /* User-specified "owner process ID" */ - lphandleint_intfunc *bb_usenode; - void *bb_nodehandle; /* User-specified "owner process ID" */ - lphandleint_intfunc *bb_usebranch; - void *bb_branchhandle; /* User-specified "owner process ID" */ - - /* replacement of static variables */ - char *rowcol_name; /* The name of a row/column */ -}; - - -#ifdef __cplusplus -__EXTERN_C { -#endif - - -/* User and system function interfaces */ -/* ------------------------------------------------------------------------- */ - -void __EXPORT_TYPE __WINAPI lp_solve_version(int *majorversion, int *minorversion, int *release, int *build); - -lprec __EXPORT_TYPE * __WINAPI make_lp(int rows, int columns); -MYBOOL __EXPORT_TYPE __WINAPI resize_lp(lprec *lp, int rows, int columns); -int __EXPORT_TYPE __WINAPI get_status(lprec *lp); -char __EXPORT_TYPE * __WINAPI get_statustext(lprec *lp, int statuscode); -MYBOOL __EXPORT_TYPE __WINAPI is_obj_in_basis(lprec *lp); -void __EXPORT_TYPE __WINAPI set_obj_in_basis(lprec *lp, MYBOOL obj_in_basis); -/* Create and initialise a lprec structure defaults */ - -lprec __EXPORT_TYPE * __WINAPI copy_lp(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI dualize_lp(lprec *lp); -STATIC MYBOOL memopt_lp(lprec *lp, int rowextra, int colextra, int nzextra); -/* Copy or dualize the lp */ - -void __EXPORT_TYPE __WINAPI delete_lp(lprec *lp); -void __EXPORT_TYPE __WINAPI free_lp(lprec **plp); -/* Remove problem from memory */ - -MYBOOL __EXPORT_TYPE __WINAPI set_lp_name(lprec *lp, char *lpname); -char __EXPORT_TYPE * __WINAPI get_lp_name(lprec *lp); -/* Set and get the problem name */ - -MYBOOL __EXPORT_TYPE __WINAPI has_BFP(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI is_nativeBFP(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI set_BFP(lprec *lp, char *filename); -/* Set basis factorization engine */ - -lprec __EXPORT_TYPE * __WINAPI read_XLI(char *xliname, char *modelname, char *dataname, char *options, int verbose); -MYBOOL __EXPORT_TYPE __WINAPI write_XLI(lprec *lp, char *filename, char *options, MYBOOL results); -MYBOOL __EXPORT_TYPE __WINAPI has_XLI(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI is_nativeXLI(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI set_XLI(lprec *lp, char *filename); -/* Set external language interface */ - -MYBOOL __EXPORT_TYPE __WINAPI set_obj(lprec *lp, int colnr, LPSREAL value); -MYBOOL __EXPORT_TYPE __WINAPI set_obj_fn(lprec *lp, LPSREAL *row); -MYBOOL __EXPORT_TYPE __WINAPI set_obj_fnex(lprec *lp, int count, LPSREAL *row, int *colno); -/* set the objective function (Row 0) of the matrix */ -MYBOOL __EXPORT_TYPE __WINAPI str_set_obj_fn(lprec *lp, char *row_string); -/* The same, but with string input */ -void __EXPORT_TYPE __WINAPI set_sense(lprec *lp, MYBOOL maximize); -void __EXPORT_TYPE __WINAPI set_maxim(lprec *lp); -void __EXPORT_TYPE __WINAPI set_minim(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI is_maxim(lprec *lp); -/* Set optimization direction for the objective function */ - -MYBOOL __EXPORT_TYPE __WINAPI add_constraint(lprec *lp, LPSREAL *row, int constr_type, LPSREAL rh); -MYBOOL __EXPORT_TYPE __WINAPI add_constraintex(lprec *lp, int count, LPSREAL *row, int *colno, int constr_type, LPSREAL rh); -MYBOOL __EXPORT_TYPE __WINAPI set_add_rowmode(lprec *lp, MYBOOL turnon); -MYBOOL __EXPORT_TYPE __WINAPI is_add_rowmode(lprec *lp); -/* Add a constraint to the problem, row is the constraint row, rh is the right hand side, - constr_type is the type of constraint (LE (<=), GE(>=), EQ(=)) */ -MYBOOL __EXPORT_TYPE __WINAPI str_add_constraint(lprec *lp, char *row_string, int constr_type, LPSREAL rh); -/* The same, but with string input */ - -MYBOOL __EXPORT_TYPE __WINAPI set_row(lprec *lp, int rownr, LPSREAL *row); -MYBOOL __EXPORT_TYPE __WINAPI set_rowex(lprec *lp, int rownr, int count, LPSREAL *row, int *colno); -MYBOOL __EXPORT_TYPE __WINAPI get_row(lprec *lp, int rownr, LPSREAL *row); -int __EXPORT_TYPE __WINAPI get_rowex(lprec *lp, int rownr, LPSREAL *row, int *colno); -/* Fill row with the row row_nr from the problem */ - -MYBOOL __EXPORT_TYPE __WINAPI del_constraint(lprec *lp, int rownr); -STATIC MYBOOL del_constraintex(lprec *lp, LLrec *rowmap); -/* Remove constrain nr del_row from the problem */ - -MYBOOL __EXPORT_TYPE __WINAPI add_lag_con(lprec *lp, LPSREAL *row, int con_type, LPSREAL rhs); -/* add a Lagrangian constraint of form Row' x contype Rhs */ -MYBOOL __EXPORT_TYPE __WINAPI str_add_lag_con(lprec *lp, char *row_string, int con_type, LPSREAL rhs); -/* The same, but with string input */ -void __EXPORT_TYPE __WINAPI set_lag_trace(lprec *lp, MYBOOL lag_trace); -MYBOOL __EXPORT_TYPE __WINAPI is_lag_trace(lprec *lp); -/* Set debugging/tracing mode of the Lagrangean solver */ - -MYBOOL __EXPORT_TYPE __WINAPI set_constr_type(lprec *lp, int rownr, int con_type); -int __EXPORT_TYPE __WINAPI get_constr_type(lprec *lp, int rownr); -LPSREAL __EXPORT_TYPE __WINAPI get_constr_value(lprec *lp, int rownr, int count, LPSREAL *primsolution, int *nzindex); -MYBOOL __EXPORT_TYPE __WINAPI is_constr_type(lprec *lp, int rownr, int mask); -STATIC char *get_str_constr_type(lprec *lp, int con_type); -STATIC int get_constr_class(lprec *lp, int rownr); -STATIC char *get_str_constr_class(lprec *lp, int con_class); -/* Set the type of constraint in row Row (LE, GE, EQ) */ - -MYBOOL __EXPORT_TYPE __WINAPI set_rh(lprec *lp, int rownr, LPSREAL value); -LPSREAL __EXPORT_TYPE __WINAPI get_rh(lprec *lp, int rownr); -/* Set and get the right hand side of a constraint row */ -MYBOOL __EXPORT_TYPE __WINAPI set_rh_range(lprec *lp, int rownr, LPSREAL deltavalue); -LPSREAL __EXPORT_TYPE __WINAPI get_rh_range(lprec *lp, int rownr); -/* Set the RHS range; i.e. the lower and upper bounds of a constraint row */ -void __EXPORT_TYPE __WINAPI set_rh_vec(lprec *lp, LPSREAL *rh); -/* Set the right hand side vector */ -MYBOOL __EXPORT_TYPE __WINAPI str_set_rh_vec(lprec *lp, char *rh_string); -/* The same, but with string input */ - -MYBOOL __EXPORT_TYPE __WINAPI add_column(lprec *lp, LPSREAL *column); -MYBOOL __EXPORT_TYPE __WINAPI add_columnex(lprec *lp, int count, LPSREAL *column, int *rowno); -MYBOOL __EXPORT_TYPE __WINAPI str_add_column(lprec *lp, char *col_string); -/* Add a column to the problem */ - -MYBOOL __EXPORT_TYPE __WINAPI set_column(lprec *lp, int colnr, LPSREAL *column); -MYBOOL __EXPORT_TYPE __WINAPI set_columnex(lprec *lp, int colnr, int count, LPSREAL *column, int *rowno); -/* Overwrite existing column data */ - -int __EXPORT_TYPE __WINAPI column_in_lp(lprec *lp, LPSREAL *column); -/* Returns the column index if column is already present in lp, otherwise 0. - (Does not look at bounds and types, only looks at matrix values */ - -int __EXPORT_TYPE __WINAPI get_columnex(lprec *lp, int colnr, LPSREAL *column, int *nzrow); -MYBOOL __EXPORT_TYPE __WINAPI get_column(lprec *lp, int colnr, LPSREAL *column); -/* Fill column with the column col_nr from the problem */ - -MYBOOL __EXPORT_TYPE __WINAPI del_column(lprec *lp, int colnr); -STATIC MYBOOL del_columnex(lprec *lp, LLrec *colmap); -/* Delete a column */ - -MYBOOL __EXPORT_TYPE __WINAPI set_mat(lprec *lp, int rownr, int colnr, LPSREAL value); -/* Fill in element (Row,Column) of the matrix - Row in [0..Rows] and Column in [1..Columns] */ -LPSREAL __EXPORT_TYPE __WINAPI get_mat(lprec *lp, int rownr, int colnr); -LPSREAL __EXPORT_TYPE __WINAPI get_mat_byindex(lprec *lp, int matindex, MYBOOL isrow, MYBOOL adjustsign); -int __EXPORT_TYPE __WINAPI get_nonzeros(lprec *lp); -/* get a single element from the matrix */ /* Name changed from "mat_elm" by KE */ - -void __EXPORT_TYPE __WINAPI set_bounds_tighter(lprec *lp, MYBOOL tighten); -MYBOOL get_bounds(lprec *lp, int column, LPSREAL *lower, LPSREAL *upper); -MYBOOL __EXPORT_TYPE __WINAPI get_bounds_tighter(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI set_upbo(lprec *lp, int colnr, LPSREAL value); -LPSREAL __EXPORT_TYPE __WINAPI get_upbo(lprec *lp, int colnr); -MYBOOL __EXPORT_TYPE __WINAPI set_lowbo(lprec *lp, int colnr, LPSREAL value); -LPSREAL __EXPORT_TYPE __WINAPI get_lowbo(lprec *lp, int colnr); -MYBOOL __EXPORT_TYPE __WINAPI set_bounds(lprec *lp, int colnr, LPSREAL lower, LPSREAL upper); -MYBOOL __EXPORT_TYPE __WINAPI set_unbounded(lprec *lp, int colnr); -MYBOOL __EXPORT_TYPE __WINAPI is_unbounded(lprec *lp, int colnr); -/* Set the upper and lower bounds of a variable */ - -MYBOOL __EXPORT_TYPE __WINAPI set_int(lprec *lp, int colnr, MYBOOL must_be_int); -MYBOOL __EXPORT_TYPE __WINAPI is_int(lprec *lp, int colnr); -MYBOOL __EXPORT_TYPE __WINAPI set_binary(lprec *lp, int colnr, MYBOOL must_be_bin); -MYBOOL __EXPORT_TYPE __WINAPI is_binary(lprec *lp, int colnr); -MYBOOL __EXPORT_TYPE __WINAPI set_semicont(lprec *lp, int colnr, MYBOOL must_be_sc); -MYBOOL __EXPORT_TYPE __WINAPI is_semicont(lprec *lp, int colnr); -MYBOOL __EXPORT_TYPE __WINAPI is_negative(lprec *lp, int colnr); -MYBOOL __EXPORT_TYPE __WINAPI set_var_weights(lprec *lp, LPSREAL *weights); -int __EXPORT_TYPE __WINAPI get_var_priority(lprec *lp, int colnr); -/* Set the type of variable */ - -MYBOOL __EXPORT_TYPE __WINAPI set_pseudocosts(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int *updatelimit); -MYBOOL __EXPORT_TYPE __WINAPI get_pseudocosts(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int *updatelimit); -/* Set initial values for, or get computed pseudocost vectors; - note that setting of pseudocosts can only happen in response to a - call-back function optionally requesting this */ - -int __EXPORT_TYPE __WINAPI add_SOS(lprec *lp, char *name, int sostype, int priority, int count, int *sosvars, LPSREAL *weights); -MYBOOL __EXPORT_TYPE __WINAPI is_SOS_var(lprec *lp, int colnr); -/* Add SOS constraints */ - -MYBOOL __EXPORT_TYPE __WINAPI set_row_name(lprec *lp, int rownr, char *new_name); -char __EXPORT_TYPE * __WINAPI get_row_name(lprec *lp, int rownr); -char __EXPORT_TYPE * __WINAPI get_origrow_name(lprec *lp, int rownr); -/* Set/Get the name of a constraint row */ /* Get added by KE */ - -MYBOOL __EXPORT_TYPE __WINAPI set_col_name(lprec *lp, int colnr, char *new_name); -char __EXPORT_TYPE * __WINAPI get_col_name(lprec *lp, int colnr); -char __EXPORT_TYPE * __WINAPI get_origcol_name(lprec *lp, int colnr); -/* Set/Get the name of a variable column */ /* Get added by KE */ - -void __EXPORT_TYPE __WINAPI unscale(lprec *lp); -/* Undo previous scaling of the problem */ - -void __EXPORT_TYPE __WINAPI set_preferdual(lprec *lp, MYBOOL dodual); -void __EXPORT_TYPE __WINAPI set_simplextype(lprec *lp, int simplextype); -int __EXPORT_TYPE __WINAPI get_simplextype(lprec *lp); -/* Set/Get if lp_solve should prefer the dual simplex over the primal -- added by KE */ - -void __EXPORT_TYPE __WINAPI default_basis(lprec *lp); -void __EXPORT_TYPE __WINAPI set_basiscrash(lprec *lp, int mode); -int __EXPORT_TYPE __WINAPI get_basiscrash(lprec *lp); -int __EXPORT_TYPE __WINAPI set_basisvar(lprec *lp, int basisPos, int enteringCol); -MYBOOL __EXPORT_TYPE __WINAPI set_basis(lprec *lp, int *bascolumn, MYBOOL nonbasic); -MYBOOL __EXPORT_TYPE __WINAPI get_basis(lprec *lp, int *bascolumn, MYBOOL nonbasic); -void __EXPORT_TYPE __WINAPI reset_basis(lprec *lp); -/* Set/Get basis for a re-solved system */ /* Added by KE */ -MYBOOL __EXPORT_TYPE __WINAPI guess_basis(lprec *lp, LPSREAL *guessvector, int *basisvector); - -MYBOOL __EXPORT_TYPE __WINAPI is_feasible(lprec *lp, LPSREAL *values, LPSREAL threshold); -/* returns TRUE if the vector in values is a feasible solution to the lp */ - -int __EXPORT_TYPE __WINAPI solve(lprec *lp); -/* Solve the problem */ - -LPSREAL __EXPORT_TYPE __WINAPI time_elapsed(lprec *lp); -/* Return the number of seconds since start of solution process */ - -void __EXPORT_TYPE __WINAPI put_bb_nodefunc(lprec *lp, lphandleint_intfunc newnode, void *bbnodehandle); -void __EXPORT_TYPE __WINAPI put_bb_branchfunc(lprec *lp, lphandleint_intfunc newbranch, void *bbbranchhandle); -/* Allow the user to override B&B node and branching decisions */ - -void __EXPORT_TYPE __WINAPI put_abortfunc(lprec *lp, lphandle_intfunc newctrlc, void *ctrlchandle); -/* Allow the user to define an interruption callback function */ - -void __EXPORT_TYPE __WINAPI put_logfunc(lprec *lp, lphandlestr_func newlog, void *loghandle); -/* Allow the user to define a logging function */ - -void __EXPORT_TYPE __WINAPI put_msgfunc(lprec *lp, lphandleint_func newmsg, void *msghandle, int mask); -/* Allow the user to define an event-driven message/reporting */ - -MYBOOL __EXPORT_TYPE __WINAPI get_primal_solution(lprec *lp, LPSREAL *pv); -MYBOOL __EXPORT_TYPE __WINAPI get_ptr_primal_solution(lprec *lp, LPSREAL **pv); -MYBOOL __EXPORT_TYPE __WINAPI get_dual_solution(lprec *lp, LPSREAL *rc); -MYBOOL __EXPORT_TYPE __WINAPI get_ptr_dual_solution(lprec *lp, LPSREAL **rc); -MYBOOL __EXPORT_TYPE __WINAPI get_lambda(lprec *lp, LPSREAL *lambda); -MYBOOL __EXPORT_TYPE __WINAPI get_ptr_lambda(lprec *lp, LPSREAL **lambda); -/* Get the primal, dual/reduced costs and Lambda vectors */ - -/* Read an MPS file */ -lprec __EXPORT_TYPE * __WINAPI read_MPS(char *filename, int options); -lprec __EXPORT_TYPE * __WINAPI read_mps(FILE *filename, int options); -lprec __EXPORT_TYPE * __WINAPI read_freeMPS(char *filename, int options); -lprec __EXPORT_TYPE * __WINAPI read_freemps(FILE *filename, int options); - -/* Write a MPS file to output */ -MYBOOL __EXPORT_TYPE __WINAPI write_mps(lprec *lp, char *filename); -MYBOOL __EXPORT_TYPE __WINAPI write_MPS(lprec *lp, FILE *output); -MYBOOL __EXPORT_TYPE __WINAPI write_freemps(lprec *lp, char *filename); -MYBOOL __EXPORT_TYPE __WINAPI write_freeMPS(lprec *lp, FILE *output); - -MYBOOL __EXPORT_TYPE __WINAPI write_lp(lprec *lp, char *filename); -MYBOOL __EXPORT_TYPE __WINAPI write_LP(lprec *lp, FILE *output); - /* Write a LP file to output */ - -MYBOOL __WINAPI LP_readhandle(lprec **lp, FILE *filename, int verbose, char *lp_name); -lprec __EXPORT_TYPE * __WINAPI read_lp(FILE *filename, int verbose, char *lp_name); -lprec __EXPORT_TYPE * __WINAPI read_LP(char *filename, int verbose, char *lp_name); -/* Old-style lp format file parser */ - -MYBOOL __EXPORT_TYPE __WINAPI write_basis(lprec *lp, char *filename); -MYBOOL __EXPORT_TYPE __WINAPI read_basis(lprec *lp, char *filename, char *info); -/* Read and write basis from/to file in CPLEX BAS format */ - -MYBOOL __EXPORT_TYPE __WINAPI write_params(lprec *lp, char *filename, char *options); -MYBOOL __EXPORT_TYPE __WINAPI read_params(lprec *lp, char *filename, char *options); -void __EXPORT_TYPE __WINAPI reset_params(lprec *lp); -/* Read and write parameter file */ - -void __EXPORT_TYPE __WINAPI print_lp(lprec *lp); -void __EXPORT_TYPE __WINAPI print_tableau(lprec *lp); -/* Print the current problem, only useful in very small (test) problems */ - -void __EXPORT_TYPE __WINAPI print_objective(lprec *lp); -void __EXPORT_TYPE __WINAPI print_solution(lprec *lp, int columns); -void __EXPORT_TYPE __WINAPI print_constraints(lprec *lp, int columns); -/* Print the solution to stdout */ - -void __EXPORT_TYPE __WINAPI print_duals(lprec *lp); -/* Print the dual variables of the solution */ - -void __EXPORT_TYPE __WINAPI print_scales(lprec *lp); -/* If scaling is used, print the scaling factors */ - -void __EXPORT_TYPE __WINAPI print_str(lprec *lp, char *str); - -void __EXPORT_TYPE __WINAPI set_outputstream(lprec *lp, FILE *stream); -MYBOOL __EXPORT_TYPE __WINAPI set_outputfile(lprec *lp, char *filename); - -void __EXPORT_TYPE __WINAPI set_verbose(lprec *lp, int verbose); -int __EXPORT_TYPE __WINAPI get_verbose(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_timeout(lprec *lp, long sectimeout); -long __EXPORT_TYPE __WINAPI get_timeout(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_print_sol(lprec *lp, int print_sol); -int __EXPORT_TYPE __WINAPI get_print_sol(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_debug(lprec *lp, MYBOOL debug); -MYBOOL __EXPORT_TYPE __WINAPI is_debug(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_trace(lprec *lp, MYBOOL trace); -MYBOOL __EXPORT_TYPE __WINAPI is_trace(lprec *lp); - -MYBOOL __EXPORT_TYPE __WINAPI print_debugdump(lprec *lp, char *filename); - -void __EXPORT_TYPE __WINAPI set_anti_degen(lprec *lp, int anti_degen); -int __EXPORT_TYPE __WINAPI get_anti_degen(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI is_anti_degen(lprec *lp, int testmask); - -void __EXPORT_TYPE __WINAPI set_presolve(lprec *lp, int presolvemode, int maxloops); -int __EXPORT_TYPE __WINAPI get_presolve(lprec *lp); -int __EXPORT_TYPE __WINAPI get_presolveloops(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI is_presolve(lprec *lp, int testmask); - -int __EXPORT_TYPE __WINAPI get_orig_index(lprec *lp, int lp_index); -int __EXPORT_TYPE __WINAPI get_lp_index(lprec *lp, int orig_index); - -void __EXPORT_TYPE __WINAPI set_maxpivot(lprec *lp, int max_num_inv); -int __EXPORT_TYPE __WINAPI get_maxpivot(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_obj_bound(lprec *lp, LPSREAL obj_bound); -LPSREAL __EXPORT_TYPE __WINAPI get_obj_bound(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_mip_gap(lprec *lp, MYBOOL absolute, LPSREAL mip_gap); -LPSREAL __EXPORT_TYPE __WINAPI get_mip_gap(lprec *lp, MYBOOL absolute); - -void __EXPORT_TYPE __WINAPI set_bb_rule(lprec *lp, int bb_rule); -int __EXPORT_TYPE __WINAPI get_bb_rule(lprec *lp); - -MYBOOL __EXPORT_TYPE __WINAPI set_var_branch(lprec *lp, int colnr, int branch_mode); -int __EXPORT_TYPE __WINAPI get_var_branch(lprec *lp, int colnr); - -MYBOOL __EXPORT_TYPE __WINAPI is_infinite(lprec *lp, LPSREAL value); -void __EXPORT_TYPE __WINAPI set_infinite(lprec *lp, LPSREAL infinite); -LPSREAL __EXPORT_TYPE __WINAPI get_infinite(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_epsint(lprec *lp, LPSREAL epsint); -LPSREAL __EXPORT_TYPE __WINAPI get_epsint(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_epsb(lprec *lp, LPSREAL epsb); -LPSREAL __EXPORT_TYPE __WINAPI get_epsb(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_epsd(lprec *lp, LPSREAL epsd); -LPSREAL __EXPORT_TYPE __WINAPI get_epsd(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_epsel(lprec *lp, LPSREAL epsel); -LPSREAL __EXPORT_TYPE __WINAPI get_epsel(lprec *lp); - -MYBOOL __EXPORT_TYPE __WINAPI set_epslevel(lprec *lp, int epslevel); - -void __EXPORT_TYPE __WINAPI set_scaling(lprec *lp, int scalemode); -int __EXPORT_TYPE __WINAPI get_scaling(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI is_scalemode(lprec *lp, int testmask); -MYBOOL __EXPORT_TYPE __WINAPI is_scaletype(lprec *lp, int scaletype); -MYBOOL __EXPORT_TYPE __WINAPI is_integerscaling(lprec *lp); -void __EXPORT_TYPE __WINAPI set_scalelimit(lprec *lp, LPSREAL scalelimit); -LPSREAL __EXPORT_TYPE __WINAPI get_scalelimit(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_improve(lprec *lp, int improve); -int __EXPORT_TYPE __WINAPI get_improve(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_pivoting(lprec *lp, int piv_rule); -int __EXPORT_TYPE __WINAPI get_pivoting(lprec *lp); -MYBOOL __EXPORT_TYPE __WINAPI set_partialprice(lprec *lp, int blockcount, int *blockstart, MYBOOL isrow); -void __EXPORT_TYPE __WINAPI get_partialprice(lprec *lp, int *blockcount, int *blockstart, MYBOOL isrow); - -MYBOOL __EXPORT_TYPE __WINAPI set_multiprice(lprec *lp, int multiblockdiv); -int __EXPORT_TYPE __WINAPI get_multiprice(lprec *lp, MYBOOL getabssize); - -MYBOOL __EXPORT_TYPE __WINAPI is_use_names(lprec *lp, MYBOOL isrow); -void __EXPORT_TYPE __WINAPI set_use_names(lprec *lp, MYBOOL isrow, MYBOOL use_names); - -int __EXPORT_TYPE __WINAPI get_nameindex(lprec *lp, char *varname, MYBOOL isrow); - -MYBOOL __EXPORT_TYPE __WINAPI is_piv_mode(lprec *lp, int testmask); -MYBOOL __EXPORT_TYPE __WINAPI is_piv_rule(lprec *lp, int rule); - -void __EXPORT_TYPE __WINAPI set_break_at_first(lprec *lp, MYBOOL break_at_first); -MYBOOL __EXPORT_TYPE __WINAPI is_break_at_first(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_bb_floorfirst(lprec *lp, int bb_floorfirst); -int __EXPORT_TYPE __WINAPI get_bb_floorfirst(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_bb_depthlimit(lprec *lp, int bb_maxlevel); -int __EXPORT_TYPE __WINAPI get_bb_depthlimit(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_break_at_value(lprec *lp, LPSREAL break_at_value); -LPSREAL __EXPORT_TYPE __WINAPI get_break_at_value(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_negrange(lprec *lp, LPSREAL negrange); -LPSREAL __EXPORT_TYPE __WINAPI get_negrange(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_epsperturb(lprec *lp, LPSREAL epsperturb); -LPSREAL __EXPORT_TYPE __WINAPI get_epsperturb(lprec *lp); - -void __EXPORT_TYPE __WINAPI set_epspivot(lprec *lp, LPSREAL epspivot); -LPSREAL __EXPORT_TYPE __WINAPI get_epspivot(lprec *lp); - -int __EXPORT_TYPE __WINAPI get_max_level(lprec *lp); -COUNTER __EXPORT_TYPE __WINAPI get_total_nodes(lprec *lp); -COUNTER __EXPORT_TYPE __WINAPI get_total_iter(lprec *lp); - -LPSREAL __EXPORT_TYPE __WINAPI get_objective(lprec *lp); -LPSREAL __EXPORT_TYPE __WINAPI get_working_objective(lprec *lp); - -LPSREAL __EXPORT_TYPE __WINAPI get_var_primalresult(lprec *lp, int index); -LPSREAL __EXPORT_TYPE __WINAPI get_var_dualresult(lprec *lp, int index); - -MYBOOL __EXPORT_TYPE __WINAPI get_variables(lprec *lp, LPSREAL *var); -MYBOOL __EXPORT_TYPE __WINAPI get_ptr_variables(lprec *lp, LPSREAL **var); - -MYBOOL __EXPORT_TYPE __WINAPI get_constraints(lprec *lp, LPSREAL *constr); -MYBOOL __EXPORT_TYPE __WINAPI get_ptr_constraints(lprec *lp, LPSREAL **constr); - -MYBOOL __EXPORT_TYPE __WINAPI get_sensitivity_rhs(lprec *lp, LPSREAL *duals, LPSREAL *dualsfrom, LPSREAL *dualstill); -MYBOOL __EXPORT_TYPE __WINAPI get_ptr_sensitivity_rhs(lprec *lp, LPSREAL **duals, LPSREAL **dualsfrom, LPSREAL **dualstill); - -MYBOOL __EXPORT_TYPE __WINAPI get_sensitivity_obj(lprec *lp, LPSREAL *objfrom, LPSREAL *objtill); -MYBOOL __EXPORT_TYPE __WINAPI get_sensitivity_objex(lprec *lp, LPSREAL *objfrom, LPSREAL *objtill, LPSREAL *objfromvalue, LPSREAL *objtillvalue); -MYBOOL __EXPORT_TYPE __WINAPI get_ptr_sensitivity_obj(lprec *lp, LPSREAL **objfrom, LPSREAL **objtill); -MYBOOL __EXPORT_TYPE __WINAPI get_ptr_sensitivity_objex(lprec *lp, LPSREAL **objfrom, LPSREAL **objtill, LPSREAL **objfromvalue, LPSREAL **objtillvalue); - -void __EXPORT_TYPE __WINAPI set_solutionlimit(lprec *lp, int limit); -int __EXPORT_TYPE __WINAPI get_solutionlimit(lprec *lp); -int __EXPORT_TYPE __WINAPI get_solutioncount(lprec *lp); - -int __EXPORT_TYPE __WINAPI get_Norig_rows(lprec *lp); -int __EXPORT_TYPE __WINAPI get_Nrows(lprec *lp); -int __EXPORT_TYPE __WINAPI get_Lrows(lprec *lp); - -int __EXPORT_TYPE __WINAPI get_Norig_columns(lprec *lp); -int __EXPORT_TYPE __WINAPI get_Ncolumns(lprec *lp); - -typedef int (__WINAPI read_modeldata_func)(void *userhandle, char *buf, int max_size); -typedef int (__WINAPI write_modeldata_func)(void *userhandle, char *buf); -MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func read_modeldata, int typeMPS, int options); - -/* #if defined develop */ -lprec __EXPORT_TYPE * __WINAPI read_lpex(void *userhandle, read_modeldata_func read_modeldata, int verbose, char *lp_name); -MYBOOL __EXPORT_TYPE __WINAPI write_lpex(lprec *lp, void *userhandle, write_modeldata_func write_modeldata); - -lprec __EXPORT_TYPE * __WINAPI read_mpsex(void *userhandle, read_modeldata_func read_modeldata, int options); -lprec __EXPORT_TYPE * __WINAPI read_freempsex(void *userhandle, read_modeldata_func read_modeldata, int options); - -MYBOOL __EXPORT_TYPE __WINAPI MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_modeldata_func write_modeldata); -/* #endif */ - -#ifdef __cplusplus -} -#endif - - -/* Forward definitions of functions used internaly by the lp toolkit */ -MYBOOL set_callbacks(lprec *lp); -STATIC int yieldformessages(lprec *lp); -MYBOOL __WINAPI userabort(lprec *lp, int message); -/*char * __VACALL explain(lprec *lp, char *format, ...); -void __VACALL report(lprec *lp, int level, char *format, ...);*/ - -/* Memory management routines */ -STATIC MYBOOL append_rows(lprec *lp, int deltarows); -STATIC MYBOOL append_columns(lprec *lp, int deltacolumns); -STATIC void inc_rows(lprec *lp, int delta); -STATIC void inc_columns(lprec *lp, int delta); -STATIC MYBOOL init_rowcol_names(lprec *lp); -STATIC MYBOOL inc_row_space(lprec *lp, int deltarows); -STATIC MYBOOL inc_col_space(lprec *lp, int deltacols); -STATIC MYBOOL shift_rowcoldata(lprec *lp, int base, int delta, LLrec *usedmap, MYBOOL isrow); -STATIC MYBOOL shift_basis(lprec *lp, int base, int delta, LLrec *usedmap, MYBOOL isrow); -STATIC MYBOOL shift_rowdata(lprec *lp, int base, int delta, LLrec *usedmap); -STATIC MYBOOL shift_coldata(lprec *lp, int base, int delta, LLrec *usedmap); - -/* INLINE */ MYBOOL is_chsign(lprec *lp, int rownr); - -STATIC MYBOOL inc_lag_space(lprec *lp, int deltarows, MYBOOL ignoreMAT); -lprec *make_lag(lprec *server); - -LPSREAL get_rh_upper(lprec *lp, int rownr); -LPSREAL get_rh_lower(lprec *lp, int rownr); -MYBOOL set_rh_upper(lprec *lp, int rownr, LPSREAL value); -MYBOOL set_rh_lower(lprec *lp, int rownr, LPSREAL value); -STATIC int bin_count(lprec *lp, MYBOOL working); -STATIC int MIP_count(lprec *lp); -STATIC int SOS_count(lprec *lp); -STATIC int GUB_count(lprec *lp); -STATIC int identify_GUB(lprec *lp, MYBOOL mark); -STATIC int prepare_GUB(lprec *lp); - -STATIC MYBOOL refactRecent(lprec *lp); -STATIC MYBOOL check_if_less(lprec *lp, LPSREAL x, LPSREAL y, int variable); -STATIC MYBOOL feasiblePhase1(lprec *lp, LPSREAL epsvalue); -STATIC void free_duals(lprec *lp); -STATIC void initialize_solution(lprec *lp, MYBOOL shiftbounds); -STATIC void recompute_solution(lprec *lp, MYBOOL shiftbounds); -STATIC int verify_solution(lprec *lp, MYBOOL reinvert, char *info); -STATIC int check_solution(lprec *lp, int lastcolumn, LPSREAL *solution, - LPSREAL *upbo, LPSREAL *lowbo, LPSREAL tolerance); -/* INLINE */ MYBOOL is_fixedvar(lprec *lp, int variable); -/* INLINE */ MYBOOL is_splitvar(lprec *lp, int colnr); - -void __WINAPI set_action(int *actionvar, int actionmask); -void __WINAPI clear_action(int *actionvar, int actionmask); -MYBOOL __WINAPI is_action(int actionvar, int testmask); - -/* INLINE */ MYBOOL is_bb_rule(lprec *lp, int bb_rule); -/* INLINE */ MYBOOL is_bb_mode(lprec *lp, int bb_mask); -/* INLINE */ int get_piv_rule(lprec *lp); -STATIC char *get_str_piv_rule(int rule); -STATIC MYBOOL __WINAPI set_var_priority(lprec *lp); -STATIC int find_sc_bbvar(lprec *lp, int *count); -STATIC int find_sos_bbvar(lprec *lp, int *count, MYBOOL intsos); -STATIC int find_int_bbvar(lprec *lp, int *count, BBrec *BB, MYBOOL *isfeasible); - -/* Solution-related functions */ -STATIC LPSREAL compute_dualslacks(lprec *lp, int target, LPSREAL **dvalues, int **nzdvalues, MYBOOL dosum); -STATIC MYBOOL solution_is_int(lprec *lp, int index, MYBOOL checkfixed); -STATIC MYBOOL bb_better(lprec *lp, int target, int mode); -STATIC void construct_solution(lprec *lp, LPSREAL *target); -STATIC void transfer_solution_var(lprec *lp, int uservar); -STATIC MYBOOL construct_duals(lprec *lp); -STATIC MYBOOL construct_sensitivity_duals(lprec *lp); -STATIC MYBOOL construct_sensitivity_obj(lprec *lp); - -STATIC int add_GUB(lprec *lp, char *name, int priority, int count, int *sosvars); -STATIC basisrec *push_basis(lprec *lp, int *basisvar, MYBOOL *isbasic, MYBOOL *islower); -STATIC MYBOOL compare_basis(lprec *lp); -STATIC MYBOOL restore_basis(lprec *lp); -STATIC MYBOOL pop_basis(lprec *lp, MYBOOL restore); -STATIC MYBOOL is_BasisReady(lprec *lp); -STATIC MYBOOL is_slackbasis(lprec *lp); -STATIC MYBOOL verify_basis(lprec *lp); -STATIC int unload_basis(lprec *lp, MYBOOL restorelast); - -STATIC int perturb_bounds(lprec *lp, BBrec *perturbed, MYBOOL doRows, MYBOOL doCols, MYBOOL includeFIXED); -STATIC MYBOOL validate_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo); -STATIC MYBOOL impose_bounds(lprec *lp, LPSREAL * upbo, LPSREAL *lowbo); -STATIC int unload_BB(lprec *lp); - -STATIC LPSREAL feasibilityOffset(lprec *lp, MYBOOL isdual); -STATIC MYBOOL isP1extra(lprec *lp); -STATIC LPSREAL get_refactfrequency(lprec *lp, MYBOOL final); -STATIC int findBasicFixedvar(lprec *lp, int afternr, MYBOOL slacksonly); -STATIC MYBOOL isBasisVarFeasible(lprec *lp, LPSREAL tol, int basis_row); -STATIC MYBOOL isPrimalFeasible(lprec *lp, LPSREAL tol, int infeasibles[], LPSREAL *feasibilitygap); -STATIC MYBOOL isDualFeasible(lprec *lp, LPSREAL tol, int *boundflips, int infeasibles[], LPSREAL *feasibilitygap); - -/* Main simplex driver routines */ -STATIC int preprocess(lprec *lp); -STATIC void postprocess(lprec *lp); -STATIC MYBOOL performiteration(lprec *lp, int rownr, int varin, LREAL theta, MYBOOL primal, MYBOOL allowminit, LPSREAL *prow, int *nzprow, LPSREAL *pcol, int *nzpcol, int *boundswaps); -STATIC void transfer_solution_var(lprec *lp, int uservar); -STATIC void transfer_solution(lprec *lp, MYBOOL dofinal); - -/* Scaling utilities */ -STATIC LPSREAL scaled_floor(lprec *lp, int colnr, LPSREAL value, LPSREAL epsscale); -STATIC LPSREAL scaled_ceil(lprec *lp, int colnr, LPSREAL value, LPSREAL epsscale); - -/* Variable mapping utility routines */ -STATIC void varmap_lock(lprec *lp); -STATIC void varmap_clear(lprec *lp); -STATIC MYBOOL varmap_canunlock(lprec *lp); -STATIC void varmap_addconstraint(lprec *lp); -STATIC void varmap_addcolumn(lprec *lp); -STATIC void varmap_delete(lprec *lp, int base, int delta, LLrec *varmap); -STATIC void varmap_compact(lprec *lp, int prev_rows, int prev_cols); -STATIC MYBOOL varmap_validate(lprec *lp, int varno); -/* STATIC MYBOOL del_varnameex(lprec *lp, hashelem **namelist, hashtable *ht, int varnr, LLrec *varmap); */ - STATIC MYBOOL del_varnameex(lprec *lp, hashelem **namelist, int items, hashtable *ht, int varnr, LLrec *varmap); - -/* Pseudo-cost routines (internal) */ -STATIC BBPSrec *init_pseudocost(lprec *lp, int pseudotype); -STATIC void free_pseudocost(lprec *lp); -STATIC LPSREAL get_pseudorange(BBPSrec *pc, int mipvar, int varcode); -STATIC void update_pseudocost(BBPSrec *pc, int mipvar, int varcode, MYBOOL capupper, LPSREAL varsol); -STATIC LPSREAL get_pseudobranchcost(BBPSrec *pc, int mipvar, MYBOOL dofloor); -STATIC LPSREAL get_pseudonodecost(BBPSrec *pc, int mipvar, int vartype, LPSREAL varsol); - -/* Matrix access and equation solving routines */ -STATIC void set_OF_override(lprec *lp, LPSREAL *ofVector); -STATIC void set_OF_p1extra(lprec *lp, LPSREAL p1extra); -STATIC void unset_OF_p1extra(lprec *lp); -MYBOOL modifyOF1(lprec *lp, int index, LPSREAL *ofValue, LPSREAL mult); -LPSREAL __WINAPI get_OF_active(lprec *lp, int varnr, LPSREAL mult); -STATIC MYBOOL is_OF_nz(lprec *lp, int colnr); - -STATIC int get_basisOF(lprec *lp, int coltarget[], LPSREAL crow[], int colno[]); -int __WINAPI get_basiscolumn(lprec *lp, int j, int rn[], double bj[]); -int __WINAPI obtain_column(lprec *lp, int varin, LPSREAL *pcol, int *nzlist, int *maxabs); -STATIC int compute_theta(lprec *lp, int rownr, LREAL *theta, int isupbound, LPSREAL HarrisScalar, MYBOOL primal); - -/* Pivot utility routines */ -STATIC int findBasisPos(lprec *lp, int notint, int *var_basic); -STATIC MYBOOL check_degeneracy(lprec *lp, LPSREAL *pcol, int *degencount); - -#endif /* HEADER_lp_lib */ diff --git a/src/lpsolve/headers/include/lp_matrix.h b/src/lpsolve/headers/include/lp_matrix.h deleted file mode 100644 index a8a05f91..00000000 --- a/src/lpsolve/headers/include/lp_matrix.h +++ /dev/null @@ -1,257 +0,0 @@ -#ifndef HEADER_lp_matrix -#define HEADER_lp_matrix - -#include "lp_types.h" -#include "lp_utils.h" - - -/* Sparse matrix element (ordered columnwise) */ -typedef struct _MATitem -{ - int rownr; - int colnr; - LPSREAL value; -} MATitem; - -/* Constants for matrix product rounding options */ -#define MAT_ROUNDNONE 0 -#define MAT_ROUNDABS 1 -#define MAT_ROUNDREL 2 -#define MAT_ROUNDABSREL (MAT_ROUNDABS + MAT_ROUNDREL) -#define MAT_ROUNDRC 4 -#define MAT_ROUNDRCMIN 1.0 /* lp->epspivot */ -#if 1 - #define MAT_ROUNDDEFAULT MAT_ROUNDREL /* Typically increases performance */ -#else - #define MAT_ROUNDDEFAULT MAT_ROUNDABS /* Probably gives more precision */ -#endif - -/* Compiler option development features */ -/*#define DebugInv*/ /* Report array values at factorization/inversion */ -#define NoLoopUnroll /* Do not do loop unrolling */ -#define DirectArrayOF /* Reference lp->obj[] array instead of function call */ - - -/* Matrix column access macros to be able to easily change storage model */ -#define CAM_Record 0 -#define CAM_Vector 1 -#if 0 - #define MatrixColAccess CAM_Record -#else - #define MatrixColAccess CAM_Vector -#endif - -#if MatrixColAccess==CAM_Record -#define SET_MAT_ijA(item,i,j,A) mat->col_mat[item].rownr = i; \ - mat->col_mat[item].colnr = j; \ - mat->col_mat[item].value = A -#define COL_MAT_COLNR(item) (mat->col_mat[item].colnr) -#define COL_MAT_ROWNR(item) (mat->col_mat[item].rownr) -#define COL_MAT_VALUE(item) (mat->col_mat[item].value) -#define COL_MAT_COPY(left,right) mat->col_mat[left] = mat->col_mat[right] -#define COL_MAT_MOVE(to,from,rec) MEMMOVE(&(mat->col_mat[to]),&(mat->col_mat[from]),rec) -#define COL_MAT2_COLNR(item) (mat2->col_mat[item].colnr) -#define COL_MAT2_ROWNR(item) (mat2->col_mat[item].rownr) -#define COL_MAT2_VALUE(item) (mat2->col_mat[item].value) -#define matRowColStep (sizeof(MATitem)/sizeof(int)) -#define matValueStep (sizeof(MATitem)/sizeof(LPSREAL)) - -#else /* if MatrixColAccess==CAM_Vector */ -#define SET_MAT_ijA(item,i,j,A) mat->col_mat_rownr[item] = i; \ - mat->col_mat_colnr[item] = j; \ - mat->col_mat_value[item] = A -#define COL_MAT_COLNR(item) (mat->col_mat_colnr[item]) -#define COL_MAT_ROWNR(item) (mat->col_mat_rownr[item]) -#define COL_MAT_VALUE(item) (mat->col_mat_value[item]) -#define COL_MAT_COPY(left,right) COL_MAT_COLNR(left) = COL_MAT_COLNR(right); \ - COL_MAT_ROWNR(left) = COL_MAT_ROWNR(right); \ - COL_MAT_VALUE(left) = COL_MAT_VALUE(right) -#define COL_MAT_MOVE(to,from,rec) MEMMOVE(&COL_MAT_COLNR(to),&COL_MAT_COLNR(from),rec); \ - MEMMOVE(&COL_MAT_ROWNR(to),&COL_MAT_ROWNR(from),rec); \ - MEMMOVE(&COL_MAT_VALUE(to),&COL_MAT_VALUE(from),rec) -#define COL_MAT2_COLNR(item) (mat2->col_mat_colnr[item]) -#define COL_MAT2_ROWNR(item) (mat2->col_mat_rownr[item]) -#define COL_MAT2_VALUE(item) (mat2->col_mat_value[item]) -#define matRowColStep 1 -#define matValueStep 1 - -#endif - - -/* Matrix row access macros to be able to easily change storage model */ -#define RAM_Index 0 -#define RAM_FullCopy 1 -#define MatrixRowAccess RAM_Index - -#if MatrixRowAccess==RAM_Index -#define ROW_MAT_COLNR(item) COL_MAT_COLNR(mat->row_mat[item]) -#define ROW_MAT_ROWNR(item) COL_MAT_ROWNR(mat->row_mat[item]) -#define ROW_MAT_VALUE(item) COL_MAT_VALUE(mat->row_mat[item]) - -#elif MatrixColAccess==CAM_Record -#define ROW_MAT_COLNR(item) (mat->row_mat[item].colnr) -#define ROW_MAT_ROWNR(item) (mat->row_mat[item].rownr) -#define ROW_MAT_VALUE(item) (mat->row_mat[item].value) - -#else /* if MatrixColAccess==CAM_Vector */ -#define ROW_MAT_COLNR(item) (mat->row_mat_colnr[item]) -#define ROW_MAT_ROWNR(item) (mat->row_mat_rownr[item]) -#define ROW_MAT_VALUE(item) (mat->row_mat_value[item]) - -#endif - - -typedef struct _MATrec -{ - /* Owner reference */ - lprec *lp; - - /* Active dimensions */ - int rows; - int columns; - - /* Allocated memory */ - int rows_alloc; - int columns_alloc; - int mat_alloc; /* The allocated size for matrix sized structures */ - - /* Sparse problem matrix storage */ -#if MatrixColAccess==CAM_Record - MATitem *col_mat; /* mat_alloc : The sparse data storage */ -#else /*MatrixColAccess==CAM_Vector*/ - int *col_mat_colnr; - int *col_mat_rownr; - LPSREAL *col_mat_value; -#endif - int *col_end; /* columns_alloc+1 : col_end[i] is the index of the - first element after column i; column[i] is stored - in elements col_end[i-1] to col_end[i]-1 */ - int *col_tag; /* user-definable tag associated with each column */ - -#if MatrixRowAccess==RAM_Index - int *row_mat; /* mat_alloc : From index 0, row_mat contains the - row-ordered index of the elements of col_mat */ -#elif MatrixColAccess==CAM_Record - MATitem *row_mat; /* mat_alloc : From index 0, row_mat contains the - row-ordered copy of the elements in col_mat */ -#else /*if MatrixColAccess==CAM_Vector*/ - int *row_mat_colnr; - int *row_mat_rownr; - LPSREAL *row_mat_value; -#endif - int *row_end; /* rows_alloc+1 : row_end[i] is the index of the - first element in row_mat after row i */ - int *row_tag; /* user-definable tag associated with each row */ - - LPSREAL *colmax; /* Array of maximum values of each column */ - LPSREAL *rowmax; /* Array of maximum values of each row */ - - LPSREAL epsvalue; /* Zero element rejection threshold */ - LPSREAL infnorm; /* The largest absolute value in the matrix */ - LPSREAL dynrange; - MYBOOL row_end_valid; /* TRUE if row_end & row_mat are valid */ - MYBOOL is_roworder; /* TRUE if the current (temporary) matrix order is row-wise */ - -} MATrec; - -typedef struct _DeltaVrec -{ - lprec *lp; - int activelevel; - MATrec *tracker; -} DeltaVrec; - - -#ifdef __cplusplus -__EXTERN_C { -#endif - -/* Sparse matrix routines */ -STATIC MATrec *mat_create(lprec *lp, int rows, int columns, LPSREAL epsvalue); -STATIC MYBOOL mat_memopt(MATrec *mat, int rowextra, int colextra, int nzextra); -STATIC void mat_free(MATrec **matrix); -STATIC MYBOOL inc_matrow_space(MATrec *mat, int deltarows); -STATIC int mat_mapreplace(MATrec *mat, LLrec *rowmap, LLrec *colmap, MATrec *insmat); -STATIC int mat_matinsert(MATrec *mat, MATrec *insmat); -STATIC int mat_zerocompact(MATrec *mat); -STATIC int mat_rowcompact(MATrec *mat, MYBOOL dozeros); -STATIC int mat_colcompact(MATrec *mat, int prev_rows, int prev_cols); -STATIC MYBOOL inc_matcol_space(MATrec *mat, int deltacols); -STATIC MYBOOL inc_mat_space(MATrec *mat, int mindelta); -STATIC int mat_shiftrows(MATrec *mat, int *bbase, int delta, LLrec *varmap); -STATIC int mat_shiftcols(MATrec *mat, int *bbase, int delta, LLrec *varmap); -STATIC MATrec *mat_extractmat(MATrec *mat, LLrec *rowmap, LLrec *colmap, MYBOOL negated); -STATIC int mat_appendrow(MATrec *mat, int count, LPSREAL *row, int *colno, LPSREAL mult, MYBOOL checkrowmode); -STATIC int mat_appendcol(MATrec *mat, int count, LPSREAL *column, int *rowno, LPSREAL mult, MYBOOL checkrowmode); -MYBOOL mat_get_data(lprec *lp, int matindex, MYBOOL isrow, int **rownr, int **colnr, LPSREAL **value); -MYBOOL mat_set_rowmap(MATrec *mat, int row_mat_index, int rownr, int colnr, int col_mat_index); -STATIC MYBOOL mat_indexrange(MATrec *mat, int index, MYBOOL isrow, int *startpos, int *endpos); -STATIC MYBOOL mat_validate(MATrec *mat); -STATIC MYBOOL mat_equalRows(MATrec *mat, int baserow, int comprow); -STATIC int mat_findelm(MATrec *mat, int row, int column); -STATIC int mat_findins(MATrec *mat, int row, int column, int *insertpos, MYBOOL validate); -STATIC void mat_multcol(MATrec *mat, int col_nr, LPSREAL mult, MYBOOL DoObj); -STATIC LPSREAL mat_getitem(MATrec *mat, int row, int column); -STATIC MYBOOL mat_setitem(MATrec *mat, int row, int column, LPSREAL value); -STATIC MYBOOL mat_additem(MATrec *mat, int row, int column, LPSREAL delta); -STATIC MYBOOL mat_setvalue(MATrec *mat, int Row, int Column, LPSREAL Value, MYBOOL doscale); -STATIC int mat_nonzeros(MATrec *mat); -STATIC int mat_collength(MATrec *mat, int colnr); -STATIC int mat_rowlength(MATrec *mat, int rownr); -STATIC void mat_multrow(MATrec *mat, int row_nr, LPSREAL mult); -STATIC void mat_multadd(MATrec *mat, LPSREAL *lhsvector, int varnr, LPSREAL mult); -STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, LPSREAL *row, int *colno, MYBOOL doscale, MYBOOL checkrowmode); -STATIC MYBOOL mat_setcol(MATrec *mat, int colno, int count, LPSREAL *column, int *rowno, MYBOOL doscale, MYBOOL checkrowmode); -STATIC MYBOOL mat_mergemat(MATrec *target, MATrec *source, MYBOOL usecolmap); -STATIC int mat_checkcounts(MATrec *mat, int *rownum, int *colnum, MYBOOL freeonexit); -STATIC int mat_expandcolumn(MATrec *mat, int colnr, LPSREAL *column, int *nzlist, MYBOOL signedA); -STATIC MYBOOL mat_computemax(MATrec *mat); -STATIC MYBOOL mat_transpose(MATrec *mat); - -/* Refactorization and recomputation routine */ -MYBOOL __WINAPI invert(lprec *lp, MYBOOL shiftbounds, MYBOOL final); - -/* Vector compression and expansion routines */ -STATIC MYBOOL vec_compress(LPSREAL *densevector, int startpos, int endpos, LPSREAL epsilon, LPSREAL *nzvector, int *nzindex); -STATIC MYBOOL vec_expand(LPSREAL *nzvector, int *nzindex, LPSREAL *densevector, int startpos, int endpos); - -/* Sparse matrix products */ -STATIC MYBOOL get_colIndexA(lprec *lp, int varset, int *colindex, MYBOOL append); -STATIC int prod_Ax(lprec *lp, int *coltarget, LPSREAL *input, int *nzinput, LPSREAL roundzero, LPSREAL ofscalar, LPSREAL *output, int *nzoutput, int roundmode); -STATIC int prod_xA(lprec *lp, int *coltarget, LPSREAL *input, int *nzinput, LPSREAL roundzero, LPSREAL ofscalar, LPSREAL *output, int *nzoutput, int roundmode); -STATIC MYBOOL prod_xA2(lprec *lp, int *coltarget, LPSREAL *prow, LPSREAL proundzero, int *pnzprow, - LPSREAL *drow, LPSREAL droundzero, int *dnzdrow, LPSREAL ofscalar, int roundmode); - -/* Equation solution */ -STATIC MYBOOL fimprove(lprec *lp, LPSREAL *pcol, int *nzidx, LPSREAL roundzero); -STATIC void ftran(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero); -STATIC MYBOOL bimprove(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero); -STATIC void btran(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero); - -/* Combined equation solution and matrix product for simplex operations */ -STATIC MYBOOL fsolve(lprec *lp, int varin, LPSREAL *pcol, int *nzidx, LPSREAL roundzero, LPSREAL ofscalar, MYBOOL prepareupdate); -STATIC MYBOOL bsolve(lprec *lp, int row_nr, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero, LPSREAL ofscalar); -STATIC void bsolve_xA2(lprec *lp, int* coltarget, - int row_nr1, LPSREAL *vector1, LPSREAL roundzero1, int *nzvector1, - int row_nr2, LPSREAL *vector2, LPSREAL roundzero2, int *nzvector2, int roundmode); - -/* Change-tracking routines (primarily for B&B and presolve) */ -STATIC DeltaVrec *createUndoLadder(lprec *lp, int levelitems, int maxlevels); -STATIC int incrementUndoLadder(DeltaVrec *DV); -STATIC MYBOOL modifyUndoLadder(DeltaVrec *DV, int itemno, LPSREAL target[], LPSREAL newvalue); -STATIC int countsUndoLadder(DeltaVrec *DV); -STATIC int restoreUndoLadder(DeltaVrec *DV, LPSREAL target[]); -STATIC int decrementUndoLadder(DeltaVrec *DV); -STATIC MYBOOL freeUndoLadder(DeltaVrec **DV); - -/* Specialized presolve undo functions */ -STATIC MYBOOL appendUndoPresolve(lprec *lp, MYBOOL isprimal, LPSREAL beta, int colnrDep); -STATIC MYBOOL addUndoPresolve(lprec *lp, MYBOOL isprimal, int colnrElim, LPSREAL alpha, LPSREAL beta, int colnrDep); - - -#ifdef __cplusplus -} -#endif - -#endif /* HEADER_lp_matrix */ - diff --git a/src/lpsolve/headers/include/lp_mipbb.h b/src/lpsolve/headers/include/lp_mipbb.h deleted file mode 100644 index 7741ddbc..00000000 --- a/src/lpsolve/headers/include/lp_mipbb.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef HEADER_lp_mipbb -#define HEADER_lp_mipbb - -#include "lp_types.h" -#include "lp_utils.h" - - -/* Bounds storage for B&B routines */ -typedef struct _BBrec -{ - struct _BBrec *parent; - struct _BBrec *child; - lprec *lp; - int varno; - int vartype; - int lastvarcus; /* Count of non-int variables of the previous branch */ - int lastrcf; - int nodesleft; - int nodessolved; - int nodestatus; - LPSREAL noderesult; - LPSREAL lastsolution; /* Optimal solution of the previous branch */ - LPSREAL sc_bound; - LPSREAL *upbo, *lowbo; - LPSREAL UPbound, LObound; - int UBtrack, LBtrack; /* Signals that incoming bounds were changed */ - MYBOOL contentmode; /* Flag indicating if we "own" the bound vectors */ - MYBOOL sc_canset; - MYBOOL isSOS; - MYBOOL isGUB; - int *varmanaged; /* Extended list of variables managed by this B&B level */ - MYBOOL isfloor; /* State variable indicating the active B&B bound */ - MYBOOL UBzerobased; /* State variable indicating if bounds have been rebased */ -} BBrec; - -#ifdef __cplusplus -extern "C" { -#endif - -STATIC BBrec *create_BB(lprec *lp, BBrec *parentBB, MYBOOL dofullcopy); -STATIC BBrec *push_BB(lprec *lp, BBrec *parentBB, int varno, int vartype, int varcus); -STATIC MYBOOL initbranches_BB(BBrec *BB); -STATIC MYBOOL fillbranches_BB(BBrec *BB); -STATIC MYBOOL nextbranch_BB(BBrec *BB); -STATIC MYBOOL strongbranch_BB(lprec *lp, BBrec *BB, int varno, int vartype, int varcus); -STATIC MYBOOL initcuts_BB(lprec *lp); -STATIC int updatecuts_BB(lprec *lp); -STATIC MYBOOL freecuts_BB(lprec *lp); -STATIC BBrec *findself_BB(BBrec *BB); -STATIC int solve_LP(lprec *lp, BBrec *BB); -STATIC int rcfbound_BB(BBrec *BB, int varno, MYBOOL isINT, LPSREAL *newbound, MYBOOL *isfeasible); -STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus); -STATIC int solve_BB(BBrec *BB); -STATIC MYBOOL free_BB(BBrec **BB); -STATIC BBrec *pop_BB(BBrec *BB); - -STATIC int run_BB(lprec *lp); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_mipbb */ - diff --git a/src/lpsolve/headers/include/lp_presolve.h b/src/lpsolve/headers/include/lp_presolve.h deleted file mode 100644 index 417c5f31..00000000 --- a/src/lpsolve/headers/include/lp_presolve.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef HEADER_lp_presolve -#define HEADER_lp_presolve - -#include "lp_types.h" -#include "lp_matrix.h" - -/* -------------------------------------------------------------------------------------------- */ -/* Defines for various presolve options */ -/* -------------------------------------------------------------------------------------------- */ - -#define MAX_PSMERGELOOPS 2 /* Max loops to merge compatible constraints */ -#define MAX_PSLINDEPLOOPS 1 /* Max loops to detect linearly dependendent constraints */ -#define MAX_PSBOUNDTIGHTENLOOPS 5 /* Maximumn number of loops to allow bound tightenings */ -#define MIN_SOS1LENGTH 4 /* Minimum length of a constraint for conversion to SOS1 */ -#if 1 - #define PRESOLVE_EPSVALUE (0.1*lp->epsprimal) -#else - #define PRESOLVE_EPSVALUE lp->epsvalue -#endif -#define PRESOLVE_EPSPIVOT 1.0e-3 /* Looses robustness at values smaller than ~1.0e-3 */ -#define PRESOLVE_BOUNDSLACK 10 /* Extra error recovery/tolerance margin */ - -#define DoPresolveRounding /* Use absolute and directed rounding (disable at own risk) */ -/*#define DoPresolveRelativeTest*/ - -/*#define PresolveForceUpdateMax*/ - -/*#define DualFeasibilityLogicEQ2*/ /* Add low-order feasibility/accuracy logic to elimEQ2 */ -#define DivisorIntegralityLogicEQ2 /* Always prefer integer divisors */ -#define FindImpliedEqualities /* Detect equalities (default is enabled) */ -#define Eq2Reldiff - -/*#define SavePresolveEliminated */ /* Enable to activate storage of eliminated matrix data */ -/*#define UseDualPresolve */ /* Enable to use full dual information for presolve */ - - -typedef struct _psrec -{ - LLrec *varmap; - int **next; - int *empty; - int *plucount; - int *negcount; - int *pluneg; - int *infcount; - LPSREAL *plulower; - LPSREAL *neglower; - LPSREAL *pluupper; - LPSREAL *negupper; - int allocsize; -} psrec; - -typedef struct _presolverec -{ - psrec *rows; - psrec *cols; - LLrec *EQmap; - LLrec *LTmap; - LLrec *INTmap; - LPSREAL *pv_upbo; - LPSREAL *pv_lobo; - LPSREAL *dv_upbo; - LPSREAL *dv_lobo; - lprec *lp; - LPSREAL epsvalue; - LPSREAL epspivot; - int innerloops; - int middleloops; - int outerloops; - int nzdeleted; - MYBOOL forceupdate; -} presolverec; - -#ifdef __cplusplus -extern "C" { -#endif - -/* Put function headers here */ - -STATIC MYBOOL presolve_createUndo(lprec *lp); -STATIC MYBOOL presolve_rebuildUndo(lprec *lp, MYBOOL isprimal); -STATIC MYBOOL inc_presolve_space(lprec *lp, int delta, MYBOOL isrows); -STATIC MYBOOL presolve_setOrig(lprec *lp, int orig_rows, int orig_cols); -STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, LPSREAL newvalue, MYBOOL remove, int *tally); -STATIC MYBOOL presolve_fillUndo(lprec *lp, int orig_rows, int orig_cols, MYBOOL setOrig); -STATIC MYBOOL presolve_freeUndo(lprec *lp); - -STATIC MYBOOL presolve_updatesums(presolverec *psdata); - -int presolve_nextrow(presolverec *psdata, int colnr, int *previtem); -int presolve_nextcol(presolverec *psdata, int rownr, int *previtem); - -STATIC presolverec *presolve_init(lprec *lp); -STATIC void presolve_free(presolverec **psdata); -STATIC int presolve_shrink(presolverec *psdata, int *nConRemove, int *nVarRemove); -STATIC void presolve_rowremove(presolverec *psdata, int rownr, MYBOOL allowcoldelete); -STATIC int presolve_colremove(presolverec *psdata, int colnr, MYBOOL allowrowdelete); - -STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, LPSREAL *fixValue, int *status); - -int presolve_rowlength(presolverec *psdata, int rownr); -int presolve_collength(presolverec *psdata, int colnr); - -STATIC int presolve(lprec *lp); -STATIC MYBOOL postsolve(lprec *lp, int status); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_presolve */ diff --git a/src/lpsolve/headers/include/lp_price.h b/src/lpsolve/headers/include/lp_price.h deleted file mode 100644 index f16a699f..00000000 --- a/src/lpsolve/headers/include/lp_price.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef HEADER_lp_price -#define HEADER_lp_price - -/* Local defines */ -/* ------------------------------------------------------------------------- */ -#define UseSortOnBound_Improve -/*#define UseSortOnBound_Substitute*/ - -#if 0 /* Stricter feasibility-preserving tolerance; use w/ *_UseRejectionList */ - #define UseRelativeFeasibility /* Use machine-precision and A-scale data */ -#endif -#if 0 /* Stricter pivot-selection criteria; use w/ *UseRejectionList */ - #define UseRelativePivot_Primal /* In rowprim based on A-scale data */ - #define UseRelativePivot_Dual /* In coldual based on A-scale data */ -#endif - - -/* Include required library headers */ -/* ------------------------------------------------------------------------- */ -#include "lp_types.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Comparison and validity routines */ -int CMP_CALLMODEL compareImprovementVar(const pricerec *current, const pricerec *candidate); -int CMP_CALLMODEL compareSubstitutionVar(const pricerec *current, const pricerec *candidate); -int CMP_CALLMODEL compareBoundFlipVar(const pricerec *current, const pricerec *candidate); -STATIC int addCandidateVar(pricerec *candidate, multirec *multi, findCompare_func findCompare, MYBOOL allowSortedExpand); -STATIC MYBOOL collectMinorVar(pricerec *candidate, multirec *longsteps, MYBOOL isphase2, MYBOOL isbatch); -STATIC MYBOOL validImprovementVar(pricerec *candidate); -STATIC MYBOOL validSubstitutionVar(pricerec *candidate); - -/* Row+column selection routines */ -STATIC MYBOOL findImprovementVar(pricerec *current, pricerec *candidate, MYBOOL collectMP, int *candidatecount); -STATIC MYBOOL findSubstitutionVar(pricerec *current, pricerec *candidate, int *candidatecount); -LPSREAL normalizeEdge(lprec *lp, int item, LPSREAL edge, MYBOOL isdual); -STATIC void makePriceLoop(lprec *lp, int *start, int *end, int *delta); - -/* Computation of reduced costs */ -STATIC void update_reducedcosts(lprec *lp, MYBOOL isdual, int leave_nr, int enter_nr, LPSREAL *prow, LPSREAL *drow); -STATIC void compute_reducedcosts(lprec *lp, MYBOOL isdual, int row_nr, int *coltarget, MYBOOL dosolve, - LPSREAL *prow, int *nzprow, - LPSREAL *drow, int *nzdrow, - int roundmode); - -/* Leaving variable selection and entering column pricing loops */ -STATIC int find_rowReplacement(lprec *lp, int rownr, LPSREAL *prow, int *nzprow); -STATIC int colprim(lprec *lp, LPSREAL *drow, int *nzdrow, - MYBOOL skipupdate, int partialloop, int *candidatecount, MYBOOL updateinfeas, LPSREAL *xviol); -STATIC int rowprim(lprec *lp, int colnr, LREAL *theta, LPSREAL *pcol, int *nzpcol, MYBOOL forceoutEQ, LPSREAL *xviol); -STATIC int rowdual(lprec *lp, LPSREAL *rhvec, MYBOOL forceoutEQ, MYBOOL updateinfeas, LPSREAL *xviol); -STATIC int coldual(lprec *lp, int row_nr, - LPSREAL *prow, int *nzprow, LPSREAL *drow, int *nzdrow, - MYBOOL dualphase1, MYBOOL skipupdate, - int *candidatecount, LPSREAL *xviol); - -/* Partial pricing management routines */ -STATIC partialrec *partial_createBlocks(lprec *lp, MYBOOL isrow); -STATIC int partial_countBlocks(lprec *lp, MYBOOL isrow); -STATIC int partial_activeBlocks(lprec *lp, MYBOOL isrow); -STATIC void partial_freeBlocks(partialrec **blockdata); - -/* Partial pricing utility routines */ -STATIC int partial_findBlocks(lprec *lp, MYBOOL autodefine, MYBOOL isrow); -STATIC int partial_blockStart(lprec *lp, MYBOOL isrow); -STATIC int partial_blockEnd(lprec *lp, MYBOOL isrow); -STATIC int partial_blockNextPos(lprec *lp, int block, MYBOOL isrow); - -STATIC MYBOOL partial_blockStep(lprec *lp, MYBOOL isrow); -STATIC MYBOOL partial_isVarActive(lprec *lp, int varno, MYBOOL isrow); - -/* Multiple pricing / dual long step management routines */ -STATIC multirec *multi_create(lprec *lp, MYBOOL truncinf); -STATIC MYBOOL multi_resize(multirec *multi, int blocksize, int blockdiv, MYBOOL doVlist, MYBOOL doIset); -STATIC int multi_restart(multirec *multi); -STATIC int multi_size(multirec *multi); -STATIC int multi_used(multirec *multi); -STATIC MYBOOL multi_truncatingvar(multirec *multi, int varnr); -STATIC MYBOOL multi_mustupdate(multirec *multi); -STATIC void multi_valueInit(multirec *multi, LPSREAL step_base, LPSREAL obj_base); -STATIC LPSREAL *multi_valueList(multirec *multi); -STATIC int *multi_indexSet(multirec *multi, MYBOOL regenerate); -STATIC int multi_getvar(multirec *multi, int item); -STATIC MYBOOL multi_recompute(multirec *multi, int index, MYBOOL isphase2, MYBOOL fullupdate); -STATIC MYBOOL multi_removevar(multirec *multi, int varnr); -STATIC int multi_enteringvar(multirec *multi, pricerec *current, int priority); -STATIC LPSREAL multi_enteringtheta(multirec *multi); -STATIC void multi_free(multirec **multi); -STATIC int multi_populateSet(multirec *multi, int **list, int excludenr); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_price */ - diff --git a/src/lpsolve/headers/include/lp_pricePSE.h b/src/lpsolve/headers/include/lp_pricePSE.h deleted file mode 100644 index e8a31782..00000000 --- a/src/lpsolve/headers/include/lp_pricePSE.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef HEADER_lp_pricePSE -#define HEADER_lp_pricePSE - -#include "lp_types.h" - -#define ApplySteepestEdgeMinimum - -#ifdef __cplusplus -extern "C" { -#endif - -/* Price norm management routines */ -STATIC MYBOOL initPricer(lprec *lp); -MYBOOL applyPricer(lprec *lp); -STATIC void simplexPricer(lprec *lp, MYBOOL isdual); -STATIC void freePricer(lprec *lp); -STATIC MYBOOL resizePricer(lprec *lp); -STATIC LPSREAL getPricer(lprec *lp, int item, MYBOOL isdual); -STATIC MYBOOL restartPricer(lprec *lp, MYBOOL isdual); -STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSREAL *prow, int *nzprow); -STATIC MYBOOL verifyPricer(lprec *lp); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_pricePSE */ - diff --git a/src/lpsolve/headers/include/lp_report.h b/src/lpsolve/headers/include/lp_report.h deleted file mode 100644 index bdb08ba8..00000000 --- a/src/lpsolve/headers/include/lp_report.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef HEADER_lp_report -#define HEADER_lp_report - -#ifdef __cplusplus -extern "C" { -#endif - -/* General information functions */ -char * __VACALL explain(lprec *lp, char *format, ...); -void __VACALL report(lprec *lp, int level, char *format, ...); - -/* Prototypes for debugging and general data dumps */ -void debug_print(lprec *lp, char *format, ...); -void debug_print_solution(lprec *lp); -void debug_print_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo); -void blockWriteLREAL(FILE *output, char *label, LREAL *vector, int first, int last); -void blockWriteAMAT(FILE *output, const char *label, lprec* lp, int first, int last); -void blockWriteBMAT(FILE *output, const char *label, lprec* lp, int first, int last); - - -/* Model reporting headers */ -void REPORT_objective(lprec *lp); -void REPORT_solution(lprec *lp, int columns); -void REPORT_constraints(lprec *lp, int columns); -void REPORT_duals(lprec *lp); -void REPORT_extended(lprec *lp); - -/* Other rarely used, but sometimes extremely useful reports */ -void REPORT_constraintinfo(lprec *lp, char *datainfo); -void REPORT_modelinfo(lprec *lp, MYBOOL doName, char *datainfo); -void REPORT_lp(lprec *lp); -MYBOOL REPORT_tableau(lprec *lp); -void REPORT_scales(lprec *lp); -MYBOOL REPORT_debugdump(lprec *lp, char *filename, MYBOOL livedata); -MYBOOL REPORT_mat_mmsave(lprec *lp, char *filename, int *colndx, MYBOOL includeOF, char *infotext); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_report */ - diff --git a/src/lpsolve/headers/include/lp_rlp.h b/src/lpsolve/headers/include/lp_rlp.h deleted file mode 100644 index 76c74658..00000000 --- a/src/lpsolve/headers/include/lp_rlp.h +++ /dev/null @@ -1,2453 +0,0 @@ - - - -#define YY_INT_ALIGNED short int - -/* A lexical scanner generated by flex */ - -#define FLEX_SCANNER -#define YY_FLEX_MAJOR_VERSION 2 -#define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 35 -#if YY_FLEX_SUBMINOR_VERSION > 0 -#define FLEX_BETA -#endif - -/* First, we deal with platform-specific or compiler-specific issues. */ - -/* begin standard C headers. */ -#include -#include -#include -#include - -/* end standard C headers. */ - -/* flex integer type definitions */ - -#ifndef FLEXINT_H -#define FLEXINT_H - -/* C99 systems have . Non-C99 systems may or may not. */ - -#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - -/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, - * if you want the limit (max/min) macros for int types. - */ -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS 1 -#endif - -#include -typedef int8_t flex_int8_t; -typedef uint8_t flex_uint8_t; -typedef int16_t flex_int16_t; -typedef uint16_t flex_uint16_t; -typedef int32_t flex_int32_t; -typedef uint32_t flex_uint32_t; -#else -typedef signed char flex_int8_t; -typedef short int flex_int16_t; -typedef int flex_int32_t; -typedef unsigned char flex_uint8_t; -typedef unsigned short int flex_uint16_t; -typedef unsigned int flex_uint32_t; -#endif /* ! C99 */ - -/* Limits of integral types. */ -#ifndef INT8_MIN -#define INT8_MIN (-128) -#endif -#ifndef INT16_MIN -#define INT16_MIN (-32767-1) -#endif -#ifndef INT32_MIN -#define INT32_MIN (-2147483647-1) -#endif -#ifndef INT8_MAX -#define INT8_MAX (127) -#endif -#ifndef INT16_MAX -#define INT16_MAX (32767) -#endif -#ifndef INT32_MAX -#define INT32_MAX (2147483647) -#endif -#ifndef UINT8_MAX -#define UINT8_MAX (255U) -#endif -#ifndef UINT16_MAX -#define UINT16_MAX (65535U) -#endif -#ifndef UINT32_MAX -#define UINT32_MAX (4294967295U) -#endif - -#endif /* ! FLEXINT_H */ - -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -/* C99 requires __STDC__ to be defined as 1. */ -#if defined (__STDC__) - -#define YY_USE_CONST - -#endif /* defined (__STDC__) */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST -#define lp_yyconst const -#else -#define lp_yyconst -#endif - -/* Returned upon end-of-file. */ -#define YY_NULL 0 - -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. - */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) - -/* An opaque pointer. */ -#ifndef YY_TYPEDEF_YY_SCANNER_T -#define YY_TYPEDEF_YY_SCANNER_T -typedef void* lp_yyscan_t; -#endif - -/* For convenience, these vars (plus the bison vars far below) - are macros in the reentrant scanner. */ -#define lp_yyin lp_yyg->lp_yyin_r -#define lp_yyout lp_yyg->lp_yyout_r -#define lp_yyextra lp_yyg->lp_yyextra_r -#define lp_yyleng lp_yyg->lp_yyleng_r -#define lp_yytext lp_yyg->lp_yytext_r -#define lp_yylineno (YY_CURRENT_BUFFER_LVALUE->lp_yy_bs_lineno) -#define lp_yycolumn (YY_CURRENT_BUFFER_LVALUE->lp_yy_bs_column) -#define lp_yy_flex_debug lp_yyg->lp_yy_flex_debug_r - -/* Enter a start condition. This macro really ought to take a parameter, - * but we do it the disgusting crufty way forced on us by the ()-less - * definition of BEGIN. - */ -#define BEGIN lp_yyg->lp_yy_start = 1 + 2 * - -/* Translate the current start state into a value that can be later handed - * to BEGIN to return to the state. The YYSTATE alias is for lex - * compatibility. - */ -#define YY_START ((lp_yyg->lp_yy_start - 1) / 2) -#define YYSTATE YY_START - -/* Action number for EOF rule of a given start state. */ -#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - -/* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE lp_yyrestart(lp_yyin ,lp_yyscanner ) - -#define YY_END_OF_BUFFER_CHAR 0 - -/* Size of default input buffer. */ -#ifndef YY_BUF_SIZE -#define YY_BUF_SIZE 16384 -#endif - -/* The state buf must be large enough to hold one state per character in the main buffer. - */ -#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(lp_yy_state_type)) - -#ifndef YY_TYPEDEF_YY_BUFFER_STATE -#define YY_TYPEDEF_YY_BUFFER_STATE -typedef struct lp_yy_buffer_state *YY_BUFFER_STATE; -#endif - -#define EOB_ACT_CONTINUE_SCAN 0 -#define EOB_ACT_END_OF_FILE 1 -#define EOB_ACT_LAST_MATCH 2 - - /* Note: We specifically omit the test for lp_yy_rule_can_match_eol because it requires - * access to the local variable lp_yy_act. Since lp_yyless() is a macro, it would break - * existing scanners that call lp_yyless() from OUTSIDE lp_yylex. - * One obvious solution it to make lp_yy_act a global. I tried that, and saw - * a 5% performance hit in a non-lp_yylineno scanner, because lp_yy_act is - * normally declared as a register variable-- so it is not worth it. - */ - #define YY_LESS_LINENO(n) \ - do { \ - int lp_yyl;\ - for ( lp_yyl = n; lp_yyl < lp_yyleng; ++lp_yyl )\ - if ( lp_yytext[lp_yyl] == '\n' )\ - --lp_yylineno;\ - }while(0) - -/* Return all but the first "n" matched characters back to the input stream. */ -#define lp_yyless(n) \ - do \ - { \ - /* Undo effects of setting up lp_yytext. */ \ - int lp_yyless_macro_arg = (n); \ - YY_LESS_LINENO(lp_yyless_macro_arg);\ - *lp_yy_cp = lp_yyg->lp_yy_hold_char; \ - YY_RESTORE_YY_MORE_OFFSET \ - lp_yyg->lp_yy_c_buf_p = lp_yy_cp = lp_yy_bp + lp_yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up lp_yytext again */ \ - } \ - while ( 0 ) - -#define unput(c) lp_yyunput( c, lp_yyg->lp_yytext_ptr , lp_yyscanner ) - -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef size_t lp_yy_size_t; -#endif - -#ifndef YY_STRUCT_YY_BUFFER_STATE -#define YY_STRUCT_YY_BUFFER_STATE -struct lp_yy_buffer_state - { - FILE *lp_yy_input_file; - - char *lp_yy_ch_buf; /* input buffer */ - char *lp_yy_buf_pos; /* current position in input buffer */ - - /* Size of input buffer in bytes, not including room for EOB - * characters. - */ - lp_yy_size_t lp_yy_buf_size; - - /* Number of characters read into lp_yy_ch_buf, not including EOB - * characters. - */ - int lp_yy_n_chars; - - /* Whether we "own" the buffer - i.e., we know we created it, - * and can realloc() it to grow it, and should free() it to - * delete it. - */ - int lp_yy_is_our_buffer; - - /* Whether this is an "interactive" input source; if so, and - * if we're using stdio for input, then we want to use getc() - * instead of fread(), to make sure we stop fetching input after - * each newline. - */ - int lp_yy_is_interactive; - - /* Whether we're considered to be at the beginning of a line. - * If so, '^' rules will be active on the next match, otherwise - * not. - */ - int lp_yy_at_bol; - - int lp_yy_bs_lineno; /**< The line count. */ - int lp_yy_bs_column; /**< The column count. */ - - /* Whether to try to fill the input buffer when we reach the - * end of it. - */ - int lp_yy_fill_buffer; - - int lp_yy_buffer_status; - -#define YY_BUFFER_NEW 0 -#define YY_BUFFER_NORMAL 1 - /* When an EOF's been seen but there's still some text to process - * then we mark the buffer as YY_EOF_PENDING, to indicate that we - * shouldn't try reading from the input source any more. We might - * still have a bunch of tokens to match, though, because of - * possible backing-up. - * - * When we actually see the EOF, we change the status to "new" - * (via lp_yyrestart()), so that the user can continue scanning by - * just pointing lp_yyin at a new input file. - */ -#define YY_BUFFER_EOF_PENDING 2 - - }; -#endif /* !YY_STRUCT_YY_BUFFER_STATE */ - -/* We provide macros for accessing buffer states in case in the - * future we want to put the buffer states in a more general - * "scanner state". - * - * Returns the top of the stack, or NULL. - */ -#define YY_CURRENT_BUFFER ( lp_yyg->lp_yy_buffer_stack \ - ? lp_yyg->lp_yy_buffer_stack[lp_yyg->lp_yy_buffer_stack_top] \ - : NULL) - -/* Same as previous macro, but useful when we know that the buffer stack is not - * NULL or when we need an lvalue. For internal use only. - */ -#define YY_CURRENT_BUFFER_LVALUE lp_yyg->lp_yy_buffer_stack[lp_yyg->lp_yy_buffer_stack_top] - -void lp_yyrestart (FILE *input_file ,lp_yyscan_t lp_yyscanner ); -void lp_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,lp_yyscan_t lp_yyscanner ); -YY_BUFFER_STATE lp_yy_create_buffer (FILE *file,int size ,lp_yyscan_t lp_yyscanner ); -void lp_yy_delete_buffer (YY_BUFFER_STATE b ,lp_yyscan_t lp_yyscanner ); -void lp_yy_flush_buffer (YY_BUFFER_STATE b ,lp_yyscan_t lp_yyscanner ); -void lp_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,lp_yyscan_t lp_yyscanner ); -void lp_yypop_buffer_state (lp_yyscan_t lp_yyscanner ); - -static void lp_yyensure_buffer_stack (lp_yyscan_t lp_yyscanner ); -static void lp_yy_load_buffer_state (lp_yyscan_t lp_yyscanner ); -static void lp_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,lp_yyscan_t lp_yyscanner ); - -#define YY_FLUSH_BUFFER lp_yy_flush_buffer(YY_CURRENT_BUFFER ,lp_yyscanner) - -YY_BUFFER_STATE lp_yy_scan_buffer (char *base,lp_yy_size_t size ,lp_yyscan_t lp_yyscanner ); -YY_BUFFER_STATE lp_yy_scan_string (lp_yyconst char *lp_yy_str ,lp_yyscan_t lp_yyscanner ); -YY_BUFFER_STATE lp_yy_scan_bytes (lp_yyconst char *bytes,int len ,lp_yyscan_t lp_yyscanner ); - -void *lp_yyalloc (lp_yy_size_t ,lp_yyscan_t lp_yyscanner ); -void *lp_yyrealloc (void *,lp_yy_size_t ,lp_yyscan_t lp_yyscanner ); -void lp_yyfree (void * ,lp_yyscan_t lp_yyscanner ); - -#define lp_yy_new_buffer lp_yy_create_buffer - -#define lp_yy_set_interactive(is_interactive) \ - { \ - if ( ! YY_CURRENT_BUFFER ){ \ - lp_yyensure_buffer_stack (lp_yyscanner); \ - YY_CURRENT_BUFFER_LVALUE = \ - lp_yy_create_buffer(lp_yyin,YY_BUF_SIZE ,lp_yyscanner); \ - } \ - YY_CURRENT_BUFFER_LVALUE->lp_yy_is_interactive = is_interactive; \ - } - -#define lp_yy_set_bol(at_bol) \ - { \ - if ( ! YY_CURRENT_BUFFER ){\ - lp_yyensure_buffer_stack (lp_yyscanner); \ - YY_CURRENT_BUFFER_LVALUE = \ - lp_yy_create_buffer(lp_yyin,YY_BUF_SIZE ,lp_yyscanner); \ - } \ - YY_CURRENT_BUFFER_LVALUE->lp_yy_at_bol = at_bol; \ - } - -#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->lp_yy_at_bol) - -/* Begin user sect3 */ - -#define lp_yywrap(n) 1 -#define YY_SKIP_YYWRAP - -typedef unsigned char YY_CHAR; - -typedef int lp_yy_state_type; - -#define lp_yytext_ptr lp_yytext_r - -static lp_yy_state_type lp_yy_get_previous_state (lp_yyscan_t lp_yyscanner ); -static lp_yy_state_type lp_yy_try_NUL_trans (lp_yy_state_type current_state ,lp_yyscan_t lp_yyscanner); -static int lp_yy_get_next_buffer (lp_yyscan_t lp_yyscanner ); -static void lp_yy_fatal_error (lp_yyconst char msg[] ,lp_yyscan_t lp_yyscanner ); - -/* Done after the current pattern has been matched and before the - * corresponding action - sets up lp_yytext. - */ -#define YY_DO_BEFORE_ACTION \ - lp_yyg->lp_yytext_ptr = lp_yy_bp; \ - lp_yyleng = (size_t) (lp_yy_cp - lp_yy_bp); \ - lp_yyg->lp_yy_hold_char = *lp_yy_cp; \ - *lp_yy_cp = '\0'; \ - lp_yyg->lp_yy_c_buf_p = lp_yy_cp; - -#define YY_NUM_RULES 33 -#define YY_END_OF_BUFFER 34 -/* This struct is not used in this scanner, - but its presence is necessary. */ -struct lp_yy_trans_info - { - flex_int32_t lp_yy_verify; - flex_int32_t lp_yy_nxt; - }; -static lp_yyconst flex_int16_t lp_yy_accept[144] = - { 0, - 0, 0, 0, 0, 0, 0, 34, 32, 10, 10, - 27, 17, 11, 32, 32, 14, 26, 31, 29, 28, - 30, 25, 25, 10, 25, 25, 25, 25, 3, 4, - 3, 3, 9, 7, 8, 10, 17, 17, 0, 15, - 1, 6, 15, 14, 0, 29, 30, 0, 25, 24, - 0, 25, 25, 10, 0, 0, 0, 0, 25, 25, - 25, 25, 25, 2, 0, 15, 0, 15, 22, 0, - 25, 25, 0, 0, 0, 0, 0, 19, 25, 18, - 20, 25, 25, 21, 0, 25, 0, 13, 25, 0, - 12, 25, 19, 0, 18, 20, 21, 25, 23, 25, - - 20, 21, 21, 16, 16, 0, 25, 25, 0, 23, - 0, 21, 25, 25, 0, 0, 25, 25, 0, 0, - 19, 25, 0, 0, 25, 25, 19, 0, 18, 0, - 0, 25, 25, 18, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - } ; - -static lp_yyconst flex_int32_t lp_yy_ec[256] = - { 0, - 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, - 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 1, 1, 5, 6, 5, 5, 5, 1, - 1, 7, 8, 9, 10, 11, 12, 13, 14, 14, - 13, 13, 13, 13, 13, 13, 13, 15, 16, 17, - 18, 19, 1, 5, 20, 21, 22, 23, 24, 25, - 26, 23, 27, 23, 23, 23, 28, 29, 30, 23, - 23, 31, 32, 33, 34, 23, 23, 35, 36, 37, - 5, 1, 5, 5, 5, 1, 20, 21, 22, 23, - - 24, 25, 26, 23, 27, 23, 23, 23, 28, 29, - 30, 23, 23, 31, 32, 33, 34, 23, 23, 35, - 36, 37, 5, 1, 5, 5, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1 - } ; - -static lp_yyconst flex_int32_t lp_yy_meta[38] = - { 0, - 1, 2, 3, 3, 4, 5, 6, 3, 6, 3, - 5, 5, 5, 5, 7, 6, 7, 6, 6, 4, - 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4 - } ; - -static lp_yyconst flex_int16_t lp_yy_base[150] = - { 0, - 0, 36, 36, 38, 43, 45, 366, 388, 48, 62, - 388, 338, 388, 40, 48, 60, 388, 388, 346, 388, - 326, 60, 65, 91, 81, 74, 85, 102, 388, 388, - 388, 330, 388, 388, 388, 125, 313, 134, 308, 96, - 388, 388, 117, 132, 139, 388, 388, 88, 146, 320, - 0, 149, 152, 0, 307, 301, 294, 83, 153, 156, - 157, 189, 160, 388, 286, 126, 65, 108, 388, 289, - 181, 185, 272, 273, 250, 249, 220, 199, 203, 208, - 192, 211, 219, 227, 243, 109, 163, 225, 202, 174, - 224, 215, 213, 207, 191, 388, 189, 227, 228, 231, - - 244, 240, 253, 276, 388, 170, 260, 262, 166, 388, - 169, 179, 263, 241, 166, 159, 270, 272, 149, 155, - 284, 288, 130, 124, 296, 303, 388, 103, 300, 96, - 45, 324, 328, 388, 82, 311, 79, 68, 54, 56, - 25, 12, 388, 345, 353, 360, 367, 372, 379 - } ; - -static lp_yyconst flex_int16_t lp_yy_def[150] = - { 0, - 143, 1, 144, 144, 145, 145, 143, 143, 143, 143, - 143, 146, 143, 143, 143, 143, 143, 143, 143, 143, - 143, 147, 147, 143, 147, 147, 147, 147, 143, 143, - 143, 143, 143, 143, 143, 143, 146, 143, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 147, 143, - 148, 147, 147, 24, 143, 143, 143, 143, 147, 147, - 147, 147, 147, 143, 143, 143, 143, 143, 143, 148, - 147, 147, 143, 143, 143, 143, 143, 147, 147, 147, - 147, 147, 147, 147, 149, 143, 143, 143, 62, 143, - 143, 62, 143, 143, 143, 143, 143, 62, 62, 62, - - 62, 62, 62, 143, 143, 143, 62, 62, 143, 143, - 143, 143, 62, 62, 143, 143, 62, 62, 143, 143, - 62, 62, 143, 143, 62, 62, 143, 143, 62, 143, - 143, 147, 147, 143, 143, 149, 143, 143, 143, 143, - 143, 143, 0, 143, 143, 143, 143, 143, 143 - } ; - -static lp_yyconst flex_int16_t lp_yy_nxt[426] = - { 0, - 8, 9, 10, 9, 8, 8, 11, 12, 13, 12, - 14, 15, 16, 16, 17, 18, 19, 20, 21, 22, - 22, 22, 22, 22, 22, 22, 22, 23, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 24, 30, 31, - 30, 31, 32, 96, 32, 34, 35, 34, 35, 36, - 36, 36, 40, 40, 41, 37, 25, 37, 142, 42, - 26, 48, 27, 36, 36, 36, 48, 28, 136, 37, - 43, 37, 44, 44, 50, 48, 51, 68, 68, 50, - 136, 51, 48, 45, 52, 141, 48, 140, 50, 48, - 51, 53, 54, 36, 36, 50, 139, 51, 37, 50, - - 37, 51, 50, 48, 60, 138, 76, 59, 40, 40, - 48, 55, 77, 61, 137, 56, 50, 57, 51, 45, - 68, 68, 58, 50, 135, 62, 36, 36, 36, 66, - 66, 63, 37, 134, 37, 38, 38, 38, 66, 66, - 45, 38, 43, 38, 44, 44, 67, 48, 67, 45, - 48, 68, 68, 48, 48, 45, 131, 48, 48, 130, - 50, 48, 51, 50, 87, 51, 50, 50, 51, 51, - 50, 50, 51, 51, 50, 90, 51, 88, 128, 79, - 72, 78, 87, 71, 127, 124, 90, 123, 91, 80, - 48, 84, 112, 48, 120, 88, 119, 51, 116, 91, - - 48, 51, 112, 50, 48, 51, 50, 89, 51, 48, - 81, 92, 48, 50, 111, 51, 82, 50, 98, 51, - 48, 83, 50, 49, 51, 50, 99, 51, 48, 107, - 110, 100, 109, 50, 49, 51, 49, 101, 69, 69, - 103, 50, 108, 51, 104, 104, 104, 49, 49, 49, - 102, 97, 49, 115, 49, 49, 114, 113, 49, 49, - 49, 49, 49, 49, 122, 49, 103, 49, 49, 106, - 96, 49, 49, 49, 49, 81, 49, 104, 104, 104, - 49, 49, 95, 49, 49, 49, 117, 49, 118, 49, - 49, 49, 49, 49, 49, 49, 94, 49, 121, 49, - - 93, 125, 49, 126, 49, 49, 125, 86, 126, 49, - 85, 49, 104, 104, 104, 49, 49, 49, 129, 132, - 49, 49, 75, 49, 49, 87, 133, 49, 49, 90, - 49, 74, 49, 73, 69, 49, 65, 143, 88, 39, - 51, 64, 91, 47, 51, 29, 29, 29, 29, 29, - 29, 29, 29, 33, 33, 33, 33, 33, 33, 33, - 33, 38, 38, 46, 39, 143, 143, 38, 49, 143, - 49, 49, 143, 49, 49, 70, 70, 143, 143, 70, - 105, 105, 143, 105, 105, 105, 105, 7, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - 143, 143, 143, 143, 143 - } ; - -static lp_yyconst flex_int16_t lp_yy_chk[426] = - { 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, - 4, 4, 3, 142, 4, 5, 5, 6, 6, 9, - 9, 9, 14, 14, 15, 9, 2, 9, 141, 15, - 2, 22, 2, 10, 10, 10, 23, 2, 131, 10, - 16, 10, 16, 16, 22, 26, 22, 67, 67, 23, - 131, 23, 25, 16, 23, 140, 27, 139, 26, 48, - 26, 23, 24, 24, 24, 25, 138, 25, 24, 27, - - 24, 27, 48, 28, 26, 137, 58, 25, 40, 40, - 86, 24, 58, 27, 135, 24, 28, 24, 28, 40, - 68, 68, 24, 86, 130, 28, 36, 36, 36, 43, - 43, 28, 36, 128, 36, 38, 38, 38, 66, 66, - 43, 38, 44, 38, 44, 44, 45, 49, 45, 66, - 52, 45, 45, 53, 59, 44, 124, 60, 61, 123, - 49, 63, 49, 52, 87, 52, 53, 59, 53, 59, - 60, 61, 60, 61, 63, 90, 63, 87, 120, 60, - 53, 59, 71, 52, 119, 116, 72, 115, 90, 61, - 62, 63, 112, 81, 111, 71, 109, 71, 106, 72, - - 78, 72, 97, 62, 79, 62, 81, 71, 81, 80, - 62, 72, 82, 78, 95, 78, 62, 79, 78, 79, - 83, 62, 80, 89, 80, 82, 79, 82, 84, 89, - 94, 80, 93, 83, 89, 83, 92, 82, 91, 88, - 84, 84, 92, 84, 85, 85, 85, 92, 98, 99, - 83, 77, 100, 101, 98, 99, 100, 98, 100, 98, - 99, 102, 114, 100, 114, 101, 103, 102, 114, 85, - 76, 101, 102, 114, 103, 101, 101, 104, 104, 104, - 103, 107, 75, 108, 113, 103, 107, 107, 108, 108, - 113, 117, 107, 118, 108, 113, 74, 117, 113, 118, - - 73, 117, 117, 118, 118, 121, 117, 70, 118, 122, - 65, 121, 136, 136, 136, 122, 121, 125, 122, 125, - 122, 129, 57, 125, 126, 132, 126, 129, 125, 133, - 126, 56, 129, 55, 50, 126, 39, 136, 132, 37, - 132, 32, 133, 21, 133, 144, 144, 144, 144, 144, - 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, - 145, 146, 146, 19, 12, 7, 0, 146, 147, 0, - 147, 147, 0, 147, 147, 148, 148, 0, 0, 148, - 149, 149, 0, 149, 149, 149, 149, 143, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - 143, 143, 143, 143, 143 - } ; - -/* Table of booleans, true if rule could match eol. */ -static lp_yyconst flex_int32_t lp_yy_rule_can_match_eol[34] = - { 0, -0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - -/* The intent behind this definition is that it'll catch - * any uses of REJECT which flex missed. - */ -#define REJECT reject_used_but_not_detected -#define lp_yymore() lp_yymore_used_but_not_detected -#define YY_MORE_ADJ 0 -#define YY_RESTORE_YY_MORE_OFFSET -/* - made reentrant with help of - http://www.usualcoding.eu/post/2007/09/03/Building-a-reentrant-parser-in-C-with-Flex/Bison -*/ -/* - Note that a minimum version of flex is needed to be able to compile this. - Older version don't know the reentrant code. - Version 2.5.4 is not enough. Probably at least v2.5.31 is needed. Tested with v2.5.35 -*/ -/* -** We want the scanner to be reentrant, therefore generate no global variables. -** That what the 'reentrant' option is for. -** 'bison-bridge' is used to create a bison compatible scanner and share lp_yylval -*/ - -#define INITIAL 0 -#define COMMENT 1 -#define LINECOMMENT 2 - -#ifndef YY_NO_UNISTD_H -/* Special case for "unistd.h", since it is non-ANSI. We include it way - * down here because we want the user's section 1 to have been scanned first. - * The user has a chance to override it with an option. - */ -#include -#endif - -#ifndef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#endif - -/* Holds the entire state of the reentrant scanner. */ -struct lp_yyguts_t - { - - /* User-defined. Not touched by flex. */ - YY_EXTRA_TYPE lp_yyextra_r; - - /* The rest are the same as the globals declared in the non-reentrant scanner. */ - FILE *lp_yyin_r, *lp_yyout_r; - size_t lp_yy_buffer_stack_top; /**< index of top of stack. */ - size_t lp_yy_buffer_stack_max; /**< capacity of stack. */ - YY_BUFFER_STATE * lp_yy_buffer_stack; /**< Stack as an array. */ - char lp_yy_hold_char; - int lp_yy_n_chars; - int lp_yyleng_r; - char *lp_yy_c_buf_p; - int lp_yy_init; - int lp_yy_start; - int lp_yy_did_buffer_switch_on_eof; - int lp_yy_start_stack_ptr; - int lp_yy_start_stack_depth; - int *lp_yy_start_stack; - lp_yy_state_type lp_yy_last_accepting_state; - char* lp_yy_last_accepting_cpos; - - int lp_yylineno_r; - int lp_yy_flex_debug_r; - - char *lp_yytext_r; - int lp_yy_more_flag; - int lp_yy_more_len; - - YYSTYPE * lp_yylval_r; - - }; /* end struct lp_yyguts_t */ - -static int lp_yy_init_globals (lp_yyscan_t lp_yyscanner ); - - /* This must go here because YYSTYPE and YYLTYPE are included - * from bison output in section 1.*/ - # define lp_yylval lp_yyg->lp_yylval_r - -int lp_yylex_init (lp_yyscan_t* scanner); - -int lp_yylex_init_extra (YY_EXTRA_TYPE user_defined,lp_yyscan_t* scanner); - -/* Accessor methods to globals. - These are made visible to non-reentrant scanners for convenience. */ - -int lp_yylex_destroy (lp_yyscan_t lp_yyscanner ); - -int lp_yyget_debug (lp_yyscan_t lp_yyscanner ); - -void lp_yyset_debug (int debug_flag ,lp_yyscan_t lp_yyscanner ); - -YY_EXTRA_TYPE lp_yyget_extra (lp_yyscan_t lp_yyscanner ); - -void lp_yyset_extra (YY_EXTRA_TYPE user_defined ,lp_yyscan_t lp_yyscanner ); - -FILE *lp_yyget_in (lp_yyscan_t lp_yyscanner ); - -void lp_yyset_in (FILE * in_str ,lp_yyscan_t lp_yyscanner ); - -FILE *lp_yyget_out (lp_yyscan_t lp_yyscanner ); - -void lp_yyset_out (FILE * out_str ,lp_yyscan_t lp_yyscanner ); - -int lp_yyget_leng (lp_yyscan_t lp_yyscanner ); - -char *lp_yyget_text (lp_yyscan_t lp_yyscanner ); - -int lp_yyget_lineno (lp_yyscan_t lp_yyscanner ); - -void lp_yyset_lineno (int line_number ,lp_yyscan_t lp_yyscanner ); - -YYSTYPE * lp_yyget_lval (lp_yyscan_t lp_yyscanner ); - -void lp_yyset_lval (YYSTYPE * lp_yylval_param ,lp_yyscan_t lp_yyscanner ); - -/* Macros after this point can all be overridden by user definitions in - * section 1. - */ - -#ifndef YY_SKIP_YYWRAP -#ifdef __cplusplus -extern "C" int lp_yywrap (lp_yyscan_t lp_yyscanner ); -#else -extern int lp_yywrap (lp_yyscan_t lp_yyscanner ); -#endif -#endif - - static void lp_yyunput (int c,char *buf_ptr ,lp_yyscan_t lp_yyscanner); - -#ifndef lp_yytext_ptr -static void lp_yy_flex_strncpy (char *,lp_yyconst char *,int ,lp_yyscan_t lp_yyscanner); -#endif - -#ifdef YY_NEED_STRLEN -static int lp_yy_flex_strlen (lp_yyconst char * ,lp_yyscan_t lp_yyscanner); -#endif - -#ifndef YY_NO_INPUT - -#ifdef __cplusplus -static int lp_yyinput (lp_yyscan_t lp_yyscanner ); -#else -static int input (lp_yyscan_t lp_yyscanner ); -#endif - -#endif - -/* Amount of stuff to slurp up with each read. */ -#ifndef YY_READ_BUF_SIZE -#define YY_READ_BUF_SIZE 8192 -#endif - -/* Copy whatever the last rule matched to the standard output. */ -#ifndef ECHO -/* This used to be an fputs(), but since the string might contain NUL's, - * we now use fwrite(). - */ -#define ECHO fwrite( lp_yytext, lp_yyleng, 1, lp_yyout ) -#endif - -/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, - * is returned in "result". - */ -#ifndef YY_INPUT -#define YY_INPUT(buf,result,max_size) \ - if ( YY_CURRENT_BUFFER_LVALUE->lp_yy_is_interactive ) \ - { \ - int c = '*'; \ - int n; \ - for ( n = 0; n < max_size && \ - (c = getc( lp_yyin )) != EOF && c != '\n'; ++n ) \ - buf[n] = (char) c; \ - if ( c == '\n' ) \ - buf[n++] = (char) c; \ - if ( c == EOF && ferror( lp_yyin ) ) \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - result = n; \ - } \ - else \ - { \ - errno=0; \ - while ( (result = fread(buf, 1, max_size, lp_yyin))==0 && ferror(lp_yyin)) \ - { \ - if( errno != EINTR) \ - { \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - break; \ - } \ - errno=0; \ - clearerr(lp_yyin); \ - } \ - }\ -\ - -#endif - -/* No semi-colon after return; correct usage is to write "lp_yyterminate();" - - * we don't want an extra ';' after the "return" because that will cause - * some compilers to complain about unreachable statements. - */ -#ifndef lp_yyterminate -#define lp_yyterminate() return YY_NULL -#endif - -/* Number of entries by which start-condition stack grows. */ -#ifndef YY_START_STACK_INCR -#define YY_START_STACK_INCR 25 -#endif - -/* Report a fatal error. */ -#ifndef YY_FATAL_ERROR -#define YY_FATAL_ERROR(msg) lp_yy_fatal_error( msg , lp_yyscanner) -#endif - -/* end tables serialization structures and prototypes */ - -/* Default declaration of generated scanner - a define so the user can - * easily add parameters. - */ -#ifndef YY_DECL -#define YY_DECL_IS_OURS 1 - -extern int lp_yylex \ - (YYSTYPE * lp_yylval_param ,lp_yyscan_t lp_yyscanner); - -#define YY_DECL int lp_yylex \ - (YYSTYPE * lp_yylval_param , lp_yyscan_t lp_yyscanner) -#endif /* !YY_DECL */ - -/* Code executed at the beginning of each rule, after lp_yytext and lp_yyleng - * have been set up. - */ -#ifndef YY_USER_ACTION -#define YY_USER_ACTION -#endif - -/* Code executed at the end of each rule. */ -#ifndef YY_BREAK -#define YY_BREAK break; -#endif - -#define YY_RULE_SETUP \ - if ( lp_yyleng > 0 ) \ - YY_CURRENT_BUFFER_LVALUE->lp_yy_at_bol = \ - (lp_yytext[lp_yyleng - 1] == '\n'); \ - YY_USER_ACTION - -/** The main scanner function which does all the work. - */ -YY_DECL -{ - register lp_yy_state_type lp_yy_current_state; - register char *lp_yy_cp, *lp_yy_bp; - register int lp_yy_act; - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - lp_yylval = lp_yylval_param; - - if ( !lp_yyg->lp_yy_init ) - { - lp_yyg->lp_yy_init = 1; - -#ifdef YY_USER_INIT - YY_USER_INIT; -#endif - - if ( ! lp_yyg->lp_yy_start ) - lp_yyg->lp_yy_start = 1; /* first start state */ - - if ( ! lp_yyin ) - lp_yyin = stdin; - - if ( ! lp_yyout ) - lp_yyout = stdout; - - if ( ! YY_CURRENT_BUFFER ) { - lp_yyensure_buffer_stack (lp_yyscanner); - YY_CURRENT_BUFFER_LVALUE = - lp_yy_create_buffer(lp_yyin,YY_BUF_SIZE ,lp_yyscanner); - } - - lp_yy_load_buffer_state(lp_yyscanner ); - } - - while ( 1 ) /* loops until end-of-file is reached */ - { - lp_yy_cp = lp_yyg->lp_yy_c_buf_p; - - /* Support of lp_yytext. */ - *lp_yy_cp = lp_yyg->lp_yy_hold_char; - - /* lp_yy_bp points to the position in lp_yy_ch_buf of the start of - * the current run. - */ - lp_yy_bp = lp_yy_cp; - - lp_yy_current_state = lp_yyg->lp_yy_start; - lp_yy_current_state += YY_AT_BOL(); -lp_yy_match: - do - { - register YY_CHAR lp_yy_c = lp_yy_ec[YY_SC_TO_UI(*lp_yy_cp)]; - if ( lp_yy_accept[lp_yy_current_state] ) - { - lp_yyg->lp_yy_last_accepting_state = lp_yy_current_state; - lp_yyg->lp_yy_last_accepting_cpos = lp_yy_cp; - } - while ( lp_yy_chk[lp_yy_base[lp_yy_current_state] + lp_yy_c] != lp_yy_current_state ) - { - lp_yy_current_state = (int) lp_yy_def[lp_yy_current_state]; - if ( lp_yy_current_state >= 144 ) - lp_yy_c = lp_yy_meta[(unsigned int) lp_yy_c]; - } - lp_yy_current_state = lp_yy_nxt[lp_yy_base[lp_yy_current_state] + (unsigned int) lp_yy_c]; - ++lp_yy_cp; - } - while ( lp_yy_base[lp_yy_current_state] != 388 ); - -lp_yy_find_action: - lp_yy_act = lp_yy_accept[lp_yy_current_state]; - if ( lp_yy_act == 0 ) - { /* have to back up */ - lp_yy_cp = lp_yyg->lp_yy_last_accepting_cpos; - lp_yy_current_state = lp_yyg->lp_yy_last_accepting_state; - lp_yy_act = lp_yy_accept[lp_yy_current_state]; - } - - YY_DO_BEFORE_ACTION; - - if ( lp_yy_act != YY_END_OF_BUFFER && lp_yy_rule_can_match_eol[lp_yy_act] ) - { - int lp_yyl; - for ( lp_yyl = 0; lp_yyl < lp_yyleng; ++lp_yyl ) - if ( lp_yytext[lp_yyl] == '\n' ) - - do{ lp_yylineno++; - lp_yycolumn=0; - }while(0) -; - } - -do_action: /* This label is used only to access EOF actions. */ - - switch ( lp_yy_act ) - { /* beginning of action switch */ - case 0: /* must back up */ - /* undo the effects of YY_DO_BEFORE_ACTION */ - *lp_yy_cp = lp_yyg->lp_yy_hold_char; - lp_yy_cp = lp_yyg->lp_yy_last_accepting_cpos; - lp_yy_current_state = lp_yyg->lp_yy_last_accepting_state; - goto lp_yy_find_action; - -case 1: -YY_RULE_SETUP -{ - BEGIN COMMENT; -} /* begin skip comment */ - YY_BREAK -case 2: -YY_RULE_SETUP -{ - BEGIN INITIAL; -} /* end skip comment */ - YY_BREAK -case 3: -YY_RULE_SETUP -{ -} - YY_BREAK -case 4: -/* rule 4 can match eol */ -YY_RULE_SETUP -{ -} - YY_BREAK -case 5: -YY_RULE_SETUP -{ -} - YY_BREAK -case 6: -YY_RULE_SETUP -{ - BEGIN LINECOMMENT; -} /* begin skip LINECOMMENT */ - YY_BREAK -case 7: -/* rule 7 can match eol */ -YY_RULE_SETUP -{ - BEGIN INITIAL; -} /* end skip LINECOMMENT */ - YY_BREAK -case 8: -YY_RULE_SETUP -{ - BEGIN INITIAL; -} /* end skip LINECOMMENT */ - YY_BREAK -case 9: -YY_RULE_SETUP -{ -} - YY_BREAK -case 10: -/* rule 10 can match eol */ -YY_RULE_SETUP -{ -} - YY_BREAK -case 11: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - - pp->lineno = lp_yylineno; - return(COMMA); -} - YY_BREAK -case 12: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - - pp->lineno = lp_yylineno; - return(MINIMISE); -} - YY_BREAK -case 13: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - - pp->lineno = lp_yylineno; - return(MAXIMISE); -} - YY_BREAK -case 14: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - pv->f = atof((char *)lp_yytext); - return(INTCONS); -} /* f contains the last float */ - YY_BREAK -case 15: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - pv->f = atof((char *)lp_yytext); - return(CONS); -} /* f contains the last float */ - YY_BREAK -case 16: -/* rule 16 can match eol */ -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - char *ptr, c; - - pp->lineno = lp_yylineno; - pv->f = DEF_INFINITE; - pv->Sign = 0; - ptr = (char *)lp_yytext; - while (isspace(*ptr)) ptr++; - if(*ptr == '-') - pv->Sign = 1; - if(lp_yyleng > 0) { - c = lp_yytext[lp_yyleng - 1]; - if(!isalnum(c)) - unput(c); - } - return(INF); -} /* f contains the last float */ - YY_BREAK -case 17: -/* rule 17 can match eol */ -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - int x; - - pp->lineno = lp_yylineno; - pv->Sign = 0; - for(x = 0; x < lp_yyleng; x++) - if(lp_yytext[x] == '-' || lp_yytext[x] == '+') - pv->Sign = (pv->Sign == (lp_yytext[x] == '+')); - return (TOK_SIGN); - /* Sign is TRUE if the sign-string - represents a '-'. Otherwise Sign - is FALSE */ -} - YY_BREAK -case 18: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - if((!pv->Within_int_decl) && (!pv->Within_sec_decl) && (!pv->Within_sos_decl) && (!pv->Within_free_decl)) { - pv->Within_int_decl = 1; - pv->Within_sos_decl1 = FALSE; - } - return(SEC_INT); -} - YY_BREAK -case 19: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - if((!pv->Within_int_decl) && (!pv->Within_sec_decl) && (!pv->Within_sos_decl) && (!pv->Within_free_decl)) { - pv->Within_int_decl = 2; - pv->Within_sos_decl1 = FALSE; - } - return(SEC_BIN); -} - YY_BREAK -case 20: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - if((!pv->Within_int_decl) && (!pv->Within_sec_decl) && (!pv->Within_sos_decl) && (!pv->Within_free_decl)) { - pv->Within_sec_decl = TRUE; - pv->Within_sos_decl1 = FALSE; - } - return(SEC_SEC); -} - YY_BREAK -case 21: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - if(!pv->Within_sos_decl) - pv->SOStype0 = (short)atoi(((char *)lp_yytext) + 3); - if((!pv->Within_int_decl) && (!pv->Within_sec_decl) && (!pv->Within_sos_decl) && (!pv->Within_free_decl)) - pv->Within_sos_decl = TRUE; - return(SEC_SOS); -} - YY_BREAK -case 22: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - FREE(pv->Last_var); - pv->Last_var = strdup((char *)lp_yytext); - pv->Last_var[strlen(pv->Last_var) - 2] = 0; - return(SOSDESCR); -} - YY_BREAK -case 23: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - if((!pv->Within_int_decl) && (!pv->Within_sec_decl) && (!pv->Within_sos_decl) && (!pv->Within_free_decl)) { - pv->Within_free_decl = TRUE; - pv->Within_sos_decl1 = FALSE; - } - return(SEC_FREE); -} - YY_BREAK -case 24: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - char *ptr; - - pp->lineno = lp_yylineno; - FREE(pv->Last_var); - pv->Last_var = strdup((char *)lp_yytext); - ptr = pv->Last_var + strlen(pv->Last_var); - ptr[-1] = ' '; - while ((--ptr >= pv->Last_var) && (isspace(*ptr))) - *ptr = 0; - return(VARIABLECOLON); -} - YY_BREAK -case 25: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - FREE(pv->Last_var); - pv->Last_var = strdup((char *)lp_yytext); - return(VAR); -} - YY_BREAK -case 26: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - - pp->lineno = lp_yylineno; - return (COLON); -} - YY_BREAK -case 27: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - - pp->lineno = lp_yylineno; - return(AR_M_OP); -} - YY_BREAK -case 28: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - pv->OP = *lp_yytext; - return(RE_OPEQ); -} - YY_BREAK -case 29: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - pv->OP = *lp_yytext; - return(RE_OPLE); -} - YY_BREAK -case 30: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - pv->OP = *lp_yytext; - return(RE_OPGE); -} - YY_BREAK -case 31: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - parse_vars *pv = (parse_vars *) pp->parse_vars; - - pp->lineno = lp_yylineno; - pv->Within_int_decl = pv->Within_sec_decl = pv->Within_sos_decl = pv->Within_free_decl = FALSE; - check_int_sec_sos_free_decl(pp, (int) pv->Within_int_decl, (int) pv->Within_sec_decl, (int) pv->Within_sos_decl, (int) pv->Within_free_decl); - return(END_C); -} - YY_BREAK -case 32: -YY_RULE_SETUP -{ - parse_parm *pp = PARM; - - pp->lineno = lp_yylineno; - report(NULL, CRITICAL, "LEX ERROR : %s lineno %d\n", lp_yytext, lp_yylineno); - return(UNDEFINED); -} - YY_BREAK -case 33: -YY_RULE_SETUP -ECHO; - YY_BREAK -case YY_STATE_EOF(INITIAL): -case YY_STATE_EOF(COMMENT): -case YY_STATE_EOF(LINECOMMENT): - lp_yyterminate(); - - case YY_END_OF_BUFFER: - { - /* Amount of text matched not including the EOB char. */ - int lp_yy_amount_of_matched_text = (int) (lp_yy_cp - lp_yyg->lp_yytext_ptr) - 1; - - /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *lp_yy_cp = lp_yyg->lp_yy_hold_char; - YY_RESTORE_YY_MORE_OFFSET - - if ( YY_CURRENT_BUFFER_LVALUE->lp_yy_buffer_status == YY_BUFFER_NEW ) - { - /* We're scanning a new file or input source. It's - * possible that this happened because the user - * just pointed lp_yyin at a new source and called - * lp_yylex(). If so, then we have to assure - * consistency between YY_CURRENT_BUFFER and our - * globals. Here is the right place to do so, because - * this is the first action (other than possibly a - * back-up) that will match for the new input source. - */ - lp_yyg->lp_yy_n_chars = YY_CURRENT_BUFFER_LVALUE->lp_yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->lp_yy_input_file = lp_yyin; - YY_CURRENT_BUFFER_LVALUE->lp_yy_buffer_status = YY_BUFFER_NORMAL; - } - - /* Note that here we test for lp_yy_c_buf_p "<=" to the position - * of the first EOB in the buffer, since lp_yy_c_buf_p will - * already have been incremented past the NUL character - * (since all states make transitions on EOB to the - * end-of-buffer state). Contrast this with the test - * in input(). - */ - if ( lp_yyg->lp_yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[lp_yyg->lp_yy_n_chars] ) - { /* This was really a NUL. */ - lp_yy_state_type lp_yy_next_state; - - lp_yyg->lp_yy_c_buf_p = lp_yyg->lp_yytext_ptr + lp_yy_amount_of_matched_text; - - lp_yy_current_state = lp_yy_get_previous_state( lp_yyscanner ); - - /* Okay, we're now positioned to make the NUL - * transition. We couldn't have - * lp_yy_get_previous_state() go ahead and do it - * for us because it doesn't know how to deal - * with the possibility of jamming (and we don't - * want to build jamming into it because then it - * will run more slowly). - */ - - lp_yy_next_state = lp_yy_try_NUL_trans( lp_yy_current_state , lp_yyscanner); - - lp_yy_bp = lp_yyg->lp_yytext_ptr + YY_MORE_ADJ; - - if ( lp_yy_next_state ) - { - /* Consume the NUL. */ - lp_yy_cp = ++lp_yyg->lp_yy_c_buf_p; - lp_yy_current_state = lp_yy_next_state; - goto lp_yy_match; - } - - else - { - lp_yy_cp = lp_yyg->lp_yy_c_buf_p; - goto lp_yy_find_action; - } - } - - else switch ( lp_yy_get_next_buffer( lp_yyscanner ) ) - { - case EOB_ACT_END_OF_FILE: - { - lp_yyg->lp_yy_did_buffer_switch_on_eof = 0; - - if ( lp_yywrap(lp_yyscanner ) ) - { - /* Note: because we've taken care in - * lp_yy_get_next_buffer() to have set up - * lp_yytext, we can now set up - * lp_yy_c_buf_p so that if some total - * hoser (like flex itself) wants to - * call the scanner after we return the - * YY_NULL, it'll still work - another - * YY_NULL will get returned. - */ - lp_yyg->lp_yy_c_buf_p = lp_yyg->lp_yytext_ptr + YY_MORE_ADJ; - - lp_yy_act = YY_STATE_EOF(YY_START); - goto do_action; - } - - else - { - if ( ! lp_yyg->lp_yy_did_buffer_switch_on_eof ) - YY_NEW_FILE; - } - break; - } - - case EOB_ACT_CONTINUE_SCAN: - lp_yyg->lp_yy_c_buf_p = - lp_yyg->lp_yytext_ptr + lp_yy_amount_of_matched_text; - - lp_yy_current_state = lp_yy_get_previous_state( lp_yyscanner ); - - lp_yy_cp = lp_yyg->lp_yy_c_buf_p; - lp_yy_bp = lp_yyg->lp_yytext_ptr + YY_MORE_ADJ; - goto lp_yy_match; - - case EOB_ACT_LAST_MATCH: - lp_yyg->lp_yy_c_buf_p = - &YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[lp_yyg->lp_yy_n_chars]; - - lp_yy_current_state = lp_yy_get_previous_state( lp_yyscanner ); - - lp_yy_cp = lp_yyg->lp_yy_c_buf_p; - lp_yy_bp = lp_yyg->lp_yytext_ptr + YY_MORE_ADJ; - goto lp_yy_find_action; - } - break; - } - - default: - YY_FATAL_ERROR( - "fatal flex scanner internal error--no action found" ); - } /* end of action switch */ - } /* end of scanning one token */ -} /* end of lp_yylex */ - -/* lp_yy_get_next_buffer - try to read in a new buffer - * - * Returns a code representing an action: - * EOB_ACT_LAST_MATCH - - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position - * EOB_ACT_END_OF_FILE - end of file - */ -static int lp_yy_get_next_buffer (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - register char *dest = YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf; - register char *source = lp_yyg->lp_yytext_ptr; - register int number_to_move, i; - int ret_val; - - if ( lp_yyg->lp_yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[lp_yyg->lp_yy_n_chars + 1] ) - YY_FATAL_ERROR( - "fatal flex scanner internal error--end of buffer missed" ); - - if ( YY_CURRENT_BUFFER_LVALUE->lp_yy_fill_buffer == 0 ) - { /* Don't try to fill the buffer, so this is an EOF. */ - if ( lp_yyg->lp_yy_c_buf_p - lp_yyg->lp_yytext_ptr - YY_MORE_ADJ == 1 ) - { - /* We matched a single character, the EOB, so - * treat this as a final EOF. - */ - return EOB_ACT_END_OF_FILE; - } - - else - { - /* We matched some text prior to the EOB, first - * process it. - */ - return EOB_ACT_LAST_MATCH; - } - } - - /* Try to read more data. */ - - /* First move last chars to start of buffer. */ - number_to_move = (int) (lp_yyg->lp_yy_c_buf_p - lp_yyg->lp_yytext_ptr) - 1; - - for ( i = 0; i < number_to_move; ++i ) - *(dest++) = *(source++); - - if ( YY_CURRENT_BUFFER_LVALUE->lp_yy_buffer_status == YY_BUFFER_EOF_PENDING ) - /* don't do the read, it's not guaranteed to return an EOF, - * just force an EOF - */ - YY_CURRENT_BUFFER_LVALUE->lp_yy_n_chars = lp_yyg->lp_yy_n_chars = 0; - - else - { - int num_to_read = - YY_CURRENT_BUFFER_LVALUE->lp_yy_buf_size - number_to_move - 1; - - while ( num_to_read <= 0 ) - { /* Not enough room in the buffer - grow it. */ - - /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; - - int lp_yy_c_buf_p_offset = - (int) (lp_yyg->lp_yy_c_buf_p - b->lp_yy_ch_buf); - - if ( b->lp_yy_is_our_buffer ) - { - int new_size = b->lp_yy_buf_size * 2; - - if ( new_size <= 0 ) - b->lp_yy_buf_size += b->lp_yy_buf_size / 8; - else - b->lp_yy_buf_size *= 2; - - b->lp_yy_ch_buf = (char *) - /* Include room in for 2 EOB chars. */ - lp_yyrealloc((void *) b->lp_yy_ch_buf,b->lp_yy_buf_size + 2 ,lp_yyscanner ); - } - else - /* Can't grow it, we don't own it. */ - b->lp_yy_ch_buf = 0; - - if ( ! b->lp_yy_ch_buf ) - YY_FATAL_ERROR( - "fatal error - scanner input buffer overflow" ); - - lp_yyg->lp_yy_c_buf_p = &b->lp_yy_ch_buf[lp_yy_c_buf_p_offset]; - - num_to_read = YY_CURRENT_BUFFER_LVALUE->lp_yy_buf_size - - number_to_move - 1; - - } - - if ( num_to_read > YY_READ_BUF_SIZE ) - num_to_read = YY_READ_BUF_SIZE; - - /* Read in more data. */ - YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[number_to_move]), - lp_yyg->lp_yy_n_chars, (size_t) num_to_read ); - - YY_CURRENT_BUFFER_LVALUE->lp_yy_n_chars = lp_yyg->lp_yy_n_chars; - } - - if ( lp_yyg->lp_yy_n_chars == 0 ) - { - if ( number_to_move == YY_MORE_ADJ ) - { - ret_val = EOB_ACT_END_OF_FILE; - lp_yyrestart(lp_yyin ,lp_yyscanner); - } - - else - { - ret_val = EOB_ACT_LAST_MATCH; - YY_CURRENT_BUFFER_LVALUE->lp_yy_buffer_status = - YY_BUFFER_EOF_PENDING; - } - } - - else - ret_val = EOB_ACT_CONTINUE_SCAN; - - if ((lp_yy_size_t) (lp_yyg->lp_yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->lp_yy_buf_size) { - /* Extend the array by 50%, plus the number we really need. */ - lp_yy_size_t new_size = lp_yyg->lp_yy_n_chars + number_to_move + (lp_yyg->lp_yy_n_chars >> 1); - YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf = (char *) lp_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf,new_size ,lp_yyscanner ); - if ( ! YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in lp_yy_get_next_buffer()" ); - } - - lp_yyg->lp_yy_n_chars += number_to_move; - YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[lp_yyg->lp_yy_n_chars] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[lp_yyg->lp_yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; - - lp_yyg->lp_yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[0]; - - return ret_val; -} - -/* lp_yy_get_previous_state - get the state just before the EOB char was reached */ - - static lp_yy_state_type lp_yy_get_previous_state (lp_yyscan_t lp_yyscanner) -{ - register lp_yy_state_type lp_yy_current_state; - register char *lp_yy_cp; - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - lp_yy_current_state = lp_yyg->lp_yy_start; - lp_yy_current_state += YY_AT_BOL(); - - for ( lp_yy_cp = lp_yyg->lp_yytext_ptr + YY_MORE_ADJ; lp_yy_cp < lp_yyg->lp_yy_c_buf_p; ++lp_yy_cp ) - { - register YY_CHAR lp_yy_c = (*lp_yy_cp ? lp_yy_ec[YY_SC_TO_UI(*lp_yy_cp)] : 1); - if ( lp_yy_accept[lp_yy_current_state] ) - { - lp_yyg->lp_yy_last_accepting_state = lp_yy_current_state; - lp_yyg->lp_yy_last_accepting_cpos = lp_yy_cp; - } - while ( lp_yy_chk[lp_yy_base[lp_yy_current_state] + lp_yy_c] != lp_yy_current_state ) - { - lp_yy_current_state = (int) lp_yy_def[lp_yy_current_state]; - if ( lp_yy_current_state >= 144 ) - lp_yy_c = lp_yy_meta[(unsigned int) lp_yy_c]; - } - lp_yy_current_state = lp_yy_nxt[lp_yy_base[lp_yy_current_state] + (unsigned int) lp_yy_c]; - } - - return lp_yy_current_state; -} - -/* lp_yy_try_NUL_trans - try to make a transition on the NUL character - * - * synopsis - * next_state = lp_yy_try_NUL_trans( current_state ); - */ - static lp_yy_state_type lp_yy_try_NUL_trans (lp_yy_state_type lp_yy_current_state , lp_yyscan_t lp_yyscanner) -{ - register int lp_yy_is_jam; - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; /* This var may be unused depending upon options. */ - register char *lp_yy_cp = lp_yyg->lp_yy_c_buf_p; - - register YY_CHAR lp_yy_c = 1; - if ( lp_yy_accept[lp_yy_current_state] ) - { - lp_yyg->lp_yy_last_accepting_state = lp_yy_current_state; - lp_yyg->lp_yy_last_accepting_cpos = lp_yy_cp; - } - while ( lp_yy_chk[lp_yy_base[lp_yy_current_state] + lp_yy_c] != lp_yy_current_state ) - { - lp_yy_current_state = (int) lp_yy_def[lp_yy_current_state]; - if ( lp_yy_current_state >= 144 ) - lp_yy_c = lp_yy_meta[(unsigned int) lp_yy_c]; - } - lp_yy_current_state = lp_yy_nxt[lp_yy_base[lp_yy_current_state] + (unsigned int) lp_yy_c]; - lp_yy_is_jam = (lp_yy_current_state == 143); - - return lp_yy_is_jam ? 0 : lp_yy_current_state; -} - - static void lp_yyunput (int c, register char * lp_yy_bp , lp_yyscan_t lp_yyscanner) -{ - register char *lp_yy_cp; - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - lp_yy_cp = lp_yyg->lp_yy_c_buf_p; - - /* undo effects of setting up lp_yytext */ - *lp_yy_cp = lp_yyg->lp_yy_hold_char; - - if ( lp_yy_cp < YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf + 2 ) - { /* need to shift things up to make room */ - /* +2 for EOB chars. */ - register int number_to_move = lp_yyg->lp_yy_n_chars + 2; - register char *dest = &YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[ - YY_CURRENT_BUFFER_LVALUE->lp_yy_buf_size + 2]; - register char *source = - &YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[number_to_move]; - - while ( source > YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf ) - *--dest = *--source; - - lp_yy_cp += (int) (dest - source); - lp_yy_bp += (int) (dest - source); - YY_CURRENT_BUFFER_LVALUE->lp_yy_n_chars = - lp_yyg->lp_yy_n_chars = YY_CURRENT_BUFFER_LVALUE->lp_yy_buf_size; - - if ( lp_yy_cp < YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf + 2 ) - YY_FATAL_ERROR( "flex scanner push-back overflow" ); - } - - *--lp_yy_cp = (char) c; - - if ( c == '\n' ){ - --lp_yylineno; - } - - lp_yyg->lp_yytext_ptr = lp_yy_bp; - lp_yyg->lp_yy_hold_char = *lp_yy_cp; - lp_yyg->lp_yy_c_buf_p = lp_yy_cp; -} - -#ifndef YY_NO_INPUT -#ifdef __cplusplus - static int lp_yyinput (lp_yyscan_t lp_yyscanner) -#else - static int input (lp_yyscan_t lp_yyscanner) -#endif - -{ - int c; - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - *lp_yyg->lp_yy_c_buf_p = lp_yyg->lp_yy_hold_char; - - if ( *lp_yyg->lp_yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) - { - /* lp_yy_c_buf_p now points to the character we want to return. - * If this occurs *before* the EOB characters, then it's a - * valid NUL; if not, then we've hit the end of the buffer. - */ - if ( lp_yyg->lp_yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->lp_yy_ch_buf[lp_yyg->lp_yy_n_chars] ) - /* This was really a NUL. */ - *lp_yyg->lp_yy_c_buf_p = '\0'; - - else - { /* need more input */ - int offset = lp_yyg->lp_yy_c_buf_p - lp_yyg->lp_yytext_ptr; - ++lp_yyg->lp_yy_c_buf_p; - - switch ( lp_yy_get_next_buffer( lp_yyscanner ) ) - { - case EOB_ACT_LAST_MATCH: - /* This happens because lp_yy_g_n_b() - * sees that we've accumulated a - * token and flags that we need to - * try matching the token before - * proceeding. But for input(), - * there's no matching to consider. - * So convert the EOB_ACT_LAST_MATCH - * to EOB_ACT_END_OF_FILE. - */ - - /* Reset buffer status. */ - lp_yyrestart(lp_yyin ,lp_yyscanner); - - /*FALLTHROUGH*/ - - case EOB_ACT_END_OF_FILE: - { - if ( lp_yywrap(lp_yyscanner ) ) - return EOF; - - if ( ! lp_yyg->lp_yy_did_buffer_switch_on_eof ) - YY_NEW_FILE; -#ifdef __cplusplus - return lp_yyinput(lp_yyscanner); -#else - return input(lp_yyscanner); -#endif - } - - case EOB_ACT_CONTINUE_SCAN: - lp_yyg->lp_yy_c_buf_p = lp_yyg->lp_yytext_ptr + offset; - break; - } - } - } - - c = *(unsigned char *) lp_yyg->lp_yy_c_buf_p; /* cast for 8-bit char's */ - *lp_yyg->lp_yy_c_buf_p = '\0'; /* preserve lp_yytext */ - lp_yyg->lp_yy_hold_char = *++lp_yyg->lp_yy_c_buf_p; - - YY_CURRENT_BUFFER_LVALUE->lp_yy_at_bol = (c == '\n'); - if ( YY_CURRENT_BUFFER_LVALUE->lp_yy_at_bol ) - - do{ lp_yylineno++; - lp_yycolumn=0; - }while(0) -; - - return c; -} -#endif /* ifndef YY_NO_INPUT */ - -/** Immediately switch to a different input stream. - * @param input_file A readable stream. - * @param lp_yyscanner The scanner object. - * @note This function does not reset the start condition to @c INITIAL . - */ - void lp_yyrestart (FILE * input_file , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - if ( ! YY_CURRENT_BUFFER ){ - lp_yyensure_buffer_stack (lp_yyscanner); - YY_CURRENT_BUFFER_LVALUE = - lp_yy_create_buffer(lp_yyin,YY_BUF_SIZE ,lp_yyscanner); - } - - lp_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,lp_yyscanner); - lp_yy_load_buffer_state(lp_yyscanner ); -} - -/** Switch to a different input buffer. - * @param new_buffer The new input buffer. - * @param lp_yyscanner The scanner object. - */ - void lp_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - /* TODO. We should be able to replace this entire function body - * with - * lp_yypop_buffer_state(); - * lp_yypush_buffer_state(new_buffer); - */ - lp_yyensure_buffer_stack (lp_yyscanner); - if ( YY_CURRENT_BUFFER == new_buffer ) - return; - - if ( YY_CURRENT_BUFFER ) - { - /* Flush out information for old buffer. */ - *lp_yyg->lp_yy_c_buf_p = lp_yyg->lp_yy_hold_char; - YY_CURRENT_BUFFER_LVALUE->lp_yy_buf_pos = lp_yyg->lp_yy_c_buf_p; - YY_CURRENT_BUFFER_LVALUE->lp_yy_n_chars = lp_yyg->lp_yy_n_chars; - } - - YY_CURRENT_BUFFER_LVALUE = new_buffer; - lp_yy_load_buffer_state(lp_yyscanner ); - - /* We don't actually know whether we did this switch during - * EOF (lp_yywrap()) processing, but the only time this flag - * is looked at is after lp_yywrap() is called, so it's safe - * to go ahead and always set it. - */ - lp_yyg->lp_yy_did_buffer_switch_on_eof = 1; -} - -static void lp_yy_load_buffer_state (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - lp_yyg->lp_yy_n_chars = YY_CURRENT_BUFFER_LVALUE->lp_yy_n_chars; - lp_yyg->lp_yytext_ptr = lp_yyg->lp_yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->lp_yy_buf_pos; - lp_yyin = YY_CURRENT_BUFFER_LVALUE->lp_yy_input_file; - lp_yyg->lp_yy_hold_char = *lp_yyg->lp_yy_c_buf_p; -} - -/** Allocate and initialize an input buffer state. - * @param file A readable stream. - * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * @param lp_yyscanner The scanner object. - * @return the allocated buffer state. - */ - YY_BUFFER_STATE lp_yy_create_buffer (FILE * file, int size , lp_yyscan_t lp_yyscanner) -{ - YY_BUFFER_STATE b; - - b = (YY_BUFFER_STATE) lp_yyalloc(sizeof( struct lp_yy_buffer_state ) ,lp_yyscanner ); - if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in lp_yy_create_buffer()" ); - - b->lp_yy_buf_size = size; - - /* lp_yy_ch_buf has to be 2 characters longer than the size given because - * we need to put in 2 end-of-buffer characters. - */ - b->lp_yy_ch_buf = (char *) lp_yyalloc(b->lp_yy_buf_size + 2 ,lp_yyscanner ); - if ( ! b->lp_yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in lp_yy_create_buffer()" ); - - b->lp_yy_is_our_buffer = 1; - - lp_yy_init_buffer(b,file ,lp_yyscanner); - - return b; -} - -/** Destroy the buffer. - * @param b a buffer created with lp_yy_create_buffer() - * @param lp_yyscanner The scanner object. - */ - void lp_yy_delete_buffer (YY_BUFFER_STATE b , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - if ( ! b ) - return; - - if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ - YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; - - if ( b->lp_yy_is_our_buffer ) - lp_yyfree((void *) b->lp_yy_ch_buf ,lp_yyscanner ); - - lp_yyfree((void *) b ,lp_yyscanner ); -} - -#ifndef __cplusplus -extern int isatty (int ); -#endif /* __cplusplus */ - -/* Initializes or reinitializes a buffer. - * This function is sometimes called more than once on the same buffer, - * such as during a lp_yyrestart() or at EOF. - */ - static void lp_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , lp_yyscan_t lp_yyscanner) - -{ - int oerrno = errno; - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - lp_yy_flush_buffer(b ,lp_yyscanner); - - b->lp_yy_input_file = file; - b->lp_yy_fill_buffer = 1; - - /* If b is the current buffer, then lp_yy_init_buffer was _probably_ - * called from lp_yyrestart() or through lp_yy_get_next_buffer. - * In that case, we don't want to reset the lineno or column. - */ - if (b != YY_CURRENT_BUFFER){ - b->lp_yy_bs_lineno = 1; - b->lp_yy_bs_column = 0; - } - - b->lp_yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; - - errno = oerrno; -} - -/** Discard all buffered characters. On the next scan, YY_INPUT will be called. - * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * @param lp_yyscanner The scanner object. - */ - void lp_yy_flush_buffer (YY_BUFFER_STATE b , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - if ( ! b ) - return; - - b->lp_yy_n_chars = 0; - - /* We always need two end-of-buffer characters. The first causes - * a transition to the end-of-buffer state. The second causes - * a jam in that state. - */ - b->lp_yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; - b->lp_yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; - - b->lp_yy_buf_pos = &b->lp_yy_ch_buf[0]; - - b->lp_yy_at_bol = 1; - b->lp_yy_buffer_status = YY_BUFFER_NEW; - - if ( b == YY_CURRENT_BUFFER ) - lp_yy_load_buffer_state(lp_yyscanner ); -} - -/** Pushes the new state onto the stack. The new state becomes - * the current state. This function will allocate the stack - * if necessary. - * @param new_buffer The new state. - * @param lp_yyscanner The scanner object. - */ -void lp_yypush_buffer_state (YY_BUFFER_STATE new_buffer , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - if (new_buffer == NULL) - return; - - lp_yyensure_buffer_stack(lp_yyscanner); - - /* This block is copied from lp_yy_switch_to_buffer. */ - if ( YY_CURRENT_BUFFER ) - { - /* Flush out information for old buffer. */ - *lp_yyg->lp_yy_c_buf_p = lp_yyg->lp_yy_hold_char; - YY_CURRENT_BUFFER_LVALUE->lp_yy_buf_pos = lp_yyg->lp_yy_c_buf_p; - YY_CURRENT_BUFFER_LVALUE->lp_yy_n_chars = lp_yyg->lp_yy_n_chars; - } - - /* Only push if top exists. Otherwise, replace top. */ - if (YY_CURRENT_BUFFER) - lp_yyg->lp_yy_buffer_stack_top++; - YY_CURRENT_BUFFER_LVALUE = new_buffer; - - /* copied from lp_yy_switch_to_buffer. */ - lp_yy_load_buffer_state(lp_yyscanner ); - lp_yyg->lp_yy_did_buffer_switch_on_eof = 1; -} - -/** Removes and deletes the top of the stack, if present. - * The next element becomes the new top. - * @param lp_yyscanner The scanner object. - */ -void lp_yypop_buffer_state (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - if (!YY_CURRENT_BUFFER) - return; - - lp_yy_delete_buffer(YY_CURRENT_BUFFER ,lp_yyscanner); - YY_CURRENT_BUFFER_LVALUE = NULL; - if (lp_yyg->lp_yy_buffer_stack_top > 0) - --lp_yyg->lp_yy_buffer_stack_top; - - if (YY_CURRENT_BUFFER) { - lp_yy_load_buffer_state(lp_yyscanner ); - lp_yyg->lp_yy_did_buffer_switch_on_eof = 1; - } -} - -/* Allocates the stack if it does not exist. - * Guarantees space for at least one push. - */ -static void lp_yyensure_buffer_stack (lp_yyscan_t lp_yyscanner) -{ - int num_to_alloc; - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - if (!lp_yyg->lp_yy_buffer_stack) { - - /* First allocation is just for 2 elements, since we don't know if this - * scanner will even need a stack. We use 2 instead of 1 to avoid an - * immediate realloc on the next call. - */ - num_to_alloc = 1; - lp_yyg->lp_yy_buffer_stack = (struct lp_yy_buffer_state**)lp_yyalloc - (num_to_alloc * sizeof(struct lp_yy_buffer_state*) - , lp_yyscanner); - if ( ! lp_yyg->lp_yy_buffer_stack ) - YY_FATAL_ERROR( "out of dynamic memory in lp_yyensure_buffer_stack()" ); - - memset(lp_yyg->lp_yy_buffer_stack, 0, num_to_alloc * sizeof(struct lp_yy_buffer_state*)); - - lp_yyg->lp_yy_buffer_stack_max = num_to_alloc; - lp_yyg->lp_yy_buffer_stack_top = 0; - return; - } - - if (lp_yyg->lp_yy_buffer_stack_top >= (lp_yyg->lp_yy_buffer_stack_max) - 1){ - - /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; - - num_to_alloc = lp_yyg->lp_yy_buffer_stack_max + grow_size; - lp_yyg->lp_yy_buffer_stack = (struct lp_yy_buffer_state**)lp_yyrealloc - (lp_yyg->lp_yy_buffer_stack, - num_to_alloc * sizeof(struct lp_yy_buffer_state*) - , lp_yyscanner); - if ( ! lp_yyg->lp_yy_buffer_stack ) - YY_FATAL_ERROR( "out of dynamic memory in lp_yyensure_buffer_stack()" ); - - /* zero only the new slots.*/ - memset(lp_yyg->lp_yy_buffer_stack + lp_yyg->lp_yy_buffer_stack_max, 0, grow_size * sizeof(struct lp_yy_buffer_state*)); - lp_yyg->lp_yy_buffer_stack_max = num_to_alloc; - } -} - -/** Setup the input buffer state to scan directly from a user-specified character buffer. - * @param base the character buffer - * @param size the size in bytes of the character buffer - * @param lp_yyscanner The scanner object. - * @return the newly allocated buffer state object. - */ -YY_BUFFER_STATE lp_yy_scan_buffer (char * base, lp_yy_size_t size , lp_yyscan_t lp_yyscanner) -{ - YY_BUFFER_STATE b; - - if ( size < 2 || - base[size-2] != YY_END_OF_BUFFER_CHAR || - base[size-1] != YY_END_OF_BUFFER_CHAR ) - /* They forgot to leave room for the EOB's. */ - return 0; - - b = (YY_BUFFER_STATE) lp_yyalloc(sizeof( struct lp_yy_buffer_state ) ,lp_yyscanner ); - if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in lp_yy_scan_buffer()" ); - - b->lp_yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ - b->lp_yy_buf_pos = b->lp_yy_ch_buf = base; - b->lp_yy_is_our_buffer = 0; - b->lp_yy_input_file = 0; - b->lp_yy_n_chars = b->lp_yy_buf_size; - b->lp_yy_is_interactive = 0; - b->lp_yy_at_bol = 1; - b->lp_yy_fill_buffer = 0; - b->lp_yy_buffer_status = YY_BUFFER_NEW; - - lp_yy_switch_to_buffer(b ,lp_yyscanner ); - - return b; -} - -/** Setup the input buffer state to scan a string. The next call to lp_yylex() will - * scan from a @e copy of @a str. - * @param lp_yystr a NUL-terminated string to scan - * @param lp_yyscanner The scanner object. - * @return the newly allocated buffer state object. - * @note If you want to scan bytes that may contain NUL values, then use - * lp_yy_scan_bytes() instead. - */ -YY_BUFFER_STATE lp_yy_scan_string (lp_yyconst char * lp_yystr , lp_yyscan_t lp_yyscanner) -{ - - return lp_yy_scan_bytes(lp_yystr,strlen(lp_yystr) ,lp_yyscanner); -} - -/** Setup the input buffer state to scan the given bytes. The next call to lp_yylex() will - * scan from a @e copy of @a bytes. - * @param bytes the byte buffer to scan - * @param len the number of bytes in the buffer pointed to by @a bytes. - * @param lp_yyscanner The scanner object. - * @return the newly allocated buffer state object. - */ -YY_BUFFER_STATE lp_yy_scan_bytes (lp_yyconst char * lp_yybytes, int _lp_yybytes_len , lp_yyscan_t lp_yyscanner) -{ - YY_BUFFER_STATE b; - char *buf; - lp_yy_size_t n; - int i; - - /* Get memory for full buffer, including space for trailing EOB's. */ - n = _lp_yybytes_len + 2; - buf = (char *) lp_yyalloc(n ,lp_yyscanner ); - if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in lp_yy_scan_bytes()" ); - - for ( i = 0; i < _lp_yybytes_len; ++i ) - buf[i] = lp_yybytes[i]; - - buf[_lp_yybytes_len] = buf[_lp_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - - b = lp_yy_scan_buffer(buf,n ,lp_yyscanner); - if ( ! b ) - YY_FATAL_ERROR( "bad buffer in lp_yy_scan_bytes()" ); - - /* It's okay to grow etc. this buffer, and we should throw it - * away when we're done. - */ - b->lp_yy_is_our_buffer = 1; - - return b; -} - -#ifndef YY_EXIT_FAILURE -#define YY_EXIT_FAILURE 2 -#endif - -static void lp_yy_fatal_error (lp_yyconst char* msg , lp_yyscan_t lp_yyscanner) -{ - (void) fprintf( stderr, "%s\n", msg ); - exit( YY_EXIT_FAILURE ); -} - -/* Redefine lp_yyless() so it works in section 3 code. */ - -#undef lp_yyless -#define lp_yyless(n) \ - do \ - { \ - /* Undo effects of setting up lp_yytext. */ \ - int lp_yyless_macro_arg = (n); \ - YY_LESS_LINENO(lp_yyless_macro_arg);\ - lp_yytext[lp_yyleng] = lp_yyg->lp_yy_hold_char; \ - lp_yyg->lp_yy_c_buf_p = lp_yytext + lp_yyless_macro_arg; \ - lp_yyg->lp_yy_hold_char = *lp_yyg->lp_yy_c_buf_p; \ - *lp_yyg->lp_yy_c_buf_p = '\0'; \ - lp_yyleng = lp_yyless_macro_arg; \ - } \ - while ( 0 ) - -/* Accessor methods (get/set functions) to struct members. */ - -/** Get the user-defined data for this scanner. - * @param lp_yyscanner The scanner object. - */ -YY_EXTRA_TYPE lp_yyget_extra (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - return lp_yyextra; -} - -/** Get the current line number. - * @param lp_yyscanner The scanner object. - */ -int lp_yyget_lineno (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - if (! YY_CURRENT_BUFFER) - return 0; - - return lp_yylineno; -} - -/** Get the current column number. - * @param lp_yyscanner The scanner object. - */ -int lp_yyget_column (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - if (! YY_CURRENT_BUFFER) - return 0; - - return lp_yycolumn; -} - -/** Get the input stream. - * @param lp_yyscanner The scanner object. - */ -FILE *lp_yyget_in (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - return lp_yyin; -} - -/** Get the output stream. - * @param lp_yyscanner The scanner object. - */ -FILE *lp_yyget_out (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - return lp_yyout; -} - -/** Get the length of the current token. - * @param lp_yyscanner The scanner object. - */ -int lp_yyget_leng (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - return lp_yyleng; -} - -/** Get the current token. - * @param lp_yyscanner The scanner object. - */ - -char *lp_yyget_text (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - return lp_yytext; -} - -/** Set the user-defined data. This data is never touched by the scanner. - * @param user_defined The data to be associated with this scanner. - * @param lp_yyscanner The scanner object. - */ -void lp_yyset_extra (YY_EXTRA_TYPE user_defined , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - lp_yyextra = user_defined ; -} - -/** Set the current line number. - * @param line_number - * @param lp_yyscanner The scanner object. - */ -void lp_yyset_lineno (int line_number , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - /* lineno is only valid if an input buffer exists. */ - if (! YY_CURRENT_BUFFER ) - lp_yy_fatal_error( "lp_yyset_lineno called with no buffer" , lp_yyscanner); - - lp_yylineno = line_number; -} - -/** Set the current column. - * @param line_number - * @param lp_yyscanner The scanner object. - */ -void lp_yyset_column (int column_no , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - /* column is only valid if an input buffer exists. */ - if (! YY_CURRENT_BUFFER ) - lp_yy_fatal_error( "lp_yyset_column called with no buffer" , lp_yyscanner); - - lp_yycolumn = column_no; -} - -/** Set the input stream. This does not discard the current - * input buffer. - * @param in_str A readable stream. - * @param lp_yyscanner The scanner object. - * @see lp_yy_switch_to_buffer - */ -void lp_yyset_in (FILE * in_str , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - lp_yyin = in_str ; -} - -void lp_yyset_out (FILE * out_str , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - lp_yyout = out_str ; -} - -int lp_yyget_debug (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - return lp_yy_flex_debug; -} - -void lp_yyset_debug (int bdebug , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - lp_yy_flex_debug = bdebug ; -} - -/* Accessor methods for lp_yylval and lp_yylloc */ - -YYSTYPE * lp_yyget_lval (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - return lp_yylval; -} - -void lp_yyset_lval (YYSTYPE * lp_yylval_param , lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - lp_yylval = lp_yylval_param; -} - -/* User-visible API */ - -/* lp_yylex_init is special because it creates the scanner itself, so it is - * the ONLY reentrant function that doesn't take the scanner as the last argument. - * That's why we explicitly handle the declaration, instead of using our macros. - */ - -int lp_yylex_init(lp_yyscan_t* ptr_lp_yy_globals) - -{ - if (ptr_lp_yy_globals == NULL){ - errno = EINVAL; - return 1; - } - - *ptr_lp_yy_globals = (lp_yyscan_t) lp_yyalloc ( sizeof( struct lp_yyguts_t ), NULL ); - - if (*ptr_lp_yy_globals == NULL){ - errno = ENOMEM; - return 1; - } - - /* By setting to 0xAA, we expose bugs in lp_yy_init_globals. Leave at 0x00 for releases. */ - memset(*ptr_lp_yy_globals,0x00,sizeof(struct lp_yyguts_t)); - - return lp_yy_init_globals ( *ptr_lp_yy_globals ); -} - -/* lp_yylex_init_extra has the same functionality as lp_yylex_init, but follows the - * convention of taking the scanner as the last argument. Note however, that - * this is a *pointer* to a scanner, as it will be allocated by this call (and - * is the reason, too, why this function also must handle its own declaration). - * The user defined value in the first argument will be available to lp_yyalloc in - * the lp_yyextra field. - */ - -int lp_yylex_init_extra(YY_EXTRA_TYPE lp_yy_user_defined,lp_yyscan_t* ptr_lp_yy_globals ) - -{ - struct lp_yyguts_t dummy_lp_yyguts; - - lp_yyset_extra (lp_yy_user_defined, &dummy_lp_yyguts); - - if (ptr_lp_yy_globals == NULL){ - errno = EINVAL; - return 1; - } - - *ptr_lp_yy_globals = (lp_yyscan_t) lp_yyalloc ( sizeof( struct lp_yyguts_t ), &dummy_lp_yyguts ); - - if (*ptr_lp_yy_globals == NULL){ - errno = ENOMEM; - return 1; - } - - /* By setting to 0xAA, we expose bugs in - lp_yy_init_globals. Leave at 0x00 for releases. */ - memset(*ptr_lp_yy_globals,0x00,sizeof(struct lp_yyguts_t)); - - lp_yyset_extra (lp_yy_user_defined, *ptr_lp_yy_globals); - - return lp_yy_init_globals ( *ptr_lp_yy_globals ); -} - -static int lp_yy_init_globals (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - /* Initialization is the same as for the non-reentrant scanner. - * This function is called from lp_yylex_destroy(), so don't allocate here. - */ - - lp_yyg->lp_yy_buffer_stack = 0; - lp_yyg->lp_yy_buffer_stack_top = 0; - lp_yyg->lp_yy_buffer_stack_max = 0; - lp_yyg->lp_yy_c_buf_p = (char *) 0; - lp_yyg->lp_yy_init = 0; - lp_yyg->lp_yy_start = 0; - - lp_yyg->lp_yy_start_stack_ptr = 0; - lp_yyg->lp_yy_start_stack_depth = 0; - lp_yyg->lp_yy_start_stack = NULL; - -/* Defined in main.c */ -#ifdef YY_STDINIT - lp_yyin = stdin; - lp_yyout = stdout; -#else - lp_yyin = (FILE *) 0; - lp_yyout = (FILE *) 0; -#endif - - /* For future reference: Set errno on error, since we are called by - * lp_yylex_init() - */ - return 0; -} - -/* lp_yylex_destroy is for both reentrant and non-reentrant scanners. */ -int lp_yylex_destroy (lp_yyscan_t lp_yyscanner) -{ - struct lp_yyguts_t * lp_yyg = (struct lp_yyguts_t*)lp_yyscanner; - - /* Pop the buffer stack, destroying each element. */ - while(YY_CURRENT_BUFFER){ - lp_yy_delete_buffer(YY_CURRENT_BUFFER ,lp_yyscanner ); - YY_CURRENT_BUFFER_LVALUE = NULL; - lp_yypop_buffer_state(lp_yyscanner); - } - - /* Destroy the stack itself. */ - lp_yyfree(lp_yyg->lp_yy_buffer_stack ,lp_yyscanner); - lp_yyg->lp_yy_buffer_stack = NULL; - - /* Destroy the start condition stack. */ - lp_yyfree(lp_yyg->lp_yy_start_stack ,lp_yyscanner ); - lp_yyg->lp_yy_start_stack = NULL; - - /* Reset the globals. This is important in a non-reentrant scanner so the next time - * lp_yylex() is called, initialization will occur. */ - lp_yy_init_globals( lp_yyscanner); - - /* Destroy the main struct (reentrant only). */ - lp_yyfree ( lp_yyscanner , lp_yyscanner ); - lp_yyscanner = NULL; - return 0; -} - -/* - * Internal utility routines. - */ - -#ifndef lp_yytext_ptr -static void lp_yy_flex_strncpy (char* s1, lp_yyconst char * s2, int n , lp_yyscan_t lp_yyscanner) -{ - register int i; - for ( i = 0; i < n; ++i ) - s1[i] = s2[i]; -} -#endif - -#ifdef YY_NEED_STRLEN -static int lp_yy_flex_strlen (lp_yyconst char * s , lp_yyscan_t lp_yyscanner) -{ - register int n; - for ( n = 0; s[n]; ++n ) - ; - - return n; -} -#endif - -void *lp_yyalloc (lp_yy_size_t size , lp_yyscan_t lp_yyscanner) -{ - return (void *) malloc( size ); -} - -void *lp_yyrealloc (void * ptr, lp_yy_size_t size , lp_yyscan_t lp_yyscanner) -{ - /* The cast to (char *) in the following accommodates both - * implementations that use char* generic pointers, and those - * that use void* generic pointers. It works with the latter - * because both ANSI C and C++ allow castless assignment from - * any pointer type to void*, and deal with argument conversions - * as though doing an assignment. - */ - return (void *) realloc( (char *) ptr, size ); -} - -void lp_yyfree (void * ptr , lp_yyscan_t lp_yyscanner) -{ - free( (char *) ptr ); /* see lp_yyrealloc() for (char *) cast */ -} - -#define YYTABLES_NAME "lp_yytables" - diff --git a/src/lpsolve/headers/include/lp_scale.h b/src/lpsolve/headers/include/lp_scale.h deleted file mode 100644 index 352fe6f9..00000000 --- a/src/lpsolve/headers/include/lp_scale.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef HEADER_lp_scale -#define HEADER_lp_scale - -#include "lp_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Put function headers here */ -STATIC MYBOOL scale_updatecolumns(lprec *lp, LPSREAL *scalechange, MYBOOL updateonly); -STATIC MYBOOL scale_updaterows(lprec *lp, LPSREAL *scalechange, MYBOOL updateonly); -STATIC MYBOOL scale_rows(lprec *lp, LPSREAL *scaledelta); -STATIC MYBOOL scale_columns(lprec *lp, LPSREAL *scaledelta); -STATIC void unscale_columns(lprec *lp); -STATIC LPSREAL scale(lprec *lp, LPSREAL *scaledelta); -STATIC LPSREAL scaled_mat(lprec *lp, LPSREAL value, int rownr, int colnr); -STATIC LPSREAL unscaled_mat(lprec *lp, LPSREAL value, int rownr, int colnr); -STATIC LPSREAL scaled_value(lprec *lp, LPSREAL value, int index); -STATIC LPSREAL unscaled_value(lprec *lp, LPSREAL value, int index); -STATIC MYBOOL scaleCR(lprec *lp, LPSREAL *scaledelta); -STATIC MYBOOL finalize_scaling(lprec *lp, LPSREAL *scaledelta); -STATIC LPSREAL auto_scale(lprec *lp); -void undoscale(lprec *lp); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_scale */ - diff --git a/src/lpsolve/headers/include/lp_simplex.h b/src/lpsolve/headers/include/lp_simplex.h deleted file mode 100644 index 034584e0..00000000 --- a/src/lpsolve/headers/include/lp_simplex.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef HEADER_lp_simplex -#define HEADER_lp_simplex - -#include "lp_types.h" - -#define ForceDualSimplexInBB /* Force use/switch of dual simplex in B&B */ -#define AssumeHighAccuracyInBB /* No iteration of simplex solves at infeasibility */ -/*#define UseLongStepPruning*/ -/*#define UseLongStepDualPhase1*/ -#define primal_UseRejectionList -#define dual_UseRejectionList -#define dual_RemoveBasicFixedVars -/*#define dual_Phase1PriceEqualities */ /* Force elimination of equality slacks */ -#define AcceptMarginalAccuracy - -#ifdef __cplusplus -extern "C" { -#endif - -/* Put function headers here */ -STATIC int primloop(lprec *lp, MYBOOL primalfeasible, LPSREAL primaloffset); -STATIC int dualloop(lprec *lp, MYBOOL dualfeasible, int dualinfeasibles[], LPSREAL dualoffset); -STATIC int spx_run(lprec *lp, MYBOOL validInvB); -STATIC int spx_solve(lprec *lp); -STATIC int lag_solve(lprec *lp, LPSREAL start_bound, int num_iter); -STATIC int heuristics(lprec *lp, int mode); -STATIC int lin_solve(lprec *lp); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_simplex */ - diff --git a/src/lpsolve/headers/include/lp_types.h b/src/lpsolve/headers/include/lp_types.h deleted file mode 100644 index 871f76cb..00000000 --- a/src/lpsolve/headers/include/lp_types.h +++ /dev/null @@ -1,330 +0,0 @@ -#ifndef HEADER_lp_types -#define HEADER_lp_types - -#ifdef WIN32 - #include -#endif - -/* Define data types */ -/* ------------------------------------------------------------------------- */ -#ifndef LLONG - #if defined __BORLANDC__ - #define LLONG __int64 - #elif !defined _MSC_VER || _MSC_VER >= 1310 - #define LLONG long long - #else - #define LLONG __int64 - #endif -#endif - -#ifndef COUNTER - #define COUNTER LLONG -#endif - -#ifndef LPSREAL - #define LPSREAL double -#endif - -#ifndef REALXP - #if 1 - #define REALXP long double /* Set local accumulation variable as long double */ - #else - #define REALXP LPSREAL /* Set local accumulation as default precision */ - #endif -#endif - -#ifndef LREAL - #if 0 - #define LREAL long double /* Set global solution update variable as long double */ - #else - #define LREAL LPSREAL /* Set global solution update variable as default precision */ - #endif -#endif - -#define RESULTVALUEMASK "%18.12g" /* Set fixed-format real-valued output precision; - suggested width: ABS(exponent of DEF_EPSVALUE)+6. */ -#define INDEXVALUEMASK "%8d" /* Set fixed-format integer-valued output width */ - -#ifndef DEF_STRBUFSIZE - #define DEF_STRBUFSIZE 512 -#endif -#ifndef MAXINT32 - #define MAXINT32 2147483647 -#endif -#ifndef MAXUINT32 - #define MAXUINT32 4294967295 -#endif - -#ifndef MAXINT64 - #if defined _LONGLONG || defined __LONG_LONG_MAX__ || defined LLONG_MAX - #define MAXINT64 9223372036854775807ll - #else - #define MAXINT64 9223372036854775807l - #endif -#endif -#ifndef MAXUINT64 - #if defined _LONGLONG || defined __LONG_LONG_MAX__ || defined LLONG_MAX - #define MAXUINT64 18446744073709551615ll - #else - #define MAXUINT64 18446744073709551615l - #endif -#endif - -#ifndef CHAR_BIT - #define CHAR_BIT 8 -#endif -#ifndef MYBOOL - #define MYBOOL unsigned char /* Conserve memory, could be unsigned int */ -#endif - - -/* Constants */ -/* ------------------------------------------------------------------------- */ -#ifndef NULL - #define NULL 0 -#endif - -/* Byte-sized Booleans and extended options */ -#define FALSE 0 -#define TRUE 1 -#define AUTOMATIC 2 -#define DYNAMIC 4 - -/* Sorting and comparison constants */ -#define COMP_PREFERCANDIDATE 1 -#define COMP_PREFERNONE 0 -#define COMP_PREFERINCUMBENT -1 - -/* Library load status values */ -#define LIB_LOADED 0 -#define LIB_NOTFOUND 1 -#define LIB_NOINFO 2 -#define LIB_NOFUNCTION 3 -#define LIB_VERINVALID 4 -#define LIB_STR_LOADED "Successfully loaded" -#define LIB_STR_NOTFOUND "File not found" -#define LIB_STR_NOINFO "No version data" -#define LIB_STR_NOFUNCTION "Missing function header" -#define LIB_STR_VERINVALID "Incompatible version" -#define LIB_STR_MAXLEN 23 - - -/* Compiler/target settings */ -/* ------------------------------------------------------------------------- */ -#if (defined _WIN32) || (defined WIN32) || (defined _WIN64) || (defined WIN64) -# define __WINAPI WINAPI -#else -# define __WINAPI -#endif - -#if (defined _WIN32) || (defined WIN32) || (defined _WIN64) || (defined WIN64) -# define __VACALL __cdecl -#else -# define __VACALL -#endif - -#ifndef __BORLANDC__ - - #ifdef _USRDLL - - #if 1 - #define __EXPORT_TYPE __declspec(dllexport) - #else - /* Set up for the Microsoft compiler */ - #ifdef LP_SOLVE_EXPORTS - #define __EXPORT_TYPE __declspec(dllexport) - #else - #define __EXPORT_TYPE __declspec(dllimport) - #endif - #endif - - #else - - #define __EXPORT_TYPE - - #endif - - #ifdef __cplusplus - #define __EXTERN_C extern "C" - #else - #define __EXTERN_C - #endif - -#else /* Otherwise set up for the Borland compiler */ - - #ifdef __DLL__ - - #define _USRDLL - #define __EXTERN_C extern "C" - - #ifdef __READING_THE_DLL - #define __EXPORT_TYPE __import - #else - #define __EXPORT_TYPE __export - #endif - - #else - - #define __EXPORT_TYPE - #define __EXTERN_C extern "C" - - #endif - -#endif - - -#if 0 - #define STATIC static -#else - #define STATIC -#endif - -#if !defined INLINE - #if defined __cplusplus - #define INLINE inline - #elif (defined _WIN32) || (defined WIN32) || (defined _WIN64) || (defined WIN64) - #define INLINE __inline - #else - #define INLINE static - #endif -#endif - -/* Function macros */ -/* ------------------------------------------------------------------------- */ -#define my_limitrange(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x))) -#ifndef my_mod - #define my_mod(n, m) ((n) % (m)) -#endif -#define my_if(t, x, y) ((t) ? (x) : (y)) -#define my_sign(x) ((x) < 0 ? -1 : 1) -#if 1 - #define my_chsign(t, x) ( ((t) && ((x) != 0)) ? -(x) : (x)) -#else - #define my_chsign(t, x) ( (2*((t) == 0) - 1) * (x) ) /* "Pipelined", but problem with "negative zero" and possible problems on AIX */ -#endif -#define my_flipsign(x) ( fabs((LPSREAL) (x)) == 0 ? 0 : -(x) ) -#define my_roundzero(val, eps) if (fabs((LPSREAL) (val)) < eps) val = 0 -#define my_avoidtiny(val, eps) (fabs((LPSREAL) (val)) < eps ? 0 : val) - -#if 1 - #define my_infinite(lp, val) ( (MYBOOL) (fabs(val) >= lp->infinite) ) -#else - #define my_infinite(lp, val) is_infinite(lp, val) -#endif -#define my_inflimit(lp, val) ( my_infinite(lp, val) ? lp->infinite * my_sign(val) : (val) ) -#if 0 - #define my_precision(val, eps) ((fabs((LPSREAL) (val))) < (eps) ? 0 : (val)) -#else - #define my_precision(val, eps) restoreINT(val, eps) -#endif -#define my_reldiff(x, y) (((x) - (y)) / (1.0 + fabs((LPSREAL) (y)))) -#define my_boundstr(x) (fabs(x) < lp->infinite ? sprintf("%g",x) : ((x) < 0 ? "-Inf" : "Inf") ) -#ifndef my_boolstr - #define my_boolstr(x) (!(x) ? "FALSE" : "TRUE") -#endif -#define my_basisstr(isbasic) ((isbasic) ? "BASIC" : "NON-BASIC") -#define my_simplexstr(isdual) ((isdual) ? "DUAL" : "PRIMAL") -#define my_plural_std(count) (count == 1 ? "" : "s") -#define my_plural_y(count) (count == 1 ? "y" : "ies") -#define my_lowbound(x) ((FULLYBOUNDEDSIMPLEX) ? (x) : 0) - - -/* Bound macros usable for both the standard and fully bounded simplex */ -/* ------------------------------------------------------------------------- */ -/* -#define my_lowbo(lp, varnr) ( lp->isfullybounded ? lp->lowbo[varnr] : 0.0 ) -#define my_upbo(lp, varnr) ( lp->isfullybounded ? lp->upbo[varnr] : lp->lowbo[varnr] + lp->upbo[varnr] ) -#define my_rangebo(lp, varnr) ( lp->isfullybounded ? lp->upbo[varnr] - lp->lowbo[varnr] : lp->upbo[varnr] ) -*/ -#define my_lowbo(lp, varnr) ( 0.0 ) -#define my_upbo(lp, varnr) ( lp->lowbo[varnr] + lp->upbo[varnr] ) -#define my_rangebo(lp, varnr) ( lp->upbo[varnr] ) - -#define my_unbounded(lp, varnr) ((lp->upbo[varnr] >= lp->infinite) && (lp->lowbo[varnr] <= -lp->infinite)) -#define my_bounded(lp, varnr) ((lp->upbo[varnr] < lp->infinite) && (lp->lowbo[varnr] > -lp->infinite)) - -/* Forward declarations */ -/* ------------------------------------------------------------------------- */ -typedef struct _lprec lprec; -typedef struct _INVrec INVrec; -union QSORTrec; - -#ifndef UNIONTYPE - #ifdef __cplusplus - #define UNIONTYPE - #else - #define UNIONTYPE union - #endif -#endif - -/* B4 factorization optimization data */ -typedef struct _B4rec -{ - int *B4_var; /* Position of basic columns in the B4 basis */ - int *var_B4; /* Variable in the B4 basis */ - int *B4_row; /* B4 position of the i'th row */ - int *row_B4; /* Original position of the i'th row */ - LPSREAL *wcol; - int *nzwcol; -} B4rec; - -#define OBJ_STEPS 5 -typedef struct _OBJmonrec { - lprec *lp; - int oldpivstrategy, - oldpivrule, pivrule, ruleswitches, - limitstall[2], limitruleswitches, - idxstep[OBJ_STEPS], countstep, startstep, currentstep, - Rcycle, Ccycle, Ncycle, Mcycle, Icount; - LPSREAL thisobj, prevobj, - objstep[OBJ_STEPS], - thisinfeas, previnfeas, - epsvalue; - char spxfunc[10]; - MYBOOL pivdynamic; - MYBOOL isdual; - MYBOOL active; -} OBJmonrec; - -typedef struct _edgerec -{ - LPSREAL *edgeVector; -} edgerec; - -typedef struct _pricerec -{ - LPSREAL theta; - LPSREAL pivot; - LPSREAL epspivot; - int varno; - lprec *lp; - MYBOOL isdual; -} pricerec; -typedef struct _multirec -{ - lprec *lp; - int size; /* The maximum number of multiply priced rows/columns */ - int used; /* The current / active number of multiply priced rows/columns */ - int limit; /* The active/used count at which a full update is triggered */ - pricerec *items; /* Array of best multiply priced rows/columns */ - int *freeList; /* The indeces of available positions in "items" */ - UNIONTYPE QSORTrec *sortedList; /* List of pointers to "pricerec" items in sorted order */ - LPSREAL *stepList; /* Working array (values in sortedList order) */ - LPSREAL *valueList; /* Working array (values in sortedList order) */ - int *indexSet; /* The final exported index list of pivot variables */ - int active; /* Index of currently active multiply priced row/column */ - int retries; - LPSREAL step_base; - LPSREAL step_last; - LPSREAL obj_base; - LPSREAL obj_last; - LPSREAL epszero; - LPSREAL maxpivot; - LPSREAL maxbound; - MYBOOL sorted; - MYBOOL truncinf; - MYBOOL objcheck; - MYBOOL dirty; -} multirec; - -#endif /* HEADER_lp_types */ diff --git a/src/lpsolve/headers/include/lp_utils.h b/src/lpsolve/headers/include/lp_utils.h deleted file mode 100644 index 8554fc90..00000000 --- a/src/lpsolve/headers/include/lp_utils.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef HEADER_lp_utils -#define HEADER_lp_utils - -#ifdef FORTIFY - -#include "lp_fortify.h" - -#define allocCHAR allocCHAR_FORTIFY -#define allocMYBOOL allocMYBOOL_FORTIFY -#define allocINT allocINT_FORTIFY -#define allocREAL allocREAL_FORTIFY -#define allocLREAL allocLREAL_FORTIFY - -#endif - -#include "lp_types.h" - -/* Temporary data storage arrays */ -typedef struct _workarraysrec -{ - lprec *lp; - int size; - int count; - char **vectorarray; - int *vectorsize; -} workarraysrec; - -typedef struct _LLrec -{ - int size; /* The allocated list size */ - int count; /* The current entry count */ - int firstitem; - int lastitem; - int *map; /* The list of forward and backward-mapped entries */ -} LLrec; - -typedef struct _PVrec -{ - int count; /* The allocated list item count */ - int *startpos; /* Starting index of the current value */ - LPSREAL *value; /* The list of forward and backward-mapped entries */ - struct _PVrec *parent; /* The parent record in a pushed chain */ -} PVrec; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Put function headers here */ -STATIC MYBOOL allocCHAR(lprec *lp, char **ptr, int size, MYBOOL clear); -STATIC MYBOOL allocMYBOOL(lprec *lp, MYBOOL **ptr, int size, MYBOOL clear); -STATIC MYBOOL allocINT(lprec *lp, int **ptr, int size, MYBOOL clear); -STATIC MYBOOL allocREAL(lprec *lp, LPSREAL **ptr, int size, MYBOOL clear); -STATIC MYBOOL allocLREAL(lprec *lp, LREAL **ptr, int size, MYBOOL clear); -STATIC MYBOOL allocFREE(lprec *lp, void **ptr); -LPSREAL *cloneREAL(lprec *lp, LPSREAL *origlist, int size); -MYBOOL *cloneMYBOOL(lprec *lp, MYBOOL *origlist, int size); -int *cloneINT(lprec *lp, int *origlist, int size); - -int comp_bits(MYBOOL *bitarray1, MYBOOL *bitarray2, int items); - -STATIC workarraysrec *mempool_create(lprec *lp); -STATIC char *mempool_obtainVector(workarraysrec *mempool, int count, int unitsize); -STATIC MYBOOL mempool_releaseVector(workarraysrec *mempool, char *memvector, MYBOOL forcefree); -STATIC MYBOOL mempool_free(workarraysrec **mempool); - -STATIC void roundVector(LREAL *myvector, int endpos, LREAL roundzero); -STATIC LPSREAL normalizeVector(LPSREAL *myvector, int endpos); - -STATIC void swapINT(int *item1, int *item2); -STATIC void swapREAL(LPSREAL *item1, LPSREAL *item2); -STATIC void swapPTR(void **item1, void **item2); -STATIC LPSREAL restoreINT(LPSREAL valREAL, LPSREAL epsilon); -STATIC LPSREAL roundToPrecision(LPSREAL value, LPSREAL precision); - -STATIC int searchFor(int target, int *attributes, int size, int offset, MYBOOL absolute); - -STATIC MYBOOL isINT(lprec *lp, LPSREAL value); -STATIC MYBOOL isOrigFixed(lprec *lp, int varno); -STATIC void chsign_bounds(LPSREAL *lobound, LPSREAL *upbound); -STATIC LPSREAL rand_uniform(lprec *lp, LPSREAL range); - -/* Doubly linked list routines */ -STATIC int createLink(int size, LLrec **linkmap, MYBOOL *usedpos); -STATIC MYBOOL freeLink(LLrec **linkmap); -STATIC int sizeLink(LLrec *linkmap); -STATIC MYBOOL isActiveLink(LLrec *linkmap, int itemnr); -STATIC int countActiveLink(LLrec *linkmap); -STATIC int countInactiveLink(LLrec *linkmap); -STATIC int firstActiveLink(LLrec *linkmap); -STATIC int lastActiveLink(LLrec *linkmap); -STATIC MYBOOL appendLink(LLrec *linkmap, int newitem); -STATIC MYBOOL insertLink(LLrec *linkmap, int afteritem, int newitem); -STATIC MYBOOL setLink(LLrec *linkmap, int newitem); -STATIC MYBOOL fillLink(LLrec *linkmap); -STATIC int nextActiveLink(LLrec *linkmap, int backitemnr); -STATIC int prevActiveLink(LLrec *linkmap, int forwitemnr); -STATIC int firstInactiveLink(LLrec *linkmap); -STATIC int lastInactiveLink(LLrec *linkmap); -STATIC int nextInactiveLink(LLrec *linkmap, int backitemnr); -STATIC int prevInactiveLink(LLrec *linkmap, int forwitemnr); -STATIC int removeLink(LLrec *linkmap, int itemnr); -STATIC LLrec *cloneLink(LLrec *sourcemap, int newsize, MYBOOL freesource); -STATIC int compareLink(LLrec *linkmap1, LLrec *linkmap2); -STATIC MYBOOL verifyLink(LLrec *linkmap, int itemnr, MYBOOL doappend); - -/* Packed vector routines */ -STATIC PVrec *createPackedVector(int size, LPSREAL *values, int *workvector); -STATIC void pushPackedVector(PVrec *PV, PVrec *parent); -STATIC MYBOOL unpackPackedVector(PVrec *PV, LPSREAL **target); -STATIC LPSREAL getvaluePackedVector(PVrec *PV, int index); -STATIC PVrec *popPackedVector(PVrec *PV); -STATIC MYBOOL freePackedVector(PVrec **PV); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_utils */ - -#ifdef FORTIFY - -#if defined CODE_lp_utils && !defined CODE_lp_utils_ -int _Fortify_ret; -#else -extern int _Fortify_ret; -#endif - -#ifdef CODE_lp_utils -#define CODE_lp_utils_ -#else -# undef allocCHAR -# undef allocMYBOOL -# undef allocINT -# undef allocREAL -# undef allocLREAL -# define allocCHAR(lp, ptr, size, clear) (Fortify_LINE(__LINE__), Fortify_FILE(__FILE__), _Fortify_ret = allocCHAR_FORTIFY(lp, ptr, size, clear), Fortify_LINE(0), Fortify_FILE(NULL), _Fortify_ret) -# define allocMYBOOL(lp, ptr, size, clear) (Fortify_LINE(__LINE__), Fortify_FILE(__FILE__), _Fortify_ret = allocMYBOOL_FORTIFY(lp, ptr, size, clear), Fortify_LINE(0), Fortify_FILE(NULL), _Fortify_ret) -# define allocINT(lp, ptr, size, clear) (Fortify_LINE(__LINE__), Fortify_FILE(__FILE__), _Fortify_ret = allocINT_FORTIFY(lp, ptr, size, clear), Fortify_LINE(0), Fortify_FILE(NULL), _Fortify_ret) -# define allocREAL(lp, ptr, size, clear) (Fortify_LINE(__LINE__), Fortify_FILE(__FILE__), _Fortify_ret = allocREAL_FORTIFY(lp, ptr, size, clear), Fortify_LINE(0), Fortify_FILE(NULL), _Fortify_ret) -# define allocLREAL(lp, ptr, size, clear) (Fortify_LINE(__LINE__), Fortify_FILE(__FILE__), _Fortify_ret = allocLREAL_FORTIFY(lp, ptr, size, clear), Fortify_LINE(0), Fortify_FILE(NULL), _Fortify_ret) -#endif - -#endif - diff --git a/src/lpsolve/headers/include/lpkit.h b/src/lpsolve/headers/include/lpkit.h deleted file mode 100644 index 36835dcc..00000000 --- a/src/lpsolve/headers/include/lpkit.h +++ /dev/null @@ -1,34 +0,0 @@ -#include "lp_lib.h" -#include "lp_report.h" - -#define MALLOC(ptr, nr, type)\ - ((((nr) == 0) || ((ptr = (type *) malloc((size_t)((nr) * sizeof(*ptr)))) == NULL)) ? \ - report(NULL, CRITICAL, "malloc of %d bytes failed on line %d of file %s\n",\ - (nr) * sizeof(*ptr), __LINE__, __FILE__), (ptr = NULL /* (void *) 0 */) : \ - ptr\ - ) - -#define CALLOC(ptr, nr, type)\ - ((((nr) == 0) || ((ptr = (type *) calloc((size_t)(nr), sizeof(*ptr))) == NULL)) ? \ - report(NULL, CRITICAL, "calloc of %d bytes failed on line %d of file %s\n",\ - (nr) * sizeof(*ptr), __LINE__, __FILE__), (ptr = NULL /* (void *) 0 */) : \ - ptr\ - ) - -#define REALLOC(ptr, nr, type)\ - ((((nr) == 0) || ((ptr = (type *) realloc(ptr, (size_t)((nr) * sizeof(*ptr)))) == NULL)) ? \ - report(NULL, CRITICAL, "realloc of %d bytes failed on line %d of file %s\n",\ - (nr) * sizeof(*ptr), __LINE__, __FILE__), (ptr = NULL /* (void *) 0 */) : \ - ptr\ - ) - -#if defined FREE -# undef FREE -#endif - -#define FREE(ptr) if (ptr != NULL) {free(ptr), ptr = NULL;} else - -/* @FS: This is only used once, so there is no reason to define this! -#define MALLOCCPY(nptr, optr, nr, type)\ - (MALLOC(nptr, nr, type), (nptr != NULL) ? memcpy(nptr, optr, (size_t)((nr) * sizeof(*optr))) : 0, nptr) -*/ diff --git a/src/lpsolve/headers/include/lusol.h b/src/lpsolve/headers/include/lusol.h deleted file mode 100644 index 0734cfa0..00000000 --- a/src/lpsolve/headers/include/lusol.h +++ /dev/null @@ -1,357 +0,0 @@ -#ifndef HEADER_LUSOL -#define HEADER_LUSOL - -/* Include necessary libraries */ -/* ------------------------------------------------------------------------- */ -#include -#include "commonlib.h" - -/* Version information */ -/* ------------------------------------------------------------------------- */ -#define LUSOL_VERMAJOR 2 -#define LUSOL_VERMINOR 2 -#define LUSOL_RELEASE 2 -#define LUSOL_BUILD 0 - -/* Dynamic memory management macros */ -/* ------------------------------------------------------------------------- */ -#ifdef MATLAB - #define LUSOL_MALLOC(bytesize) mxMalloc(bytesize) - #define LUSOL_CALLOC(count, recsize) mxCalloc(count, recsize) - #define LUSOL_REALLOC(ptr, bytesize) mxRealloc((void *) ptr, bytesize) - #define LUSOL_FREE(ptr) {mxFree(ptr); ptr=NULL;} -#else - #define LUSOL_MALLOC(bytesize) malloc(bytesize) - #define LUSOL_CALLOC(count, recsize) calloc(count, recsize) - #define LUSOL_REALLOC(ptr, bytesize) realloc((void *) ptr, bytesize) - #define LUSOL_FREE(ptr) {free(ptr); ptr=NULL;} -#endif - -/* Performance compiler options */ -/* ------------------------------------------------------------------------- */ -#if 1 - #define ForceInitialization /* Satisfy compilers, check during debugging! */ - #define LUSOLFastDenseIndex /* Increment the linearized dense address */ - #define LUSOLFastClear /* Use intrinsic functions for memory zeroing */ - #define LUSOLFastMove /* Use intrinsic functions for memory moves */ - #define LUSOLFastCopy /* Use intrinsic functions for memory copy */ - #define LUSOLFastSolve /* Use pointer operations in equation solving */ - #define LUSOLSafeFastUpdate /* Use separate array for LU6L result storage */ -/*#define UseOld_LU6CHK_20040510 */ -/*#define AlwaysSeparateHamaxR */ /* Enabled when the pivot model is fixed */ - #if 0 - #define ForceRowBasedL0 /* Create a row-sorted version of L0 */ - #endif -/* #define SetSmallToZero*/ -/* #define DoTraceL0 */ -#endif -/*#define UseTimer */ - - -/* Legacy compatibility and testing options (Fortran-LUSOL) */ -/* ------------------------------------------------------------------------- */ -#if 0 - #define LegacyTesting - #define StaticMemAlloc /* Preallocated vs. dynamic memory allocation */ - #define ClassicdiagU /* Store diagU at end of a */ - #define ClassicHamaxR /* Store H+AmaxR at end of a/indc/indr */ -#endif - - -/* General constants and data type definitions */ -/* ------------------------------------------------------------------------- */ -#define LUSOL_ARRAYOFFSET 1 -#ifndef ZERO - #define ZERO 0 -#endif -#ifndef ONE - #define ONE 1 -#endif -#ifndef FALSE - #define FALSE 0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif -#ifndef NULL - #define NULL 0 -#endif -#ifndef LPSREAL - #define LPSREAL double -#endif -#ifndef REALXP - #define REALXP long double -#endif -#ifndef MYBOOL - #define MYBOOL unsigned char -#endif - - -/* User-settable default parameter values */ -/* ------------------------------------------------------------------------- */ -#define LUSOL_DEFAULT_GAMMA 2.0 -#define LUSOL_SMALLNUM 1.0e-20 /* IAEE doubles have precision 2.22e-16 */ -#define LUSOL_BIGNUM 1.0e+20 -#define LUSOL_MINDELTA_FACTOR 4 -#define LUSOL_MINDELTA_a 10000 -#if 1 - #define LUSOL_MULT_nz_a 2 /* Suggested by Yin Zhang */ -#else - #define LUSOL_MULT_nz_a 5 /* Could consider 6 or 7 */ -#endif -#define LUSOL_MINDELTA_rc 1000 -#define LUSOL_DEFAULT_SMARTRATIO 0.667 - -/* Fixed system parameters (changeable only by developers) */ -/* ------------------------------------------------------------------------- */ - -/* parmlu INPUT parameters: */ -#define LUSOL_RP_SMARTRATIO 0 -#define LUSOL_RP_FACTORMAX_Lij 1 -#define LUSOL_RP_UPDATEMAX_Lij 2 -#define LUSOL_RP_ZEROTOLERANCE 3 -#define LUSOL_RP_SMALLDIAG_U 4 -#define LUSOL_RP_EPSDIAG_U 5 -#define LUSOL_RP_COMPSPACE_U 6 -#define LUSOL_RP_MARKOWITZ_CONLY 7 -#define LUSOL_RP_MARKOWITZ_DENSE 8 -#define LUSOL_RP_GAMMA 9 - -/* parmlu OUPUT parameters: */ -#define LUSOL_RP_MAXELEM_A 10 -#define LUSOL_RP_MAXMULT_L 11 -#define LUSOL_RP_MAXELEM_U 12 -#define LUSOL_RP_MAXELEM_DIAGU 13 -#define LUSOL_RP_MINELEM_DIAGU 14 -#define LUSOL_RP_MAXELEM_TCP 15 -#define LUSOL_RP_GROWTHRATE 16 -#define LUSOL_RP_USERDATA_1 17 -#define LUSOL_RP_USERDATA_2 18 -#define LUSOL_RP_USERDATA_3 19 -#define LUSOL_RP_RESIDUAL_U 20 -#define LUSOL_RP_LASTITEM LUSOL_RP_RESIDUAL_U - -/* luparm INPUT parameters: */ -#define LUSOL_IP_USERDATA_0 0 -#define LUSOL_IP_PRINTUNIT 1 -#define LUSOL_IP_PRINTLEVEL 2 -#define LUSOL_IP_MARKOWITZ_MAXCOL 3 -#define LUSOL_IP_SCALAR_NZA 4 -#define LUSOL_IP_UPDATELIMIT 5 -#define LUSOL_IP_PIVOTTYPE 6 -#define LUSOL_IP_ACCELERATION 7 -#define LUSOL_IP_KEEPLU 8 -#define LUSOL_IP_SINGULARLISTSIZE 9 - -/* luparm OUTPUT parameters: */ -#define LUSOL_IP_INFORM 10 -#define LUSOL_IP_SINGULARITIES 11 -#define LUSOL_IP_SINGULARINDEX 12 -#define LUSOL_IP_MINIMUMLENA 13 -#define LUSOL_IP_MAXLEN 14 -#define LUSOL_IP_UPDATECOUNT 15 -#define LUSOL_IP_RANK_U 16 -#define LUSOL_IP_COLCOUNT_DENSE1 17 -#define LUSOL_IP_COLCOUNT_DENSE2 18 -#define LUSOL_IP_COLINDEX_DUMIN 19 -#define LUSOL_IP_COLCOUNT_L0 20 -#define LUSOL_IP_NONZEROS_L0 21 -#define LUSOL_IP_NONZEROS_U0 22 -#define LUSOL_IP_NONZEROS_L 23 -#define LUSOL_IP_NONZEROS_U 24 -#define LUSOL_IP_NONZEROS_ROW 25 -#define LUSOL_IP_COMPRESSIONS_LU 26 -#define LUSOL_IP_MARKOWITZ_MERIT 27 -#define LUSOL_IP_TRIANGROWS_U 28 -#define LUSOL_IP_TRIANGROWS_L 29 -#define LUSOL_IP_FTRANCOUNT 30 -#define LUSOL_IP_BTRANCOUNT 31 -#define LUSOL_IP_ROWCOUNT_L0 32 -#define LUSOL_IP_LASTITEM LUSOL_IP_ROWCOUNT_L0 - - -/* Macros for matrix-based access for dense part of A and timer mapping */ -/* ------------------------------------------------------------------------- */ -#define DAPOS(row, col) (row + (col-1)*LDA) -#define timer(text, id) LUSOL_timer(LUSOL, id, text) - - -/* Parameter/option defines */ -/* ------------------------------------------------------------------------- */ -#define LUSOL_MSG_NONE -1 -#define LUSOL_MSG_SINGULARITY 0 -#define LUSOL_MSG_STATISTICS 10 -#define LUSOL_MSG_PIVOT 50 - -#define LUSOL_BASEORDER 0 -#define LUSOL_OTHERORDER 1 -#define LUSOL_AUTOORDER 2 -#define LUSOL_ACCELERATE_L0 4 -#define LUSOL_ACCELERATE_U 8 - -#define LUSOL_PIVMOD_NOCHANGE -2 /* Don't change active pivoting model */ -#define LUSOL_PIVMOD_DEFAULT -1 /* Set pivoting model to default */ -#define LUSOL_PIVMOD_TPP 0 /* Threshold Partial pivoting (normal) */ -#define LUSOL_PIVMOD_TRP 1 /* Threshold Rook pivoting */ -#define LUSOL_PIVMOD_TCP 2 /* Threshold Complete pivoting */ -#define LUSOL_PIVMOD_TSP 3 /* Threshold Symmetric pivoting */ -#define LUSOL_PIVMOD_MAX LUSOL_PIVMOD_TSP - -#define LUSOL_PIVTOL_NOCHANGE 0 -#define LUSOL_PIVTOL_BAGGY 1 -#define LUSOL_PIVTOL_LOOSE 2 -#define LUSOL_PIVTOL_NORMAL 3 -#define LUSOL_PIVTOL_SLIM 4 -#define LUSOL_PIVTOL_TIGHT 5 -#define LUSOL_PIVTOL_SUPER 6 -#define LUSOL_PIVTOL_CORSET 7 -#define LUSOL_PIVTOL_DEFAULT LUSOL_PIVTOL_SLIM -#define LUSOL_PIVTOL_MAX LUSOL_PIVTOL_CORSET - -#define LUSOL_UPDATE_OLDEMPTY 0 /* No/empty current column. */ -#define LUSOL_UPDATE_OLDNONEMPTY 1 /* Current column need not have been empty. */ -#define LUSOL_UPDATE_NEWEMPTY 0 /* New column is taken to be zero. */ -#define LUSOL_UPDATE_NEWNONEMPTY 1 /* v(*) contains the new column; - on exit, v(*) satisfies L*v = a(new). */ -#define LUSOL_UPDATE_USEPREPARED 2 /* v(*) must satisfy L*v = a(new). */ - -#define LUSOL_SOLVE_Lv_v 1 /* v solves L v = v(input). w is not touched. */ -#define LUSOL_SOLVE_Ltv_v 2 /* v solves L'v = v(input). w is not touched. */ -#define LUSOL_SOLVE_Uw_v 3 /* w solves U w = v. v is not altered. */ -#define LUSOL_SOLVE_Utv_w 4 /* v solves U'v = w. w is destroyed. */ -#define LUSOL_SOLVE_Aw_v 5 /* w solves A w = v. v is altered as in 1. */ -#define LUSOL_FTRAN LUSOL_SOLVE_Aw_v -#define LUSOL_SOLVE_Atv_w 6 /* v solves A'v = w. w is destroyed. */ -#define LUSOL_BTRAN LUSOL_SOLVE_Atv_w - -/* If mode = 3,4,5,6, v and w must not be the same arrays. - If lu1fac has just been used to factorize a symmetric matrix A - (which must be definite or quasi-definite), the factors A = L U - may be regarded as A = LDL', where D = diag(U). In such cases, - the following (faster) solve codes may be used: */ -#define LUSOL_SOLVE_Av_v 7 /* v solves A v = L D L'v = v(input). w is not touched. */ -#define LUSOL_SOLVE_LDLtv_v 8 /* v solves L |D| L'v = v(input). w is not touched. */ - -#define LUSOL_INFORM_RANKLOSS -1 -#define LUSOL_INFORM_LUSUCCESS 0 -#define LUSOL_INFORM_LUSINGULAR 1 -#define LUSOL_INFORM_LUUNSTABLE 2 -#define LUSOL_INFORM_ADIMERR 3 -#define LUSOL_INFORM_ADUPLICATE 4 -#define LUSOL_INFORM_ANEEDMEM 7 /* Set lena >= luparm[LUSOL_IP_MINIMUMLENA] */ -#define LUSOL_INFORM_FATALERR 8 -#define LUSOL_INFORM_NOPIVOT 9 /* No diagonal pivot found with TSP or TDP. */ -#define LUSOL_INFORM_NOMEMLEFT 10 - -#define LUSOL_INFORM_MIN LUSOL_INFORM_RANKLOSS -#define LUSOL_INFORM_MAX LUSOL_INFORM_NOMEMLEFT - -#define LUSOL_INFORM_GETLAST 10 /* Code for LUSOL_informstr. */ -#define LUSOL_INFORM_SERIOUS LUSOL_INFORM_LUUNSTABLE - - -/* Prototypes for call-back functions */ -/* ------------------------------------------------------------------------- */ -typedef void LUSOLlogfunc(void *lp, void *userhandle, char *buf); - - -/* Sparse matrix data */ -typedef struct _LUSOLmat { - LPSREAL *a; - int *lenx, *indr, *indc, *indx; -} LUSOLmat; - - -/* The main LUSOL data record */ -/* ------------------------------------------------------------------------- */ -typedef struct _LUSOLrec { - - /* General data */ - FILE *outstream; /* Output stream, initialized to STDOUT */ - LUSOLlogfunc *writelog; - void *loghandle; - LUSOLlogfunc *debuginfo; - - /* Parameter storage arrays */ - int luparm[LUSOL_IP_LASTITEM + 1]; - LPSREAL parmlu[LUSOL_RP_LASTITEM + 1]; - - /* Arrays of length lena+1 */ - int lena, nelem; - int *indc, *indr; - LPSREAL *a; - - /* Arrays of length maxm+1 (row storage) */ - int maxm, m; - int *lenr, *ip, *iqloc, *ipinv, *locr; - - /* Arrays of length maxn+1 (column storage) */ - int maxn, n; - int *lenc, *iq, *iploc, *iqinv, *locc; - LPSREAL *w, *vLU6L; - - /* List of singular columns, with dynamic size allocation */ - int *isingular; - - /* Extra arrays of length n for TCP and keepLU == FALSE */ - LPSREAL *Ha, *diagU; - int *Hj, *Hk; - - /* Extra arrays of length m for TRP*/ - LPSREAL *amaxr; - - /* Extra array for L0 and U stored by row/column for faster btran/ftran */ - LUSOLmat *L0; - LUSOLmat *U; - - /* Miscellaneous data */ - int expanded_a; - int replaced_c; - int replaced_r; - -} LUSOLrec; - - -LUSOLrec *LUSOL_create(FILE *outstream, int msgfil, int pivotmodel, int updatelimit); -MYBOOL LUSOL_sizeto(LUSOLrec *LUSOL, int init_r, int init_c, int init_a); -MYBOOL LUSOL_assign(LUSOLrec *LUSOL, int iA[], int jA[], LPSREAL Aij[], - int nzcount, MYBOOL istriplet); -void LUSOL_clear(LUSOLrec *LUSOL, MYBOOL nzonly); -void LUSOL_free(LUSOLrec *LUSOL); - -LUSOLmat *LUSOL_matcreate(int dim, int nz); -void LUSOL_matfree(LUSOLmat **mat); - -int LUSOL_loadColumn(LUSOLrec *LUSOL, int iA[], int jA, LPSREAL Aij[], int nzcount, int offset1); -void LUSOL_setpivotmodel(LUSOLrec *LUSOL, int pivotmodel, int initlevel); -int LUSOL_factorize(LUSOLrec *LUSOL); -int LUSOL_replaceColumn(LUSOLrec *LUSOL, int jcol, LPSREAL v[]); - -MYBOOL LUSOL_tightenpivot(LUSOLrec *LUSOL); -MYBOOL LUSOL_addSingularity(LUSOLrec *LUSOL, int singcol, int *inform); -int LUSOL_getSingularity(LUSOLrec *LUSOL, int singitem); -int LUSOL_findSingularityPosition(LUSOLrec *LUSOL, int singcol); - -char *LUSOL_pivotLabel(LUSOLrec *LUSOL); -char *LUSOL_informstr(LUSOLrec *LUSOL, int inform); -LPSREAL LUSOL_vecdensity(LUSOLrec *LUSOL, LPSREAL V[]); -void LUSOL_report(LUSOLrec *LUSOL, int msglevel, char *format, ...); -void LUSOL_timer(LUSOLrec *LUSOL, int timerid, char *text); - -int LUSOL_ftran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[], MYBOOL prepareupdate); -int LUSOL_btran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[]); - -void LU1FAC(LUSOLrec *LUSOL, int *INFORM); -MYBOOL LU1L0(LUSOLrec *LUSOL, LUSOLmat **mat, int *inform); -void LU6SOL(LUSOLrec *LUSOL, int MODE, LPSREAL V[], LPSREAL W[], int NZidx[], int *INFORM); -void LU8RPC(LUSOLrec *LUSOL, int MODE1, int MODE2, - int JREP, LPSREAL V[], LPSREAL W[], - int *INFORM, LPSREAL *DIAG, LPSREAL *VNORM); - -void LUSOL_dump(FILE *output, LUSOLrec *LUSOL); - - -void print_L0(LUSOLrec *LUSOL); - - -#endif /* HEADER_LUSOL */ diff --git a/src/lpsolve/headers/include/mmio.h b/src/lpsolve/headers/include/mmio.h deleted file mode 100644 index c72f1735..00000000 --- a/src/lpsolve/headers/include/mmio.h +++ /dev/null @@ -1,134 +0,0 @@ -/* -* Matrix Market I/O library for ANSI C -* -* See http://math.nist.gov/MatrixMarket for details. -* -* -*/ - -#ifndef MM_IO_H -#define MM_IO_H - -#define MM_MAX_LINE_LENGTH 1025 -#define MatrixMarketBanner "%%MatrixMarket" -#define MM_MAX_TOKEN_LENGTH 64 - -typedef char MM_typecode[4]; - -char *mm_typecode_to_str(MM_typecode matcode); - -int mm_read_banner(FILE *f, MM_typecode *matcode); -int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz); -int mm_read_mtx_array_size(FILE *f, int *M, int *N); - -int mm_write_banner(FILE *f, MM_typecode matcode); -int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz); -int mm_write_mtx_array_size(FILE *f, int M, int N); - - -/********************* MM_typecode query fucntions ***************************/ - -#define mm_is_matrix(typecode) ((typecode)[0]=='M') - -#define mm_is_sparse(typecode) ((typecode)[1]=='C') -#define mm_is_coordinate(typecode)((typecode)[1]=='C') -#define mm_is_dense(typecode) ((typecode)[1]=='A') -#define mm_is_array(typecode) ((typecode)[1]=='A') - -#define mm_is_complex(typecode) ((typecode)[2]=='C') -#define mm_is_real(typecode) ((typecode)[2]=='R') -#define mm_is_pattern(typecode) ((typecode)[2]=='P') -#define mm_is_integer(typecode) ((typecode)[2]=='I') - -#define mm_is_symmetric(typecode)((typecode)[3]=='S') -#define mm_is_general(typecode) ((typecode)[3]=='G') -#define mm_is_skew(typecode) ((typecode)[3]=='K') -#define mm_is_hermitian(typecode)((typecode)[3]=='H') - -int mm_is_valid(MM_typecode matcode); /* too complex for a macro */ - - -/********************* MM_typecode modify fucntions ***************************/ - -#define mm_set_matrix(typecode) ((*typecode)[0]='M') -#define mm_set_coordinate(typecode) ((*typecode)[1]='C') -#define mm_set_array(typecode) ((*typecode)[1]='A') -#define mm_set_dense(typecode) mm_set_array(typecode) -#define mm_set_sparse(typecode) mm_set_coordinate(typecode) - -#define mm_set_complex(typecode)((*typecode)[2]='C') -#define mm_set_real(typecode) ((*typecode)[2]='R') -#define mm_set_pattern(typecode)((*typecode)[2]='P') -#define mm_set_integer(typecode)((*typecode)[2]='I') - - -#define mm_set_symmetric(typecode)((*typecode)[3]='S') -#define mm_set_general(typecode)((*typecode)[3]='G') -#define mm_set_skew(typecode) ((*typecode)[3]='K') -#define mm_set_hermitian(typecode)((*typecode)[3]='H') - -#define mm_clear_typecode(typecode) ((*typecode)[0]=(*typecode)[1]= \ - (*typecode)[2]=' ',(*typecode)[3]='G') - -#define mm_initialize_typecode(typecode) mm_clear_typecode(typecode) - - -/********************* Matrix Market error codes ***************************/ - - -#define MM_COULD_NOT_READ_FILE 11 -#define MM_PREMATURE_EOF 12 -#define MM_NOT_MTX 13 -#define MM_NO_HEADER 14 -#define MM_UNSUPPORTED_TYPE 15 -#define MM_LINE_TOO_LONG 16 -#define MM_COULD_NOT_WRITE_FILE 17 - - -/******************** Matrix Market internal definitions ******************** - - MM_matrix_typecode: 4-character sequence - - ojbect sparse/ data storage - dense type scheme - - string position: [0] [1] [2] [3] - - Matrix typecode: M(atrix) C(oord) R(eal) G(eneral) - A(array) C(omplex) H(ermitian) - P(attern) S(ymmetric) - I(nteger) K(kew) - - ***********************************************************************/ - -#define MM_MTX_STR "matrix" -#define MM_ARRAY_STR "array" -#define MM_DENSE_STR "array" -#define MM_COORDINATE_STR "coordinate" -#define MM_SPARSE_STR "coordinate" -#define MM_COMPLEX_STR "complex" -#define MM_REAL_STR "real" -#define MM_INT_STR "integer" -#define MM_GENERAL_STR "general" -#define MM_SYMM_STR "symmetric" -#define MM_HERM_STR "hermitian" -#define MM_SKEW_STR "skew-symmetric" -#define MM_PATTERN_STR "pattern" - - -/* high level routines */ - -int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], - double val[], MM_typecode matcode); -int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], - double val[], MM_typecode matcode); -int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *img, - MM_typecode matcode); - -#ifndef R_EMBEDDED_LPSOLVE -int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, - double **val_, int **I_, int **J_); -#endif - - -#endif diff --git a/src/lpsolve/headers/include/myblas.h b/src/lpsolve/headers/include/myblas.h deleted file mode 100644 index 4408e7be..00000000 --- a/src/lpsolve/headers/include/myblas.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef HEADER_myblas -#define HEADER_myblas - -/* ************************************************************************ */ -/* BLAS function interface with local and external loadable versions */ -/* Author: Kjell Eikland */ -/* Version: Initial version spring 2004 */ -/* Licence: LGPL */ -/* ************************************************************************ */ -/* Changes: 19 September 2004 Moved function pointer variable */ -/* declarations from myblas.h to myblas.c */ -/* to avoid linker problems with the Mac. */ -/* 20 April 2005 Modified all double types to REAL to self- */ -/* adjust to global settings. Note that BLAS */ -/* as of now does not have double double. */ -/* 11/11/2014 Modified for R package. */ -/* ************************************************************************ */ - -#define BLAS_BASE 1 -#include "commonlib.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/* ************************************************************************ */ -/* BLAS functions */ -/* ************************************************************************ */ - -void init_BLAS(void); -MYBOOL is_nativeBLAS(void); -MYBOOL load_BLAS(char *libname); -MYBOOL unload_BLAS(void); - -/* ************************************************************************ */ -/* User-callable BLAS definitions (C base 1) */ -/* ************************************************************************ */ - -void lps_dscal ( int n, LPSREAL da, LPSREAL *dx, int incx ); -void lps_dcopy ( int n, LPSREAL *dx, int incx, LPSREAL *dy, int incy ); -void lps_daxpy ( int n, LPSREAL da, LPSREAL *dx, int incx, LPSREAL *dy, int incy ); -int lps_idamax ( int n, LPSREAL *x, int is ); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/lpsolve/headers/include/yacc_read.h b/src/lpsolve/headers/include/yacc_read.h deleted file mode 100644 index 1deace4d..00000000 --- a/src/lpsolve/headers/include/yacc_read.h +++ /dev/null @@ -1,57 +0,0 @@ -/* prototypes of functions used in the parser */ - -#include - -#ifndef __READ_H__ -#define __READ_H__ - -struct _tmp_store_struct -{ - char *name; - int row; - LPSREAL value; - LPSREAL rhs_value; - short relat; -}; - -typedef struct parse_parm_s -{ - void *scanner; - long lineno; - int Verbose; - jmp_buf jump_buf; - long Rows, Columns, Non_zeros, Lin_term_count; - struct rside *First_rside, *rs; - short SOStype; /* SOS type */ - char Ignore_int_decl, int_decl, Ignore_sec_decl, Ignore_free_decl, sos_decl, Maximise; - hashtable *Hash_tab, *Hash_constraints; - struct structcoldata *coldata; - struct structSOS *FirstSOS, *LastSOS; - struct _tmp_store_struct tmp_store; - char *title; - short *relat; - void *parse_vars; -} parse_parm; - -void lex_fatal_error(parse_parm *, void *, char *); -int set_title(parse_parm *pp, char *name); -int add_constraint_name(parse_parm *pp, char *name); -int store_re_op(parse_parm *pp, char OP, int HadConstraint, int HadVar, int Had_lineair_sum); -void null_tmp_store(parse_parm *pp, int init_Lin_term_count); -int store_bounds(parse_parm *pp, int warn); -void storevarandweight(parse_parm *pp, char *name); -int set_sos_type(parse_parm *pp, int SOStype); -int set_sos_weight(parse_parm *pp, double weight, int sos_decl); -int set_sec_threshold(parse_parm *pp, char *name, LPSREAL threshold); -int rhs_store(parse_parm *pp, LPSREAL value, int HadConstraint, int HadVar, int Had_lineair_sum); -int var_store(parse_parm *pp, char *var, LPSREAL value, int HadConstraint, int HadVar, int Had_lineair_sum); -int negate_constraint(parse_parm *pp); -void add_row(parse_parm *pp); -void add_sos_row(parse_parm *pp, short SOStype); - -void read_error(parse_parm *, void *, char *); -void check_int_sec_sos_free_decl(parse_parm *, int, int, int, int); -lprec *yacc_read(lprec *lp, int verbose, char *lp_name, int (*parse) (parse_parm *pp), parse_parm *pp, void (*delete_allocated_memory) (parse_parm *pp)); - -#define set_obj_dir(pp, maximise) pp->Maximise = maximise -#endif diff --git a/src/lpsolve/headers/run_headers/lp_MDO.h b/src/lpsolve/headers/run_headers/lp_MDO.h deleted file mode 100644 index 84753638..00000000 --- a/src/lpsolve/headers/run_headers/lp_MDO.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef HEADER_MDO -#define HEADER_MDO - -#include "lp_types.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -int __WINAPI getMDO(lprec *lp, MYBOOL *usedpos, int *colorder, int *size, MYBOOL symmetric); - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_MDO */ - diff --git a/src/lpsolve/headers/run_headers/lp_wlp.h b/src/lpsolve/headers/run_headers/lp_wlp.h deleted file mode 100644 index 010858ba..00000000 --- a/src/lpsolve/headers/run_headers/lp_wlp.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef HEADER_lp_lp -#define HEADER_lp_lp - -#include "lp_types.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Put function headers here */ -MYBOOL LP_writefile(lprec *lp, char *filename); -MYBOOL LP_writehandle(lprec *lp, FILE *output); - - -#ifdef __cplusplus - } -#endif - -#endif /* HEADER_lp_lp */ - From 7548826612d95e497fb46abcadb4ad2a3ac8d560 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Thu, 29 Feb 2024 15:08:56 +0200 Subject: [PATCH 14/17] Resolve deprecated ftime warning --- src/lpSolve/src/Makefile | 3 +- src/lpSolve_patches/commonlib.c | 820 ++++++++++++++++++++++++++++++++ 2 files changed, 822 insertions(+), 1 deletion(-) create mode 100644 src/lpSolve_patches/commonlib.c diff --git a/src/lpSolve/src/Makefile b/src/lpSolve/src/Makefile index 4ff5fbc4..8c0ff3a4 100644 --- a/src/lpSolve/src/Makefile +++ b/src/lpSolve/src/Makefile @@ -4,7 +4,8 @@ LP_SOLVE_CPPFLAGS=$(CPPFLAGS) -I. \ -DPARSER_LP -DINVERSE_ACTIVE=INVERSE_LUSOL \ -DRoleIsExternalInvEngine -LP_SOLVE_SOURCES=colamd.c lp_MDO.c lp_mipbb.c lp_rlp.c mmio.c commonlib.c \ +LP_SOLVE_SOURCES=colamd.c lp_MDO.c lp_mipbb.c lp_rlp.c mmio.c \ + ../../lpSolve_patches/commonlib.c \ lp_MPS.c lp_params.c lp_scale.c lp_SOS.c \ lp_presolve.c lp_simplex.c yacc_read.c ini.c lp_crash.c \ lp_price.c lp_utils.c lp_Hash.c lp_lib.c lp_pricePSE.c \ diff --git a/src/lpSolve_patches/commonlib.c b/src/lpSolve_patches/commonlib.c new file mode 100644 index 00000000..9e4943f9 --- /dev/null +++ b/src/lpSolve_patches/commonlib.c @@ -0,0 +1,820 @@ + +#include + +#ifdef INTEGERTIME +# include +#else +# include +#endif + +#include +#include +#ifdef WIN32 +# include /* Used in file search functions */ +#endif +#include +#include +#include +#include "commonlib.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +#include + + +/* Math operator equivalence function */ +int intpow(int base, int exponent) +{ + int result = 1; + while(exponent > 0) { + result *= base; + exponent--; + } + while(exponent < 0) { + result /= base; + exponent++; + } + return( result ); +} +int mod(int n, int d) +{ + return(n % d); +} + +/* Some string functions */ +void strtoup(char *s) +{ + if(s != NULL) + while (*s) { + *s = toupper(*s); + s++; + } +} +void strtolo(char *s) +{ + if(s != NULL) + while (*s) { + *s = tolower(*s); + s++; + } +} +void strcpyup(char *t, char *s) +{ + if((s != NULL) && (t != NULL)) { + while (*s) { + *t = toupper(*s); + t++; + s++; + } + *t = '\0'; + } +} +void strcpylo(char *t, char *s) +{ + if((s != NULL) && (t != NULL)) { + while (*s) { + *t = tolower(*s); + t++; + s++; + } + *t = '\0'; + } +} + +/* Unix library naming utility function */ +MYBOOL so_stdname(char *stdname, char *descname, int buflen) +{ + char *ptr; + + if((descname == NULL) || (stdname == NULL) || (((int) strlen(descname)) >= buflen - 6)) + return( FALSE ); + + strcpy(stdname, descname); + if((ptr = strrchr(descname, '/')) == NULL) + ptr = descname; + else + ptr++; + stdname[(int) (ptr - descname)] = 0; + if(strncmp(ptr, "lib", 3)) + strcat(stdname, "lib"); + strcat(stdname, ptr); + if(strcmp(stdname + strlen(stdname) - 3, ".so")) + strcat(stdname, ".so"); + return( TRUE ); +} + +/* Return the greatest common divisor of a and b, or -1 if it is + not defined. Return through the pointer arguments the integers + such that gcd(a,b) = c*a + b*d. */ +int gcd(LLONG a, LLONG b, int *c, int *d) +{ + LLONG q,r,t; + int cret,dret,C,D,rval, sgn_a = 1,sgn_b = 1, swap = 0; + + if((a == 0) || (b == 0)) + return( -1 ); + + /* Use local multiplier instances, if necessary */ + if(c == NULL) + c = &cret; + if(d == NULL) + d = &dret; + + /* Normalize so that 0 < a <= b */ + if(a < 0){ + a = -a; + sgn_a = -1; + } + if(b < 0){ + b = -b; + sgn_b = -1; + } + if(b < a){ + t = b; + b = a; + a = t; + swap = 1; + } + + /* Now a <= b and both >= 1. */ + q = b/a; + r = b - a*q; + if(r == 0) { + if(swap){ + *d = 1; + *c = 0; + } + else { + *c = 1; + *d = 0; + } + *c = sgn_a*(*c); + *d = sgn_b*(*d); + return( (int) a ); + } + + rval = gcd(a,r,&C,&D); + if(swap){ + *d = (int) (C-D*q); + *c = D; + } + else { + *d = D; + *c = (int) (C-D*q); + } + *c = sgn_a*(*c); + *d = sgn_b*(*d); + return( rval ); +} + +/* Array search functions */ +int findIndex(int target, int *attributes, int count, int offset) +{ + int focusPos, beginPos, endPos; + int focusAttrib, beginAttrib, endAttrib; + + /* Set starting and ending index offsets */ + beginPos = offset; + endPos = beginPos + count - 1; + if(endPos < beginPos) + return(-1); + + /* Do binary search logic based on a sorted (decending) attribute vector */ + focusPos = (beginPos + endPos) / 2; + beginAttrib = attributes[beginPos]; + focusAttrib = attributes[focusPos]; + endAttrib = attributes[endPos]; + + while(endPos - beginPos > LINEARSEARCH) { + if(beginAttrib == target) { + focusAttrib = beginAttrib; + endPos = beginPos; + } + else if(endAttrib == target) { + focusAttrib = endAttrib; + beginPos = endPos; + } + else if(focusAttrib < target) { + beginPos = focusPos + 1; + beginAttrib = attributes[beginPos]; + focusPos = (beginPos + endPos) / 2; + focusAttrib = attributes[focusPos]; + } + else if(focusAttrib > target) { + endPos = focusPos - 1; + endAttrib = attributes[endPos]; + focusPos = (beginPos + endPos) / 2; + focusAttrib = attributes[focusPos]; + } + else { + beginPos = focusPos; + endPos = focusPos; + } + } + + /* Do linear (unsorted) search logic */ + if(endPos - beginPos <= LINEARSEARCH) { + + /* CPU intensive loop; provide alternative evaluation models */ +#if defined DOFASTMATH + /* Do fast pointer arithmetic */ + int *attptr = attributes + beginPos; + while((beginPos < endPos) && ((*attptr) < target)) { + beginPos++; + attptr++; + } + focusAttrib = (*attptr); +#else + /* Do traditional indexed access */ + focusAttrib = attributes[beginPos]; + while((beginPos < endPos) && (focusAttrib < target)) { + beginPos++; + focusAttrib = attributes[beginPos]; + } +#endif + } + + /* Return the index if a match was found, or signal failure with a -1 */ + if(focusAttrib == target) /* Found; return retrieval index */ + return(beginPos); + else if(focusAttrib > target) /* Not found; last item */ + return(-beginPos); + else if(beginPos > offset+count-1) + return(-(endPos+1)); /* Not found; end of list */ + else + return(-(beginPos+1)); /* Not found; intermediate point */ + +} +int findIndexEx(void *target, void *attributes, int count, int offset, int recsize, findCompare_func findCompare, MYBOOL ascending) +{ + int focusPos, beginPos, endPos, compare, order; + void *focusAttrib, *beginAttrib, *endAttrib; + + /* Set starting and ending index offsets */ + beginPos = offset; + endPos = beginPos + count - 1; + if(endPos < beginPos) + return(-1); + order = (ascending ? -1 : 1); + + /* Do binary search logic based on a sorted attribute vector */ + focusPos = (beginPos + endPos) / 2; + beginAttrib = CMP_ATTRIBUTES(beginPos); + focusAttrib = CMP_ATTRIBUTES(focusPos); + endAttrib = CMP_ATTRIBUTES(endPos); + + compare = 0; + while(endPos - beginPos > LINEARSEARCH) { + if(findCompare(target, beginAttrib) == 0) { + focusAttrib = beginAttrib; + endPos = beginPos; + } + else if(findCompare(target, endAttrib) == 0) { + focusAttrib = endAttrib; + beginPos = endPos; + } + else { + compare = findCompare(target, focusAttrib)*order; + if(compare < 0) { + beginPos = focusPos + 1; + beginAttrib = CMP_ATTRIBUTES(beginPos); + focusPos = (beginPos + endPos) / 2; + focusAttrib = CMP_ATTRIBUTES(focusPos); + } + else if(compare > 0) { + endPos = focusPos - 1; + endAttrib = CMP_ATTRIBUTES(endPos); + focusPos = (beginPos + endPos) / 2; + focusAttrib = CMP_ATTRIBUTES(focusPos); + } + else { + beginPos = focusPos; + endPos = focusPos; + } + } + } + + /* Do linear (unsorted) search logic */ + if(endPos - beginPos <= LINEARSEARCH) { + + /* Do traditional indexed access */ + focusAttrib = CMP_ATTRIBUTES(beginPos); + if(beginPos == endPos) + compare = findCompare(target, focusAttrib)*order; + else + while((beginPos < endPos) && + ((compare = findCompare(target, focusAttrib)*order) < 0)) { + beginPos++; + focusAttrib = CMP_ATTRIBUTES(beginPos); + } + } + + /* Return the index if a match was found, or signal failure with a -1 */ + if(compare == 0) /* Found; return retrieval index */ + return(beginPos); + else if(compare > 0) /* Not found; last item */ + return(-beginPos); + else if(beginPos > offset+count-1) + return(-(endPos+1)); /* Not found; end of list */ + else + return(-(beginPos+1)); /* Not found; intermediate point */ + +} + +/* Simple sorting and searching comparison "operators" */ +int CMP_CALLMODEL compareCHAR(const void *current, const void *candidate) +{ + return( CMP_COMPARE( *(char *) current, *(char *) candidate ) ); +} +int CMP_CALLMODEL compareINT(const void *current, const void *candidate) +{ + return( CMP_COMPARE( *(int *) current, *(int *) candidate ) ); +} +int CMP_CALLMODEL compareREAL(const void *current, const void *candidate) +{ + return( CMP_COMPARE( *(REAL *) current, *(REAL *) candidate ) ); +} + +/* Heap sort function (procedurally based on the Numerical Recipes version, + but expanded and generalized to hande any object with the use of + qsort-style comparison operator). An expanded version is also implemented, + where interchanges are reflected in a caller-initialized integer "tags" list. */ +void hpsort(void *attributes, int count, int offset, int recsize, MYBOOL descending, findCompare_func findCompare) +{ + register int i, j, k, ir, order; + register char *hold, *base; + char *save; + + if(count < 2) + return; + offset -= 1; + attributes = CMP_ATTRIBUTES(offset); + base = CMP_ATTRIBUTES(1); + save = (char *) malloc(recsize); + if(descending) + order = -1; + else + order = 1; + + k = (count >> 1) + 1; + ir = count; + + for(;;) { + if(k > 1) { + MEMCOPY(save, CMP_ATTRIBUTES(--k), recsize); + } + else { + hold = CMP_ATTRIBUTES(ir); + MEMCOPY(save, hold, recsize); + MEMCOPY(hold, base, recsize); + if(--ir == 1) { + MEMCOPY(base, save, recsize); + break; + } + } + + i = k; + j = k << 1; + while(j <= ir) { + hold = CMP_ATTRIBUTES(j); + if( (j < ir) && (findCompare(hold, CMP_ATTRIBUTES(j+1))*order < 0) ) { + hold += recsize; + j++; + } + if(findCompare(save, hold)*order < 0) { + MEMCOPY(CMP_ATTRIBUTES(i), hold, recsize); + i = j; + j <<= 1; + } + else + break; + } + MEMCOPY(CMP_ATTRIBUTES(i), save, recsize); + } + + FREE(save); +} +void hpsortex(void *attributes, int count, int offset, int recsize, MYBOOL descending, findCompare_func findCompare, int *tags) +{ + if(count < 2) + return; + if(tags == NULL) { + hpsort(attributes, count, offset, recsize, descending, findCompare); + return; + } + else { + register int i, j, k, ir, order; + register char *hold, *base; + char *save; + int savetag; + + offset -= 1; + attributes = CMP_ATTRIBUTES(offset); + tags += offset; + base = CMP_ATTRIBUTES(1); + save = (char *) malloc(recsize); + if(descending) + order = -1; + else + order = 1; + + k = (count >> 1) + 1; + ir = count; + + for(;;) { + if(k > 1) { + MEMCOPY(save, CMP_ATTRIBUTES(--k), recsize); + savetag = tags[k]; + } + else { + hold = CMP_ATTRIBUTES(ir); + MEMCOPY(save, hold, recsize); + MEMCOPY(hold, base, recsize); + savetag = tags[ir]; + tags[ir] = tags[1]; + if(--ir == 1) { + MEMCOPY(base, save, recsize); + tags[1] = savetag; + break; + } + } + + i = k; + j = k << 1; + while(j <= ir) { + hold = CMP_ATTRIBUTES(j); + if( (j < ir) && (findCompare(hold, CMP_ATTRIBUTES(j+1))*order < 0) ) { + hold += recsize; + j++; + } + if(findCompare(save, hold)*order < 0) { + MEMCOPY(CMP_ATTRIBUTES(i), hold, recsize); + tags[i] = tags[j]; + i = j; + j <<= 1; + } + else + break; + } + MEMCOPY(CMP_ATTRIBUTES(i), save, recsize); + tags[i] = savetag; + } + + FREE(save); + } +} + + +/* This is a "specialized generic" version of C.A.R Hoare's Quick Sort algorithm. + It will handle arrays that are already sorted, and arrays with duplicate keys. + The implementation here requires the user to pass a comparison operator and + assumes that the array passed has the QSORTrec format, which i.a. includes + the ability for to do linked list sorting. If the passed comparison operator + is NULL, the comparison is assumed to be for integers. */ +#define QS_IS_switch 4 /* Threshold for switching to insertion sort */ + +void QS_swap(UNIONTYPE QSORTrec a[], int i, int j) +{ + UNIONTYPE QSORTrec T = a[i]; + a[i] = a[j]; + a[j] = T; +} +int QS_addfirst(UNIONTYPE QSORTrec a[], void *mydata) +{ + a[0].pvoid2.ptr = mydata; + return( 0 ); +} +int QS_append(UNIONTYPE QSORTrec a[], int ipos, void *mydata) +{ + if(ipos <= 0) + ipos = QS_addfirst(a, mydata); + else + a[ipos].pvoid2.ptr = mydata; + return( ipos ); +} +void QS_replace(UNIONTYPE QSORTrec a[], int ipos, void *mydata) +{ + a[ipos].pvoid2.ptr = mydata; +} +void QS_insert(UNIONTYPE QSORTrec a[], int ipos, void *mydata, int epos) +{ + for(; epos > ipos; epos--) + a[epos] = a[epos-1]; + a[ipos].pvoid2.ptr = mydata; +} +void QS_delete(UNIONTYPE QSORTrec a[], int ipos, int epos) +{ + for(; epos > ipos; epos--) + a[epos] = a[epos-1]; +} +int QS_sort(UNIONTYPE QSORTrec a[], int l, int r, findCompare_func findCompare) +{ + register int i, j, nmove = 0; + UNIONTYPE QSORTrec v; + + /* Perform the a fast QuickSort */ + if((r-l) > QS_IS_switch) { + i = (r+l)/2; + + /* Tri-Median Method */ + if(findCompare((char *) &a[l], (char *) &a[i]) > 0) + { nmove++; QS_swap(a,l,i); } + if(findCompare((char *) &a[l], (char *) &a[r]) > 0) + { nmove++; QS_swap(a,l,r); } + if(findCompare((char *) &a[i], (char *) &a[r]) > 0) + { nmove++; QS_swap(a,i,r); } + + j = r-1; + QS_swap(a,i,j); + i = l; + v = a[j]; + for(;;) { + while(findCompare((char *) &a[++i], (char *) &v) < 0); + while(findCompare((char *) &a[--j], (char *) &v) > 0); + if(j < i) break; + nmove++; QS_swap (a,i,j); + } + nmove++; QS_swap(a,i,r-1); + nmove += QS_sort(a,l,j,findCompare); + nmove += QS_sort(a,i+1,r,findCompare); + } + return( nmove ); +} +int QS_finish(UNIONTYPE QSORTrec a[], int lo0, int hi0, findCompare_func findCompare) +{ + int i, j, nmove = 0; + UNIONTYPE QSORTrec v; + + /* This is actually InsertionSort, which is faster for local sorts */ + for(i = lo0+1; i <= hi0; i++) { + + /* Save bottom-most item */ + v = a[i]; + + /* Shift down! */ + j = i; + while ((j > lo0) && (findCompare((char *) &a[j-1], (char *) &v) > 0)) { + a[j] = a[j-1]; + j--; + nmove++; + } + + /* Store bottom-most item at the top */ + a[j] = v; + } + return( nmove ); +} +MYBOOL QS_execute(UNIONTYPE QSORTrec a[], int count, findCompare_func findCompare, int *nswaps) +{ + int iswaps = 0; + + /* Check and initialize */ + if(count <= 1) + goto Finish; + count--; + + /* Perform sort */ + iswaps = QS_sort(a, 0, count, findCompare); +#if QS_IS_switch > 0 + iswaps += QS_finish(a, 0, count, findCompare); +#endif + +Finish: + if(nswaps != NULL) + *nswaps = iswaps; + return( TRUE ); +} + + + +/* Simple specialized bubble/insertion sort functions */ +int sortByREAL(int *item, REAL *weight, int size, int offset, MYBOOL unique) +{ + int i, ii, saveI; + REAL saveW; + + for(i = 1; i < size; i++) { + ii = i+offset-1; + while ((ii >= offset) && (weight[ii] >= weight[ii+1])) { + if(weight[ii] == weight[ii+1]) { + if(unique) + return(item[ii]); + } + else { + saveI = item[ii]; + saveW = weight[ii]; + item[ii] = item[ii+1]; + weight[ii] = weight[ii+1]; + item[ii+1] = saveI; + weight[ii+1] = saveW; + } + ii--; + } + } + return(0); +} +int sortByINT(int *item, int *weight, int size, int offset, MYBOOL unique) +{ + int i, ii, saveI; + int saveW; + + for(i = 1; i < size; i++) { + ii = i+offset-1; + while ((ii >= offset) && (weight[ii] >= weight[ii+1])) { + if(weight[ii] == weight[ii+1]) { + if(unique) + return(item[ii]); + } + else { + saveI = item[ii]; + saveW = weight[ii]; + item[ii] = item[ii+1]; + weight[ii] = weight[ii+1]; + item[ii+1] = saveI; + weight[ii+1] = saveW; + } + ii--; + } + } + return(0); +} +REAL sortREALByINT(REAL *item, int *weight, int size, int offset, MYBOOL unique) +{ + int i, ii, saveW; + REAL saveI; + + for(i = 1; i < size; i++) { + ii = i+offset-1; + while ((ii >= offset) && (weight[ii] >= weight[ii+1])) { + if(weight[ii] == weight[ii+1]) { + if(unique) + return(item[ii]); + } + else { + saveI = item[ii]; + saveW = weight[ii]; + item[ii] = item[ii+1]; + weight[ii] = weight[ii+1]; + item[ii+1] = saveI; + weight[ii+1] = saveW; + } + ii--; + } + } + return(0); +} + + +/* Time and message functions */ +double timeNow(void) +{ +#ifdef INTEGERTIME + return((double)time(NULL)); +#elif defined CLOCKTIME + return((double)clock()/CLOCKS_PER_SEC /* CLK_TCK */); +#else + return((double)0); +#endif +} + + +/* Miscellaneous reporting functions */ + +/* List a vector of INT values for the given index range */ +void blockWriteINT(FILE *output, char *label, int *myvector, int first, int last) +{ + int i, k = 0; + + fprintf(output, "%s", label); + fprintf(output, "\n"); + for(i = first; i <= last; i++) { + fprintf(output, " %5d", myvector[i]); + k++; + if(k % 12 == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(k % 12 != 0) + fprintf(output, "\n"); +} + +/* List a vector of MYBOOL values for the given index range */ +void blockWriteBOOL(FILE *output, char *label, MYBOOL *myvector, int first, int last, MYBOOL asRaw) +{ + int i, k = 0; + + fprintf(output, "%s", label); + fprintf(output, "\n"); + for(i = first; i <= last; i++) { + if(asRaw) + fprintf(output, " %1d", myvector[i]); + else + fprintf(output, " %5s", my_boolstr(myvector[i])); + k++; + if(k % 36 == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(k % 36 != 0) + fprintf(output, "\n"); +} + +/* List a vector of REAL values for the given index range */ +void blockWriteREAL(FILE *output, char *label, REAL *myvector, int first, int last) +{ + int i, k = 0; + + fprintf(output, "%s", label); + fprintf(output, "\n"); + for(i = first; i <= last; i++) { + fprintf(output, " %18g", myvector[i]); + k++; + if(k % 4 == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(k % 4 != 0) + fprintf(output, "\n"); +} + + +/* CONSOLE vector and matrix printing routines */ +void printvec( int n, REAL *x, int modulo ) +{ + int i; + + if (modulo <= 0) modulo = 5; + for (i = 1; i<=n; i++) { + if(mod(i, modulo) == 1) + Rprintf("\n%2d:%12g", i, x[i]); + else + Rprintf(" %2d:%12g", i, x[i]); + } + if(i % modulo != 0) Rprintf("\n"); +} + + +void printmatUT( int size, int n, REAL *U, int modulo) +{ + int i, ll; + ll = 0; + for(i = 1; i<=n; i++) { + printvec(n-i+1, &U[ll], modulo); + ll += size-i+1; + } +} + + +void printmatSQ( int size, int n, REAL *X, int modulo) +{ + int i, ll; + ll = 0; + for(i = 1; i<=n; i++) { + printvec(n, &X[ll], modulo); + ll += size; + } +} + +/* Miscellaneous file functions */ +#if defined _MSC_VER +/* Check MS versions before 7 */ +#if _MSC_VER < 1300 +# define intptr_t long +#endif + +int fileCount( char *filemask ) +{ + struct _finddata_t c_file; + intptr_t hFile; + int count = 0; + + /* Find first .c file in current directory */ + if( (hFile = _findfirst( filemask, &c_file )) == -1L ) + ; + /* Iterate over all matching names */ + else { + while( _findnext( hFile, &c_file ) == 0 ) + count++; + _findclose( hFile ); + } + return( count ); +} +MYBOOL fileSearchPath( char *envvar, char *searchfile, char *foundpath ) +{ + char pathbuffer[_MAX_PATH]; + + _searchenv( searchfile, envvar, pathbuffer ); + if(pathbuffer[0] == '\0') + return( FALSE ); + else { + if(foundpath != NULL) + strcpy(foundpath, pathbuffer); + return( TRUE ); + } +} +#endif From ee66fb408814f43b9b4c51766f65a71268de4d0c Mon Sep 17 00:00:00 2001 From: vfisikop Date: Thu, 29 Feb 2024 15:24:53 +0200 Subject: [PATCH 15/17] Move lpSolve to external directory and update copyrights --- inst/COPYRIGHTS | 6 +- src/Makevars | 10 +- src/Makevars.win | 10 +- src/{ => external}/lpSolve/src/Makefile | 0 src/{ => external}/lpSolve/src/Makevars | 0 src/{ => external}/lpSolve/src/Makevars.win | 0 src/{ => external}/lpSolve/src/colamd.c | 0 src/{ => external}/lpSolve/src/colamd.h | 0 src/{ => external}/lpSolve/src/commonlib.c | 0 src/{ => external}/lpSolve/src/commonlib.h | 0 src/{ => external}/lpSolve/src/declare.h | 0 src/{ => external}/lpSolve/src/fortify.h | 0 src/{ => external}/lpSolve/src/hbio.c | 2 +- src/{ => external}/lpSolve/src/hbio.h | 0 src/{ => external}/lpSolve/src/ini.c | 0 src/{ => external}/lpSolve/src/ini.h | 0 src/{ => external}/lpSolve/src/init.c | 0 src/{ => external}/lpSolve/src/isfixedvar.c | 0 src/{ => external}/lpSolve/src/lp_BFP.h | 0 src/{ => external}/lpSolve/src/lp_BFP1.h | 0 src/{ => external}/lpSolve/src/lp_BFP2.h | 0 src/{ => external}/lpSolve/src/lp_Hash.c | 0 src/{ => external}/lpSolve/src/lp_Hash.h | 0 src/{ => external}/lpSolve/src/lp_LUSOL.c | 0 src/{ => external}/lpSolve/src/lp_LUSOL.h | 0 src/{ => external}/lpSolve/src/lp_MDO.c | 0 src/{ => external}/lpSolve/src/lp_MDO.h | 0 src/{ => external}/lpSolve/src/lp_MPS.c | 0 src/{ => external}/lpSolve/src/lp_MPS.h | 0 src/{ => external}/lpSolve/src/lp_SOS.c | 0 src/{ => external}/lpSolve/src/lp_SOS.h | 0 src/{ => external}/lpSolve/src/lp_crash.c | 0 src/{ => external}/lpSolve/src/lp_crash.h | 0 src/{ => external}/lpSolve/src/lp_explicit.h | 0 src/{ => external}/lpSolve/src/lp_fortify.h | 0 src/{ => external}/lpSolve/src/lp_lib.c | 0 src/{ => external}/lpSolve/src/lp_lib.h | 0 src/{ => external}/lpSolve/src/lp_matrix.c | 0 src/{ => external}/lpSolve/src/lp_matrix.h | 0 src/{ => external}/lpSolve/src/lp_mipbb.c | 0 src/{ => external}/lpSolve/src/lp_mipbb.h | 0 src/{ => external}/lpSolve/src/lp_params.c | 0 src/{ => external}/lpSolve/src/lp_presolve.c | 0 src/{ => external}/lpSolve/src/lp_presolve.h | 0 src/{ => external}/lpSolve/src/lp_price.c | 0 src/{ => external}/lpSolve/src/lp_price.h | 0 src/{ => external}/lpSolve/src/lp_pricePSE.c | 0 src/{ => external}/lpSolve/src/lp_pricePSE.h | 0 src/{ => external}/lpSolve/src/lp_report.c | 0 src/{ => external}/lpSolve/src/lp_report.h | 0 src/{ => external}/lpSolve/src/lp_rlp.c | 0 src/{ => external}/lpSolve/src/lp_rlp.h | 0 src/{ => external}/lpSolve/src/lp_scale.c | 0 src/{ => external}/lpSolve/src/lp_scale.h | 0 src/{ => external}/lpSolve/src/lp_simplex.c | 0 src/{ => external}/lpSolve/src/lp_simplex.h | 0 src/{ => external}/lpSolve/src/lp_types.h | 0 src/{ => external}/lpSolve/src/lp_utils.c | 0 src/{ => external}/lpSolve/src/lp_utils.h | 0 src/{ => external}/lpSolve/src/lp_wlp.c | 0 src/{ => external}/lpSolve/src/lp_wlp.h | 0 src/{ => external}/lpSolve/src/lpkit.h | 0 src/{ => external}/lpSolve/src/lpslink56.c | 0 src/{ => external}/lpSolve/src/lpsolve.h | 0 src/{ => external}/lpSolve/src/lusol.c | 0 src/{ => external}/lpSolve/src/lusol.h | 0 src/{ => external}/lpSolve/src/lusol1.h | 0 src/{ => external}/lpSolve/src/lusol2.h | 0 src/{ => external}/lpSolve/src/lusol6a.h | 0 src/{ => external}/lpSolve/src/lusol6l0.h | 0 src/{ => external}/lpSolve/src/lusol6u.h | 0 src/{ => external}/lpSolve/src/lusol7a.h | 0 src/{ => external}/lpSolve/src/lusol8a.h | 0 src/{ => external}/lpSolve/src/lusolio.c | 0 src/{ => external}/lpSolve/src/lusolio.h | 0 src/{ => external}/lpSolve/src/mmio.c | 0 src/{ => external}/lpSolve/src/mmio.h | 0 src/{ => external}/lpSolve/src/myblas.c | 0 src/{ => external}/lpSolve/src/myblas.h | 0 src/{ => external}/lpSolve/src/sparselib.c | 0 src/{ => external}/lpSolve/src/sparselib.h | 0 src/{ => external}/lpSolve/src/ufortify.h | 0 src/{ => external}/lpSolve/src/yacc_read.c | 0 src/{ => external}/lpSolve/src/yacc_read.h | 0 .../lpSolve_patches/commonlib.c | 0 src/lpSolve/DESCRIPTION | 19 -- src/lpSolve/MD5 | 94 ------ src/lpSolve/NAMESPACE | 3 - src/lpSolve/R/lp.R | 273 ------------------ src/lpSolve/R/lp.assign.R | 114 -------- src/lpSolve/R/lp.transport.R | 155 ---------- src/lpSolve/R/make.q8.R | 47 --- src/lpSolve/R/print.lp.R | 11 - src/lpSolve/R/zzz.R | 3 - src/lpSolve/man/lp.Rd | 136 --------- src/lpSolve/man/lp.assign.Rd | 56 ---- src/lpSolve/man/lp.object.Rd | 22 -- src/lpSolve/man/lp.transport.Rd | 84 ------ src/lpSolve/man/make.q8.Rd | 21 -- src/lpSolve/man/print.lp.Rd | 25 -- 100 files changed, 14 insertions(+), 1077 deletions(-) rename src/{ => external}/lpSolve/src/Makefile (100%) rename src/{ => external}/lpSolve/src/Makevars (100%) rename src/{ => external}/lpSolve/src/Makevars.win (100%) rename src/{ => external}/lpSolve/src/colamd.c (100%) rename src/{ => external}/lpSolve/src/colamd.h (100%) rename src/{ => external}/lpSolve/src/commonlib.c (100%) rename src/{ => external}/lpSolve/src/commonlib.h (100%) rename src/{ => external}/lpSolve/src/declare.h (100%) rename src/{ => external}/lpSolve/src/fortify.h (100%) rename src/{ => external}/lpSolve/src/hbio.c (99%) rename src/{ => external}/lpSolve/src/hbio.h (100%) rename src/{ => external}/lpSolve/src/ini.c (100%) rename src/{ => external}/lpSolve/src/ini.h (100%) rename src/{ => external}/lpSolve/src/init.c (100%) rename src/{ => external}/lpSolve/src/isfixedvar.c (100%) rename src/{ => external}/lpSolve/src/lp_BFP.h (100%) rename src/{ => external}/lpSolve/src/lp_BFP1.h (100%) rename src/{ => external}/lpSolve/src/lp_BFP2.h (100%) rename src/{ => external}/lpSolve/src/lp_Hash.c (100%) rename src/{ => external}/lpSolve/src/lp_Hash.h (100%) rename src/{ => external}/lpSolve/src/lp_LUSOL.c (100%) rename src/{ => external}/lpSolve/src/lp_LUSOL.h (100%) rename src/{ => external}/lpSolve/src/lp_MDO.c (100%) rename src/{ => external}/lpSolve/src/lp_MDO.h (100%) rename src/{ => external}/lpSolve/src/lp_MPS.c (100%) rename src/{ => external}/lpSolve/src/lp_MPS.h (100%) rename src/{ => external}/lpSolve/src/lp_SOS.c (100%) rename src/{ => external}/lpSolve/src/lp_SOS.h (100%) rename src/{ => external}/lpSolve/src/lp_crash.c (100%) rename src/{ => external}/lpSolve/src/lp_crash.h (100%) rename src/{ => external}/lpSolve/src/lp_explicit.h (100%) rename src/{ => external}/lpSolve/src/lp_fortify.h (100%) rename src/{ => external}/lpSolve/src/lp_lib.c (100%) rename src/{ => external}/lpSolve/src/lp_lib.h (100%) rename src/{ => external}/lpSolve/src/lp_matrix.c (100%) rename src/{ => external}/lpSolve/src/lp_matrix.h (100%) rename src/{ => external}/lpSolve/src/lp_mipbb.c (100%) rename src/{ => external}/lpSolve/src/lp_mipbb.h (100%) rename src/{ => external}/lpSolve/src/lp_params.c (100%) rename src/{ => external}/lpSolve/src/lp_presolve.c (100%) rename src/{ => external}/lpSolve/src/lp_presolve.h (100%) rename src/{ => external}/lpSolve/src/lp_price.c (100%) rename src/{ => external}/lpSolve/src/lp_price.h (100%) rename src/{ => external}/lpSolve/src/lp_pricePSE.c (100%) rename src/{ => external}/lpSolve/src/lp_pricePSE.h (100%) rename src/{ => external}/lpSolve/src/lp_report.c (100%) rename src/{ => external}/lpSolve/src/lp_report.h (100%) rename src/{ => external}/lpSolve/src/lp_rlp.c (100%) rename src/{ => external}/lpSolve/src/lp_rlp.h (100%) rename src/{ => external}/lpSolve/src/lp_scale.c (100%) rename src/{ => external}/lpSolve/src/lp_scale.h (100%) rename src/{ => external}/lpSolve/src/lp_simplex.c (100%) rename src/{ => external}/lpSolve/src/lp_simplex.h (100%) rename src/{ => external}/lpSolve/src/lp_types.h (100%) rename src/{ => external}/lpSolve/src/lp_utils.c (100%) rename src/{ => external}/lpSolve/src/lp_utils.h (100%) rename src/{ => external}/lpSolve/src/lp_wlp.c (100%) rename src/{ => external}/lpSolve/src/lp_wlp.h (100%) rename src/{ => external}/lpSolve/src/lpkit.h (100%) rename src/{ => external}/lpSolve/src/lpslink56.c (100%) rename src/{ => external}/lpSolve/src/lpsolve.h (100%) rename src/{ => external}/lpSolve/src/lusol.c (100%) rename src/{ => external}/lpSolve/src/lusol.h (100%) rename src/{ => external}/lpSolve/src/lusol1.h (100%) rename src/{ => external}/lpSolve/src/lusol2.h (100%) rename src/{ => external}/lpSolve/src/lusol6a.h (100%) rename src/{ => external}/lpSolve/src/lusol6l0.h (100%) rename src/{ => external}/lpSolve/src/lusol6u.h (100%) rename src/{ => external}/lpSolve/src/lusol7a.h (100%) rename src/{ => external}/lpSolve/src/lusol8a.h (100%) rename src/{ => external}/lpSolve/src/lusolio.c (100%) rename src/{ => external}/lpSolve/src/lusolio.h (100%) rename src/{ => external}/lpSolve/src/mmio.c (100%) rename src/{ => external}/lpSolve/src/mmio.h (100%) rename src/{ => external}/lpSolve/src/myblas.c (100%) rename src/{ => external}/lpSolve/src/myblas.h (100%) rename src/{ => external}/lpSolve/src/sparselib.c (100%) rename src/{ => external}/lpSolve/src/sparselib.h (100%) rename src/{ => external}/lpSolve/src/ufortify.h (100%) rename src/{ => external}/lpSolve/src/yacc_read.c (100%) rename src/{ => external}/lpSolve/src/yacc_read.h (100%) rename src/{ => external}/lpSolve_patches/commonlib.c (100%) delete mode 100644 src/lpSolve/DESCRIPTION delete mode 100644 src/lpSolve/MD5 delete mode 100644 src/lpSolve/NAMESPACE delete mode 100644 src/lpSolve/R/lp.R delete mode 100644 src/lpSolve/R/lp.assign.R delete mode 100644 src/lpSolve/R/lp.transport.R delete mode 100644 src/lpSolve/R/make.q8.R delete mode 100644 src/lpSolve/R/print.lp.R delete mode 100644 src/lpSolve/R/zzz.R delete mode 100644 src/lpSolve/man/lp.Rd delete mode 100644 src/lpSolve/man/lp.assign.Rd delete mode 100644 src/lpSolve/man/lp.object.Rd delete mode 100644 src/lpSolve/man/lp.transport.Rd delete mode 100644 src/lpSolve/man/make.q8.Rd delete mode 100644 src/lpSolve/man/print.lp.Rd diff --git a/inst/COPYRIGHTS b/inst/COPYRIGHTS index 39cabd5a..5f83b029 100644 --- a/inst/COPYRIGHTS +++ b/inst/COPYRIGHTS @@ -1,6 +1,6 @@ -All files in src/external are taken from -(i) lpsolve (https://cran.r-project.org/src/contrib/Archive/lpSolveAPI) version 5.5.2.0, -(ii) bnmin1 (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) version 1.11 +All files in src/external are taken from +(i) lpsolve (https://cran.r-project.org/package=lpSolve) version 5.6.20, +(ii) minimum_ellipsoid (or bnmin1) (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) version 1.11 Copyrights and modification details are explicitly described at the beginning of each of those files. diff --git a/src/Makevars b/src/Makevars index 69d45b5b..e0ef376b 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,12 +1,12 @@ -PKG_CPPFLAGS=-Ivolesti/external -IlpSolve/src -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra +PKG_CPPFLAGS=-Ivolesti/external -Iexternal/lpSolve/src -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra PKG_CXXFLAGS= -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-LlpSolve/src -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-Lexternal/lpSolve/src -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): lpSolve/src/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a +$(SHLIB): external/lpSolve/src/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a -lpSolve/src/liblp_solve.a: - @(cd lpSolve/src && $(MAKE) liblp_solve.a \ +external/lpSolve/src/liblp_solve.a: + @(cd external/lpSolve/src && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" RANLIB="$(RANLIB)") diff --git a/src/Makevars.win b/src/Makevars.win index 27abfb91..554a2bce 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,12 +1,12 @@ -PKG_CPPFLAGS=-Ivolesti/external -IlpSolve/src -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra +PKG_CPPFLAGS=-Ivolesti/external -Iexternal/lpSolve/src -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra PKG_CXXFLAGS= -lm -ldl -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-LlpSolve/src -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-Lexternal/lpSolve/src -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): lpSolve/src/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a +$(SHLIB): external/lpSolve/src/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a -lpSolve/src/liblp_solve.a: - @(cd lpSolve/src && $(MAKE) liblp_solve.a \ +external/lpSolve/src/liblp_solve.a: + @(cd external/lpSolve/src && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS) -DUSRDLL -DINLINE=static" \ CFLAGS="$(CFLAGS)" CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" \ RANLIB="$(RANLIB)") diff --git a/src/lpSolve/src/Makefile b/src/external/lpSolve/src/Makefile similarity index 100% rename from src/lpSolve/src/Makefile rename to src/external/lpSolve/src/Makefile diff --git a/src/lpSolve/src/Makevars b/src/external/lpSolve/src/Makevars similarity index 100% rename from src/lpSolve/src/Makevars rename to src/external/lpSolve/src/Makevars diff --git a/src/lpSolve/src/Makevars.win b/src/external/lpSolve/src/Makevars.win similarity index 100% rename from src/lpSolve/src/Makevars.win rename to src/external/lpSolve/src/Makevars.win diff --git a/src/lpSolve/src/colamd.c b/src/external/lpSolve/src/colamd.c similarity index 100% rename from src/lpSolve/src/colamd.c rename to src/external/lpSolve/src/colamd.c diff --git a/src/lpSolve/src/colamd.h b/src/external/lpSolve/src/colamd.h similarity index 100% rename from src/lpSolve/src/colamd.h rename to src/external/lpSolve/src/colamd.h diff --git a/src/lpSolve/src/commonlib.c b/src/external/lpSolve/src/commonlib.c similarity index 100% rename from src/lpSolve/src/commonlib.c rename to src/external/lpSolve/src/commonlib.c diff --git a/src/lpSolve/src/commonlib.h b/src/external/lpSolve/src/commonlib.h similarity index 100% rename from src/lpSolve/src/commonlib.h rename to src/external/lpSolve/src/commonlib.h diff --git a/src/lpSolve/src/declare.h b/src/external/lpSolve/src/declare.h similarity index 100% rename from src/lpSolve/src/declare.h rename to src/external/lpSolve/src/declare.h diff --git a/src/lpSolve/src/fortify.h b/src/external/lpSolve/src/fortify.h similarity index 100% rename from src/lpSolve/src/fortify.h rename to src/external/lpSolve/src/fortify.h diff --git a/src/lpSolve/src/hbio.c b/src/external/lpSolve/src/hbio.c similarity index 99% rename from src/lpSolve/src/hbio.c rename to src/external/lpSolve/src/hbio.c index 2ea44ad8..0a259821 100644 --- a/src/lpSolve/src/hbio.c +++ b/src/external/lpSolve/src/hbio.c @@ -1626,6 +1626,6 @@ void IOHBTerminate(char* message) { /* fprintf(stderr,message); ** exit(1); */ - error(message); + error("%s", message); } diff --git a/src/lpSolve/src/hbio.h b/src/external/lpSolve/src/hbio.h similarity index 100% rename from src/lpSolve/src/hbio.h rename to src/external/lpSolve/src/hbio.h diff --git a/src/lpSolve/src/ini.c b/src/external/lpSolve/src/ini.c similarity index 100% rename from src/lpSolve/src/ini.c rename to src/external/lpSolve/src/ini.c diff --git a/src/lpSolve/src/ini.h b/src/external/lpSolve/src/ini.h similarity index 100% rename from src/lpSolve/src/ini.h rename to src/external/lpSolve/src/ini.h diff --git a/src/lpSolve/src/init.c b/src/external/lpSolve/src/init.c similarity index 100% rename from src/lpSolve/src/init.c rename to src/external/lpSolve/src/init.c diff --git a/src/lpSolve/src/isfixedvar.c b/src/external/lpSolve/src/isfixedvar.c similarity index 100% rename from src/lpSolve/src/isfixedvar.c rename to src/external/lpSolve/src/isfixedvar.c diff --git a/src/lpSolve/src/lp_BFP.h b/src/external/lpSolve/src/lp_BFP.h similarity index 100% rename from src/lpSolve/src/lp_BFP.h rename to src/external/lpSolve/src/lp_BFP.h diff --git a/src/lpSolve/src/lp_BFP1.h b/src/external/lpSolve/src/lp_BFP1.h similarity index 100% rename from src/lpSolve/src/lp_BFP1.h rename to src/external/lpSolve/src/lp_BFP1.h diff --git a/src/lpSolve/src/lp_BFP2.h b/src/external/lpSolve/src/lp_BFP2.h similarity index 100% rename from src/lpSolve/src/lp_BFP2.h rename to src/external/lpSolve/src/lp_BFP2.h diff --git a/src/lpSolve/src/lp_Hash.c b/src/external/lpSolve/src/lp_Hash.c similarity index 100% rename from src/lpSolve/src/lp_Hash.c rename to src/external/lpSolve/src/lp_Hash.c diff --git a/src/lpSolve/src/lp_Hash.h b/src/external/lpSolve/src/lp_Hash.h similarity index 100% rename from src/lpSolve/src/lp_Hash.h rename to src/external/lpSolve/src/lp_Hash.h diff --git a/src/lpSolve/src/lp_LUSOL.c b/src/external/lpSolve/src/lp_LUSOL.c similarity index 100% rename from src/lpSolve/src/lp_LUSOL.c rename to src/external/lpSolve/src/lp_LUSOL.c diff --git a/src/lpSolve/src/lp_LUSOL.h b/src/external/lpSolve/src/lp_LUSOL.h similarity index 100% rename from src/lpSolve/src/lp_LUSOL.h rename to src/external/lpSolve/src/lp_LUSOL.h diff --git a/src/lpSolve/src/lp_MDO.c b/src/external/lpSolve/src/lp_MDO.c similarity index 100% rename from src/lpSolve/src/lp_MDO.c rename to src/external/lpSolve/src/lp_MDO.c diff --git a/src/lpSolve/src/lp_MDO.h b/src/external/lpSolve/src/lp_MDO.h similarity index 100% rename from src/lpSolve/src/lp_MDO.h rename to src/external/lpSolve/src/lp_MDO.h diff --git a/src/lpSolve/src/lp_MPS.c b/src/external/lpSolve/src/lp_MPS.c similarity index 100% rename from src/lpSolve/src/lp_MPS.c rename to src/external/lpSolve/src/lp_MPS.c diff --git a/src/lpSolve/src/lp_MPS.h b/src/external/lpSolve/src/lp_MPS.h similarity index 100% rename from src/lpSolve/src/lp_MPS.h rename to src/external/lpSolve/src/lp_MPS.h diff --git a/src/lpSolve/src/lp_SOS.c b/src/external/lpSolve/src/lp_SOS.c similarity index 100% rename from src/lpSolve/src/lp_SOS.c rename to src/external/lpSolve/src/lp_SOS.c diff --git a/src/lpSolve/src/lp_SOS.h b/src/external/lpSolve/src/lp_SOS.h similarity index 100% rename from src/lpSolve/src/lp_SOS.h rename to src/external/lpSolve/src/lp_SOS.h diff --git a/src/lpSolve/src/lp_crash.c b/src/external/lpSolve/src/lp_crash.c similarity index 100% rename from src/lpSolve/src/lp_crash.c rename to src/external/lpSolve/src/lp_crash.c diff --git a/src/lpSolve/src/lp_crash.h b/src/external/lpSolve/src/lp_crash.h similarity index 100% rename from src/lpSolve/src/lp_crash.h rename to src/external/lpSolve/src/lp_crash.h diff --git a/src/lpSolve/src/lp_explicit.h b/src/external/lpSolve/src/lp_explicit.h similarity index 100% rename from src/lpSolve/src/lp_explicit.h rename to src/external/lpSolve/src/lp_explicit.h diff --git a/src/lpSolve/src/lp_fortify.h b/src/external/lpSolve/src/lp_fortify.h similarity index 100% rename from src/lpSolve/src/lp_fortify.h rename to src/external/lpSolve/src/lp_fortify.h diff --git a/src/lpSolve/src/lp_lib.c b/src/external/lpSolve/src/lp_lib.c similarity index 100% rename from src/lpSolve/src/lp_lib.c rename to src/external/lpSolve/src/lp_lib.c diff --git a/src/lpSolve/src/lp_lib.h b/src/external/lpSolve/src/lp_lib.h similarity index 100% rename from src/lpSolve/src/lp_lib.h rename to src/external/lpSolve/src/lp_lib.h diff --git a/src/lpSolve/src/lp_matrix.c b/src/external/lpSolve/src/lp_matrix.c similarity index 100% rename from src/lpSolve/src/lp_matrix.c rename to src/external/lpSolve/src/lp_matrix.c diff --git a/src/lpSolve/src/lp_matrix.h b/src/external/lpSolve/src/lp_matrix.h similarity index 100% rename from src/lpSolve/src/lp_matrix.h rename to src/external/lpSolve/src/lp_matrix.h diff --git a/src/lpSolve/src/lp_mipbb.c b/src/external/lpSolve/src/lp_mipbb.c similarity index 100% rename from src/lpSolve/src/lp_mipbb.c rename to src/external/lpSolve/src/lp_mipbb.c diff --git a/src/lpSolve/src/lp_mipbb.h b/src/external/lpSolve/src/lp_mipbb.h similarity index 100% rename from src/lpSolve/src/lp_mipbb.h rename to src/external/lpSolve/src/lp_mipbb.h diff --git a/src/lpSolve/src/lp_params.c b/src/external/lpSolve/src/lp_params.c similarity index 100% rename from src/lpSolve/src/lp_params.c rename to src/external/lpSolve/src/lp_params.c diff --git a/src/lpSolve/src/lp_presolve.c b/src/external/lpSolve/src/lp_presolve.c similarity index 100% rename from src/lpSolve/src/lp_presolve.c rename to src/external/lpSolve/src/lp_presolve.c diff --git a/src/lpSolve/src/lp_presolve.h b/src/external/lpSolve/src/lp_presolve.h similarity index 100% rename from src/lpSolve/src/lp_presolve.h rename to src/external/lpSolve/src/lp_presolve.h diff --git a/src/lpSolve/src/lp_price.c b/src/external/lpSolve/src/lp_price.c similarity index 100% rename from src/lpSolve/src/lp_price.c rename to src/external/lpSolve/src/lp_price.c diff --git a/src/lpSolve/src/lp_price.h b/src/external/lpSolve/src/lp_price.h similarity index 100% rename from src/lpSolve/src/lp_price.h rename to src/external/lpSolve/src/lp_price.h diff --git a/src/lpSolve/src/lp_pricePSE.c b/src/external/lpSolve/src/lp_pricePSE.c similarity index 100% rename from src/lpSolve/src/lp_pricePSE.c rename to src/external/lpSolve/src/lp_pricePSE.c diff --git a/src/lpSolve/src/lp_pricePSE.h b/src/external/lpSolve/src/lp_pricePSE.h similarity index 100% rename from src/lpSolve/src/lp_pricePSE.h rename to src/external/lpSolve/src/lp_pricePSE.h diff --git a/src/lpSolve/src/lp_report.c b/src/external/lpSolve/src/lp_report.c similarity index 100% rename from src/lpSolve/src/lp_report.c rename to src/external/lpSolve/src/lp_report.c diff --git a/src/lpSolve/src/lp_report.h b/src/external/lpSolve/src/lp_report.h similarity index 100% rename from src/lpSolve/src/lp_report.h rename to src/external/lpSolve/src/lp_report.h diff --git a/src/lpSolve/src/lp_rlp.c b/src/external/lpSolve/src/lp_rlp.c similarity index 100% rename from src/lpSolve/src/lp_rlp.c rename to src/external/lpSolve/src/lp_rlp.c diff --git a/src/lpSolve/src/lp_rlp.h b/src/external/lpSolve/src/lp_rlp.h similarity index 100% rename from src/lpSolve/src/lp_rlp.h rename to src/external/lpSolve/src/lp_rlp.h diff --git a/src/lpSolve/src/lp_scale.c b/src/external/lpSolve/src/lp_scale.c similarity index 100% rename from src/lpSolve/src/lp_scale.c rename to src/external/lpSolve/src/lp_scale.c diff --git a/src/lpSolve/src/lp_scale.h b/src/external/lpSolve/src/lp_scale.h similarity index 100% rename from src/lpSolve/src/lp_scale.h rename to src/external/lpSolve/src/lp_scale.h diff --git a/src/lpSolve/src/lp_simplex.c b/src/external/lpSolve/src/lp_simplex.c similarity index 100% rename from src/lpSolve/src/lp_simplex.c rename to src/external/lpSolve/src/lp_simplex.c diff --git a/src/lpSolve/src/lp_simplex.h b/src/external/lpSolve/src/lp_simplex.h similarity index 100% rename from src/lpSolve/src/lp_simplex.h rename to src/external/lpSolve/src/lp_simplex.h diff --git a/src/lpSolve/src/lp_types.h b/src/external/lpSolve/src/lp_types.h similarity index 100% rename from src/lpSolve/src/lp_types.h rename to src/external/lpSolve/src/lp_types.h diff --git a/src/lpSolve/src/lp_utils.c b/src/external/lpSolve/src/lp_utils.c similarity index 100% rename from src/lpSolve/src/lp_utils.c rename to src/external/lpSolve/src/lp_utils.c diff --git a/src/lpSolve/src/lp_utils.h b/src/external/lpSolve/src/lp_utils.h similarity index 100% rename from src/lpSolve/src/lp_utils.h rename to src/external/lpSolve/src/lp_utils.h diff --git a/src/lpSolve/src/lp_wlp.c b/src/external/lpSolve/src/lp_wlp.c similarity index 100% rename from src/lpSolve/src/lp_wlp.c rename to src/external/lpSolve/src/lp_wlp.c diff --git a/src/lpSolve/src/lp_wlp.h b/src/external/lpSolve/src/lp_wlp.h similarity index 100% rename from src/lpSolve/src/lp_wlp.h rename to src/external/lpSolve/src/lp_wlp.h diff --git a/src/lpSolve/src/lpkit.h b/src/external/lpSolve/src/lpkit.h similarity index 100% rename from src/lpSolve/src/lpkit.h rename to src/external/lpSolve/src/lpkit.h diff --git a/src/lpSolve/src/lpslink56.c b/src/external/lpSolve/src/lpslink56.c similarity index 100% rename from src/lpSolve/src/lpslink56.c rename to src/external/lpSolve/src/lpslink56.c diff --git a/src/lpSolve/src/lpsolve.h b/src/external/lpSolve/src/lpsolve.h similarity index 100% rename from src/lpSolve/src/lpsolve.h rename to src/external/lpSolve/src/lpsolve.h diff --git a/src/lpSolve/src/lusol.c b/src/external/lpSolve/src/lusol.c similarity index 100% rename from src/lpSolve/src/lusol.c rename to src/external/lpSolve/src/lusol.c diff --git a/src/lpSolve/src/lusol.h b/src/external/lpSolve/src/lusol.h similarity index 100% rename from src/lpSolve/src/lusol.h rename to src/external/lpSolve/src/lusol.h diff --git a/src/lpSolve/src/lusol1.h b/src/external/lpSolve/src/lusol1.h similarity index 100% rename from src/lpSolve/src/lusol1.h rename to src/external/lpSolve/src/lusol1.h diff --git a/src/lpSolve/src/lusol2.h b/src/external/lpSolve/src/lusol2.h similarity index 100% rename from src/lpSolve/src/lusol2.h rename to src/external/lpSolve/src/lusol2.h diff --git a/src/lpSolve/src/lusol6a.h b/src/external/lpSolve/src/lusol6a.h similarity index 100% rename from src/lpSolve/src/lusol6a.h rename to src/external/lpSolve/src/lusol6a.h diff --git a/src/lpSolve/src/lusol6l0.h b/src/external/lpSolve/src/lusol6l0.h similarity index 100% rename from src/lpSolve/src/lusol6l0.h rename to src/external/lpSolve/src/lusol6l0.h diff --git a/src/lpSolve/src/lusol6u.h b/src/external/lpSolve/src/lusol6u.h similarity index 100% rename from src/lpSolve/src/lusol6u.h rename to src/external/lpSolve/src/lusol6u.h diff --git a/src/lpSolve/src/lusol7a.h b/src/external/lpSolve/src/lusol7a.h similarity index 100% rename from src/lpSolve/src/lusol7a.h rename to src/external/lpSolve/src/lusol7a.h diff --git a/src/lpSolve/src/lusol8a.h b/src/external/lpSolve/src/lusol8a.h similarity index 100% rename from src/lpSolve/src/lusol8a.h rename to src/external/lpSolve/src/lusol8a.h diff --git a/src/lpSolve/src/lusolio.c b/src/external/lpSolve/src/lusolio.c similarity index 100% rename from src/lpSolve/src/lusolio.c rename to src/external/lpSolve/src/lusolio.c diff --git a/src/lpSolve/src/lusolio.h b/src/external/lpSolve/src/lusolio.h similarity index 100% rename from src/lpSolve/src/lusolio.h rename to src/external/lpSolve/src/lusolio.h diff --git a/src/lpSolve/src/mmio.c b/src/external/lpSolve/src/mmio.c similarity index 100% rename from src/lpSolve/src/mmio.c rename to src/external/lpSolve/src/mmio.c diff --git a/src/lpSolve/src/mmio.h b/src/external/lpSolve/src/mmio.h similarity index 100% rename from src/lpSolve/src/mmio.h rename to src/external/lpSolve/src/mmio.h diff --git a/src/lpSolve/src/myblas.c b/src/external/lpSolve/src/myblas.c similarity index 100% rename from src/lpSolve/src/myblas.c rename to src/external/lpSolve/src/myblas.c diff --git a/src/lpSolve/src/myblas.h b/src/external/lpSolve/src/myblas.h similarity index 100% rename from src/lpSolve/src/myblas.h rename to src/external/lpSolve/src/myblas.h diff --git a/src/lpSolve/src/sparselib.c b/src/external/lpSolve/src/sparselib.c similarity index 100% rename from src/lpSolve/src/sparselib.c rename to src/external/lpSolve/src/sparselib.c diff --git a/src/lpSolve/src/sparselib.h b/src/external/lpSolve/src/sparselib.h similarity index 100% rename from src/lpSolve/src/sparselib.h rename to src/external/lpSolve/src/sparselib.h diff --git a/src/lpSolve/src/ufortify.h b/src/external/lpSolve/src/ufortify.h similarity index 100% rename from src/lpSolve/src/ufortify.h rename to src/external/lpSolve/src/ufortify.h diff --git a/src/lpSolve/src/yacc_read.c b/src/external/lpSolve/src/yacc_read.c similarity index 100% rename from src/lpSolve/src/yacc_read.c rename to src/external/lpSolve/src/yacc_read.c diff --git a/src/lpSolve/src/yacc_read.h b/src/external/lpSolve/src/yacc_read.h similarity index 100% rename from src/lpSolve/src/yacc_read.h rename to src/external/lpSolve/src/yacc_read.h diff --git a/src/lpSolve_patches/commonlib.c b/src/external/lpSolve_patches/commonlib.c similarity index 100% rename from src/lpSolve_patches/commonlib.c rename to src/external/lpSolve_patches/commonlib.c diff --git a/src/lpSolve/DESCRIPTION b/src/lpSolve/DESCRIPTION deleted file mode 100644 index a7942b4e..00000000 --- a/src/lpSolve/DESCRIPTION +++ /dev/null @@ -1,19 +0,0 @@ -Package: lpSolve -Version: 5.6.19 -Title: Interface to 'Lp_solve' v. 5.5 to Solve Linear/Integer Programs -Author: Michel Berkelaar and others -Maintainer: Gábor Csárdi -Description: Lp_solve is freely available (under LGPL 2) software for - solving linear, integer and mixed integer programs. In this - implementation we supply a "wrapper" function in C and some R - functions that solve general linear/integer problems, - assignment problems, and transportation problems. This version - calls lp_solve version 5.5. -License: LGPL-2 -URL: https://github.com/gaborcsardi/lpSolve -BugReports: https://github.com/gaborcsardi/lpSolve/issues -Encoding: UTF-8 -NeedsCompilation: yes -Packaged: 2023-09-12 18:07:47 UTC; gaborcsardi -Repository: CRAN -Date/Publication: 2023-09-13 11:20:02 UTC diff --git a/src/lpSolve/MD5 b/src/lpSolve/MD5 deleted file mode 100644 index 5583d4b7..00000000 --- a/src/lpSolve/MD5 +++ /dev/null @@ -1,94 +0,0 @@ -eed30fcee198872214cb36077153750d *DESCRIPTION -5d954745be287207485c9af3411e4a9d *NAMESPACE -c3c52167c5e85957227571cad076ff4f *R/lp.R -89b2e3700cc47dcb8fb39f951603e008 *R/lp.assign.R -693f4392cf60f9cf8b89ffca74dc7126 *R/lp.transport.R -1b08b692aa9466144a38a6610140141d *R/make.q8.R -e8c949571124717686a90610235a4e01 *R/print.lp.R -82d9d64c4e3e654e990c97ab7e4a599e *R/zzz.R -4e2f2ffcdec5627a6f1eceb7920300a6 *man/lp.Rd -495aa3d5e82dd181e4c6c51b4886d983 *man/lp.assign.Rd -06c29ffbfa3881831f21cb49ac4e0761 *man/lp.object.Rd -d037a6837540cf8e544b484f8e00a830 *man/lp.transport.Rd -cae07d4610fee186d872be8b72b4a3a4 *man/make.q8.Rd -a002ef080a26026bc3c5f043b9b036c0 *man/print.lp.Rd -50b1d9e03d07b9e0c2181f6b290ae5e3 *src/Makevars -c2ab53abec8229377522a31738ba7b10 *src/Makevars.win -76e3867049928b1eb34a883c54bbb7cb *src/colamd.c -85250c070bb1c6f41cb2ba4b70f0f3ab *src/colamd.h -149cb4963976fa10b7f88c492ea967d0 *src/commonlib.c -d411f086bd6e138a7c1b00a71c7362b8 *src/commonlib.h -6bafa998341646f528777d87971d79f8 *src/declare.h -0c7ea81c7229f9afe1b4d6708a2c9b5d *src/fortify.h -dc441364a9259f0b973b83c9ed746306 *src/hbio.c -9640c6cfa3574729cec66868a7063c50 *src/hbio.h -7ed886f0f1677ca7141e80cb1151129f *src/ini.c -4504a63202117a0f2c5a94345094ef39 *src/ini.h -fe7898386bf89130afb8b539ed40a729 *src/init.c -385f8a752e28d168a462ff199e8a626c *src/isfixedvar.c -ec1603dafa740c89a5c13d30662e96bf *src/lp_BFP.h -6d387ddea7e501c9f45ac8862b10d630 *src/lp_BFP1.h -5ecae67efa4b7bbce92ad29204ca0297 *src/lp_BFP2.h -f5857b472a5e5d5da3297ccb746d590e *src/lp_Hash.c -ce0a0d53e631a97b88fadc584331913c *src/lp_Hash.h -8fcdff02e2674a8a8dd7208d24bdb840 *src/lp_LUSOL.c -fa869ca21a3fbb48dbf789ad6cb63a51 *src/lp_LUSOL.h -5d59a4b965236021be19b4f484a93db8 *src/lp_MDO.c -07bc5ba751db2629a38b7932cc70cbe4 *src/lp_MDO.h -78e881d0dd9ecc0a8c09f2623e0aafec *src/lp_MPS.c -7871ebb358485f8e8c3ef9443980d497 *src/lp_MPS.h -a38903d6d76b4facd163017c55f89389 *src/lp_SOS.c -70c13990a39ffce5c0222bf58ea8d0f3 *src/lp_SOS.h -856e487693dd757a70b65f2de0c99f51 *src/lp_crash.c -91c43ad6fde554bb8bfa84b4d2de50d9 *src/lp_crash.h -eb42ea6b62c3173610f3ffd652719e24 *src/lp_explicit.h -1175c90bc8cc00005069b734eacee99c *src/lp_fortify.h -5012b501ccb10aab9ddabe983938bd50 *src/lp_lib.c -f61de993e64612611a9552db3d3d682c *src/lp_lib.h -670b7b1092aa5b134f8e7c22e1952c2b *src/lp_matrix.c -0e0411fc13742423c3ad9daf4898a207 *src/lp_matrix.h -241a136626b10914f972ab159fb6b890 *src/lp_mipbb.c -6a9e5268c49558066d7c1e6d513a030b *src/lp_mipbb.h -285f3eab3c9e2dff2a438f93acfbe3a1 *src/lp_params.c -24b3596a0955674eccc4bbec2da57070 *src/lp_presolve.c -94b37b4e5490ec5bfd9a7878a3757cb8 *src/lp_presolve.h -19e0dbc4e1bfed660a66d53ef1386c43 *src/lp_price.c -dc0064126161d25e11fca8b5e1157ed0 *src/lp_price.h -6a4339fad7430c21e5a1b42a9efa3914 *src/lp_pricePSE.c -dc50c729e494a1e9c696a583fc8071b9 *src/lp_pricePSE.h -155eb77552c988866e08d52660566212 *src/lp_report.c -c63130e91e32ad6ccb07ed74b858b2f6 *src/lp_report.h -29dd83c8ebcd859a24358acf121bec81 *src/lp_rlp.c -c02fa3e4c7d0ee6205084dc503bb20a2 *src/lp_rlp.h -c0d6d17b778a6abddce060ae7c4eb485 *src/lp_scale.c -58d5ec0d2d218782a74448fc2c2d49cf *src/lp_scale.h -695799aa902e816047d86d070d75724d *src/lp_simplex.c -15a71c4bc5331ff6006d2ab46e8ad5dd *src/lp_simplex.h -5e54874bc04eed6bcc7cea3e6e303fb7 *src/lp_types.h -312b52b479860d4884cc3dedc9914da7 *src/lp_utils.c -2f4334f740e62ab78cb7c30a35f65a00 *src/lp_utils.h -f1c11764f310c464d1d939445a549830 *src/lp_wlp.c -e38ffb7e5dbfb1aa5d6589ac0e6be58b *src/lp_wlp.h -238c51b4c76117e4f774a6646517d626 *src/lpkit.h -2cc58edd5502d11455c509b95138c750 *src/lpslink56.c -7ef44c14a40fe999edd2e437bec993fd *src/lpsolve.h -3dc305aea45e7e242ebb66d80c8a6f8f *src/lusol.c -52b8d1269fab176c6c81632c544de26d *src/lusol.h -c226ea7cd9844e8885331b69148e1732 *src/lusol1.h -a5801e5989ac98e0b250e0eee8c2a41c *src/lusol2.h -a93d1dfb72e8c130df6c450137e0ea0d *src/lusol6a.h -e92bf5c4a3f3345bf38c2d06fa96b14b *src/lusol6l0.h -eb600f9e480a8cb363ae81901ad4be32 *src/lusol6u.h -2962a1720d6e6a10b727899a44b1b9c5 *src/lusol7a.h -46a3891578edc8cd114808cf564699f6 *src/lusol8a.h -ad1867767ee064effa32ea9e880fe632 *src/lusolio.c -3e67bb130740b63befbb2c6ed01989b8 *src/lusolio.h -4ad03c261870928d5ae228f2c3b25600 *src/mmio.c -7ff75e953e30ec3c26bffce310809500 *src/mmio.h -7d7a35fefee657e82631e36573f62e97 *src/myblas.c -c8a3b2f20143aae3f8caec9d28055bd2 *src/myblas.h -b08069fb86201ded0b9dcb009c209d12 *src/sparselib.c -4bae4e6a2367d3b9ef07b7dae5e608ba *src/sparselib.h -ca9689ad85eac09f9bdb3f3db52222d0 *src/ufortify.h -9029bb6578493b199eaef28ffe0648f8 *src/yacc_read.c -b2919866cb9ee057db8606f3bd02eb3d *src/yacc_read.h diff --git a/src/lpSolve/NAMESPACE b/src/lpSolve/NAMESPACE deleted file mode 100644 index 5a51f0d2..00000000 --- a/src/lpSolve/NAMESPACE +++ /dev/null @@ -1,3 +0,0 @@ -useDynLib("lpSolve") -export("lp", "lp.transport", "lp.assign", "make.q8") -S3method("print", "lp") diff --git a/src/lpSolve/R/lp.R b/src/lpSolve/R/lp.R deleted file mode 100644 index b45b746c..00000000 --- a/src/lpSolve/R/lp.R +++ /dev/null @@ -1,273 +0,0 @@ -lp <- function(direction = "min", objective.in, const.mat, const.dir, const.rhs, - transpose.constraints = TRUE, int.vec, presolve = 0, compute.sens = 0, - binary.vec, all.int=FALSE, all.bin=FALSE, scale=196, dense.const, - num.bin.solns=1, use.rw=FALSE, timeout = 0L) -{ - # - # lp: solve a general linear program - # - # Arguments: - # direction: Character: direction of optimization: "min" (default) - # or "max." - # objective.in: Numeric vector (or one-column data frame) of - # coefficients of objective function - # const.mat: Matrix of numeric constraint coefficients, one row - # per constraint, one column per variable (unless - # transpose.constraints = FALSE; see below). - # const.dir: Vector of character strings giving the direction of the - # constraints: each value should be one of "<," "<=," "=," "==," - # ">," or ">=." - # const.rhs: Vector of numeric values for the right-hand sides - # of the constraints. - # transpose.constraints: By default each constraint occupies a - # row of const.mat, and that matrix needs to be transposed before - # being passed to the optimizing code. For very large - # constraint matrices it may be wiser to construct the - # constraints in a matrix column-by-column. In that case set - # transpose.constraints to FALSE. - # int.vec: Numeric vector giving the indices of variables that are - # required to be integer. The length of this vector will - # therefore be the number of integer variables. - # presolve: Numeric: Should presolve be done (in lp_solve)? - # Default: 0 (no). - # A non-zero value means "yes." Currently mostly ignored. - # compute.sens: Numeric: compute sensitivities? Default 0 (no). - # Any non-zero value means "yes." - # binary.vec: Numeric vector giving indices of binary variables - # all.int: logical: True if all variables should be integers. This - # overrides anything in int.vec. - # all.bin: logical: True if all variables should be binary. This - # overrides anything in binary.vec. - # dense.const: alternative specification of constraints in the - # form of a three-column matrix or data frame. If a row contains - # (i, j, k), it means "constraint i, variable j = value k." This - # is ignored if const.mat is supplied. - # scale: integer giving scaling. Possible values can be found in - # in the lpSolve documentatation. 0 = no scaling. Default: 196. - # num.bin.solns: If all.bin is True, we will attempt to find up to - # num.bin.solns solutions and return them in a matrix. - # use.rw: Work around a bug when num.bin.solns=TRUE by writing each - # solution out to a file and reading it back in. - # timeout: set as timeout variable in lpslink. - # - # Set up the direction. - # - if(direction == "min") - direction <- 0 - else if (direction == "max") - direction <- 1 - else stop ("Direction must be 'max' or 'min'") - # - # Convert one-column data frame objective to vector. Add leading 0 to - # obejctive. - # - if(is.data.frame(objective.in)) { - if(ncol(objective.in) > 1) - stop("Objective vector has more than one column") - objective.in <- unlist(objective.in) - names(objective.in) <- NULL - } - # - # Set up solution, status, x.count (= number of variables) - # - objective <- c(0, objective.in) - solution <- numeric(length(objective.in)) - status <- objval <- 0 - x.count <- length(objective.in) - constraints <- 0 - const.count <- 0 - # - # Constraint matrix stuff. If const.mat is passed in, convert it - # to a matrix if necessary (and set NA's to 0). Also transpose - # if necessary. - # - use.dense <- FALSE - if (!missing (const.mat)) { - dense.const <- 0; dense.const.nrow = 0; dense.ctr = 0 - if(is.data.frame(const.mat)) { - cm <- as.numeric(unlist(const.mat)) - names(cm) <- NULL - const.mat <- matrix(cm, nrow = nrow(const.mat)) - } - const.mat[is.na(const.mat)] <- 0 - if(transpose.constraints) - const.mat <- t(const.mat) - const.count <- ncol(const.mat) - } - else - { - # - # If there was no const.mat, our constraints are in dense.const (or - # there are no constraints). If there's a dense.const, it needs to have - # three columns, each row having , and . - # The number of constraints is the largest value in the const column. - # Check to make sure every value in the range is present, and that - # there aren't references to variables that don't exist. It will - # be convenient to sort this by constraint number. - # - if (missing (dense.const)) { - dense.const <- 0; dense.const.nrow = 0; dense.ctr = 0 - } else { - if (!is.matrix (dense.const)) - stop ("Dense constraints need to be matrix or data frame.") - if (is.data.frame (dense.const)) dense.const <- as.matrix (dense.const) - if (ncol (dense.const) != 3) - stop ("Dense constraints must be in three columns.") - dimnames(dense.const) <- list (NULL, c("Const", "Var", "Value")) - dense.const <- dense.const[order(dense.const[,"Const"]),,drop=FALSE] - const.indices <- unique (dense.const[,"Const"]) - if (length(const.indices) != max (const.indices)) - stop ("Error in constraint numbering") - const.count <- max (const.indices) - # - # It's convenient to send in one other piece of information. The - # "dense.ctr" vector's ith entry says how many non-zero elements - # are found in constraint i. Once we have that we don't need the - # "Const" column of dense.const, either. - # - dense.ctr <- table (dense.const[,"Const"]) - names(dense.ctr) <- NULL - dense.const <- dense.const[,c("Var", "Value"), drop=FALSE] - dense.const.nrow <- nrow (dense.const) - use.dense <- TRUE - }} - # - # Set up constraint signs... - # - const.dir.num <- rep(-1, length(const.dir)) - const.dir.num[const.dir == "<" | const.dir == "<="] <- 1 - const.dir.num[const.dir == "=" | const.dir == "=="] <- 3 - const.dir.num[const.dir == ">" | const.dir == ">="] <- 2 - if(any(const.dir.num == -1)) - stop("Unknown constraint direction found\n") - # - # ...constraint count, and right-hand sides. - # - if(is.data.frame(const.rhs)) - const.rhs <- as.matrix(const.rhs) - const.rhs <- c(const.rhs) - names(const.rhs) <- NULL - # - # For regular (non-dense) constraints, set up big matrix of - # constraint info; add a 0 on the front. - # - if (!missing (const.mat)) { - big.const.mat <- rbind(const.mat, const.dir.num, const.rhs) - constraints <- c(0, c(big.const.mat)) - } - # - # For dense constraints, just add rows of directions and rhs's - # to dense.ctr, which will then be a 3-row matrix w/ const.count cols. - # - else if (!missing (dense.const)) { - dense.ctr <- rbind (dense.ctr, const.dir.num, const.rhs) - big.const.mat <- 0 - constraints <- 0 - } - # - # Set up int.vec. If all.int is present (and TRUE), use that. - # - if (!missing (all.int) && all.int) { - int.vec <- 1:length(solution) - int.count <- length(int.vec) - } - else - { - if(missing(int.vec)) { - int.count <- 0 - int.vec <- 0 - } - else - int.count <- length(int.vec) - } - # - # Do the same for binary.vec. - # - if (!missing (all.bin) && all.bin) { - binary.vec <- 1:length(solution) - bin.count <- length(binary.vec) - } - else - { - if(missing(binary.vec)) { - bin.count <- 0 - binary.vec <- 0 - } - else - bin.count <- length(binary.vec) - } - # If all variables are binary, set all.bin to TRUE. - if (length(binary.vec) == length(solution)) all.bin <- TRUE - # - # If more than one solution is called for, prepare for that. "Solution" will need - # one extra element. - # - if (num.bin.solns > 1) - if (all.bin) - solution <- c(0, rep (solution, num.bin.solns)) - else { - warning ("Num.bin.solns can only be > 1 if all variables are binary") - num.bin.solns <- 1 - } - # - # Set up sensitivity stuff. - # - sens.coef.from <- sens.coef.to <- 0 - duals <- duals.from <- duals.to <- 0 - if(compute.sens != 0) { - sens.coef.from <- sens.coef.to <- numeric(x.count) - duals <- duals.from <- duals.to <- numeric(x.count + const.count) - } - # - - if (num.bin.solns > 1 && use.rw == TRUE) - tmp <- tempfile () - else - tmp <- "Nobody will ever look at this" - if (missing (dense.const) || !is.matrix (dense.const)) - { - dense.col <- dense.val <- 0 - } - else - { - dense.col = dense.const[,"Var"] - dense.val = dense.const[,"Value"] - } - lp.out <- .C("lpslink", - direction = as.integer(direction), - x.count = as.integer(x.count), - objective = as.double(objective), - const.count = as.integer(const.count), - constraints = as.double(constraints), - int.count = as.integer(int.count), - int.vec = as.integer(int.vec), - bin.count = as.integer(bin.count), - binary.vec = as.integer(binary.vec), - num.bin.solns = as.integer (num.bin.solns), - objval = as.double(objval), - solution = as.double(solution), - presolve = as.integer(presolve), - compute.sens = as.integer(compute.sens), - sens.coef.from = as.double(sens.coef.from), - sens.coef.to = as.double(sens.coef.to), - duals = as.double(duals), - duals.from = as.double(duals.from), - duals.to = as.double(duals.to), - scale = as.integer (scale), - use.dense = as.integer (use.dense), - dense.col = as.integer (dense.col), - dense.val = as.double (dense.val), - dense.const.nrow = as.integer (dense.const.nrow), - dense.ctr = as.double (dense.ctr), - use.rw = as.integer (use.rw), - tmp = as.character(tmp), - status = as.integer(status), - timeout = as.integer(timeout), - PACKAGE="lpSolve") - lp.out$objective <- objective.in - lp.out$constraints <- big.const.mat - if(any(names(version) == "language")) - class(lp.out) <- "lp" - else oldClass(lp.out) <- "lp" - return(lp.out) -} diff --git a/src/lpSolve/R/lp.assign.R b/src/lpSolve/R/lp.assign.R deleted file mode 100644 index 067e3092..00000000 --- a/src/lpSolve/R/lp.assign.R +++ /dev/null @@ -1,114 +0,0 @@ -lp.assign <- function (cost.mat, direction="min", presolve = 0, compute.sens = 0) -{ -# -# lp.assign: use lpsolve.dll to solve an assignment problem. This -# is a linear program with an ixj matrix of decision variables, -# and i+j constraints: that the rows and columns all add up to one. -# -# Arguments: -# cost.mat: matrix or data.frame of costs -# direction: "min" (default) or "max" -# presolve: numeric. Presolve? Default 0. Currently ignored. -# compute.sens: numeric. Compute sensitivities? Default 0 (no). -# Any non-zero number means "yes" and, in that -# case, presolving is attempted. -# -# Return value: list from lpsolve, including objective and -# assignments. -# -# Check for the lpslink function, dyn.open if needed. (It should -# from data.frame if needed. -# - if (!is.matrix(cost.mat)) - stop("Matrix of costs required.") - if (is.data.frame(cost.mat)) - cost.mat <- as.matrix(cost.mat) -# -# Set up the stuff. -# - nr <- nrow(cost.mat) - nc <- ncol(cost.mat) - rnum.signs <- rep (3, nr) - row.rhs <- rep (1, nr) - cnum.signs <- rep (3, nc) - col.rhs <- rep (1, nc) - if (direction == "min") - direction <- as.integer(0) - else - if (direction == "max") - direction <- as.integer (1) - else - stop ("Direction must be 'min' or 'max'") - varcount <- as.integer(nr * nc) - objective <- as.double(c(0, c(t(cost.mat)))) -# -# Set up the row and column constraints. Each is of the -# "=1" type, represented by 3 (for "equals") 1. -# - const.count <- as.integer(nr + nc) - intcount <- as.integer(varcount) # number of integers - intvec <- as.integer(1:varcount) # indicators of integers -# -# Prepare objective value, integer indicators, solution, and status -# - objval <- as.double(0) - int.count <- nc * nr - integers <- as.integer (numeric (int.count)) - solution <- as.double(numeric(nc * nr)) - status <- as.integer(0) -# -# Set up sensitivity stuff -# - sens.coef.from <- sens.coef.to <- 0 - duals <- duals.from <- duals.to <- 0 - if (compute.sens) { - sens.coef.from <- sens.coef.to <- numeric(varcount) - duals <- duals.from <- duals.to <- numeric(varcount + - const.count) - } - ## costs <- as.double (c(0, c(cost.mat))) - lps.out <- .C("lp_transbig", - direction = direction, - rcount = as.integer (nr), - ccount = as.integer (nc), - costs = objective, - rsigns = as.integer (rnum.signs), - rrhs = as.double (row.rhs), - csigns = as.integer (cnum.signs), - crhs = as.double (col.rhs), - objval = objval, - int.count = int.count, - integers = integers, - solution = solution, - presolve = as.integer(presolve), - compute.sens = as.integer(compute.sens), - sens.coef.from = as.double(sens.coef.from), - sens.coef.to = as.double(sens.coef.to), - duals = as.double(duals), - duals.from = as.double(duals.from), - duals.to = as.double(duals.to), - status = status, PACKAGE="lpSolve") -# -# Reset solution back into matrix form. -# - lps.out$solution = matrix(lps.out$solution, nr, nc, byrow = TRUE) - if (length(duals) > 0) { - lps.out$sens.coef.from <- matrix(lps.out$sens.coef.from, - nr, nc, byrow = TRUE) - lps.out$sens.coef.to <- matrix(lps.out$sens.coef.to, - nr, nc, byrow = TRUE) - lps.out$duals <- matrix(lps.out$duals, nr, nc, byrow = TRUE) - lps.out$duals.from <- matrix(lps.out$duals.from, nr, - nc, byrow = TRUE) - lps.out$duals.to <- matrix(lps.out$duals.to, nr, nc, - byrow = TRUE) - } -# -# Reset the costs, to which we had to add a 0 -# - lps.out$costs <- cost.mat - if(any(names(version) == "language")) - class(lps.out) <- "lp" - else oldClass(lps.out) <- "lp" - lps.out -} diff --git a/src/lpSolve/R/lp.transport.R b/src/lpSolve/R/lp.transport.R deleted file mode 100644 index 75a7b2c2..00000000 --- a/src/lpSolve/R/lp.transport.R +++ /dev/null @@ -1,155 +0,0 @@ -lp.transport <- function (cost.mat, direction = "min", row.signs, row.rhs, col.signs, - col.rhs, presolve = 0, compute.sens = 0, integers = 1:(nc * nr)) -{ -# -# lp.transport: use lpsolve.dll to solve a transportation problem. -# This is a linear program with an ixj matrix of decision variables, -# and constraints on the row and column sums (and no others) -# -# Arguments: cost.mat: matrix or data.frame of costs -# dir: direction ("min" or "max") -# row.signs: signs for row constraints -# row.rhs: values for row constraints -# col.signs: signs for column constraints -# col.rhs: values for column constraints -# presolve: Numeric: should we presolve? Default 0 (no); non-0 -# values means "yes." Currently mostly ignored. -# compute.sens: Numeric: compute sensitivities? Default 0 (no); -# non-zero value means "yes." -# integers: Indicator of integer variables: default, all. -# -# Return value: list from lpsolve, including objective and optimal values. -# -# Check that the cost matrix is in fact a matrix; convert -# from data.frame if needed. -# - if (!is.matrix(cost.mat)) - stop("Matrix of costs required.") - if (is.data.frame(cost.mat)) - cost.mat <- as.matrix(cost.mat) -# -# Set up the stuff. -# - nr <- nrow(cost.mat) - nc <- ncol(cost.mat) - # - # Ensure that row stuff is of the correct size. - # - if (is.matrix(row.signs)) - row.signs <- as.vector(row.signs) - if (length(row.signs) != nr) - stop(paste("Error: We have", length(row.signs), "signs, but", - nr, "rows")) - if (is.matrix(row.rhs)) - row.rhs <- as.vector(row.rhs) - if (length(row.rhs) != nr) - stop(paste("Error: We have", length(row.rhs), "rhs's, but", - nr, "rows")) - # - # Ensure that col stuff is of the correct size. - # - if (is.matrix(col.signs)) - col.signs <- as.vector(col.signs) - if (length(col.signs) != nc) - stop(paste("Error: We have", length(col.signs), "signs, but", - nc, "columns")) - if (is.matrix(col.rhs)) - col.rhs <- as.vector(col.rhs) - if (length(col.rhs) != nc) - stop(paste("Error: We have", length(col.rhs), "rhs's, but", - nc, "rows")) - if (direction == "min") - direction <- as.integer(0) - else - if (direction == "max") - direction <- as.integer(1) - else - stop ("Direction should be 'min' or 'max'") - varcount <- as.integer(nr * nc) # no of vars - objective <- as.double(c(0, c(t(cost.mat)))) - if (is.null (integers)) { - int.count <- 0 - integers <- 0 - } - else - int.count <- length(integers) - const.count <- as.integer(nr + nc) # no of constraints - rnum.signs <- rep(-1, nr) # sign holder -# -# Set the signs: <, >, = turn into 1,2,3 respectively. We also -# allow those followed by another "=". Anything else is an error. -# - rnum.signs[row.signs == "<" | row.signs == "<="] <- 1 - rnum.signs[row.signs == "=" | row.signs == "=="] <- 3 - rnum.signs[row.signs == ">" | row.signs == ">="] <- 2 - if (any(rnum.signs == -1)) - stop(paste("Unknown row sign in position ", which(rnum.signs == - -1)[1])) -# -# Column signs. -# - cnum.signs <- rep(-1, nc) - cnum.signs[col.signs == "<" | col.signs == "<="] <- 1 - cnum.signs[col.signs == "=" | col.signs == "=="] <- 3 - cnum.signs[col.signs == ">" | col.signs == ">="] <- 2 - if (any(cnum.signs == -1)) - stop(paste("Unknown column sign in position ", which(cnum.signs == - -1)[1])) -# -# A few more things, plus dual action. -# - objval <- as.double(0) - solution <- as.double(numeric(nc * nr)) - status <- as.integer(0) - sens.coef.from <- sens.coef.to <- 0 - duals <- duals.from <- duals.to <- 0 - if (compute.sens) { - sens.coef.from <- sens.coef.to <- numeric(varcount) - duals <- duals.from <- duals.to <- numeric(varcount + - const.count) - } -# -# Stick a zero on the front of costs, and off we go. -# - costs <- as.double (c(0, c(cost.mat))) - lps.out <- .C("lp_transbig", - direction = direction, - rcount = as.integer (nr), - ccount = as.integer (nc), - costs = costs, - rsigns = as.integer (rnum.signs), - rrhs = as.double (row.rhs), - csigns = as.integer (cnum.signs), - crhs = as.double (col.rhs), - objval = objval, - int.count = int.count, - integers = integers, - solution = solution, - presolve = as.integer(presolve), - compute.sens = as.integer(compute.sens), - sens.coef.from = as.double(sens.coef.from), - sens.coef.to = as.double(sens.coef.to), - duals = as.double(duals), - duals.from = as.double(duals.from), - duals.to = as.double(duals.to), - status = status, PACKAGE="lpSolve") -# -# Set solution back into a matrix. -# - lps.out$solution = matrix(lps.out$solution, nr, nc, byrow = FALSE) - if (length(duals) > 0) { - lps.out$sens.coef.from <- matrix(lps.out$sens.coef.from, - nr, nc, byrow = TRUE) - lps.out$sens.coef.to <- matrix(lps.out$sens.coef.to, - nr, nc, byrow = TRUE) - lps.out$duals <- matrix(lps.out$duals, nr, nc, byrow = TRUE) - lps.out$duals.from <- matrix(lps.out$duals.from, nr, - nc, byrow = TRUE) - lps.out$duals.to <- matrix(lps.out$duals.to, nr, nc, - byrow = TRUE) - } - if(any(names(version) == "language")) - class(lps.out) <- "lp" - else oldClass(lps.out) <- "lp" - lps.out -} diff --git a/src/lpSolve/R/make.q8.R b/src/lpSolve/R/make.q8.R deleted file mode 100644 index f07d35b0..00000000 --- a/src/lpSolve/R/make.q8.R +++ /dev/null @@ -1,47 +0,0 @@ -make.q8 <- function () -{ -# -# Q8: Set up sparse constraints (in the "lp" package sense) for the 8 queens problem. -# -# "Chess" holds the chess board. -# -chess <- matrix (1:64, 8, 8, byrow=T) -# -# Row and column constraints. Recall that each constraint has three entries. -# -row.const <- cbind (rep (1:8, each=8), c(t(chess)), 1) -col.const <- cbind (rep (9:16, each=8), c(chess), 1) -# -# Diagonals, bottom left to top right. -# -const.ctr <- 17 -chess <- matrix (1:64, 8, 8, byrow=T) -row.chess <- row(chess); col.chess <- col(chess) -d1.const <- NULL -rplusc <- row.chess + col.chess -for (i in 3:15) { -d1.const <- rbind (d1.const, cbind (const.ctr, chess[rplusc == i], 1)) -const.ctr <- const.ctr + 1 -} -# -# Now the other way. -# -start <- seq (49,1, by=-8) -for (i in start) { -d1.const <- rbind (d1.const, cbind (const.ctr, seq (i, 64, by=9), 1)) -const.ctr <- const.ctr + 1 -} -# -# Also a few more! -# -for (i in 2:7) -{ -d1.const <- rbind (d1.const, cbind (const.ctr, seq (i, chess[9-i,8], by=9), 1)) -const.ctr <- const.ctr + 1 -} -# -# Return all constraints. -# -rbind (row.const, col.const, d1.const) -} - diff --git a/src/lpSolve/R/print.lp.R b/src/lpSolve/R/print.lp.R deleted file mode 100644 index c726575b..00000000 --- a/src/lpSolve/R/print.lp.R +++ /dev/null @@ -1,11 +0,0 @@ -print.lp <- function(x, ...) -{ - if(x$status == 0) { - cat("Success: the objective function is", x$objval, "\n") - if (any (names(x) == "num.bin.solns") && x$num.bin.solns > 1) - cat ("\t", x$num.bin.solns, "solutions returned\n") - } - else if(x$status == 2) - cat("Error: no feasible solution found") - else cat("Error: status", x$status, "\n") -} diff --git a/src/lpSolve/R/zzz.R b/src/lpSolve/R/zzz.R deleted file mode 100644 index 737da057..00000000 --- a/src/lpSolve/R/zzz.R +++ /dev/null @@ -1,3 +0,0 @@ -# .First.lib <- function(libname, pkgname) { -# library.dynam ("lpSolve", pkgname, libname) -# } diff --git a/src/lpSolve/man/lp.Rd b/src/lpSolve/man/lp.Rd deleted file mode 100644 index bc0db301..00000000 --- a/src/lpSolve/man/lp.Rd +++ /dev/null @@ -1,136 +0,0 @@ -\name{lp} -\alias{lp} -\title{Linear and Integer Programming} -\description{Interface to \code{lp_solve} linear/integer programming system} -\usage{ -lp (direction = "min", objective.in, const.mat, const.dir, const.rhs, - transpose.constraints = TRUE, int.vec, presolve=0, compute.sens=0, - binary.vec, all.int=FALSE, all.bin=FALSE, scale = 196, dense.const, - num.bin.solns=1, use.rw=FALSE, timeout = 0L) -} -\arguments{ -\item{direction}{Character string giving direction of optimization: -"min" (default) or "max."} -\item{objective.in}{Numeric vector of coefficients of objective function} -\item{const.mat}{Matrix of numeric constraint coefficients, one row -per constraint, one column per variable (unless transpose.constraints = -FALSE; see below).} -\item{const.dir}{Vector of character strings giving the direction of -the constraint: each value should be one of "<," "<=," "=," "==," ">," or ">=". -(In each pair the two values are identical.)} -\item{const.rhs}{Vector of numeric values for the right-hand sides of -the constraints.} -\item{transpose.constraints}{By default each constraint occupies a row -of const.mat, and that matrix needs to be transposed before being passed -to the optimizing code. For very large constraint matrices it may be wiser -to construct the constraints in a matrix column-by-column. In that case set -transpose.constraints to FALSE.} -\item{int.vec}{Numeric vector giving the indices of variables that are -required to be integer. The length of this vector will therefore be the -number of integer variables.} -\item{presolve}{Numeric: presolve? Default 0 (no); any -non-zero value means "yes." Currently ignored.} -\item{compute.sens}{Numeric: compute sensitivity? Default 0 (no); any -non-zero value means "yes."} -\item{binary.vec}{Numeric vector like int.vec giving the indices of variables -that are required to be binary.} -\item{all.int}{Logical: should all variables be integer? Default: FALSE.} -\item{all.bin}{Logical: should all variables be binary? Default: FALSE.} -\item{scale}{Integer: value for lpSolve scaling. Details can be found in -the lpSolve documentation. Set to 0 for no scaling. Default: 196} -\item{dense.const}{Three column dense constraint array. This is ignored if -const.mat is supplied. Otherwise the columns are constraint number, column -number, and value; there should be one row for each non-zero entry in the -constraint matrix.} -\item{num.bin.solns}{Integer: if all.bin=TRUE, the user can request up to -num.bin.solns optimal solutions to be returned.} -\item{use.rw}{Logical: if TRUE and num.bin.solns > 1, write the lp out to a -file and read it back in for each solution after the first. This is just to -defeat a bug somewhere. Although the default is FALSE, we recommend you set -this to TRUE if you need num.bin.solns > 1, until the bug is found.} -\item{timeout}{Integer: timeout variable in seconds, defaults to 0L which means -no limit is set.} -} -\details{ -This function calls the \code{lp_solve} 5.5 solver. That system has many options not -supported here. The current version is maintained at -\url{https://lpsolve.sourceforge.net/5.5/} - -Note that every variable is assumed to be >= 0! -} -\value{ -An lp object. See \code{\link{lp.object}} for details. -} -\author{Sam Buttrey, \email{buttrey@nps.edu}} -\seealso{\code{\link{lp.assign}}, \code{\link{lp.transport}}} -\examples{ -# -# Set up problem: maximize -# x1 + 9 x2 + x3 subject to -# x1 + 2 x2 + 3 x3 <= 9 -# 3 x1 + 2 x2 + 2 x3 <= 15 -# -f.obj <- c(1, 9, 1) -f.con <- matrix (c(1, 2, 3, 3, 2, 2), nrow=2, byrow=TRUE) -f.dir <- c("<=", "<=") -f.rhs <- c(9, 15) -# -# Now run. -# -lp ("max", f.obj, f.con, f.dir, f.rhs) -\dontrun{Success: the objective function is 40.5} -lp ("max", f.obj, f.con, f.dir, f.rhs)$solution -\dontrun{[1] 0.0 4.5 0.0} -# -# The same problem using the dense constraint approach: -# -f.con.d <- matrix (c(rep (1:2,each=3), rep (1:3, 2), t(f.con)), ncol=3) -lp ("max", f.obj, , f.dir, f.rhs, dense.const=f.con.d) -\dontrun{Success: the objective function is 40.5} -# -# Get sensitivities -# -lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$sens.coef.from -\dontrun{[1] -1e+30 2e+00 -1e+30} -lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$sens.coef.to -\dontrun{[1] 4.50e+00 1.00e+30 1.35e+01} -# -# Right now the dual values for the constraints and the variables are -# combined, constraints coming first. So in this example... -# -lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals -\dontrun{[1] 4.5 0.0 -3.5 0.0 -10.5} -# -# ...the duals of the constraints are 4.5 and 0, and of the variables, -# -3.5, 0.0, -10.5. Here are the lower and upper limits on these: -# -lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals.from -\dontrun{[1] 0e+00 -1e+30 -1e+30 -1e+30 -6e+00} -lp ("max", f.obj, f.con, f.dir, f.rhs, compute.sens=TRUE)$duals.to -\dontrun{[1] 1.5e+01 1.0e+30 3.0e+00 1.0e+30 3.0e+00} -# -# Run again, this time requiring that all three variables be integer -# -lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3) -\dontrun{Success: the objective function is 37} -lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3)$solution -\dontrun{[1] 1 4 0} -# -# You can get sensitivities in the integer case, but they're harder to -# interpret. -# -lp ("max", f.obj, f.con, f.dir, f.rhs, int.vec=1:3, compute.sens=TRUE)$duals -\dontrun{[1] 1 0 0 7 0} -# -# Here's an example in which we want more than one solution to a problem -# in which all variables are binary: the 8-queens problem, -# with dense constraints. -# -chess.obj <- rep (1, 64) -q8 <- make.q8 () -chess.dir <- rep (c("=", "<"), c(16, 26)) -chess.rhs <- rep (1, 42) -lp ('max', chess.obj, , chess.dir, chess.rhs, dense.const = q8, - all.bin=TRUE, num.bin.solns=3) -} -\keyword{optimize} diff --git a/src/lpSolve/man/lp.assign.Rd b/src/lpSolve/man/lp.assign.Rd deleted file mode 100644 index c9753dae..00000000 --- a/src/lpSolve/man/lp.assign.Rd +++ /dev/null @@ -1,56 +0,0 @@ -\name{lp.assign} -\alias{lp.assign} -\title{Integer Programming for the Assignment Problem} -\description{Interface to \code{lp_solve} linear/integer programming -system specifically for solving assignment problems} -\usage{ -lp.assign (cost.mat, direction = "min", presolve = 0, compute.sens = 0) -} -\arguments{ -\item{cost.mat}{Matrix of costs: the ij-th element is the cost of -assigning source i to destination j.} -\item{direction}{Character vector, length 1, containing either "min" -(the default) or "max"} -\item{presolve}{Numeric: presolve? Default 0 (no); any -non-zero value means "yes." Currently ignored.} -\item{compute.sens}{Numeric: compute sensitivity? Default 0 (no); any -non-zero value means "yes." In that case presolving is attempted.} -} -\details{ -This is a particular integer programming problem. All the decision variables -are assumed to be integers; each row has the constraint that its entries -must add up to 1 (so that there is one 1 and the remaining entries are 0) -and each column has the same constraint. This is assumed to be a -minimization problem. -} -\value{ -An \code{\link{lp}} object. See documentation for details. The constraints -are assumed (each row adds to 1, each column adds to 1, and no others) and -are not returned. -} -\author{Sam Buttrey, \email{buttrey@nps.edu}} -\seealso{\code{\link{lp}}, \code{\link{lp.transport}}} -\examples{ -assign.costs <- matrix (c(2, 7, 7, 2, 7, 7, 3, 2, 7, 2, 8, 10, 1, 9, 8, 2), 4, 4) -\dontrun{ -> assign.costs - [,1] [,2] [,3] [,4] -[1,] 2 7 7 1 -[2,] 7 7 2 9 -[3,] 7 3 8 8 -[4,] 2 2 10 2 -} -lp.assign (assign.costs) -\dontrun{Success: the objective function is 8} -lp.assign (assign.costs)$solution -\dontrun{ - [,1] [,2] [,3] [,4] -[1,] 0 0 0 1 -[2,] 0 0 1 0 -[3,] 0 1 0 0 -[4,] 1 0 0 0 -} -} -\keyword{optimize} - - diff --git a/src/lpSolve/man/lp.object.Rd b/src/lpSolve/man/lp.object.Rd deleted file mode 100644 index afc2db24..00000000 --- a/src/lpSolve/man/lp.object.Rd +++ /dev/null @@ -1,22 +0,0 @@ -\name{lp.object} -\alias{lp.object} -\title{LP (linear programming) object} -\description{Structure of lp object} -\value{ -An lp.object is a list containing the following elements: -\item{direction}{Optimization direction, as entered} -\item{x.count}{Number of variables in objective function} -\item{objective}{Vector of objective function coefficients, as entered} -\item{const.count}{Number of constraints entered} -\item{constraints}{Constraint matrix, as entered (not returned -by \code{\link{lp.assign}} or \code{\link{lp.transport}})} -\item{int.count}{Number of integer variables} -\item{int.vec}{Vector of integer variables' indices, as entered} -\item{objval}{{Value of objective function at optimum}} -\item{solution}{Vector of optimal coefficients} -\item{num.bin.solns}{Numeric indicator of number of solutions returned} -\item{status}{Numeric indicator: 0 = success, 2 = no feasible solution} -} -\author{Sam Buttrey, \email{buttrey@nps.edu}} -\seealso{\code{\link{lp}}, \code{\link{lp.assign}}, \code{\link{lp.transport}}} -\keyword{optimize} diff --git a/src/lpSolve/man/lp.transport.Rd b/src/lpSolve/man/lp.transport.Rd deleted file mode 100644 index e9c1ff8a..00000000 --- a/src/lpSolve/man/lp.transport.Rd +++ /dev/null @@ -1,84 +0,0 @@ -\name{lp.transport} -\alias{lp.transport} -\title{Integer Programming for the Transportation Problem} -\description{Interface to \code{lp_solve} linear/integer programming -system specifically for solving transportation problems} -\usage{ -lp.transport (cost.mat, direction="min", row.signs, row.rhs, col.signs, - col.rhs, presolve=0, compute.sens=0, integers = 1:(nc*nr) ) -} -\arguments{ -\item{cost.mat}{Matrix of costs; ij-th element is the cost of transporting -one item from source i to destination j.} -\item{direction}{Character, length 1: "min" or "max"} -\item{row.signs}{Vector of character strings giving the direction of the -row constraints: each value should be one of "<," "<=," "=," "==," ">," -or ">=." (In each pair the two values are identical.)} -\item{row.rhs}{Vector of numeric values for the right-hand sides of the -row constraints.} -\item{col.signs}{Vector of character strings giving the direction of the -column constraints: each value should be one of "<," "<=," "=," "==," ">," -or ">=."} -\item{col.rhs}{Vector of numeric values for the right-hand sides of the -column constraints.} -\item{presolve}{Numeric: presolve? Default 0 (no); any -non-zero value means "yes." Currently ignored.} -\item{compute.sens}{Numeric: compute sensitivity? Default 0 (no); any -non-zero value means "yes."} -\item{integers}{Vector of integers whose ith element gives the index -of the ith integer variable. Its length will be the number of integer -variables. Default: all variables are integer. Set to NULL to have no -variables be integer.} -} -\details{ -This is a particular integer programming problem. All the decision variables -are assumed to be integers, and there is one constraint per row and one per -column (and no others). This is assumed to be a minimization problem. -} -\value{ -An \code{\link{lp}} object. Constraints are implicit and not returned. -See documentation for details. -} -\references{Example problem from Bronson (1981), \emph{Operations Research}, -Scahum's Outline Series, McGraw-Hill.} -\author{Sam Buttrey, \email{buttrey@nps.edu}} -\seealso{\code{\link{lp.assign}}, \code{\link{lp.transport}}} - -\examples{ -# -# Transportation problem, Bronson, problem 9.1, p. 86 -# -# Set up cost matrix -# -costs <- matrix (10000, 8, 5); costs[4,1] <- costs[-4,5] <- 0 -costs[1,2] <- costs[2,3] <- costs[3,4] <- 7; costs[1,3] <- costs[2,4] <- 7.7 -costs[5,1] <- costs[7,3] <- 8; costs[1,4] <- 8.4; costs[6,2] <- 9 -costs[8,4] <- 10; costs[4,2:4] <- c(.7, 1.4, 2.1) -# -# Set up constraint signs and right-hand sides. -# -row.signs <- rep ("<", 8) -row.rhs <- c(200, 300, 350, 200, 100, 50, 100, 150) -col.signs <- rep (">", 5) -col.rhs <- c(250, 100, 400, 500, 200) -# -# Run -# -lp.transport (costs, "min", row.signs, row.rhs, col.signs, col.rhs) -\dontrun{Success: the objective function is 7790} -lp.transport (costs, "min", row.signs, row.rhs, col.signs, col.rhs)$solution -\dontrun{ - [,1] [,2] [,3] [,4] [,5] -[1,] 0 100 0 100 0 -[2,] 0 0 300 0 0 -[3,] 0 0 0 350 0 -[4,] 200 0 0 0 0 -[5,] 50 0 0 0 50 -[6,] 0 0 0 0 50 -[7,] 0 0 100 0 0 -[8,] 0 0 0 50 100 -} -} -\keyword{optimize} - - diff --git a/src/lpSolve/man/make.q8.Rd b/src/lpSolve/man/make.q8.Rd deleted file mode 100644 index 23cd2f7a..00000000 --- a/src/lpSolve/man/make.q8.Rd +++ /dev/null @@ -1,21 +0,0 @@ -\name{make.q8} -\alias{make.q8} -\alias{8-queens problem} -\title{Generate sparse constraint matrix for 8-queens problem} -\description{Generate sparse constraint matrix for 8-queens problem} -\usage{ make.q8 () } -\arguments{None.} -\details{ -Sparse constraints come in a three-column matrix or data frame. Each row -gives the row number, column number, and value of a particular non-zero -entry in the constraint matrix. This function produces the sparse constraint -matrix for the 8-queens problem (in which the object is to place eight queens -on a chessboard with no two sharing a row, column or diagonal). The resulting -sparse representation is 252 x 3, compared to 42 x 64 for the usual -representation.} -\value{ -A 252 x 3 numeric matrix. See \link{lp} for the complete example. -} -\author{Sam Buttrey, \email{buttrey@nps.edu}} -\seealso{\code{\link{lp}}} -\keyword{optimize} diff --git a/src/lpSolve/man/print.lp.Rd b/src/lpSolve/man/print.lp.Rd deleted file mode 100644 index bbe94027..00000000 --- a/src/lpSolve/man/print.lp.Rd +++ /dev/null @@ -1,25 +0,0 @@ -\name{print.lp} -\alias{print.lp} -\title{Print an lp object} -\description{Print method for lp objects} -\usage{ -\method{print}{lp} (x, \ldots) -} -\arguments{ -\item{x}{List with items named \code{objval} and \code{status}. -Normally this will have been called by \code{\link{lp}}, -\code{\link{lp.assign}}, or \code{\link{lp.transport}}.} -\item{...}{Other arguments, all currently ignored} -} -\details{ -This function prints the objective function value, together with the -word "Success" if the operation is successful, or an indication of the -error if not. If multiple solutions have been produced (because this was -an all-binary problem and lp was called with num.bin.solns > 1) the number -of solutions is also displayed.} -\value{ -None -} -\author{Sam Buttrey, \email{buttrey@nps.edu}} -\seealso{\code{\link{lp}}, \code{\link{lp.assign}}, \code{\link{lp.transport}}} -\keyword{optimize} From a54c8a7b747d8143d407f5392a5b2a647aa4b745 Mon Sep 17 00:00:00 2001 From: vfisikop Date: Thu, 29 Feb 2024 16:27:36 +0200 Subject: [PATCH 16/17] Remove submodules and add volesti and externals directly in the repo. commit hash from volesti 5bf9188fb11fddab17de98fabc505e993216ca5f --- .github/workflows/R-CMD-check-macOS.yml | 7 - .github/workflows/R-CMD-check-ubuntu.yml | 7 - .github/workflows/R-CMD-check-windows.yml | 7 - .gitmodules | 3 - inst/AUTHORS | 9 +- inst/COPYRIGHTS | 7 +- src/Makevars | 10 +- src/Makevars.win | 10 +- src/external/PackedCSparse/FloatArray.h | 307 ++ src/external/PackedCSparse/FloatArrayAVX2.h | 222 ++ src/external/PackedCSparse/PackedChol.h | 308 ++ src/external/PackedCSparse/SparseMatrix.h | 230 ++ src/external/PackedCSparse/add.h | 102 + src/external/PackedCSparse/chol.h | 247 ++ src/external/PackedCSparse/leverage.h | 65 + src/external/PackedCSparse/leverageJL.h | 148 + src/external/PackedCSparse/multiply.h | 142 + src/external/PackedCSparse/outerprod.h | 86 + src/external/PackedCSparse/projinv.h | 90 + src/external/PackedCSparse/qd/COPYING | 16 + src/external/PackedCSparse/qd/Makefile | 15 + src/external/PackedCSparse/qd/NEWS | 181 ++ src/external/PackedCSparse/qd/README | 437 +++ src/external/PackedCSparse/qd/bits.cc | 85 + src/external/PackedCSparse/qd/bits.h | 32 + src/external/PackedCSparse/qd/c_dd.cc | 314 ++ src/external/PackedCSparse/qd/c_dd.h | 98 + src/external/PackedCSparse/qd/c_qd.cc | 450 +++ src/external/PackedCSparse/qd/c_qd.h | 119 + src/external/PackedCSparse/qd/dd_const.cc | 40 + src/external/PackedCSparse/qd/dd_inline.h | 621 ++++ src/external/PackedCSparse/qd/dd_real.cc | 1303 ++++++++ src/external/PackedCSparse/qd/dd_real.h | 293 ++ src/external/PackedCSparse/qd/fpu.cc | 124 + src/external/PackedCSparse/qd/fpu.h | 39 + src/external/PackedCSparse/qd/inline.h | 143 + src/external/PackedCSparse/qd/qd.pdf | Bin 0 -> 194502 bytes src/external/PackedCSparse/qd/qd_config.h | 92 + src/external/PackedCSparse/qd/qd_const.cc | 62 + src/external/PackedCSparse/qd/qd_inline.h | 1047 +++++++ src/external/PackedCSparse/qd/qd_real.cc | 2624 +++++++++++++++++ src/external/PackedCSparse/qd/qd_real.h | 296 ++ src/external/PackedCSparse/qd/util.cc | 22 + src/external/PackedCSparse/qd/util.h | 4 + src/external/PackedCSparse/transpose.h | 90 + .../Spectra/include/Spectra/GenEigsBase.h | 479 +++ .../Spectra/GenEigsComplexShiftSolver.h | 156 + .../include/Spectra/GenEigsRealShiftSolver.h | 90 + .../Spectra/include/Spectra/GenEigsSolver.h | 160 + .../Spectra/include/Spectra/LinAlg/Arnoldi.h | 283 ++ .../Spectra/include/Spectra/LinAlg/BKLDLT.h | 522 ++++ .../include/Spectra/LinAlg/DoubleShiftQR.h | 378 +++ .../Spectra/include/Spectra/LinAlg/Lanczos.h | 170 ++ .../include/Spectra/LinAlg/TridiagEigen.h | 219 ++ .../Spectra/LinAlg/UpperHessenbergEigen.h | 317 ++ .../Spectra/LinAlg/UpperHessenbergQR.h | 670 +++++ .../include/Spectra/MatOp/DenseCholesky.h | 110 + .../Spectra/MatOp/DenseGenComplexShiftSolve.h | 104 + .../include/Spectra/MatOp/DenseGenMatProd.h | 82 + .../Spectra/MatOp/DenseGenRealShiftSolve.h | 90 + .../include/Spectra/MatOp/DenseSymMatProd.h | 75 + .../Spectra/MatOp/DenseSymShiftSolve.h | 94 + .../include/Spectra/MatOp/SparseCholesky.h | 111 + .../include/Spectra/MatOp/SparseGenMatProd.h | 76 + .../Spectra/MatOp/SparseGenRealShiftSolve.h | 95 + .../Spectra/MatOp/SparseRegularInverse.h | 102 + .../include/Spectra/MatOp/SparseSymMatProd.h | 75 + .../Spectra/MatOp/SparseSymShiftSolve.h | 97 + .../Spectra/MatOp/internal/ArnoldiOp.h | 155 + .../MatOp/internal/SymGEigsCholeskyOp.h | 77 + .../Spectra/MatOp/internal/SymGEigsRegInvOp.h | 74 + .../Spectra/include/Spectra/SymEigsBase.h | 447 +++ .../include/Spectra/SymEigsShiftSolver.h | 205 ++ .../Spectra/include/Spectra/SymEigsSolver.h | 174 ++ .../Spectra/include/Spectra/SymGEigsSolver.h | 334 +++ .../Spectra/include/Spectra/Util/CompInfo.h | 37 + .../Spectra/include/Spectra/Util/GEigsMode.h | 34 + .../include/Spectra/Util/SelectionRule.h | 277 ++ .../include/Spectra/Util/SimpleRandom.h | 93 + .../Spectra/include/Spectra/Util/TypeTraits.h | 73 + .../include/Spectra/contrib/LOBPCGSolver.h | 501 ++++ .../Spectra/contrib/PartialSVDSolver.h | 203 ++ src/external/minimum_ellipsoid/bnmin_main.h | 87 + src/external/minimum_ellipsoid/khach.h | 220 ++ src/external/minimum_ellipsoid/mcpoint.h | 477 +++ src/volesti | 1 - src/volesti/include/SDPAFormatManager.h | 271 ++ .../include/cartesian_geom/cartesian_kernel.h | 39 + src/volesti/include/cartesian_geom/point.h | 238 ++ src/volesti/include/convex_bodies/ball.h | 176 ++ .../convex_bodies/ballintersectconvex.h | 401 +++ src/volesti/include/convex_bodies/barriers.h | 52 + .../include/convex_bodies/convex_body.h | 109 + .../correlation_matrices/corre_matrix.hpp | 161 + .../correlation_spectrahedron.hpp | 266 ++ .../correlation_spectrahedron_MT.hpp | 180 ++ src/volesti/include/convex_bodies/ellipsoid.h | 281 ++ src/volesti/include/convex_bodies/hpolytope.h | 927 ++++++ .../include/convex_bodies/orderpolytope.h | 755 +++++ .../include/convex_bodies/spectrahedra/LMI.h | 251 ++ .../spectrahedra/spectrahedron.h | 492 ++++ .../convex_bodies/vpolyintersectvpoly.h | 429 +++ src/volesti/include/convex_bodies/vpolytope.h | 648 ++++ .../convex_bodies/zonoIntersecthpoly.h | 272 ++ src/volesti/include/convex_bodies/zpolytope.h | 619 ++++ .../include/diagnostics/diagnostics.hpp | 24 + .../diagnostics/effective_sample_size.hpp | 123 + .../ess_updater_autocovariance.hpp | 69 + .../diagnostics/ess_window_updater.hpp | 154 + src/volesti/include/diagnostics/geweke.hpp | 79 + .../include/diagnostics/interval_psrf.hpp | 79 + .../include/diagnostics/multivariate_psrf.hpp | 55 + .../include/diagnostics/print_diagnostics.hpp | 56 + src/volesti/include/diagnostics/raftery.hpp | 142 + .../raftery_subroutines/empquant.hpp | 32 + .../raftery_subroutines/indtest.hpp | 43 + .../diagnostics/raftery_subroutines/mcest.hpp | 28 + .../raftery_subroutines/mctest.hpp | 63 + .../diagnostics/raftery_subroutines/ppnd.hpp | 56 + .../diagnostics/raftery_subroutines/thin.hpp | 32 + .../include/diagnostics/thin_samples.hpp | 37 + .../include/diagnostics/univariate_psrf.hpp | 67 + .../boost_random_number_generator.hpp | 95 + .../generators/convex_bodies_generator.h | 124 + .../generators/h_polytopes_generator.h | 61 + .../generators/known_polytope_generators.h | 361 +++ .../generators/order_polytope_generator.h | 63 + .../include/generators/sdp_generator.h | 151 + .../generators/v_polytopes_generators.h | 175 ++ .../generators/z_polytopes_generators.h | 145 + .../integration/simple_MC_integration.hpp | 276 ++ src/volesti/include/lp_oracles/misc_lp.h | 145 + src/volesti/include/lp_oracles/solve_lp.h | 296 ++ src/volesti/include/lp_oracles/vpolyoracles.h | 427 +++ src/volesti/include/lp_oracles/zpolyoracles.h | 221 ++ .../matrix_operations/DenseProductMatrix.h | 105 + .../matrix_operations/EigenDenseMatrix.h | 76 + .../matrix_operations/EigenvaluesProblems.h | 450 +++ src/volesti/include/misc/linear_extensions.h | 92 + src/volesti/include/misc/misc.h | 268 ++ src/volesti/include/misc/poset.h | 131 + src/volesti/include/misc/print_table.hpp | 384 +++ .../include/nlp_oracles/nlp_hpolyoracles.hpp | 572 ++++ .../include/nlp_oracles/nlp_oracles.hpp | 22 + .../include/nlp_oracles/nlp_vpolyoracles.hpp | 314 ++ src/volesti/include/ode_solvers/basis.hpp | 186 ++ .../include/ode_solvers/collocation.hpp | 298 ++ src/volesti/include/ode_solvers/euler.hpp | 130 + .../ode_solvers/generalized_leapfrog.hpp | 172 ++ .../include/ode_solvers/implicit_midpoint.hpp | 177 ++ .../ode_solvers/integral_collocation.hpp | 289 ++ src/volesti/include/ode_solvers/leapfrog.hpp | 221 ++ .../include/ode_solvers/ode_solvers.hpp | 64 + .../include/ode_solvers/oracle_functors.hpp | 397 +++ .../ode_solvers/oracle_functors_rcpp.hpp | 159 + .../ode_solvers/randomized_midpoint.hpp | 226 ++ .../ode_solvers/richardson_extrapolation.hpp | 188 ++ .../include/ode_solvers/runge_kutta.hpp | 172 ++ .../optimization/simulated_annealing.hpp | 152 + .../include/optimization/sliding_window.hpp | 66 + .../preprocess/crhmc/analytic_center.h | 173 ++ .../preprocess/crhmc/constraint_problem.h | 81 + .../include/preprocess/crhmc/crhmc_input.h | 172 ++ .../include/preprocess/crhmc/crhmc_problem.h | 703 +++++ .../include/preprocess/crhmc/crhmc_utils.h | 406 +++ .../include/preprocess/crhmc/lewis_center.h | 188 ++ src/volesti/include/preprocess/crhmc/opts.h | 62 + .../preprocess/crhmc/two_sided_barrier.h | 173 ++ .../crhmc/weighted_two_sided_barrier.h | 166 ++ .../estimate_L_smooth_parameter.hpp | 73 + .../include/preprocess/max_inscribed_ball.hpp | 216 ++ .../preprocess/max_inscribed_ellipsoid.hpp | 236 ++ .../max_inscribed_ellipsoid_rounding.hpp | 77 + ...n_sampling_covering_ellipsoid_rounding.hpp | 123 + .../include/preprocess/svd_rounding.hpp | 176 ++ .../random_walks/boltzmann_hmc_walk.hpp | 216 ++ .../random_walks/boundary_cdhr_walk.hpp | 91 + .../random_walks/boundary_rdhr_walk.hpp | 85 + .../include/random_walks/compute_diameter.hpp | 225 ++ .../crhmc/additional_units/auto_tuner.hpp | 64 + .../additional_units/dynamic_regularizer.hpp | 59 + .../additional_units/dynamic_step_size.hpp | 118 + .../crhmc/additional_units/dynamic_weight.hpp | 76 + .../include/random_walks/crhmc/crhmc_walk.hpp | 245 ++ .../random_walks/crhmc/hamiltonian.hpp | 249 ++ .../random_walks/ellipsoid_walks/README.md | 7 + .../ellipsoid_walks/dikin_walker.h | 157 + .../ellipsoid_walks/john_walker.h | 204 ++ .../ellipsoid_walks/math_functions.h | 54 + .../ellipsoid_walks/vaidya_walker.h | 171 ++ ...ial_hamiltonian_monte_carlo_exact_walk.hpp | 300 ++ .../gaussian_accelerated_billiard_walk.hpp | 248 ++ .../random_walks/gaussian_ball_walk.hpp | 107 + .../random_walks/gaussian_cdhr_walk.hpp | 151 + ...ian_hamiltonian_monte_carlo_exact_walk.hpp | 275 ++ .../include/random_walks/gaussian_helpers.hpp | 48 + .../random_walks/gaussian_rdhr_walk.hpp | 118 + .../hamiltonian_monte_carlo_walk.hpp | 179 ++ .../include/random_walks/langevin_walk.hpp | 153 + .../random_walks/multithread_walks.hpp | 739 +++++ .../include/random_walks/nuts_hmc_walk.hpp | 380 +++ .../include/random_walks/random_walks.hpp | 34 + .../uniform_accelerated_billiard_walk.hpp | 309 ++ ...orm_accelerated_billiard_walk_parallel.hpp | 311 ++ .../random_walks/uniform_ball_walk.hpp | 96 + .../random_walks/uniform_billiard_walk.hpp | 200 ++ .../random_walks/uniform_cdhr_walk.hpp | 97 + .../random_walks/uniform_dikin_walk.hpp | 102 + .../random_walks/uniform_john_walk.hpp | 94 + .../random_walks/uniform_rdhr_walk.hpp | 92 + .../random_walks/uniform_vaidya_walk.hpp | 95 + .../include/root_finders/mp_solve_wrapper.hpp | 75 + .../include/root_finders/newton_raphson.hpp | 49 + .../quadratic_polynomial_solvers.hpp | 42 + .../include/root_finders/root_finders.hpp | 28 + src/volesti/include/sampling/ellipsoid.hpp | 82 + src/volesti/include/sampling/mmcs.hpp | 128 + .../include/sampling/parallel_mmcs.hpp | 203 ++ .../sampling/random_point_generators.hpp | 395 +++ .../random_point_generators_multithread.hpp | 256 ++ .../sampling/sample_correlation_matrices.hpp | 153 + src/volesti/include/sampling/sampling.hpp | 603 ++++ src/volesti/include/sampling/simplex.hpp | 392 +++ src/volesti/include/sampling/sphere.hpp | 132 + src/volesti/include/sos/NonSymmetricIPM.h | 333 +++ src/volesti/include/sos/NonSymmetricIPM.hpp | 781 +++++ src/volesti/include/sos/README.md | 104 + .../sos/barriers/DualSOSConeStandardBarrier.h | 49 + .../barriers/DualSOSConeStandardBarrier.hpp | 81 + .../include/sos/barriers/FullSpaceBarrier.h | 46 + .../include/sos/barriers/FullSpaceBarrier.hpp | 66 + .../sos/barriers/InterpolantDualSOSBarrier.h | 113 + .../barriers/InterpolantDualSOSBarrier.hpp | 493 ++++ src/volesti/include/sos/barriers/LHSCB.h | 89 + src/volesti/include/sos/barriers/LHSCB.hpp | 89 + .../include/sos/barriers/LPStandardBarrier.h | 47 + .../sos/barriers/LPStandardBarrier.hpp | 43 + .../include/sos/barriers/ProductBarrier.h | 113 + .../include/sos/barriers/ProductBarrier.hpp | 173 ++ src/volesti/include/sos/barriers/SumBarrier.h | 59 + .../include/sos/barriers/SumBarrier.hpp | 101 + .../include/sos/barriers/ZeroSpaceBarrier.h | 44 + .../include/sos/barriers/ZeroSpaceBarrier.hpp | 46 + src/volesti/include/sos/config/config.json | 21 + src/volesti/include/sos/gsoc_history.md | 15 + src/volesti/include/sos/include/cxxtimer.hpp | 184 ++ .../include/matplotlib-cpp/matplotlibcpp.h | 2366 +++++++++++++++ src/volesti/include/sos/plot_saved.png | Bin 0 -> 132753 bytes src/volesti/include/sos/utils.cpp | 6 + src/volesti/include/sos/utils.h | 331 +++ src/volesti/include/volume/copulas.h | 244 ++ src/volesti/include/volume/exact_vols.h | 101 + src/volesti/include/volume/math_helpers.hpp | 46 + src/volesti/include/volume/rotating.hpp | 64 + .../include/volume/sampling_policies.hpp | 50 + .../include/volume/volume_cooling_balls.hpp | 853 ++++++ .../volume/volume_cooling_gaussians.hpp | 490 +++ .../include/volume/volume_cooling_hpoly.hpp | 385 +++ .../volume/volume_sequence_of_balls.hpp | 266 ++ 259 files changed, 54098 insertions(+), 41 deletions(-) delete mode 100644 .gitmodules create mode 100644 src/external/PackedCSparse/FloatArray.h create mode 100644 src/external/PackedCSparse/FloatArrayAVX2.h create mode 100644 src/external/PackedCSparse/PackedChol.h create mode 100644 src/external/PackedCSparse/SparseMatrix.h create mode 100644 src/external/PackedCSparse/add.h create mode 100644 src/external/PackedCSparse/chol.h create mode 100644 src/external/PackedCSparse/leverage.h create mode 100644 src/external/PackedCSparse/leverageJL.h create mode 100644 src/external/PackedCSparse/multiply.h create mode 100644 src/external/PackedCSparse/outerprod.h create mode 100644 src/external/PackedCSparse/projinv.h create mode 100644 src/external/PackedCSparse/qd/COPYING create mode 100644 src/external/PackedCSparse/qd/Makefile create mode 100644 src/external/PackedCSparse/qd/NEWS create mode 100644 src/external/PackedCSparse/qd/README create mode 100644 src/external/PackedCSparse/qd/bits.cc create mode 100644 src/external/PackedCSparse/qd/bits.h create mode 100644 src/external/PackedCSparse/qd/c_dd.cc create mode 100644 src/external/PackedCSparse/qd/c_dd.h create mode 100644 src/external/PackedCSparse/qd/c_qd.cc create mode 100644 src/external/PackedCSparse/qd/c_qd.h create mode 100644 src/external/PackedCSparse/qd/dd_const.cc create mode 100644 src/external/PackedCSparse/qd/dd_inline.h create mode 100644 src/external/PackedCSparse/qd/dd_real.cc create mode 100644 src/external/PackedCSparse/qd/dd_real.h create mode 100644 src/external/PackedCSparse/qd/fpu.cc create mode 100644 src/external/PackedCSparse/qd/fpu.h create mode 100644 src/external/PackedCSparse/qd/inline.h create mode 100644 src/external/PackedCSparse/qd/qd.pdf create mode 100644 src/external/PackedCSparse/qd/qd_config.h create mode 100644 src/external/PackedCSparse/qd/qd_const.cc create mode 100644 src/external/PackedCSparse/qd/qd_inline.h create mode 100644 src/external/PackedCSparse/qd/qd_real.cc create mode 100644 src/external/PackedCSparse/qd/qd_real.h create mode 100644 src/external/PackedCSparse/qd/util.cc create mode 100644 src/external/PackedCSparse/qd/util.h create mode 100644 src/external/PackedCSparse/transpose.h create mode 100644 src/external/Spectra/include/Spectra/GenEigsBase.h create mode 100644 src/external/Spectra/include/Spectra/GenEigsComplexShiftSolver.h create mode 100644 src/external/Spectra/include/Spectra/GenEigsRealShiftSolver.h create mode 100644 src/external/Spectra/include/Spectra/GenEigsSolver.h create mode 100644 src/external/Spectra/include/Spectra/LinAlg/Arnoldi.h create mode 100644 src/external/Spectra/include/Spectra/LinAlg/BKLDLT.h create mode 100644 src/external/Spectra/include/Spectra/LinAlg/DoubleShiftQR.h create mode 100644 src/external/Spectra/include/Spectra/LinAlg/Lanczos.h create mode 100644 src/external/Spectra/include/Spectra/LinAlg/TridiagEigen.h create mode 100644 src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergEigen.h create mode 100644 src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergQR.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseCholesky.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseGenMatProd.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseSymMatProd.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/DenseSymShiftSolve.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseCholesky.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseGenMatProd.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseRegularInverse.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseSymMatProd.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/SparseSymShiftSolve.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/internal/ArnoldiOp.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h create mode 100644 src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h create mode 100644 src/external/Spectra/include/Spectra/SymEigsBase.h create mode 100644 src/external/Spectra/include/Spectra/SymEigsShiftSolver.h create mode 100644 src/external/Spectra/include/Spectra/SymEigsSolver.h create mode 100644 src/external/Spectra/include/Spectra/SymGEigsSolver.h create mode 100644 src/external/Spectra/include/Spectra/Util/CompInfo.h create mode 100644 src/external/Spectra/include/Spectra/Util/GEigsMode.h create mode 100644 src/external/Spectra/include/Spectra/Util/SelectionRule.h create mode 100644 src/external/Spectra/include/Spectra/Util/SimpleRandom.h create mode 100644 src/external/Spectra/include/Spectra/Util/TypeTraits.h create mode 100644 src/external/Spectra/include/Spectra/contrib/LOBPCGSolver.h create mode 100644 src/external/Spectra/include/Spectra/contrib/PartialSVDSolver.h create mode 100644 src/external/minimum_ellipsoid/bnmin_main.h create mode 100644 src/external/minimum_ellipsoid/khach.h create mode 100644 src/external/minimum_ellipsoid/mcpoint.h delete mode 160000 src/volesti create mode 100644 src/volesti/include/SDPAFormatManager.h create mode 100644 src/volesti/include/cartesian_geom/cartesian_kernel.h create mode 100644 src/volesti/include/cartesian_geom/point.h create mode 100644 src/volesti/include/convex_bodies/ball.h create mode 100644 src/volesti/include/convex_bodies/ballintersectconvex.h create mode 100644 src/volesti/include/convex_bodies/barriers.h create mode 100644 src/volesti/include/convex_bodies/convex_body.h create mode 100755 src/volesti/include/convex_bodies/correlation_matrices/corre_matrix.hpp create mode 100755 src/volesti/include/convex_bodies/correlation_matrices/correlation_spectrahedron.hpp create mode 100755 src/volesti/include/convex_bodies/correlation_matrices/correlation_spectrahedron_MT.hpp create mode 100644 src/volesti/include/convex_bodies/ellipsoid.h create mode 100644 src/volesti/include/convex_bodies/hpolytope.h create mode 100644 src/volesti/include/convex_bodies/orderpolytope.h create mode 100644 src/volesti/include/convex_bodies/spectrahedra/LMI.h create mode 100644 src/volesti/include/convex_bodies/spectrahedra/spectrahedron.h create mode 100644 src/volesti/include/convex_bodies/vpolyintersectvpoly.h create mode 100644 src/volesti/include/convex_bodies/vpolytope.h create mode 100644 src/volesti/include/convex_bodies/zonoIntersecthpoly.h create mode 100644 src/volesti/include/convex_bodies/zpolytope.h create mode 100644 src/volesti/include/diagnostics/diagnostics.hpp create mode 100644 src/volesti/include/diagnostics/effective_sample_size.hpp create mode 100644 src/volesti/include/diagnostics/ess_updater_autocovariance.hpp create mode 100644 src/volesti/include/diagnostics/ess_window_updater.hpp create mode 100644 src/volesti/include/diagnostics/geweke.hpp create mode 100644 src/volesti/include/diagnostics/interval_psrf.hpp create mode 100644 src/volesti/include/diagnostics/multivariate_psrf.hpp create mode 100644 src/volesti/include/diagnostics/print_diagnostics.hpp create mode 100644 src/volesti/include/diagnostics/raftery.hpp create mode 100644 src/volesti/include/diagnostics/raftery_subroutines/empquant.hpp create mode 100644 src/volesti/include/diagnostics/raftery_subroutines/indtest.hpp create mode 100644 src/volesti/include/diagnostics/raftery_subroutines/mcest.hpp create mode 100644 src/volesti/include/diagnostics/raftery_subroutines/mctest.hpp create mode 100644 src/volesti/include/diagnostics/raftery_subroutines/ppnd.hpp create mode 100644 src/volesti/include/diagnostics/raftery_subroutines/thin.hpp create mode 100644 src/volesti/include/diagnostics/thin_samples.hpp create mode 100644 src/volesti/include/diagnostics/univariate_psrf.hpp create mode 100644 src/volesti/include/generators/boost_random_number_generator.hpp create mode 100644 src/volesti/include/generators/convex_bodies_generator.h create mode 100644 src/volesti/include/generators/h_polytopes_generator.h create mode 100644 src/volesti/include/generators/known_polytope_generators.h create mode 100644 src/volesti/include/generators/order_polytope_generator.h create mode 100644 src/volesti/include/generators/sdp_generator.h create mode 100644 src/volesti/include/generators/v_polytopes_generators.h create mode 100644 src/volesti/include/generators/z_polytopes_generators.h create mode 100644 src/volesti/include/integration/simple_MC_integration.hpp create mode 100644 src/volesti/include/lp_oracles/misc_lp.h create mode 100644 src/volesti/include/lp_oracles/solve_lp.h create mode 100644 src/volesti/include/lp_oracles/vpolyoracles.h create mode 100644 src/volesti/include/lp_oracles/zpolyoracles.h create mode 100644 src/volesti/include/matrix_operations/DenseProductMatrix.h create mode 100644 src/volesti/include/matrix_operations/EigenDenseMatrix.h create mode 100644 src/volesti/include/matrix_operations/EigenvaluesProblems.h create mode 100644 src/volesti/include/misc/linear_extensions.h create mode 100644 src/volesti/include/misc/misc.h create mode 100644 src/volesti/include/misc/poset.h create mode 100644 src/volesti/include/misc/print_table.hpp create mode 100644 src/volesti/include/nlp_oracles/nlp_hpolyoracles.hpp create mode 100644 src/volesti/include/nlp_oracles/nlp_oracles.hpp create mode 100644 src/volesti/include/nlp_oracles/nlp_vpolyoracles.hpp create mode 100644 src/volesti/include/ode_solvers/basis.hpp create mode 100644 src/volesti/include/ode_solvers/collocation.hpp create mode 100644 src/volesti/include/ode_solvers/euler.hpp create mode 100644 src/volesti/include/ode_solvers/generalized_leapfrog.hpp create mode 100644 src/volesti/include/ode_solvers/implicit_midpoint.hpp create mode 100644 src/volesti/include/ode_solvers/integral_collocation.hpp create mode 100644 src/volesti/include/ode_solvers/leapfrog.hpp create mode 100644 src/volesti/include/ode_solvers/ode_solvers.hpp create mode 100644 src/volesti/include/ode_solvers/oracle_functors.hpp create mode 100644 src/volesti/include/ode_solvers/oracle_functors_rcpp.hpp create mode 100644 src/volesti/include/ode_solvers/randomized_midpoint.hpp create mode 100644 src/volesti/include/ode_solvers/richardson_extrapolation.hpp create mode 100644 src/volesti/include/ode_solvers/runge_kutta.hpp create mode 100644 src/volesti/include/optimization/simulated_annealing.hpp create mode 100644 src/volesti/include/optimization/sliding_window.hpp create mode 100644 src/volesti/include/preprocess/crhmc/analytic_center.h create mode 100644 src/volesti/include/preprocess/crhmc/constraint_problem.h create mode 100644 src/volesti/include/preprocess/crhmc/crhmc_input.h create mode 100644 src/volesti/include/preprocess/crhmc/crhmc_problem.h create mode 100644 src/volesti/include/preprocess/crhmc/crhmc_utils.h create mode 100644 src/volesti/include/preprocess/crhmc/lewis_center.h create mode 100644 src/volesti/include/preprocess/crhmc/opts.h create mode 100644 src/volesti/include/preprocess/crhmc/two_sided_barrier.h create mode 100644 src/volesti/include/preprocess/crhmc/weighted_two_sided_barrier.h create mode 100644 src/volesti/include/preprocess/estimate_L_smooth_parameter.hpp create mode 100644 src/volesti/include/preprocess/max_inscribed_ball.hpp create mode 100644 src/volesti/include/preprocess/max_inscribed_ellipsoid.hpp create mode 100644 src/volesti/include/preprocess/max_inscribed_ellipsoid_rounding.hpp create mode 100644 src/volesti/include/preprocess/min_sampling_covering_ellipsoid_rounding.hpp create mode 100644 src/volesti/include/preprocess/svd_rounding.hpp create mode 100644 src/volesti/include/random_walks/boltzmann_hmc_walk.hpp create mode 100644 src/volesti/include/random_walks/boundary_cdhr_walk.hpp create mode 100644 src/volesti/include/random_walks/boundary_rdhr_walk.hpp create mode 100644 src/volesti/include/random_walks/compute_diameter.hpp create mode 100644 src/volesti/include/random_walks/crhmc/additional_units/auto_tuner.hpp create mode 100644 src/volesti/include/random_walks/crhmc/additional_units/dynamic_regularizer.hpp create mode 100644 src/volesti/include/random_walks/crhmc/additional_units/dynamic_step_size.hpp create mode 100644 src/volesti/include/random_walks/crhmc/additional_units/dynamic_weight.hpp create mode 100644 src/volesti/include/random_walks/crhmc/crhmc_walk.hpp create mode 100644 src/volesti/include/random_walks/crhmc/hamiltonian.hpp create mode 100644 src/volesti/include/random_walks/ellipsoid_walks/README.md create mode 100644 src/volesti/include/random_walks/ellipsoid_walks/dikin_walker.h create mode 100644 src/volesti/include/random_walks/ellipsoid_walks/john_walker.h create mode 100644 src/volesti/include/random_walks/ellipsoid_walks/math_functions.h create mode 100644 src/volesti/include/random_walks/ellipsoid_walks/vaidya_walker.h create mode 100644 src/volesti/include/random_walks/exponential_hamiltonian_monte_carlo_exact_walk.hpp create mode 100644 src/volesti/include/random_walks/gaussian_accelerated_billiard_walk.hpp create mode 100644 src/volesti/include/random_walks/gaussian_ball_walk.hpp create mode 100644 src/volesti/include/random_walks/gaussian_cdhr_walk.hpp create mode 100644 src/volesti/include/random_walks/gaussian_hamiltonian_monte_carlo_exact_walk.hpp create mode 100644 src/volesti/include/random_walks/gaussian_helpers.hpp create mode 100644 src/volesti/include/random_walks/gaussian_rdhr_walk.hpp create mode 100644 src/volesti/include/random_walks/hamiltonian_monte_carlo_walk.hpp create mode 100644 src/volesti/include/random_walks/langevin_walk.hpp create mode 100644 src/volesti/include/random_walks/multithread_walks.hpp create mode 100644 src/volesti/include/random_walks/nuts_hmc_walk.hpp create mode 100644 src/volesti/include/random_walks/random_walks.hpp create mode 100644 src/volesti/include/random_walks/uniform_accelerated_billiard_walk.hpp create mode 100644 src/volesti/include/random_walks/uniform_accelerated_billiard_walk_parallel.hpp create mode 100644 src/volesti/include/random_walks/uniform_ball_walk.hpp create mode 100644 src/volesti/include/random_walks/uniform_billiard_walk.hpp create mode 100644 src/volesti/include/random_walks/uniform_cdhr_walk.hpp create mode 100644 src/volesti/include/random_walks/uniform_dikin_walk.hpp create mode 100644 src/volesti/include/random_walks/uniform_john_walk.hpp create mode 100644 src/volesti/include/random_walks/uniform_rdhr_walk.hpp create mode 100644 src/volesti/include/random_walks/uniform_vaidya_walk.hpp create mode 100644 src/volesti/include/root_finders/mp_solve_wrapper.hpp create mode 100644 src/volesti/include/root_finders/newton_raphson.hpp create mode 100644 src/volesti/include/root_finders/quadratic_polynomial_solvers.hpp create mode 100644 src/volesti/include/root_finders/root_finders.hpp create mode 100644 src/volesti/include/sampling/ellipsoid.hpp create mode 100644 src/volesti/include/sampling/mmcs.hpp create mode 100644 src/volesti/include/sampling/parallel_mmcs.hpp create mode 100644 src/volesti/include/sampling/random_point_generators.hpp create mode 100644 src/volesti/include/sampling/random_point_generators_multithread.hpp create mode 100644 src/volesti/include/sampling/sample_correlation_matrices.hpp create mode 100644 src/volesti/include/sampling/sampling.hpp create mode 100644 src/volesti/include/sampling/simplex.hpp create mode 100644 src/volesti/include/sampling/sphere.hpp create mode 100644 src/volesti/include/sos/NonSymmetricIPM.h create mode 100644 src/volesti/include/sos/NonSymmetricIPM.hpp create mode 100644 src/volesti/include/sos/README.md create mode 100644 src/volesti/include/sos/barriers/DualSOSConeStandardBarrier.h create mode 100644 src/volesti/include/sos/barriers/DualSOSConeStandardBarrier.hpp create mode 100644 src/volesti/include/sos/barriers/FullSpaceBarrier.h create mode 100644 src/volesti/include/sos/barriers/FullSpaceBarrier.hpp create mode 100644 src/volesti/include/sos/barriers/InterpolantDualSOSBarrier.h create mode 100644 src/volesti/include/sos/barriers/InterpolantDualSOSBarrier.hpp create mode 100644 src/volesti/include/sos/barriers/LHSCB.h create mode 100644 src/volesti/include/sos/barriers/LHSCB.hpp create mode 100644 src/volesti/include/sos/barriers/LPStandardBarrier.h create mode 100644 src/volesti/include/sos/barriers/LPStandardBarrier.hpp create mode 100644 src/volesti/include/sos/barriers/ProductBarrier.h create mode 100644 src/volesti/include/sos/barriers/ProductBarrier.hpp create mode 100644 src/volesti/include/sos/barriers/SumBarrier.h create mode 100644 src/volesti/include/sos/barriers/SumBarrier.hpp create mode 100644 src/volesti/include/sos/barriers/ZeroSpaceBarrier.h create mode 100644 src/volesti/include/sos/barriers/ZeroSpaceBarrier.hpp create mode 100644 src/volesti/include/sos/config/config.json create mode 100644 src/volesti/include/sos/gsoc_history.md create mode 100644 src/volesti/include/sos/include/cxxtimer.hpp create mode 100644 src/volesti/include/sos/include/matplotlib-cpp/matplotlibcpp.h create mode 100644 src/volesti/include/sos/plot_saved.png create mode 100644 src/volesti/include/sos/utils.cpp create mode 100644 src/volesti/include/sos/utils.h create mode 100644 src/volesti/include/volume/copulas.h create mode 100644 src/volesti/include/volume/exact_vols.h create mode 100644 src/volesti/include/volume/math_helpers.hpp create mode 100644 src/volesti/include/volume/rotating.hpp create mode 100644 src/volesti/include/volume/sampling_policies.hpp create mode 100644 src/volesti/include/volume/volume_cooling_balls.hpp create mode 100644 src/volesti/include/volume/volume_cooling_gaussians.hpp create mode 100644 src/volesti/include/volume/volume_cooling_hpoly.hpp create mode 100644 src/volesti/include/volume/volume_sequence_of_balls.hpp diff --git a/.github/workflows/R-CMD-check-macOS.yml b/.github/workflows/R-CMD-check-macOS.yml index 05b3cf25..c7ba3483 100644 --- a/.github/workflows/R-CMD-check-macOS.yml +++ b/.github/workflows/R-CMD-check-macOS.yml @@ -37,13 +37,6 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - - name: Fetch and update submodule - run: git submodule update --init --recursive; - cd src/volesti; - git config core.sparseCheckoutCone 'false'; - git sparse-checkout disable; - git sparse-checkout set include external; - - name: Install dependencies run: Rscript -e "install.packages(c('devtools', dependencies=TRUE))" -e "install.packages(c('rcmdcheck', 'devtools', 'Rcpp', 'RcppEigen', 'BH', 'testthat', 'downloader', 'xfun'))"; diff --git a/.github/workflows/R-CMD-check-ubuntu.yml b/.github/workflows/R-CMD-check-ubuntu.yml index 57780b1c..55f2c518 100644 --- a/.github/workflows/R-CMD-check-ubuntu.yml +++ b/.github/workflows/R-CMD-check-ubuntu.yml @@ -39,13 +39,6 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - - name: Fetch and update submodule - run: git submodule update --init --recursive; - cd src/volesti; - git config core.sparseCheckoutCone 'false'; - git sparse-checkout disable; - git sparse-checkout set include external; - - name: Install dependencies run: Rscript -e "install.packages(c('testthat', 'pkgload', 'rcmdcheck', 'devtools', 'Rcpp', 'RcppEigen', 'BH', 'downloader', 'xfun', dependencies=TRUE))"; diff --git a/.github/workflows/R-CMD-check-windows.yml b/.github/workflows/R-CMD-check-windows.yml index ae3f3bef..db0a8bd0 100644 --- a/.github/workflows/R-CMD-check-windows.yml +++ b/.github/workflows/R-CMD-check-windows.yml @@ -36,13 +36,6 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - - name: Fetch and update submodule - run: git submodule update --init --recursive; - cd src/volesti; - git config core.sparseCheckoutCone 'false'; - git sparse-checkout disable; - git sparse-checkout set include external; - - name: Install dependencies run: Rscript -e "install.packages(c('devtools', dependencies=TRUE))" -e "install.packages(c('rcmdcheck', 'devtools', 'Rcpp', 'RcppEigen', 'BH', 'testthat', 'downloader', 'xfun'))" diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 42627744..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "src/volesti"] - path = src/volesti - url = https://github.com/GeomScale/volesti.git diff --git a/inst/AUTHORS b/inst/AUTHORS index 956eb325..213c3a8f 100644 --- a/inst/AUTHORS +++ b/inst/AUTHORS @@ -1,4 +1,5 @@ -1. Bojan Nikolic . We have modified the implementations of Khachiyan's Algorithm by B. Nikolic from bnmin1-1.11 package for the Computation of Minimum Volume Enclosing Ellipsoids in /src/external/minimum_ellipsoid. - -2. Kjell Konis , Stefan I. Larimore and Timothy A. Davis , Kjell Eikland, Michel Berkelaar, Richard Stallman, Authors of lpsolve package , in /src/external/lpsolve. - +1. Kjell Konis , Stefan I. Larimore and Timothy A. Davis , Kjell Eikland, Michel Berkelaar, Richard Stallman, Authors of lpsolve package , in /src/external/lpSolve. +2. Bojan Nikolic . We have modified the implementations of Khachiyan's Algorithm by B. Nikolic from bnmin1-1.11 package for the Computation of Minimum Volume Enclosing Ellipsoids in /src/external/minimum_ellipsoid. +3. Authors of Spectra are described in detail here: https://github.com/yixuan/spectra/blob/master/AUTHORS.md +4. Author of PackedCSparse is Yin Tat Lee as described in https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/tree/master/code/solver/PackedCSparse +5. David H. Bailey author of qd library \ No newline at end of file diff --git a/inst/COPYRIGHTS b/inst/COPYRIGHTS index 5f83b029..a0c8033f 100644 --- a/inst/COPYRIGHTS +++ b/inst/COPYRIGHTS @@ -1,6 +1,9 @@ All files in src/external are taken from -(i) lpsolve (https://cran.r-project.org/package=lpSolve) version 5.6.20, -(ii) minimum_ellipsoid (or bnmin1) (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) version 1.11 +1. lpsolve (https://cran.r-project.org/package=lpSolve) version 5.6.20, +2. minimum_ellipsoid (or bnmin1) (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) version 1.11 +3. Spectra (https://github.com/yixuan/spectra/tree/master) version 0.8.1 +4. PackedCSparse (https://github.com/ConstrainedSampler/PolytopeSamplerMatlab) +5. qd (https://www.davidhbailey.com/dhbsoftware/) version 2.3.22 Copyrights and modification details are explicitly described at the beginning of each of those files. diff --git a/src/Makevars b/src/Makevars index e0ef376b..eb27e4d5 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,16 +1,16 @@ -PKG_CPPFLAGS=-Ivolesti/external -Iexternal/lpSolve/src -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra +PKG_CPPFLAGS=-Iexternal -Iexternal/lpSolve/src -Iexternal/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra PKG_CXXFLAGS= -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-Lexternal/lpSolve/src -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-Lexternal/lpSolve/src -llp_solve -Lexternal/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): external/lpSolve/src/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a +$(SHLIB): external/lpSolve/src/liblp_solve.a external/PackedCSparse/qd/libqd.a external/lpSolve/src/liblp_solve.a: @(cd external/lpSolve/src && $(MAKE) liblp_solve.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" RANLIB="$(RANLIB)") -volesti/external/PackedCSparse/qd/libqd.a: - @(cd volesti/external/PackedCSparse/qd && $(MAKE) libqd.a \ +external/PackedCSparse/qd/libqd.a: + @(cd external/PackedCSparse/qd && $(MAKE) libqd.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)") diff --git a/src/Makevars.win b/src/Makevars.win index 554a2bce..2a06a4d5 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,9 +1,9 @@ -PKG_CPPFLAGS=-Ivolesti/external -Iexternal/lpSolve/src -Ivolesti/external/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra +PKG_CPPFLAGS=-Iexternal -Iexternal/lpSolve/src -Iexternal/minimum_ellipsoid -Ivolesti/include -Ivolesti/include/convex_bodies/spectrahedra PKG_CXXFLAGS= -lm -ldl -DBOOST_NO_AUTO_PTR -DDISABLE_NLP_ORACLES -PKG_LIBS=-Lexternal/lpSolve/src -llp_solve -Lvolesti/external/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) +PKG_LIBS=-Lexternal/lpSolve/src -llp_solve -Lexternal/PackedCSparse/qd -lqd $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -$(SHLIB): external/lpSolve/src/liblp_solve.a volesti/external/PackedCSparse/qd/libqd.a +$(SHLIB): external/lpSolve/src/liblp_solve.a external/PackedCSparse/qd/libqd.a external/lpSolve/src/liblp_solve.a: @(cd external/lpSolve/src && $(MAKE) liblp_solve.a \ @@ -11,7 +11,7 @@ external/lpSolve/src/liblp_solve.a: CFLAGS="$(CFLAGS)" CPICFLAGS="$(CPICFLAGS)" AR="$(AR)" \ RANLIB="$(RANLIB)") -volesti/external/PackedCSparse/qd/libqd.a: - @(cd volesti/external/PackedCSparse/qd && $(MAKE) libqd.a \ +external/PackedCSparse/qd/libqd.a: + @(cd external/PackedCSparse/qd && $(MAKE) libqd.a \ CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ CPICFLAGS="$(CPICFLAGS)" AR="$(AR)") \ No newline at end of file diff --git a/src/external/PackedCSparse/FloatArray.h b/src/external/PackedCSparse/FloatArray.h new file mode 100644 index 00000000..28d1c5da --- /dev/null +++ b/src/external/PackedCSparse/FloatArray.h @@ -0,0 +1,307 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +#pragma once +#include +#include +#include +namespace PackedCSparse { + template + struct BaseImpl + { + T x[k]; + + BaseImpl() {}; + + BaseImpl(const T& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] = rhs; + } + + BaseImpl operator+(const BaseImpl& rhs) const + { + BaseImpl lhs; + for (size_t i = 0; i < k; i++) + lhs.x[i] = x[i] + rhs.x[i]; + return lhs; + } + + BaseImpl operator-(const BaseImpl& rhs) const + { + BaseImpl lhs; + for (size_t i = 0; i < k; i++) + lhs.x[i] = x[i] - rhs.x[i]; + return lhs; + } + + BaseImpl operator*(const BaseImpl& rhs) const + { + BaseImpl lhs; + for (size_t i = 0; i < k; i++) + lhs.x[i] = x[i] * rhs.x[i]; + return lhs; + } + + BaseImpl operator/(const BaseImpl& rhs) const + { + BaseImpl lhs; + for (size_t i = 0; i < k; i++) + lhs.x[i] = x[i] / rhs.x[i]; + return lhs; + } + + BaseImpl& operator+=(const BaseImpl& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] += rhs.x[i]; + return *this; + } + + BaseImpl& operator-=(const BaseImpl& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] -= rhs.x[i]; + return *this; + } + + BaseImpl& operator*=(const BaseImpl& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] *= rhs.x[i]; + return *this; + } + + BaseImpl& operator/=(const BaseImpl& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] /= rhs.x[i]; + return *this; + } + + explicit operator bool() const + { + bool ret = false; + for (size_t i = 0; i < k; i++) + ret = ret || bool(x[i]); + return ret; + } + + static T get(const BaseImpl& a, size_t index) + { + return a.x[index]; + } + + static void set(BaseImpl& a, size_t index, const T& value) + { + a.x[index] = value; + } + + static BaseImpl abs(const BaseImpl& a) + { + BaseImpl out; + for (size_t i = 0; i < k; i++) + out.x[i] = std::abs(a.x[i]); + return out; + } + + static BaseImpl log(const BaseImpl& a) + { + BaseImpl out; + for (size_t i = 0; i < k; i++) + out.x[i] = std::log(a.x[i]); + return out; + } + + static void fmadd(BaseImpl& a, const BaseImpl& b, const BaseImpl& c) + { + for (size_t i = 0; i < k; i++) + a.x[i] += b.x[i] * c.x[i]; + } + + static void fnmadd(BaseImpl& a, const BaseImpl& b, const BaseImpl& c) + { + for (size_t i = 0; i < k; i++) + a.x[i] -= b.x[i] * c.x[i]; + } + + static void fmadd(BaseImpl& a, const BaseImpl& b, const T& c) + { + for (size_t i = 0; i < k; i++) + a.x[i] += b.x[i] * c; + } + + static void fnmadd(BaseImpl& a, const BaseImpl& b, const T& c) + { + for (size_t i = 0; i < k; i++) + a.x[i] -= b.x[i] * c; + } + + static BaseImpl clipped_sqrt(const BaseImpl& a, const T nonpos_output) + { + BaseImpl out; + for (size_t i = 0; i < k; i++) + { + T r = a.x[i]; + if (r > 0) + out.x[i] = sqrt(r); + else + out.x[i] = nonpos_output; + } + return out; + } + + static BaseImpl sign(std::mt19937_64& gen) + { + BaseImpl out; + unsigned long long seed = gen(); + for (size_t i = 0; i < k; i++) + { + out.x[i] = T((2 * ((seed >> i) & 1)) - 1.0); + if ((i & 63) == 63) seed = gen(); + } + return out; + } + + }; + + template + struct BaseScalarImpl + { + static T get(const T& x, size_t index) + { + return x; + } + + static void set(T& x, size_t index, T& value) + { + x = value; + } + + static T abs(const T &x) + { + return ::abs(x); + } + + static T log(const T &x) + { + return ::log(x); + } + + static void fmadd(T& a, const T& b, const T& c) + { + a += b * c; + } + + static void fnmadd(T& a, const T& b, const T& c) + { + a -= b * c; + } + + static T clipped_sqrt(const T& x, const T& nonpos_output) + { + if (x > 0.0) + return sqrt(x); + else + return nonpos_output; + } + + static T sign(std::mt19937_64& gen) + { + unsigned long long seed = gen(); + return T((2 * (seed & 1)) - 1.0); + } + }; + + template + struct FloatTypeSelector + { + using type = typename std::conditional>::type; + using funcImpl = typename std::conditional, BaseImpl>::type; + }; + + #ifdef __AVX2__ + #include "FloatArrayAVX2.h" + #else + template + struct FloatTypeSelector + { + using type = typename std::conditional< k == 1, double, BaseImpl>::type; + using funcImpl = typename std::conditional< k == 1, BaseScalarImpl, BaseImpl>::type; + }; + + template + struct FloatTypeSelector, l> + { + using type = BaseImpl; + using funcImpl = BaseImpl; + }; + #endif + + template + struct FloatTypeSelector, l> + { + using type = BaseImpl; + using funcImpl = BaseImpl; + }; + + template + using FloatArray = typename FloatTypeSelector::type; + + template + using FloatArrayFunc = typename FloatTypeSelector::funcImpl; + + template + auto get(const T& a, size_t index) -> decltype(FloatArrayFunc::get(a, index)) + { + return FloatArrayFunc::get(a, index); + } + + template + void set(T1& a, size_t index, T2 value) + { + FloatArrayFunc::set(a, index, value); + } + + template + void fmadd(T1& a, const T2& b, const T3& c) + { + FloatArrayFunc::fmadd(a, b, c); + } + + template + void fnmadd(T1& a, const T2& b, const T3& c) + { + FloatArrayFunc::fnmadd(a, b, c); + } + + template + T1 clipped_sqrt(const T1& a, const T2 b) + { + return FloatArrayFunc::clipped_sqrt(a, b); + } + + template + T abs(const T& a) + { + return FloatArrayFunc::abs(a); + } + + template + T log(const T& a) + { + return FloatArrayFunc::log(a); + } + + template + T sign(std::mt19937_64& gen) + { + return FloatArrayFunc::sign(gen); + } +} diff --git a/src/external/PackedCSparse/FloatArrayAVX2.h b/src/external/PackedCSparse/FloatArrayAVX2.h new file mode 100644 index 00000000..3ae7592a --- /dev/null +++ b/src/external/PackedCSparse/FloatArrayAVX2.h @@ -0,0 +1,222 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +template + struct m256dArray +{ + __m256d x[k]; + + m256dArray() {}; + + m256dArray(const double rhs) + { + for (size_t i = 0; i < k; i++) + x[i] = _mm256_set1_pd(rhs); + } + + template + m256dArray(const m256dArray& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] = rhs.x[i % k2]; + } + + m256dArray operator+(const m256dArray& rhs) const + { + m256dArray out; + for (size_t i = 0; i < k; i++) + out.x[i] = _mm256_add_pd(x[i], rhs.x[i]); + return out; + } + + m256dArray operator-(const m256dArray& rhs) const + { + m256dArray out; + for (size_t i = 0; i < k; i++) + out.x[i] = _mm256_sub_pd(x[i], rhs.x[i]); + return out; + } + + m256dArray operator*(const m256dArray& rhs) const + { + m256dArray out; + for (size_t i = 0; i < k; i++) + out.x[i] = _mm256_mul_pd(x[i], rhs.x[i]); + return out; + } + + m256dArray operator/(const m256dArray& rhs) const + { + m256dArray out; + for (size_t i = 0; i < k; i++) + out.x[i] = _mm256_div_pd(x[i], rhs.x[i]); + return out; + } + + m256dArray& operator+=(const m256dArray& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] = _mm256_add_pd(x[i], rhs.x[i]); + return *this; + } + + m256dArray& operator-=(const m256dArray& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] = _mm256_sub_pd(x[i], rhs.x[i]); + return *this; + } + + m256dArray& operator*=(const m256dArray& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] = _mm256_mul_pd(x[i], rhs.x[i]); + return *this; + } + + m256dArray& operator/=(const m256dArray& rhs) + { + for (size_t i = 0; i < k; i++) + x[i] = _mm256_div_pd(x[i], rhs.x[i]); + return *this; + } + + explicit operator bool() const + { + bool ret = false; + __m256d z = _mm256_set1_pd(0.0); + for (size_t i = 0; i < k; i++) + { + __m256d c = _mm256_cmp_pd(x[i], z, _CMP_EQ_OQ); + ret = ret || (_mm256_movemask_pd(c) != 0xf); + } + return ret; + } + + static double get(const m256dArray& x, size_t index) + { + double y[4]; + _mm256_store_pd(y, x.x[index / 4]); + return y[index & 3]; + } + + static void set(m256dArray& x, size_t index, double value) + { + __m256d v = _mm256_broadcast_sd(&value); + switch (index & 3) + { + case 0: x.x[index / 4] = _mm256_blend_pd(x.x[index / 4], v, 1); break; + case 1: x.x[index / 4] = _mm256_blend_pd(x.x[index / 4], v, 2); break; + case 2: x.x[index / 4] = _mm256_blend_pd(x.x[index / 4], v, 4); break; + default: x.x[index / 4] = _mm256_blend_pd(x.x[index / 4], v, 8); break; + } + } + + static m256dArray abs(const m256dArray& x) + { + const __m256d mask = _mm256_castsi256_pd(_mm256_set1_epi64x(0x7FFFFFFFFFFFFFFF)); + + m256dArray out; + for (size_t i = 0; i < k; i++) + out.x[i] = _mm256_and_pd(x.x[i], mask); + return out; + } + + static m256dArray log(const m256dArray& x) + { + // gcc does not support _mm256_log_pd + // Do it sequentially instead + + //m256dArray out; + //for (size_t i = 0; i < k; i++) + // out.x[i] = _mm256_log_pd(x.x[i]); + + m256dArray out; + for (size_t i = 0; i < 4*k; i++) + set(out, i, std::log(get(x,i))); + return out; + } + + static void fmadd(m256dArray& a, const m256dArray& b, const double& c) + { + auto cx = _mm256_set1_pd(c); + for (size_t i = 0; i < k; i++) + a.x[i] = _mm256_fmadd_pd(b.x[i], cx, a.x[i]); + } + + static void fnmadd(m256dArray& a, const m256dArray& b, const double& c) + { + auto cx = _mm256_set1_pd(c); + for (size_t i = 0; i < k; i++) + a.x[i] = _mm256_fnmadd_pd(b.x[i], cx, a.x[i]); + } + + static void fmadd(m256dArray& a, const m256dArray& b, const m256dArray& c) + { + for (size_t i = 0; i < k; i++) + a.x[i] = _mm256_fmadd_pd(b.x[i], c.x[i], a.x[i]); + } + + static void fnmadd(m256dArray& a, const m256dArray& b, const m256dArray& c) + { + for (size_t i = 0; i < k; i++) + a.x[i] = _mm256_fnmadd_pd(b.x[i], c.x[i], a.x[i]); + } + + static m256dArray clipped_sqrt(const m256dArray& x, const double nonpos_output) + { + m256dArray out; + + const __m256d large = { nonpos_output, nonpos_output, nonpos_output, nonpos_output }; + const __m256d zero = _mm256_setzero_pd(); + for (size_t i = 0; i < k; i++) + { + __m256d xi = x.x[i]; + __m256d mask = _mm256_cmp_pd(xi, zero, _CMP_LE_OS); // mask = (rhs.x[i]<= 0) ? -1 : 0 + out.x[i] = _mm256_blendv_pd(_mm256_sqrt_pd(xi), large, mask); + } + return out; + } + + static m256dArray sign(std::mt19937_64& gen) + { + m256dArray out; + const __m256i bits = _mm256_set_epi64x(1, 2, 4, 8); + const __m256d zero = _mm256_setzero_pd(); + const __m256d pos = _mm256_set_pd(1.0, 1.0, 1.0, 1.0); + const __m256d neg = _mm256_set_pd(-1.0, -1.0, -1.0, -1.0); + + unsigned long long seed = gen(); + for (size_t i = 0; i < k; i++) + { + __m256i s = _mm256_set1_epi64x((seed >> (4 * i)) & 15); + __m256i xi = _mm256_and_si256(s, bits); + __m256d x = _mm256_castsi256_pd(xi); + __m256d mask = _mm256_cmp_pd(x, zero, _CMP_EQ_OQ); // mask = (rhs.x[i] == 0) ? -1 : 0 + out.x[i] = _mm256_blendv_pd(pos, neg, mask); + if ((i & 63) == 63) seed = gen(); + } + return out; + } +}; + +template + struct FloatTypeSelector +{ + static_assert(k == 1 || k % 4 == 0, "Array assumes k = 1 or a multiple of 4"); + using type = typename std::conditional< k == 1, double, m256dArray>::type; + using funcImpl = typename std::conditional< k == 1, BaseScalarImpl, m256dArray>::type; +}; + +template + struct FloatTypeSelector, l> +{ + using type = m256dArray; + using funcImpl = m256dArray; +}; diff --git a/src/external/PackedCSparse/PackedChol.h b/src/external/PackedCSparse/PackedChol.h new file mode 100644 index 00000000..97d10220 --- /dev/null +++ b/src/external/PackedCSparse/PackedChol.h @@ -0,0 +1,308 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +#pragma once +#include "Eigen/Eigen" +#include "SparseMatrix.h" +#include "chol.h" +#include "leverage.h" +#include "leverageJL.h" +#include "multiply.h" +#include "qd/dd_real.h" +#include +#include +using namespace PackedCSparse; + +template +void get_slice(Tout *out, Tin *in, size_t n, size_t idx) { + for (size_t j = 0; j < n; j++) + out[j] = to_double(get(in[j], idx)); +} + +template +void set_slice(Tout *out, Tin *in, size_t n, size_t idx) { + for (size_t j = 0; j < n; j++) + set(out[j], idx, to_double(in[j])); +} + +template struct PackedChol { + using Tx = double; + using Tx2 = FloatArray; + using Te = dd_real; + + // parameters + SparseMatrix A; + SparseMatrix At; + UniqueAlignedPtr w; + Tx accuracyThreshold = 1e-6; + std::vector + exactIdx; // k size array. Indices we perform high precision calculation + std::vector + numExact; // number of times we perform high precision decompose (length + // k+1, the last one records how many times we do decompose) + bool decomposed = false; + + // preprocess info for different CSparse operations (PackedDouble) + MultiplyOutput H; // cache for H = A W A' + CholOutput L; // cache for L = chol(H) + LeverageOutput diagP; // cache for L = chol(H) + LeverageJLOutput diagPJL; // cache for L = chol(H) + + // preprocess info for different CSparse operations (dd_real) + MultiplyOutput H_exact; // cache for H = A W A' + CholOutput L_exact; // cache for L = chol(H) + LeverageOutput diagP_exact; // cache for L = chol(H) + LeverageJLOutput diagPJL_exact; // cache for L = chol(H) + SparseMatrix Le[k]; // store output of L_exact + + PackedChol(const SparseMatrix &A_) { + A = std::move(A_.clone()); + At = transpose(A); + w.reset(pcs_aligned_new(A.n)); + numExact.resize(k + 1); + } + + void setSeed(unsigned long long seed) { + diagPJL.gen.seed(seed); + diagPJL_exact.gen.seed(seed); + } + + bool allExact() { return exactIdx.size() == k; } + + bool hasExact() { return exactIdx.size() > 0; } + + template Tx2 decompose(const Tv2_ *w_in) { + + Tx2 acc = Tx2(0.0); + + // record w + Ti n = A.n; + + for (Ti j = 0; j < n; j++) { + w[j] = w_in[j]; + } + // compute chol + ++numExact[k]; + if (accuracyThreshold > 0.0 || + !decomposed) // the first time we call, always run the double chol. + { + multiply(H, A, w.get(), At); + chol(L, H); + decomposed = true; + + exactIdx.clear(); + acc = estimateAccuracy(); + for (size_t i = 0; i < k; i++) { + if (get(acc, i) >= + accuracyThreshold) // >= is important for the case accuracyThreshold + // = 0.0, we need to compute everything exactly + exactIdx.push_back(i); + } + } else if (!allExact()) { + exactIdx.clear(); + for (size_t i = 0; i < k; i++) + exactIdx.push_back(i); + } + + if (hasExact()) { + Te *w_exact = new Te[n]; + + for (size_t i : exactIdx) { + ++numExact[i]; + get_slice(w_exact, w.get(), n, i); + multiply(H_exact, A, w_exact, At); + chol(L_exact, H_exact); + + // copy result to Le[i] + if (!Le[i].initialized()) + Le[i] = std::move(L_exact.template clone()); + else { + Ti nz = L_exact.nnz(); + for (Ti s = 0; s < nz; ++s) + Le[i].x[s] = (L_exact.x[s]); + } + } + + delete[] w_exact; + } + return acc; + } + + Tx2 logdet() { + pcs_assert(decomposed, "logdet: Need to call decompose first."); + + Ti m = A.m; + Tx2 ret = Tx2(0); + + if (!allExact()) { + Ti *Lp = L.p.get(); + Tx2 *Lx = L.x.get(); + for (Ti j = 0; j < m; j++) + ret += log(Lx[Lp[j]]); + } + + if (hasExact()) { + for (size_t i : exactIdx) { + Te ret_e = 0.0; + Ti *Lp = Le[i].p.get(); + Te *Lx = Le[i].x.get(); + + for (Ti j = 0; j < m; j++) + ret_e += log(Lx[Lp[j]]); + + set(ret, i, to_double(ret_e)); + } + } + + return ret * Tx2(2.0); + } + + void diagL(Tx2 *out) { + pcs_assert(decomposed, "diagL: Need to call decompose first."); + + Ti m = A.m; + + if (!allExact()) { + Ti *Li = L.i.get(), *Lp = L.p.get(); + Tx2 *Lx = L.x.get(); + for (Ti j = 0; j < m; j++) + out[j] = Lx[Lp[j]]; + } + + if (hasExact()) { + for (size_t i : exactIdx) { + Ti *Lp = Le[i].p.get(); + Te *Lx = Le[i].x.get(); + + for (Ti j = 0; j < m; j++) + set(out[j], i, to_double(Lx[Lp[j]])); + } + } + } + + SparseMatrix getL(Ti i) { + pcs_assert(decomposed, "getL: Need to call decompose first."); + + Ti m = L.m, n = L.n, nz = L.nnz(); + SparseMatrix out(m, n, nz); + + Ti *outp = out.p.get(), *Lp = L.p.get(); + Ti *outi = out.i.get(), *Li = L.i.get(); + + for (Ti s = 0; s <= n; s++) + outp[s] = Lp[s]; + + for (Ti s = 0; s < nz; s++) + outi[s] = Li[s]; + + bool isExact = false; + for (size_t i_ : exactIdx) { + if (i_ == i) + isExact = true; + } + + double *outx = out.x.get(); + if (isExact) { + Te *Lx = Le[i].x.get(); + for (Ti s = 0; s < nz; s++) + outx[s] = to_double(Lx[s]); + } else { + Tx2 *Lx = L.x.get(); + for (Ti s = 0; s < nz; s++) + outx[s] = get(Lx[s], i); + } + + return std::move(out); + } + + void solve(Tx2 *b, Tx2 *out) { + pcs_assert(decomposed, "solve: Need to call decompose first."); + + if (!allExact()) { + lsolve(L, b, out); + ltsolve(L, out, out); + } + + if (hasExact()) { + Ti m = A.m; + Te *b_exact = new Te[m]; + Te *out_exact = new Te[m]; + + for (size_t i : exactIdx) { + get_slice(b_exact, b, m, i); + lsolve(Le[i], b_exact, out_exact); + ltsolve(Le[i], out_exact, out_exact); + set_slice(out, out_exact, m, i); + } + + delete[] b_exact; + delete[] out_exact; + } + }; + + void leverageScoreComplement(Tx2 *out) { + pcs_assert(decomposed, + "leverageScoreComplement: Need to call decompose first."); + + Ti n = A.n, m = A.m; + + if (!allExact()) { + Tx2 T1 = Tx2(1.0), T2 = Tx2(2.0); + leverage(diagP, L, A, At); + + Tx2 *tau = diagP.x.get(); + for (Ti j = 0; j < n; j++) + out[j] = T1 - tau[j] * w[j]; + } + + if (hasExact()) { + Te T1 = Te(1.0), T2 = Te(2.0); + for (size_t i : exactIdx) { + leverage(diagP_exact, Le[i], A, At); + + Te *tau = diagP_exact.x.get(); + for (Ti j = 0; j < n; j++) + set(out[j], i, to_double(T1 - tau[j] * get(w[j], i))); + } + } + } + + void leverageScoreComplementJL(Tx2 *out, size_t JL_k) { + pcs_assert(decomposed, + "leverageScoreComplementJL: Need to call decompose first."); + + Ti m = A.m, n = A.n; + + if (!allExact()) { + Tx2 T1 = Tx2(1.0), T2 = Tx2(2.0); + leverageJL(diagPJL, L, A, At, JL_k); + + Tx2 *tau = diagPJL.x.get(); + for (Ti j = 0; j < n; j++) + out[j] = T1 - tau[j] * w[j]; + } + + if (hasExact()) { + Te T1 = Te(1.0), T2 = Te(2.0); + for (size_t i : exactIdx) { + leverageJL(diagPJL_exact, Le[i], A, At, JL_k); + + Te *tau = diagPJL_exact.x.get(); + for (Ti j = 0; j < n; j++) + set(out[j], i, to_double(T1 - tau[j] * get(w[j], i))); + } + } + } + + Tx2 estimateAccuracy() { + pcs_assert(decomposed, "estimateAccuracy: Need to call decompose first."); + + return cholAccuracy(diagPJL, L, A, At, w.get()); + } +}; diff --git a/src/external/PackedCSparse/SparseMatrix.h b/src/external/PackedCSparse/SparseMatrix.h new file mode 100644 index 00000000..5dca4286 --- /dev/null +++ b/src/external/PackedCSparse/SparseMatrix.h @@ -0,0 +1,230 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis +#pragma once +#include +#include +#include "FloatArray.h" + +namespace PackedCSparse { + static void pcs_assert(bool value, const char* message) + { + if (value == false) + throw std::logic_error(message); + } + + template + T* pcs_aligned_new(size_t size) + { + int alignment = 64; // size of memory cache line + int offset = alignment - 1 + sizeof(void*); + void* p1 = (void*)new char[size * sizeof(T) + offset]; + void** p2 = (void**)(((size_t)(p1)+offset) & ~(alignment - 1)); + p2[-1] = p1; + return (T*)p2; + } + + template + struct AlignedDeleter + { + void operator()(T* p) const + { + delete[](char*)(((void**)p)[-1]); + } + }; + + template + using UniqueAlignedPtr = std::unique_ptr>; + + template + using UniquePtr = std::unique_ptr; + + // Tx = Type for entries, Ti = Type for indices. + // if Tx == bool, the matrix stores only sparsity information + template + struct SparseMatrix + { + Ti m = 0; /* number of rows */ + Ti n = 0; /* number of columns */ + UniquePtr p; /* column pointers (size n+1) */ + UniquePtr i; /* row indices, size nnz */ + UniqueAlignedPtr x; /* numerical values, size nnz */ + + SparseMatrix() = default; + + SparseMatrix(Ti m_, Ti n_, Ti nzmax_) + { + initialize(m_, n_, nzmax_); + } + + bool initialized() const + { + return p && i; + } + + void initialize(Ti m_, Ti n_, Ti nzmax) + { + if (nzmax < 1) nzmax = 1; + m = m_; n = n_; + p.reset(new Ti[n + 1]); + i.reset(new Ti[nzmax]); + if (!std::is_same::value) + x.reset(pcs_aligned_new(nzmax)); + } + + Ti nnz() const + { + return p[n]; + } + + template + SparseMatrix clone() const + { + SparseMatrix C(m, n, nnz()); + Ti* Ap = p.get(), * Ai = i.get(); Tx* Ax = x.get(); + Ti2* Cp = C.p.get(), * Ci = C.i.get(); Tx2* Cx = C.x.get(); + + for (Ti s = 0; s <= n; s++) + Cp[s] = Ti2(Ap[s]); + + Ti nz = nnz(); + for (Ti s = 0; s < nz; s++) + Ci[s] = Ti2(Ai[s]); + + if (Cx) + { + for (Ti s = 0; s < nz; s++) + Cx[s] = Ax? Tx2(Ax[s]): Tx2(1.0); + } + + return C; + } + }; + + template + struct DenseVector + { + Ti n = 0; /* number of columns */ + UniqueAlignedPtr x; /* numerical values, size nnz */ + + DenseVector() = default; + + DenseVector(Ti n_) + { + initialize(n_); + } + + bool initialized() const + { + return bool(x); + } + + void initialize(Ti n_) + { + n = n_; + x.reset(pcs_aligned_new(n_)); + } + }; + + + // basic functions + template + SparseMatrix speye(Ti n, Tx* d = nullptr) + { + SparseMatrix D(n, n, n); + + for (Ti k = 0; k < n; k++) + { + D.i[k] = k; + D.p[k] = k; + } + D.p[n] = n; + + Tx Tx1 = Tx(1.0); + for (Ti k = 0; k < n; k++) + D.x[k] = (d ? d[k] : Tx1); + return D; + } + + // Solve L out = x + // Input: L in Tx^{n by n}, x in Tx2^{n} + // Output: out in Tx2^{n}. + // If out is provided, we will output to out. Else, output to x. + template + void lsolve(const SparseMatrix& L, Tx2* x, Tx2* out = nullptr) + { + pcs_assert(L.initialized(), "lsolve: bad inputs."); + pcs_assert(L.n == L.m, "lsolve: dimensions mismatch."); + + Ti n = L.n, * Lp = L.p.get(), * Li = L.i.get(); Tx* Lx = L.x.get(); + + if (!out) out = x; + if (x != out) std::copy(x, x + n, out); + + for (Ti j = 0; j < n; j++) + { + Tx2 out_j = out[j] / Lx[Lp[j]]; + out[j] = out_j; + + Ti p_start = Lp[j] + 1, p_end = Lp[j + 1]; + for (Ti p = p_start; p < p_end; p++) + { //out[Li[p]] -= Lx[p] * out[j]; + fnmadd(out[Li[p]], out_j, Lx[p]); + } + } + } + + // Solve L' out = x + // Input: L in Tx^{n by n}, x in Tx2^{n} + // Output: out in Tx2^{n}. + // If out is provided, we will output to out. Else, output to x. + template + void ltsolve(const SparseMatrix& L, Tx2* x, Tx2* out = nullptr) + { + pcs_assert(L.initialized(), "ltsolve: bad inputs."); + pcs_assert(L.n == L.m, "ltsolve: dimensions mismatch."); + + Ti n = L.n, * Lp = L.p.get(), * Li = L.i.get(); Tx* Lx = L.x.get(); + + if (!out) out = x; + if (x != out) std::copy(x, x + n, out); + + for (Ti j = n - 1; j != -1; j--) + { + Tx2 out_j = out[j]; + + Ti p_start = Lp[j] + 1, p_end = Lp[j + 1]; + for (Ti p = p_start; p < p_end; p++) + { //out[j] -= Lx[p] * out[Li[p]]; + fnmadd(out_j, out[Li[p]], Lx[p]); + } + + out[j] = out_j / Tx2(Lx[Lp[j]]); + } + } + + // Update y <-- y + A x + // Input: A in Tx^{n by n}, x, y in Tx2^{n} + template + void gaxpy(const SparseMatrix& A, const Tx2* x, Tx2* y) + { + pcs_assert(A.initialized(), "gaxpy: bad inputs."); + Ti m = A.m, n = A.n, * Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); + + for (Ti j = 0; j < n; j++) + { + Tx2 x_j = x[j]; + + Ti p_start = Ap[j], p_end = Ap[j + 1]; + for (Ti p = p_start; p < p_end; p++) + { //y[Ai[p]] += Ax[p] * x[j]; + fmadd(y[Ai[p]], x_j, Ax[p]); + } + } + } +}; diff --git a/src/external/PackedCSparse/add.h b/src/external/PackedCSparse/add.h new file mode 100644 index 00000000..16195722 --- /dev/null +++ b/src/external/PackedCSparse/add.h @@ -0,0 +1,102 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +#pragma once +#include +#include "SparseMatrix.h" + +// Problem: +// Compute M = A + B + +// Algorithm: +// M = 0 +// M(A != 0) += A(A != 0) +// M(B != 0) += B(A != 0) + +namespace PackedCSparse { + template + struct AddOutput : SparseMatrix + { + UniquePtr forwardA; + UniquePtr forwardB; + + template + void initialize(const SparseMatrix& A, const SparseMatrix& B) + { + pcs_assert(A.initialized() && B.initialized(), "add: bad inputs."); + pcs_assert(A.n == B.n && A.m == B.m, "add: dimensions mismatch."); + + Ti m = A.m, n = A.n; + Ti Anz = A.nnz(); Ti* Ap = A.p.get(), * Ai = A.i.get(); + Ti Bnz = B.nnz(); Ti* Bp = B.p.get(), * Bi = B.i.get(); + this->m = A.m; this->n = A.n; + + std::vector Ci; + Ti* Cp = new Ti[n + 1]; + forwardA.reset(new Ti[Anz]); + forwardB.reset(new Ti[Bnz]); + + Cp[0] = 0; + for (Ti i = 0; i < n; i++) + { + Ti s1 = Ap[i], s2 = Bp[i], end1 = Ap[i + 1], end2 = Bp[i + 1]; + while ((s1 < end1) || (s2 < end2)) + { + Ti q = Ti(Ci.size()); + Ti i1 = (s1 < end1) ? Ai[s1] : m; + Ti i2 = (s2 < end2) ? Bi[s2] : m; + Ti min_i = std::min(i1, i2); + Ci.push_back(min_i); + + if (i1 == min_i) + forwardA[s1++] = q; + + if (i2 == min_i) + forwardB[s2++] = q; + } + Cp[i + 1] = Ti(Ci.size()); + } + + this->p.reset(Cp); + this->i.reset(new Ti[Ci.size()]); + this->x.reset(pcs_aligned_new(Ci.size())); + std::copy(Ci.begin(), Ci.end(), this->i.get()); + } + }; + + template + void add(AddOutput& o, const SparseMatrix& A, const SparseMatrix& B) + { + if (!o.initialized()) + o.initialize(A, B); + + Ti m = o.m, n = o.n; + Ti Anz = A.nnz(); Ti* Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); + Ti Bnz = B.nnz(); Ti* Bp = B.p.get(), * Bi = B.i.get(); Tx* Bx = B.x.get(); + Ti Cnz = o.nnz(); Ti* Cp = o.p.get(), * Ci = o.i.get(); Tx2* Cx = o.x.get(); + Ti* forwardA = o.forwardA.get(), *forwardB = o.forwardB.get(); + + for (Ti s = 0; s < Cnz; s++) + Cx[s] = 0; + + for (Ti s = 0; s < Anz; s++) + Cx[forwardA[s]] = Ax[s]; + + for (Ti s = 0; s < Bnz; s++) + Cx[forwardB[s]] += Bx[s]; + } + + template + AddOutput add(const SparseMatrix& A, const SparseMatrix& B) + { + AddOutput o; + add(o, A, B); + return o; + } +} diff --git a/src/external/PackedCSparse/chol.h b/src/external/PackedCSparse/chol.h new file mode 100644 index 00000000..d6001ae2 --- /dev/null +++ b/src/external/PackedCSparse/chol.h @@ -0,0 +1,247 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +#pragma once +#include +#include +#include "SparseMatrix.h" +#include "transpose.h" + +// Problem: +// Compute chol(A) + +// Algorithm: +// We need to study this later as this is the bottleneck. +// Document it as a lyx. +// chol_up_looking: +// Compute L row by row +// This is faster when it is compute bound. +// +// chol_left_looking: +// Compute L col by col +// This is faster when it is memory bound. + + +namespace PackedCSparse { + template + struct CholOutput : SparseMatrix + { + TransposeOutput Lt; // sparsity pattern of the Lt + UniquePtr diag; // the index for diagonal element. Ax[diag[k]] is A_kk + UniquePtr c; // c[i] = index the last nonzero on column i in the current L + UniqueAlignedPtr w; // the row of L we are computing + + // The cost of this is roughly 3 times larger than chol + // One can optimize it by using other data structure + void initialize(const SparseMatrix& A) + { + pcs_assert(A.initialized(), "chol: bad inputs."); + pcs_assert(A.n == A.m, "chol: dimensions mismatch."); + + Ti n = A.n, * Ap = A.p.get(), * Ai = A.i.get(); + + // initialize + this->diag.reset(new Ti[n]); + this->c.reset(new Ti[n]); + this->w.reset(pcs_aligned_new(n)); + + // compute the sparsity pattern of L and diag + using queue = std::priority_queue, std::greater>; + queue q; // sol'n of the current row of L + Ti* mark = new Ti[n]; // used to prevent same indices push to q twice + std::vector* cols = new std::vector[n]; // stores the non-zeros of each col of L + Ti nz = 0, Anz = Ap[n]; + + for (Ti i = 0; i < n; i++) + mark[i] = -1; + + // for each row of A + for (Ti i = 0; i < n; i++) + { // push i-th row of A, called a_12, into mark + Ti s; + for (s = Ap[i]; s < Ap[i + 1]; s++) + { + Ti j = Ai[s]; + if (j >= i) break; + + q.push(j); + mark[j] = i; + } + if (s >= Anz) // this case happens only if the diagonal is 0. No cholesky in this case. + this->diag[i] = 0; + else + this->diag[i] = s; + + // Solve L_11 l_12 = a_12 + while (!q.empty()) + { + Ti j = q.top(); + + for (Ti k : cols[j]) + { + if (mark[k] != i) + { + q.push(k); + mark[k] = i; + } + } + q.pop(); + + // update j col + cols[j].push_back(i); + ++nz; + } + + // diag + cols[i].push_back(i); + ++nz; + } + delete[] mark; + + // write it as the compress form + SparseMatrix::initialize(n, n, nz); + + Ti s_start = 0; Ti s = 0; + for (Ti i = 0; i < n; i++) + { + this->p[i] = s_start; + for (Ti k : cols[i]) + this->i[s++] = k; + s_start += Ti(cols[i].size()); + } + this->p[n] = s_start; + delete[] cols; + + this->Lt = transpose(*this); + + // initialize w to 0 + Tx Tv0 = Tx(0); + for (Ti k = 0; k < n; k++) + w[k] = Tv0; + } + }; + + template + void chol(CholOutput& o, const SparseMatrix& A) + { + if (!o.initialized()) + o.initialize(A); + + //chol_up_looking(o, A); + chol_left_looking(o, A); + } + + template + void chol_up_looking(CholOutput& o, const SparseMatrix& A) + { + Ti *Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); + Ti nzmax = o.nzmax; Ti n = A.n; + Ti *Lp = o.p.get(); Ti* Li = o.i.get(); + Ti *Ltp = o.Lt.p.get(); Ti* Lti = o.Lt.i.get(); + + Tx T0 = Tx(0); + Tx* Lx = o.x.get(); Tx* w = o.w.get(); Ti* c = o.c.get(); + Ti* diag = o.diag.get(); + + Ti* Lti_ptr = Lti; + for (Ti k = 0; k < n; ++k) + { + c[k] = Lp[k]; + + Ti s_end = diag[k]; + for (Ti s = Ap[k]; s < s_end; ++s) + w[Ai[s]] = Ax[s]; + + // Solve L_11 l_12 = a_12 + Tx d = Ax[s_end]; Ti i; + for (; (i = *(Lti_ptr++)) < k;) + { + Ti dLi = Lp[i], ci = c[i]++; + Tx Lki = w[i] / Lx[dLi]; + w[i] = T0; // maintain x = 0 for the (k+1) iteration + + for (Ti q = dLi + 1; q < ci; ++q) + fnmadd(w[Li[q]], Lx[q], Lki); + + d -= Lki * Lki; + Lx[ci] = Lki; + } + + // l_22 = sqrt(a22 - ) + Lx[c[k]++] = clipped_sqrt(d); + } + } + + template + void chol_left_looking(CholOutput& o, const SparseMatrix& A) + { + Ti* Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); + Ti nzmax = o.nnz(); Ti n = A.n; + Ti* Lp = o.p.get(); Ti* Li = o.i.get(); + Ti* Ltp = o.Lt.p.get(); Ti* Lti = o.Lt.i.get(); + + Tx T0 = Tx(0), T1 = Tx(1); + Tx* Lx = o.x.get(); + Tx* w = o.w.get(); Ti* c = o.c.get(); + Ti* diag = o.diag.get(); + + for (Ti j = 0; j < n; ++j) + { + c[j] = Lp[j]; + + // x = A_{j:n, j} + { + Ti is_start = diag[j], is_end = Ap[j + 1]; + for (Ti is = is_start; is < is_end; ++is) + w[Ai[is]] = Ax[is]; + } + + // for each p in L_{j, 1:j-1} + Ti ps_start = Ltp[j], ps_end = Ltp[j + 1] - 1; + for (Ti ps = ps_start; ps < ps_end; ++ps) + { + Ti p = Lti[ps]; + Ti cp = c[p]++; + Tx Ljp = Lx[cp]; + + // for each i in L_{j:n,p} + Ti is_start = cp, is_end = Lp[p + 1]; + for (Ti is = is_start; is < is_end; ++is) + { + Ti i = Li[is]; + fnmadd(w[i], Lx[is], Ljp); + } + } + + Tx Ljj = clipped_sqrt(w[j], 1e128); + Lx[c[j]++] = Ljj; + Tx inv_Ljj = T1 / Ljj; + w[j] = T0; + + // for each i in L_{:,j} + { + Ti is_start = Lp[j] + 1, is_end = Lp[j + 1]; + for (Ti is = is_start; is < is_end; ++is) + { + Ti i = Li[is]; + Lx[is] = w[i] * inv_Ljj; + w[i] = T0; + } + } + } + } + + template + CholOutput chol(const SparseMatrix& A) + { + CholOutput o; + chol(o, A); + return o; + } +} diff --git a/src/external/PackedCSparse/leverage.h b/src/external/PackedCSparse/leverage.h new file mode 100644 index 00000000..69549308 --- /dev/null +++ b/src/external/PackedCSparse/leverage.h @@ -0,0 +1,65 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +#pragma once +#include "SparseMatrix.h" +#include "projinv.h" +#include "outerprod.h" + +// Problem: +// Compute M = diag(A' inv(LL') A) + +namespace PackedCSparse { + template + struct LeverageOutput : DenseVector + { + ProjinvOutput Hinv; // Hinv = inv(H)|_L + OuterprodOutput tau; // tau = diag(A' Hinv A) + + template + void initialize(const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) + { + pcs_assert(L.initialized() && A.initialized() && At.initialized(), "leverage: bad inputs."); + pcs_assert(L.m == L.n && L.n == A.m && L.n == At.n && A.n == At.m, "leverage: dimensions mismatch."); + DenseVector::initialize(A.n); + Hinv.initialize(L); + tau.initialize(A, Hinv, At); + } + }; + + template + void leverage(LeverageOutput& o, const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) + { + if (!o.initialized()) + o.initialize(L, A, At); + + Tx T1 = Tx(1.0), T2 = Tx(2.0); + projinv(o.Hinv, L); + + Ti m = A.m, n = A.n; + Ti* Sp = o.Hinv.p.get(); Tx* Sv = o.Hinv.x.get(); + for (Ti k = 0; k < m; ++k) + Sv[Sp[k]] /= T2; + + outerprod(o.tau, A, o.Hinv, At); + + Tx* x = o.x.get(), * tau = o.tau.x.get(); + for (Ti j = 0; j < n; j++) + x[j] = T2 * tau[j]; + } + + + template + LeverageOutput leverage(const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) + { + LeverageOutput o; + leverage(o, L, A, At); + return o; + } +} diff --git a/src/external/PackedCSparse/leverageJL.h b/src/external/PackedCSparse/leverageJL.h new file mode 100644 index 00000000..a5001bcd --- /dev/null +++ b/src/external/PackedCSparse/leverageJL.h @@ -0,0 +1,148 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +#pragma once +#include +#include "SparseMatrix.h" + +// Problem: +// Approximate M = diag(A' inv(LL') A) +namespace PackedCSparse { + const size_t JLPackedSize = 4; + + template + struct LeverageJLOutput : DenseVector + { + UniqueAlignedPtr d; // random direction d + UniqueAlignedPtr L_d; // random direction d + UniqueAlignedPtr AtL_d; // A' L^{-1} d + Ti m = 0; + std::mt19937_64 gen; + + template + void initialize(const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) + { + pcs_assert(L.initialized() && A.initialized() && At.initialized(), "leverageJL: bad inputs."); + pcs_assert(L.m == L.n && L.n == A.m && L.n == At.n && A.n == At.m, "leverageJL: dimensions mismatch."); + this->n = A.n; this->m = A.m; + this->x.reset(pcs_aligned_new(this->n)); + this->d.reset(pcs_aligned_new(this->m * 2 * JLPackedSize)); + this->L_d.reset(pcs_aligned_new(this->m * 2 * JLPackedSize)); + this->AtL_d.reset(pcs_aligned_new(this->n * 2 * JLPackedSize)); + } + }; + + // compute sum_{j=1}^{k} (A' L^{-T} u_j) .* (A' L^{-T} u_j) + template + void projectionJL(LeverageJLOutput& o, const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At) + { + Ti m = A.m, n = A.n; + Tx T0 = Tx(0.0), T1 = Tx(1.0); + Tx* d = o.d.get(), * L_d = o.L_d.get(), * AtL_d = o.AtL_d.get(), * x = o.x.get(); + + for (Ti i = 0; i < m * k; i++) + d[i] = sign(o.gen); + + for (Ti i = 0; i < n * k; i++) + AtL_d[i] = T0; + + ltsolve(L, (BaseImpl*)d, (BaseImpl*)L_d); + gaxpy(At, (BaseImpl*)L_d, (BaseImpl*)AtL_d); + + for (Ti i = 0; i < n; i++) + { + Tx ret_i = T0; + for (Ti j = 0; j < k; j++) + ret_i += AtL_d[i * k + j] * AtL_d[i * k + j]; + + x[i] += ret_i; + } + } + + template + void leverageJL(LeverageJLOutput& o, const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At, size_t k) + { + if (!o.initialized()) + o.initialize(L, A, At); + + Ti n = A.n; Tx* x = o.x.get(); + for (Ti i = 0; i < n; i++) + x[i] = Tx(0.0); + + constexpr size_t k_step = JLPackedSize; + for(size_t i = 1; i <= k / k_step; i++) + projectionJL(o, L, A, At); + + for (size_t i = 1; i <= k % k_step; i++) + projectionJL<1>(o, L, A, At); + + Tx ratio = Tx(1 / double(k)); + for (Ti i = 0; i < n; i++) + x[i] *= ratio; + } + + template + LeverageJLOutput leverageJL(const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At, size_t k) + { + LeverageJLOutput o; + leverageJL(o, L, A, At, k); + return o; + } + + + // compute (A' L^{-T} u_j) .* (A' L^{-T} v_j) for j = 1, 2, ... k + template + Tx cholAccuracy(LeverageJLOutput& o, const SparseMatrix& L, const SparseMatrix& A, const SparseMatrix& At, const Tx* w) + { + if (!o.initialized()) + o.initialize(L, A, At); + + constexpr Ti k = JLPackedSize; + constexpr Ti k_ = 2 * k; + + + Ti m = A.m, n = A.n; + Tx T0 = Tx(0.0), T1 = Tx(1.0); + Tx* d = o.d.get(), * L_d = o.L_d.get(), * AtL_d = o.AtL_d.get(), * x = o.x.get(); + + std::uniform_real_distribution distribution(-sqrt(3.0),sqrt(3.0)); + for (Ti i = 0; i < m * k_; i++) + d[i] = Tx(distribution(o.gen)); // roughly uniform distribution with variance 1 + + for (Ti i = 0; i < n * k_; i++) + AtL_d[i] = T0; + + ltsolve(L, (BaseImpl*)d, (BaseImpl*)L_d); + gaxpy(At, (BaseImpl*)L_d, (BaseImpl*)AtL_d); + + Tx result[k]; + for (Ti j = 0; j < k; j++) + result[j] = Tx(0.0); + + for (Ti i = 0; i < m; i++) + { + Tx* d = o.d.get() + i * (2 * k); + for (Ti j = 0; j < k; j++) + result[j] -= d[j] * d[j + k]; + } + + for (Ti i = 0; i < n; i++) + { + Tx w_i = w[i]; + for (Ti j = 0; j < k; j++) + result[j] += AtL_d[i * k_ + j] * AtL_d[i * k_ + j + k] * w_i; + } + + Tx est = Tx(0.0); + for (Ti j = 0; j < k; j++) + est += result[j] * result[j]; + + return clipped_sqrt(est/Tx(double(k)), 0.0); + } +} diff --git a/src/external/PackedCSparse/multiply.h b/src/external/PackedCSparse/multiply.h new file mode 100644 index 00000000..cfd53375 --- /dev/null +++ b/src/external/PackedCSparse/multiply.h @@ -0,0 +1,142 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +#pragma once +#include +#include +#include "SparseMatrix.h" + +// Problem: +// Compute M = A diag(w) B + +// Algorithm: +// Compute M col by col + +namespace PackedCSparse { + template + struct MultiplyOutput : SparseMatrix + { + UniqueAlignedPtr c; + + template + void initialize(const SparseMatrix& A, const SparseMatrix& B) + { + pcs_assert(A.initialized() && B.initialized(), "multiply: bad inputs."); + pcs_assert(A.n == B.m, "multiply: dimensions mismatch."); + + Ti m = A.m, n = B.n; + Ti* Ap = A.p.get(), * Ai = A.i.get(); + Ti* Bp = B.p.get(), * Bi = B.i.get(); + + this->c.reset(pcs_aligned_new(m)); + + Ti* last_j = new Ti[m]; + for (Ti i = 0; i < m; i++) + { + last_j[i] = -1; + this->c[i] = Tx(0.0); + } + + Ti* Cp = new Ti[size_t(n)+1]; + std::vector Ci; + + Cp[0] = 0; + for (Ti j1 = 0; j1 < n; j1++) + { + for (Ti p1 = Bp[j1]; p1 < Bp[j1 + 1]; p1++) + { + Ti j2 = Bi[p1]; + for (Ti p2 = Ap[j2]; p2 < Ap[j2 + 1]; p2++) + { + Ti i = Ai[p2]; + if (last_j[i] != j1) + { + last_j[i] = j1; + Ci.push_back(i); + } + } + } + Cp[j1 + 1] = Ti(Ci.size()); + } + delete[] last_j; + + for (Ti j = 0; j < n; j++) + std::sort(Ci.begin() + Cp[j], Ci.begin() + Cp[j + 1]); + + this->m = m; this->n = n; + this->x.reset(pcs_aligned_new(Ci.size())); + this->p.reset(Cp); + this->i.reset(new Ti[Ci.size()]); + std::copy(Ci.begin(), Ci.end(), this->i.get()); + } + }; + + template + void multiply_general(MultiplyOutput& o, const SparseMatrix& A, const Tx2* w, const SparseMatrix& B) + { + if (!o.initialized()) + o.initialize(A, B); + + Ti m = o.m, n = o.n; + Ti* Ap = A.p.get(), * Ai = A.i.get(); Tx* Ax = A.x.get(); + Ti* Bp = B.p.get(), * Bi = B.i.get(); Tx* Bx = B.x.get(); + Ti* Cp = o.p.get(), * Ci = o.i.get(); Tx2* Cx = o.x.get(); + Tx2* c = o.c.get(); // initialized to 0 + + const Tx2 T0 = Tx2(0); + for (Ti j1 = 0; j1 < n; j1++) + { + for (Ti p1 = Bp[j1]; p1 < Bp[j1 + 1]; p1++) + { + Ti j2 = Bi[p1]; + Tx2 beta = has_weight? (Tx2(Bx[p1]) * w[j2]) : Tx2(Bx[p1]); + + for (Ti p2 = Ap[j2]; p2 < Ap[j2 + 1]; p2++) + { + //x[Ai[p2]] += beta * Ax[p2]; + fmadd(c[Ai[p2]], beta, Ax[p2]); + } + } + + for (Ti p1 = Cp[j1]; p1 < Cp[j1 + 1]; p1++) + { + Cx[p1] = c[Ci[p1]]; + c[Ci[p1]] = T0; // ensure c is 0 after the call + } + } + } + + template + void multiply(MultiplyOutput& o, const SparseMatrix& A, const SparseMatrix& B) + { + multiply_general(o, A, nullptr, B); + } + + template + void multiply(MultiplyOutput& o, const SparseMatrix& A, const Tx2* w, const SparseMatrix& B) + { + multiply_general(o, A, w, B); + } + + template + MultiplyOutput multiply(const SparseMatrix& A, const Tx2* w, const SparseMatrix& B) + { + MultiplyOutput o; + multiply(o, A, w, B); + return o; + } + + template + MultiplyOutput multiply(const SparseMatrix& A, const SparseMatrix& B) + { + MultiplyOutput o; + multiply(o, A, B); + return o; + } +} diff --git a/src/external/PackedCSparse/outerprod.h b/src/external/PackedCSparse/outerprod.h new file mode 100644 index 00000000..6f0e98e3 --- /dev/null +++ b/src/external/PackedCSparse/outerprod.h @@ -0,0 +1,86 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis +#pragma once +#include "SparseMatrix.h" + +// Problem: +// Compute x = diag(At S Bt) + +// Algorithm: +// Note that x = diag(B St A) = grad_H Tr(St A H B) +// We run autodiff on the function Tr(St A H B). +// Hence, the algorithm is essentially same as multiply(A, B) with the same runtime. + +namespace PackedCSparse { + template + struct OuterprodOutput : DenseVector + { + UniqueAlignedPtr s_col; + UniquePtr s_mark; + + template + void initialize(const SparseMatrix& A, const SparseMatrix& S, const SparseMatrix& B) + { + pcs_assert(A.initialized() && B.initialized() && S.initialized(), "outerprod: bad inputs."); + pcs_assert(A.m == S.m && S.n == B.n, "outerprod: dimensions mismatch."); + + DenseVector::initialize(A.n); + s_col.reset(pcs_aligned_new(S.m)); + s_mark.reset(new Ti[S.m]); + } + }; + + template + void outerprod(OuterprodOutput& o, const SparseMatrix& A, const SparseMatrix& S, const SparseMatrix& B) + { + if (!o.initialized()) + o.initialize(A, S, B); + + Ti Sn = S.n, Sm = S.m, An = A.n; + Ti* Ap = A.p.get(), * Ai = A.i.get(); Tx2* Ax = A.x.get(); + Ti* Bp = B.p.get(), * Bi = B.i.get(); Tx2* Bx = B.x.get(); + Ti* Sp = S.p.get(), * Si = S.i.get(); Tx* Sx = S.x.get(); + Tx* s_col = o.s_col.get(); + Ti* s_mark = o.s_mark.get(); + Tx* x = o.x.get(); + + std::fill(s_mark, s_mark + Sm, Ti(-1)); + std::fill(x, x + An, Tx(0.0)); + + for (Ti j = 0; j < Sn; j++) + { + for (Ti p = Sp[j]; p < Sp[j + 1]; p++) + { + s_col[Si[p]] = Sx[p]; + s_mark[Si[p]] = j; + } + + for (Ti p = Bp[j]; p < Bp[j + 1]; p++) + { + Ti i = Bi[p]; Tx b = Bx[p]; + for (Ti q = Ap[i]; q < Ap[i + 1]; q++) + { + Tx a = Ax[q]; Ti a_i = Ai[q]; + if (s_mark[a_i] == j) + { //x[i] += s_col[a_i] * a * b; + fmadd(x[i], s_col[a_i], a * b); + } + } + } + } + } + + template + OuterprodOutput outerprod(const SparseMatrix& A, const SparseMatrix& S, const SparseMatrix& B) + { + OuterprodOutput o; + outerprod(o, A, S, B); + return o; + } +} diff --git a/src/external/PackedCSparse/projinv.h b/src/external/PackedCSparse/projinv.h new file mode 100644 index 00000000..a319a47e --- /dev/null +++ b/src/external/PackedCSparse/projinv.h @@ -0,0 +1,90 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis + +#pragma once +#include "SparseMatrix.h" + +// Problem: +// Compute inv(L L') restricted on L + +// Algorithm: +// We need to study this later as this is the bottleneck. +// Document it as a lyx. + +namespace PackedCSparse { + template + struct ProjinvOutput : SparseMatrix + { + TransposeOutput Lt; // sparsity pattern of the Lt + UniqueAlignedPtr w; // the row of L we are computing + UniquePtr c; // c[i] = index the last nonzero on column i in the current L + + void initialize(const SparseMatrix& L) + { + pcs_assert(L.initialized(), "chol: bad inputs."); + pcs_assert(L.n == L.m, "chol: dimensions mismatch."); + + // Copy the sparsity of L + SparseMatrix::operator=(std::move(L.clone())); + + // allocate workspaces + Ti n = L.n; + w.reset(pcs_aligned_new(n)); + c.reset(new Ti[n]); + Lt = transpose(L); + } + }; + + template + void projinv(ProjinvOutput& o, const SparseMatrix& L) + { + if (!o.initialized()) + o.initialize(L); + + Tx* Sx = o.x.get(); Ti n = o.n; + Ti* Li = L.i.get(), * Lp = L.p.get(); Tx* Lv = L.x.get(); + Ti* Lti = o.Lt.i.get(), * Ltp = o.Lt.p.get(); + Tx* w = o.w.get(); + Ti* c = o.c.get(); + Tx T0 = Tx(0), T1 = Tx(1); + + for (Ti k = 0; k < n; k++) + c[k] = Lp[k + 1] - 1; + + for (Ti k = n - 1; k != -1; k--) + { + for (Ti p = Lp[k] + 1; p < Lp[k + 1]; p++) + w[Li[p]] = Sx[p]; + + Tx sum = T1 / Lv[Lp[k]]; + for (Ti p = Ltp[k + 1] - 1; p != Ltp[k] - 1; p--) + { + Ti i = Lti[p], Lpi = Lp[i]; + + for (Ti q = Lp[i + 1] - 1; q != Lpi; q--) + fnmadd(sum, Lv[q], w[Li[q]]); + //sum -= Lv[q] * w[Li[q]]; + + sum = sum / Lv[Lpi]; + w[i] = sum; + Sx[c[i]] = sum; + c[i]--; + sum = T0; + } + } + } + + template + ProjinvOutput projinv(const SparseMatrix& L) + { + ProjinvOutput o; + projinv(o, L); + return o; + } +} diff --git a/src/external/PackedCSparse/qd/COPYING b/src/external/PackedCSparse/qd/COPYING new file mode 100644 index 00000000..a20ad70e --- /dev/null +++ b/src/external/PackedCSparse/qd/COPYING @@ -0,0 +1,16 @@ +This work was supported by the Director, Office of Science, Division +of Mathematical, Information, and Computational Sciences of the +U.S. Department of Energy under contract numbers DE-AC03-76SF00098 and +DE-AC02-05CH11231. + +Copyright (c) 2003-2009, The Regents of the University of California, +through Lawrence Berkeley National Laboratory (subject to receipt of +any required approvals from U.S. Dept. of Energy) All rights reserved. + +By downloading or using this software you are agreeing to the modified +BSD license that is in file "BSD-LBNL-License.doc" in the main ARPREC +directory. If you wish to use the software for commercial purposes +please contact the Technology Transfer Department at TTD@lbl.gov or +call 510-286-6457." + + diff --git a/src/external/PackedCSparse/qd/Makefile b/src/external/PackedCSparse/qd/Makefile new file mode 100644 index 00000000..791e92b9 --- /dev/null +++ b/src/external/PackedCSparse/qd/Makefile @@ -0,0 +1,15 @@ +QD_CPPFLAGS=$(CPPFLAGS) -I$(R_INCLUDE_DIR) -march=native + +QD_SOURCES= bits.cc c_dd.cc c_qd.cc dd_const.cc dd_real.cc fpu.cc \ + qd_const.cc qd_real.cc util.cc + +QD_OBJECTS=$(QD_SOURCES:.cc=.o) + +libqd.a: $(QD_OBJECTS) + $(AR) rc libqd.a $(QD_OBJECTS) + +.cc.o: + $(CC) $(CFLAGS) $(CPICFLAGS) $(QD_CPPFLAGS) -c $< -o $@ + +clean: + rm -rf $(QD_OBJECTS) libqd.a diff --git a/src/external/PackedCSparse/qd/NEWS b/src/external/PackedCSparse/qd/NEWS new file mode 100644 index 00000000..f32a7575 --- /dev/null +++ b/src/external/PackedCSparse/qd/NEWS @@ -0,0 +1,181 @@ +Changes for 2.3.22 + Made changes suggested by Vasiliy Sotnikov + +Changes for 2.3.21 + Changed renorm in include/qd/qd_inline.h + +Changes for 2.3.20 + added #include to quadt_test.cpp + changed references to 2.3.20 from 2.3.18 + +Changes for 2.3.19 + - Updated qd_real.cpp and dd_real.cpp to fix a buffer overflow problem. + +Changes for 2.3.18 + - Updated qd_real.cpp and dd_real.cpp to fix a problem in output. + +Changes for 2.3.17 + - updated qd_real.cpp, to fix a problem with improper treatment of + negative arguments in nroot. + +Changes for 2.3.16 + - Updated dd_real.cpp, to fix a problem with inaccurate values of + tanh for small arguments. + +Changes for 2.3.15 + - Updated qd_real.cpp, to fix a problem with static definitions. + +Changes for 2.3.14 + - Updated autoconfig (replaced config.sub and config.guess) + +Changes for 2.3.7 + - Fixed bug in to_digits where digits larger than 10 + where output occasionally. + +Changes for 2.3.6 + - Added fmod (C++) and mod (Fortran) functions. + +Changes for 2.3.5 + - Fixed bug in division of qd_real by dd_real. + - Fixed bug in ddoutc (Fortran ddmod.f). + - Now compiles with g++ 4.3. + - Distribute tests/coeff.dat. + +Changes for 2.3.4 + - Fixed bug in Makefile for cygwin / mingw systems. + +Changes for 2.3.3 + - Fixed bug in atan2. + +Changes for 2.3.2 + - Fixed bug in sin / cos / sincos where too much accuracy was + lost for (moderately) large angles. + - Use fused-multiply add intrinsics on IA-64 platforms if + compiled by Intel compiler. + - Fixed bug in c_dd_write and c_qd_write. + - Fixed bug were qdext.mod was not being installed. + +Changes for 2.3.1 + - Fixed bug in sincos and cos_taylor. This affected the result + of trigonometric functions in some cases. + +Changes for 2.3.0 + This is a fairly significant change, breaking API compatibility. + - Moved C++ main entry in libqdmod.a to libqd_f_main.a. + This allows to link Fortran code using QD with custom + C++ main function. Pure Fortran code will need to be linked + with qd_f_main library in addition to qdmod and qd library. + - Constructors accepting pointers made explicit. + - Fortran routines labeled as elemental or pure, where appropriate. + - Write() is now to_string(), and now takes a single fmtflag. + - dd_real addition and multiplication made commutative. + - dd_real now represented as array of two doubles, instead of + two discrete scalars. + - New Fortran generic routines to read / write, operations with + complex and integers. + - Improved exp, sin, and cos functions. + - Removed unused constants and obscure constants only used internally + from public interface. + +Changes for 2.2.6 + - Fixed bug in mixed precision multiplication: qd_real * dd_real. + +Changes for 2.2.5 + - Bug fix in qd_real addition when --enable-ieee-add is specified. + - Debugging routines dump and dump_bits updated; + dump_components removed (just use dump). + - Fortran support for Fortran strings. Use character arrays instead. + - Return NaN under error conditions. + - Added _inf constant; exp now returns Inf when argument is too large. + - Output formatting fixes for Inf and NaNs. + - Added more real-complex mixed arithmetic routines in Fortran + interface. + +Changes for 2.2.4 + - Added random_number interface for Fortran modules. + - Use slightly more conservative values for eps. + - Avoid unnecessary overflow near overflow threshold. + - Added radix, digits, min/maxexponent, range, and precision + intrinsics to Fortran interface. + - Added safe_max (C++) and safe_huge (Fortran). + +Changes for 2.2.3 + - Fix sign function bug in Fortran modules. + +Changes for 2.2.2 + - Do not bother setting uninitialized dd_real and qd_reals to zero. + - Use clock_gettime if available for timing. + - Fortran I/O should be more consistent with C++ version. + - fpu.h is now included with dd_real.h. + +Changes for 2.2.1 + - Minor fixes when printing in scientific format. + - Change search order of C++ compilers in Apple systems to avoid + case insensitive filesystems. + +Changes for 2.2.0 + - Added F95 interface for complex types. + - Renamed dd.h and qd.h to dd_real.h and qd_real.h, respectively. + This will break older C++ code using 2.1.x library, but it was + conflicting with QuickDraw libraries on Macs. (Hence the version + bump to 2.2). + - Removed overloaded typecast operators for int and double. These + permitted *automatic* conversion of dd_real/qd_real to double or + int, which is somewhat dangerous. Instead to_int and to_double + routines are added. + +Changes for 2.1.214 + - Updated pslq_test. + - Implmented numeric_limits<>. + - Better polyroot. + - Added isnan, isfinite, isinf functions. + - Fix / improve input output functions. + - Drop Microsoft Visual C++ 6.0 support. + - More efficient dd_real::sin. + +Changes for 2.1.213 + - Support for x86_64 platforms. + - Drop libtool support for now. + +Changes for 2.1.212 + - Support for pathCC compiler. + - Added accurate and sloppy versions of add / sub / mul / div avaialble. + - Added autodetection of fma functions. + +Changes for 2.1 (2003-12-30) + - added automake scripts. + - use libtool to compile / link and build libraries. + - supports standard installation targets (make install). + - support for Intel C++ compilers (icc / ecc). + - Fortran programs are now linked by C++ compiler. + - support for building shared library. + - minor bug fixes. + +Changes for 2.0 (2003-12-08) + - all header files are in "include/qd" directory. + - added autoconf scripts. + - added config.h and qd_config.h to store configuration information. + - renamed x86_* routines to fpu_* routines. + - added separate Fortran interface (f_* routines). + - options for sloppy multiply and sloppy divison separated. + - fixed C interface to be actually in C syntax. + - updated / added README, AUTHORS, NEWS, and LICENSE files. + - minor bug fixes. + +Changes for 1.2 (2003-12-04) + - added "dist-clean" target in Makefile + - initialize dd and qd variables to zero + - increases tolerance for qd / dd tests + - changed .cc extension to .cpp + - updated README, COPYING, and NEWS files + - added ChangeLog file + - fixed bug in '-all' flag in qd_test + - minor bug fixes + +Changes for 1.1 (2002-10-22) + - added "Changes" file (this file) + - fixed to + - fixed constant (3/4) * pi + - fixed exp(x) to return zero if x is a large negative number + - removed "docs" target in Makefile + diff --git a/src/external/PackedCSparse/qd/README b/src/external/PackedCSparse/qd/README new file mode 100644 index 00000000..8a085d72 --- /dev/null +++ b/src/external/PackedCSparse/qd/README @@ -0,0 +1,437 @@ +Quad Double computation package +Copyright (C) 2003-2019 +================================================ + +Revision date: 26 February 2019 + +Authors: +Yozo Hida U.C. Berkeley yozo@cs.berkeley.edu +Xiaoye S. Li Lawrence Berkeley Natl Lab xiaoye@nersc.gov +David H. Bailey Lawrence Berkeley Natl Lab dhbailey@lbl.gov + +C++ usage guide: +Alex Kaiser Lawrence Berkeley Natl Lab adkaiser@lbl.gov + +This work was supported by the Director, Office of Science, Division of Mathematical, +Information, and Computational Sciences of the U.S. Department of Energy under contract +number DE-AC02-05CH11231. + +This work was supported by the Director, Office of Science, Division of Mathematical, +Information, and Computational Sciences of the U.S. Department of Energy under contract +numbers DE-AC03-76SF00098 and DE-AC02-05CH11231. + +*** IMPORTANT NOTES: + +See the file COPYING for modified BSD license information. +See the file INSTALL for installation instructions. +See the file NEWS for recent revisions. +See the file docs/qd.pdf for additional information. + +Outline: + +I. Introduction +II. Installation of package, and linking and executing user files +III. C++ Usage +IV. Fortran Usage +V. Note on x86-Based Processors (MOST systems in use today) + + +I. Introduction + +This package provides numeric types of twice the precision of IEEE double (106 mantissa +bits, or approximately 32 decimal digits) and four times the precision of IEEE double (212 +mantissa bits, or approximately 64 decimal digits). Due to features such as operator and +function overloading, these facilities can be utilized with only minor modifications to +conventional C++ and Fortran-90 programs. + +In addition to the basic arithmetic operations (add, subtract, multiply, divide, square root), +common transcendental functions such as the exponential, logarithm, trigonometric and +hyperbolic functions are also included. A detailed description of the algorithms used is +available in the docs subdirectory (see docs/qd.pdf). An abridged version of this paper, +which was presented at the ARITH-15 conference, is also available at: + +Yozo Hida, Xiaoye S. Li and David H. Bailey, "Algorithms for quad-double precision + floating point arithmetic," 15th IEEE Symposium on Computer Arithmetic, IEEE Computer + Society, 2001, pg. 155-162, available at + https://www.davidhbailey.com/dhbpapers/arith15.pdf. + + +II. Installation of package, and linking and executing user files + +A. Directories + +There are six directories and several files in the main directory of this distribution, +described below + +src This contains the source code of the quad-double and double-double + library. This source code does not include inline functions, + which are found in the header files in the include directory. + +include This directory contains the header files. + +fortran This directory contains Fortran-90 files. + +tests This directory contains some simple (not comprehensive) tests. + +docs This directory contains a technical paper describing the algorithms. + +config This directory contains various scripts used by the configure + script and the Makefile. + +Please note that all commands refer to a Unix-type environment such as Mac OSX or Ubuntu +Linux using the bash shell. + +B. Installing and building + +To build the library, first run the included configure script by typing + + ./configure + +This script automatically generates makefiles for building the library and selects compilers +and necessary flags and libraries to include. If the user wishes to specify compilers or flags +they may use the following options. + + CXX C++ compiler to use + CXXFLAGS C++ compiler flags to use + CC C compiler to use (for C demo program) + CFLAGS C compiler flags to use (for C demo program) + FC Fortran 90 compiler + FCFLAGS Fortran 90 compiler flags to use + FCLIBS Fortran 90 libraries needed to link with C++ code. + +For example, if one is using GNU compilers, configure with: + + ./configure CXX=g++ FC=gfortran + +The Fortran and C++ compilers must produce compatible binaries. On some systems +additional flags must be included to ensure that portions of the +library are not built with 32 and 64 bit object files. For example, on +64-Bit Mac OSX 10.6 (Snow Leopard) and 10.7 (Lion) the correct +configure line using GNU compilers is: + + ./configure CXX=g++ FC=gfortran FCFLAGS=-m64 + +To build the library, simply type + + make + +and the automatically generated makefiles will build the library including archive files. + +To allow for easy linking to the library, the user may also wish to +install the archive files to a standard place. To do this type: + + make install + +This will also build the library if it has not already been built. Many systems, including Mac +and Ubuntu Linux systems, require administrator privileges to install the library at such +standard places. On such systems, one may type: + + sudo make install + +instead if one has sufficient access. + +The directory "tests" contains programs for high precision quadrature and integer-relation +detection. To build such programs, type: + + make demo + +in the "tests" directory. + +C. Linking and executing user programs + +C++ source files: + +The simplest way to link to the library is to install it to a standard place as described above, and use the -l option. For example + + g++ compileExample.cpp -o compileExample -l qd + +One can also use this method to build with make. A file called "compileExample.cpp" and the +associated makefile "makeCompileExample" illustrate the process. + +A third alternative is to use a link script. If one types "make demo" in the test +directory, the output produced gives guidance as to how to build the files. By +following the structure of the compiling commands one may copy the appropriate portions, +perhaps replacing the filename with an argument that the user can include at link time. +An example of such a script is as follows: + +g++ -DHAVE_CONFIG_H -I.. -I../include -I../include -O2 -MT $1.o -MD -MP -MF +.deps/qd_test.Tpo -c -o $1.o $1.cpp +mv -f .deps/$1.Tpo .deps/$1.Po +g++ -O2 -o $1 $1.o ../src/libqd.a -lm + +To use the link script, make it executable (by typing "chmod +x link.scr) and then type: + +./link.scr compileExample + +Note that the file extension is not included because the script handles all extensions, +expecting the source file to have the extension ".cpp". + +Fortran-90 source files: + +Similarly, a script for compiling fortran programs may be constructed as follows. +In the fortran directory, type "make quadtsq". This compiles the Fortran program +tquadts.f, links with all necessary library files, and produces the executable +"quadts". As this is being done, all flags and linked libraries are displayed. +For instance, on a 2019-era Apple Macintosh system, where the library was installed +as above with g++ for C++ and gfortran for Fortran-90, the following is output: + +gfortran -m64 -ffree-form -c -o tquadtsq.o tquadtsq.f +/bin/sh ../libtool --tag=CXX --mode=link g++ -O2 -o quadtsq tquadtsq.o second.o +libqdmod.la libqd_f_main.la ../src/libqd.la +-L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0 +-L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0/../../.. +-lgfortran -lquadmath -lm -lm + +Thus a general compile-link script is the following: + +gfortran -m64 -ffree-form -c -o $1.o $1.f90 +/bin/sh ../libtool --tag=CXX --mode=link g++ -O2 -o $1 $1.o second.o \ + libqdmod.la libqd_f_main.la ../src/libqd.la \ + -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0 \ + -L/usr/local/gfortran/lib/gcc/x86_64-apple-darwin16/6.3.0/../../.. \ + -lgfortran -lquadmath -lm -lm + +Note that if the .f90 suffix is used for Fortran-90 source files, the +-ffree-form flag may be omitted, but the first line above should end with +"$1.f90" (as shown above). After forming the script, name file, "complink.scr", +and then type "chmod +x complink.scr". To use this script compile and link a +program named "prog.f90", type "./complink.scr prog". + + +III. C++ usage + +As much as possible, operator overloading is included to make basic programming as much +like using standard typed floating-point arithmetic. Changing many codes should be as +simple as changing type statements and a few other lines. + +i. Constructors + +To create dd_real and qd_real variables calculated to the proper precision, one must use +care to use the included constructors properly. Many computations in which variables are +not explicitly typed to multiple-precision may be evaluated with double-precision +arithmetic. The user must take care to ensure that this does not cause errors. In particular, +an expression such as 1.0/3.0 will be evaluated to double precision before assignment or +further arithmetic. Upon assignment to a multi-precision variable, the value will be zero +padded. This problem is serious and potentially difficult to debug. To avoid this, use the +included constructors to force arithmetic to be performed in the full precision requested. + +For a table with descriptions, please see the documentation file qd.pdf in the docs directory. + +ii. Included functions and Constants + +Supported functions include assignment operators, comparisons, arithmetic and +assignment operators, and increments for integer types. Standard C math functions such as +exponentiation, trigonometric, logarithmic, hyperbolic, exponential and rounding functions +are included. As in assignment statements, one must be careful with implied typing of +constants when using these functions. Many codes need particular conversion for the power +function, which is frequently used with constants that must be explicitly typed for multi- +precision codes. + +Many constants are included, which are global and calculated upon initialization. The +following list of constants is calculated for both the dd_real and qd_real classes separately. +Use care to select the correct value. + +For a table with descriptions, please see the included file README.pdf + +ii. Conversion of types + +Static casts may be used to convert constants between types. One may also use constructors +to return temporary multi-precision types within expressions, but should be careful, as this +will waste memory if done repeatedly. For example: + + qd_real y ; + y = sin( qd_real(4.0) / 3.0 ) ; + +C-style casts may be used, but are not recommended. Dynamic and reinterpret casts are +not supported and should be considered unreliable. Casting between multi-precision and +standard precision types can be dangerous, and care must be taken to ensure that programs +are working properly and accuracy has not degraded by use of a misplaced type-conversion. + +D. Available precision, Control of Precision Levels, + +The library provides greatly extended accuracy when compared to standard double +precision. The type dd_real provides for 106 mantissa bits, or about 32 decimal digits. The +type qd_real provides for 212 mantissa bits, or about 64 decimal digits. + +Both the dd_real and qd_real values use the exponent from the highest double-precision +word for arithmetic, and as such do not extend the total range of values available. That +means that the maximum absolute value for either data type is the same as that of double- +precision, or approximately 10^308. The precision near this range, however, is greatly +increased. + +E. I/O + +The standard I/O stream routines have been overloaded to be fully compatible with all +included data types. One may need to manually reset the precision of the stream to obtain +full output. For example, if 60 digits are desired, use: + +cout.precision(60) ; + +When reading values using cin, each input numerical value must start on a separate +line. Two formats are acceptable: + + 1. Write the full constant + 3. Mantissa e exponent + +Here are three valid examples: + + 1.1 + 3.14159 26535 89793 + 123.123123e50 + +When read using cin, these constants will be converted using full multi-precision accuracy. + + +IV. Fortran-90 Usage + +NEW (2007-01-10): The Fortran translation modules now support the complex datatypes +"dd_complex" and "qd_complex". + +Since the quad-double library is written in C++, it must be linked in with a C++ compiler (so +that C++ specific things such as static initializations are correctly handled). Thus the main +program must be written in C/C++ and call the Fortran 90 subroutine. The Fortran 90 +subroutine should be called f_main. + +Here is a sample Fortran-90 program, equivalent to the above C++ program: + + subroutine f_main + use qdmodule + implicit none + type (qd_real) a, b + a = 1.d0 + b = cos(a)**2 + sin(a)**2 - 1.d0 + call qdwrite(6, b) + stop + end subroutine + +This verifies that cos^2(1) + sin^2(1) = 1 to 64 digit accuracy. + +Most operators and generic function references, including many mixed-mode type +combinations with double-precision (ie real*8), have been overloaded (extended) to work +with double-double and quad-double data. It is important, however, that users keep in +mind the fact that expressions are evaluated strictly according to conventional Fortran +operator precedence rules. Thus some subexpressions may be evaluated only to 15-digit +accuracy. For example, with the code + + real*8 d1 + type (dd_real) t1, t2 + ... + t1 = cos (t2) + d1/3.d0 + +the expression d1/3.d0 is computed to real*8 accuracy only (about 15 digits), since both d1 +and 3.d0 have type real*8. This result is then converted to dd_real by zero extension before +being added to cos(t2). So, for example, if d1 held the value 1.d0, then the quotient d1/3.d0 +would only be accurate to 15 digits. If a fully accurate double-double quotient is required, +this should be written: + + real*8 d1 + type (dd_real) t1, t2 + ... + t1 = cos (t2) + ddreal (d1) / 3.d0 + +which forces all operations to be performed with double-double arithmetic. + +Along this line, a constant such as 1.1 appearing in an expression is evaluated only to real*4 +accuracy, and a constant such as 1.1d0 is evaluated only to real*8 accuracy (this is +according to standard Fortran conventions). If full quad-double accuracy is required, for +instance, one should write + + type (qd_real) t1 + ... + t1 = '1.1' + +The quotes enclosing 1.1 specify to the compiler that the constant is to be converted to +binary using quad-double arithmetic, before assignment to t1. Quoted constants may only +appear in assignment statements such as this. + +To link a Fortran-90 program with the C++ qd library, it is recommended to link with the +C++ compiler used to generate the library. The Fortran 90 interface (along with a C-style +main function calling f_main) is found in qdmod library. The qd-config script installed +during "make install" can be used to determine which flags to pass to compile and link your +programs: + + "qd-config --fcflags" displays compiler flags needed to compile your Fortran files. + "qd-config --fclibs" displays linker flags needed by the C++ linker to link in all the +necessary libraries. + +A sample Makefile that can be used as a template for compiling Fortran programs using +quad-double library is found in fortran/Makefile.sample. + +F90 functions defined with dd_real arguments: + Arithmetic: + - * / ** + Comparison tests: == < > <= >= /= + Others: abs, acos, aint, anint, asin, atan, atan2, cos, cosh, dble, erf, + erfc, exp, int, log, log10, max, min, mod, ddcsshf (cosh and sinh), + ddcssnf (cos and sin), ddranf (random number generator in (0,1)), + ddnrtf (n-th root), sign, sin, sinh, sqr, sqrt, tan, tanh + +Similar functions are provided for qd_real arguments with function names qdcsshf, +qdcssnf, qdranf and qdnrtf instead of the names in the list above. + +Input and output of double-double and quad-double data is done using the special +subroutines ddread, ddwrite, qdread and qdwrite. The first argument of these subroutines +is the Fortran I/O unit number, while additional arguments (as many as needed, up to 9 +arguments) are scalar variables or array elements of the appropriate type. Example: + + integer n + type (qd_real) qda, qdb, qdc(n) + ... + call qdwrite (6, qda, qdb) + do j = 1, n + call qdwrite (6, qdc(j)) + enddo + +Each input values must be on a separate line, and may include D or E exponents. Double- +double and quad-double constants may also be specified in assignment statements by +enclosing them in quotes, as in + + ... + type (qd_real) pi + ... + pi = +"3.14159265358979323846264338327950288419716939937510582097494459230" + ... + +Sample Fortran-90 programs illustrating some of these features are provided in the f90 +subdirectory. + + +V. Note on x86-Based Processors (MOST systems in use today) + +The algorithms in this library assume IEEE double precision floating point arithmetic. Since +Intel x86 processors have extended (80-bit) floating point registers, some compilers, +albeit a declining number, may generate commands for the 80-bit instructions. The QD +library does NOT work correctly with 80-bit instructions, so if one's code does not operate +correctly, this may be the reason. To avoid such problems, the round-to-double flag must be +enabled in the control word of the FPU for this library to function properly. The following +functions contains appropriate code to facilitate manipulation of this flag. For non-x86 +systems these functions do nothing (but still exist). + +fpu_fix_start This turns on the round-to-double bit in the control word. +fpu_fix_end This restores the control flag. + +These functions must be called by the main program, as follows: + + int main() { + unsigned int old_cw; + fpu_fix_start(&old_cw); + + ... user code using quad-double library ... + + fpu_fix_end(&old_cw); + } + +A Fortran-90 example is the following: + + subroutine f_main + use qdmodule + implicit none + integer*4 old_cw + + call f_fpu_fix_start(old_cw) + + ... user code using quad-double library ... + + call f_fpu_fix_end(old_cw) + end subroutine + diff --git a/src/external/PackedCSparse/qd/bits.cc b/src/external/PackedCSparse/qd/bits.cc new file mode 100644 index 00000000..4eaf9a26 --- /dev/null +++ b/src/external/PackedCSparse/qd/bits.cc @@ -0,0 +1,85 @@ +/* + * src/bits.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Defines various routines to get / set bits of a IEEE floating point + * number. This used by the library for debugging purposes. + */ + +#include +#include +#include +#include + +#include "qd_config.h" +#include "inline.h" +#include "bits.h" + +#ifdef HAVE_IEEEFP_H +#include +#endif + +using std::setw; + +int get_double_expn(double x) { + if (x == 0.0) + return INT_MIN; + if (QD_ISINF(x) || QD_ISNAN(x)) + return INT_MAX; + + double y = std::abs(x); + int i = 0; + if (y < 1.0) { + while (y < 1.0) { + y *= 2.0; + i++; + } + return -i; + } else if (y >= 2.0) { + while (y >= 2.0) { + y *= 0.5; + i++; + } + return i; + } + return 0; +} + +void print_double_info(std::ostream &os, double x) { + std::streamsize old_prec = os.precision(19); + std::ios_base::fmtflags old_flags = os.flags(); + os << std::scientific; + + os << setw(27) << x << ' '; + if (QD_ISNAN(x) || QD_ISINF(x) || (x == 0.0)) { + os << " "; + } else { + + x = std::abs(x); + int expn = get_double_expn(x); + double d = std::ldexp(1.0, expn); + os << setw(5) << expn << " "; + for (int i = 0; i < 53; i++) { + if (x >= d) { + x -= d; + os << '1'; + } else + os << '0'; + d *= 0.5; + } + + if (x != 0.0) { + // should not happen + os << " +trailing stuff"; + } + } + + os.precision(old_prec); + os.flags(old_flags); +} + diff --git a/src/external/PackedCSparse/qd/bits.h b/src/external/PackedCSparse/qd/bits.h new file mode 100644 index 00000000..58570aac --- /dev/null +++ b/src/external/PackedCSparse/qd/bits.h @@ -0,0 +1,32 @@ +/* + * include/bits.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * This file defines various routines to get / set bits of a IEEE floating + * point number. This is used by the library for debugging purposes. + */ + +#ifndef _QD_BITS_H +#define _QD_BITS_H + +#include +#include "qd_config.h" + +/* Returns the exponent of the double precision number. + Returns INT_MIN is x is zero, and INT_MAX if x is INF or NaN. */ +int get_double_expn(double x); + +/* Prints + SIGN EXPN MANTISSA + of the given double. If x is NaN, INF, or Zero, this + prints out the strings NaN, +/- INF, and 0. */ +void print_double_info(std::ostream &os, double x); + + +#endif /* _QD_BITS_H */ + diff --git a/src/external/PackedCSparse/qd/c_dd.cc b/src/external/PackedCSparse/qd/c_dd.cc new file mode 100644 index 00000000..0a7c12ac --- /dev/null +++ b/src/external/PackedCSparse/qd/c_dd.cc @@ -0,0 +1,314 @@ +/* + * src/c_dd.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains the C wrapper functions for double-double precision arithmetic. + * This can be used from Fortran code. + */ +#include + +#include "qd_config.h" +#include "dd_real.h" +#include "c_dd.h" + +#define TO_DOUBLE_PTR(a, ptr) ptr[0] = a.x[0]; ptr[1] = a.x[1]; + +extern "C" { + +/* add */ +void c_dd_add(const double *a, const double *b, double *c) { + dd_real cc; + cc = dd_real(a) + dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_dd_add_dd_d(const double *a, double b, double *c) { + dd_real cc; + cc = dd_real(a) + b; + TO_DOUBLE_PTR(cc, c); +} +void c_dd_add_d_dd(double a, const double *b, double *c) { + dd_real cc; + cc = a + dd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + +/* sub */ +void c_dd_sub(const double *a, const double *b, double *c) { + dd_real cc; + cc = dd_real(a) - dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_dd_sub_dd_d(const double *a, double b, double *c) { + dd_real cc; + cc = dd_real(a) - b; + TO_DOUBLE_PTR(cc, c); +} +void c_dd_sub_d_dd(double a, const double *b, double *c) { + dd_real cc; + cc = a - dd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + +/* mul */ +void c_dd_mul(const double *a, const double *b, double *c) { + dd_real cc; + cc = dd_real(a) * dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_dd_mul_dd_d(const double *a, double b, double *c) { + dd_real cc; + cc = dd_real(a) * b; + TO_DOUBLE_PTR(cc, c); +} +void c_dd_mul_d_dd(double a, const double *b, double *c) { + dd_real cc; + cc = a * dd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + +/* div */ +void c_dd_div(const double *a, const double *b, double *c) { + dd_real cc; + cc = dd_real(a) / dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_dd_div_dd_d(const double *a, double b, double *c) { + dd_real cc; + cc = dd_real(a) / b; + TO_DOUBLE_PTR(cc, c); +} +void c_dd_div_d_dd(double a, const double *b, double *c) { + dd_real cc; + cc = a / dd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + +/* copy */ +void c_dd_copy(const double *a, double *b) { + b[0] = a[0]; + b[1] = a[1]; +} +void c_dd_copy_d(double a, double *b) { + b[0] = a; + b[1] = 0.0; +} + + +void c_dd_sqrt(const double *a, double *b) { + dd_real bb; + bb = sqrt(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_sqr(const double *a, double *b) { + dd_real bb; + bb = sqr(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_abs(const double *a, double *b) { + dd_real bb; + bb = abs(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_npwr(const double *a, int n, double *b) { + dd_real bb; + bb = npwr(dd_real(a), n); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_nroot(const double *a, int n, double *b) { + dd_real bb; + bb = nroot(dd_real(a), n); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_nint(const double *a, double *b) { + dd_real bb; + bb = nint(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_aint(const double *a, double *b) { + dd_real bb; + bb = aint(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_floor(const double *a, double *b) { + dd_real bb; + bb = floor(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_ceil(const double *a, double *b) { + dd_real bb; + bb = ceil(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_log(const double *a, double *b) { + dd_real bb; + bb = log(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_log10(const double *a, double *b) { + dd_real bb; + bb = log10(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_exp(const double *a, double *b) { + dd_real bb; + bb = exp(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_sin(const double *a, double *b) { + dd_real bb; + bb = sin(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_cos(const double *a, double *b) { + dd_real bb; + bb = cos(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_tan(const double *a, double *b) { + dd_real bb; + bb = tan(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_asin(const double *a, double *b) { + dd_real bb; + bb = asin(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_acos(const double *a, double *b) { + dd_real bb; + bb = acos(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_atan(const double *a, double *b) { + dd_real bb; + bb = atan(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_atan2(const double *a, const double *b, double *c) { + dd_real cc; + cc = atan2(dd_real(a), dd_real(b)); + TO_DOUBLE_PTR(cc, c); +} + +void c_dd_sinh(const double *a, double *b) { + dd_real bb; + bb = sinh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_cosh(const double *a, double *b) { + dd_real bb; + bb = cosh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_tanh(const double *a, double *b) { + dd_real bb; + bb = tanh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_asinh(const double *a, double *b) { + dd_real bb; + bb = asinh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_acosh(const double *a, double *b) { + dd_real bb; + bb = acosh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_dd_atanh(const double *a, double *b) { + dd_real bb; + bb = atanh(dd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_dd_sincos(const double *a, double *s, double *c) { + dd_real ss, cc; + sincos(dd_real(a), ss, cc); + TO_DOUBLE_PTR(ss, s); + TO_DOUBLE_PTR(cc, c); +} + +void c_dd_sincosh(const double *a, double *s, double *c) { + dd_real ss, cc; + sincosh(dd_real(a), ss, cc); + TO_DOUBLE_PTR(ss, s); + TO_DOUBLE_PTR(cc, c); +} + +void c_dd_read(const char *s, double *a) { + dd_real aa(s); + TO_DOUBLE_PTR(aa, a); +} + +void c_dd_swrite(const double *a, int precision, char *s, int len) { + dd_real(a).write(s, len, precision); +} + +void c_dd_write(const double *a) { + std::cout << dd_real(a).to_string(dd_real::_ndigits) << std::endl; +} + +void c_dd_neg(const double *a, double *b) { + b[0] = -a[0]; + b[1] = -a[1]; +} + +void c_dd_rand(double *a) { + dd_real aa; + aa = ddrand(); + TO_DOUBLE_PTR(aa, a); +} + +void c_dd_comp(const double *a, const double *b, int *result) { + dd_real aa(a), bb(b); + if (aa < bb) + *result = -1; + else if (aa > bb) + *result = 1; + else + *result = 0; +} + +void c_dd_comp_dd_d(const double *a, double b, int *result) { + dd_real aa(a), bb(b); + if (aa < bb) + *result = -1; + else if (aa > bb) + *result = 1; + else + *result = 0; +} + +void c_dd_comp_d_dd(double a, const double *b, int *result) { + dd_real aa(a), bb(b); + if (aa < bb) + *result = -1; + else if (aa > bb) + *result = 1; + else + *result = 0; +} + +void c_dd_pi(double *a) { + TO_DOUBLE_PTR(dd_real::_pi, a); +} + +} diff --git a/src/external/PackedCSparse/qd/c_dd.h b/src/external/PackedCSparse/qd/c_dd.h new file mode 100644 index 00000000..310162ed --- /dev/null +++ b/src/external/PackedCSparse/qd/c_dd.h @@ -0,0 +1,98 @@ +/* + * include/c_dd.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains C wrapper function prototypes for double-double precision + * arithmetic. This can also be used from fortran code. + */ +#ifndef _QD_C_DD_H +#define _QD_C_DD_H + +#include "qd_config.h" +#include "fpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* add */ +void c_dd_add(const double *a, const double *b, double *c); +void c_dd_add_d_dd(double a, const double *b, double *c); +void c_dd_add_dd_d(const double *a, double b, double *c); + +/* sub */ +void c_dd_sub(const double *a, const double *b, double *c); +void c_dd_sub_d_dd(double a, const double *b, double *c); +void c_dd_sub_dd_d(const double *a, double b, double *c); + +/* mul */ +void c_dd_mul(const double *a, const double *b, double *c); +void c_dd_mul_d_dd(double a, const double *b, double *c); +void c_dd_mul_dd_d(const double *a, double b, double *c); + +/* div */ +void c_dd_div(const double *a, const double *b, double *c); +void c_dd_div_d_dd(double a, const double *b, double *c); +void c_dd_div_dd_d(const double *a, double b, double *c); + +/* copy */ +void c_dd_copy(const double *a, double *b); +void c_dd_copy_d(double a, double *b); + +void c_dd_sqrt(const double *a, double *b); +void c_dd_sqr(const double *a, double *b); + +void c_dd_abs(const double *a, double *b); + +void c_dd_npwr(const double *a, int b, double *c); +void c_dd_nroot(const double *a, int b, double *c); + +void c_dd_nint(const double *a, double *b); +void c_dd_aint(const double *a, double *b); +void c_dd_floor(const double *a, double *b); +void c_dd_ceil(const double *a, double *b); + +void c_dd_exp(const double *a, double *b); +void c_dd_log(const double *a, double *b); +void c_dd_log10(const double *a, double *b); + +void c_dd_sin(const double *a, double *b); +void c_dd_cos(const double *a, double *b); +void c_dd_tan(const double *a, double *b); + +void c_dd_asin(const double *a, double *b); +void c_dd_acos(const double *a, double *b); +void c_dd_atan(const double *a, double *b); +void c_dd_atan2(const double *a, const double *b, double *c); + +void c_dd_sinh(const double *a, double *b); +void c_dd_cosh(const double *a, double *b); +void c_dd_tanh(const double *a, double *b); + +void c_dd_asinh(const double *a, double *b); +void c_dd_acosh(const double *a, double *b); +void c_dd_atanh(const double *a, double *b); + +void c_dd_sincos(const double *a, double *s, double *c); +void c_dd_sincosh(const double *a, double *s, double *c); + +void c_dd_read(const char *s, double *a); +void c_dd_swrite(const double *a, int precision, char *s, int len); +void c_dd_write(const double *a); +void c_dd_neg(const double *a, double *b); +void c_dd_rand(double *a); +void c_dd_comp(const double *a, const double *b, int *result); +void c_dd_comp_dd_d(const double *a, double b, int *result); +void c_dd_comp_d_dd(double a, const double *b, int *result); +void c_dd_pi(double *a); + +#ifdef __cplusplus +} +#endif + +#endif /* _QD_C_DD_H */ diff --git a/src/external/PackedCSparse/qd/c_qd.cc b/src/external/PackedCSparse/qd/c_qd.cc new file mode 100644 index 00000000..fd50e922 --- /dev/null +++ b/src/external/PackedCSparse/qd/c_qd.cc @@ -0,0 +1,450 @@ +/* + * src/c_qd.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains C wrapper function for quad-double precision arithmetic. + * This can be used from fortran code. + */ +#include + +#include "qd_config.h" +#include "qd_real.h" +#include "c_qd.h" + +#define TO_DOUBLE_PTR(a, ptr) ptr[0] = a.x[0]; ptr[1] = a.x[1]; \ + ptr[2] = a.x[2]; ptr[3] = a.x[3]; + +extern "C" { + + + +/* add */ +void c_qd_add(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) + qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_add_qd_dd(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) + dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_add_dd_qd(const double *a, const double *b, double *c) { + qd_real cc; + cc = dd_real(a) + qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_add_qd_d(const double *a, double b, double *c) { + qd_real cc; + cc = qd_real(a) + b; + TO_DOUBLE_PTR(cc, c); +} +void c_qd_add_d_qd(double a, const double *b, double *c) { + qd_real cc; + cc = a + qd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + + +/* sub */ +void c_qd_sub(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) - qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_sub_qd_dd(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) - dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_sub_dd_qd(const double *a, const double *b, double *c) { + qd_real cc; + cc = dd_real(a) - qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_sub_qd_d(const double *a, double b, double *c) { + qd_real cc; + cc = qd_real(a) - b; + TO_DOUBLE_PTR(cc, c); +} +void c_qd_sub_d_qd(double a, const double *b, double *c) { + qd_real cc; + cc = a - qd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + + +/* mul */ +void c_qd_mul(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) * qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_mul_qd_dd(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) * dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_mul_dd_qd(const double *a, const double *b, double *c) { + qd_real cc; + cc = dd_real(a) * qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_mul_qd_d(const double *a, double b, double *c) { + qd_real cc; + cc = qd_real(a) * b; + TO_DOUBLE_PTR(cc, c); +} +void c_qd_mul_d_qd(double a, const double *b, double *c) { + qd_real cc; + cc = a * qd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + + +/* div */ +void c_qd_div(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) / qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_div_qd_dd(const double *a, const double *b, double *c) { + qd_real cc; + cc = qd_real(a) / dd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_div_dd_qd(const double *a, const double *b, double *c) { + qd_real cc; + cc = dd_real(a) / qd_real(b); + TO_DOUBLE_PTR(cc, c); +} +void c_qd_div_qd_d(const double *a, double b, double *c) { + qd_real cc; + cc = qd_real(a) / b; + TO_DOUBLE_PTR(cc, c); +} +void c_qd_div_d_qd(double a, const double *b, double *c) { + qd_real cc; + cc = a / qd_real(b); + TO_DOUBLE_PTR(cc, c); +} + + + + +/* selfadd */ +void c_qd_selfadd(const double *a, double *b) { + qd_real bb(b); + bb += qd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfadd_dd(const double *a, double *b) { + qd_real bb(b); + bb += dd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfadd_d(double a, double *b) { + qd_real bb(b); + bb += a; + TO_DOUBLE_PTR(bb, b); +} + + + +/* selfsub */ +void c_qd_selfsub(const double *a, double *b) { + qd_real bb(b); + bb -= qd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfsub_dd(const double *a, double *b) { + qd_real bb(b); + bb -= dd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfsub_d(double a, double *b) { + qd_real bb(b); + bb -= a; + TO_DOUBLE_PTR(bb, b); +} + + + +/* selfmul */ +void c_qd_selfmul(const double *a, double *b) { + qd_real bb(b); + bb *= qd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfmul_dd(const double *a, double *b) { + qd_real bb(b); + bb *= dd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfmul_d(double a, double *b) { + qd_real bb(b); + bb *= a; + TO_DOUBLE_PTR(bb, b); +} + + + +/* selfdiv */ +void c_qd_selfdiv(const double *a, double *b) { + qd_real bb(b); + bb /= qd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfdiv_dd(const double *a, double *b) { + qd_real bb(b); + bb /= dd_real(a); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_selfdiv_d(double a, double *b) { + qd_real bb(b); + bb /= a; + TO_DOUBLE_PTR(bb, b); +} + + + +/* copy */ +void c_qd_copy(const double *a, double *b) { + b[0] = a[0]; + b[1] = a[1]; + b[2] = a[2]; + b[3] = a[3]; +} +void c_qd_copy_dd(const double *a, double *b) { + b[0] = a[0]; + b[1] = a[1]; + b[2] = 0.0; + b[3] = 0.0; +} +void c_qd_copy_d(double a, double *b) { + b[0] = a; + b[1] = 0.0; + b[2] = 0.0; + b[3] = 0.0; +} + + +void c_qd_sqrt(const double *a, double *b) { + qd_real bb; + bb = sqrt(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_sqr(const double *a, double *b) { + qd_real bb; + bb = sqr(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_abs(const double *a, double *b) { + qd_real bb; + bb = abs(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_npwr(const double *a, int n, double *b) { + qd_real bb; + bb = npwr(qd_real(a), n); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_nroot(const double *a, int n, double *b) { + qd_real bb; + bb = nroot(qd_real(a), n); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_nint(const double *a, double *b) { + qd_real bb; + bb = nint(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_aint(const double *a, double *b) { + qd_real bb; + bb = aint(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_floor(const double *a, double *b) { + qd_real bb; + bb = floor(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_ceil(const double *a, double *b) { + qd_real bb; + bb = ceil(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_log(const double *a, double *b) { + qd_real bb; + bb = log(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_log10(const double *a, double *b) { + qd_real bb; + bb = log10(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_exp(const double *a, double *b) { + qd_real bb; + bb = exp(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_sin(const double *a, double *b) { + qd_real bb; + bb = sin(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_cos(const double *a, double *b) { + qd_real bb; + bb = cos(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_tan(const double *a, double *b) { + qd_real bb; + bb = tan(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_asin(const double *a, double *b) { + qd_real bb; + bb = asin(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_acos(const double *a, double *b) { + qd_real bb; + bb = acos(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_atan(const double *a, double *b) { + qd_real bb; + bb = atan(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_atan2(const double *a, const double *b, double *c) { + qd_real cc; + cc = atan2(qd_real(a), qd_real(b)); + TO_DOUBLE_PTR(cc, c); +} + +void c_qd_sinh(const double *a, double *b) { + qd_real bb; + bb = sinh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_cosh(const double *a, double *b) { + qd_real bb; + bb = cosh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_tanh(const double *a, double *b) { + qd_real bb; + bb = tanh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_asinh(const double *a, double *b) { + qd_real bb; + bb = asinh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_acosh(const double *a, double *b) { + qd_real bb; + bb = acosh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} +void c_qd_atanh(const double *a, double *b) { + qd_real bb; + bb = atanh(qd_real(a)); + TO_DOUBLE_PTR(bb, b); +} + +void c_qd_sincos(const double *a, double *s, double *c) { + qd_real ss, cc; + sincos(qd_real(a), ss, cc); + TO_DOUBLE_PTR(cc, c); + TO_DOUBLE_PTR(ss, s); +} + +void c_qd_sincosh(const double *a, double *s, double *c) { + qd_real ss, cc; + sincosh(qd_real(a), ss, cc); + TO_DOUBLE_PTR(cc, c); + TO_DOUBLE_PTR(ss, s); +} + +void c_qd_read(const char *s, double *a) { + qd_real aa(s); + TO_DOUBLE_PTR(aa, a); +} + +void c_qd_swrite(const double *a, int precision, char *s, int len) { + qd_real(a).write(s, len, precision); +} + +void c_qd_write(const double *a) { + std::cout << qd_real(a).to_string(qd_real::_ndigits) << std::endl; +} + +void c_qd_neg(const double *a, double *b) { + b[0] = -a[0]; + b[1] = -a[1]; + b[2] = -a[2]; + b[3] = -a[3]; +} + +void c_qd_rand(double *a) { + qd_real aa; + aa = qdrand(); + TO_DOUBLE_PTR(aa, a); +} + +void c_qd_comp(const double *a, const double *b, int *result) { + qd_real aa(a), bb(b); + if (aa < bb) + *result = -1; + else if (aa > bb) + *result = 1; + else + *result = 0; +} + +void c_qd_comp_qd_d(const double *a, double b, int *result) { + qd_real aa(a); + if (aa < b) + *result = -1; + else if (aa > b) + *result = 1; + else + *result = 0; +} + +void c_qd_comp_d_qd(double a, const double *b, int *result) { + qd_real bb(b); + if (a < bb) + *result = -1; + else if (a > bb) + *result = 1; + else + *result = 0; +} + +void c_qd_pi(double *a) { + TO_DOUBLE_PTR(qd_real::_pi, a); +} + +} diff --git a/src/external/PackedCSparse/qd/c_qd.h b/src/external/PackedCSparse/qd/c_qd.h new file mode 100644 index 00000000..d11a7ff1 --- /dev/null +++ b/src/external/PackedCSparse/qd/c_qd.h @@ -0,0 +1,119 @@ +/* + * include/c_qd.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains C wrapper function prototypes for quad-double precision + * arithmetic. This can also be used from fortran code. + */ +#ifndef _QD_C_QD_H +#define _QD_C_QD_H + +#include "c_dd.h" +#include "qd_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* add */ +void c_qd_add(const double *a, const double *b, double *c); +void c_qd_add_dd_qd(const double *a, const double *b, double *c); +void c_qd_add_qd_dd(const double *a, const double *b, double *c); +void c_qd_add_d_qd(double a, const double *b, double *c); +void c_qd_add_qd_d(const double *a, double b, double *c); +void c_qd_selfadd(const double *a, double *b); +void c_qd_selfadd_dd(const double *a, double *b); +void c_qd_selfadd_d(double a, double *b); + +/* sub */ +void c_qd_sub(const double *a, const double *b, double *c); +void c_qd_sub_dd_qd(const double *a, const double *b, double *c); +void c_qd_sub_qd_dd(const double *a, const double *b, double *c); +void c_qd_sub_d_qd(double a, const double *b, double *c); +void c_qd_sub_qd_d(const double *a, double b, double *c); +void c_qd_selfsub(const double *a, double *b); +void c_qd_selfsub_dd(const double *a, double *b); +void c_qd_selfsub_d(double a, double *b); + +/* mul */ +void c_qd_mul(const double *a, const double *b, double *c); +void c_qd_mul_dd_qd(const double *a, const double *b, double *c); +void c_qd_mul_qd_dd(const double *a, const double *b, double *c); +void c_qd_mul_d_qd(double a, const double *b, double *c); +void c_qd_mul_qd_d(const double *a, double b, double *c); +void c_qd_selfmul(const double *a, double *b); +void c_qd_selfmul_dd(const double *a, double *b); +void c_qd_selfmul_d(double a, double *b); + +/* div */ +void c_qd_div(const double *a, const double *b, double *c); +void c_qd_div_dd_qd(const double *a, const double *b, double *c); +void c_qd_div_qd_dd(const double *a, const double *b, double *c); +void c_qd_div_d_qd(double a, const double *b, double *c); +void c_qd_div_qd_d(const double *a, double b, double *c); +void c_qd_selfdiv(const double *a, double *b); +void c_qd_selfdiv_dd(const double *a, double *b); +void c_qd_selfdiv_d(double a, double *b); + +/* copy */ +void c_qd_copy(const double *a, double *b); +void c_qd_copy_dd(const double *a, double *b); +void c_qd_copy_d(double a, double *b); + +void c_qd_sqrt(const double *a, double *b); +void c_qd_sqr(const double *a, double *b); + +void c_qd_abs(const double *a, double *b); + +void c_qd_npwr(const double *a, int b, double *c); +void c_qd_nroot(const double *a, int b, double *c); + +void c_qd_nint(const double *a, double *b); +void c_qd_aint(const double *a, double *b); +void c_qd_floor(const double *a, double *b); +void c_qd_ceil(const double *a, double *b); + +void c_qd_exp(const double *a, double *b); +void c_qd_log(const double *a, double *b); +void c_qd_log10(const double *a, double *b); + +void c_qd_sin(const double *a, double *b); +void c_qd_cos(const double *a, double *b); +void c_qd_tan(const double *a, double *b); + +void c_qd_asin(const double *a, double *b); +void c_qd_acos(const double *a, double *b); +void c_qd_atan(const double *a, double *b); +void c_qd_atan2(const double *a, const double *b, double *c); + +void c_qd_sinh(const double *a, double *b); +void c_qd_cosh(const double *a, double *b); +void c_qd_tanh(const double *a, double *b); + +void c_qd_asinh(const double *a, double *b); +void c_qd_acosh(const double *a, double *b); +void c_qd_atanh(const double *a, double *b); + +void c_qd_sincos(const double *a, double *s, double *c); +void c_qd_sincosh(const double *a, double *s, double *c); + +void c_qd_read(const char *s, double *a); +void c_qd_swrite(const double *a, int precision, char *s, int len); +void c_qd_write(const double *a); +void c_qd_neg(const double *a, double *b); +void c_qd_rand(double *a); +void c_qd_comp(const double *a, const double *b, int *result); +void c_qd_comp_qd_d(const double *a, double b, int *result); +void c_qd_comp_d_qd(double a, const double *b, int *result); +void c_qd_pi(double *a); + +#ifdef __cplusplus +} +#endif + +#endif /* _QD_C_QD_H */ diff --git a/src/external/PackedCSparse/qd/dd_const.cc b/src/external/PackedCSparse/qd/dd_const.cc new file mode 100644 index 00000000..38b7b5ae --- /dev/null +++ b/src/external/PackedCSparse/qd/dd_const.cc @@ -0,0 +1,40 @@ +/* + * src/dd_const.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + */ +#include "qd_config.h" +#include "dd_real.h" + +const dd_real dd_real::_2pi = dd_real(6.283185307179586232e+00, + 2.449293598294706414e-16); +const dd_real dd_real::_pi = dd_real(3.141592653589793116e+00, + 1.224646799147353207e-16); +const dd_real dd_real::_pi2 = dd_real(1.570796326794896558e+00, + 6.123233995736766036e-17); +const dd_real dd_real::_pi4 = dd_real(7.853981633974482790e-01, + 3.061616997868383018e-17); +const dd_real dd_real::_3pi4 = dd_real(2.356194490192344837e+00, + 9.1848509936051484375e-17); +const dd_real dd_real::_e = dd_real(2.718281828459045091e+00, + 1.445646891729250158e-16); +const dd_real dd_real::_log2 = dd_real(6.931471805599452862e-01, + 2.319046813846299558e-17); +const dd_real dd_real::_log10 = dd_real(2.302585092994045901e+00, + -2.170756223382249351e-16); +const dd_real dd_real::_nan = dd_real(qd::_d_nan, qd::_d_nan); +const dd_real dd_real::_inf = dd_real(qd::_d_inf, qd::_d_inf); + +const double dd_real::_eps = 4.93038065763132e-32; // 2^-104 +const double dd_real::_min_normalized = 2.0041683600089728e-292; // = 2^(-1022 + 53) +const dd_real dd_real::_max = + dd_real(1.79769313486231570815e+308, 9.97920154767359795037e+291); +const dd_real dd_real::_safe_max = + dd_real(1.7976931080746007281e+308, 9.97920154767359795037e+291); +const int dd_real::_ndigits = 31; + + diff --git a/src/external/PackedCSparse/qd/dd_inline.h b/src/external/PackedCSparse/qd/dd_inline.h new file mode 100644 index 00000000..89bc24f2 --- /dev/null +++ b/src/external/PackedCSparse/qd/dd_inline.h @@ -0,0 +1,621 @@ +/* + * include/dd_inline.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains small functions (suitable for inlining) in the double-double + * arithmetic package. + */ +#ifndef _QD_DD_INLINE_H +#define _QD_DD_INLINE_H + +#include +#include "inline.h" + +#ifndef QD_INLINE +#define inline +#endif + + +/*********** Additions ************/ +/* double-double = double + double */ +inline dd_real dd_real::add(double a, double b) { + double s, e; + s = qd::two_sum(a, b, e); + return dd_real(s, e); +} + +/* double-double + double */ +inline dd_real operator+(const dd_real &a, double b) { + double s1, s2; + s1 = qd::two_sum(a.x[0], b, s2); + s2 += a.x[1]; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +} + +/* double-double + double-double */ +inline dd_real dd_real::ieee_add(const dd_real &a, const dd_real &b) { + /* This one satisfies IEEE style error bound, + due to K. Briggs and W. Kahan. */ + double s1, s2, t1, t2; + + s1 = qd::two_sum(a.x[0], b.x[0], s2); + t1 = qd::two_sum(a.x[1], b.x[1], t2); + s2 += t1; + s1 = qd::quick_two_sum(s1, s2, s2); + s2 += t2; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +} + +inline dd_real dd_real::sloppy_add(const dd_real &a, const dd_real &b) { + /* This is the less accurate version ... obeys Cray-style + error bound. */ + double s, e; + + s = qd::two_sum(a.x[0], b.x[0], e); + e += (a.x[1] + b.x[1]); + s = qd::quick_two_sum(s, e, e); + return dd_real(s, e); +} + +inline dd_real operator+(const dd_real &a, const dd_real &b) { +#ifndef QD_IEEE_ADD + return dd_real::sloppy_add(a, b); +#else + return dd_real::ieee_add(a, b); +#endif +} + +/* double + double-double */ +inline dd_real operator+(double a, const dd_real &b) { + return (b + a); +} + + +/*********** Self-Additions ************/ +/* double-double += double */ +inline dd_real &dd_real::operator+=(double a) { + double s1, s2; + s1 = qd::two_sum(x[0], a, s2); + s2 += x[1]; + x[0] = qd::quick_two_sum(s1, s2, x[1]); + return *this; +} + +/* double-double += double-double */ +inline dd_real &dd_real::operator+=(const dd_real &a) { +#ifndef QD_IEEE_ADD + double s, e; + s = qd::two_sum(x[0], a.x[0], e); + e += x[1]; + e += a.x[1]; + x[0] = qd::quick_two_sum(s, e, x[1]); + return *this; +#else + double s1, s2, t1, t2; + s1 = qd::two_sum(x[0], a.x[0], s2); + t1 = qd::two_sum(x[1], a.x[1], t2); + s2 += t1; + s1 = qd::quick_two_sum(s1, s2, s2); + s2 += t2; + x[0] = qd::quick_two_sum(s1, s2, x[1]); + return *this; +#endif +} + +/*********** Subtractions ************/ +/* double-double = double - double */ +inline dd_real dd_real::sub(double a, double b) { + double s, e; + s = qd::two_diff(a, b, e); + return dd_real(s, e); +} + +/* double-double - double */ +inline dd_real operator-(const dd_real &a, double b) { + double s1, s2; + s1 = qd::two_diff(a.x[0], b, s2); + s2 += a.x[1]; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +} + +/* double-double - double-double */ +inline dd_real operator-(const dd_real &a, const dd_real &b) { +#ifndef QD_IEEE_ADD + double s, e; + s = qd::two_diff(a.x[0], b.x[0], e); + e += a.x[1]; + e -= b.x[1]; + s = qd::quick_two_sum(s, e, e); + return dd_real(s, e); +#else + double s1, s2, t1, t2; + s1 = qd::two_diff(a.x[0], b.x[0], s2); + t1 = qd::two_diff(a.x[1], b.x[1], t2); + s2 += t1; + s1 = qd::quick_two_sum(s1, s2, s2); + s2 += t2; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +#endif +} + +/* double - double-double */ +inline dd_real operator-(double a, const dd_real &b) { + double s1, s2; + s1 = qd::two_diff(a, b.x[0], s2); + s2 -= b.x[1]; + s1 = qd::quick_two_sum(s1, s2, s2); + return dd_real(s1, s2); +} + +/*********** Self-Subtractions ************/ +/* double-double -= double */ +inline dd_real &dd_real::operator-=(double a) { + double s1, s2; + s1 = qd::two_diff(x[0], a, s2); + s2 += x[1]; + x[0] = qd::quick_two_sum(s1, s2, x[1]); + return *this; +} + +/* double-double -= double-double */ +inline dd_real &dd_real::operator-=(const dd_real &a) { +#ifndef QD_IEEE_ADD + double s, e; + s = qd::two_diff(x[0], a.x[0], e); + e += x[1]; + e -= a.x[1]; + x[0] = qd::quick_two_sum(s, e, x[1]); + return *this; +#else + double s1, s2, t1, t2; + s1 = qd::two_diff(x[0], a.x[0], s2); + t1 = qd::two_diff(x[1], a.x[1], t2); + s2 += t1; + s1 = qd::quick_two_sum(s1, s2, s2); + s2 += t2; + x[0] = qd::quick_two_sum(s1, s2, x[1]); + return *this; +#endif +} + +/*********** Unary Minus ***********/ +inline dd_real dd_real::operator-() const { + return dd_real(-x[0], -x[1]); +} + +/*********** Multiplications ************/ +/* double-double = double * double */ +inline dd_real dd_real::mul(double a, double b) { + double p, e; + p = qd::two_prod(a, b, e); + return dd_real(p, e); +} + +/* double-double * (2.0 ^ exp) */ +inline dd_real ldexp(const dd_real &a, int exp) { + return dd_real(std::ldexp(a.x[0], exp), std::ldexp(a.x[1], exp)); +} + +/* double-double * double, where double is a power of 2. */ +inline dd_real mul_pwr2(const dd_real &a, double b) { + return dd_real(a.x[0] * b, a.x[1] * b); +} + +/* double-double * double */ +inline dd_real operator*(const dd_real &a, double b) { + double p1, p2; + + p1 = qd::two_prod(a.x[0], b, p2); + p2 += (a.x[1] * b); + p1 = qd::quick_two_sum(p1, p2, p2); + return dd_real(p1, p2); +} + +/* double-double * double-double */ +inline dd_real operator*(const dd_real &a, const dd_real &b) { + double p1, p2; + + p1 = qd::two_prod(a.x[0], b.x[0], p2); + p2 += (a.x[0] * b.x[1] + a.x[1] * b.x[0]); + p1 = qd::quick_two_sum(p1, p2, p2); + return dd_real(p1, p2); +} + +/* double * double-double */ +inline dd_real operator*(double a, const dd_real &b) { + return (b * a); +} + +/*********** Self-Multiplications ************/ +/* double-double *= double */ +inline dd_real &dd_real::operator*=(double a) { + double p1, p2; + p1 = qd::two_prod(x[0], a, p2); + p2 += x[1] * a; + x[0] = qd::quick_two_sum(p1, p2, x[1]); + return *this; +} + +/* double-double *= double-double */ +inline dd_real &dd_real::operator*=(const dd_real &a) { + double p1, p2; + p1 = qd::two_prod(x[0], a.x[0], p2); + p2 += a.x[1] * x[0]; + p2 += a.x[0] * x[1]; + x[0] = qd::quick_two_sum(p1, p2, x[1]); + return *this; +} + +/*********** Divisions ************/ +inline dd_real dd_real::div(double a, double b) { + double q1, q2; + double p1, p2; + double s, e; + + q1 = a / b; + + /* Compute a - q1 * b */ + p1 = qd::two_prod(q1, b, p2); + s = qd::two_diff(a, p1, e); + e -= p2; + + /* get next approximation */ + q2 = (s + e) / b; + + s = qd::quick_two_sum(q1, q2, e); + + return dd_real(s, e); +} + +/* double-double / double */ +inline dd_real operator/(const dd_real &a, double b) { + + double q1, q2; + double p1, p2; + double s, e; + dd_real r; + + q1 = a.x[0] / b; /* approximate quotient. */ + + /* Compute this - q1 * d */ + p1 = qd::two_prod(q1, b, p2); + s = qd::two_diff(a.x[0], p1, e); + e += a.x[1]; + e -= p2; + + /* get next approximation. */ + q2 = (s + e) / b; + + /* renormalize */ + r.x[0] = qd::quick_two_sum(q1, q2, r.x[1]); + + return r; +} + +inline dd_real dd_real::sloppy_div(const dd_real &a, const dd_real &b) { + double s1, s2; + double q1, q2; + dd_real r; + + q1 = a.x[0] / b.x[0]; /* approximate quotient */ + + /* compute this - q1 * dd */ + r = b * q1; + s1 = qd::two_diff(a.x[0], r.x[0], s2); + s2 -= r.x[1]; + s2 += a.x[1]; + + /* get next approximation */ + q2 = (s1 + s2) / b.x[0]; + + /* renormalize */ + r.x[0] = qd::quick_two_sum(q1, q2, r.x[1]); + return r; +} + +inline dd_real dd_real::accurate_div(const dd_real &a, const dd_real &b) { + double q1, q2, q3; + dd_real r; + + q1 = a.x[0] / b.x[0]; /* approximate quotient */ + + r = a - q1 * b; + + q2 = r.x[0] / b.x[0]; + r -= (q2 * b); + + q3 = r.x[0] / b.x[0]; + + q1 = qd::quick_two_sum(q1, q2, q2); + r = dd_real(q1, q2) + q3; + return r; +} + +/* double-double / double-double */ +inline dd_real operator/(const dd_real &a, const dd_real &b) { +#ifdef QD_SLOPPY_DIV + return dd_real::sloppy_div(a, b); +#else + return dd_real::accurate_div(a, b); +#endif +} + +/* double / double-double */ +inline dd_real operator/(double a, const dd_real &b) { + return dd_real(a) / b; +} + +inline dd_real inv(const dd_real &a) { + return 1.0 / a; +} + +/*********** Self-Divisions ************/ +/* double-double /= double */ +inline dd_real &dd_real::operator/=(double a) { + *this = *this / a; + return *this; +} + +/* double-double /= double-double */ +inline dd_real &dd_real::operator/=(const dd_real &a) { + *this = *this / a; + return *this; +} + +/********** Remainder **********/ +inline dd_real drem(const dd_real &a, const dd_real &b) { + dd_real n = nint(a / b); + return (a - n * b); +} + +inline dd_real divrem(const dd_real &a, const dd_real &b, dd_real &r) { + dd_real n = nint(a / b); + r = a - n * b; + return n; +} + +/*********** Squaring **********/ +inline dd_real sqr(const dd_real &a) { + double p1, p2; + double s1, s2; + p1 = qd::two_sqr(a.x[0], p2); + p2 += 2.0 * a.x[0] * a.x[1]; + p2 += a.x[1] * a.x[1]; + s1 = qd::quick_two_sum(p1, p2, s2); + return dd_real(s1, s2); +} + +inline dd_real dd_real::sqr(double a) { + double p1, p2; + p1 = qd::two_sqr(a, p2); + return dd_real(p1, p2); +} + + +/********** Exponentiation **********/ +inline dd_real dd_real::operator^(int n) { + return npwr(*this, n); +} + + +/*********** Assignments ************/ +/* double-double = double */ +inline dd_real &dd_real::operator=(double a) { + x[0] = a; + x[1] = 0.0; + return *this; +} + +/*********** Equality Comparisons ************/ +/* double-double == double */ +inline bool operator==(const dd_real &a, double b) { + return (a.x[0] == b && a.x[1] == 0.0); +} + +/* double-double == double-double */ +inline bool operator==(const dd_real &a, const dd_real &b) { + return (a.x[0] == b.x[0] && a.x[1] == b.x[1]); +} + +/* double == double-double */ +inline bool operator==(double a, const dd_real &b) { + return (a == b.x[0] && b.x[1] == 0.0); +} + +/*********** Greater-Than Comparisons ************/ +/* double-double > double */ +inline bool operator>(const dd_real &a, double b) { + return (a.x[0] > b || (a.x[0] == b && a.x[1] > 0.0)); +} + +/* double-double > double-double */ +inline bool operator>(const dd_real &a, const dd_real &b) { + return (a.x[0] > b.x[0] || (a.x[0] == b.x[0] && a.x[1] > b.x[1])); +} + +/* double > double-double */ +inline bool operator>(double a, const dd_real &b) { + return (a > b.x[0] || (a == b.x[0] && b.x[1] < 0.0)); +} + +/*********** Less-Than Comparisons ************/ +/* double-double < double */ +inline bool operator<(const dd_real &a, double b) { + return (a.x[0] < b || (a.x[0] == b && a.x[1] < 0.0)); +} + +/* double-double < double-double */ +inline bool operator<(const dd_real &a, const dd_real &b) { + return (a.x[0] < b.x[0] || (a.x[0] == b.x[0] && a.x[1] < b.x[1])); +} + +/* double < double-double */ +inline bool operator<(double a, const dd_real &b) { + return (a < b.x[0] || (a == b.x[0] && b.x[1] > 0.0)); +} + +/*********** Greater-Than-Or-Equal-To Comparisons ************/ +/* double-double >= double */ +inline bool operator>=(const dd_real &a, double b) { + return (a.x[0] > b || (a.x[0] == b && a.x[1] >= 0.0)); +} + +/* double-double >= double-double */ +inline bool operator>=(const dd_real &a, const dd_real &b) { + return (a.x[0] > b.x[0] || (a.x[0] == b.x[0] && a.x[1] >= b.x[1])); +} + +/* double >= double-double */ +inline bool operator>=(double a, const dd_real &b) { + return (b <= a); +} + +/*********** Less-Than-Or-Equal-To Comparisons ************/ +/* double-double <= double */ +inline bool operator<=(const dd_real &a, double b) { + return (a.x[0] < b || (a.x[0] == b && a.x[1] <= 0.0)); +} + +/* double-double <= double-double */ +inline bool operator<=(const dd_real &a, const dd_real &b) { + return (a.x[0] < b.x[0] || (a.x[0] == b.x[0] && a.x[1] <= b.x[1])); +} + +/* double <= double-double */ +inline bool operator<=(double a, const dd_real &b) { + return (b >= a); +} + +/*********** Not-Equal-To Comparisons ************/ +/* double-double != double */ +inline bool operator!=(const dd_real &a, double b) { + return (a.x[0] != b || a.x[1] != 0.0); +} + +/* double-double != double-double */ +inline bool operator!=(const dd_real &a, const dd_real &b) { + return (a.x[0] != b.x[0] || a.x[1] != b.x[1]); +} + +/* double != double-double */ +inline bool operator!=(double a, const dd_real &b) { + return (a != b.x[0] || b.x[1] != 0.0); +} + +/*********** Micellaneous ************/ +/* this == 0 */ +inline bool dd_real::is_zero() const { + return (x[0] == 0.0); +} + +/* this == 1 */ +inline bool dd_real::is_one() const { + return (x[0] == 1.0 && x[1] == 0.0); +} + +/* this > 0 */ +inline bool dd_real::is_positive() const { + return (x[0] > 0.0); +} + +/* this < 0 */ +inline bool dd_real::is_negative() const { + return (x[0] < 0.0); +} + +inline dd_real::operator bool() const { + return (x[0] != 0.0); +} + +inline dd_real::operator double() const { + return to_double(*this); +} + +/* Absolute value */ +inline dd_real abs(const dd_real &a) { + return (a.x[0] < 0.0) ? -a : a; +} + +inline dd_real fabs(const dd_real &a) { + return abs(a); +} + +/* Round to Nearest integer */ +inline dd_real nint(const dd_real &a) { + double hi = qd::nint(a.x[0]); + double lo; + + if (hi == a.x[0]) { + /* High word is an integer already. Round the low word.*/ + lo = qd::nint(a.x[1]); + + /* Renormalize. This is needed if x[0] = some integer, x[1] = 1/2.*/ + hi = qd::quick_two_sum(hi, lo, lo); + } else { + /* High word is not an integer. */ + lo = 0.0; + if (std::abs(hi-a.x[0]) == 0.5 && a.x[1] < 0.0) { + /* There is a tie in the high word, consult the low word + to break the tie. */ + hi -= 1.0; /* NOTE: This does not cause INEXACT. */ + } + } + + return dd_real(hi, lo); +} + +inline dd_real floor(const dd_real &a) { + double hi = std::floor(a.x[0]); + double lo = 0.0; + + if (hi == a.x[0]) { + /* High word is integer already. Round the low word. */ + lo = std::floor(a.x[1]); + hi = qd::quick_two_sum(hi, lo, lo); + } + + return dd_real(hi, lo); +} + +inline dd_real ceil(const dd_real &a) { + double hi = std::ceil(a.x[0]); + double lo = 0.0; + + if (hi == a.x[0]) { + /* High word is integer already. Round the low word. */ + lo = std::ceil(a.x[1]); + hi = qd::quick_two_sum(hi, lo, lo); + } + + return dd_real(hi, lo); +} + +inline dd_real aint(const dd_real &a) { + return (a.x[0] >= 0.0) ? floor(a) : ceil(a); +} + +/* Cast to double. */ +inline double to_double(const dd_real &a) { + return a.x[0]; +} + +/* Cast to int. */ +inline int to_int(const dd_real &a) { + return static_cast(a.x[0]); +} + +/* Random number generator */ +inline dd_real dd_real::rand() { + return ddrand(); +} + +#endif /* _QD_DD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/dd_real.cc b/src/external/PackedCSparse/qd/dd_real.cc new file mode 100644 index 00000000..ff4d5223 --- /dev/null +++ b/src/external/PackedCSparse/qd/dd_real.cc @@ -0,0 +1,1303 @@ +/* + * src/dd_real.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + * + * Contains implementation of non-inlined functions of double-double + * package. Inlined functions are found in dd_inline.h (in include directory). + */ +#include +#include +#include +#include +#include +#include +#include + +#include "qd_config.h" +#include "dd_real.h" +#include "util.h" + +#include "bits.h" + +#ifndef QD_INLINE +#include "dd_inline.h" +#endif + +using std::cout; +using std::cerr; +using std::endl; +using std::ostream; +using std::istream; +using std::ios_base; +using std::string; +using std::setw; + +/* This routine is called whenever a fatal error occurs. */ +void dd_real::error(const char *msg) { + //if (msg) { cerr << "ERROR " << msg << endl; } +} + +/* Computes the square root of the double-double number dd. + NOTE: dd must be a non-negative number. */ +QD_API dd_real sqrt(const dd_real &a) { + /* Strategy: Use Karp's trick: if x is an approximation + to sqrt(a), then + + sqrt(a) = a*x + [a - (a*x)^2] * x / 2 (approx) + + The approximation is accurate to twice the accuracy of x. + Also, the multiplication (a*x) and [-]*x can be done with + only half the precision. + */ + + if (a.is_zero()) + return 0.0; + + if (a.is_negative()) { + dd_real::error("(dd_real::sqrt): Negative argument."); + return dd_real::_nan; + } + + double x = 1.0 / std::sqrt(a.x[0]); + double ax = a.x[0] * x; + return dd_real::add(ax, (a - dd_real::sqr(ax)).x[0] * (x * 0.5)); +} + +/* Computes the square root of a double in double-double precision. + NOTE: d must not be negative. */ +dd_real dd_real::sqrt(double d) { + return ::sqrt(dd_real(d)); +} + +/* Computes the n-th root of the double-double number a. + NOTE: n must be a positive integer. + NOTE: If n is even, then a must not be negative. */ +dd_real nroot(const dd_real &a, int n) { + /* Strategy: Use Newton iteration for the function + + f(x) = x^(-n) - a + + to find its root a^{-1/n}. The iteration is thus + + x' = x + x * (1 - a * x^n) / n + + which converges quadratically. We can then find + a^{1/n} by taking the reciprocal. + */ + + if (n <= 0) { + dd_real::error("(dd_real::nroot): N must be positive."); + return dd_real::_nan; + } + + if (n%2 == 0 && a.is_negative()) { + dd_real::error("(dd_real::nroot): Negative argument."); + return dd_real::_nan; + } + + if (n == 1) { + return a; + } + if (n == 2) { + return sqrt(a); + } + + if (a.is_zero()) + return 0.0; + + /* Note a^{-1/n} = exp(-log(a)/n) */ + dd_real r = abs(a); + dd_real x = std::exp(-std::log(r.x[0]) / n); + + /* Perform Newton's iteration. */ + x += x * (1.0 - r * npwr(x, n)) / static_cast(n); + if (a.x[0] < 0.0) + x = -x; + return 1.0/x; +} + +/* Computes the n-th power of a double-double number. + NOTE: 0^0 causes an error. */ +dd_real npwr(const dd_real &a, int n) { + + if (n == 0) { + if (a.is_zero()) { + dd_real::error("(dd_real::npwr): Invalid argument."); + return dd_real::_nan; + } + return 1.0; + } + + dd_real r = a; + dd_real s = 1.0; + int N = std::abs(n); + + if (N > 1) { + /* Use binary exponentiation */ + while (N > 0) { + if (N % 2 == 1) { + s *= r; + } + N /= 2; + if (N > 0) + r = sqr(r); + } + } else { + s = r; + } + + /* Compute the reciprocal if n is negative. */ + if (n < 0) + return (1.0 / s); + + return s; +} + +dd_real pow(const dd_real &a, int n) { + return npwr(a, n); +} + +dd_real pow(const dd_real &a, const dd_real &b) { + return exp(b * log(a)); +} + +static const int n_inv_fact = 15; +static const double inv_fact[n_inv_fact][2] = { + { 1.66666666666666657e-01, 9.25185853854297066e-18}, + { 4.16666666666666644e-02, 2.31296463463574266e-18}, + { 8.33333333333333322e-03, 1.15648231731787138e-19}, + { 1.38888888888888894e-03, -5.30054395437357706e-20}, + { 1.98412698412698413e-04, 1.72095582934207053e-22}, + { 2.48015873015873016e-05, 2.15119478667758816e-23}, + { 2.75573192239858925e-06, -1.85839327404647208e-22}, + { 2.75573192239858883e-07, 2.37677146222502973e-23}, + { 2.50521083854417202e-08, -1.44881407093591197e-24}, + { 2.08767569878681002e-09, -1.20734505911325997e-25}, + { 1.60590438368216133e-10, 1.25852945887520981e-26}, + { 1.14707455977297245e-11, 2.06555127528307454e-28}, + { 7.64716373181981641e-13, 7.03872877733453001e-30}, + { 4.77947733238738525e-14, 4.39920548583408126e-31}, + { 2.81145725434552060e-15, 1.65088427308614326e-31} +}; + +/* Exponential. Computes exp(x) in double-double precision. */ +dd_real exp(const dd_real &a) { + /* Strategy: We first reduce the size of x by noting that + + exp(kr + m * log(2)) = 2^m * exp(r)^k + + where m and k are integers. By choosing m appropriately + we can make |kr| <= log(2) / 2 = 0.347. Then exp(r) is + evaluated using the familiar Taylor series. Reducing the + argument substantially speeds up the convergence. */ + + const double k = 512.0; + const double inv_k = 1.0 / k; + + if (a.x[0] <= -709.0) + return 0.0; + + if (a.x[0] >= 709.0) + return dd_real::_inf; + + if (a.is_zero()) + return 1.0; + + if (a.is_one()) + return dd_real::_e; + + double m = std::floor(a.x[0] / dd_real::_log2.x[0] + 0.5); + dd_real r = mul_pwr2(a - dd_real::_log2 * m, inv_k); + dd_real s, t, p; + + p = sqr(r); + s = r + mul_pwr2(p, 0.5); + p *= r; + t = p * dd_real(inv_fact[0][0], inv_fact[0][1]); + int i = 0; + do { + s += t; + p *= r; + ++i; + t = p * dd_real(inv_fact[i][0], inv_fact[i][1]); + } while (std::abs(to_double(t)) > inv_k * dd_real::_eps && i < 5); + + s += t; + + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s += 1.0; + + return ldexp(s, static_cast(m)); +} + +/* Logarithm. Computes log(x) in double-double precision. + This is a natural logarithm (i.e., base e). */ +dd_real log(const dd_real &a) { + /* Strategy. The Taylor series for log converges much more + slowly than that of exp, due to the lack of the factorial + term in the denominator. Hence this routine instead tries + to determine the root of the function + + f(x) = exp(x) - a + + using Newton iteration. The iteration is given by + + x' = x - f(x)/f'(x) + = x - (1 - a * exp(-x)) + = x + a * exp(-x) - 1. + + Only one iteration is needed, since Newton's iteration + approximately doubles the number of digits per iteration. */ + + if (a.is_one()) { + return 0.0; + } + + if (a.x[0] <= 0.0) { + dd_real::error("(dd_real::log): Non-positive argument."); + return dd_real::_nan; + } + + dd_real x = std::log(a.x[0]); /* Initial approximation */ + + x = x + a * exp(-x) - 1.0; + return x; +} + +dd_real log10(const dd_real &a) { + return log(a) / dd_real::_log10; +} + +static const dd_real _pi16 = dd_real(1.963495408493620697e-01, + 7.654042494670957545e-18); + +/* Table of sin(k * pi/16) and cos(k * pi/16). */ +static const double sin_table [4][2] = { + {1.950903220161282758e-01, -7.991079068461731263e-18}, + {3.826834323650897818e-01, -1.005077269646158761e-17}, + {5.555702330196021776e-01, 4.709410940561676821e-17}, + {7.071067811865475727e-01, -4.833646656726456726e-17} +}; + +static const double cos_table [4][2] = { + {9.807852804032304306e-01, 1.854693999782500573e-17}, + {9.238795325112867385e-01, 1.764504708433667706e-17}, + {8.314696123025452357e-01, 1.407385698472802389e-18}, + {7.071067811865475727e-01, -4.833646656726456726e-17} +}; + +/* Computes sin(a) using Taylor series. + Assumes |a| <= pi/32. */ +static dd_real sin_taylor(const dd_real &a) { + const double thresh = 0.5 * std::abs(to_double(a)) * dd_real::_eps; + dd_real r, s, t, x; + + if (a.is_zero()) { + return 0.0; + } + + int i = 0; + x = -sqr(a); + s = a; + r = a; + do { + r *= x; + t = r * dd_real(inv_fact[i][0], inv_fact[i][1]); + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + return s; +} + +static dd_real cos_taylor(const dd_real &a) { + const double thresh = 0.5 * dd_real::_eps; + dd_real r, s, t, x; + + if (a.is_zero()) { + return 1.0; + } + + x = -sqr(a); + r = x; + s = 1.0 + mul_pwr2(r, 0.5); + int i = 1; + do { + r *= x; + t = r * dd_real(inv_fact[i][0], inv_fact[i][1]); + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + return s; +} + +static void sincos_taylor(const dd_real &a, + dd_real &sin_a, dd_real &cos_a) { + if (a.is_zero()) { + sin_a = 0.0; + cos_a = 1.0; + return; + } + + sin_a = sin_taylor(a); + cos_a = sqrt(1.0 - sqr(sin_a)); +} + + +dd_real sin(const dd_real &a) { + + /* Strategy. To compute sin(x), we choose integers a, b so that + + x = s + a * (pi/2) + b * (pi/16) + + and |s| <= pi/32. Using the fact that + + sin(pi/16) = 0.5 * sqrt(2 - sqrt(2 + sqrt(2))) + + we can compute sin(x) from sin(s), cos(s). This greatly + increases the convergence of the sine Taylor series. */ + + if (a.is_zero()) { + return 0.0; + } + + // approximately reduce modulo 2*pi + dd_real z = nint(a / dd_real::_2pi); + dd_real r = a - dd_real::_2pi * z; + + // approximately reduce modulo pi/2 and then modulo pi/16. + dd_real t; + double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); + t = r - dd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi16.x[0] + 0.5); + t -= _pi16 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + dd_real::error("(dd_real::sin): Cannot reduce modulo pi/2."); + return dd_real::_nan; + } + + if (abs_k > 4) { + dd_real::error("(dd_real::sin): Cannot reduce modulo pi/16."); + return dd_real::_nan; + } + + if (k == 0) { + switch (j) { + case 0: + return sin_taylor(t); + case 1: + return cos_taylor(t); + case -1: + return -cos_taylor(t); + default: + return -sin_taylor(t); + } + } + + dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); + dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); + dd_real sin_t, cos_t; + sincos_taylor(t, sin_t, cos_t); + if (j == 0) { + if (k > 0) { + r = u * sin_t + v * cos_t; + } else { + r = u * sin_t - v * cos_t; + } + } else if (j == 1) { + if (k > 0) { + r = u * cos_t - v * sin_t; + } else { + r = u * cos_t + v * sin_t; + } + } else if (j == -1) { + if (k > 0) { + r = v * sin_t - u * cos_t; + } else if (k < 0) { + r = -u * cos_t - v * sin_t; + } + } else { + if (k > 0) { + r = -u * sin_t - v * cos_t; + } else { + r = v * cos_t - u * sin_t; + } + } + + return r; +} + +dd_real cos(const dd_real &a) { + + if (a.is_zero()) { + return 1.0; + } + + // approximately reduce modulo 2*pi + dd_real z = nint(a / dd_real::_2pi); + dd_real r = a - z * dd_real::_2pi; + + // approximately reduce modulo pi/2 and then modulo pi/16 + dd_real t; + double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); + t = r - dd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi16.x[0] + 0.5); + t -= _pi16 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + dd_real::error("(dd_real::cos): Cannot reduce modulo pi/2."); + return dd_real::_nan; + } + + if (abs_k > 4) { + dd_real::error("(dd_real::cos): Cannot reduce modulo pi/16."); + return dd_real::_nan; + } + + if (k == 0) { + switch (j) { + case 0: + return cos_taylor(t); + case 1: + return -sin_taylor(t); + case -1: + return sin_taylor(t); + default: + return -cos_taylor(t); + } + } + + dd_real sin_t, cos_t; + sincos_taylor(t, sin_t, cos_t); + dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); + dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); + + if (j == 0) { + if (k > 0) { + r = u * cos_t - v * sin_t; + } else { + r = u * cos_t + v * sin_t; + } + } else if (j == 1) { + if (k > 0) { + r = - u * sin_t - v * cos_t; + } else { + r = v * cos_t - u * sin_t; + } + } else if (j == -1) { + if (k > 0) { + r = u * sin_t + v * cos_t; + } else { + r = u * sin_t - v * cos_t; + } + } else { + if (k > 0) { + r = v * sin_t - u * cos_t; + } else { + r = - u * cos_t - v * sin_t; + } + } + + return r; +} + +void sincos(const dd_real &a, dd_real &sin_a, dd_real &cos_a) { + + if (a.is_zero()) { + sin_a = 0.0; + cos_a = 1.0; + return; + } + + // approximately reduce modulo 2*pi + dd_real z = nint(a / dd_real::_2pi); + dd_real r = a - dd_real::_2pi * z; + + // approximately reduce module pi/2 and pi/16 + dd_real t; + double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); + t = r - dd_real::_pi2 * q; + int j = static_cast(q); + int abs_j = std::abs(j); + q = std::floor(t.x[0] / _pi16.x[0] + 0.5); + t -= _pi16 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (abs_j > 2) { + dd_real::error("(dd_real::sincos): Cannot reduce modulo pi/2."); + cos_a = sin_a = dd_real::_nan; + return; + } + + if (abs_k > 4) { + dd_real::error("(dd_real::sincos): Cannot reduce modulo pi/16."); + cos_a = sin_a = dd_real::_nan; + return; + } + + dd_real sin_t, cos_t; + dd_real s, c; + + sincos_taylor(t, sin_t, cos_t); + + if (abs_k == 0) { + s = sin_t; + c = cos_t; + } else { + dd_real u(cos_table[abs_k-1][0], cos_table[abs_k-1][1]); + dd_real v(sin_table[abs_k-1][0], sin_table[abs_k-1][1]); + + if (k > 0) { + s = u * sin_t + v * cos_t; + c = u * cos_t - v * sin_t; + } else { + s = u * sin_t - v * cos_t; + c = u * cos_t + v * sin_t; + } + } + + if (abs_j == 0) { + sin_a = s; + cos_a = c; + } else if (j == 1) { + sin_a = c; + cos_a = -s; + } else if (j == -1) { + sin_a = -c; + cos_a = s; + } else { + sin_a = -s; + cos_a = -c; + } + +} + +dd_real atan(const dd_real &a) { + return atan2(a, dd_real(1.0)); +} + +dd_real atan2(const dd_real &y, const dd_real &x) { + /* Strategy: Instead of using Taylor series to compute + arctan, we instead use Newton's iteration to solve + the equation + + sin(z) = y/r or cos(z) = x/r + + where r = sqrt(x^2 + y^2). + The iteration is given by + + z' = z + (y - sin(z)) / cos(z) (for equation 1) + z' = z - (x - cos(z)) / sin(z) (for equation 2) + + Here, x and y are normalized so that x^2 + y^2 = 1. + If |x| > |y|, then first iteration is used since the + denominator is larger. Otherwise, the second is used. + */ + + if (x.is_zero()) { + + if (y.is_zero()) { + /* Both x and y is zero. */ + dd_real::error("(dd_real::atan2): Both arguments zero."); + return dd_real::_nan; + } + + return (y.is_positive()) ? dd_real::_pi2 : -dd_real::_pi2; + } else if (y.is_zero()) { + return (x.is_positive()) ? dd_real(0.0) : dd_real::_pi; + } + + if (x == y) { + return (y.is_positive()) ? dd_real::_pi4 : -dd_real::_3pi4; + } + + if (x == -y) { + return (y.is_positive()) ? dd_real::_3pi4 : -dd_real::_pi4; + } + + dd_real r = sqrt(sqr(x) + sqr(y)); + dd_real xx = x / r; + dd_real yy = y / r; + + /* Compute double precision approximation to atan. */ + dd_real z = std::atan2(to_double(y), to_double(x)); + dd_real sin_z, cos_z; + + if (std::abs(xx.x[0]) > std::abs(yy.x[0])) { + /* Use Newton iteration 1. z' = z + (y - sin(z)) / cos(z) */ + sincos(z, sin_z, cos_z); + z += (yy - sin_z) / cos_z; + } else { + /* Use Newton iteration 2. z' = z - (x - cos(z)) / sin(z) */ + sincos(z, sin_z, cos_z); + z -= (xx - cos_z) / sin_z; + } + + return z; +} + +dd_real tan(const dd_real &a) { + dd_real s, c; + sincos(a, s, c); + return s/c; +} + +dd_real asin(const dd_real &a) { + dd_real abs_a = abs(a); + + if (abs_a > 1.0) { + dd_real::error("(dd_real::asin): Argument out of domain."); + return dd_real::_nan; + } + + if (abs_a.is_one()) { + return (a.is_positive()) ? dd_real::_pi2 : -dd_real::_pi2; + } + + return atan2(a, sqrt(1.0 - sqr(a))); +} + +dd_real acos(const dd_real &a) { + dd_real abs_a = abs(a); + + if (abs_a > 1.0) { + dd_real::error("(dd_real::acos): Argument out of domain."); + return dd_real::_nan; + } + + if (abs_a.is_one()) { + return (a.is_positive()) ? dd_real(0.0) : dd_real::_pi; + } + + return atan2(sqrt(1.0 - sqr(a)), a); +} + +dd_real sinh(const dd_real &a) { + if (a.is_zero()) { + return 0.0; + } + + if (abs(a) > 0.05) { + dd_real ea = exp(a); + return mul_pwr2(ea - inv(ea), 0.5); + } + + /* since a is small, using the above formula gives + a lot of cancellation. So use Taylor series. */ + dd_real s = a; + dd_real t = a; + dd_real r = sqr(t); + double m = 1.0; + double thresh = std::abs((to_double(a)) * dd_real::_eps); + + do { + m += 2.0; + t *= r; + t /= (m-1) * m; + + s += t; + } while (abs(t) > thresh); + + return s; + +} + +dd_real cosh(const dd_real &a) { + if (a.is_zero()) { + return 1.0; + } + + dd_real ea = exp(a); + return mul_pwr2(ea + inv(ea), 0.5); +} + +dd_real tanh(const dd_real &a) { + if (a.is_zero()) { + return 0.0; + } + + if (std::abs(to_double(a)) > 0.05) { + dd_real ea = exp(a); + dd_real inv_ea = inv(ea); + return (ea - inv_ea) / (ea + inv_ea); + } else { + dd_real s, c; + s = sinh(a); + c = sqrt(1.0 + sqr(s)); + return s / c; + } +} + +void sincosh(const dd_real &a, dd_real &s, dd_real &c) { + if (std::abs(to_double(a)) <= 0.05) { + s = sinh(a); + c = sqrt(1.0 + sqr(s)); + } else { + dd_real ea = exp(a); + dd_real inv_ea = inv(ea); + s = mul_pwr2(ea - inv_ea, 0.5); + c = mul_pwr2(ea + inv_ea, 0.5); + } +} + +dd_real asinh(const dd_real &a) { + return log(a + sqrt(sqr(a) + 1.0)); +} + +dd_real acosh(const dd_real &a) { + if (a < 1.0) { + dd_real::error("(dd_real::acosh): Argument out of domain."); + return dd_real::_nan; + } + + return log(a + sqrt(sqr(a) - 1.0)); +} + +dd_real atanh(const dd_real &a) { + if (abs(a) >= 1.0) { + dd_real::error("(dd_real::atanh): Argument out of domain."); + return dd_real::_nan; + } + + return mul_pwr2(log((1.0 + a) / (1.0 - a)), 0.5); +} + +QD_API dd_real fmod(const dd_real &a, const dd_real &b) { + dd_real n = aint(a / b); + return (a - b * n); +} + +QD_API dd_real ddrand() { + static const double m_const = 4.6566128730773926e-10; /* = 2^{-31} */ + double m = m_const; + dd_real r = 0.0; + double d; + + /* Strategy: Generate 31 bits at a time, using lrand48 + random number generator. Shift the bits, and reapeat + 4 times. */ + + for (int i = 0; i < 4; i++, m *= m_const) { +// d = lrand48() * m; + d = std::rand() * m; + r += d; + } + + return r; +} + +/* polyeval(c, n, x) + Evaluates the given n-th degree polynomial at x. + The polynomial is given by the array of (n+1) coefficients. */ +dd_real polyeval(const dd_real *c, int n, const dd_real &x) { + /* Just use Horner's method of polynomial evaluation. */ + dd_real r = c[n]; + + for (int i = n-1; i >= 0; i--) { + r *= x; + r += c[i]; + } + + return r; +} + +/* polyroot(c, n, x0) + Given an n-th degree polynomial, finds a root close to + the given guess x0. Note that this uses simple Newton + iteration scheme, and does not work for multiple roots. */ +QD_API dd_real polyroot(const dd_real *c, int n, + const dd_real &x0, int max_iter, double thresh) { + dd_real x = x0; + dd_real f; + dd_real *d = new dd_real[n]; + bool conv = false; + int i; + double max_c = std::abs(to_double(c[0])); + double v; + + if (thresh == 0.0) thresh = dd_real::_eps; + + /* Compute the coefficients of the derivatives. */ + for (i = 1; i <= n; i++) { + v = std::abs(to_double(c[i])); + if (v > max_c) max_c = v; + d[i-1] = c[i] * static_cast(i); + } + thresh *= max_c; + + /* Newton iteration. */ + for (i = 0; i < max_iter; i++) { + f = polyeval(c, n, x); + + if (abs(f) < thresh) { + conv = true; + break; + } + x -= (f / polyeval(d, n-1, x)); + } + delete [] d; + + if (!conv) { + dd_real::error("(dd_real::polyroot): Failed to converge."); + return dd_real::_nan; + } + + return x; +} + + +/* Constructor. Reads a double-double number from the string s + and constructs a double-double number. */ +dd_real::dd_real(const char *s) { + if (dd_real::read(s, *this)) { + dd_real::error("(dd_real::dd_real): INPUT ERROR."); + *this = dd_real::_nan; + } +} + +dd_real &dd_real::operator=(const char *s) { + if (dd_real::read(s, *this)) { + dd_real::error("(dd_real::operator=): INPUT ERROR."); + *this = dd_real::_nan; + } + return *this; +} + +/* Outputs the double-double number dd. */ +ostream &operator<<(ostream &os, const dd_real &dd) { + bool showpos = (os.flags() & ios_base::showpos) != 0; + bool uppercase = (os.flags() & ios_base::uppercase) != 0; + return os << dd.to_string((int)os.precision(), (int)os.width(), os.flags(), + showpos, uppercase, os.fill()); +} + +/* Reads in the double-double number a. */ +istream &operator>>(istream &s, dd_real &a) { + char str[255]; + s >> str; + a = dd_real(str); + return s; +} + +void dd_real::to_digits(char *s, int &expn, int precision) const { + int D = precision + 1; /* number of digits to compute */ + + dd_real r = abs(*this); + int e; /* exponent */ + int i, d; + + if (x[0] == 0.0) { + /* this == 0.0 */ + expn = 0; + for (i = 0; i < precision; i++) s[i] = '0'; + return; + } + + /* First determine the (approximate) exponent. */ + e = to_int(std::floor(std::log10(std::abs(x[0])))); + + if (e < -300) { + r *= dd_real(10.0) ^ 300; + r /= dd_real(10.0) ^ (e + 300); + } else if (e > 300) { + r = ldexp(r, -53); + r /= dd_real(10.0) ^ e; + r = ldexp(r, 53); + } else { + r /= dd_real(10.0) ^ e; + } + + /* Fix exponent if we are off by one */ + if (r >= 10.0) { + r /= 10.0; + e++; + } else if (r < 1.0) { + r *= 10.0; + e--; + } + + if (r >= 10.0 || r < 1.0) { + dd_real::error("(dd_real::to_digits): can't compute exponent."); + return; + } + + /* Extract the digits */ + for (i = 0; i < D; i++) { + d = static_cast(r.x[0]); + r -= d; + r *= 10.0; + + s[i] = static_cast(d + '0'); + } + + /* Fix out of range digits. */ + for (i = D-1; i > 0; i--) { + if (s[i] < '0') { + s[i-1]--; + s[i] += 10; + } else if (s[i] > '9') { + s[i-1]++; + s[i] -= 10; + } + } + + if (s[0] <= '0') { + dd_real::error("(dd_real::to_digits): non-positive leading digit."); + return; + } + + /* Round, handle carry */ + if (s[D-1] >= '5') { + s[D-2]++; + + i = D-2; + while (i > 0 && s[i] > '9') { + s[i] -= 10; + s[--i]++; + } + } + + /* If first digit is 10, shift everything. */ + if (s[0] > '9') { + e++; + for (i = precision; i >= 2; i--) s[i] = s[i-1]; + s[0] = '1'; + s[1] = '0'; + } + + s[precision] = 0; + expn = e; +} + +/* Writes the double-double number into the character array s of length len. + The integer d specifies how many significant digits to write. + The string s must be able to hold at least (d+8) characters. + showpos indicates whether to use the + sign, and uppercase indicates + whether the E or e is to be used for the exponent. */ +void dd_real::write(char *s, int len, int precision, + bool showpos, bool uppercase) const { + string str = to_string(precision, 0, ios_base::scientific, showpos, uppercase); + std::strncpy(s, str.c_str(), len-1); + s[len-1] = 0; +} + + +void round_string(char *s, int precision, int *offset){ + /* + Input string must be all digits or errors will occur. + */ + + int i; + int D = precision ; + + /* Round, handle carry */ + if (D>0 && s[D] >= '5') { + s[D-1]++; + + i = D-1; + while (i > 0 && s[i] > '9') { + s[i] -= 10; + s[--i]++; + } + } + + /* If first digit is 10, shift everything. */ + if (s[0] > '9') { + // e++; // don't modify exponent here + for (i = precision; i >= 1; i--) s[i+1] = s[i]; + s[0] = '1'; + s[1] = '0'; + + (*offset)++ ; // now offset needs to be increased by one + precision++ ; + } + + s[precision] = 0; // add terminator for array +} + +string dd_real::to_string(int precision, int width, ios_base::fmtflags fmt, + bool showpos, bool uppercase, char fill) const { + string s; + bool fixed = (fmt & ios_base::fixed) != 0; + bool sgn = true; + int i, e = 0; + + if (isnan()) { + s = uppercase ? "NAN" : "nan"; + sgn = false; + } else { + if (*this < 0.0) + s += '-'; + else if (showpos) + s += '+'; + else + sgn = false; + + if (isinf()) { + s += uppercase ? "INF" : "inf"; + } else if (*this == 0.0) { + /* Zero case */ + s += '0'; + if (precision > 0) { + s += '.'; + s.append(precision, '0'); + } + } else { + /* Non-zero case */ + int off = (fixed ? (1 + to_int(floor(log10(abs(*this))))) : 1); + int d = precision + off; + + int d_with_extra = d; + if(fixed) + d_with_extra = std::max(60, d); // longer than the max accuracy for DD + + // highly special case - fixed mode, precision is zero, abs(*this) < 1.0 + // without this trap a number like 0.9 printed fixed with 0 precision prints as 0 + // should be rounded to 1. + if(fixed && (precision == 0) && (abs(*this) < 1.0)){ + if(abs(*this) >= 0.5) + s += '1'; + else + s += '0'; + + return s; + } + + // handle near zero to working precision (but not exactly zero) + if (fixed && d <= 0) { + s += '0'; + if (precision > 0) { + s += '.'; + s.append(precision, '0'); + } + } else { // default + + char *t; // = new char[d+1]; + int j; + + if(fixed){ + t = new char[d_with_extra+1]; + to_digits(t, e, d_with_extra); + } + else{ + t = new char[d+1]; + to_digits(t, e, d); + } + + off = e + 1; + + if (fixed) { + // fix the string if it's been computed incorrectly + // round here in the decimal string if required + round_string(t, d, &off); + + if (off > 0) { + for (i = 0; i < off; i++) s += t[i]; + if (precision > 0) { + s += '.'; + for (j = 0; j < precision; j++, i++) s += t[i]; + } + } else { + s += "0."; + if (off < 0) s.append(-off, '0'); + for (i = 0; i < d; i++) s += t[i]; + } + } else { + s += t[0]; + if (precision > 0) s += '.'; + + for (i = 1; i <= precision; i++) + s += t[i]; + + } + delete [] t; + } + } + + // trap for improper offset with large values + // without this trap, output of values of the for 10^j - 1 fail for j > 28 + // and are output with the point in the wrong place, leading to a dramatically off value + if(fixed && (precision > 0)){ + // make sure that the value isn't dramatically larger + double from_string = atof(s.c_str()); + + // if this ratio is large, then we've got problems + if( fabs( from_string / this->x[0] ) > 3.0 ){ + + // loop on the string, find the point, move it up one + // don't act on the first character + for(i=1; i < (int)s.length(); i++){ + if(s[i] == '.'){ + s[i] = s[i-1] ; + s[i-1] = '.' ; + break; + } + } + + from_string = atof(s.c_str()); + // if this ratio is large, then the string has not been fixed + if( fabs( from_string / this->x[0] ) > 3.0 ){ + dd_real::error("Re-rounding unsuccessful in large number fixed point trap.") ; + } + } + } + + + if (!fixed && !isinf()) { + /* Fill in exponent part */ + s += uppercase ? 'E' : 'e'; + append_expn(s, e); + } + } + + /* Fill in the blanks */ + int len = s.length(); + if (len < width) { + int delta = width - len; + if (fmt & ios_base::internal) { + if (sgn) + s.insert(static_cast(1), delta, fill); + else + s.insert(static_cast(0), delta, fill); + } else if (fmt & ios_base::left) { + s.append(delta, fill); + } else { + s.insert(static_cast(0), delta, fill); + } + } + + return s; +} + +/* Reads in a double-double number from the string s. */ +int dd_real::read(const char *s, dd_real &a) { + const char *p = s; + char ch; + int sign = 0; + int point = -1; + int nd = 0; + int e = 0; + bool done = false; + dd_real r = 0.0; + int nread; + + /* Skip any leading spaces */ + while (*p == ' ') + p++; + + while (!done && (ch = *p) != '\0') { + if (ch >= '0' && ch <= '9') { + int d = ch - '0'; + r *= 10.0; + r += static_cast(d); + nd++; + } else { + + switch (ch) { + + case '.': + if (point >= 0) + return -1; + point = nd; + break; + + case '-': + case '+': + if (sign != 0 || nd > 0) + return -1; + sign = (ch == '-') ? -1 : 1; + break; + + case 'E': + case 'e': + nread = std::sscanf(p+1, "%d", &e); + done = true; + if (nread != 1) + return -1; + break; + + default: + return -1; + } + } + + p++; + } + + if (point >= 0) { + e -= (nd - point); + } + + if (e != 0) { + r *= (dd_real(10.0) ^ e); + } + + a = (sign == -1) ? -r : r; + return 0; +} + +/* Debugging routines */ +void dd_real::dump(const string &name, std::ostream &os) const { + std::ios_base::fmtflags old_flags = os.flags(); + std::streamsize old_prec = os.precision(19); + os << std::scientific; + + if (name.length() > 0) os << name << " = "; + os << "[ " << setw(27) << x[0] << ", " << setw(27) << x[1] << " ]" << endl; + + os.precision(old_prec); + os.flags(old_flags); +} + +void dd_real::dump_bits(const string &name, std::ostream &os) const { + string::size_type len = name.length(); + if (len > 0) { + os << name << " = "; + len +=3; + } + os << "[ "; + len += 2; + print_double_info(os, x[0]); + os << endl; + for (string::size_type i = 0; i < len; i++) os << ' '; + print_double_info(os, x[1]); + os << " ]" << endl; +} + +dd_real dd_real::debug_rand() { + + if (std::rand() % 2 == 0) + return ddrand(); + + int expn = 0; + dd_real a = 0.0; + double d; + for (int i = 0; i < 2; i++) { + d = std::ldexp(static_cast(std::rand()) / RAND_MAX, -expn); + a += d; + expn = expn + 54 + std::rand() % 200; + } + return a; +} diff --git a/src/external/PackedCSparse/qd/dd_real.h b/src/external/PackedCSparse/qd/dd_real.h new file mode 100644 index 00000000..e16438aa --- /dev/null +++ b/src/external/PackedCSparse/qd/dd_real.h @@ -0,0 +1,293 @@ +/* + * include/dd_real.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + * + * Double-double precision (>= 106-bit significand) floating point + * arithmetic package based on David Bailey's Fortran-90 double-double + * package, with some changes. See + * + * http://www.nersc.gov/~dhbailey/mpdist/mpdist.html + * + * for the original Fortran-90 version. + * + * Overall structure is similar to that of Keith Brigg's C++ double-double + * package. See + * + * http://www-epidem.plansci.cam.ac.uk/~kbriggs/doubledouble.html + * + * for more details. In particular, the fix for x86 computers is borrowed + * from his code. + * + * Yozo Hida + */ + +#ifndef _QD_DD_REAL_H +#define _QD_DD_REAL_H + +#include +#include +#include +#include +#include "qd_config.h" +#include "fpu.h" + +// Some compilers define isnan, isfinite, and isinf as macros, even for +// C++ codes, which cause havoc when overloading these functions. We undef +// them here. +#ifdef isnan +#undef isnan +#endif + +#ifdef isfinite +#undef isfinite +#endif + +#ifdef isinf +#undef isinf +#endif + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +struct QD_API dd_real { + double x[2]; + + dd_real(double hi, double lo) { x[0] = hi; x[1] = lo; } + dd_real() {x[0] = 0.0; x[1] = 0.0; } + dd_real(double h) { x[0] = h; x[1] = 0.0; } + dd_real(int h) { + x[0] = (static_cast(h)); + x[1] = 0.0; + } + + dd_real (const char *s); + explicit dd_real (const double *d) { + x[0] = d[0]; x[1] = d[1]; + } + + static void error(const char *msg); + + double _hi() const { return x[0]; } + double _lo() const { return x[1]; } + + static const dd_real _2pi; + static const dd_real _pi; + static const dd_real _3pi4; + static const dd_real _pi2; + static const dd_real _pi4; + static const dd_real _e; + static const dd_real _log2; + static const dd_real _log10; + static const dd_real _nan; + static const dd_real _inf; + + static const double _eps; + static const double _min_normalized; + static const dd_real _max; + static const dd_real _safe_max; + static const int _ndigits; + + bool isnan() const { return QD_ISNAN(x[0]) || QD_ISNAN(x[1]); } + bool isfinite() const { return QD_ISFINITE(x[0]); } + bool isinf() const { return QD_ISINF(x[0]); } + + static dd_real add(double a, double b); + static dd_real ieee_add(const dd_real &a, const dd_real &b); + static dd_real sloppy_add(const dd_real &a, const dd_real &b); + + dd_real &operator+=(double a); + dd_real &operator+=(const dd_real &a); + + static dd_real sub(double a, double b); + + dd_real &operator-=(double a); + dd_real &operator-=(const dd_real &a); + + dd_real operator-() const; + + static dd_real mul(double a, double b); + + dd_real &operator*=(double a); + dd_real &operator*=(const dd_real &a); + + static dd_real div(double a, double b); + static dd_real sloppy_div(const dd_real &a, const dd_real &b); + static dd_real accurate_div(const dd_real &a, const dd_real &b); + + dd_real &operator/=(double a); + dd_real &operator/=(const dd_real &a); + + dd_real &operator=(double a); + dd_real &operator=(const char *s); + + dd_real operator^(int n); + static dd_real sqr(double d); + + static dd_real sqrt(double a); + + bool is_zero() const; + bool is_one() const; + bool is_positive() const; + bool is_negative() const; + + explicit operator bool() const; // new + explicit operator double() const; // new + + static dd_real rand(void); + + void to_digits(char *s, int &expn, int precision = _ndigits) const; + void write(char *s, int len, int precision = _ndigits, + bool showpos = false, bool uppercase = false) const; + std::string to_string(int precision = _ndigits, int width = 0, + std::ios_base::fmtflags fmt = static_cast(0), + bool showpos = false, bool uppercase = false, char fill = ' ') const; + int read(const char *s, dd_real &a); + + /* Debugging Methods */ + void dump(const std::string &name, std::ostream &os = std::cerr) const; + void dump_bits(const std::string &name, + std::ostream &os = std::cerr) const; + + static dd_real debug_rand(); +}; + + +namespace std { + template <> + class numeric_limits : public numeric_limits { + public: + inline static double epsilon() { return dd_real::_eps; } + inline static dd_real max() { return dd_real::_max; } + inline static dd_real safe_max() { return dd_real::_safe_max; } + inline static double min() { return dd_real::_min_normalized; } + static const int digits = 104; + static const int digits10 = 31; + }; +} + +QD_API dd_real ddrand(void); +QD_API dd_real sqrt(const dd_real &a); + +QD_API dd_real polyeval(const dd_real *c, int n, const dd_real &x); +QD_API dd_real polyroot(const dd_real *c, int n, + const dd_real &x0, int max_iter = 32, double thresh = 0.0); + +QD_API inline bool isnan(const dd_real &a) { return a.isnan(); } +QD_API inline bool isfinite(const dd_real &a) { return a.isfinite(); } +QD_API inline bool isinf(const dd_real &a) { return a.isinf(); } + +/* Computes dd * d where d is known to be a power of 2. */ +QD_API dd_real mul_pwr2(const dd_real &dd, double d); + +QD_API dd_real operator+(const dd_real &a, double b); +QD_API dd_real operator+(double a, const dd_real &b); +QD_API dd_real operator+(const dd_real &a, const dd_real &b); + +QD_API dd_real operator-(const dd_real &a, double b); +QD_API dd_real operator-(double a, const dd_real &b); +QD_API dd_real operator-(const dd_real &a, const dd_real &b); + +QD_API dd_real operator*(const dd_real &a, double b); +QD_API dd_real operator*(double a, const dd_real &b); +QD_API dd_real operator*(const dd_real &a, const dd_real &b); + +QD_API dd_real operator/(const dd_real &a, double b); +QD_API dd_real operator/(double a, const dd_real &b); +QD_API dd_real operator/(const dd_real &a, const dd_real &b); + +QD_API dd_real inv(const dd_real &a); + +QD_API dd_real rem(const dd_real &a, const dd_real &b); +QD_API dd_real drem(const dd_real &a, const dd_real &b); +QD_API dd_real divrem(const dd_real &a, const dd_real &b, dd_real &r); + +QD_API dd_real pow(const dd_real &a, int n); +QD_API dd_real pow(const dd_real &a, const dd_real &b); +QD_API dd_real npwr(const dd_real &a, int n); +QD_API dd_real sqr(const dd_real &a); + +QD_API dd_real sqrt(const dd_real &a); +QD_API dd_real nroot(const dd_real &a, int n); + +QD_API bool operator==(const dd_real &a, double b); +QD_API bool operator==(double a, const dd_real &b); +QD_API bool operator==(const dd_real &a, const dd_real &b); + +QD_API bool operator<=(const dd_real &a, double b); +QD_API bool operator<=(double a, const dd_real &b); +QD_API bool operator<=(const dd_real &a, const dd_real &b); + +QD_API bool operator>=(const dd_real &a, double b); +QD_API bool operator>=(double a, const dd_real &b); +QD_API bool operator>=(const dd_real &a, const dd_real &b); + +QD_API bool operator<(const dd_real &a, double b); +QD_API bool operator<(double a, const dd_real &b); +QD_API bool operator<(const dd_real &a, const dd_real &b); + +QD_API bool operator>(const dd_real &a, double b); +QD_API bool operator>(double a, const dd_real &b); +QD_API bool operator>(const dd_real &a, const dd_real &b); + +QD_API bool operator!=(const dd_real &a, double b); +QD_API bool operator!=(double a, const dd_real &b); +QD_API bool operator!=(const dd_real &a, const dd_real &b); + +QD_API dd_real nint(const dd_real &a); +QD_API dd_real floor(const dd_real &a); +QD_API dd_real ceil(const dd_real &a); +QD_API dd_real aint(const dd_real &a); + +QD_API dd_real ddrand(void); + +double to_double(const dd_real &a); +int to_int(const dd_real &a); + +QD_API dd_real exp(const dd_real &a); +QD_API dd_real ldexp(const dd_real &a, int exp); +QD_API dd_real log(const dd_real &a); +QD_API dd_real log10(const dd_real &a); + +QD_API dd_real sin(const dd_real &a); +QD_API dd_real cos(const dd_real &a); +QD_API dd_real tan(const dd_real &a); +QD_API void sincos(const dd_real &a, dd_real &sin_a, dd_real &cos_a); + +QD_API dd_real asin(const dd_real &a); +QD_API dd_real acos(const dd_real &a); +QD_API dd_real atan(const dd_real &a); +QD_API dd_real atan2(const dd_real &y, const dd_real &x); + +QD_API dd_real sinh(const dd_real &a); +QD_API dd_real cosh(const dd_real &a); +QD_API dd_real tanh(const dd_real &a); +QD_API void sincosh(const dd_real &a, + dd_real &sinh_a, dd_real &cosh_a); + +QD_API dd_real asinh(const dd_real &a); +QD_API dd_real acosh(const dd_real &a); +QD_API dd_real atanh(const dd_real &a); + +QD_API dd_real fabs(const dd_real &a); +QD_API dd_real abs(const dd_real &a); /* same as fabs */ + +QD_API dd_real fmod(const dd_real &a, const dd_real &b); + +QD_API std::ostream& operator<<(std::ostream &s, const dd_real &a); +QD_API std::istream& operator>>(std::istream &s, dd_real &a); +#ifdef QD_INLINE +#include "dd_inline.h" +#endif + +#endif /* _QD_DD_REAL_H */ + diff --git a/src/external/PackedCSparse/qd/fpu.cc b/src/external/PackedCSparse/qd/fpu.cc new file mode 100644 index 00000000..96ddc488 --- /dev/null +++ b/src/external/PackedCSparse/qd/fpu.cc @@ -0,0 +1,124 @@ +/* + * src/fpu.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains functions to set and restore the round-to-double flag in the + * control word of a x86 FPU. + */ + +#include "qd_config.h" +#include "fpu.h" + +#ifdef X86 +#ifdef _WIN32 +#include +#else + +#ifdef HAVE_FPU_CONTROL_H +#include +#endif + +#ifndef _FPU_GETCW +#define _FPU_GETCW(x) asm volatile ("fnstcw %0":"=m" (x)); +#endif + +#ifndef _FPU_SETCW +#define _FPU_SETCW(x) asm volatile ("fldcw %0": :"m" (x)); +#endif + +#ifndef _FPU_EXTENDED +#define _FPU_EXTENDED 0x0300 +#endif + +#ifndef _FPU_DOUBLE +#define _FPU_DOUBLE 0x0200 +#endif + +#endif +#endif /* X86 */ + +extern "C" { + +void fpu_fix_start(unsigned int *old_cw) { +#ifdef X86 +#ifdef _WIN32 +#ifdef __BORLANDC__ + /* Win 32 Borland C */ + unsigned short cw = _control87(0, 0); + _control87(0x0200, 0x0300); + if (old_cw) { + *old_cw = cw; + } +#else + /* Win 32 MSVC */ + unsigned int cw = _control87(0, 0); + _control87(0x00010000, 0x00030000); + if (old_cw) { + *old_cw = cw; + } +#endif +#else + /* Linux */ + volatile unsigned short cw, new_cw; + _FPU_GETCW(cw); + + new_cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE; + _FPU_SETCW(new_cw); + + if (old_cw) { + *old_cw = cw; + } +#endif +#endif +} + +void fpu_fix_end(unsigned int *old_cw) { +#ifdef X86 +#ifdef _WIN32 + +#ifdef __BORLANDC__ + /* Win 32 Borland C */ + if (old_cw) { + unsigned short cw = (unsigned short) *old_cw; + _control87(cw, 0xFFFF); + } +#else + /* Win 32 MSVC */ + if (old_cw) { + _control87(*old_cw, 0xFFFFFFFF); + } +#endif + +#else + /* Linux */ + if (old_cw) { + int cw; + cw = *old_cw; + _FPU_SETCW(cw); + } +#endif +#endif +} + +#ifdef HAVE_FORTRAN + +#define f_fpu_fix_start FC_FUNC_(f_fpu_fix_start, F_FPU_FIX_START) +#define f_fpu_fix_end FC_FUNC_(f_fpu_fix_end, F_FPU_FIX_END) + +void f_fpu_fix_start(unsigned int *old_cw) { + fpu_fix_start(old_cw); +} + +void f_fpu_fix_end(unsigned int *old_cw) { + fpu_fix_end(old_cw); +} + +#endif + +} + diff --git a/src/external/PackedCSparse/qd/fpu.h b/src/external/PackedCSparse/qd/fpu.h new file mode 100644 index 00000000..35eab18c --- /dev/null +++ b/src/external/PackedCSparse/qd/fpu.h @@ -0,0 +1,39 @@ +/* + * include/fpu.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2001 + * + * Contains functions to set and restore the round-to-double flag in the + * control word of a x86 FPU. The algorithms in the double-double and + * quad-double package does not function with the extended mode found in + * these FPU. + */ +#ifndef _QD_FPU_H +#define _QD_FPU_H + +#include "qd_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Set the round-to-double flag, and save the old control word in old_cw. + * If old_cw is NULL, the old control word is not saved. + */ +QD_API void fpu_fix_start(unsigned int *old_cw); + +/* + * Restore the control word. + */ +QD_API void fpu_fix_end(unsigned int *old_cw); + +#ifdef __cplusplus +} +#endif + +#endif /* _QD_FPU_H */ diff --git a/src/external/PackedCSparse/qd/inline.h b/src/external/PackedCSparse/qd/inline.h new file mode 100644 index 00000000..52425545 --- /dev/null +++ b/src/external/PackedCSparse/qd/inline.h @@ -0,0 +1,143 @@ +/* + * include/inline.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * This file contains the basic functions used both by double-double + * and quad-double package. These are declared as inline functions as + * they are the smallest building blocks of the double-double and + * quad-double arithmetic. + */ +#ifndef _QD_INLINE_H +#define _QD_INLINE_H + +#define _QD_SPLITTER 134217729.0 // = 2^27 + 1 +#define _QD_SPLIT_THRESH 6.69692879491417e+299 // = 2^996 + +#ifdef QD_VACPP_BUILTINS_H +/* For VisualAge C++ __fmadd */ +#include +#endif + +#include +#include + +namespace qd { + +static const double _d_nan = std::numeric_limits::quiet_NaN(); +static const double _d_inf = std::numeric_limits::infinity(); + +/*********** Basic Functions ************/ +/* Computes fl(a+b) and err(a+b). Assumes |a| >= |b|. */ +inline double quick_two_sum(double a, double b, double &err) { + double s = a + b; + err = b - (s - a); + return s; +} + +/* Computes fl(a-b) and err(a-b). Assumes |a| >= |b| */ +inline double quick_two_diff(double a, double b, double &err) { + double s = a - b; + err = (a - s) - b; + return s; +} + +/* Computes fl(a+b) and err(a+b). */ +inline double two_sum(double a, double b, double &err) { + double s = a + b; + double bb = s - a; + err = (a - (s - bb)) + (b - bb); + return s; +} + +/* Computes fl(a-b) and err(a-b). */ +inline double two_diff(double a, double b, double &err) { + double s = a - b; + double bb = s - a; + err = (a - (s - bb)) - (b + bb); + return s; +} + +#ifndef QD_FMS +/* Computes high word and lo word of a */ +inline void split(double a, double &hi, double &lo) { + double temp; + if (a > _QD_SPLIT_THRESH || a < -_QD_SPLIT_THRESH) { + a *= 3.7252902984619140625e-09; // 2^-28 + temp = _QD_SPLITTER * a; + hi = temp - (temp - a); + lo = a - hi; + hi *= 268435456.0; // 2^28 + lo *= 268435456.0; // 2^28 + } else { + temp = _QD_SPLITTER * a; + hi = temp - (temp - a); + lo = a - hi; + } +} +#endif + +/* Computes fl(a*b) and err(a*b). */ +inline double two_prod(double a, double b, double &err) { +#ifdef QD_FMS + double p = a * b; + err = QD_FMS(a, b, p); + return p; +#else + double a_hi, a_lo, b_hi, b_lo; + double p = a * b; + split(a, a_hi, a_lo); + split(b, b_hi, b_lo); + err = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo; + return p; +#endif +} + +/* Computes fl(a*a) and err(a*a). Faster than the above method. */ +inline double two_sqr(double a, double &err) { +#ifdef QD_FMS + double p = a * a; + err = QD_FMS(a, a, p); + return p; +#else + double hi, lo; + double q = a * a; + split(a, hi, lo); + err = ((hi * hi - q) + 2.0 * hi * lo) + lo * lo; + return q; +#endif +} + +/* Computes the nearest integer to d. */ +inline double nint(double d) { + if (d == std::floor(d)) + return d; + return std::floor(d + 0.5); +} + +/* Computes the truncated integer. */ +inline double aint(double d) { + return (d >= 0.0) ? std::floor(d) : std::ceil(d); +} + +/* These are provided to give consistent + interface for double with double-double and quad-double. */ +inline void sincosh(double t, double &sinh_t, double &cosh_t) { + sinh_t = std::sinh(t); + cosh_t = std::cosh(t); +} + +inline double sqr(double t) { + return t * t; +} + +inline double to_double(double a) { return a; } +inline int to_int(double a) { return static_cast(a); } + +} + +#endif /* _QD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/qd.pdf b/src/external/PackedCSparse/qd/qd.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9525b8cac490f4c3229bc7074e37075cebe7eae0 GIT binary patch literal 194502 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 SmE72tYykD$gkxvSUpTr@w^W=dn^OhAde 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 +#include + +/* include/qd/qd_config.h. Generated from qd_config.h.in by configure. */ +#ifndef _QD_QD_CONFIG_H +#define _QD_QD_CONFIG_H 1 + +#ifndef QD_API +#define QD_API /**/ +#endif + +/* Set to 1 if using VisualAge C++ compiler for __fmadd builtin. */ +#ifndef QD_VACPP_BUILTINS_H +/* #undef QD_VACPP_BUILTINS_H */ +#endif + +/* If fused multiply-add is available, define to correct macro for + using it. It is invoked as QD_FMA(a, b, c) to compute fl(a * b + c). + If correctly rounded multiply-add is not available (or if unsure), + keep it undefined.*/ +#ifndef QD_FMA +/* #undef QD_FMA */ +#endif + +/* If fused multiply-subtract is available, define to correct macro for + using it. It is invoked as QD_FMS(a, b, c) to compute fl(a * b - c). + If correctly rounded multiply-add is not available (or if unsure), + keep it undefined.*/ +#ifndef QD_FMS +#define QD_FMS(a, b, c) std::fma(a,b,-c) +/* #undef QD_FMS */ +#endif + +/* Set the following to 1 to define commonly used function + to be inlined. This should be set to 1 unless the compiler + does not support the "inline" keyword, or if building for + debugging purposes. */ +#ifndef QD_INLINE +#define QD_INLINE 1 +#endif + +/* Set the following to 1 to use ANSI C++ standard header files + such as cmath, iostream, etc. If set to zero, it will try to + include math.h, iostream.h, etc, instead. */ +#ifndef QD_HAVE_STD +#define QD_HAVE_STD 1 +#endif + +/* Set the following to 1 to make the addition and subtraction + to satisfy the IEEE-style error bound + + fl(a + b) = (1 + d) * (a + b) + + where |d| <= eps. If set to 0, the addition and subtraction + will satisfy the weaker Cray-style error bound + + fl(a + b) = (1 + d1) * a + (1 + d2) * b + + where |d1| <= eps and |d2| eps. */ +#ifndef QD_IEEE_ADD +/* #undef QD_IEEE_ADD */ +#endif + +/* Set the following to 1 to use slightly inaccurate but faster + version of multiplication. */ +#ifndef QD_SLOPPY_MUL +#define QD_SLOPPY_MUL 1 +#endif + +/* Set the following to 1 to use slightly inaccurate but faster + version of division. */ +#ifndef QD_SLOPPY_DIV +#define QD_SLOPPY_DIV 1 +#endif + +/* Define this macro to be the isfinite(x) function. */ +#ifndef QD_ISFINITE +#define QD_ISFINITE(x) std::isfinite(x) +#endif + +/* Define this macro to be the isinf(x) function. */ +#ifndef QD_ISINF +#define QD_ISINF(x) std::isinf(x) +#endif + +/* Define this macro to be the isnan(x) function. */ +#ifndef QD_ISNAN +#define QD_ISNAN(x) std::isnan(x) +#endif + + +#endif /* _QD_QD_CONFIG_H */ diff --git a/src/external/PackedCSparse/qd/qd_const.cc b/src/external/PackedCSparse/qd/qd_const.cc new file mode 100644 index 00000000..6f4e01d2 --- /dev/null +++ b/src/external/PackedCSparse/qd/qd_const.cc @@ -0,0 +1,62 @@ +/* + * src/qd_const.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Defines constants used in quad-double package. + */ +#include "qd_config.h" +#include "qd_real.h" + +/* Some useful constants. */ +const qd_real qd_real::_2pi = qd_real(6.283185307179586232e+00, + 2.449293598294706414e-16, + -5.989539619436679332e-33, + 2.224908441726730563e-49); +const qd_real qd_real::_pi = qd_real(3.141592653589793116e+00, + 1.224646799147353207e-16, + -2.994769809718339666e-33, + 1.112454220863365282e-49); +const qd_real qd_real::_pi2 = qd_real(1.570796326794896558e+00, + 6.123233995736766036e-17, + -1.497384904859169833e-33, + 5.562271104316826408e-50); +const qd_real qd_real::_pi4 = qd_real(7.853981633974482790e-01, + 3.061616997868383018e-17, + -7.486924524295849165e-34, + 2.781135552158413204e-50); +const qd_real qd_real::_3pi4 = qd_real(2.356194490192344837e+00, + 9.1848509936051484375e-17, + 3.9168984647504003225e-33, + -2.5867981632704860386e-49); +const qd_real qd_real::_e = qd_real(2.718281828459045091e+00, + 1.445646891729250158e-16, + -2.127717108038176765e-33, + 1.515630159841218954e-49); +const qd_real qd_real::_log2 = qd_real(6.931471805599452862e-01, + 2.319046813846299558e-17, + 5.707708438416212066e-34, + -3.582432210601811423e-50); +const qd_real qd_real::_log10 = qd_real(2.302585092994045901e+00, + -2.170756223382249351e-16, + -9.984262454465776570e-33, + -4.023357454450206379e-49); +const qd_real qd_real::_nan = qd_real(qd::_d_nan, qd::_d_nan, + qd::_d_nan, qd::_d_nan); +const qd_real qd_real::_inf = qd_real(qd::_d_inf, qd::_d_inf, + qd::_d_inf, qd::_d_inf); + +const double qd_real::_eps = 1.21543267145725e-63; // = 2^-209 +const double qd_real::_min_normalized = 1.6259745436952323e-260; // = 2^(-1022 + 3*53) +const qd_real qd_real::_max = qd_real( + 1.79769313486231570815e+308, 9.97920154767359795037e+291, + 5.53956966280111259858e+275, 3.07507889307840487279e+259); +const qd_real qd_real::_safe_max = qd_real( + 1.7976931080746007281e+308, 9.97920154767359795037e+291, + 5.53956966280111259858e+275, 3.07507889307840487279e+259); +const int qd_real::_ndigits = 62; + diff --git a/src/external/PackedCSparse/qd/qd_inline.h b/src/external/PackedCSparse/qd/qd_inline.h new file mode 100644 index 00000000..89ba275b --- /dev/null +++ b/src/external/PackedCSparse/qd/qd_inline.h @@ -0,0 +1,1047 @@ +/* + * include/qd_inline.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Contains small functions (suitable for inlining) in the quad-double + * arithmetic package. + */ +#ifndef _QD_QD_INLINE_H +#define _QD_QD_INLINE_H + +#include +#include "inline.h" + +#ifndef QD_INLINE +#define inline +#endif + +/********** Constructors **********/ +inline qd_real::qd_real(double x0, double x1, double x2, double x3) { + x[0] = x0; + x[1] = x1; + x[2] = x2; + x[3] = x3; +} + +inline qd_real::qd_real(const double *xx) { + x[0] = xx[0]; + x[1] = xx[1]; + x[2] = xx[2]; + x[3] = xx[3]; +} + +inline qd_real::qd_real(double x0) { + x[0] = x0; + x[1] = x[2] = x[3] = 0.0; +} + +inline qd_real::qd_real() { + x[0] = 0.0; + x[1] = 0.0; + x[2] = 0.0; + x[3] = 0.0; +} + +inline qd_real::qd_real(const dd_real &a) { + x[0] = a._hi(); + x[1] = a._lo(); + x[2] = x[3] = 0.0; +} + +inline qd_real::qd_real(int i) { + x[0] = static_cast(i); + x[1] = x[2] = x[3] = 0.0; +} + +/********** Accessors **********/ +inline double qd_real::operator[](int i) const { + return x[i]; +} + +inline double &qd_real::operator[](int i) { + return x[i]; +} + +inline bool qd_real::isnan() const { + return QD_ISNAN(x[0]) || QD_ISNAN(x[1]) || QD_ISNAN(x[2]) || QD_ISNAN(x[3]); +} + +/********** Renormalization **********/ +namespace qd { +inline void quick_renorm(double &c0, double &c1, + double &c2, double &c3, double &c4) { + double t0, t1, t2, t3; + double s; + s = qd::quick_two_sum(c3, c4, t3); + s = qd::quick_two_sum(c2, s , t2); + s = qd::quick_two_sum(c1, s , t1); + c0 = qd::quick_two_sum(c0, s , t0); + + s = qd::quick_two_sum(t2, t3, t2); + s = qd::quick_two_sum(t1, s , t1); + c1 = qd::quick_two_sum(t0, s , t0); + + s = qd::quick_two_sum(t1, t2, t1); + c2 = qd::quick_two_sum(t0, s , t0); + + c3 = t0 + t1; +} + +inline void renorm(double &c0, double &c1, + double &c2, double &c3) { + double s0, s1, s2 = 0.0, s3 = 0.0; + + if (QD_ISINF(c0)) return; + + s0 = qd::quick_two_sum(c2, c3, c3); + s0 = qd::quick_two_sum(c1, s0, c2); + c0 = qd::quick_two_sum(c0, s0, c1); + + s0 = c0; + s1 = c1; + if (s1 != 0.0) { + s1 = qd::quick_two_sum(s1, c2, s2); + if (s2 != 0.0) + s2 = qd::quick_two_sum(s2, c3, s3); + else + s1 = qd::quick_two_sum(s1, c3, s2); + } else { + s0 = qd::quick_two_sum(s0, c2, s1); + if (s1 != 0.0) + s1 = qd::quick_two_sum(s1, c3, s2); + else + s0 = qd::quick_two_sum(s0, c3, s1); + } + + c0 = s0; + c1 = s1; + c2 = s2; + c3 = s3; +} + +inline void renorm(double &c0, double &c1, + double &c2, double &c3, double &c4) { + double s0, s1, s2 = 0.0, s3 = 0.0; + + if (QD_ISINF(c0)) return; + + s0 = qd::quick_two_sum(c3, c4, c4); + s0 = qd::quick_two_sum(c2, s0, c3); + s0 = qd::quick_two_sum(c1, s0, c2); + c0 = qd::quick_two_sum(c0, s0, c1); + + s0 = c0; + s1 = c1; + + if (s1 != 0.0) { + s1 = qd::quick_two_sum(s1, c2, s2); + if (s2 != 0.0) { + s2 = qd::quick_two_sum(s2, c3, s3); + if (s3 != 0.0) + s3 += c4; + else + s2 = qd::quick_two_sum(s2, c4, s3); + } else { + s1 = qd::quick_two_sum(s1, c3, s2); + if (s2 != 0.0) + s2 = qd::quick_two_sum(s2, c4, s3); + else + s1 = qd::quick_two_sum(s1, c4, s2); + } + } else { + s0 = qd::quick_two_sum(s0, c2, s1); + if (s1 != 0.0) { + s1 = qd::quick_two_sum(s1, c3, s2); + if (s2 != 0.0) + s2 = qd::quick_two_sum(s2, c4, s3); + else + s1 = qd::quick_two_sum(s1, c4, s2); + } else { + s0 = qd::quick_two_sum(s0, c3, s1); + if (s1 != 0.0) + s1 = qd::quick_two_sum(s1, c4, s2); + else + s0 = qd::quick_two_sum(s0, c4, s1); + } + } + + c0 = s0; + c1 = s1; + c2 = s2; + c3 = s3; +} +} + +inline void qd_real::renorm() { + qd::renorm(x[0], x[1], x[2], x[3]); +} + +inline void qd_real::renorm(double &e) { + qd::renorm(x[0], x[1], x[2], x[3], e); +} + + +/********** Additions ************/ +namespace qd { + +inline void three_sum(double &a, double &b, double &c) { + double t1, t2, t3; + t1 = qd::two_sum(a, b, t2); + a = qd::two_sum(c, t1, t3); + b = qd::two_sum(t2, t3, c); +} + +inline void three_sum2(double &a, double &b, double &c) { + double t1, t2, t3; + t1 = qd::two_sum(a, b, t2); + a = qd::two_sum(c, t1, t3); + b = t2 + t3; +} + +} + +/* quad-double + double */ +inline qd_real operator+(const qd_real &a, double b) { + double c0, c1, c2, c3; + double e; + + c0 = qd::two_sum(a[0], b, e); + c1 = qd::two_sum(a[1], e, e); + c2 = qd::two_sum(a[2], e, e); + c3 = qd::two_sum(a[3], e, e); + + qd::renorm(c0, c1, c2, c3, e); + + return qd_real(c0, c1, c2, c3); +} + +/* quad-double + double-double */ +inline qd_real operator+(const qd_real &a, const dd_real &b) { + + double s0, s1, s2, s3; + double t0, t1; + + s0 = qd::two_sum(a[0], b._hi(), t0); + s1 = qd::two_sum(a[1], b._lo(), t1); + + s1 = qd::two_sum(s1, t0, t0); + + s2 = a[2]; + qd::three_sum(s2, t0, t1); + + s3 = qd::two_sum(t0, a[3], t0); + t0 += t1; + + qd::renorm(s0, s1, s2, s3, t0); + return qd_real(s0, s1, s2, s3); +} + + +/* double + quad-double */ +inline qd_real operator+(double a, const qd_real &b) { + return (b + a); +} + +/* double-double + quad-double */ +inline qd_real operator+(const dd_real &a, const qd_real &b) { + return (b + a); +} + +namespace qd { + +/* s = quick_three_accum(a, b, c) adds c to the dd-pair (a, b). + * If the result does not fit in two doubles, then the sum is + * output into s and (a,b) contains the remainder. Otherwise + * s is zero and (a,b) contains the sum. */ +inline double quick_three_accum(double &a, double &b, double c) { + double s; + bool za, zb; + + s = qd::two_sum(b, c, b); + s = qd::two_sum(a, s, a); + + za = (a != 0.0); + zb = (b != 0.0); + + if (za && zb) + return s; + + if (!zb) { + b = a; + a = s; + } else { + a = s; + } + + return 0.0; +} + +} + +inline qd_real qd_real::ieee_add(const qd_real &a, const qd_real &b) { + int i, j, k; + double s, t; + double u, v; /* double-length accumulator */ + double x[4] = {0.0, 0.0, 0.0, 0.0}; + + i = j = k = 0; + if (std::abs(a[i]) > std::abs(b[j])) + u = a[i++]; + else + u = b[j++]; + if (std::abs(a[i]) > std::abs(b[j])) + v = a[i++]; + else + v = b[j++]; + + u = qd::quick_two_sum(u, v, v); + + while (k < 4) { + if (i >= 4 && j >= 4) { + x[k] = u; + if (k < 3) + x[++k] = v; + break; + } + + if (i >= 4) + t = b[j++]; + else if (j >= 4) + t = a[i++]; + else if (std::abs(a[i]) > std::abs(b[j])) { + t = a[i++]; + } else + t = b[j++]; + + s = qd::quick_three_accum(u, v, t); + + if (s != 0.0) { + x[k++] = s; + } + } + + /* add the rest. */ + for (k = i; k < 4; k++) + x[3] += a[k]; + for (k = j; k < 4; k++) + x[3] += b[k]; + + qd::renorm(x[0], x[1], x[2], x[3]); + return qd_real(x[0], x[1], x[2], x[3]); +} + +inline qd_real qd_real::sloppy_add(const qd_real &a, const qd_real &b) { + /* + double s0, s1, s2, s3; + double t0, t1, t2, t3; + + s0 = qd::two_sum(a[0], b[0], t0); + s1 = qd::two_sum(a[1], b[1], t1); + s2 = qd::two_sum(a[2], b[2], t2); + s3 = qd::two_sum(a[3], b[3], t3); + + s1 = qd::two_sum(s1, t0, t0); + qd::three_sum(s2, t0, t1); + qd::three_sum2(s3, t0, t2); + t0 = t0 + t1 + t3; + + qd::renorm(s0, s1, s2, s3, t0); + return qd_real(s0, s1, s2, s3, t0); + */ + + /* Same as above, but addition re-organized to minimize + data dependency ... unfortunately some compilers are + not very smart to do this automatically */ + double s0, s1, s2, s3; + double t0, t1, t2, t3; + + double v0, v1, v2, v3; + double u0, u1, u2, u3; + double w0, w1, w2, w3; + + s0 = a[0] + b[0]; + s1 = a[1] + b[1]; + s2 = a[2] + b[2]; + s3 = a[3] + b[3]; + + v0 = s0 - a[0]; + v1 = s1 - a[1]; + v2 = s2 - a[2]; + v3 = s3 - a[3]; + + u0 = s0 - v0; + u1 = s1 - v1; + u2 = s2 - v2; + u3 = s3 - v3; + + w0 = a[0] - u0; + w1 = a[1] - u1; + w2 = a[2] - u2; + w3 = a[3] - u3; + + u0 = b[0] - v0; + u1 = b[1] - v1; + u2 = b[2] - v2; + u3 = b[3] - v3; + + t0 = w0 + u0; + t1 = w1 + u1; + t2 = w2 + u2; + t3 = w3 + u3; + + s1 = qd::two_sum(s1, t0, t0); + qd::three_sum(s2, t0, t1); + qd::three_sum2(s3, t0, t2); + t0 = t0 + t1 + t3; + + /* renormalize */ + qd::renorm(s0, s1, s2, s3, t0); + return qd_real(s0, s1, s2, s3); +} + +/* quad-double + quad-double */ +inline qd_real operator+(const qd_real &a, const qd_real &b) { +#ifndef QD_IEEE_ADD + return qd_real::sloppy_add(a, b); +#else + return qd_real::ieee_add(a, b); +#endif +} + + + +/********** Self-Additions ************/ +/* quad-double += double */ +inline qd_real &qd_real::operator+=(double a) { + *this = *this + a; + return *this; +} + +/* quad-double += double-double */ +inline qd_real &qd_real::operator+=(const dd_real &a) { + *this = *this + a; + return *this; +} + +/* quad-double += quad-double */ +inline qd_real &qd_real::operator+=(const qd_real &a) { + *this = *this + a; + return *this; +} + +/********** Unary Minus **********/ +inline qd_real qd_real::operator-() const { + return qd_real(-x[0], -x[1], -x[2], -x[3]); +} + +/********** Subtractions **********/ +inline qd_real operator-(const qd_real &a, double b) { + return (a + (-b)); +} + +inline qd_real operator-(double a, const qd_real &b) { + return (a + (-b)); +} + +inline qd_real operator-(const qd_real &a, const dd_real &b) { + return (a + (-b)); +} + +inline qd_real operator-(const dd_real &a, const qd_real &b) { + return (a + (-b)); +} + +inline qd_real operator-(const qd_real &a, const qd_real &b) { + return (a + (-b)); +} + +/********** Self-Subtractions **********/ +inline qd_real &qd_real::operator-=(double a) { + return ((*this) += (-a)); +} + +inline qd_real &qd_real::operator-=(const dd_real &a) { + return ((*this) += (-a)); +} + +inline qd_real &qd_real::operator-=(const qd_real &a) { + return ((*this) += (-a)); +} + + +inline qd_real operator*(double a, const qd_real &b) { + return (b * a); +} + +inline qd_real operator*(const dd_real &a, const qd_real &b) { + return (b * a); +} + +inline qd_real mul_pwr2(const qd_real &a, double b) { + return qd_real(a[0] * b, a[1] * b, a[2] * b, a[3] * b); +} + +/********** Multiplications **********/ +inline qd_real operator*(const qd_real &a, double b) { + double p0, p1, p2, p3; + double q0, q1, q2; + double s0, s1, s2, s3, s4; + + p0 = qd::two_prod(a[0], b, q0); + p1 = qd::two_prod(a[1], b, q1); + p2 = qd::two_prod(a[2], b, q2); + p3 = a[3] * b; + + s0 = p0; + + s1 = qd::two_sum(q0, p1, s2); + + qd::three_sum(s2, q1, p2); + + qd::three_sum2(q1, q2, p3); + s3 = q1; + + s4 = q2 + p2; + + qd::renorm(s0, s1, s2, s3, s4); + return qd_real(s0, s1, s2, s3); + +} + +/* quad-double * double-double */ +/* a0 * b0 0 + a0 * b1 1 + a1 * b0 2 + a1 * b1 3 + a2 * b0 4 + a2 * b1 5 + a3 * b0 6 + a3 * b1 7 */ +inline qd_real operator*(const qd_real &a, const dd_real &b) { + double p0, p1, p2, p3, p4; + double q0, q1, q2, q3, q4; + double s0, s1, s2; + double t0, t1; + + p0 = qd::two_prod(a[0], b._hi(), q0); + p1 = qd::two_prod(a[0], b._lo(), q1); + p2 = qd::two_prod(a[1], b._hi(), q2); + p3 = qd::two_prod(a[1], b._lo(), q3); + p4 = qd::two_prod(a[2], b._hi(), q4); + + qd::three_sum(p1, p2, q0); + + /* Five-Three-Sum */ + qd::three_sum(p2, p3, p4); + q1 = qd::two_sum(q1, q2, q2); + s0 = qd::two_sum(p2, q1, t0); + s1 = qd::two_sum(p3, q2, t1); + s1 = qd::two_sum(s1, t0, t0); + s2 = t0 + t1 + p4; + p2 = s0; + + p3 = a[2] * b._hi() + a[3] * b._lo() + q3 + q4; + qd::three_sum2(p3, q0, s1); + p4 = q0 + s2; + + qd::renorm(p0, p1, p2, p3, p4); + return qd_real(p0, p1, p2, p3); +} + +/* quad-double * quad-double */ +/* a0 * b0 0 + a0 * b1 1 + a1 * b0 2 + a0 * b2 3 + a1 * b1 4 + a2 * b0 5 + a0 * b3 6 + a1 * b2 7 + a2 * b1 8 + a3 * b0 9 */ +inline qd_real qd_real::sloppy_mul(const qd_real &a, const qd_real &b) { + double p0, p1, p2, p3, p4, p5; + double q0, q1, q2, q3, q4, q5; + double t0, t1; + double s0, s1, s2; + + p0 = qd::two_prod(a[0], b[0], q0); + + p1 = qd::two_prod(a[0], b[1], q1); + p2 = qd::two_prod(a[1], b[0], q2); + + p3 = qd::two_prod(a[0], b[2], q3); + p4 = qd::two_prod(a[1], b[1], q4); + p5 = qd::two_prod(a[2], b[0], q5); + + /* Start Accumulation */ + qd::three_sum(p1, p2, q0); + + /* Six-Three Sum of p2, q1, q2, p3, p4, p5. */ + qd::three_sum(p2, q1, q2); + qd::three_sum(p3, p4, p5); + /* compute (s0, s1, s2) = (p2, q1, q2) + (p3, p4, p5). */ + s0 = qd::two_sum(p2, p3, t0); + s1 = qd::two_sum(q1, p4, t1); + s2 = q2 + p5; + s1 = qd::two_sum(s1, t0, t0); + s2 += (t0 + t1); + + /* O(eps^3) order terms */ + s1 += a[0]*b[3] + a[1]*b[2] + a[2]*b[1] + a[3]*b[0] + q0 + q3 + q4 + q5; + qd::renorm(p0, p1, s0, s1, s2); + return qd_real(p0, p1, s0, s1); +} + +inline qd_real qd_real::accurate_mul(const qd_real &a, const qd_real &b) { + double p0, p1, p2, p3, p4, p5; + double q0, q1, q2, q3, q4, q5; + double p6, p7, p8, p9; + double q6, q7, q8, q9; + double r0, r1; + double t0, t1; + double s0, s1, s2; + + p0 = qd::two_prod(a[0], b[0], q0); + + p1 = qd::two_prod(a[0], b[1], q1); + p2 = qd::two_prod(a[1], b[0], q2); + + p3 = qd::two_prod(a[0], b[2], q3); + p4 = qd::two_prod(a[1], b[1], q4); + p5 = qd::two_prod(a[2], b[0], q5); + + /* Start Accumulation */ + qd::three_sum(p1, p2, q0); + + /* Six-Three Sum of p2, q1, q2, p3, p4, p5. */ + qd::three_sum(p2, q1, q2); + qd::three_sum(p3, p4, p5); + /* compute (s0, s1, s2) = (p2, q1, q2) + (p3, p4, p5). */ + s0 = qd::two_sum(p2, p3, t0); + s1 = qd::two_sum(q1, p4, t1); + s2 = q2 + p5; + s1 = qd::two_sum(s1, t0, t0); + s2 += (t0 + t1); + + /* O(eps^3) order terms */ + p6 = qd::two_prod(a[0], b[3], q6); + p7 = qd::two_prod(a[1], b[2], q7); + p8 = qd::two_prod(a[2], b[1], q8); + p9 = qd::two_prod(a[3], b[0], q9); + + /* Nine-Two-Sum of q0, s1, q3, q4, q5, p6, p7, p8, p9. */ + q0 = qd::two_sum(q0, q3, q3); + q4 = qd::two_sum(q4, q5, q5); + p6 = qd::two_sum(p6, p7, p7); + p8 = qd::two_sum(p8, p9, p9); + /* Compute (t0, t1) = (q0, q3) + (q4, q5). */ + t0 = qd::two_sum(q0, q4, t1); + t1 += (q3 + q5); + /* Compute (r0, r1) = (p6, p7) + (p8, p9). */ + r0 = qd::two_sum(p6, p8, r1); + r1 += (p7 + p9); + /* Compute (q3, q4) = (t0, t1) + (r0, r1). */ + q3 = qd::two_sum(t0, r0, q4); + q4 += (t1 + r1); + /* Compute (t0, t1) = (q3, q4) + s1. */ + t0 = qd::two_sum(q3, s1, t1); + t1 += q4; + + /* O(eps^4) terms -- Nine-One-Sum */ + t1 += a[1] * b[3] + a[2] * b[2] + a[3] * b[1] + q6 + q7 + q8 + q9 + s2; + + qd::renorm(p0, p1, s0, t0, t1); + return qd_real(p0, p1, s0, t0); +} + +inline qd_real operator*(const qd_real &a, const qd_real &b) { +#ifdef QD_SLOPPY_MUL + return qd_real::sloppy_mul(a, b); +#else + return qd_real::accurate_mul(a, b); +#endif +} + +/* quad-double ^ 2 = (x0 + x1 + x2 + x3) ^ 2 + = x0 ^ 2 + 2 x0 * x1 + (2 x0 * x2 + x1 ^ 2) + + (2 x0 * x3 + 2 x1 * x2) */ +inline qd_real sqr(const qd_real &a) { + double p0, p1, p2, p3, p4, p5; + double q0, q1, q2, q3; + double s0, s1; + double t0, t1; + + p0 = qd::two_sqr(a[0], q0); + p1 = qd::two_prod(2.0 * a[0], a[1], q1); + p2 = qd::two_prod(2.0 * a[0], a[2], q2); + p3 = qd::two_sqr(a[1], q3); + + p1 = qd::two_sum(q0, p1, q0); + + q0 = qd::two_sum(q0, q1, q1); + p2 = qd::two_sum(p2, p3, p3); + + s0 = qd::two_sum(q0, p2, t0); + s1 = qd::two_sum(q1, p3, t1); + + s1 = qd::two_sum(s1, t0, t0); + t0 += t1; + + s1 = qd::quick_two_sum(s1, t0, t0); + p2 = qd::quick_two_sum(s0, s1, t1); + p3 = qd::quick_two_sum(t1, t0, q0); + + p4 = 2.0 * a[0] * a[3]; + p5 = 2.0 * a[1] * a[2]; + + p4 = qd::two_sum(p4, p5, p5); + q2 = qd::two_sum(q2, q3, q3); + + t0 = qd::two_sum(p4, q2, t1); + t1 = t1 + p5 + q3; + + p3 = qd::two_sum(p3, t0, p4); + p4 = p4 + q0 + t1; + + qd::renorm(p0, p1, p2, p3, p4); + return qd_real(p0, p1, p2, p3); + +} + +/********** Self-Multiplication **********/ +/* quad-double *= double */ +inline qd_real &qd_real::operator*=(double a) { + *this = (*this * a); + return *this; +} + +/* quad-double *= double-double */ +inline qd_real &qd_real::operator*=(const dd_real &a) { + *this = (*this * a); + return *this; +} + +/* quad-double *= quad-double */ +inline qd_real &qd_real::operator*=(const qd_real &a) { + *this = *this * a; + return *this; +} + +inline qd_real operator/ (const qd_real &a, const dd_real &b) { +#ifdef QD_SLOPPY_DIV + return qd_real::sloppy_div(a, b); +#else + return qd_real::accurate_div(a, b); +#endif +} + +inline qd_real operator/(const qd_real &a, const qd_real &b) { +#ifdef QD_SLOPPY_DIV + return qd_real::sloppy_div(a, b); +#else + return qd_real::accurate_div(a, b); +#endif +} + +/* double / quad-double */ +inline qd_real operator/(double a, const qd_real &b) { + return qd_real(a) / b; +} + +/* double-double / quad-double */ +inline qd_real operator/(const dd_real &a, const qd_real &b) { + return qd_real(a) / b; +} + +/********** Self-Divisions **********/ +/* quad-double /= double */ +inline qd_real &qd_real::operator/=(double a) { + *this = (*this / a); + return *this; +} + +/* quad-double /= double-double */ +inline qd_real &qd_real::operator/=(const dd_real &a) { + *this = (*this / a); + return *this; +} + +/* quad-double /= quad-double */ +inline qd_real &qd_real::operator/=(const qd_real &a) { + *this = (*this / a); + return *this; +} + + +/********** Exponentiation **********/ +inline qd_real qd_real::operator^(int n) const { + return pow(*this, n); +} + +/********** Miscellaneous **********/ +inline qd_real abs(const qd_real &a) { + return (a[0] < 0.0) ? -a : a; +} + +inline qd_real fabs(const qd_real &a) { + return abs(a); +} + +/* Quick version. May be off by one when qd is very close + to the middle of two integers. */ +inline qd_real quick_nint(const qd_real &a) { + qd_real r = qd_real(qd::nint(a[0]), qd::nint(a[1]), + qd::nint(a[2]), qd::nint(a[3])); + r.renorm(); + return r; +} + +/*********** Assignments ************/ +/* quad-double = double */ +inline qd_real &qd_real::operator=(double a) { + x[0] = a; + x[1] = x[2] = x[3] = 0.0; + return *this; +} + +/* quad-double = double-double */ +inline qd_real &qd_real::operator=(const dd_real &a) { + x[0] = a._hi(); + x[1] = a._lo(); + x[2] = x[3] = 0.0; + return *this; +} + +/********** Equality Comparison **********/ +inline bool operator==(const qd_real &a, double b) { + return (a[0] == b && a[1] == 0.0 && a[2] == 0.0 && a[3] == 0.0); +} + +inline bool operator==(double a, const qd_real &b) { + return (b == a); +} + +inline bool operator==(const qd_real &a, const dd_real &b) { + return (a[0] == b._hi() && a[1] == b._lo() && + a[2] == 0.0 && a[3] == 0.0); +} + +inline bool operator==(const dd_real &a, const qd_real &b) { + return (b == a); +} + +inline bool operator==(const qd_real &a, const qd_real &b) { + return (a[0] == b[0] && a[1] == b[1] && + a[2] == b[2] && a[3] == b[3]); +} + + +/********** Less-Than Comparison ***********/ +inline bool operator<(const qd_real &a, double b) { + return (a[0] < b || (a[0] == b && a[1] < 0.0)); +} + +inline bool operator<(double a, const qd_real &b) { + return (b > a); +} + +inline bool operator<(const qd_real &a, const dd_real &b) { + return (a[0] < b._hi() || + (a[0] == b._hi() && (a[1] < b._lo() || + (a[1] == b._lo() && a[2] < 0.0)))); +} + +inline bool operator<(const dd_real &a, const qd_real &b) { + return (b > a); +} + +inline bool operator<(const qd_real &a, const qd_real &b) { + return (a[0] < b[0] || + (a[0] == b[0] && (a[1] < b[1] || + (a[1] == b[1] && (a[2] < b[2] || + (a[2] == b[2] && a[3] < b[3])))))); +} + +/********** Greater-Than Comparison ***********/ +inline bool operator>(const qd_real &a, double b) { + return (a[0] > b || (a[0] == b && a[1] > 0.0)); +} + +inline bool operator>(double a, const qd_real &b) { + return (b < a); +} + +inline bool operator>(const qd_real &a, const dd_real &b) { + return (a[0] > b._hi() || + (a[0] == b._hi() && (a[1] > b._lo() || + (a[1] == b._lo() && a[2] > 0.0)))); +} + +inline bool operator>(const dd_real &a, const qd_real &b) { + return (b < a); +} + +inline bool operator>(const qd_real &a, const qd_real &b) { + return (a[0] > b[0] || + (a[0] == b[0] && (a[1] > b[1] || + (a[1] == b[1] && (a[2] > b[2] || + (a[2] == b[2] && a[3] > b[3])))))); +} + + +/********** Less-Than-Or-Equal-To Comparison **********/ +inline bool operator<=(const qd_real &a, double b) { + return (a[0] < b || (a[0] == b && a[1] <= 0.0)); +} + +inline bool operator<=(double a, const qd_real &b) { + return (b >= a); +} + +inline bool operator<=(const qd_real &a, const dd_real &b) { + return (a[0] < b._hi() || + (a[0] == b._hi() && (a[1] < b._lo() || + (a[1] == b._lo() && a[2] <= 0.0)))); +} + +inline bool operator<=(const dd_real &a, const qd_real &b) { + return (b >= a); +} + +inline bool operator<=(const qd_real &a, const qd_real &b) { + return (a[0] < b[0] || + (a[0] == b[0] && (a[1] < b[1] || + (a[1] == b[1] && (a[2] < b[2] || + (a[2] == b[2] && a[3] <= b[3])))))); +} + +/********** Greater-Than-Or-Equal-To Comparison **********/ +inline bool operator>=(const qd_real &a, double b) { + return (a[0] > b || (a[0] == b && a[1] >= 0.0)); +} + +inline bool operator>=(double a, const qd_real &b) { + return (b <= a); +} + +inline bool operator>=(const qd_real &a, const dd_real &b) { + return (a[0] > b._hi() || + (a[0] == b._hi() && (a[1] > b._lo() || + (a[1] == b._lo() && a[2] >= 0.0)))); +} + +inline bool operator>=(const dd_real &a, const qd_real &b) { + return (b <= a); +} + +inline bool operator>=(const qd_real &a, const qd_real &b) { + return (a[0] > b[0] || + (a[0] == b[0] && (a[1] > b[1] || + (a[1] == b[1] && (a[2] > b[2] || + (a[2] == b[2] && a[3] >= b[3])))))); +} + + + +/********** Not-Equal-To Comparison **********/ +inline bool operator!=(const qd_real &a, double b) { + return !(a == b); +} + +inline bool operator!=(double a, const qd_real &b) { + return !(a == b); +} + +inline bool operator!=(const qd_real &a, const dd_real &b) { + return !(a == b); +} + +inline bool operator!=(const dd_real &a, const qd_real &b) { + return !(a == b); +} + +inline bool operator!=(const qd_real &a, const qd_real &b) { + return !(a == b); +} + + + +inline qd_real aint(const qd_real &a) { + return (a[0] >= 0) ? floor(a) : ceil(a); +} + +inline bool qd_real::is_zero() const { + return (x[0] == 0.0); +} + +inline bool qd_real::is_one() const { + return (x[0] == 1.0 && x[1] == 0.0 && x[2] == 0.0 && x[3] == 0.0); +} + +inline bool qd_real::is_positive() const { + return (x[0] > 0.0); +} + +inline bool qd_real::is_negative() const { + return (x[0] < 0.0); +} + +inline qd_real::operator bool() const { + return (x[0] != 0.0); +} + +inline qd_real::operator double() const { + return to_double(*this); +} + +inline dd_real to_dd_real(const qd_real &a) { + return dd_real(a[0], a[1]); +} + +inline double to_double(const qd_real &a) { + return a[0]; +} + +inline int to_int(const qd_real &a) { + return static_cast(a[0]); +} + +inline qd_real inv(const qd_real &qd) { + return 1.0 / qd; +} + +inline qd_real max(const qd_real &a, const qd_real &b) { + return (a > b) ? a : b; +} + +inline qd_real max(const qd_real &a, const qd_real &b, + const qd_real &c) { + return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); +} + +inline qd_real min(const qd_real &a, const qd_real &b) { + return (a < b) ? a : b; +} + +inline qd_real min(const qd_real &a, const qd_real &b, + const qd_real &c) { + return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); +} + +/* Random number generator */ +inline qd_real qd_real::rand() { + return qdrand(); +} + +inline qd_real ldexp(const qd_real &a, int n) { + return qd_real(std::ldexp(a[0], n), std::ldexp(a[1], n), + std::ldexp(a[2], n), std::ldexp(a[3], n)); +} + +#endif /* _QD_QD_INLINE_H */ diff --git a/src/external/PackedCSparse/qd/qd_real.cc b/src/external/PackedCSparse/qd/qd_real.cc new file mode 100644 index 00000000..02cb7aa3 --- /dev/null +++ b/src/external/PackedCSparse/qd/qd_real.cc @@ -0,0 +1,2624 @@ +/* + * src/qd_real.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + * + * Contains implementation of non-inlined functions of quad-double + * package. Inlined functions are found in qd_inline.h (in include directory). + */ +#include +#include +#include +#include +#include +#include +#include + +#include "qd_config.h" +#include "qd_real.h" +#include "util.h" + +#include "bits.h" + +#ifndef QD_INLINE +#include "qd_inline.h" +#endif + +using std::cout; +using std::cerr; +using std::endl; +using std::istream; +using std::ostream; +using std::ios_base; +using std::string; +using std::setw; + +using namespace qd; + +void qd_real::error(const char *msg) { + //if (msg) { cerr << "ERROR " << msg << endl; } +} + +/********** Multiplications **********/ + +qd_real nint(const qd_real &a) { + double x0, x1, x2, x3; + + x0 = nint(a[0]); + x1 = x2 = x3 = 0.0; + + if (x0 == a[0]) { + /* First double is already an integer. */ + x1 = nint(a[1]); + + if (x1 == a[1]) { + /* Second double is already an integer. */ + x2 = nint(a[2]); + + if (x2 == a[2]) { + /* Third double is already an integer. */ + x3 = nint(a[3]); + } else { + if (std::abs(x2 - a[2]) == 0.5 && a[3] < 0.0) { + x2 -= 1.0; + } + } + + } else { + if (std::abs(x1 - a[1]) == 0.5 && a[2] < 0.0) { + x1 -= 1.0; + } + } + + } else { + /* First double is not an integer. */ + if (std::abs(x0 - a[0]) == 0.5 && a[1] < 0.0) { + x0 -= 1.0; + } + } + + renorm(x0, x1, x2, x3); + return qd_real(x0, x1, x2, x3); +} + +qd_real floor(const qd_real &a) { + double x0, x1, x2, x3; + x1 = x2 = x3 = 0.0; + x0 = std::floor(a[0]); + + if (x0 == a[0]) { + x1 = std::floor(a[1]); + + if (x1 == a[1]) { + x2 = std::floor(a[2]); + + if (x2 == a[2]) { + x3 = std::floor(a[3]); + } + } + + renorm(x0, x1, x2, x3); + return qd_real(x0, x1, x2, x3); + } + + return qd_real(x0, x1, x2, x3); +} + +qd_real ceil(const qd_real &a) { + double x0, x1, x2, x3; + x1 = x2 = x3 = 0.0; + x0 = std::ceil(a[0]); + + if (x0 == a[0]) { + x1 = std::ceil(a[1]); + + if (x1 == a[1]) { + x2 = std::ceil(a[2]); + + if (x2 == a[2]) { + x3 = std::ceil(a[3]); + } + } + + renorm(x0, x1, x2, x3); + return qd_real(x0, x1, x2, x3); + } + + return qd_real(x0, x1, x2, x3); +} + + + +/********** Divisions **********/ +/* quad-double / double */ +qd_real operator/(const qd_real &a, double b) { + /* Strategy: compute approximate quotient using high order + doubles, and then correct it 3 times using the remainder. + (Analogous to long division.) */ + double t0, t1; + double q0, q1, q2, q3; + qd_real r; + + q0 = a[0] / b; /* approximate quotient */ + + /* Compute the remainder a - q0 * b */ + t0 = two_prod(q0, b, t1); + r = a - dd_real(t0, t1); + + /* Compute the first correction */ + q1 = r[0] / b; + t0 = two_prod(q1, b, t1); + r -= dd_real(t0, t1); + + /* Second correction to the quotient. */ + q2 = r[0] / b; + t0 = two_prod(q2, b, t1); + r -= dd_real(t0, t1); + + /* Final correction to the quotient. */ + q3 = r[0] / b; + + renorm(q0, q1, q2, q3); + return qd_real(q0, q1, q2, q3); +} + +qd_real::qd_real(const char *s) { + if (qd_real::read(s, *this)) { + qd_real::error("(qd_real::qd_real): INPUT ERROR."); + *this = qd_real::_nan; + } +} + +qd_real &qd_real::operator=(const char *s) { + if (qd_real::read(s, *this)) { + qd_real::error("(qd_real::operator=): INPUT ERROR."); + *this = qd_real::_nan; + } + return *this; +} + +istream &operator>>(istream &s, qd_real &qd) { + char str[255]; + s >> str; + qd = qd_real(str); + return s; +} + +ostream &operator<<(ostream &os, const qd_real &qd) { + bool showpos = (os.flags() & ios_base::showpos) != 0; + bool uppercase = (os.flags() & ios_base::uppercase) != 0; + return os << qd.to_string((int)os.precision(), (int)os.width(), os.flags(), + showpos, uppercase, os.fill()); +} + +/* Read a quad-double from s. */ +int qd_real::read(const char *s, qd_real &qd) { + const char *p = s; + char ch; + int sign = 0; + int point = -1; /* location of decimal point */ + int nd = 0; /* number of digits read */ + int e = 0; /* exponent. */ + bool done = false; + qd_real r = 0.0; /* number being read */ + + /* Skip any leading spaces */ + while (*p == ' ') p++; + + while (!done && (ch = *p) != '\0') { + if (ch >= '0' && ch <= '9') { + /* It's a digit */ + int d = ch - '0'; + r *= 10.0; + r += static_cast(d); + nd++; + } else { + /* Non-digit */ + switch (ch) { + case '.': + if (point >= 0) + return -1; /* we've already encountered a decimal point. */ + point = nd; + break; + case '-': + case '+': + if (sign != 0 || nd > 0) + return -1; /* we've already encountered a sign, or if its + not at first position. */ + sign = (ch == '-') ? -1 : 1; + break; + case 'E': + case 'e': + int nread; + nread = std::sscanf(p+1, "%d", &e); + done = true; + if (nread != 1) + return -1; /* read of exponent failed. */ + break; + case ' ': + done = true; + break; + default: + return -1; + + } + } + + p++; + } + + + + /* Adjust exponent to account for decimal point */ + if (point >= 0) { + e -= (nd - point); + } + + /* Multiply the the exponent */ + if (e != 0) { + r *= (qd_real(10.0) ^ e); + } + + qd = (sign < 0) ? -r : r; + return 0; +} + +void qd_real::to_digits(char *s, int &expn, int precision) const { + int D = precision + 1; /* number of digits to compute */ + + qd_real r = abs(*this); + int e; /* exponent */ + int i, d; + + if (x[0] == 0.0) { + /* this == 0.0 */ + expn = 0; + for (i = 0; i < precision; i++) s[i] = '0'; + return; + } + + /* First determine the (approximate) exponent. */ + e = static_cast(std::floor(std::log10(std::abs(x[0])))); + + if (e < -300) { + r *= qd_real(10.0) ^ 300; + r /= qd_real(10.0) ^ (e + 300); + } else if (e > 300) { + r = ldexp(r, -53); + r /= qd_real(10.0) ^ e; + r = ldexp(r, 53); + } else { + r /= qd_real(10.0) ^ e; + } + + /* Fix exponent if we are off by one */ + if (r >= 10.0) { + r /= 10.0; + e++; + } else if (r < 1.0) { + r *= 10.0; + e--; + } + + if (r >= 10.0 || r < 1.0) { + qd_real::error("(qd_real::to_digits): can't compute exponent."); + return; + } + + /* Extract the digits */ + for (i = 0; i < D; i++) { + d = static_cast(r[0]); + r -= d; + r *= 10.0; + + s[i] = static_cast(d + '0'); + } + + /* Fix out of range digits. */ + for (i = D-1; i > 0; i--) { + if (s[i] < '0') { + s[i-1]--; + s[i] += 10; + } else if (s[i] > '9') { + s[i-1]++; + s[i] -= 10; + } + } + + if (s[0] <= '0') { + qd_real::error("(qd_real::to_digits): non-positive leading digit."); + return; + } + + /* Round, handle carry */ + if (s[D-1] >= '5') { + s[D-2]++; + + i = D-2; + while (i > 0 && s[i] > '9') { + s[i] -= 10; + s[--i]++; + } + } + + /* If first digit is 10, shift everything. */ + if (s[0] > '9') { + e++; + for (i = precision; i >= 2; i--) s[i] = s[i-1]; + s[0] = '1'; + s[1] = '0'; + } + + s[precision] = 0; + expn = e; +} + +/* Writes the quad-double number into the character array s of length len. + The integer d specifies how many significant digits to write. + The string s must be able to hold at least (d+8) characters. + showpos indicates whether to use the + sign, and uppercase indicates + whether the E or e is to be used for the exponent. */ +void qd_real::write(char *s, int len, int precision, + bool showpos, bool uppercase) const { + string str = to_string(precision, 0, ios_base::scientific, showpos, uppercase); + strncpy(s, str.c_str(), len-1); + s[len-1] = 0; +} + +void round_string_qd(char *s, int precision, int *offset){ + /* + Input string must be all digits or errors will occur. + */ + + int i; + int D = precision ; + + /* Round, handle carry */ + if (D>0 && s[D] >= '5') { + s[D-1]++; + + i = D-1; + while (i > 0 && s[i] > '9') { + s[i] -= 10; + s[--i]++; + } + } + + /* If first digit is 10, shift everything. */ + if (s[0] > '9') { + // e++; // don't modify exponent here + for (i = precision; i >= 1; i--) s[i+1] = s[i]; + s[0] = '1'; + s[1] = '0'; + + (*offset)++ ; // now offset needs to be increased by one + precision++ ; + } + + s[precision] = 0; // add terminator for array +} + + +string qd_real::to_string(int precision, int width, ios_base::fmtflags fmt, + bool showpos, bool uppercase, char fill) const { + string s; + bool fixed = (fmt & ios_base::fixed) != 0; + bool sgn = true; + int i, e = 0; + + if (isinf()) { + if (*this < 0.0) + s += '-'; + else if (showpos) + s += '+'; + else + sgn = false; + s += uppercase ? "INF" : "inf"; + } else if (isnan()) { + s = uppercase ? "NAN" : "nan"; + sgn = false; + } else { + if (*this < 0.0) + s += '-'; + else if (showpos) + s += '+'; + else + sgn = false; + + if (*this == 0.0) { + /* Zero case */ + s += '0'; + if (precision > 0) { + s += '.'; + s.append(precision, '0'); + } + } else { + /* Non-zero case */ + int off = (fixed ? (1 + to_int(floor(log10(abs(*this))))) : 1); + int d = precision + off; + + int d_with_extra = d; + if(fixed) + d_with_extra = std::max(120, d); // longer than the max accuracy for DD + + // highly special case - fixed mode, precision is zero, abs(*this) < 1.0 + // without this trap a number like 0.9 printed fixed with 0 precision prints as 0 + // should be rounded to 1. + if(fixed && (precision == 0) && (abs(*this) < 1.0)){ + if(abs(*this) >= 0.5) + s += '1'; + else + s += '0'; + + return s; + } + + // handle near zero to working precision (but not exactly zero) + if (fixed && d <= 0) { + s += '0'; + if (precision > 0) { + s += '.'; + s.append(precision, '0'); + } + } else { // default + + char *t ; // = new char[d+1]; + int j; + + if(fixed){ + t = new char[d_with_extra+1]; + to_digits(t, e, d_with_extra); + } + else{ + t = new char[d+1]; + to_digits(t, e, d); + } + + off = e + 1; + + if (fixed) { + // fix the string if it's been computed incorrectly + // round here in the decimal string if required + round_string_qd(t, d, &off); + + if (off > 0) { + for (i = 0; i < off; i++) s += t[i]; + if (precision > 0) { + s += '.'; + for (j = 0; j < precision; j++, i++) s += t[i]; + } + } else { + s += "0."; + if (off < 0) s.append(-off, '0'); + for (i = 0; i < d; i++) s += t[i]; + } + } else { + s += t[0]; + if (precision > 0) s += '.'; + + for (i = 1; i <= precision; i++) + s += t[i]; + + } + delete [] t; + } + } + + // trap for improper offset with large values + // without this trap, output of values of the for 10^j - 1 fail for j > 28 + // and are output with the point in the wrong place, leading to a dramatically off value + if(fixed && (precision > 0)){ + // make sure that the value isn't dramatically larger + double from_string = atof(s.c_str()); + + // if this ratio is large, then we've got problems + if( fabs( from_string / this->x[0] ) > 3.0 ){ + + // loop on the string, find the point, move it up one + // don't act on the first character + for(i=1; i < (int)s.length(); i++){ + if(s[i] == '.'){ + s[i] = s[i-1] ; + s[i-1] = '.' ; + break; + } + } + + from_string = atof(s.c_str()); + // if this ratio is large, then the string has not been fixed + if( fabs( from_string / this->x[0] ) > 3.0 ){ + dd_real::error("Re-rounding unsuccessful in large number fixed point trap.") ; + } + } + } + + if (!fixed) { + /* Fill in exponent part */ + s += uppercase ? 'E' : 'e'; + append_expn(s, e); + } + } + + /* Fill in the blanks */ + size_t len = s.length(); + if (len < width) { + int delta = width - len; + if (fmt & ios_base::internal) { + if (sgn) + s.insert(static_cast(1), delta, fill); + else + s.insert(static_cast(0), delta, fill); + } else if (fmt & ios_base::left) { + s.append(delta, fill); + } else { + s.insert(static_cast(0), delta, fill); + } + } + + return s; +} + +/* Computes qd^n, where n is an integer. */ +qd_real pow(const qd_real &a, int n) { + if (n == 0) + return 1.0; + + qd_real r = a; /* odd-case multiplier */ + qd_real s = 1.0; /* current answer */ + int N = std::abs(n); + + if (N > 1) { + + /* Use binary exponentiation. */ + while (N > 0) { + if (N % 2 == 1) { + /* If odd, multiply by r. Note eventually N = 1, so this + eventually executes. */ + s *= r; + } + N /= 2; + if (N > 0) + r = sqr(r); + } + + } else { + s = r; + } + + if (n < 0) + return (1.0 / s); + + return s; +} + +qd_real pow(const qd_real &a, const qd_real &b) { + return exp(b * log(a)); +} + +qd_real npwr(const qd_real &a, int n) { + return pow(a, n); +} + +/* Debugging routines */ +void qd_real::dump_bits(const string &name, std::ostream &os) const { + string::size_type len = name.length(); + if (len > 0) { + os << name << " = "; + len += 3; + } + os << "[ "; + len += 2; + for (int j = 0; j < 4; j++) { + if (j > 0) for (string::size_type i = 0; i < len; i++) os << ' '; + print_double_info(os, x[j]); + if (j < 3) + os << endl; + else + os << " ]" << endl; + } +} + +void qd_real::dump(const string &name, std::ostream &os) const { + std::ios_base::fmtflags old_flags = os.flags(); + std::streamsize old_prec = os.precision(19); + os << std::scientific; + + string::size_type len = name.length(); + if (len > 0) { + os << name << " = "; + len += 3; + } + os << "[ "; + len += 2; + os << setw(27) << x[0] << ", " << setw(26) << x[1] << "," << endl; + for (string::size_type i = 0; i < len; i++) os << ' '; + os << setw(27) << x[2] << ", " << setw(26) << x[3] << " ]" << endl; + + os.precision(old_prec); + os.flags(old_flags); +} + +/* Divisions */ +/* quad-double / double-double */ +qd_real qd_real::sloppy_div(const qd_real &a, const dd_real &b) { + double q0, q1, q2, q3; + qd_real r; + qd_real qd_b(b); + + q0 = a[0] / b._hi(); + r = a - q0 * qd_b; + + q1 = r[0] / b._hi(); + r -= (q1 * qd_b); + + q2 = r[0] / b._hi(); + r -= (q2 * qd_b); + + q3 = r[0] / b._hi(); + + ::renorm(q0, q1, q2, q3); + return qd_real(q0, q1, q2, q3); +} + +qd_real qd_real::accurate_div(const qd_real &a, const dd_real &b) { + double q0, q1, q2, q3, q4; + qd_real r; + qd_real qd_b(b); + + q0 = a[0] / b._hi(); + r = a - q0 * qd_b; + + q1 = r[0] / b._hi(); + r -= (q1 * qd_b); + + q2 = r[0] / b._hi(); + r -= (q2 * qd_b); + + q3 = r[0] / b._hi(); + r -= (q3 * qd_b); + + q4 = r[0] / b._hi(); + + ::renorm(q0, q1, q2, q3, q4); + return qd_real(q0, q1, q2, q3); +} + +/* quad-double / quad-double */ +qd_real qd_real::sloppy_div(const qd_real &a, const qd_real &b) { + double q0, q1, q2, q3; + + qd_real r; + + q0 = a[0] / b[0]; + r = a - (b * q0); + + q1 = r[0] / b[0]; + r -= (b * q1); + + q2 = r[0] / b[0]; + r -= (b * q2); + + q3 = r[0] / b[0]; + + ::renorm(q0, q1, q2, q3); + + return qd_real(q0, q1, q2, q3); +} + +qd_real qd_real::accurate_div(const qd_real &a, const qd_real &b) { + double q0, q1, q2, q3; + + qd_real r; + + q0 = a[0] / b[0]; + r = a - (b * q0); + + q1 = r[0] / b[0]; + r -= (b * q1); + + q2 = r[0] / b[0]; + r -= (b * q2); + + q3 = r[0] / b[0]; + + r -= (b * q3); + double q4 = r[0] / b[0]; + + ::renorm(q0, q1, q2, q3, q4); + + return qd_real(q0, q1, q2, q3); +} + +QD_API qd_real sqrt(const qd_real &a) { + /* Strategy: + + Perform the following Newton iteration: + + x' = x + (1 - a * x^2) * x / 2; + + which converges to 1/sqrt(a), starting with the + double precision approximation to 1/sqrt(a). + Since Newton's iteration more or less doubles the + number of correct digits, we only need to perform it + twice. + */ + + if (a.is_zero()) + return 0.0; + + if (a.is_negative()) { + qd_real::error("(qd_real::sqrt): Negative argument."); + return qd_real::_nan; + } + + qd_real r = (1.0 / std::sqrt(a[0])); + qd_real h = mul_pwr2(a, 0.5); + + r += ((0.5 - h * sqr(r)) * r); + r += ((0.5 - h * sqr(r)) * r); + r += ((0.5 - h * sqr(r)) * r); + + r *= a; + return r; +} + + +/* Computes the n-th root of a */ +qd_real nroot(const qd_real &a, int n) { + /* Strategy: Use Newton's iteration to solve + + 1/(x^n) - a = 0 + + Newton iteration becomes + + x' = x + x * (1 - a * x^n) / n + + Since Newton's iteration converges quadratically, + we only need to perform it twice. + + */ + if (n <= 0) { + qd_real::error("(qd_real::nroot): N must be positive."); + return qd_real::_nan; + } + + if (n % 2 == 0 && a.is_negative()) { + qd_real::error("(qd_real::nroot): Negative argument."); + return qd_real::_nan; + } + + if (n == 1) { + return a; + } + if (n == 2) { + return sqrt(a); + } + if (a.is_zero()) { + return qd_real(0.0); + } + + + /* Note a^{-1/n} = exp(-log(a)/n) */ + qd_real r = abs(a); + qd_real x = std::exp(-std::log(r.x[0]) / n); + + /* Perform Newton's iteration. */ + double dbl_n = static_cast(n); + x += x * (1.0 - r * npwr(x, n)) / dbl_n; + x += x * (1.0 - r * npwr(x, n)) / dbl_n; + x += x * (1.0 - r * npwr(x, n)) / dbl_n; + if (a[0] < 0.0){ + x = -x; + } + return 1.0 / x; +} + +static const int n_inv_fact = 15; +static const qd_real inv_fact[n_inv_fact] = { + qd_real( 1.66666666666666657e-01, 9.25185853854297066e-18, + 5.13581318503262866e-34, 2.85094902409834186e-50), + qd_real( 4.16666666666666644e-02, 2.31296463463574266e-18, + 1.28395329625815716e-34, 7.12737256024585466e-51), + qd_real( 8.33333333333333322e-03, 1.15648231731787138e-19, + 1.60494162032269652e-36, 2.22730392507682967e-53), + qd_real( 1.38888888888888894e-03, -5.30054395437357706e-20, + -1.73868675534958776e-36, -1.63335621172300840e-52), + qd_real( 1.98412698412698413e-04, 1.72095582934207053e-22, + 1.49269123913941271e-40, 1.29470326746002471e-58), + qd_real( 2.48015873015873016e-05, 2.15119478667758816e-23, + 1.86586404892426588e-41, 1.61837908432503088e-59), + qd_real( 2.75573192239858925e-06, -1.85839327404647208e-22, + 8.49175460488199287e-39, -5.72661640789429621e-55), + qd_real( 2.75573192239858883e-07, 2.37677146222502973e-23, + -3.26318890334088294e-40, 1.61435111860404415e-56), + qd_real( 2.50521083854417202e-08, -1.44881407093591197e-24, + 2.04267351467144546e-41, -8.49632672007163175e-58), + qd_real( 2.08767569878681002e-09, -1.20734505911325997e-25, + 1.70222792889287100e-42, 1.41609532150396700e-58), + qd_real( 1.60590438368216133e-10, 1.25852945887520981e-26, + -5.31334602762985031e-43, 3.54021472597605528e-59), + qd_real( 1.14707455977297245e-11, 2.06555127528307454e-28, + 6.88907923246664603e-45, 5.72920002655109095e-61), + qd_real( 7.64716373181981641e-13, 7.03872877733453001e-30, + -7.82753927716258345e-48, 1.92138649443790242e-64), + qd_real( 4.77947733238738525e-14, 4.39920548583408126e-31, + -4.89221204822661465e-49, 1.20086655902368901e-65), + qd_real( 2.81145725434552060e-15, 1.65088427308614326e-31, + -2.87777179307447918e-50, 4.27110689256293549e-67) +}; + +qd_real exp(const qd_real &a) { + /* Strategy: We first reduce the size of x by noting that + + exp(kr + m * log(2)) = 2^m * exp(r)^k + + where m and k are integers. By choosing m appropriately + we can make |kr| <= log(2) / 2 = 0.347. Then exp(r) is + evaluated using the familiar Taylor series. Reducing the + argument substantially speeds up the convergence. */ + + const double k = ldexp(1.0, 16); + const double inv_k = 1.0 / k; + + if (a[0] <= -709.0) + return 0.0; + + if (a[0] >= 709.0) + return qd_real::_inf; + + if (a.is_zero()) + return 1.0; + + if (a.is_one()) + return qd_real::_e; + + double m = std::floor(a.x[0] / qd_real::_log2.x[0] + 0.5); + qd_real r = mul_pwr2(a - qd_real::_log2 * m, inv_k); + qd_real s, p, t; + double thresh = inv_k * qd_real::_eps; + + p = sqr(r); + s = r + mul_pwr2(p, 0.5); + int i = 0; + do { + p *= r; + t = p * inv_fact[i++]; + s += t; + } while (std::abs(to_double(t)) > thresh && i < 9); + + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s = mul_pwr2(s, 2.0) + sqr(s); + s += 1.0; + return ldexp(s, static_cast(m)); +} + +/* Logarithm. Computes log(x) in quad-double precision. + This is a natural logarithm (i.e., base e). */ +qd_real log(const qd_real &a) { + /* Strategy. The Taylor series for log converges much more + slowly than that of exp, due to the lack of the factorial + term in the denominator. Hence this routine instead tries + to determine the root of the function + + f(x) = exp(x) - a + + using Newton iteration. The iteration is given by + + x' = x - f(x)/f'(x) + = x - (1 - a * exp(-x)) + = x + a * exp(-x) - 1. + + Two iteration is needed, since Newton's iteration + approximately doubles the number of digits per iteration. */ + + if (a.is_one()) { + return 0.0; + } + + if (a[0] <= 0.0) { + qd_real::error("(qd_real::log): Non-positive argument."); + return qd_real::_nan; + } + + if (a[0] == 0.0) { + return -qd_real::_inf; + } + + qd_real x = std::log(a[0]); /* Initial approximation */ + + x = x + a * exp(-x) - 1.0; + x = x + a * exp(-x) - 1.0; + x = x + a * exp(-x) - 1.0; + + return x; +} + +qd_real log10(const qd_real &a) { + return log(a) / qd_real::_log10; +} + +static const qd_real _pi1024 = qd_real( + 3.067961575771282340e-03, 1.195944139792337116e-19, + -2.924579892303066080e-36, 1.086381075061880158e-52); + +/* Table of sin(k * pi/1024) and cos(k * pi/1024). */ +static const qd_real sin_table [] = { + qd_real( 3.0679567629659761e-03, 1.2690279085455925e-19, + 5.2879464245328389e-36, -1.7820334081955298e-52), + qd_real( 6.1358846491544753e-03, 9.0545257482474933e-20, + 1.6260113133745320e-37, -9.7492001208767410e-55), + qd_real( 9.2037547820598194e-03, -1.2136591693535934e-19, + 5.5696903949425567e-36, 1.2505635791936951e-52), + qd_real( 1.2271538285719925e-02, 6.9197907640283170e-19, + -4.0203726713435555e-36, -2.0688703606952816e-52), + qd_real( 1.5339206284988102e-02, -8.4462578865401696e-19, + 4.6535897505058629e-35, -1.3923682978570467e-51), + qd_real( 1.8406729905804820e-02, 7.4195533812833160e-19, + 3.9068476486787607e-35, 3.6393321292898614e-52), + qd_real( 2.1474080275469508e-02, -4.5407960207688566e-19, + -2.2031770119723005e-35, 1.2709814654833741e-51), + qd_real( 2.4541228522912288e-02, -9.1868490125778782e-20, + 4.8706148704467061e-36, -2.8153947855469224e-52), + qd_real( 2.7608145778965743e-02, -1.5932358831389269e-18, + -7.0475416242776030e-35, -2.7518494176602744e-51), + qd_real( 3.0674803176636626e-02, -1.6936054844107918e-20, + -2.0039543064442544e-36, -1.6267505108658196e-52), + qd_real( 3.3741171851377587e-02, -2.0096074292368340e-18, + -1.3548237016537134e-34, 6.5554881875899973e-51), + qd_real( 3.6807222941358832e-02, 6.1060088803529842e-19, + -4.0448721259852727e-35, -2.1111056765671495e-51), + qd_real( 3.9872927587739811e-02, 4.6657453481183289e-19, + 3.4119333562288684e-35, 2.4007534726187511e-51), + qd_real( 4.2938256934940820e-02, 2.8351940588660907e-18, + 1.6991309601186475e-34, 6.8026536098672629e-51), + qd_real( 4.6003182130914630e-02, -1.1182813940157788e-18, + 7.5235020270378946e-35, 4.1187304955493722e-52), + qd_real( 4.9067674327418015e-02, -6.7961037205182801e-19, + -4.4318868124718325e-35, -9.9376628132525316e-52), + qd_real( 5.2131704680283324e-02, -2.4243695291953779e-18, + -1.3675405320092298e-34, -8.3938137621145070e-51), + qd_real( 5.5195244349689941e-02, -1.3340299860891103e-18, + -3.4359574125665608e-35, 1.1911462755409369e-51), + qd_real( 5.8258264500435759e-02, 2.3299905496077492e-19, + 1.9376108990628660e-36, -5.1273775710095301e-53), + qd_real( 6.1320736302208578e-02, -5.1181134064638108e-19, + -4.2726335866706313e-35, 2.6368495557440691e-51), + qd_real( 6.4382630929857465e-02, -4.2325997000052705e-18, + 3.3260117711855937e-35, 1.4736267706718352e-51), + qd_real( 6.7443919563664065e-02, -6.9221796556983636e-18, + 1.5909286358911040e-34, -7.8828946891835218e-51), + qd_real( 7.0504573389613870e-02, -6.8552791107342883e-18, + -1.9961177630841580e-34, 2.0127129580485300e-50), + qd_real( 7.3564563599667426e-02, -2.7784941506273593e-18, + -9.1240375489852821e-35, -1.9589752023546795e-51), + qd_real( 7.6623861392031492e-02, 2.3253700287958801e-19, + -1.3186083921213440e-36, -4.9927872608099673e-53), + qd_real( 7.9682437971430126e-02, -4.4867664311373041e-18, + 2.8540789143650264e-34, 2.8491348583262741e-51), + qd_real( 8.2740264549375692e-02, 1.4735983530877760e-18, + 3.7284093452233713e-35, 2.9024430036724088e-52), + qd_real( 8.5797312344439894e-02, -3.3881893830684029e-18, + -1.6135529531508258e-34, 7.7294651620588049e-51), + qd_real( 8.8853552582524600e-02, -3.7501775830290691e-18, + 3.7543606373911573e-34, 2.2233701854451859e-50), + qd_real( 9.1908956497132724e-02, 4.7631594854274564e-18, + 1.5722874642939344e-34, -4.8464145447831456e-51), + qd_real( 9.4963495329639006e-02, -6.5885886400417564e-18, + -2.1371116991641965e-34, 1.3819370559249300e-50), + qd_real( 9.8017140329560604e-02, -1.6345823622442560e-18, + -1.3209238810006454e-35, -3.5691060049117942e-52), + qd_real( 1.0106986275482782e-01, 3.3164325719308656e-18, + -1.2004224885132282e-34, 7.2028828495418631e-51), + qd_real( 1.0412163387205457e-01, 6.5760254085385100e-18, + 1.7066246171219214e-34, -4.9499340996893514e-51), + qd_real( 1.0717242495680884e-01, 6.4424044279026198e-18, + -8.3956976499698139e-35, -4.0667730213318321e-51), + qd_real( 1.1022220729388306e-01, -5.6789503537823233e-19, + 1.0380274792383233e-35, 1.5213997918456695e-52), + qd_real( 1.1327095217756435e-01, 2.7100481012132900e-18, + 1.5323292999491619e-35, 4.9564432810360879e-52), + qd_real( 1.1631863091190477e-01, 1.0294914877509705e-18, + -9.3975734948993038e-35, 1.3534827323719708e-52), + qd_real( 1.1936521481099137e-01, -3.9500089391898506e-18, + 3.5317349978227311e-34, 1.8856046807012275e-51), + qd_real( 1.2241067519921620e-01, 2.8354501489965335e-18, + 1.8151655751493305e-34, -2.8716592177915192e-51), + qd_real( 1.2545498341154623e-01, 4.8686751763148235e-18, + 5.9878105258097936e-35, -3.3534629098722107e-51), + qd_real( 1.2849811079379317e-01, 3.8198603954988802e-18, + -1.8627501455947798e-34, -2.4308161133527791e-51), + qd_real( 1.3154002870288312e-01, -5.0039708262213813e-18, + -1.2983004159245552e-34, -4.6872034915794122e-51), + qd_real( 1.3458070850712620e-01, -9.1670359171480699e-18, + 1.5916493007073973e-34, 4.0237002484366833e-51), + qd_real( 1.3762012158648604e-01, 6.6253255866774482e-18, + -2.3746583031401459e-34, -9.3703876173093250e-52), + qd_real( 1.4065823933284924e-01, -7.9193932965524741e-18, + 6.0972464202108397e-34, 2.4566623241035797e-50), + qd_real( 1.4369503315029444e-01, 1.1472723016618666e-17, + -5.1884954557576435e-35, -4.2220684832186607e-51), + qd_real( 1.4673047445536175e-01, 3.7269471470465677e-18, + 3.7352398151250827e-34, -4.0881822289508634e-51), + qd_real( 1.4976453467732151e-01, 8.0812114131285151e-18, + 1.2979142554917325e-34, 9.9380667487736254e-51), + qd_real( 1.5279718525844344e-01, -7.6313573938416838e-18, + 5.7714690450284125e-34, -3.7731132582986687e-50), + qd_real( 1.5582839765426523e-01, 3.0351307187678221e-18, + -1.0976942315176184e-34, 7.8734647685257867e-51), + qd_real( 1.5885814333386145e-01, -4.0163200573859079e-18, + -9.2840580257628812e-35, -2.8567420029274875e-51), + qd_real( 1.6188639378011183e-01, 1.1850519643573528e-17, + -5.0440990519162957e-34, 3.0510028707928009e-50), + qd_real( 1.6491312048996992e-01, -7.0405288319166738e-19, + 3.3211107491245527e-35, 8.6663299254686031e-52), + qd_real( 1.6793829497473117e-01, 5.4284533721558139e-18, + -3.3263339336181369e-34, -1.8536367335123848e-50), + qd_real( 1.7096188876030122e-01, 9.1919980181759094e-18, + -6.7688743940982606e-34, -1.0377711384318389e-50), + qd_real( 1.7398387338746382e-01, 5.8151994618107928e-18, + -1.6751014298301606e-34, -6.6982259797164963e-51), + qd_real( 1.7700422041214875e-01, 6.7329300635408167e-18, + 2.8042736644246623e-34, 3.6786888232793599e-51), + qd_real( 1.8002290140569951e-01, 7.9701826047392143e-18, + -7.0765920110524977e-34, 1.9622512608461784e-50), + qd_real( 1.8303988795514095e-01, 7.7349918688637383e-18, + -4.4803769968145083e-34, 1.1201148793328890e-50), + qd_real( 1.8605515166344666e-01, -1.2564893007679552e-17, + 7.5953844248530810e-34, -3.8471695132415039e-51), + qd_real( 1.8906866414980622e-01, -7.6208955803527778e-18, + -4.4792298656662981e-34, -4.4136824096645007e-50), + qd_real( 1.9208039704989244e-01, 4.3348343941174903e-18, + -2.3404121848139937e-34, 1.5789970962611856e-50), + qd_real( 1.9509032201612828e-01, -7.9910790684617313e-18, + 6.1846270024220713e-34, -3.5840270918032937e-50), + qd_real( 1.9809841071795359e-01, -1.8434411800689445e-18, + 1.4139031318237285e-34, 1.0542811125343809e-50), + qd_real( 2.0110463484209190e-01, 1.1010032669300739e-17, + -3.9123576757413791e-34, 2.4084852500063531e-51), + qd_real( 2.0410896609281687e-01, 6.0941297773957752e-18, + -2.8275409970449641e-34, 4.6101008563532989e-51), + qd_real( 2.0711137619221856e-01, -1.0613362528971356e-17, + 2.2456805112690884e-34, 1.3483736125280904e-50), + qd_real( 2.1011183688046961e-01, 1.1561548476512844e-17, + 6.0355905610401254e-34, 3.3329909618405675e-50), + qd_real( 2.1311031991609136e-01, 1.2031873821063860e-17, + -3.4142699719695635e-34, -1.2436262780241778e-50), + qd_real( 2.1610679707621952e-01, -1.0111196082609117e-17, + 7.2789545335189643e-34, -2.9347540365258610e-50), + qd_real( 2.1910124015686980e-01, -3.6513812299150776e-19, + -2.3359499418606442e-35, 3.1785298198458653e-52), + qd_real( 2.2209362097320354e-01, -3.0337210995812162e-18, + 6.6654668033632998e-35, 2.0110862322656942e-51), + qd_real( 2.2508391135979283e-01, 3.9507040822556510e-18, + 2.4287993958305375e-35, 5.6662797513020322e-52), + qd_real( 2.2807208317088573e-01, 8.2361837339258012e-18, + 6.9786781316397937e-34, -6.4122962482639504e-51), + qd_real( 2.3105810828067111e-01, 1.0129787149761869e-17, + -6.9359234615816044e-34, -2.8877355604883782e-50), + qd_real( 2.3404195858354343e-01, -6.9922402696101173e-18, + -5.7323031922750280e-34, 5.3092579966872727e-51), + qd_real( 2.3702360599436720e-01, 8.8544852285039918e-18, + 1.3588480826354134e-34, 1.0381022520213867e-50), + qd_real( 2.4000302244874150e-01, -1.2137758975632164e-17, + -2.6448807731703891e-34, -1.9929733800670473e-51), + qd_real( 2.4298017990326390e-01, -8.7514315297196632e-18, + -6.5723260373079431e-34, -1.0333158083172177e-50), + qd_real( 2.4595505033579462e-01, -1.1129044052741832e-17, + 4.3805998202883397e-34, 1.2219399554686291e-50), + qd_real( 2.4892760574572018e-01, -8.1783436100020990e-18, + 5.5666875261111840e-34, 3.8080473058748167e-50), + qd_real( 2.5189781815421697e-01, -1.7591436032517039e-17, + -1.0959681232525285e-33, 5.6209426020232456e-50), + qd_real( 2.5486565960451457e-01, -1.3602299806901461e-19, + -6.0073844642762535e-36, -3.0072751311893878e-52), + qd_real( 2.5783110216215899e-01, 1.8480038630879957e-17, + 3.3201664714047599e-34, -5.5547819290576764e-51), + qd_real( 2.6079411791527551e-01, 4.2721420983550075e-18, + 5.6782126934777920e-35, 3.1428338084365397e-51), + qd_real( 2.6375467897483140e-01, -1.8837947680038700e-17, + 1.3720129045754794e-33, -8.2763406665966033e-50), + qd_real( 2.6671275747489837e-01, 2.0941222578826688e-17, + -1.1303466524727989e-33, 1.9954224050508963e-50), + qd_real( 2.6966832557291509e-01, 1.5765657618133259e-17, + -6.9696142173370086e-34, -4.0455346879146776e-50), + qd_real( 2.7262135544994898e-01, 7.8697166076387850e-18, + 6.6179388602933372e-35, -2.7642903696386267e-51), + qd_real( 2.7557181931095814e-01, 1.9320328962556582e-17, + 1.3932094180100280e-33, 1.3617253920018116e-50), + qd_real( 2.7851968938505312e-01, -1.0030273719543544e-17, + 7.2592115325689254e-34, -1.0068516296655851e-50), + qd_real( 2.8146493792575800e-01, -1.2322299641274009e-17, + -1.0564788706386435e-34, 7.5137424251265885e-51), + qd_real( 2.8440753721127182e-01, 2.2209268510661475e-17, + -9.1823095629523708e-34, -5.2192875308892218e-50), + qd_real( 2.8734745954472951e-01, 1.5461117367645717e-17, + -6.3263973663444076e-34, -2.2982538416476214e-50), + qd_real( 2.9028467725446239e-01, -1.8927978707774251e-17, + 1.1522953157142315e-33, 7.4738655654716596e-50), + qd_real( 2.9321916269425863e-01, 2.2385430811901833e-17, + 1.3662484646539680e-33, -4.2451325253996938e-50), + qd_real( 2.9615088824362384e-01, -2.0220736360876938e-17, + -7.9252212533920413e-35, -2.8990577729572470e-51), + qd_real( 2.9907982630804048e-01, 1.6701181609219447e-18, + 8.6091151117316292e-35, 3.9931286230012102e-52), + qd_real( 3.0200594931922808e-01, -1.7167666235262474e-17, + 2.3336182149008069e-34, 8.3025334555220004e-51), + qd_real( 3.0492922973540243e-01, -2.2989033898191262e-17, + -1.4598901099661133e-34, 3.7760487693121827e-51), + qd_real( 3.0784964004153487e-01, 2.7074088527245185e-17, + 1.2568858206899284e-33, 7.2931815105901645e-50), + qd_real( 3.1076715274961147e-01, 2.0887076364048513e-17, + -3.0130590791065942e-34, 1.3876739009935179e-51), + qd_real( 3.1368174039889146e-01, 1.4560447299968912e-17, + 3.6564186898011595e-34, 1.1654264734999375e-50), + qd_real( 3.1659337555616585e-01, 2.1435292512726283e-17, + 1.2338169231377316e-33, 3.3963542100989293e-50), + qd_real( 3.1950203081601569e-01, -1.3981562491096626e-17, + 8.1730000697411350e-34, -7.7671096270210952e-50), + qd_real( 3.2240767880106985e-01, -4.0519039937959398e-18, + 3.7438302780296796e-34, 8.7936731046639195e-51), + qd_real( 3.2531029216226293e-01, 7.9171249463765892e-18, + -6.7576622068146391e-35, 2.3021655066929538e-51), + qd_real( 3.2820984357909255e-01, -2.6693140719641896e-17, + 7.8928851447534788e-34, 2.5525163821987809e-51), + qd_real( 3.3110630575987643e-01, -2.7469465474778694e-17, + -1.3401245916610206e-33, 6.5531762489976163e-50), + qd_real( 3.3399965144200938e-01, 2.2598986806288142e-17, + 7.8063057192586115e-34, 2.0427600895486683e-50), + qd_real( 3.3688985339222005e-01, -4.2000940033475092e-19, + -2.9178652969985438e-36, -1.1597376437036749e-52), + qd_real( 3.3977688440682685e-01, 6.6028679499418282e-18, + 1.2575009988669683e-34, 2.5569067699008304e-51), + qd_real( 3.4266071731199438e-01, 1.9261518449306319e-17, + -9.2754189135990867e-34, 8.5439996687390166e-50), + qd_real( 3.4554132496398904e-01, 2.7251143672916123e-17, + 7.0138163601941737e-34, -1.4176292197454015e-50), + qd_real( 3.4841868024943456e-01, 3.6974420514204918e-18, + 3.5532146878499996e-34, 1.9565462544501322e-50), + qd_real( 3.5129275608556715e-01, -2.2670712098795844e-17, + -1.6994216673139631e-34, -1.2271556077284517e-50), + qd_real( 3.5416352542049040e-01, -1.6951763305764860e-17, + 1.2772331777814617e-33, -3.3703785435843310e-50), + qd_real( 3.5703096123343003e-01, -4.8218191137919166e-19, + -4.1672436994492361e-35, -7.1531167149364352e-52), + qd_real( 3.5989503653498817e-01, -1.7601687123839282e-17, + 1.3375125473046791e-33, 7.9467815593584340e-50), + qd_real( 3.6275572436739723e-01, -9.1668352663749849e-18, + -7.4317843956936735e-34, -2.0199582511804564e-50), + qd_real( 3.6561299780477385e-01, 1.6217898770457546e-17, + 1.1286970151961055e-33, -7.1825287318139010e-50), + qd_real( 3.6846682995337232e-01, 1.0463640796159268e-17, + 2.0554984738517304e-35, 1.0441861305618769e-51), + qd_real( 3.7131719395183754e-01, 3.4749239648238266e-19, + -7.5151053042866671e-37, -2.8153468438650851e-53), + qd_real( 3.7416406297145799e-01, 8.0114103761962118e-18, + 5.3429599813406052e-34, 1.0351378796539210e-50), + qd_real( 3.7700741021641826e-01, -2.7255302041956930e-18, + 6.3646586445018137e-35, 8.3048657176503559e-52), + qd_real( 3.7984720892405116e-01, 9.9151305855172370e-18, + 4.8761409697224886e-34, 1.4025084000776705e-50), + qd_real( 3.8268343236508978e-01, -1.0050772696461588e-17, + -2.0605316302806695e-34, -1.2717724698085205e-50), + qd_real( 3.8551605384391885e-01, 1.5177665396472313e-17, + 1.4198230518016535e-33, 5.8955167159904235e-50), + qd_real( 3.8834504669882630e-01, -1.0053770598398717e-17, + 7.5942999255057131e-34, -3.1967974046654219e-50), + qd_real( 3.9117038430225387e-01, 1.7997787858243995e-17, + -1.0613482402609856e-33, -5.4582148817791032e-50), + qd_real( 3.9399204006104810e-01, 9.7649241641239336e-18, + -2.1233599441284617e-34, -5.5529836795340819e-51), + qd_real( 3.9680998741671031e-01, 2.0545063670840126e-17, + 6.1347058801922842e-34, 1.0733788150636430e-50), + qd_real( 3.9962419984564684e-01, -1.5065497476189372e-17, + -9.9653258881867298e-34, -5.7524323712725355e-50), + qd_real( 4.0243465085941843e-01, 1.0902619339328270e-17, + 7.3998528125989765e-34, 2.2745784806823499e-50), + qd_real( 4.0524131400498986e-01, 9.9111401942899884e-18, + -2.5169070895434648e-34, 9.2772984818436573e-53), + qd_real( 4.0804416286497869e-01, -7.0006015137351311e-18, + -1.4108207334268228e-34, 1.5175546997577136e-52), + qd_real( 4.1084317105790397e-01, -2.4219835190355499e-17, + -1.1418902925313314e-33, -2.0996843165093468e-50), + qd_real( 4.1363831223843456e-01, -1.0393984940597871e-17, + -1.1481681174503880e-34, -2.0281052851028680e-51), + qd_real( 4.1642956009763721e-01, -2.5475580413131732e-17, + -3.4482678506112824e-34, 7.1788619351865480e-51), + qd_real( 4.1921688836322396e-01, -4.2232463750110590e-18, + -3.6053023045255790e-34, -2.2209673210025631e-50), + qd_real( 4.2200027079979968e-01, 4.3543266994128527e-18, + 3.1734310272251190e-34, -1.3573247980738668e-50), + qd_real( 4.2477968120910881e-01, 2.7462312204277281e-17, + -4.6552847802111948e-34, 6.5961781099193122e-51), + qd_real( 4.2755509343028208e-01, 9.4111898162954726e-18, + -1.7446682426598801e-34, -2.2054492626480169e-51), + qd_real( 4.3032648134008261e-01, 2.2259686974092690e-17, + 8.5972591314085075e-34, -2.9420897889003020e-50), + qd_real( 4.3309381885315196e-01, 1.1224283329847517e-17, + 5.3223748041075651e-35, 5.3926192627014212e-51), + qd_real( 4.3585707992225547e-01, 1.6230515450644527e-17, + -6.4371449063579431e-35, -6.9102436481386757e-51), + qd_real( 4.3861623853852766e-01, -2.0883315831075090e-17, + -1.4259583540891877e-34, 6.3864763590657077e-52), + qd_real( 4.4137126873171667e-01, 2.2360783886964969e-17, + 1.1864769603515770e-34, -3.8087003266189232e-51), + qd_real( 4.4412214457042926e-01, -2.4218874422178315e-17, + 2.2205230838703907e-34, 9.2133035911356258e-51), + qd_real( 4.4686884016237421e-01, -1.9222136142309382e-17, + -4.4425678589732049e-35, -1.3673609292149535e-51), + qd_real( 4.4961132965460660e-01, 4.8831924232035243e-18, + 2.7151084498191381e-34, -1.5653993171613154e-50), + qd_real( 4.5234958723377089e-01, -1.4827977472196122e-17, + -7.6947501088972324e-34, 1.7656856882031319e-50), + qd_real( 4.5508358712634384e-01, -1.2379906758116472e-17, + 5.5289688955542643e-34, -8.5382312840209386e-51), + qd_real( 4.5781330359887723e-01, -8.4554254922295949e-18, + -6.3770394246764263e-34, 3.1778253575564249e-50), + qd_real( 4.6053871095824001e-01, 1.8488777492177872e-17, + -1.0527732154209725e-33, 3.3235593490947102e-50), + qd_real( 4.6325978355186020e-01, -7.3514924533231707e-18, + 6.7175396881707035e-34, 3.9594127612123379e-50), + qd_real( 4.6597649576796618e-01, -3.3023547778235135e-18, + 3.4904677050476886e-35, 3.4483855263874246e-51), + qd_real( 4.6868882203582796e-01, -2.2949251681845054e-17, + -1.1364757641823658e-33, 6.8840522501918612e-50), + qd_real( 4.7139673682599764e-01, 6.5166781360690130e-18, + 2.9457546966235984e-34, -6.2159717738836630e-51), + qd_real( 4.7410021465055002e-01, -8.1451601548978075e-18, + -3.4789448555614422e-34, -1.1681943974658508e-50), + qd_real( 4.7679923006332214e-01, -1.0293515338305794e-17, + -3.6582045008369952e-34, 1.7424131479176475e-50), + qd_real( 4.7949375766015301e-01, 1.8419999662684771e-17, + -1.3040838621273312e-33, 1.0977131822246471e-50), + qd_real( 4.8218377207912277e-01, -2.5861500925520442e-17, + -6.2913197606500007e-36, 4.0802359808684726e-52), + qd_real( 4.8486924800079112e-01, -1.8034004203262245e-17, + -3.5244276906958044e-34, -1.7138318654749246e-50), + qd_real( 4.8755016014843594e-01, 1.4231090931273653e-17, + -1.8277733073262697e-34, -1.5208291790429557e-51), + qd_real( 4.9022648328829116e-01, -5.1496145643440404e-18, + -3.6903027405284104e-34, 1.5172940095151304e-50), + qd_real( 4.9289819222978404e-01, -1.0257831676562186e-18, + 6.9520817760885069e-35, -2.4260961214090389e-51), + qd_real( 4.9556526182577254e-01, -9.4323241942365362e-18, + 3.1212918657699143e-35, 4.2009072375242736e-52), + qd_real( 4.9822766697278187e-01, -1.6126383830540798e-17, + -1.5092897319298871e-33, 1.1049298890895917e-50), + qd_real( 5.0088538261124083e-01, -3.9604015147074639e-17, + -2.2208395201898007e-33, 1.3648202735839417e-49), + qd_real( 5.0353838372571758e-01, -1.6731308204967497e-17, + -1.0140233644074786e-33, 4.0953071937671477e-50), + qd_real( 5.0618664534515534e-01, -4.8321592986493711e-17, + 9.2858107226642252e-34, 4.2699802401037005e-50), + qd_real( 5.0883014254310699e-01, 4.7836968268014130e-17, + -1.0727022928806035e-33, 2.7309374513672757e-50), + qd_real( 5.1146885043797041e-01, -1.3088001221007579e-17, + 4.0929033363366899e-34, -3.7952190153477926e-50), + qd_real( 5.1410274419322177e-01, -4.5712707523615624e-17, + 1.5488279442238283e-33, -2.5853959305521130e-50), + qd_real( 5.1673179901764987e-01, 8.3018617233836515e-18, + 5.8251027467695202e-34, -2.2812397190535076e-50), + qd_real( 5.1935599016558964e-01, -5.5331248144171145e-17, + -3.1628375609769026e-35, -2.4091972051188571e-51), + qd_real( 5.2197529293715439e-01, -4.6555795692088883e-17, + 4.6378980936850430e-34, -3.3470542934689532e-51), + qd_real( 5.2458968267846895e-01, -4.3068869040082345e-17, + -4.2013155291932055e-34, -1.5096069926700274e-50), + qd_real( 5.2719913478190139e-01, -4.2202983480560619e-17, + 8.5585916184867295e-34, 7.9974339336732307e-50), + qd_real( 5.2980362468629472e-01, -4.8067841706482342e-17, + 5.8309721046630296e-34, -8.9740761521756660e-51), + qd_real( 5.3240312787719801e-01, -4.1020306135800895e-17, + -1.9239996374230821e-33, -1.5326987913812184e-49), + qd_real( 5.3499761988709726e-01, -5.3683132708358134e-17, + -1.3900569918838112e-33, 2.7154084726474092e-50), + qd_real( 5.3758707629564551e-01, -2.2617365388403054e-17, + -5.9787279033447075e-34, 3.1204419729043625e-51), + qd_real( 5.4017147272989285e-01, 2.7072447965935839e-17, + 1.1698799709213829e-33, -5.9094668515881500e-50), + qd_real( 5.4275078486451589e-01, 1.7148261004757101e-17, + -1.3525905925200870e-33, 4.9724411290727323e-50), + qd_real( 5.4532498842204646e-01, -4.1517817538384258e-17, + -1.5318930219385941e-33, 6.3629921101413974e-50), + qd_real( 5.4789405917310019e-01, -2.4065878297113363e-17, + -3.5639213669362606e-36, -2.6013270854271645e-52), + qd_real( 5.5045797293660481e-01, -8.3319903015807663e-18, + -2.3058454035767633e-34, -2.1611290432369010e-50), + qd_real( 5.5301670558002758e-01, -4.7061536623798204e-17, + -1.0617111545918056e-33, -1.6196316144407379e-50), + qd_real( 5.5557023301960218e-01, 4.7094109405616768e-17, + -2.0640520383682921e-33, 1.2290163188567138e-49), + qd_real( 5.5811853122055610e-01, 1.3481176324765226e-17, + -5.5016743873011438e-34, -2.3484822739335416e-50), + qd_real( 5.6066157619733603e-01, -7.3956418153476152e-18, + 3.9680620611731193e-34, 3.1995952200836223e-50), + qd_real( 5.6319934401383409e-01, 2.3835775146854829e-17, + 1.3511793173769814e-34, 9.3201311581248143e-51), + qd_real( 5.6573181078361323e-01, -3.4096079596590466e-17, + -1.7073289744303546e-33, 8.9147089975404507e-50), + qd_real( 5.6825895267013160e-01, -5.0935673642769248e-17, + -1.6274356351028249e-33, 9.8183151561702966e-51), + qd_real( 5.7078074588696726e-01, 2.4568151455566208e-17, + -1.2844481247560350e-33, -1.8037634376936261e-50), + qd_real( 5.7329716669804220e-01, 8.5176611669306400e-18, + -6.4443208788026766e-34, 2.2546105543273003e-50), + qd_real( 5.7580819141784534e-01, -3.7909495458942734e-17, + -2.7433738046854309e-33, 1.1130841524216795e-49), + qd_real( 5.7831379641165559e-01, -2.6237691512372831e-17, + 1.3679051680738167e-33, -3.1409808935335900e-50), + qd_real( 5.8081395809576453e-01, 1.8585338586613408e-17, + 2.7673843114549181e-34, 1.9605349619836937e-50), + qd_real( 5.8330865293769829e-01, 3.4516601079044858e-18, + 1.8065977478946306e-34, -6.3953958038544646e-51), + qd_real( 5.8579785745643886e-01, -3.7485501964311294e-18, + 2.7965403775536614e-34, -7.1816936024157202e-51), + qd_real( 5.8828154822264533e-01, -2.9292166725006846e-17, + -2.3744954603693934e-33, -1.1571631191512480e-50), + qd_real( 5.9075970185887428e-01, -4.7013584170659542e-17, + 2.4808417611768356e-33, 1.2598907673643198e-50), + qd_real( 5.9323229503979980e-01, 1.2892320944189053e-17, + 5.3058364776359583e-34, 4.1141674699390052e-50), + qd_real( 5.9569930449243336e-01, -1.3438641936579467e-17, + -6.7877687907721049e-35, -5.6046937531684890e-51), + qd_real( 5.9816070699634227e-01, 3.8801885783000657e-17, + -1.2084165858094663e-33, -4.0456610843430061e-50), + qd_real( 6.0061647938386897e-01, -4.6398198229461932e-17, + -1.6673493003710801e-33, 5.1982824378491445e-50), + qd_real( 6.0306659854034816e-01, 3.7323357680559650e-17, + 2.7771920866974305e-33, -1.6194229649742458e-49), + qd_real( 6.0551104140432555e-01, -3.1202672493305677e-17, + 1.2761267338680916e-33, -4.0859368598379647e-50), + qd_real( 6.0794978496777363e-01, 3.5160832362096660e-17, + -2.5546242776778394e-34, -1.4085313551220694e-50), + qd_real( 6.1038280627630948e-01, -2.2563265648229169e-17, + 1.3185575011226730e-33, 8.2316691420063460e-50), + qd_real( 6.1281008242940971e-01, -4.2693476568409685e-18, + 2.5839965886650320e-34, 1.6884412005622537e-50), + qd_real( 6.1523159058062682e-01, 2.6231417767266950e-17, + -1.4095366621106716e-33, 7.2058690491304558e-50), + qd_real( 6.1764730793780398e-01, -4.7478594510902452e-17, + -7.2986558263123996e-34, -3.0152327517439154e-50), + qd_real( 6.2005721176328921e-01, -2.7983410837681118e-17, + 1.1649951056138923e-33, -5.4539089117135207e-50), + qd_real( 6.2246127937414997e-01, 5.2940728606573002e-18, + -4.8486411215945827e-35, 1.2696527641980109e-52), + qd_real( 6.2485948814238634e-01, 3.3671846037243900e-17, + -2.7846053391012096e-33, 5.6102718120012104e-50), + qd_real( 6.2725181549514408e-01, 3.0763585181253225e-17, + 2.7068930273498138e-34, -1.1172240309286484e-50), + qd_real( 6.2963823891492698e-01, 4.1115334049626806e-17, + -1.9167473580230747e-33, 1.1118424028161730e-49), + qd_real( 6.3201873593980906e-01, -4.0164942296463612e-17, + -7.2208643641736723e-34, 3.7828920470544344e-50), + qd_real( 6.3439328416364549e-01, 1.0420901929280035e-17, + 4.1174558929280492e-34, -1.4464152986630705e-51), + qd_real( 6.3676186123628420e-01, 3.1419048711901611e-17, + -2.2693738415126449e-33, -1.6023584204297388e-49), + qd_real( 6.3912444486377573e-01, 1.2416796312271043e-17, + -6.2095419626356605e-34, 2.7762065999506603e-50), + qd_real( 6.4148101280858316e-01, -9.9883430115943310e-18, + 4.1969230376730128e-34, 5.6980543799257597e-51), + qd_real( 6.4383154288979150e-01, -3.2084798795046886e-17, + -1.2595311907053305e-33, -4.0205885230841536e-50), + qd_real( 6.4617601298331639e-01, -2.9756137382280815e-17, + -1.0275370077518259e-33, 8.0852478665893014e-51), + qd_real( 6.4851440102211244e-01, 3.9870270313386831e-18, + 1.9408388509540788e-34, -5.1798420636193190e-51), + qd_real( 6.5084668499638088e-01, 3.9714670710500257e-17, + 2.9178546787002963e-34, 3.8140635508293278e-51), + qd_real( 6.5317284295377676e-01, 8.5695642060026238e-18, + -6.9165322305070633e-34, 2.3873751224185395e-50), + qd_real( 6.5549285299961535e-01, 3.5638734426385005e-17, + 1.2695365790889811e-33, 4.3984952865412050e-50), + qd_real( 6.5780669329707864e-01, 1.9580943058468545e-17, + -1.1944272256627192e-33, 2.8556402616436858e-50), + qd_real( 6.6011434206742048e-01, -1.3960054386823638e-19, + 6.1515777931494047e-36, 5.3510498875622660e-52), + qd_real( 6.6241577759017178e-01, -2.2615508885764591e-17, + 5.0177050318126862e-34, 2.9162532399530762e-50), + qd_real( 6.6471097820334490e-01, -3.6227793598034367e-17, + -9.0607934765540427e-34, 3.0917036342380213e-50), + qd_real( 6.6699992230363747e-01, 3.5284364997428166e-17, + -1.0382057232458238e-33, 7.3812756550167626e-50), + qd_real( 6.6928258834663612e-01, -5.4592652417447913e-17, + -2.5181014709695152e-33, -1.6867875999437174e-49), + qd_real( 6.7155895484701844e-01, -4.0489037749296692e-17, + 3.1995835625355681e-34, -1.4044414655670960e-50), + qd_real( 6.7382900037875604e-01, 2.3091901236161086e-17, + 5.7428037192881319e-34, 1.1240668354625977e-50), + qd_real( 6.7609270357531592e-01, 3.7256902248049466e-17, + 1.7059417895764375e-33, 9.7326347795300652e-50), + qd_real( 6.7835004312986147e-01, 1.8302093041863122e-17, + 9.5241675746813072e-34, 5.0328101116133503e-50), + qd_real( 6.8060099779545302e-01, 2.8473293354522047e-17, + 4.1331805977270903e-34, 4.2579030510748576e-50), + qd_real( 6.8284554638524808e-01, -1.2958058061524531e-17, + 1.8292386959330698e-34, 3.4536209116044487e-51), + qd_real( 6.8508366777270036e-01, 2.5948135194645137e-17, + -8.5030743129500702e-34, -6.9572086141009930e-50), + qd_real( 6.8731534089175916e-01, -5.5156158714917168e-17, + 1.1896489854266829e-33, -7.8505896218220662e-51), + qd_real( 6.8954054473706694e-01, -1.5889323294806790e-17, + 9.1242356240205712e-34, 3.8315454152267638e-50), + qd_real( 6.9175925836415775e-01, 2.7406078472410668e-17, + 1.3286508943202092e-33, 1.0651869129580079e-51), + qd_real( 6.9397146088965400e-01, 7.4345076956280137e-18, + 7.5061528388197460e-34, -1.5928000240686583e-50), + qd_real( 6.9617713149146299e-01, -4.1224081213582889e-17, + -3.1838716762083291e-35, -3.9625587412119131e-51), + qd_real( 6.9837624940897280e-01, 4.8988282435667768e-17, + 1.9134010413244152e-33, 2.6161153243793989e-50), + qd_real( 7.0056879394324834e-01, 3.1027960192992922e-17, + 9.5638250509179997e-34, 4.5896916138107048e-51), + qd_real( 7.0275474445722530e-01, 2.5278294383629822e-18, + -8.6985561210674942e-35, -5.6899862307812990e-51), + qd_real( 7.0493408037590488e-01, 2.7608725585748502e-17, + 2.9816599471629137e-34, 1.1533044185111206e-50), + qd_real( 7.0710678118654757e-01, -4.8336466567264567e-17, + 2.0693376543497068e-33, 2.4677734957341755e-50) +}; + +static const qd_real cos_table [] = { + qd_real( 9.9999529380957619e-01, -1.9668064285322189e-17, + -6.3053955095883481e-34, 5.3266110855726731e-52), + qd_real( 9.9998117528260111e-01, 3.3568103522895585e-17, + -1.4740132559368063e-35, 9.8603097594755596e-52), + qd_real( 9.9995764455196390e-01, -3.1527836866647287e-17, + 2.6363251186638437e-33, 1.0007504815488399e-49), + qd_real( 9.9992470183914450e-01, 3.7931082512668012e-17, + -8.5099918660501484e-35, -4.9956973223295153e-51), + qd_real( 9.9988234745421256e-01, -3.5477814872408538e-17, + 1.7102001035303974e-33, -1.0725388519026542e-49), + qd_real( 9.9983058179582340e-01, 1.8825140517551119e-17, + -5.1383513457616937e-34, -3.8378827995403787e-50), + qd_real( 9.9976940535121528e-01, 4.2681177032289012e-17, + 1.9062302359737099e-33, -6.0221153262881160e-50), + qd_real( 9.9969881869620425e-01, -2.9851486403799753e-17, + -1.9084787370733737e-33, 5.5980260344029202e-51), + qd_real( 9.9961882249517864e-01, -4.1181965521424734e-17, + 2.0915365593699916e-33, 8.1403390920903734e-50), + qd_real( 9.9952941750109314e-01, 2.0517917823755591e-17, + -4.7673802585706520e-34, -2.9443604198656772e-50), + qd_real( 9.9943060455546173e-01, 3.9644497752257798e-17, + -2.3757223716722428e-34, -1.2856759011361726e-51), + qd_real( 9.9932238458834954e-01, -4.2858538440845682e-17, + 3.3235101605146565e-34, -8.3554272377057543e-51), + qd_real( 9.9920475861836389e-01, 9.1796317110385693e-18, + 5.5416208185868570e-34, 8.0267046717615311e-52), + qd_real( 9.9907772775264536e-01, 2.1419007653587032e-17, + -7.9048203318529618e-34, -5.3166296181112712e-50), + qd_real( 9.9894129318685687e-01, -2.0610641910058638e-17, + -1.2546525485913485e-33, -7.5175888806157064e-50), + qd_real( 9.9879545620517241e-01, -1.2291693337075465e-17, + 2.4468446786491271e-34, 1.0723891085210268e-50), + qd_real( 9.9864021818026527e-01, -4.8690254312923302e-17, + -2.9470881967909147e-34, -1.3000650761346907e-50), + qd_real( 9.9847558057329477e-01, -2.2002931182778795e-17, + -1.2371509454944992e-33, -2.4911225131232065e-50), + qd_real( 9.9830154493389289e-01, -5.1869402702792278e-17, + 1.0480195493633452e-33, -2.8995649143155511e-50), + qd_real( 9.9811811290014918e-01, 2.7935487558113833e-17, + 2.4423341255830345e-33, -6.7646699175334417e-50), + qd_real( 9.9792528619859600e-01, 1.7143659778886362e-17, + 5.7885840902887460e-34, -9.2601432603894597e-51), + qd_real( 9.9772306664419164e-01, -2.6394475274898721e-17, + -1.6176223087661783e-34, -9.9924942889362281e-51), + qd_real( 9.9751145614030345e-01, 5.6007205919806937e-18, + -5.9477673514685690e-35, -1.4166807162743627e-54), + qd_real( 9.9729045667869021e-01, 9.1647695371101735e-18, + 6.7824134309739296e-34, -8.6191392795543357e-52), + qd_real( 9.9706007033948296e-01, 1.6734093546241963e-17, + -1.3169951440780028e-33, 1.0311048767952477e-50), + qd_real( 9.9682029929116567e-01, 4.7062820708615655e-17, + 2.8412041076474937e-33, -8.0006155670263622e-50), + qd_real( 9.9657114579055484e-01, 1.1707179088390986e-17, + -7.5934413263024663e-34, 2.8474848436926008e-50), + qd_real( 9.9631261218277800e-01, 1.1336497891624735e-17, + 3.4002458674414360e-34, 7.7419075921544901e-52), + qd_real( 9.9604470090125197e-01, 2.2870031707670695e-17, + -3.9184839405013148e-34, -3.7081260416246375e-50), + qd_real( 9.9576741446765982e-01, -2.3151908323094359e-17, + -1.6306512931944591e-34, -1.5925420783863192e-51), + qd_real( 9.9548075549192694e-01, 3.2084621412226554e-18, + -4.9501292146013023e-36, -2.7811428850878516e-52), + qd_real( 9.9518472667219693e-01, -4.2486913678304410e-17, + 1.3315510772504614e-33, 6.7927987417051888e-50), + qd_real( 9.9487933079480562e-01, 4.2130813284943662e-18, + -4.2062597488288452e-35, 2.5157064556087620e-51), + qd_real( 9.9456457073425542e-01, 3.6745069641528058e-17, + -3.0603304105471010e-33, 1.0397872280487526e-49), + qd_real( 9.9424044945318790e-01, 4.4129423472462673e-17, + -3.0107231708238066e-33, 7.4201582906861892e-50), + qd_real( 9.9390697000235606e-01, -1.8964849471123746e-17, + -1.5980853777937752e-35, -8.5374807150597082e-52), + qd_real( 9.9356413552059530e-01, 2.9752309927797428e-17, + -4.5066707331134233e-34, -3.3548191633805036e-50), + qd_real( 9.9321194923479450e-01, 3.3096906261272262e-17, + 1.5592811973249567e-33, 1.4373977733253592e-50), + qd_real( 9.9285041445986510e-01, -1.4094517733693302e-17, + -1.1954558131616916e-33, 1.8761873742867983e-50), + qd_real( 9.9247953459870997e-01, 3.1093055095428906e-17, + -1.8379594757818019e-33, -3.9885758559381314e-51), + qd_real( 9.9209931314219180e-01, -3.9431926149588778e-17, + -6.2758062911047230e-34, -1.2960929559212390e-50), + qd_real( 9.9170975366909953e-01, -2.3372891311883661e-18, + 2.7073298824968591e-35, -1.2569459441802872e-51), + qd_real( 9.9131085984611544e-01, -2.5192111583372105e-17, + -1.2852471567380887e-33, 5.2385212584310483e-50), + qd_real( 9.9090263542778001e-01, 1.5394565094566704e-17, + -1.0799984133184567e-33, 2.7451115960133595e-51), + qd_real( 9.9048508425645709e-01, -5.5411437553780867e-17, + -1.4614017210753585e-33, -3.8339374397387620e-50), + qd_real( 9.9005821026229712e-01, -1.7055485906233963e-17, + 1.3454939685758777e-33, 7.3117589137300036e-50), + qd_real( 9.8962201746320089e-01, -5.2398217968132530e-17, + 1.3463189211456219e-33, 5.8021640554894872e-50), + qd_real( 9.8917650996478101e-01, -4.0987309937047111e-17, + -4.4857560552048437e-34, -3.9414504502871125e-50), + qd_real( 9.8872169196032378e-01, -1.0976227206656125e-17, + 3.2311342577653764e-34, 9.6367946583575041e-51), + qd_real( 9.8825756773074946e-01, 2.7030607784372632e-17, + 7.7514866488601377e-35, 2.1019644956864938e-51), + qd_real( 9.8778414164457218e-01, -2.3600693397159021e-17, + -1.2323283769707861e-33, 3.0130900716803339e-50), + qd_real( 9.8730141815785843e-01, -5.2332261255715652e-17, + -2.7937644333152473e-33, 1.2074160567958408e-49), + qd_real( 9.8680940181418553e-01, -5.0287214351061075e-17, + -2.2681526238144461e-33, 4.4003694320169133e-50), + qd_real( 9.8630809724459867e-01, -2.1520877103013341e-17, + 1.1866528054187716e-33, -7.8532199199813836e-50), + qd_real( 9.8579750916756748e-01, -5.1439452979953012e-17, + 2.6276439309996725e-33, 7.5423552783286347e-50), + qd_real( 9.8527764238894122e-01, 2.3155637027900207e-17, + -7.5275971545764833e-34, 1.0582231660456094e-50), + qd_real( 9.8474850180190421e-01, 1.0548144061829957e-17, + 2.8786145266267306e-34, -3.6782210081466112e-51), + qd_real( 9.8421009238692903e-01, 4.7983922627050691e-17, + 2.2597419645070588e-34, 1.7573875814863400e-50), + qd_real( 9.8366241921173025e-01, 1.9864948201635255e-17, + -1.0743046281211033e-35, 1.7975662796558100e-52), + qd_real( 9.8310548743121629e-01, 4.2170007522888628e-17, + 8.2396265656440904e-34, -8.0803700139096561e-50), + qd_real( 9.8253930228744124e-01, 1.5149580813777224e-17, + -4.1802771422186237e-34, -2.2150174326226160e-50), + qd_real( 9.8196386910955524e-01, 2.1108443711513084e-17, + -1.5253013442896054e-33, -6.8388082079337969e-50), + qd_real( 9.8137919331375456e-01, 1.3428163260355633e-17, + -6.5294290469962986e-34, 2.7965412287456268e-51), + qd_real( 9.8078528040323043e-01, 1.8546939997825006e-17, + -1.0696564445530757e-33, 6.6668174475264961e-50), + qd_real( 9.8018213596811743e-01, -3.6801786963856159e-17, + 6.3245171387992842e-34, 1.8600176137175971e-50), + qd_real( 9.7956976568544052e-01, 1.5573991584990420e-17, + -1.3401066029782990e-33, -1.7263702199862149e-50), + qd_real( 9.7894817531906220e-01, -2.3817727961148053e-18, + -1.0694750370381661e-34, -8.2293047196087462e-51), + qd_real( 9.7831737071962765e-01, -2.1623082233344895e-17, + 1.0970403012028032e-33, 7.7091923099369339e-50), + qd_real( 9.7767735782450993e-01, 5.0514136167059628e-17, + -1.3254751701428788e-33, 7.0161254312124538e-50), + qd_real( 9.7702814265775439e-01, -4.3353875751555997e-17, + 5.4948839831535478e-34, -9.2755263105377306e-51), + qd_real( 9.7636973133002114e-01, 9.3093931526213780e-18, + -4.1184949155685665e-34, -3.1913926031393690e-50), + qd_real( 9.7570213003852857e-01, -2.5572556081259686e-17, + -9.3174244508942223e-34, -8.3675863211646863e-51), + qd_real( 9.7502534506699412e-01, 2.6642660651899135e-17, + 1.7819392739353853e-34, -3.3159625385648947e-51), + qd_real( 9.7433938278557586e-01, 2.3041221476151512e-18, + 1.0758686005031430e-34, 5.1074116432809478e-51), + qd_real( 9.7364424965081198e-01, -5.1729808691005871e-17, + -1.5508473005989887e-33, -1.6505125917675401e-49), + qd_real( 9.7293995220556018e-01, -3.1311211122281800e-17, + -2.6874087789006141e-33, -2.1652434818822145e-51), + qd_real( 9.7222649707893627e-01, 3.6461169785938221e-17, + 3.0309636883883133e-33, -1.2702716907967306e-51), + qd_real( 9.7150389098625178e-01, -7.9865421122289046e-18, + -4.3628417211263380e-34, 3.4307517798759352e-51), + qd_real( 9.7077214072895035e-01, -4.7992163325114922e-17, + 3.0347528910975783e-33, 8.5989199506479701e-50), + qd_real( 9.7003125319454397e-01, 1.8365300348428844e-17, + -1.4311097571944918e-33, 8.5846781998740697e-51), + qd_real( 9.6928123535654853e-01, -4.5663660261927896e-17, + 9.6147526917239387e-34, 8.1267605207871330e-51), + qd_real( 9.6852209427441727e-01, 4.9475074918244771e-17, + 2.8558738351911241e-33, 6.2948422316507461e-50), + qd_real( 9.6775383709347551e-01, -4.5512132825515820e-17, + -1.4127617988719093e-33, -8.4620609089704578e-50), + qd_real( 9.6697647104485207e-01, 3.8496228837337864e-17, + -5.3881631542745647e-34, -3.5221863171458959e-50), + qd_real( 9.6619000344541250e-01, 5.1298840401665493e-17, + 1.4564075904769808e-34, 1.0095973971377432e-50), + qd_real( 9.6539444169768940e-01, -2.3745389918392156e-17, + 5.9221515590053862e-34, -3.8811192556231094e-50), + qd_real( 9.6458979328981276e-01, -3.4189470735959786e-17, + 2.2982074155463522e-33, -4.5128791045607634e-50), + qd_real( 9.6377606579543984e-01, 2.6463950561220029e-17, + -2.9073234590199323e-36, -1.2938328629395601e-52), + qd_real( 9.6295326687368388e-01, 8.9341960404313634e-18, + -3.9071244661020126e-34, 1.6212091116847394e-50), + qd_real( 9.6212140426904158e-01, 1.5236770453846305e-17, + -1.3050173525597142e-33, 7.9016122394092666e-50), + qd_real( 9.6128048581132064e-01, 2.0933955216674039e-18, + 1.0768607469015692e-34, -5.9453639304361774e-51), + qd_real( 9.6043051941556579e-01, 2.4653904815317185e-17, + -1.3792169410906322e-33, -4.7726598378506903e-51), + qd_real( 9.5957151308198452e-01, 1.1000640085000957e-17, + -4.2036030828223975e-34, 4.0023704842606573e-51), + qd_real( 9.5870347489587160e-01, -4.3685014392372053e-17, + 2.2001800662729131e-33, -1.0553721324358075e-49), + qd_real( 9.5782641302753291e-01, -1.7696710075371263e-17, + 1.9164034110382190e-34, 8.1489235071754813e-51), + qd_real( 9.5694033573220882e-01, 4.0553869861875701e-17, + -1.7147013364302149e-33, 2.5736745295329455e-50), + qd_real( 9.5604525134999641e-01, 3.7705045279589067e-17, + 1.9678699997347571e-33, 8.5093177731230180e-50), + qd_real( 9.5514116830577067e-01, 5.0088652955014668e-17, + -2.6983181838059211e-33, 1.0102323575596493e-49), + qd_real( 9.5422809510910567e-01, -3.7545901690626874e-17, + 1.4951619241257764e-33, -8.2717333151394973e-50), + qd_real( 9.5330604035419386e-01, -2.5190738779919934e-17, + -1.4272239821134379e-33, -4.6717286809283155e-50), + qd_real( 9.5237501271976588e-01, -2.0269300462299272e-17, + -1.0635956887246246e-33, -3.5514537666487619e-50), + qd_real( 9.5143502096900834e-01, 3.1350584123266695e-17, + -2.4824833452737813e-33, 9.5450335525380613e-51), + qd_real( 9.5048607394948170e-01, 1.9410097562630436e-17, + -8.1559393949816789e-34, -1.0501209720164562e-50), + qd_real( 9.4952818059303667e-01, -7.5544151928043298e-18, + -5.1260245024046686e-34, 1.8093643389040406e-50), + qd_real( 9.4856134991573027e-01, 2.0668262262333232e-17, + -5.9440730243667306e-34, 1.4268853111554300e-50), + qd_real( 9.4758559101774109e-01, 4.3417993852125991e-17, + -2.7728667889840373e-34, 5.5709160196519968e-51), + qd_real( 9.4660091308328353e-01, 3.5056800210680730e-17, + 9.8578536940318117e-34, 6.6035911064585197e-50), + qd_real( 9.4560732538052128e-01, 4.6019102478523738e-17, + -6.2534384769452059e-34, 1.5758941215779961e-50), + qd_real( 9.4460483726148026e-01, 8.8100545476641165e-18, + 5.2291695602757842e-34, -3.3487256018407123e-50), + qd_real( 9.4359345816196039e-01, -2.4093127844404214e-17, + 1.0283279856803939e-34, -2.3398232614531355e-51), + qd_real( 9.4257319760144687e-01, 1.3235564806436886e-17, + -5.7048262885386911e-35, 3.9947050442753744e-51), + qd_real( 9.4154406518302081e-01, -2.7896379547698341e-17, + 1.6273236356733898e-33, -5.3075944708471203e-51), + qd_real( 9.4050607059326830e-01, 2.8610421567116268e-17, + 2.9261501147538827e-33, -2.6849867690896925e-50), + qd_real( 9.3945922360218992e-01, -7.0152867943098655e-18, + -5.6395693818011210e-34, 3.5568142678987651e-50), + qd_real( 9.3840353406310806e-01, 5.4242545044795490e-17, + -1.9039966607859759e-33, -1.5627792988341215e-49), + qd_real( 9.3733901191257496e-01, -3.6570926284362776e-17, + -1.1902940071273247e-33, -1.1215082331583223e-50), + qd_real( 9.3626566717027826e-01, -1.3013766145497654e-17, + 5.2229870061990595e-34, -3.3972777075634108e-51), + qd_real( 9.3518350993894761e-01, -3.2609395302485065e-17, + -8.1813015218875245e-34, 5.5642140024928139e-50), + qd_real( 9.3409255040425887e-01, 4.4662824360767511e-17, + -2.5903243047396916e-33, 8.1505209004343043e-50), + qd_real( 9.3299279883473885e-01, 4.2041415555384355e-17, + 9.0285896495521276e-34, 5.3019984977661259e-50), + qd_real( 9.3188426558166815e-01, -4.0785944377318095e-17, + 1.7631450298754169e-33, 2.5776403305507453e-50), + qd_real( 9.3076696107898371e-01, 1.9703775102838329e-17, + 6.5657908718278205e-34, -1.9480347966259524e-51), + qd_real( 9.2964089584318121e-01, 5.1282530016864107e-17, + 2.3719739891916261e-34, -1.7230065426917127e-50), + qd_real( 9.2850608047321559e-01, -2.3306639848485943e-17, + -7.7799084333208503e-34, -5.8597558009300305e-50), + qd_real( 9.2736252565040111e-01, -2.7677111692155437e-17, + 2.2110293450199576e-34, 2.0349190819680613e-50), + qd_real( 9.2621024213831138e-01, -3.7303754586099054e-17, + 2.0464457809993405e-33, 1.3831799631231817e-49), + qd_real( 9.2504924078267758e-01, 6.0529447412576159e-18, + -8.8256517760278541e-35, 1.8285462122388328e-51), + qd_real( 9.2387953251128674e-01, 1.7645047084336677e-17, + -5.0442537321586818e-34, -4.0478677716823890e-50), + qd_real( 9.2270112833387852e-01, 5.2963798918539814e-17, + -5.7135699628876685e-34, 3.0163671797219087e-50), + qd_real( 9.2151403934204190e-01, 4.1639843390684644e-17, + 1.1891485604702356e-33, 2.0862437594380324e-50), + qd_real( 9.2031827670911059e-01, -2.7806888779036837e-17, + 2.7011013677071274e-33, 1.1998578792455499e-49), + qd_real( 9.1911385169005777e-01, -2.6496484622344718e-17, + 6.5403604763461920e-34, -2.8997180201186078e-50), + qd_real( 9.1790077562139050e-01, -3.9074579680849515e-17, + 2.3004636541490264e-33, 3.9851762744443107e-50), + qd_real( 9.1667905992104270e-01, -4.1733978698287568e-17, + 1.2094444804381172e-33, 4.9356916826097816e-50), + qd_real( 9.1544871608826783e-01, -1.3591056692900894e-17, + 5.9923027475594735e-34, 2.1403295925962879e-50), + qd_real( 9.1420975570353069e-01, -3.6316182527814423e-17, + -1.9438819777122554e-33, 2.8340679287728316e-50), + qd_real( 9.1296219042839821e-01, -4.7932505228039469e-17, + -1.7753551889428638e-33, 4.0607782903868160e-51), + qd_real( 9.1170603200542988e-01, -2.6913273175034130e-17, + -5.1928101916162528e-35, 1.1338175936090630e-51), + qd_real( 9.1044129225806725e-01, -5.0433041673313820e-17, + 1.0938746257404305e-33, 9.5378272084170731e-51), + qd_real( 9.0916798309052238e-01, -3.6878564091359894e-18, + 2.9951330310507693e-34, -1.2225666136919926e-50), + qd_real( 9.0788611648766626e-01, -4.9459964301225840e-17, + -1.6599682707075313e-33, -5.1925202712634716e-50), + qd_real( 9.0659570451491533e-01, 3.0506718955442023e-17, + -1.4478836557141204e-33, 1.8906373784448725e-50), + qd_real( 9.0529675931811882e-01, -4.1153099826889901e-17, + 2.9859368705184223e-33, 5.1145293917439211e-50), + qd_real( 9.0398929312344334e-01, -6.6097544687484308e-18, + 1.2728013034680357e-34, -4.3026097234014823e-51), + qd_real( 9.0267331823725883e-01, -1.9250787033961483e-17, + 1.3242128993244527e-33, -5.2971030688703665e-50), + qd_real( 9.0134884704602203e-01, -1.3524789367698682e-17, + 6.3605353115880091e-34, 3.6227400654573828e-50), + qd_real( 9.0001589201616028e-01, -5.0639618050802273e-17, + 1.0783525384031576e-33, 2.8130016326515111e-50), + qd_real( 8.9867446569395382e-01, 2.6316906461033013e-17, + 3.7003137047796840e-35, -2.3447719900465938e-51), + qd_real( 8.9732458070541832e-01, -3.6396283314867290e-17, + -2.3611649895474815e-33, 1.1837247047900082e-49), + qd_real( 8.9596624975618511e-01, 4.9025099114811813e-17, + -1.9440489814795326e-33, -1.7070486667767033e-49), + qd_real( 8.9459948563138270e-01, -1.7516226396814919e-17, + -1.3200670047246923e-33, -1.5953009884324695e-50), + qd_real( 8.9322430119551532e-01, -4.1161239151908913e-18, + 2.5380253805715999e-34, 4.2849455510516192e-51), + qd_real( 8.9184070939234272e-01, 4.6690228137124547e-18, + 1.6150254286841982e-34, -3.9617448820725012e-51), + qd_real( 8.9044872324475788e-01, 1.1781931459051803e-17, + -1.3346142209571930e-34, -9.4982373530733431e-51), + qd_real( 8.8904835585466457e-01, -1.1164514966766675e-17, + -3.4797636107798736e-34, -1.5605079997040631e-50), + qd_real( 8.8763962040285393e-01, 1.2805091918587960e-17, + 3.9948742059584459e-35, 3.8940716325338136e-51), + qd_real( 8.8622253014888064e-01, -6.7307369600274315e-18, + 1.2385593432917413e-34, 2.0364014759133320e-51), + qd_real( 8.8479709843093779e-01, -9.4331469628972690e-18, + -5.7106541478701439e-34, 1.8260134111907397e-50), + qd_real( 8.8336333866573158e-01, 1.5822643380255127e-17, + -7.8921320007588250e-34, -1.4782321016179836e-50), + qd_real( 8.8192126434835505e-01, -1.9843248405890562e-17, + -7.0412114007673834e-34, -1.0636770169389104e-50), + qd_real( 8.8047088905216075e-01, 1.6311096602996350e-17, + -5.7541360594724172e-34, -4.0128611862170021e-50), + qd_real( 8.7901222642863353e-01, -4.7356837291118011e-17, + 1.4388771297975192e-33, -2.9085554304479134e-50), + qd_real( 8.7754529020726124e-01, 5.0113311846499550e-17, + 2.8382769008739543e-34, 1.5550640393164140e-50), + qd_real( 8.7607009419540660e-01, 5.8729024235147677e-18, + 2.7941144391738458e-34, -1.8536073846509828e-50), + qd_real( 8.7458665227817611e-01, -5.7216617730397065e-19, + -2.9705811503689596e-35, 8.7389593969796752e-52), + qd_real( 8.7309497841829009e-01, 7.8424672990129903e-18, + -4.8685015839797165e-34, -2.2815570587477527e-50), + qd_real( 8.7159508665595109e-01, -5.5272998038551050e-17, + -2.2104090204984907e-33, -9.7749763187643172e-50), + qd_real( 8.7008699110871146e-01, -4.1888510868549968e-17, + 7.0900185861878415e-34, 3.7600251115157260e-50), + qd_real( 8.6857070597134090e-01, 2.7192781689782903e-19, + -1.6710140396932428e-35, -1.2625514734637969e-51), + qd_real( 8.6704624551569265e-01, 3.0267859550930567e-18, + -1.1559438782171572e-34, -5.3580556397808012e-52), + qd_real( 8.6551362409056909e-01, -6.3723113549628899e-18, + 2.3725520321746832e-34, 1.5911880348395175e-50), + qd_real( 8.6397285612158670e-01, 4.1486355957361607e-17, + 2.2709976932210266e-33, -8.1228385659479984e-50), + qd_real( 8.6242395611104050e-01, 3.7008992527383130e-17, + 5.2128411542701573e-34, 2.6945600081026861e-50), + qd_real( 8.6086693863776731e-01, -3.0050048898573656e-17, + -8.8706183090892111e-34, 1.5005320558097301e-50), + qd_real( 8.5930181835700836e-01, 4.2435655816850687e-17, + 7.6181814059912025e-34, -3.9592127850658708e-50), + qd_real( 8.5772861000027212e-01, -4.8183447936336620e-17, + -1.1044130517687532e-33, -8.7400233444645562e-50), + qd_real( 8.5614732837519447e-01, 9.1806925616606261e-18, + 5.6328649785951470e-34, 2.3326646113217378e-51), + qd_real( 8.5455798836540053e-01, -1.2991124236396092e-17, + 1.2893407722948080e-34, -3.6506925747583053e-52), + qd_real( 8.5296060493036363e-01, 2.7152984251981370e-17, + 7.4336483283120719e-34, 4.2162417622350668e-50), + qd_real( 8.5135519310526520e-01, -5.3279874446016209e-17, + 2.2281156380919942e-33, -4.0281886404138477e-50), + qd_real( 8.4974176800085244e-01, 5.1812347659974015e-17, + 3.0810626087331275e-33, -2.5931308201994965e-50), + qd_real( 8.4812034480329723e-01, 1.8762563415239981e-17, + 1.4048773307919617e-33, -2.4915221509958691e-50), + qd_real( 8.4649093877405213e-01, -4.7969419958569345e-17, + -2.7518267097886703e-33, -7.3518959727313350e-50), + qd_real( 8.4485356524970712e-01, -4.3631360296879637e-17, + -2.0307726853367547e-33, 4.3097229819851761e-50), + qd_real( 8.4320823964184544e-01, 9.6536707005959077e-19, + 2.8995142431556364e-36, 9.6715076811480284e-53), + qd_real( 8.4155497743689844e-01, -3.4095465391321557e-17, + -8.4130208607579595e-34, -4.9447283960568686e-50), + qd_real( 8.3989379419599952e-01, -1.6673694881511411e-17, + -1.4759184141750289e-33, -7.5795098161914058e-50), + qd_real( 8.3822470555483808e-01, -3.5560085052855026e-17, + 1.1689791577022643e-33, -5.8627347359723411e-50), + qd_real( 8.3654772722351201e-01, -2.0899059027066533e-17, + -9.8104097821002585e-35, -3.1609177868229853e-51), + qd_real( 8.3486287498638001e-01, 4.6048430609159657e-17, + -5.1827423265239912e-34, -7.0505343435504109e-51), + qd_real( 8.3317016470191319e-01, 1.3275129507229764e-18, + 4.8589164115370863e-35, 4.5422281300506859e-51), + qd_real( 8.3146961230254524e-01, 1.4073856984728024e-18, + 4.6951315383980830e-35, 5.1431906049905658e-51), + qd_real( 8.2976123379452305e-01, -2.9349109376485597e-18, + 1.1496917934149818e-34, 3.5186665544980233e-51), + qd_real( 8.2804504525775580e-01, -4.4196593225871532e-17, + 2.7967864855211251e-33, 1.0030777287393502e-49), + qd_real( 8.2632106284566353e-01, -5.3957485453612902e-17, + 6.8976896130138550e-34, 3.8106164274199196e-50), + qd_real( 8.2458930278502529e-01, -2.6512360488868275e-17, + 1.6916964350914386e-34, 6.7693974813562649e-51), + qd_real( 8.2284978137582632e-01, 1.5193019034505495e-17, + 9.6890547246521685e-34, 5.6994562923653264e-50), + qd_real( 8.2110251499110465e-01, 3.0715131609697682e-17, + -1.7037168325855879e-33, -1.1149862443283853e-49), + qd_real( 8.1934752007679701e-01, -4.8200736995191133e-17, + -1.5574489646672781e-35, -9.5647853614522216e-53), + qd_real( 8.1758481315158371e-01, -1.4883149812426772e-17, + -7.8273262771298917e-34, 4.1332149161031594e-50), + qd_real( 8.1581441080673378e-01, 8.2652693782130871e-18, + -2.3028778135179471e-34, 1.5102071387249843e-50), + qd_real( 8.1403632970594841e-01, -5.2127351877042624e-17, + -1.9047670611316360e-33, -1.6937269585941507e-49), + qd_real( 8.1225058658520388e-01, 3.1054545609214803e-17, + 2.2649541922707251e-34, -7.4221684154649405e-51), + qd_real( 8.1045719825259477e-01, 2.3520367349840499e-17, + -7.7530070904846341e-34, -7.2792616357197140e-50), + qd_real( 8.0865618158817498e-01, 9.3251597879721674e-18, + -7.1823301933068394e-34, 2.3925440846132106e-50), + qd_real( 8.0684755354379922e-01, 4.9220603766095546e-17, + 2.9796016899903487e-33, 1.5220754223615788e-49), + qd_real( 8.0503133114296355e-01, 5.1368289568212149e-17, + 6.3082807402256524e-34, 7.3277646085129827e-51), + qd_real( 8.0320753148064494e-01, -3.3060609804814910e-17, + -1.2242726252420433e-33, 2.8413673268630117e-50), + qd_real( 8.0137617172314024e-01, -2.0958013413495834e-17, + -4.3798162198006931e-34, 2.0235690497752515e-50), + qd_real( 7.9953726910790501e-01, 2.0356723822005431e-17, + -9.7448513696896360e-34, 5.3608109599696008e-52), + qd_real( 7.9769084094339116e-01, -4.6730759884788944e-17, + 2.3075897077191757e-33, 3.1605567774640253e-51), + qd_real( 7.9583690460888357e-01, -3.0062724851910721e-17, + -2.2496210832042235e-33, -6.5881774117183040e-50), + qd_real( 7.9397547755433717e-01, -7.4194631759921416e-18, + 2.4124341304631069e-34, -4.9956808616244972e-51), + qd_real( 7.9210657730021239e-01, -3.7087850202326467e-17, + -1.4874457267228264e-33, 2.9323097289153505e-50), + qd_real( 7.9023022143731003e-01, 2.3056905954954492e-17, + 1.4481080533260193e-33, -7.6725237057203488e-50), + qd_real( 7.8834642762660623e-01, 3.4396993154059708e-17, + 1.7710623746737170e-33, 1.7084159098417402e-49), + qd_real( 7.8645521359908577e-01, -9.7841429939305265e-18, + 3.3906063272445472e-34, 5.7269505320382577e-51), + qd_real( 7.8455659715557524e-01, -8.5627965423173476e-18, + -2.1106834459001849e-34, -1.6890322182469603e-50), + qd_real( 7.8265059616657573e-01, 9.0745866975808825e-18, + 6.7623847404278666e-34, -1.7173237731987271e-50), + qd_real( 7.8073722857209449e-01, -9.9198782066678806e-18, + -2.1265794012162715e-36, 3.0772165598957647e-54), + qd_real( 7.7881651238147598e-01, -2.4891385579973807e-17, + 6.7665497024807980e-35, -6.5218594281701332e-52), + qd_real( 7.7688846567323244e-01, 7.7418602570672864e-18, + -5.9986517872157897e-34, 3.0566548232958972e-50), + qd_real( 7.7495310659487393e-01, -5.2209083189826433e-17, + -9.6653593393686612e-34, 3.7027750076562569e-50), + qd_real( 7.7301045336273699e-01, -3.2565907033649772e-17, + 1.3860807251523929e-33, -3.9971329917586022e-50), + qd_real( 7.7106052426181382e-01, -4.4558442347769265e-17, + -2.9863565614083783e-33, -6.8795262083596236e-50), + qd_real( 7.6910333764557959e-01, 5.1546455184564817e-17, + 2.6142829553524292e-33, -1.6199023632773298e-49), + qd_real( 7.6713891193582040e-01, -1.8885903683750782e-17, + -1.3659359331495433e-33, -2.2538834962921934e-50), + qd_real( 7.6516726562245896e-01, -3.2707225612534598e-17, + 1.1177117747079528e-33, -3.7005182280175715e-50), + qd_real( 7.6318841726338127e-01, 2.6314748416750748e-18, + 1.4048039063095910e-34, 8.9601886626630321e-52), + qd_real( 7.6120238548426178e-01, 3.5315510881690551e-17, + 1.2833566381864357e-33, 8.6221435180890613e-50), + qd_real( 7.5920918897838807e-01, -3.8558842175523123e-17, + 2.9720241208332759e-34, -1.2521388928220163e-50), + qd_real( 7.5720884650648457e-01, -1.9909098777335502e-17, + 3.9409283266158482e-34, 2.0744254207802976e-50), + qd_real( 7.5520137689653655e-01, -1.9402238001823017e-17, + -3.7756206444727573e-34, -2.1212242308178287e-50), + qd_real( 7.5318679904361252e-01, -3.7937789838736540e-17, + -6.7009539920231559e-34, -6.7128562115050214e-51), + qd_real( 7.5116513190968637e-01, 4.3499761158645868e-17, + 2.5227718971102212e-33, -6.5969709212757102e-50), + qd_real( 7.4913639452345937e-01, -4.4729078447011889e-17, + -2.4206025249983768e-33, 1.1336681351116422e-49), + qd_real( 7.4710060598018013e-01, 1.1874824875965430e-17, + 2.1992523849833518e-34, 1.1025018564644483e-50), + qd_real( 7.4505778544146595e-01, 1.5078686911877863e-17, + 8.0898987212942471e-34, 8.2677958765323532e-50), + qd_real( 7.4300795213512172e-01, -2.5144629669719265e-17, + 7.1128989512526157e-34, 3.0181629077821220e-50), + qd_real( 7.4095112535495911e-01, -1.4708616952297345e-17, + -4.9550433827142032e-34, 3.1434132533735671e-50), + qd_real( 7.3888732446061511e-01, 3.4324874808225091e-17, + -1.3706639444717610e-33, -3.3520827530718938e-51), + qd_real( 7.3681656887736990e-01, -2.8932468101656295e-17, + -3.4649887126202378e-34, -1.8484474476291476e-50), + qd_real( 7.3473887809596350e-01, -3.4507595976263941e-17, + -2.3718000676666409e-33, -3.9696090387165402e-50), + qd_real( 7.3265427167241282e-01, 1.8918673481573520e-17, + -1.5123719544119886e-33, -9.7922152011625728e-51), + qd_real( 7.3056276922782759e-01, -2.9689959904476928e-17, + -1.1276871244239744e-33, -3.0531520961539007e-50), + qd_real( 7.2846439044822520e-01, 1.1924642323370718e-19, + 5.9001892316611011e-36, 1.2178089069502704e-52), + qd_real( 7.2635915508434601e-01, -3.1917502443460542e-17, + 7.7047912412039396e-34, 4.1455880160182123e-50), + qd_real( 7.2424708295146689e-01, 2.9198471334403004e-17, + 2.3027324968739464e-33, -1.2928820533892183e-51), + qd_real( 7.2212819392921535e-01, -2.3871262053452047e-17, + 1.0636125432862273e-33, -4.4598638837802517e-50), + qd_real( 7.2000250796138165e-01, -2.5689658854462333e-17, + -9.1492566948567925e-34, 4.4403780801267786e-50), + qd_real( 7.1787004505573171e-01, 2.7006476062511453e-17, + -2.2854956580215348e-34, 9.1726903890287867e-51), + qd_real( 7.1573082528381871e-01, -5.1581018476410262e-17, + -1.3736271349300259e-34, -1.2734611344111297e-50), + qd_real( 7.1358486878079364e-01, -4.2342504403133584e-17, + -4.2690366101617268e-34, -2.6352370883066522e-50), + qd_real( 7.1143219574521643e-01, 7.9643298613856813e-18, + 2.9488239510721469e-34, 1.6985236437666356e-50), + qd_real( 7.0927282643886569e-01, -3.7597359110245730e-17, + 1.0613125954645119e-34, 8.9465480185486032e-51), + qd_real( 7.0710678118654757e-01, -4.8336466567264567e-17, + 2.0693376543497068e-33, 2.4677734957341755e-50) +}; + +/* Computes sin(a) and cos(a) using Taylor series. + Assumes |a| <= pi/2048. */ +static void sincos_taylor(const qd_real &a, + qd_real &sin_a, qd_real &cos_a) { + const double thresh = 0.5 * qd_real::_eps * std::abs(to_double(a)); + qd_real p, s, t, x; + + if (a.is_zero()) { + sin_a = 0.0; + cos_a = 1.0; + return; + } + + x = -sqr(a); + s = a; + p = a; + int i = 0; + do { + p *= x; + t = p * inv_fact[i]; + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + sin_a = s; + cos_a = sqrt(1.0 - sqr(s)); +} + +static qd_real sin_taylor(const qd_real &a) { + const double thresh = 0.5 * qd_real::_eps * std::abs(to_double(a)); + qd_real p, s, t, x; + + if (a.is_zero()) { + return 0.0; + } + + x = -sqr(a); + s = a; + p = a; + int i = 0; + do { + p *= x; + t = p * inv_fact[i]; + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + return s; +} + +static qd_real cos_taylor(const qd_real &a) { + const double thresh = 0.5 * qd_real::_eps; + qd_real p, s, t, x; + + if (a.is_zero()) { + return 1.0; + } + + x = -sqr(a); + s = 1.0 + mul_pwr2(x, 0.5); + p = x; + int i = 1; + do { + p *= x; + t = p * inv_fact[i]; + s += t; + i += 2; + } while (i < n_inv_fact && std::abs(to_double(t)) > thresh); + + return s; +} + +qd_real sin(const qd_real &a) { + + /* Strategy. To compute sin(x), we choose integers a, b so that + + x = s + a * (pi/2) + b * (pi/1024) + + and |s| <= pi/2048. Using a precomputed table of + sin(k pi / 1024) and cos(k pi / 1024), we can compute + sin(x) from sin(s) and cos(s). This greatly increases the + convergence of the sine Taylor series. */ + + if (a.is_zero()) { + return 0.0; + } + + // approximately reduce modulo 2*pi + qd_real z = nint(a / qd_real::_2pi); + qd_real r = a - qd_real::_2pi * z; + + // approximately reduce modulo pi/2 and then modulo pi/1024 + double q = std::floor(r.x[0] / qd_real::_pi2[0] + 0.5); + qd_real t = r - qd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi1024[0] + 0.5); + t -= _pi1024 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + qd_real::error("(qd_real::sin): Cannot reduce modulo pi/2."); + return qd_real::_nan; + } + + if (abs_k > 256) { + qd_real::error("(qd_real::sin): Cannot reduce modulo pi/1024."); + return qd_real::_nan; + } + + if (k == 0) { + switch (j) { + case 0: + return sin_taylor(t); + case 1: + return cos_taylor(t); + case -1: + return -cos_taylor(t); + default: + return -sin_taylor(t); + } + } + + qd_real sin_t, cos_t; + qd_real u = cos_table[abs_k-1]; + qd_real v = sin_table[abs_k-1]; + sincos_taylor(t, sin_t, cos_t); + + if (j == 0) { + if (k > 0) { + r = u * sin_t + v * cos_t; + } else { + r = u * sin_t - v * cos_t; + } + } else if (j == 1) { + if (k > 0) { + r = u * cos_t - v * sin_t; + } else { + r = u * cos_t + v * sin_t; + } + } else if (j == -1) { + if (k > 0) { + r = v * sin_t - u * cos_t; + } else { + r = - u * cos_t - v * sin_t; + } + } else { + if (k > 0) { + r = - u * sin_t - v * cos_t; + } else { + r = v * cos_t - u * sin_t; + } + } + + return r; +} + +qd_real cos(const qd_real &a) { + + if (a.is_zero()) { + return 1.0; + } + + // approximately reduce modulo 2*pi + qd_real z = nint(a / qd_real::_2pi); + qd_real r = a - qd_real::_2pi * z; + + // approximately reduce modulo pi/2 and then modulo pi/1024 + double q = std::floor(r.x[0] / qd_real::_pi2.x[0] + 0.5); + qd_real t = r - qd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi1024.x[0] + 0.5); + t -= _pi1024 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + qd_real::error("(qd_real::cos): Cannot reduce modulo pi/2."); + return qd_real::_nan; + } + + if (abs_k > 256) { + qd_real::error("(qd_real::cos): Cannot reduce modulo pi/1024."); + return qd_real::_nan; + } + + if (k == 0) { + switch (j) { + case 0: + return cos_taylor(t); + case 1: + return -sin_taylor(t); + case -1: + return sin_taylor(t); + default: + return -cos_taylor(t); + } + } + + qd_real sin_t, cos_t; + sincos_taylor(t, sin_t, cos_t); + + qd_real u = cos_table[abs_k-1]; + qd_real v = sin_table[abs_k-1]; + + if (j == 0) { + if (k > 0) { + r = u * cos_t - v * sin_t; + } else { + r = u * cos_t + v * sin_t; + } + } else if (j == 1) { + if (k > 0) { + r = - u * sin_t - v * cos_t; + } else { + r = v * cos_t - u * sin_t; + } + } else if (j == -1) { + if (k > 0) { + r = u * sin_t + v * cos_t; + } else { + r = u * sin_t - v * cos_t; + } + } else { + if (k > 0) { + r = v * sin_t - u * cos_t; + } else { + r = - u * cos_t - v * sin_t; + } + } + + return r; +} + +void sincos(const qd_real &a, qd_real &sin_a, qd_real &cos_a) { + + if (a.is_zero()) { + sin_a = 0.0; + cos_a = 1.0; + return; + } + + // approximately reduce by 2*pi + qd_real z = nint(a / qd_real::_2pi); + qd_real t = a - qd_real::_2pi * z; + + // approximately reduce by pi/2 and then by pi/1024. + double q = std::floor(t.x[0] / qd_real::_pi2.x[0] + 0.5); + t -= qd_real::_pi2 * q; + int j = static_cast(q); + q = std::floor(t.x[0] / _pi1024.x[0] + 0.5); + t -= _pi1024 * q; + int k = static_cast(q); + int abs_k = std::abs(k); + + if (j < -2 || j > 2) { + qd_real::error("(qd_real::sincos): Cannot reduce modulo pi/2."); + cos_a = sin_a = qd_real::_nan; + return; + } + + if (abs_k > 256) { + qd_real::error("(qd_real::sincos): Cannot reduce modulo pi/1024."); + cos_a = sin_a = qd_real::_nan; + return; + } + + qd_real sin_t, cos_t; + sincos_taylor(t, sin_t, cos_t); + + if (k == 0) { + if (j == 0) { + sin_a = sin_t; + cos_a = cos_t; + } else if (j == 1) { + sin_a = cos_t; + cos_a = -sin_t; + } else if (j == -1) { + sin_a = -cos_t; + cos_a = sin_t; + } else { + sin_a = -sin_t; + cos_a = -cos_t; + } + return; + } + + qd_real u = cos_table[abs_k-1]; + qd_real v = sin_table[abs_k-1]; + + if (j == 0) { + if (k > 0) { + sin_a = u * sin_t + v * cos_t; + cos_a = u * cos_t - v * sin_t; + } else { + sin_a = u * sin_t - v * cos_t; + cos_a = u * cos_t + v * sin_t; + } + } else if (j == 1) { + if (k > 0) { + cos_a = - u * sin_t - v * cos_t; + sin_a = u * cos_t - v * sin_t; + } else { + cos_a = v * cos_t - u * sin_t; + sin_a = u * cos_t + v * sin_t; + } + } else if (j == -1) { + if (k > 0) { + cos_a = u * sin_t + v * cos_t; + sin_a = v * sin_t - u * cos_t; + } else { + cos_a = u * sin_t - v * cos_t; + sin_a = - u * cos_t - v * sin_t; + } + } else { + if (k > 0) { + sin_a = - u * sin_t - v * cos_t; + cos_a = v * sin_t - u * cos_t; + } else { + sin_a = v * cos_t - u * sin_t; + cos_a = - u * cos_t - v * sin_t; + } + } +} + +qd_real atan(const qd_real &a) { + return atan2(a, qd_real(1.0)); +} + +qd_real atan2(const qd_real &y, const qd_real &x) { + /* Strategy: Instead of using Taylor series to compute + arctan, we instead use Newton's iteration to solve + the equation + + sin(z) = y/r or cos(z) = x/r + + where r = sqrt(x^2 + y^2). + The iteration is given by + + z' = z + (y - sin(z)) / cos(z) (for equation 1) + z' = z - (x - cos(z)) / sin(z) (for equation 2) + + Here, x and y are normalized so that x^2 + y^2 = 1. + If |x| > |y|, then first iteration is used since the + denominator is larger. Otherwise, the second is used. + */ + + if (x.is_zero()) { + + if (y.is_zero()) { + /* Both x and y is zero. */ + qd_real::error("(qd_real::atan2): Both arguments zero."); + return qd_real::_nan; + } + + return (y.is_positive()) ? qd_real::_pi2 : -qd_real::_pi2; + } else if (y.is_zero()) { + return (x.is_positive()) ? qd_real(0.0) : qd_real::_pi; + } + + if (x == y) { + return (y.is_positive()) ? qd_real::_pi4 : -qd_real::_3pi4; + } + + if (x == -y) { + return (y.is_positive()) ? qd_real::_3pi4 : -qd_real::_pi4; + } + + qd_real r = sqrt(sqr(x) + sqr(y)); + qd_real xx = x / r; + qd_real yy = y / r; + + /* Compute double precision approximation to atan. */ + qd_real z = std::atan2(to_double(y), to_double(x)); + qd_real sin_z, cos_z; + + if (std::abs(xx.x[0]) > std::abs(yy.x[0])) { + /* Use Newton iteration 1. z' = z + (y - sin(z)) / cos(z) */ + sincos(z, sin_z, cos_z); + z += (yy - sin_z) / cos_z; + sincos(z, sin_z, cos_z); + z += (yy - sin_z) / cos_z; + sincos(z, sin_z, cos_z); + z += (yy - sin_z) / cos_z; + } else { + /* Use Newton iteration 2. z' = z - (x - cos(z)) / sin(z) */ + sincos(z, sin_z, cos_z); + z -= (xx - cos_z) / sin_z; + sincos(z, sin_z, cos_z); + z -= (xx - cos_z) / sin_z; + sincos(z, sin_z, cos_z); + z -= (xx - cos_z) / sin_z; + } + + return z; +} + + +qd_real drem(const qd_real &a, const qd_real &b) { + qd_real n = nint(a/b); + return (a - n * b); +} + +qd_real divrem(const qd_real &a, const qd_real &b, qd_real &r) { + qd_real n = nint(a/b); + r = a - n * b; + return n; +} + +qd_real tan(const qd_real &a) { + qd_real s, c; + sincos(a, s, c); + return s/c; +} + +qd_real asin(const qd_real &a) { + qd_real abs_a = abs(a); + + if (abs_a > 1.0) { + qd_real::error("(qd_real::asin): Argument out of domain."); + return qd_real::_nan; + } + + if (abs_a.is_one()) { + return (a.is_positive()) ? qd_real::_pi2 : -qd_real::_pi2; + } + + return atan2(a, sqrt(1.0 - sqr(a))); +} + +qd_real acos(const qd_real &a) { + qd_real abs_a = abs(a); + + if (abs_a > 1.0) { + qd_real::error("(qd_real::acos): Argument out of domain."); + return qd_real::_nan; + } + + if (abs_a.is_one()) { + return (a.is_positive()) ? qd_real(0.0) : qd_real::_pi; + } + + return atan2(sqrt(1.0 - sqr(a)), a); +} + +qd_real sinh(const qd_real &a) { + if (a.is_zero()) { + return 0.0; + } + + if (abs(a) > 0.05) { + qd_real ea = exp(a); + return mul_pwr2(ea - inv(ea), 0.5); + } + + /* Since a is small, using the above formula gives + a lot of cancellation. So use Taylor series. */ + qd_real s = a; + qd_real t = a; + qd_real r = sqr(t); + double m = 1.0; + double thresh = std::abs(to_double(a) * qd_real::_eps); + + do { + m += 2.0; + t *= r; + t /= (m-1) * m; + + s += t; + } while (abs(t) > thresh); + + return s; +} + +qd_real cosh(const qd_real &a) { + if (a.is_zero()) { + return 1.0; + } + + qd_real ea = exp(a); + return mul_pwr2(ea + inv(ea), 0.5); +} + +qd_real tanh(const qd_real &a) { + if (a.is_zero()) { + return 0.0; + } + + if (std::abs(to_double(a)) > 0.05) { + qd_real ea = exp(a); + qd_real inv_ea = inv(ea); + return (ea - inv_ea) / (ea + inv_ea); + } else { + qd_real s, c; + s = sinh(a); + c = sqrt(1.0 + sqr(s)); + return s / c; + } +} + +void sincosh(const qd_real &a, qd_real &s, qd_real &c) { + if (std::abs(to_double(a)) <= 0.05) { + s = sinh(a); + c = sqrt(1.0 + sqr(s)); + } else { + qd_real ea = exp(a); + qd_real inv_ea = inv(ea); + s = mul_pwr2(ea - inv_ea, 0.5); + c = mul_pwr2(ea + inv_ea, 0.5); + } +} + +qd_real asinh(const qd_real &a) { + return log(a + sqrt(sqr(a) + 1.0)); +} + +qd_real acosh(const qd_real &a) { + if (a < 1.0) { + qd_real::error("(qd_real::acosh): Argument out of domain."); + return qd_real::_nan; + } + + return log(a + sqrt(sqr(a) - 1.0)); +} + +qd_real atanh(const qd_real &a) { + if (abs(a) >= 1.0) { + qd_real::error("(qd_real::atanh): Argument out of domain."); + return qd_real::_nan; + } + + return mul_pwr2(log((1.0 + a) / (1.0 - a)), 0.5); +} + +QD_API qd_real fmod(const qd_real &a, const qd_real &b) { + qd_real n = aint(a / b); + return (a - b * n); +} + +QD_API qd_real qdrand() { + static const double m_const = 4.6566128730773926e-10; /* = 2^{-31} */ + double m = m_const; + qd_real r = 0.0; + double d; + + /* Strategy: Generate 31 bits at a time, using lrand48 + random number generator. Shift the bits, and repeat + 7 times. */ + + for (int i = 0; i < 7; i++, m *= m_const) { + d = std::rand() * m; + r += d; + } + + return r; +} + + +/* polyeval(c, n, x) + Evaluates the given n-th degree polynomial at x. + The polynomial is given by the array of (n+1) coefficients. */ +qd_real polyeval(const qd_real *c, int n, const qd_real &x) { + /* Just use Horner's method of polynomial evaluation. */ + qd_real r = c[n]; + + for (int i = n-1; i >= 0; i--) { + r *= x; + r += c[i]; + } + + return r; +} + +/* polyroot(c, n, x0) + Given an n-th degree polynomial, finds a root close to + the given guess x0. Note that this uses simple Newton + iteration scheme, and does not work for multiple roots. */ +QD_API qd_real polyroot(const qd_real *c, int n, + const qd_real &x0, int max_iter, double thresh) { + qd_real x = x0; + qd_real f; + qd_real *d = new qd_real[n]; + bool conv = false; + int i; + double max_c = std::abs(to_double(c[0])); + double v; + + if (thresh == 0.0) thresh = qd_real::_eps; + + /* Compute the coefficients of the derivatives. */ + for (i = 1; i <= n; i++) { + v = std::abs(to_double(c[i])); + if (v > max_c) max_c = v; + d[i-1] = c[i] * static_cast(i); + } + thresh *= max_c; + + /* Newton iteration. */ + for (i = 0; i < max_iter; i++) { + f = polyeval(c, n, x); + + if (abs(f) < thresh) { + conv = true; + break; + } + x -= (f / polyeval(d, n-1, x)); + } + delete [] d; + + if (!conv) { + qd_real::error("(qd_real::polyroot): Failed to converge."); + return qd_real::_nan; + } + + return x; +} + +qd_real qd_real::debug_rand() { + if (std::rand() % 2 == 0) + return qdrand(); + + int expn = 0; + qd_real a = 0.0; + double d; + for (int i = 0; i < 4; i++) { + d = std::ldexp(std::rand() / static_cast(RAND_MAX), -expn); + a += d; + expn = expn + 54 + std::rand() % 200; + } + return a; +} + diff --git a/src/external/PackedCSparse/qd/qd_real.h b/src/external/PackedCSparse/qd/qd_real.h new file mode 100644 index 00000000..1ecfc732 --- /dev/null +++ b/src/external/PackedCSparse/qd/qd_real.h @@ -0,0 +1,296 @@ +/* + * include/qd_real.h + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2007 + * + * Quad-double precision (>= 212-bit significand) floating point arithmetic + * package, written in ANSI C++, taking full advantage of operator overloading. + * Uses similar techniques as that of David Bailey's double-double package + * and that of Jonathan Shewchuk's adaptive precision floating point + * arithmetic package. See + * + * http://www.nersc.gov/~dhbailey/mpdist/mpdist.html + * http://www.cs.cmu.edu/~quake/robust.html + * + * for more details. + * + * Yozo Hida + */ +#ifndef _QD_QD_REAL_H +#define _QD_QD_REAL_H + +#include +#include +#include +#include "qd_config.h" +#include "dd_real.h" + +struct QD_API qd_real { + double x[4]; /* The Components. */ + + /* Eliminates any zeros in the middle component(s). */ + void zero_elim(); + void zero_elim(double &e); + + void renorm(); + void renorm(double &e); + + void quick_accum(double d, double &e); + void quick_prod_accum(double a, double b, double &e); + + qd_real(double x0, double x1, double x2, double x3); + explicit qd_real(const double *xx); + + static const qd_real _2pi; + static const qd_real _pi; + static const qd_real _3pi4; + static const qd_real _pi2; + static const qd_real _pi4; + static const qd_real _e; + static const qd_real _log2; + static const qd_real _log10; + static const qd_real _nan; + static const qd_real _inf; + + static const double _eps; + static const double _min_normalized; + static const qd_real _max; + static const qd_real _safe_max; + static const int _ndigits; + + qd_real(); + qd_real(const char *s); + qd_real(const dd_real &dd); + qd_real(double d); + qd_real(int i); + + double operator[](int i) const; + double &operator[](int i); + + static void error(const char *msg); + + bool isnan() const; + bool isfinite() const { return QD_ISFINITE(x[0]); } + bool isinf() const { return QD_ISINF(x[0]); } + + static qd_real ieee_add(const qd_real &a, const qd_real &b); + static qd_real sloppy_add(const qd_real &a, const qd_real &b); + + qd_real &operator+=(double a); + qd_real &operator+=(const dd_real &a); + qd_real &operator+=(const qd_real &a); + + qd_real &operator-=(double a); + qd_real &operator-=(const dd_real &a); + qd_real &operator-=(const qd_real &a); + + static qd_real sloppy_mul(const qd_real &a, const qd_real &b); + static qd_real accurate_mul(const qd_real &a, const qd_real &b); + + qd_real &operator*=(double a); + qd_real &operator*=(const dd_real &a); + qd_real &operator*=(const qd_real &a); + + static qd_real sloppy_div(const qd_real &a, const dd_real &b); + static qd_real accurate_div(const qd_real &a, const dd_real &b); + static qd_real sloppy_div(const qd_real &a, const qd_real &b); + static qd_real accurate_div(const qd_real &a, const qd_real &b); + + qd_real &operator/=(double a); + qd_real &operator/=(const dd_real &a); + qd_real &operator/=(const qd_real &a); + + qd_real operator^(int n) const; + + qd_real operator-() const; + + qd_real &operator=(double a); + qd_real &operator=(const dd_real &a); + qd_real &operator=(const char *s); + + bool is_zero() const; + bool is_one() const; + bool is_positive() const; + bool is_negative() const; + + explicit operator bool() const; // new + explicit operator double() const; // new + + static qd_real rand(void); + + void to_digits(char *s, int &expn, int precision = _ndigits) const; + void write(char *s, int len, int precision = _ndigits, + bool showpos = false, bool uppercase = false) const; + std::string to_string(int precision = _ndigits, int width = 0, + std::ios_base::fmtflags fmt = static_cast(0), + bool showpos = false, bool uppercase = false, char fill = ' ') const; + static int read(const char *s, qd_real &a); + + /* Debugging methods */ + void dump(const std::string &name, std::ostream &os = std::cerr) const; + void dump_bits(const std::string &name, + std::ostream &os = std::cerr) const; + + static qd_real debug_rand(); + +}; + +namespace std { + template <> + class numeric_limits : public numeric_limits { + public: + inline static double epsilon() { return qd_real::_eps; } + inline static double min() { return qd_real::_min_normalized; } + inline static qd_real max() { return qd_real::_max; } + inline static qd_real safe_max() { return qd_real::_safe_max; } + static const int digits = 209; + static const int digits10 = 62; + }; +} + +QD_API qd_real polyeval(const qd_real *c, int n, const qd_real &x); +QD_API qd_real polyroot(const qd_real *c, int n, + const qd_real &x0, int max_iter = 64, double thresh = 0.0); + +QD_API qd_real qdrand(void); +QD_API qd_real sqrt(const qd_real &a); + +QD_API inline bool isnan(const qd_real &a) { return a.isnan(); } +QD_API inline bool isfinite(const qd_real &a) { return a.isfinite(); } +QD_API inline bool isinf(const qd_real &a) { return a.isinf(); } + +/* Computes qd * d where d is known to be a power of 2. + This can be done component wise. */ +QD_API qd_real mul_pwr2(const qd_real &qd, double d); + +QD_API qd_real operator+(const qd_real &a, const qd_real &b); +QD_API qd_real operator+(const dd_real &a, const qd_real &b); +QD_API qd_real operator+(const qd_real &a, const dd_real &b); +QD_API qd_real operator+(const qd_real &a, double b); +QD_API qd_real operator+(double a, const qd_real &b); + +QD_API qd_real operator-(const qd_real &a, const qd_real &b); +QD_API qd_real operator-(const dd_real &a, const qd_real &b); +QD_API qd_real operator-(const qd_real &a, const dd_real &b); +QD_API qd_real operator-(const qd_real &a, double b); +QD_API qd_real operator-(double a, const qd_real &b); + +QD_API qd_real operator*(const qd_real &a, const qd_real &b); +QD_API qd_real operator*(const dd_real &a, const qd_real &b); +QD_API qd_real operator*(const qd_real &a, const dd_real &b); +QD_API qd_real operator*(const qd_real &a, double b); +QD_API qd_real operator*(double a, const qd_real &b); + +QD_API qd_real operator/(const qd_real &a, const qd_real &b); +QD_API qd_real operator/(const dd_real &a, const qd_real &b); +QD_API qd_real operator/(const qd_real &a, const dd_real &b); +QD_API qd_real operator/(const qd_real &a, double b); +QD_API qd_real operator/(double a, const qd_real &b); + +QD_API qd_real sqr(const qd_real &a); +QD_API qd_real sqrt(const qd_real &a); +QD_API qd_real pow(const qd_real &a, int n); +QD_API qd_real pow(const qd_real &a, const qd_real &b); +QD_API qd_real npwr(const qd_real &a, int n); + +QD_API qd_real nroot(const qd_real &a, int n); + +QD_API qd_real rem(const qd_real &a, const qd_real &b); +QD_API qd_real drem(const qd_real &a, const qd_real &b); +QD_API qd_real divrem(const qd_real &a, const qd_real &b, qd_real &r); + +dd_real to_dd_real(const qd_real &a); +double to_double(const qd_real &a); +int to_int(const qd_real &a); + +QD_API bool operator==(const qd_real &a, const qd_real &b); +QD_API bool operator==(const qd_real &a, const dd_real &b); +QD_API bool operator==(const dd_real &a, const qd_real &b); +QD_API bool operator==(double a, const qd_real &b); +QD_API bool operator==(const qd_real &a, double b); + +QD_API bool operator<(const qd_real &a, const qd_real &b); +QD_API bool operator<(const qd_real &a, const dd_real &b); +QD_API bool operator<(const dd_real &a, const qd_real &b); +QD_API bool operator<(double a, const qd_real &b); +QD_API bool operator<(const qd_real &a, double b); + +QD_API bool operator>(const qd_real &a, const qd_real &b); +QD_API bool operator>(const qd_real &a, const dd_real &b); +QD_API bool operator>(const dd_real &a, const qd_real &b); +QD_API bool operator>(double a, const qd_real &b); +QD_API bool operator>(const qd_real &a, double b); + +QD_API bool operator<=(const qd_real &a, const qd_real &b); +QD_API bool operator<=(const qd_real &a, const dd_real &b); +QD_API bool operator<=(const dd_real &a, const qd_real &b); +QD_API bool operator<=(double a, const qd_real &b); +QD_API bool operator<=(const qd_real &a, double b); + +QD_API bool operator>=(const qd_real &a, const qd_real &b); +QD_API bool operator>=(const qd_real &a, const dd_real &b); +QD_API bool operator>=(const dd_real &a, const qd_real &b); +QD_API bool operator>=(double a, const qd_real &b); +QD_API bool operator>=(const qd_real &a, double b); + +QD_API bool operator!=(const qd_real &a, const qd_real &b); +QD_API bool operator!=(const qd_real &a, const dd_real &b); +QD_API bool operator!=(const dd_real &a, const qd_real &b); +QD_API bool operator!=(double a, const qd_real &b); +QD_API bool operator!=(const qd_real &a, double b); + +QD_API qd_real fabs(const qd_real &a); +QD_API qd_real abs(const qd_real &a); /* same as fabs */ + +QD_API qd_real ldexp(const qd_real &a, int n); + +QD_API qd_real nint(const qd_real &a); +QD_API qd_real quick_nint(const qd_real &a); +QD_API qd_real floor(const qd_real &a); +QD_API qd_real ceil(const qd_real &a); +QD_API qd_real aint(const qd_real &a); + +QD_API qd_real sin(const qd_real &a); +QD_API qd_real cos(const qd_real &a); +QD_API qd_real tan(const qd_real &a); +QD_API void sincos(const qd_real &a, qd_real &s, qd_real &c); + +QD_API qd_real asin(const qd_real &a); +QD_API qd_real acos(const qd_real &a); +QD_API qd_real atan(const qd_real &a); +QD_API qd_real atan2(const qd_real &y, const qd_real &x); + +QD_API qd_real exp(const qd_real &a); +QD_API qd_real log(const qd_real &a); +QD_API qd_real log10(const qd_real &a); + +QD_API qd_real sinh(const qd_real &a); +QD_API qd_real cosh(const qd_real &a); +QD_API qd_real tanh(const qd_real &a); +QD_API void sincosh(const qd_real &a, qd_real &sin_qd, qd_real &cos_qd); + +QD_API qd_real asinh(const qd_real &a); +QD_API qd_real acosh(const qd_real &a); +QD_API qd_real atanh(const qd_real &a); + +QD_API qd_real qdrand(void); + +QD_API qd_real max(const qd_real &a, const qd_real &b); +QD_API qd_real max(const qd_real &a, const qd_real &b, const qd_real &c); +QD_API qd_real min(const qd_real &a, const qd_real &b); +QD_API qd_real min(const qd_real &a, const qd_real &b, const qd_real &c); + +QD_API qd_real fmod(const qd_real &a, const qd_real &b); + +QD_API std::ostream &operator<<(std::ostream &s, const qd_real &a); +QD_API std::istream &operator>>(std::istream &s, qd_real &a); +#ifdef QD_INLINE +#include "qd_inline.h" +#endif + +#endif /* _QD_QD_REAL_H */ + diff --git a/src/external/PackedCSparse/qd/util.cc b/src/external/PackedCSparse/qd/util.cc new file mode 100644 index 00000000..ab962081 --- /dev/null +++ b/src/external/PackedCSparse/qd/util.cc @@ -0,0 +1,22 @@ +#include +#include "util.h" + +void append_expn(std::string &str, int expn) { + int k; + + str += (expn < 0 ? '-' : '+'); + expn = std::abs(expn); + + if (expn >= 100) { + k = (expn / 100); + str += '0' + k; + expn -= 100*k; + } + + k = (expn / 10); + str += '0' + k; + expn -= 10*k; + + str += '0' + expn; +} + diff --git a/src/external/PackedCSparse/qd/util.h b/src/external/PackedCSparse/qd/util.h new file mode 100644 index 00000000..7de35836 --- /dev/null +++ b/src/external/PackedCSparse/qd/util.h @@ -0,0 +1,4 @@ +#include + +void append_expn(std::string &str, int expn); + diff --git a/src/external/PackedCSparse/transpose.h b/src/external/PackedCSparse/transpose.h new file mode 100644 index 00000000..6e1502e3 --- /dev/null +++ b/src/external/PackedCSparse/transpose.h @@ -0,0 +1,90 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2022 Ioannis Iakovidis + +// This file is converted from PolytopeSamplerMatlab +//(https://github.com/ConstrainedSampler/PolytopeSamplerMatlab/blob/master/code/solver/PackedCSparse/PackedChol.h) by Ioannis Iakovidis +#pragma once +#include "SparseMatrix.h" + +// Problem: +// Compute M = A' + +// Algorithm: +// We precompute the mapping from entries of A to entries of At + +namespace PackedCSparse { + template + struct TransposeOutput : SparseMatrix + { + UniquePtr forward; + + template + void initialize(const SparseMatrix& A) + { + pcs_assert(A.initialized(), "transpose: bad inputs."); + SparseMatrix::initialize(A.n, A.m, A.nnz()); + + Ti Am = A.m, An = A.n, * Ap = A.p.get(), * Ai = A.i.get(); + Ti Bm = this->m, Bn = this->n, * Bp = this->p.get(), * Bi = this->i.get(); + Ti nz = A.nnz(); + + // compute row counts of A + Ti* count = new Ti[Bn + 1](); + + for (Ti p = 0; p < nz; p++) + count[Ai[p]]++; + + // compute this->p + Bp[0] = 0; + for (Ti i = 0; i < Bn; i++) + { + Bp[i + 1] = Bp[i] + count[i]; + count[i] = Bp[i]; // Now, cnt[i] stores the index of the first element in the i-th row + } + + // compute i and forward + if (!std::is_same::value) + forward.reset(new Ti[nz]); + for (Ti j = 0; j < An; j++) + { + for (Ti p = Ap[j]; p < Ap[j + 1]; p++) + { + Ti q = count[Ai[p]]; + Bi[q] = j; + if (!std::is_same::value) + forward[p] = q; + count[Ai[p]]++; + } + } + + delete[] count; + } + }; + + template + void transpose(TransposeOutput& o, const SparseMatrix& A) + { + if (!o.initialized()) + o.initialize(A); + + Tx* Ax = A.x.get(); Tx2 *Bx = o.x.get(); + Ti nz = o.nnz(), *forward = o.forward.get(); + + if (!std::is_same::value) + { + for (Ti s = 0; s < nz; s++) + Bx[forward[s]] = Tx2(Ax[s]); + } + } + + template + TransposeOutput transpose(const SparseMatrix& A) + { + TransposeOutput o; + transpose(o, A); + return o; + } +} diff --git a/src/external/Spectra/include/Spectra/GenEigsBase.h b/src/external/Spectra/include/Spectra/GenEigsBase.h new file mode 100644 index 00000000..19b12c15 --- /dev/null +++ b/src/external/Spectra/include/Spectra/GenEigsBase.h @@ -0,0 +1,479 @@ +// Copyright (C) 2018-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef GEN_EIGS_BASE_H +#define GEN_EIGS_BASE_H + +#include +#include // std::vector +#include // std::abs, std::pow, std::sqrt +#include // std::min, std::copy +#include // std::complex, std::conj, std::norm, std::abs +#include // std::invalid_argument + +#include "Util/TypeTraits.h" +#include "Util/SelectionRule.h" +#include "Util/CompInfo.h" +#include "Util/SimpleRandom.h" +#include "MatOp/internal/ArnoldiOp.h" +#include "LinAlg/UpperHessenbergQR.h" +#include "LinAlg/DoubleShiftQR.h" +#include "LinAlg/UpperHessenbergEigen.h" +#include "LinAlg/Arnoldi.h" + +namespace Spectra { + + +/// +/// \ingroup EigenSolver +/// +/// This is the base class for general eigen solvers, mainly for internal use. +/// It is kept here to provide the documentation for member functions of concrete eigen solvers +/// such as GenEigsSolver and GenEigsRealShiftSolver. +/// +template < typename Scalar, + int SelectionRule, + typename OpType, + typename BOpType > +class GenEigsBase +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Array Array; + typedef Eigen::Array BoolArray; + typedef Eigen::Map MapMat; + typedef Eigen::Map MapVec; + typedef Eigen::Map MapConstVec; + + typedef std::complex Complex; + typedef Eigen::Matrix ComplexMatrix; + typedef Eigen::Matrix ComplexVector; + + typedef ArnoldiOp ArnoldiOpType; + typedef Arnoldi ArnoldiFac; + +protected: + OpType* m_op; // object to conduct matrix operation, + // e.g. matrix-vector product + const Index m_n; // dimension of matrix A + const Index m_nev; // number of eigenvalues requested + const Index m_ncv; // dimension of Krylov subspace in the Arnoldi method + Index m_nmatop; // number of matrix operations called + Index m_niter; // number of restarting iterations + + ArnoldiFac m_fac; // Arnoldi factorization + + ComplexVector m_ritz_val; // Ritz values + ComplexMatrix m_ritz_vec; // Ritz vectors + ComplexVector m_ritz_est; // last row of m_ritz_vec + +private: + BoolArray m_ritz_conv; // indicator of the convergence of Ritz values + int m_info; // status of the computation + + const Scalar m_near_0; // a very small value, but 1.0 / m_near_0 does not overflow + // ~= 1e-307 for the "double" type + const Scalar m_eps; // the machine precision, ~= 1e-16 for the "double" type + const Scalar m_eps23; // m_eps^(2/3), used to test the convergence + + // Real Ritz values calculated from UpperHessenbergEigen have exact zero imaginary part + // Complex Ritz values have exact conjugate pairs + // So we use exact tests here + static bool is_complex(const Complex& v) { return v.imag() != Scalar(0); } + static bool is_conj(const Complex& v1, const Complex& v2) { return v1 == Eigen::numext::conj(v2); } + + // Implicitly restarted Arnoldi factorization + void restart(Index k) + { + using std::norm; + + if(k >= m_ncv) + return; + + DoubleShiftQR decomp_ds(m_ncv); + UpperHessenbergQR decomp_hb(m_ncv); + Matrix Q = Matrix::Identity(m_ncv, m_ncv); + + for(Index i = k; i < m_ncv; i++) + { + if(is_complex(m_ritz_val[i]) && is_conj(m_ritz_val[i], m_ritz_val[i + 1])) + { + // H - mu * I = Q1 * R1 + // H <- R1 * Q1 + mu * I = Q1' * H * Q1 + // H - conj(mu) * I = Q2 * R2 + // H <- R2 * Q2 + conj(mu) * I = Q2' * H * Q2 + // + // (H - mu * I) * (H - conj(mu) * I) = Q1 * Q2 * R2 * R1 = Q * R + const Scalar s = Scalar(2) * m_ritz_val[i].real(); + const Scalar t = norm(m_ritz_val[i]); + + decomp_ds.compute(m_fac.matrix_H(), s, t); + + // Q -> Q * Qi + decomp_ds.apply_YQ(Q); + // H -> Q'HQ + // Matrix Q = Matrix::Identity(m_ncv, m_ncv); + // decomp_ds.apply_YQ(Q); + // m_fac_H = Q.transpose() * m_fac_H * Q; + m_fac.compress_H(decomp_ds); + + i++; + } else { + // QR decomposition of H - mu * I, mu is real + decomp_hb.compute(m_fac.matrix_H(), m_ritz_val[i].real()); + + // Q -> Q * Qi + decomp_hb.apply_YQ(Q); + // H -> Q'HQ = RQ + mu * I + m_fac.compress_H(decomp_hb); + } + } + + m_fac.compress_V(Q); + m_fac.factorize_from(k, m_ncv, m_nmatop); + + retrieve_ritzpair(); + } + + // Calculates the number of converged Ritz values + Index num_converged(Scalar tol) + { + // thresh = tol * max(m_eps23, abs(theta)), theta for Ritz value + Array thresh = tol * m_ritz_val.head(m_nev).array().abs().max(m_eps23); + Array resid = m_ritz_est.head(m_nev).array().abs() * m_fac.f_norm(); + // Converged "wanted" Ritz values + m_ritz_conv = (resid < thresh); + + return m_ritz_conv.cast().sum(); + } + + // Returns the adjusted nev for restarting + Index nev_adjusted(Index nconv) + { + using std::abs; + + Index nev_new = m_nev; + for(Index i = m_nev; i < m_ncv; i++) + if(abs(m_ritz_est[i]) < m_near_0) nev_new++; + + // Adjust nev_new, according to dnaup2.f line 660~674 in ARPACK + nev_new += std::min(nconv, (m_ncv - nev_new) / 2); + if(nev_new == 1 && m_ncv >= 6) + nev_new = m_ncv / 2; + else if(nev_new == 1 && m_ncv > 3) + nev_new = 2; + + if(nev_new > m_ncv - 2) + nev_new = m_ncv - 2; + + // Increase nev by one if ritz_val[nev - 1] and + // ritz_val[nev] are conjugate pairs + if(is_complex(m_ritz_val[nev_new - 1]) && + is_conj(m_ritz_val[nev_new - 1], m_ritz_val[nev_new])) + { + nev_new++; + } + + return nev_new; + } + + // Retrieves and sorts Ritz values and Ritz vectors + void retrieve_ritzpair() + { + UpperHessenbergEigen decomp(m_fac.matrix_H()); + const ComplexVector& evals = decomp.eigenvalues(); + ComplexMatrix evecs = decomp.eigenvectors(); + + SortEigenvalue sorting(evals.data(), evals.size()); + std::vector ind = sorting.index(); + + // Copy the Ritz values and vectors to m_ritz_val and m_ritz_vec, respectively + for(Index i = 0; i < m_ncv; i++) + { + m_ritz_val[i] = evals[ind[i]]; + m_ritz_est[i] = evecs(m_ncv - 1, ind[i]); + } + for(Index i = 0; i < m_nev; i++) + { + m_ritz_vec.col(i).noalias() = evecs.col(ind[i]); + } + } + +protected: + // Sorts the first nev Ritz pairs in the specified order + // This is used to return the final results + virtual void sort_ritzpair(int sort_rule) + { + // First make sure that we have a valid index vector + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + std::vector ind = sorting.index(); + + switch(sort_rule) + { + case LARGEST_MAGN: + break; + case LARGEST_REAL: + { + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + ind = sorting.index(); + } + break; + case LARGEST_IMAG: + { + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + ind = sorting.index(); + } + break; + case SMALLEST_MAGN: + { + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + ind = sorting.index(); + } + break; + case SMALLEST_REAL: + { + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + ind = sorting.index(); + } + break; + case SMALLEST_IMAG: + { + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + ind = sorting.index(); + } + break; + default: + throw std::invalid_argument("unsupported sorting rule"); + } + + ComplexVector new_ritz_val(m_ncv); + ComplexMatrix new_ritz_vec(m_ncv, m_nev); + BoolArray new_ritz_conv(m_nev); + + for(Index i = 0; i < m_nev; i++) + { + new_ritz_val[i] = m_ritz_val[ind[i]]; + new_ritz_vec.col(i).noalias() = m_ritz_vec.col(ind[i]); + new_ritz_conv[i] = m_ritz_conv[ind[i]]; + } + + m_ritz_val.swap(new_ritz_val); + m_ritz_vec.swap(new_ritz_vec); + m_ritz_conv.swap(new_ritz_conv); + } + +public: + /// \cond + + GenEigsBase(OpType* op, BOpType* Bop, Index nev, Index ncv) : + m_op(op), + m_n(m_op->rows()), + m_nev(nev), + m_ncv(ncv > m_n ? m_n : ncv), + m_nmatop(0), + m_niter(0), + m_fac(ArnoldiOpType(op, Bop), m_ncv), + m_info(NOT_COMPUTED), + m_near_0(TypeTraits::min() * Scalar(10)), + m_eps(Eigen::NumTraits::epsilon()), + m_eps23(Eigen::numext::pow(m_eps, Scalar(2.0) / 3)) + { + if(nev < 1 || nev > m_n - 2) + throw std::invalid_argument("nev must satisfy 1 <= nev <= n - 2, n is the size of matrix"); + + if(ncv < nev + 2 || ncv > m_n) + throw std::invalid_argument("ncv must satisfy nev + 2 <= ncv <= n, n is the size of matrix"); + } + + /// + /// Virtual destructor + /// + virtual ~GenEigsBase() {} + + /// \endcond + + /// + /// Initializes the solver by providing an initial residual vector. + /// + /// \param init_resid Pointer to the initial residual vector. + /// + /// **Spectra** (and also **ARPACK**) uses an iterative algorithm + /// to find eigenvalues. This function allows the user to provide the initial + /// residual vector. + /// + void init(const Scalar* init_resid) + { + // Reset all matrices/vectors to zero + m_ritz_val.resize(m_ncv); + m_ritz_vec.resize(m_ncv, m_nev); + m_ritz_est.resize(m_ncv); + m_ritz_conv.resize(m_nev); + + m_ritz_val.setZero(); + m_ritz_vec.setZero(); + m_ritz_est.setZero(); + m_ritz_conv.setZero(); + + m_nmatop = 0; + m_niter = 0; + + // Initialize the Arnoldi factorization + MapConstVec v0(init_resid, m_n); + m_fac.init(v0, m_nmatop); + } + + /// + /// Initializes the solver by providing a random initial residual vector. + /// + /// This overloaded function generates a random initial residual vector + /// (with a fixed random seed) for the algorithm. Elements in the vector + /// follow independent Uniform(-0.5, 0.5) distribution. + /// + void init() + { + SimpleRandom rng(0); + Vector init_resid = rng.random_vec(m_n); + init(init_resid.data()); + } + + /// + /// Conducts the major computation procedure. + /// + /// \param maxit Maximum number of iterations allowed in the algorithm. + /// \param tol Precision parameter for the calculated eigenvalues. + /// \param sort_rule Rule to sort the eigenvalues and eigenvectors. + /// Supported values are + /// `Spectra::LARGEST_MAGN`, `Spectra::LARGEST_REAL`, + /// `Spectra::LARGEST_IMAG`, `Spectra::SMALLEST_MAGN`, + /// `Spectra::SMALLEST_REAL` and `Spectra::SMALLEST_IMAG`, + /// for example `LARGEST_MAGN` indicates that eigenvalues + /// with largest magnitude come first. + /// Note that this argument is only used to + /// **sort** the final result, and the **selection** rule + /// (e.g. selecting the largest or smallest eigenvalues in the + /// full spectrum) is specified by the template parameter + /// `SelectionRule` of GenEigsSolver. + /// + /// \return Number of converged eigenvalues. + /// + Index compute(Index maxit = 1000, Scalar tol = 1e-10, int sort_rule = LARGEST_MAGN) + { + // The m-step Arnoldi factorization + m_fac.factorize_from(1, m_ncv, m_nmatop); + retrieve_ritzpair(); + // Restarting + Index i, nconv = 0, nev_adj; + for(i = 0; i < maxit; i++) + { + nconv = num_converged(tol); + if(nconv >= m_nev) + break; + + nev_adj = nev_adjusted(nconv); + restart(nev_adj); + } + // Sorting results + sort_ritzpair(sort_rule); + + m_niter += i + 1; + m_info = (nconv >= m_nev) ? SUCCESSFUL : NOT_CONVERGING; + + return std::min(m_nev, nconv); + } + + /// + /// Returns the status of the computation. + /// The full list of enumeration values can be found in \ref Enumerations. + /// + int info() const { return m_info; } + + /// + /// Returns the number of iterations used in the computation. + /// + Index num_iterations() const { return m_niter; } + + /// + /// Returns the number of matrix operations used in the computation. + /// + Index num_operations() const { return m_nmatop; } + + /// + /// Returns the converged eigenvalues. + /// + /// \return A complex-valued vector containing the eigenvalues. + /// Returned vector type will be `Eigen::Vector, ...>`, depending on + /// the template parameter `Scalar` defined. + /// + ComplexVector eigenvalues() const + { + const Index nconv = m_ritz_conv.cast().sum(); + ComplexVector res(nconv); + + if(!nconv) + return res; + + Index j = 0; + for(Index i = 0; i < m_nev; i++) + { + if(m_ritz_conv[i]) + { + res[j] = m_ritz_val[i]; + j++; + } + } + + return res; + } + + /// + /// Returns the eigenvectors associated with the converged eigenvalues. + /// + /// \param nvec The number of eigenvectors to return. + /// + /// \return A complex-valued matrix containing the eigenvectors. + /// Returned matrix type will be `Eigen::Matrix, ...>`, + /// depending on the template parameter `Scalar` defined. + /// + ComplexMatrix eigenvectors(Index nvec) const + { + const Index nconv = m_ritz_conv.cast().sum(); + nvec = std::min(nvec, nconv); + ComplexMatrix res(m_n, nvec); + + if(!nvec) + return res; + + ComplexMatrix ritz_vec_conv(m_ncv, nvec); + Index j = 0; + for(Index i = 0; i < m_nev && j < nvec; i++) + { + if(m_ritz_conv[i]) + { + ritz_vec_conv.col(j).noalias() = m_ritz_vec.col(i); + j++; + } + } + + res.noalias() = m_fac.matrix_V() * ritz_vec_conv; + + return res; + } + + /// + /// Returns all converged eigenvectors. + /// + ComplexMatrix eigenvectors() const + { + return eigenvectors(m_nev); + } +}; + + +} // namespace Spectra + +#endif // GEN_EIGS_BASE_H diff --git a/src/external/Spectra/include/Spectra/GenEigsComplexShiftSolver.h b/src/external/Spectra/include/Spectra/GenEigsComplexShiftSolver.h new file mode 100644 index 00000000..2c1aee7f --- /dev/null +++ b/src/external/Spectra/include/Spectra/GenEigsComplexShiftSolver.h @@ -0,0 +1,156 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef GEN_EIGS_COMPLEX_SHIFT_SOLVER_H +#define GEN_EIGS_COMPLEX_SHIFT_SOLVER_H + +#include + +#include "GenEigsBase.h" +#include "Util/SelectionRule.h" +#include "MatOp/DenseGenComplexShiftSolve.h" + +namespace Spectra { + + +/// +/// \ingroup EigenSolver +/// +/// This class implements the eigen solver for general real matrices with +/// a complex shift value in the **shift-and-invert mode**. The background +/// knowledge of the shift-and-invert mode can be found in the documentation +/// of the SymEigsShiftSolver class. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// \tparam SelectionRule An enumeration value indicating the selection rule of +/// the shifted-and-inverted eigenvalues. +/// The full list of enumeration values can be found in +/// \ref Enumerations. +/// \tparam OpType The name of the matrix operation class. Users could either +/// use the DenseGenComplexShiftSolve wrapper class, or define their +/// own that implements all the public member functions as in +/// DenseGenComplexShiftSolve. +/// +template > +class GenEigsComplexShiftSolver: public GenEigsBase +{ +private: + typedef Eigen::Index Index; + typedef std::complex Complex; + typedef Eigen::Matrix Vector; + typedef Eigen::Matrix ComplexVector; + + const Scalar m_sigmar; + const Scalar m_sigmai; + + // First transform back the Ritz values, and then sort + void sort_ritzpair(int sort_rule) + { + using std::abs; + using std::sqrt; + using std::norm; + + // The eigenvalues we get from the iteration is + // nu = 0.5 * (1 / (lambda - sigma) + 1 / (lambda - conj(sigma))) + // So the eigenvalues of the original problem is + // 1 \pm sqrt(1 - 4 * nu^2 * sigmai^2) + // lambda = sigmar + ----------------------------------- + // 2 * nu + // We need to pick the correct root + // Let (lambdaj, vj) be the j-th eigen pair, then A * vj = lambdaj * vj + // and inv(A - r * I) * vj = 1 / (lambdaj - r) * vj + // where r is any shift value. + // We can use this identity to determine lambdaj + // + // op(v) computes Re(inv(A - r * I) * v) for any real v + // If r is real, then op(v) is also real. Let a = Re(vj), b = Im(vj), + // then op(vj) = op(a) + op(b) * i + // By comparing op(vj) and [1 / (lambdaj - r) * vj], we can determine + // which one is the correct root + + // Select a random shift value + SimpleRandom rng(0); + const Scalar shiftr = rng.random() * m_sigmar + rng.random(); + const Complex shift = Complex(shiftr, Scalar(0)); + this->m_op->set_shift(shiftr, Scalar(0)); + + // Calculate inv(A - r * I) * vj + Vector v_real(this->m_n), v_imag(this->m_n), OPv_real(this->m_n), OPv_imag(this->m_n); + const Scalar eps = Eigen::NumTraits::epsilon(); + for(Index i = 0; i < this->m_nev; i++) + { + v_real.noalias() = this->m_fac.matrix_V() * this->m_ritz_vec.col(i).real(); + v_imag.noalias() = this->m_fac.matrix_V() * this->m_ritz_vec.col(i).imag(); + this->m_op->perform_op(v_real.data(), OPv_real.data()); + this->m_op->perform_op(v_imag.data(), OPv_imag.data()); + + // Two roots computed from the quadratic equation + const Complex nu = this->m_ritz_val[i]; + const Complex root_part1 = m_sigmar + Scalar(0.5) / nu; + const Complex root_part2 = Scalar(0.5) * sqrt(Scalar(1) - Scalar(4) * m_sigmai * m_sigmai * (nu * nu)) / nu; + const Complex root1 = root_part1 + root_part2; + const Complex root2 = root_part1 - root_part2; + + // Test roots + Scalar err1 = Scalar(0), err2 = Scalar(0); + for(int k = 0; k < this->m_n; k++) + { + const Complex rhs1 = Complex(v_real[k], v_imag[k]) / (root1 - shift); + const Complex rhs2 = Complex(v_real[k], v_imag[k]) / (root2 - shift); + const Complex OPv = Complex(OPv_real[k], OPv_imag[k]); + err1 += norm(OPv - rhs1); + err2 += norm(OPv - rhs2); + } + + const Complex lambdaj = (err1 < err2) ? root1 : root2; + this->m_ritz_val[i] = lambdaj; + + if(abs(Eigen::numext::imag(lambdaj)) > eps) + { + this->m_ritz_val[i + 1] = Eigen::numext::conj(lambdaj); + i++; + } else { + this->m_ritz_val[i] = Complex(Eigen::numext::real(lambdaj), Scalar(0)); + } + } + + GenEigsBase::sort_ritzpair(sort_rule); + } +public: + /// + /// Constructor to create a eigen solver object using the shift-and-invert mode. + /// + /// \param op Pointer to the matrix operation object. This class should implement + /// the complex shift-solve operation of \f$A\f$: calculating + /// \f$\mathrm{Re}\{(A-\sigma I)^{-1}v\}\f$ for any vector \f$v\f$. Users could either + /// create the object from the DenseGenComplexShiftSolve wrapper class, or + /// define their own that implements all the public member functions + /// as in DenseGenComplexShiftSolve. + /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-2\f$, + /// where \f$n\f$ is the size of matrix. + /// \param ncv Parameter that controls the convergence speed of the algorithm. + /// Typically a larger `ncv` means faster convergence, but it may + /// also result in greater memory use and more matrix operations + /// in each iteration. This parameter must satisfy \f$nev+2 \le ncv \le n\f$, + /// and is advised to take \f$ncv \ge 2\cdot nev + 1\f$. + /// \param sigmar The real part of the shift. + /// \param sigmai The imaginary part of the shift. + /// + GenEigsComplexShiftSolver(OpType* op, Index nev, Index ncv, const Scalar& sigmar, const Scalar& sigmai) : + GenEigsBase(op, NULL, nev, ncv), + m_sigmar(sigmar), m_sigmai(sigmai) + { + this->m_op->set_shift(m_sigmar, m_sigmai); + } +}; + + +} // namespace Spectra + +#endif // GEN_EIGS_COMPLEX_SHIFT_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/GenEigsRealShiftSolver.h b/src/external/Spectra/include/Spectra/GenEigsRealShiftSolver.h new file mode 100644 index 00000000..a7e3da8e --- /dev/null +++ b/src/external/Spectra/include/Spectra/GenEigsRealShiftSolver.h @@ -0,0 +1,90 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef GEN_EIGS_REAL_SHIFT_SOLVER_H +#define GEN_EIGS_REAL_SHIFT_SOLVER_H + +#include + +#include "GenEigsBase.h" +#include "Util/SelectionRule.h" +#include "MatOp/DenseGenRealShiftSolve.h" + +namespace Spectra { + + +/// +/// \ingroup EigenSolver +/// +/// This class implements the eigen solver for general real matrices with +/// a real shift value in the **shift-and-invert mode**. The background +/// knowledge of the shift-and-invert mode can be found in the documentation +/// of the SymEigsShiftSolver class. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// \tparam SelectionRule An enumeration value indicating the selection rule of +/// the shifted-and-inverted eigenvalues. +/// The full list of enumeration values can be found in +/// \ref Enumerations. +/// \tparam OpType The name of the matrix operation class. Users could either +/// use the wrapper classes such as DenseGenRealShiftSolve and +/// SparseGenRealShiftSolve, or define their +/// own that implements all the public member functions as in +/// DenseGenRealShiftSolve. +/// +template > +class GenEigsRealShiftSolver: public GenEigsBase +{ +private: + typedef Eigen::Index Index; + typedef std::complex Complex; + typedef Eigen::Array ComplexArray; + + const Scalar m_sigma; + + // First transform back the Ritz values, and then sort + void sort_ritzpair(int sort_rule) + { + // The eigenvalues we get from the iteration is nu = 1 / (lambda - sigma) + // So the eigenvalues of the original problem is lambda = 1 / nu + sigma + ComplexArray ritz_val_org = Scalar(1.0) / this->m_ritz_val.head(this->m_nev).array() + m_sigma; + this->m_ritz_val.head(this->m_nev) = ritz_val_org; + GenEigsBase::sort_ritzpair(sort_rule); + } +public: + /// + /// Constructor to create a eigen solver object using the shift-and-invert mode. + /// + /// \param op Pointer to the matrix operation object. This class should implement + /// the shift-solve operation of \f$A\f$: calculating + /// \f$(A-\sigma I)^{-1}v\f$ for any vector \f$v\f$. Users could either + /// create the object from the wrapper class such as DenseGenRealShiftSolve, or + /// define their own that implements all the public member functions + /// as in DenseGenRealShiftSolve. + /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-2\f$, + /// where \f$n\f$ is the size of matrix. + /// \param ncv Parameter that controls the convergence speed of the algorithm. + /// Typically a larger `ncv` means faster convergence, but it may + /// also result in greater memory use and more matrix operations + /// in each iteration. This parameter must satisfy \f$nev+2 \le ncv \le n\f$, + /// and is advised to take \f$ncv \ge 2\cdot nev + 1\f$. + /// \param sigma The real-valued shift. + /// + GenEigsRealShiftSolver(OpType* op, Index nev, Index ncv, Scalar sigma) : + GenEigsBase(op, NULL, nev, ncv), + m_sigma(sigma) + { + this->m_op->set_shift(m_sigma); + } +}; + + +} // namespace Spectra + +#endif // GEN_EIGS_REAL_SHIFT_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/GenEigsSolver.h b/src/external/Spectra/include/Spectra/GenEigsSolver.h new file mode 100644 index 00000000..a6960acf --- /dev/null +++ b/src/external/Spectra/include/Spectra/GenEigsSolver.h @@ -0,0 +1,160 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef GEN_EIGS_SOLVER_H +#define GEN_EIGS_SOLVER_H + +#include + +#include "GenEigsBase.h" +#include "Util/SelectionRule.h" +#include "MatOp/DenseGenMatProd.h" + +namespace Spectra { + + +/// +/// \ingroup EigenSolver +/// +/// This class implements the eigen solver for general real matrices, i.e., +/// to solve \f$Ax=\lambda x\f$ for a possibly non-symmetric \f$A\f$ matrix. +/// +/// Most of the background information documented in the SymEigsSolver class +/// also applies to the GenEigsSolver class here, except that the eigenvalues +/// and eigenvectors of a general matrix can now be complex-valued. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// \tparam SelectionRule An enumeration value indicating the selection rule of +/// the requested eigenvalues, for example `LARGEST_MAGN` +/// to retrieve eigenvalues with the largest magnitude. +/// The full list of enumeration values can be found in +/// \ref Enumerations. +/// \tparam OpType The name of the matrix operation class. Users could either +/// use the wrapper classes such as DenseGenMatProd and +/// SparseGenMatProd, or define their +/// own that implements all the public member functions as in +/// DenseGenMatProd. +/// +/// An example that illustrates the usage of GenEigsSolver is give below: +/// +/// \code{.cpp} +/// #include +/// #include +/// // is implicitly included +/// #include +/// +/// using namespace Spectra; +/// +/// int main() +/// { +/// // We are going to calculate the eigenvalues of M +/// Eigen::MatrixXd M = Eigen::MatrixXd::Random(10, 10); +/// +/// // Construct matrix operation object using the wrapper class +/// DenseGenMatProd op(M); +/// +/// // Construct eigen solver object, requesting the largest +/// // (in magnitude, or norm) three eigenvalues +/// GenEigsSolver< double, LARGEST_MAGN, DenseGenMatProd > eigs(&op, 3, 6); +/// +/// // Initialize and compute +/// eigs.init(); +/// int nconv = eigs.compute(); +/// +/// // Retrieve results +/// Eigen::VectorXcd evalues; +/// if(eigs.info() == SUCCESSFUL) +/// evalues = eigs.eigenvalues(); +/// +/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; +/// +/// return 0; +/// } +/// \endcode +/// +/// And also an example for sparse matrices: +/// +/// \code{.cpp} +/// #include +/// #include +/// #include +/// #include +/// #include +/// +/// using namespace Spectra; +/// +/// int main() +/// { +/// // A band matrix with 1 on the main diagonal, 2 on the below-main subdiagonal, +/// // and 3 on the above-main subdiagonal +/// const int n = 10; +/// Eigen::SparseMatrix M(n, n); +/// M.reserve(Eigen::VectorXi::Constant(n, 3)); +/// for(int i = 0; i < n; i++) +/// { +/// M.insert(i, i) = 1.0; +/// if(i > 0) +/// M.insert(i - 1, i) = 3.0; +/// if(i < n - 1) +/// M.insert(i + 1, i) = 2.0; +/// } +/// +/// // Construct matrix operation object using the wrapper class SparseGenMatProd +/// SparseGenMatProd op(M); +/// +/// // Construct eigen solver object, requesting the largest three eigenvalues +/// GenEigsSolver< double, LARGEST_MAGN, SparseGenMatProd > eigs(&op, 3, 6); +/// +/// // Initialize and compute +/// eigs.init(); +/// int nconv = eigs.compute(); +/// +/// // Retrieve results +/// Eigen::VectorXcd evalues; +/// if(eigs.info() == SUCCESSFUL) +/// evalues = eigs.eigenvalues(); +/// +/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; +/// +/// return 0; +/// } +/// \endcode +template < typename Scalar = double, + int SelectionRule = LARGEST_MAGN, + typename OpType = DenseGenMatProd > +class GenEigsSolver: public GenEigsBase +{ +private: + typedef Eigen::Index Index; + +public: + /// + /// Constructor to create a solver object. + /// + /// \param op Pointer to the matrix operation object, which should implement + /// the matrix-vector multiplication operation of \f$A\f$: + /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either + /// create the object from the wrapper class such as DenseGenMatProd, or + /// define their own that implements all the public member functions + /// as in DenseGenMatProd. + /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-2\f$, + /// where \f$n\f$ is the size of matrix. + /// \param ncv Parameter that controls the convergence speed of the algorithm. + /// Typically a larger `ncv` means faster convergence, but it may + /// also result in greater memory use and more matrix operations + /// in each iteration. This parameter must satisfy \f$nev+2 \le ncv \le n\f$, + /// and is advised to take \f$ncv \ge 2\cdot nev + 1\f$. + /// + GenEigsSolver(OpType* op, Index nev, Index ncv) : + GenEigsBase(op, NULL, nev, ncv) + {} +}; + + +} // namespace Spectra + +#endif // GEN_EIGS_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/Arnoldi.h b/src/external/Spectra/include/Spectra/LinAlg/Arnoldi.h new file mode 100644 index 00000000..b9fa75b5 --- /dev/null +++ b/src/external/Spectra/include/Spectra/LinAlg/Arnoldi.h @@ -0,0 +1,283 @@ +// Copyright (C) 2018-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef ARNOLDI_H +#define ARNOLDI_H + +#include +#include // std::sqrt +#include // std::invalid_argument +#include // std::stringstream + +#include "../MatOp/internal/ArnoldiOp.h" +#include "../Util/TypeTraits.h" +#include "../Util/SimpleRandom.h" +#include "UpperHessenbergQR.h" +#include "DoubleShiftQR.h" + +namespace Spectra { + + +// Arnoldi factorization A * V = V * H + f * e' +// A: n x n +// V: n x k +// H: k x k +// f: n x 1 +// e: [0, ..., 0, 1] +// V and H are allocated of dimension m, so the maximum value of k is m +template +class Arnoldi +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapMat; + typedef Eigen::Map MapVec; + typedef Eigen::Map MapConstMat; + typedef Eigen::Map MapConstVec; + +protected: + ArnoldiOpType m_op; // Operators for the Arnoldi factorization + + const Index m_n; // dimension of A + const Index m_m; // maximum dimension of subspace V + Index m_k; // current dimension of subspace V + + Matrix m_fac_V; // V matrix in the Arnoldi factorization + Matrix m_fac_H; // H matrix in the Arnoldi factorization + Vector m_fac_f; // residual in the Arnoldi factorization + Scalar m_beta; // ||f||, B-norm of f + + const Scalar m_near_0; // a very small value, but 1.0 / m_near_0 does not overflow + // ~= 1e-307 for the "double" type + const Scalar m_eps; // the machine precision, ~= 1e-16 for the "double" type + + // Given orthonormal basis functions V, find a nonzero vector f such that V'Bf = 0 + // Assume that f has been properly allocated + void expand_basis(MapConstMat& V, const Index seed, Vector& f, Scalar& fnorm) + { + using std::sqrt; + + const Scalar thresh = m_eps * sqrt(Scalar(m_n)); + Vector Vf(V.cols()); + for(Index iter = 0; iter < 5; iter++) + { + // Randomly generate a new vector and orthogonalize it against V + SimpleRandom rng(seed + 123 * iter); + f.noalias() = rng.random_vec(m_n); + // f <- f - V * V'Bf, so that f is orthogonal to V in B-norm + m_op.trans_product(V, f, Vf); + f.noalias() -= V * Vf; + // fnorm <- ||f|| + fnorm = m_op.norm(f); + + // If fnorm is too close to zero, we try a new random vector, + // otherwise return the result + if(fnorm >= thresh) + return; + } + } + +public: + Arnoldi(const ArnoldiOpType& op, Index m) : + m_op(op), m_n(op.rows()), m_m(m), m_k(0), + m_near_0(TypeTraits::min() * Scalar(10)), + m_eps(Eigen::NumTraits::epsilon()) + {} + + virtual ~Arnoldi() {} + + // Const-reference to internal structures + const Matrix& matrix_V() const { return m_fac_V; } + const Matrix& matrix_H() const { return m_fac_H; } + const Vector& vector_f() const { return m_fac_f; } + Scalar f_norm() const { return m_beta; } + Index subspace_dim() const { return m_k; } + + // Initialize with an operator and an initial vector + void init(MapConstVec& v0, Index& op_counter) + { + m_fac_V.resize(m_n, m_m); + m_fac_H.resize(m_m, m_m); + m_fac_f.resize(m_n); + m_fac_H.setZero(); + + // Verify the initial vector + const Scalar v0norm = m_op.norm(v0); + if(v0norm < m_near_0) + throw std::invalid_argument("initial residual vector cannot be zero"); + + // Points to the first column of V + MapVec v(m_fac_V.data(), m_n); + + // Normalize + v.noalias() = v0 / v0norm; + + // Compute H and f + Vector w(m_n); + m_op.perform_op(v.data(), w.data()); + op_counter++; + + m_fac_H(0, 0) = m_op.inner_product(v, w); + m_fac_f.noalias() = w - v * m_fac_H(0, 0); + + // In some cases f is zero in exact arithmetics, but due to rounding errors + // it may contain tiny fluctuations. When this happens, we force f to be zero + if(m_fac_f.cwiseAbs().maxCoeff() < m_eps) + { + m_fac_f.setZero(); + m_beta = Scalar(0); + } else { + m_beta = m_op.norm(m_fac_f); + } + + // Indicate that this is a step-1 factorization + m_k = 1; + } + + // Arnoldi factorization starting from step-k + virtual void factorize_from(Index from_k, Index to_m, Index& op_counter) + { + using std::sqrt; + + if(to_m <= from_k) return; + + if(from_k > m_k) + { + std::stringstream msg; + msg << "Arnoldi: from_k (= " << from_k << + ") is larger than the current subspace dimension (= " << + m_k << ")"; + throw std::invalid_argument(msg.str()); + } + + const Scalar beta_thresh = m_eps * sqrt(Scalar(m_n)); + + // Pre-allocate vectors + Vector Vf(to_m); + Vector w(m_n); + + // Keep the upperleft k x k submatrix of H and set other elements to 0 + m_fac_H.rightCols(m_m - from_k).setZero(); + m_fac_H.block(from_k, 0, m_m - from_k, from_k).setZero(); + + for(Index i = from_k; i <= to_m - 1; i++) + { + bool restart = false; + // If beta = 0, then the next V is not full rank + // We need to generate a new residual vector that is orthogonal + // to the current V, which we call a restart + if(m_beta < m_near_0) + { + MapConstMat V(m_fac_V.data(), m_n, i); // The first i columns + expand_basis(V, 2 * i, m_fac_f, m_beta); + restart = true; + } + + // v <- f / ||f|| + m_fac_V.col(i).noalias() = m_fac_f / m_beta; // The (i+1)-th column + + // Note that H[i+1, i] equals to the unrestarted beta + m_fac_H(i, i - 1) = restart ? Scalar(0) : m_beta; + + // w <- A * v, v = m_fac_V.col(i) + m_op.perform_op(&m_fac_V(0, i), w.data()); + op_counter++; + + const Index i1 = i + 1; + // First i+1 columns of V + MapConstMat Vs(m_fac_V.data(), m_n, i1); + // h = m_fac_H(0:i, i) + MapVec h(&m_fac_H(0, i), i1); + // h <- V'Bw + m_op.trans_product(Vs, w, h); + + // f <- w - V * h + m_fac_f.noalias() = w - Vs * h; + m_beta = m_op.norm(m_fac_f); + + if(m_beta > Scalar(0.717) * m_op.norm(h)) + continue; + + // f/||f|| is going to be the next column of V, so we need to test + // whether V'B(f/||f||) ~= 0 + m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); + Scalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); + // If not, iteratively correct the residual + int count = 0; + while(count < 5 && ortho_err > m_eps * m_beta) + { + // There is an edge case: when beta=||f|| is close to zero, f mostly consists + // of noises of rounding errors, so the test [ortho_err < eps * beta] is very + // likely to fail. In particular, if beta=0, then the test is ensured to fail. + // Hence when this happens, we force f to be zero, and then restart in the + // next iteration. + if(m_beta < beta_thresh) + { + m_fac_f.setZero(); + m_beta = Scalar(0); + break; + } + + // f <- f - V * Vf + m_fac_f.noalias() -= Vs * Vf.head(i1); + // h <- h + Vf + h.noalias() += Vf.head(i1); + // beta <- ||f|| + m_beta = m_op.norm(m_fac_f); + + m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); + ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); + count++; + } + } + + // Indicate that this is a step-m factorization + m_k = to_m; + } + + // Apply H -> Q'HQ, where Q is from a double shift QR decomposition + void compress_H(const DoubleShiftQR& decomp) + { + decomp.matrix_QtHQ(m_fac_H); + m_k -= 2; + } + + // Apply H -> Q'HQ, where Q is from an upper Hessenberg QR decomposition + void compress_H(const UpperHessenbergQR& decomp) + { + decomp.matrix_QtHQ(m_fac_H); + m_k--; + } + + // Apply V -> VQ and compute the new f. + // Should be called after compress_H(), since m_k is updated there. + // Only need to update the first k+1 columns of V + // The first (m - k + i) elements of the i-th column of Q are non-zero, + // and the rest are zero + void compress_V(const Matrix& Q) + { + Matrix Vs(m_n, m_k + 1); + for(Index i = 0; i < m_k; i++) + { + const Index nnz = m_m - m_k + i + 1; + MapConstVec q(&Q(0, i), nnz); + Vs.col(i).noalias() = m_fac_V.leftCols(nnz) * q; + } + Vs.col(m_k).noalias() = m_fac_V * Q.col(m_k); + m_fac_V.leftCols(m_k + 1).noalias() = Vs; + + Vector fk = m_fac_f * Q(m_m - 1, m_k - 1) + m_fac_V.col(m_k) * m_fac_H(m_k, m_k - 1); + m_fac_f.swap(fk); + m_beta = m_op.norm(m_fac_f); + } +}; + + +} // namespace Spectra + +#endif // ARNOLDI_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/BKLDLT.h b/src/external/Spectra/include/Spectra/LinAlg/BKLDLT.h new file mode 100644 index 00000000..5509749b --- /dev/null +++ b/src/external/Spectra/include/Spectra/LinAlg/BKLDLT.h @@ -0,0 +1,522 @@ +// Copyright (C) 2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef BK_LDLT_H +#define BK_LDLT_H + +#include +#include +#include + +#include "../Util/CompInfo.h" + +namespace Spectra { + + +// Bunch-Kaufman LDLT decomposition +// References: +// 1. Bunch, J. R., & Kaufman, L. (1977). Some stable methods for calculating inertia and solving symmetric linear systems. +// Mathematics of computation, 31(137), 163-179. +// 2. Golub, G. H., & Van Loan, C. F. (2012). Matrix computations (Vol. 3). JHU press. Section 4.4. +// 3. Bunch-Parlett diagonal pivoting +// 4. Ashcraft, C., Grimes, R. G., & Lewis, J. G. (1998). Accurate symmetric indefinite linear equation solvers. +// SIAM Journal on Matrix Analysis and Applications, 20(2), 513-561. +template +class BKLDLT +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapVec; + typedef Eigen::Map MapConstVec; + + typedef Eigen::Matrix IntVector; + typedef Eigen::Ref GenericVector; + typedef Eigen::Ref GenericMatrix; + typedef const Eigen::Ref ConstGenericMatrix; + typedef const Eigen::Ref ConstGenericVector; + + Index m_n; + Vector m_data; // storage for a lower-triangular matrix + std::vector m_colptr; // pointers to columns + IntVector m_perm; // [-2, -1, 3, 1, 4, 5]: 0 <-> 2, 1 <-> 1, 2 <-> 3, 3 <-> 1, 4 <-> 4, 5 <-> 5 + std::vector< std::pair > m_permc; // compressed version of m_perm: [(0, 2), (2, 3), (3, 1)] + + bool m_computed; + int m_info; + + // Access to elements + // Pointer to the k-th column + Scalar* col_pointer(Index k) { return m_colptr[k]; } + // A[i, j] -> m_colptr[j][i - j], i >= j + Scalar& coeff(Index i, Index j) { return m_colptr[j][i - j]; } + const Scalar& coeff(Index i, Index j) const { return m_colptr[j][i - j]; } + // A[i, i] -> m_colptr[i][0] + Scalar& diag_coeff(Index i) { return m_colptr[i][0]; } + const Scalar& diag_coeff(Index i) const { return m_colptr[i][0]; } + + // Compute column pointers + void compute_pointer() + { + m_colptr.clear(); + m_colptr.reserve(m_n); + Scalar* head = m_data.data(); + + for(Index i = 0; i < m_n; i++) + { + m_colptr.push_back(head); + head += (m_n - i); + } + } + + // Copy mat - shift * I to m_data + void copy_data(ConstGenericMatrix& mat, int uplo, const Scalar& shift) + { + if(uplo == Eigen::Lower) + { + for(Index j = 0; j < m_n; j++) + { + const Scalar* begin = &mat.coeffRef(j, j); + const Index len = m_n - j; + std::copy(begin, begin + len, col_pointer(j)); + diag_coeff(j) -= shift; + } + } else { + Scalar* dest = m_data.data(); + for(Index i = 0; i < m_n; i++) + { + for(Index j = i; j < m_n; j++, dest++) + { + *dest = mat.coeff(i, j); + } + diag_coeff(i) -= shift; + } + } + } + + // Compute compressed permutations + void compress_permutation() + { + for(Index i = 0; i < m_n; i++) + { + // Recover the permutation action + const Index perm = (m_perm[i] >= 0) ? (m_perm[i]) : (-m_perm[i] - 1); + if(perm != i) + m_permc.push_back(std::make_pair(i, perm)); + } + } + + // Working on the A[k:end, k:end] submatrix + // Exchange k <-> r + // Assume r >= k + void pivoting_1x1(Index k, Index r) + { + // No permutation + if(k == r) + { + m_perm[k] = r; + return; + } + + // A[k, k] <-> A[r, r] + std::swap(diag_coeff(k), diag_coeff(r)); + + // A[(r+1):end, k] <-> A[(r+1):end, r] + std::swap_ranges(&coeff(r + 1, k), col_pointer(k + 1), &coeff(r + 1, r)); + + // A[(k+1):(r-1), k] <-> A[r, (k+1):(r-1)] + Scalar* src = &coeff(k + 1, k); + for(Index j = k + 1; j < r; j++, src++) + { + std::swap(*src, coeff(r, j)); + } + + m_perm[k] = r; + } + + // Working on the A[k:end, k:end] submatrix + // Exchange [k+1, k] <-> [r, p] + // Assume p >= k, r >= k+1 + void pivoting_2x2(Index k, Index r, Index p) + { + pivoting_1x1(k, p); + pivoting_1x1(k + 1, r); + + // A[k+1, k] <-> A[r, k] + std::swap(coeff(k + 1, k), coeff(r, k)); + + // Use negative signs to indicate a 2x2 block + // Also minus one to distinguish a negative zero from a positive zero + m_perm[k] = -m_perm[k] - 1; + m_perm[k + 1] = -m_perm[k + 1] - 1; + } + + // A[r1, c1:c2] <-> A[r2, c1:c2] + // Assume r2 >= r1 > c2 >= c1 + void interchange_rows(Index r1, Index r2, Index c1, Index c2) + { + if(r1 == r2) + return; + + for(Index j = c1; j <= c2; j++) + { + std::swap(coeff(r1, j), coeff(r2, j)); + } + } + + // lambda = |A[r, k]| = max{|A[k+1, k]|, ..., |A[end, k]|} + // Largest (in magnitude) off-diagonal element in the first column of the current reduced matrix + // r is the row index + // Assume k < end + Scalar find_lambda(Index k, Index& r) + { + using std::abs; + + const Scalar* head = col_pointer(k); // => A[k, k] + const Scalar* end = col_pointer(k + 1); + // Start with r=k+1, lambda=A[k+1, k] + r = k + 1; + Scalar lambda = abs(head[1]); + // Scan remaining elements + for(const Scalar* ptr = head + 2; ptr < end; ptr++) + { + const Scalar abs_elem = abs(*ptr); + if(lambda < abs_elem) + { + lambda = abs_elem; + r = k + (ptr - head); + } + } + + return lambda; + } + + // sigma = |A[p, r]| = max {|A[k, r]|, ..., |A[end, r]|} \ {A[r, r]} + // Largest (in magnitude) off-diagonal element in the r-th column of the current reduced matrix + // p is the row index + // Assume k < r < end + Scalar find_sigma(Index k, Index r, Index& p) + { + using std::abs; + + // First search A[r+1, r], ..., A[end, r], which has the same task as find_lambda() + // If r == end, we skip this search + Scalar sigma = Scalar(-1); + if(r < m_n - 1) + sigma = find_lambda(r, p); + + // Then search A[k, r], ..., A[r-1, r], which maps to A[r, k], ..., A[r, r-1] + for(Index j = k; j < r; j++) + { + const Scalar abs_elem = abs(coeff(r, j)); + if(sigma < abs_elem) + { + sigma = abs_elem; + p = j; + } + } + + return sigma; + } + + // Generate permutations and apply to A + // Return true if the resulting pivoting is 1x1, and false if 2x2 + bool permutate_mat(Index k, const Scalar& alpha) + { + using std::abs; + + Index r = k, p = k; + const Scalar lambda = find_lambda(k, r); + + // If lambda=0, no need to interchange + if(lambda > Scalar(0)) + { + const Scalar abs_akk = abs(diag_coeff(k)); + // If |A[k, k]| >= alpha * lambda, no need to interchange + if(abs_akk < alpha * lambda) + { + const Scalar sigma = find_sigma(k, r, p); + + // If sigma * |A[k, k]| >= alpha * lambda^2, no need to interchange + if(sigma * abs_akk < alpha * lambda * lambda) + { + if(abs_akk >= alpha * sigma) + { + // Permutation on A + pivoting_1x1(k, r); + + // Permutation on L + interchange_rows(k, r, 0, k - 1); + return true; + } else { + // There are two versions of permutation here + // 1. A[k+1, k] <-> A[r, k] + // 2. A[k+1, k] <-> A[r, p], where p >= k and r >= k+1 + // + // Version 1 and 2 are used by Ref[1] and Ref[2], respectively + + // Version 1 implementation + p = k; + + // Version 2 implementation + // [r, p] and [p, r] are symmetric, but we need to make sure + // p >= k and r >= k+1, so it is safe to always make r > p + // One exception is when min{r,p} == k+1, in which case we make + // r = k+1, so that only one permutation needs to be performed + /* const Index rp_min = std::min(r, p); + const Index rp_max = std::max(r, p); + if(rp_min == k + 1) + { + r = rp_min; p = rp_max; + } else { + r = rp_max; p = rp_min; + } */ + + // Right now we use Version 1 since it reduces the overhead of interchange + + // Permutation on A + pivoting_2x2(k, r, p); + // Permutation on L + interchange_rows(k, p, 0, k - 1); + interchange_rows(k + 1, r, 0, k - 1); + return false; + } + } + } + } + + return true; + } + + // E = [e11, e12] + // [e21, e22] + // Overwrite E with inv(E) + void inverse_inplace_2x2(Scalar& e11, Scalar& e21, Scalar& e22) const + { + // inv(E) = [d11, d12], d11 = e22/delta, d21 = -e21/delta, d22 = e11/delta + // [d21, d22] + const Scalar delta = e11 * e22 - e21 * e21; + std::swap(e11, e22); + e11 /= delta; + e22 /= delta; + e21 = -e21 / delta; + } + + // Return value is the status, SUCCESSFUL/NUMERICAL_ISSUE + int gaussian_elimination_1x1(Index k) + { + // D = 1 / A[k, k] + const Scalar akk = diag_coeff(k); + // Return NUMERICAL_ISSUE if not invertible + if(akk == Scalar(0)) + return NUMERICAL_ISSUE; + + diag_coeff(k) = Scalar(1) / akk; + + // B -= l * l' / A[k, k], B := A[(k+1):end, (k+1):end], l := L[(k+1):end, k] + Scalar* lptr = col_pointer(k) + 1; + const Index ldim = m_n - k - 1; + MapVec l(lptr, ldim); + for(Index j = 0; j < ldim; j++) + { + MapVec(col_pointer(j + k + 1), ldim - j).noalias() -= (lptr[j] / akk) * l.tail(ldim - j); + } + + // l /= A[k, k] + l /= akk; + + return SUCCESSFUL; + } + + // Return value is the status, SUCCESSFUL/NUMERICAL_ISSUE + int gaussian_elimination_2x2(Index k) + { + // D = inv(E) + Scalar& e11 = diag_coeff(k); + Scalar& e21 = coeff(k + 1, k); + Scalar& e22 = diag_coeff(k + 1); + // Return NUMERICAL_ISSUE if not invertible + if(e11 * e22 - e21 * e21 == Scalar(0)) + return NUMERICAL_ISSUE; + + inverse_inplace_2x2(e11, e21, e22); + + // X = l * inv(E), l := L[(k+2):end, k:(k+1)] + Scalar* l1ptr = &coeff(k + 2, k); + Scalar* l2ptr = &coeff(k + 2, k + 1); + const Index ldim = m_n - k - 2; + MapVec l1(l1ptr, ldim), l2(l2ptr, ldim); + + Eigen::Matrix X(ldim, 2); + X.col(0).noalias() = l1 * e11 + l2 * e21; + X.col(1).noalias() = l1 * e21 + l2 * e22; + + // B -= l * inv(E) * l' = X * l', B = A[(k+2):end, (k+2):end] + for(Index j = 0; j < ldim; j++) + { + MapVec(col_pointer(j + k + 2), ldim - j).noalias() -= (X.col(0).tail(ldim - j) * l1ptr[j] + X.col(1).tail(ldim - j) * l2ptr[j]); + } + + // l = X + l1.noalias() = X.col(0); + l2.noalias() = X.col(1); + + return SUCCESSFUL; + } + +public: + BKLDLT() : + m_n(0), m_computed(false), m_info(NOT_COMPUTED) + {} + + // Factorize mat - shift * I + BKLDLT(ConstGenericMatrix& mat, int uplo = Eigen::Lower, const Scalar& shift = Scalar(0)) : + m_n(mat.rows()), m_computed(false), m_info(NOT_COMPUTED) + { + compute(mat, uplo, shift); + } + + void compute(ConstGenericMatrix& mat, int uplo = Eigen::Lower, const Scalar& shift = Scalar(0)) + { + using std::abs; + + m_n = mat.rows(); + if(m_n != mat.cols()) + throw std::invalid_argument("BKLDLT: matrix must be square"); + + m_perm.setLinSpaced(m_n, 0, m_n - 1); + m_permc.clear(); + + // Copy data + m_data.resize((m_n * (m_n + 1)) / 2); + compute_pointer(); + copy_data(mat, uplo, shift); + + const Scalar alpha = (1.0 + std::sqrt(17.0)) / 8.0; + Index k = 0; + for(k = 0; k < m_n - 1; k++) + { + // 1. Interchange rows and columns of A, and save the result to m_perm + bool is_1x1 = permutate_mat(k, alpha); + + // 2. Gaussian elimination + if(is_1x1) + { + m_info = gaussian_elimination_1x1(k); + } else { + m_info = gaussian_elimination_2x2(k); + k++; + } + + // 3. Check status + if(m_info != SUCCESSFUL) + break; + } + // Invert the last 1x1 block if it exists + if(k == m_n - 1) + { + const Scalar akk = diag_coeff(k); + if(akk == Scalar(0)) + m_info = NUMERICAL_ISSUE; + + diag_coeff(k) = Scalar(1) / diag_coeff(k); + } + + compress_permutation(); + + m_computed = true; + } + + // Solve Ax=b + void solve_inplace(GenericVector b) const + { + if(!m_computed) + throw std::logic_error("BKLDLT: need to call compute() first"); + + // PAP' = LDL' + // 1. b -> Pb + Scalar* x = b.data(); + MapVec res(x, m_n); + Index npermc = m_permc.size(); + for(Index i = 0; i < npermc; i++) + { + std::swap(x[m_permc[i].first], x[m_permc[i].second]); + } + + // 2. Lz = Pb + // If m_perm[end] < 0, then end with m_n - 3, otherwise end with m_n - 2 + const Index end = (m_perm[m_n - 1] < 0) ? (m_n - 3) : (m_n - 2); + for(Index i = 0; i <= end; i++) + { + const Index b1size = m_n - i - 1; + const Index b2size = b1size - 1; + if(m_perm[i] >= 0) + { + MapConstVec l(&coeff(i + 1, i), b1size); + res.segment(i + 1, b1size).noalias() -= l * x[i]; + } else { + MapConstVec l1(&coeff(i + 2, i), b2size); + MapConstVec l2(&coeff(i + 2, i + 1), b2size); + res.segment(i + 2, b2size).noalias() -= (l1 * x[i] + l2 * x[i + 1]); + i++; + } + } + + // 3. Dw = z + for(Index i = 0; i < m_n; i++) + { + const Scalar e11 = diag_coeff(i); + if(m_perm[i] >= 0) + { + x[i] *= e11; + } else { + const Scalar e21 = coeff(i + 1, i), e22 = diag_coeff(i + 1); + const Scalar wi = x[i] * e11 + x[i + 1] * e21; + x[i + 1] = x[i] * e21 + x[i + 1] * e22; + x[i] = wi; + i++; + } + } + + // 4. L'y = w + // If m_perm[end] < 0, then start with m_n - 3, otherwise start with m_n - 2 + Index i = (m_perm[m_n - 1] < 0) ? (m_n - 3) : (m_n - 2); + for(; i >= 0; i--) + { + const Index ldim = m_n - i - 1; + MapConstVec l(&coeff(i + 1, i), ldim); + x[i] -= res.segment(i + 1, ldim).dot(l); + + if(m_perm[i] < 0) + { + MapConstVec l2(&coeff(i + 1, i - 1), ldim); + x[i - 1] -= res.segment(i + 1, ldim).dot(l2); + i--; + } + } + + // 5. x = P'y + for(Index i = npermc - 1; i >= 0; i--) + { + std::swap(x[m_permc[i].first], x[m_permc[i].second]); + } + } + + Vector solve(ConstGenericVector& b) const + { + Vector res = b; + solve_inplace(res); + return res; + } + + int info() const { return m_info; } +}; + + +} // namespace Spectra + +#endif // BK_LDLT_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/DoubleShiftQR.h b/src/external/Spectra/include/Spectra/LinAlg/DoubleShiftQR.h new file mode 100644 index 00000000..2191909a --- /dev/null +++ b/src/external/Spectra/include/Spectra/LinAlg/DoubleShiftQR.h @@ -0,0 +1,378 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef DOUBLE_SHIFT_QR_H +#define DOUBLE_SHIFT_QR_H + +#include +#include // std::vector +#include // std::min, std::fill, std::copy +#include // std::abs, std::sqrt, std::pow +#include // std::invalid_argument, std::logic_error + +#include "../Util/TypeTraits.h" + +namespace Spectra { + + +template +class DoubleShiftQR +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Matrix3X; + typedef Eigen::Matrix Vector; + typedef Eigen::Array IntArray; + + typedef Eigen::Ref GenericMatrix; + typedef const Eigen::Ref ConstGenericMatrix; + + Index m_n; // Dimension of the matrix + Matrix m_mat_H; // A copy of the matrix to be factorized + Scalar m_shift_s; // Shift constant + Scalar m_shift_t; // Shift constant + Matrix3X m_ref_u; // Householder reflectors + IntArray m_ref_nr; // How many rows does each reflector affects + // 3 - A general reflector + // 2 - A Givens rotation + // 1 - An identity transformation + const Scalar m_near_0; // a very small value, but 1.0 / m_safe_min does not overflow + // ~= 1e-307 for the "double" type + const Scalar m_eps; // the machine precision, + // e.g. ~= 1e-16 for the "double" type + const Scalar m_eps_rel; + const Scalar m_eps_abs; + bool m_computed; // Whether matrix has been factorized + + void compute_reflector(const Scalar& x1, const Scalar& x2, const Scalar& x3, Index ind) + { + using std::abs; + + Scalar* u = &m_ref_u.coeffRef(0, ind); + unsigned char* nr = m_ref_nr.data(); + // In general case the reflector affects 3 rows + nr[ind] = 3; + Scalar x2x3 = Scalar(0); + // If x3 is zero, decrease nr by 1 + if(abs(x3) < m_near_0) + { + // If x2 is also zero, nr will be 1, and we can exit this function + if(abs(x2) < m_near_0) + { + nr[ind] = 1; + return; + } else { + nr[ind] = 2; + } + x2x3 = abs(x2); + } else { + x2x3 = Eigen::numext::hypot(x2, x3); + } + + // x1' = x1 - rho * ||x|| + // rho = -sign(x1), if x1 == 0, we choose rho = 1 + Scalar x1_new = x1 - ((x1 <= 0) - (x1 > 0)) * Eigen::numext::hypot(x1, x2x3); + Scalar x_norm = Eigen::numext::hypot(x1_new, x2x3); + // Double check the norm of new x + if(x_norm < m_near_0) + { + nr[ind] = 1; + return; + } + u[0] = x1_new / x_norm; + u[1] = x2 / x_norm; + u[2] = x3 / x_norm; + } + + void compute_reflector(const Scalar* x, Index ind) + { + compute_reflector(x[0], x[1], x[2], ind); + } + + // Update the block X = H(il:iu, il:iu) + void update_block(Index il, Index iu) + { + // Block size + const Index bsize = iu - il + 1; + + // If block size == 1, there is no need to apply reflectors + if(bsize == 1) + { + m_ref_nr.coeffRef(il) = 1; + return; + } + + const Scalar x00 = m_mat_H.coeff(il, il), + x01 = m_mat_H.coeff(il, il + 1), + x10 = m_mat_H.coeff(il + 1, il), + x11 = m_mat_H.coeff(il + 1, il + 1); + // m00 = x00 * (x00 - s) + x01 * x10 + t + const Scalar m00 = x00 * (x00 - m_shift_s) + x01 * x10 + m_shift_t; + // m10 = x10 * (x00 + x11 - s) + const Scalar m10 = x10 * (x00 + x11 - m_shift_s); + + // For block size == 2, do a Givens rotation on M = X * X - s * X + t * I + if(bsize == 2) + { + // This causes nr=2 + compute_reflector(m00, m10, 0, il); + // Apply the reflector to X + apply_PX(m_mat_H.block(il, il, 2, m_n - il), m_n, il); + apply_XP(m_mat_H.block(0, il, il + 2, 2), m_n, il); + + m_ref_nr.coeffRef(il + 1) = 1; + return; + } + + // For block size >=3, use the regular strategy + // m20 = x21 * x10 + const Scalar m20 = m_mat_H.coeff(il + 2, il + 1) * m_mat_H.coeff(il + 1, il); + compute_reflector(m00, m10, m20, il); + + // Apply the first reflector + apply_PX(m_mat_H.block(il, il, 3, m_n - il), m_n, il); + apply_XP(m_mat_H.block(0, il, il + std::min(bsize, Index(4)), 3), m_n, il); + + // Calculate the following reflectors + // If entering this loop, block size is at least 4. + for(Index i = 1; i < bsize - 2; i++) + { + compute_reflector(&m_mat_H.coeffRef(il + i, il + i - 1), il + i); + // Apply the reflector to X + apply_PX(m_mat_H.block(il + i, il + i - 1, 3, m_n - il - i + 1), m_n, il + i); + apply_XP(m_mat_H.block(0, il + i, il + std::min(bsize, Index(i + 4)), 3), m_n, il + i); + } + + // The last reflector + // This causes nr=2 + compute_reflector(m_mat_H.coeff(iu - 1, iu - 2), m_mat_H.coeff(iu, iu - 2), 0, iu - 1); + // Apply the reflector to X + apply_PX(m_mat_H.block(iu - 1, iu - 2, 2, m_n - iu + 2), m_n, iu - 1); + apply_XP(m_mat_H.block(0, iu - 1, il + bsize, 2), m_n, iu - 1); + + m_ref_nr.coeffRef(iu) = 1; + } + + // P = I - 2 * u * u' = P' + // PX = X - 2 * u * (u'X) + void apply_PX(GenericMatrix X, Index stride, Index u_ind) const + { + const Index nr = m_ref_nr.coeff(u_ind); + if(nr == 1) + return; + + const Scalar u0 = m_ref_u.coeff(0, u_ind), + u1 = m_ref_u.coeff(1, u_ind); + const Scalar u0_2 = Scalar(2) * u0, + u1_2 = Scalar(2) * u1; + + const Index nrow = X.rows(); + const Index ncol = X.cols(); + + Scalar* xptr = X.data(); + if(nr == 2 || nrow == 2) + { + for(Index i = 0; i < ncol; i++, xptr += stride) + { + const Scalar tmp = u0_2 * xptr[0] + u1_2 * xptr[1]; + xptr[0] -= tmp * u0; + xptr[1] -= tmp * u1; + } + } else { + const Scalar u2 = m_ref_u.coeff(2, u_ind); + const Scalar u2_2 = Scalar(2) * u2; + for(Index i = 0; i < ncol; i++, xptr += stride) + { + const Scalar tmp = u0_2 * xptr[0] + u1_2 * xptr[1] + u2_2 * xptr[2]; + xptr[0] -= tmp * u0; + xptr[1] -= tmp * u1; + xptr[2] -= tmp * u2; + } + } + } + + // x is a pointer to a vector + // Px = x - 2 * dot(x, u) * u + void apply_PX(Scalar* x, Index u_ind) const + { + const Index nr = m_ref_nr.coeff(u_ind); + if(nr == 1) + return; + + const Scalar u0 = m_ref_u.coeff(0, u_ind), + u1 = m_ref_u.coeff(1, u_ind), + u2 = m_ref_u.coeff(2, u_ind); + + // When the reflector only contains two elements, u2 has been set to zero + const bool nr_is_2 = (nr == 2); + const Scalar dot2 = Scalar(2) * (x[0] * u0 + x[1] * u1 + (nr_is_2 ? 0 : (x[2] * u2))); + x[0] -= dot2 * u0; + x[1] -= dot2 * u1; + if(!nr_is_2) + x[2] -= dot2 * u2; + } + + // XP = X - 2 * (X * u) * u' + void apply_XP(GenericMatrix X, Index stride, Index u_ind) const + { + const Index nr = m_ref_nr.coeff(u_ind); + if(nr == 1) + return; + + const Scalar u0 = m_ref_u.coeff(0, u_ind), + u1 = m_ref_u.coeff(1, u_ind); + const Scalar u0_2 = Scalar(2) * u0, + u1_2 = Scalar(2) * u1; + + const int nrow = X.rows(); + const int ncol = X.cols(); + Scalar *X0 = X.data(), *X1 = X0 + stride; // X0 => X.col(0), X1 => X.col(1) + + if(nr == 2 || ncol == 2) + { + // tmp = 2 * u0 * X0 + 2 * u1 * X1 + // X0 => X0 - u0 * tmp + // X1 => X1 - u1 * tmp + for(Index i = 0; i < nrow; i++) + { + const Scalar tmp = u0_2 * X0[i] + u1_2 * X1[i]; + X0[i] -= tmp * u0; + X1[i] -= tmp * u1; + } + } else { + Scalar* X2 = X1 + stride; // X2 => X.col(2) + const Scalar u2 = m_ref_u.coeff(2, u_ind); + const Scalar u2_2 = Scalar(2) * u2; + for(Index i = 0; i < nrow; i++) + { + const Scalar tmp = u0_2 * X0[i] + u1_2 * X1[i] + u2_2 * X2[i]; + X0[i] -= tmp * u0; + X1[i] -= tmp * u1; + X2[i] -= tmp * u2; + } + } + } + +public: + DoubleShiftQR(Index size) : + m_n(size), + m_near_0(TypeTraits::min() * Scalar(10)), + m_eps(Eigen::NumTraits::epsilon()), + m_eps_rel(m_eps), + m_eps_abs(m_near_0 * (m_n / m_eps)), + m_computed(false) + {} + + DoubleShiftQR(ConstGenericMatrix& mat, const Scalar& s, const Scalar& t) : + m_n(mat.rows()), + m_mat_H(m_n, m_n), + m_shift_s(s), + m_shift_t(t), + m_ref_u(3, m_n), + m_ref_nr(m_n), + m_near_0(TypeTraits::min() * Scalar(10)), + m_eps(Eigen::NumTraits::epsilon()), + m_eps_rel(m_eps), + m_eps_abs(m_near_0 * (m_n / m_eps)), + m_computed(false) + { + compute(mat, s, t); + } + + void compute(ConstGenericMatrix& mat, const Scalar& s, const Scalar& t) + { + using std::abs; + + m_n = mat.rows(); + if(m_n != mat.cols()) + throw std::invalid_argument("DoubleShiftQR: matrix must be square"); + + m_mat_H.resize(m_n, m_n); + m_shift_s = s; + m_shift_t = t; + m_ref_u.resize(3, m_n); + m_ref_nr.resize(m_n); + + // Make a copy of mat + std::copy(mat.data(), mat.data() + mat.size(), m_mat_H.data()); + + // Obtain the indices of zero elements in the subdiagonal, + // so that H can be divided into several blocks + std::vector zero_ind; + zero_ind.reserve(m_n - 1); + zero_ind.push_back(0); + Scalar* Hii = m_mat_H.data(); + for(Index i = 0; i < m_n - 2; i++, Hii += (m_n + 1)) + { + // Hii[1] => m_mat_H(i + 1, i) + const Scalar h = abs(Hii[1]); + if(h <= 0 || h <= m_eps_rel * (abs(Hii[0]) + abs(Hii[m_n + 1]))) + { + Hii[1] = 0; + zero_ind.push_back(i + 1); + } + // Make sure m_mat_H is upper Hessenberg + // Zero the elements below m_mat_H(i + 1, i) + std::fill(Hii + 2, Hii + m_n - i, Scalar(0)); + } + zero_ind.push_back(m_n); + + for(std::vector::size_type i = 0; i < zero_ind.size() - 1; i++) + { + const Index start = zero_ind[i]; + const Index end = zero_ind[i + 1] - 1; + // Compute refelctors and update each block + update_block(start, end); + } + + m_computed = true; + } + + void matrix_QtHQ(Matrix& dest) const + { + if(!m_computed) + throw std::logic_error("DoubleShiftQR: need to call compute() first"); + + dest.noalias() = m_mat_H; + } + + // Q = P0 * P1 * ... + // Q'y = P_{n-2} * ... * P1 * P0 * y + void apply_QtY(Vector& y) const + { + if(!m_computed) + throw std::logic_error("DoubleShiftQR: need to call compute() first"); + + Scalar* y_ptr = y.data(); + const Index n1 = m_n - 1; + for(Index i = 0; i < n1; i++, y_ptr++) + { + apply_PX(y_ptr, i); + } + } + + // Q = P0 * P1 * ... + // YQ = Y * P0 * P1 * ... + void apply_YQ(GenericMatrix Y) const + { + if(!m_computed) + throw std::logic_error("DoubleShiftQR: need to call compute() first"); + + const Index nrow = Y.rows(); + const Index n2 = m_n - 2; + for(Index i = 0; i < n2; i++) + { + apply_XP(Y.block(0, i, nrow, 3), nrow, i); + } + apply_XP(Y.block(0, n2, nrow, 2), nrow, n2); + } +}; + + +} // namespace Spectra + +#endif // DOUBLE_SHIFT_QR_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/Lanczos.h b/src/external/Spectra/include/Spectra/LinAlg/Lanczos.h new file mode 100644 index 00000000..2301dd30 --- /dev/null +++ b/src/external/Spectra/include/Spectra/LinAlg/Lanczos.h @@ -0,0 +1,170 @@ +// Copyright (C) 2018-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef LANCZOS_H +#define LANCZOS_H + +#include +#include // std::sqrt +#include // std::invalid_argument +#include // std::stringstream + +#include "Arnoldi.h" + +namespace Spectra { + + +// Lanczos factorization A * V = V * H + f * e' +// A: n x n +// V: n x k +// H: k x k +// f: n x 1 +// e: [0, ..., 0, 1] +// V and H are allocated of dimension m, so the maximum value of k is m +template +class Lanczos: public Arnoldi +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapMat; + typedef Eigen::Map MapVec; + typedef Eigen::Map MapConstMat; + typedef Eigen::Map MapConstVec; + + using Arnoldi::m_op; + using Arnoldi::m_n; + using Arnoldi::m_m; + using Arnoldi::m_k; + using Arnoldi::m_fac_V; + using Arnoldi::m_fac_H; + using Arnoldi::m_fac_f; + using Arnoldi::m_beta; + using Arnoldi::m_near_0; + using Arnoldi::m_eps; + +public: + Lanczos(const ArnoldiOpType& op, Index m) : + Arnoldi(op, m) + {} + + // Lanczos factorization starting from step-k + void factorize_from(Index from_k, Index to_m, Index& op_counter) + { + using std::sqrt; + + if(to_m <= from_k) return; + + if(from_k > m_k) + { + std::stringstream msg; + msg << "Lanczos: from_k (= " << from_k << + ") is larger than the current subspace dimension (= " << + m_k << ")"; + throw std::invalid_argument(msg.str()); + } + + const Scalar beta_thresh = m_eps * sqrt(Scalar(m_n)); + + // Pre-allocate vectors + Vector Vf(to_m); + Vector w(m_n); + + // Keep the upperleft k x k submatrix of H and set other elements to 0 + m_fac_H.rightCols(m_m - from_k).setZero(); + m_fac_H.block(from_k, 0, m_m - from_k, from_k).setZero(); + + for(Index i = from_k; i <= to_m - 1; i++) + { + bool restart = false; + // If beta = 0, then the next V is not full rank + // We need to generate a new residual vector that is orthogonal + // to the current V, which we call a restart + if(m_beta < m_near_0) + { + MapConstMat V(m_fac_V.data(), m_n, i); // The first i columns + this->expand_basis(V, 2 * i, m_fac_f, m_beta); + restart = true; + } + + // v <- f / ||f|| + MapVec v(&m_fac_V(0, i), m_n); // The (i+1)-th column + v.noalias() = m_fac_f / m_beta; + + // Note that H[i+1, i] equals to the unrestarted beta + m_fac_H(i, i - 1) = restart ? Scalar(0) : m_beta; + + // w <- A * v + m_op.perform_op(v.data(), w.data()); + op_counter++; + + // H[i+1, i+1] = = v'Bw + m_fac_H(i - 1, i) = m_fac_H(i, i - 1); // Due to symmetry + m_fac_H(i, i) = m_op.inner_product(v, w); + + // f <- w - V * V'Bw = w - H[i+1, i] * V{i} - H[i+1, i+1] * V{i+1} + // If restarting, we know that H[i+1, i] = 0 + if(restart) + m_fac_f.noalias() = w - m_fac_H(i, i) * v; + else + m_fac_f.noalias() = w - m_fac_H(i, i - 1) * m_fac_V.col(i - 1) - m_fac_H(i, i) * v; + + m_beta = m_op.norm(m_fac_f); + + // f/||f|| is going to be the next column of V, so we need to test + // whether V'B(f/||f||) ~= 0 + const Index i1 = i + 1; + MapMat Vs(m_fac_V.data(), m_n, i1); // The first (i+1) columns + m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); + Scalar ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); + // If not, iteratively correct the residual + int count = 0; + while(count < 5 && ortho_err > m_eps * m_beta) + { + // There is an edge case: when beta=||f|| is close to zero, f mostly consists + // of noises of rounding errors, so the test [ortho_err < eps * beta] is very + // likely to fail. In particular, if beta=0, then the test is ensured to fail. + // Hence when this happens, we force f to be zero, and then restart in the + // next iteration. + if(m_beta < beta_thresh) + { + m_fac_f.setZero(); + m_beta = Scalar(0); + break; + } + + // f <- f - V * Vf + m_fac_f.noalias() -= Vs * Vf.head(i1); + // h <- h + Vf + m_fac_H(i - 1, i) += Vf[i - 1]; + m_fac_H(i, i - 1) = m_fac_H(i - 1, i); + m_fac_H(i, i) += Vf[i]; + // beta <- ||f|| + m_beta = m_op.norm(m_fac_f); + + m_op.trans_product(Vs, m_fac_f, Vf.head(i1)); + ortho_err = Vf.head(i1).cwiseAbs().maxCoeff(); + count++; + } + } + + // Indicate that this is a step-m factorization + m_k = to_m; + } + + // Apply H -> Q'HQ, where Q is from a tridiagonal QR decomposition + void compress_H(const TridiagQR& decomp) + { + decomp.matrix_QtHQ(m_fac_H); + m_k--; + } +}; + + +} // namespace Spectra + +#endif // LANCZOS_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/TridiagEigen.h b/src/external/Spectra/include/Spectra/LinAlg/TridiagEigen.h new file mode 100644 index 00000000..b79fe8d1 --- /dev/null +++ b/src/external/Spectra/include/Spectra/LinAlg/TridiagEigen.h @@ -0,0 +1,219 @@ +// The code was adapted from Eigen/src/Eigenvaleus/SelfAdjointEigenSolver.h +// +// Copyright (C) 2008-2010 Gael Guennebaud +// Copyright (C) 2010 Jitse Niesen +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef TRIDIAG_EIGEN_H +#define TRIDIAG_EIGEN_H + +#include +#include +#include + +#include "../Util/TypeTraits.h" + +namespace Spectra { + + +template +class TridiagEigen +{ +private: + typedef Eigen::Index Index; + // For convenience in adapting the tridiagonal_qr_step() function + typedef Scalar RealScalar; + + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + + typedef Eigen::Ref GenericMatrix; + typedef const Eigen::Ref ConstGenericMatrix; + + Index m_n; + Vector m_main_diag; // Main diagonal elements of the matrix + Vector m_sub_diag; // Sub-diagonal elements of the matrix + Matrix m_evecs; // To store eigenvectors + + bool m_computed; + const Scalar m_near_0; // a very small value, ~= 1e-307 for the "double" type + + // Adapted from Eigen/src/Eigenvaleus/SelfAdjointEigenSolver.h + static void tridiagonal_qr_step(RealScalar* diag, + RealScalar* subdiag, Index start, + Index end, Scalar* matrixQ, + Index n) + { + using std::abs; + + RealScalar td = (diag[end-1] - diag[end]) * RealScalar(0.5); + RealScalar e = subdiag[end-1]; + // Note that thanks to scaling, e^2 or td^2 cannot overflow, however they can still + // underflow thus leading to inf/NaN values when using the following commented code: + // RealScalar e2 = numext::abs2(subdiag[end-1]); + // RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * sqrt(td*td + e2)); + // This explain the following, somewhat more complicated, version: + RealScalar mu = diag[end]; + if(td == Scalar(0)) + mu -= abs(e); + else + { + RealScalar e2 = Eigen::numext::abs2(subdiag[end-1]); + RealScalar h = Eigen::numext::hypot(td, e); + if(e2==RealScalar(0)) mu -= (e / (td + (td>RealScalar(0) ? RealScalar(1) : RealScalar(-1)))) * (e / h); + else mu -= e2 / (td + (td>RealScalar(0) ? h : -h)); + } + + RealScalar x = diag[start] - mu; + RealScalar z = subdiag[start]; + Eigen::Map q(matrixQ, n, n); + for(Index k = start; k < end; ++k) + { + Eigen::JacobiRotation rot; + rot.makeGivens(x, z); + + const RealScalar s = rot.s(); + const RealScalar c = rot.c(); + + // do T = G' T G + RealScalar sdk = s * diag[k] + c * subdiag[k]; + RealScalar dkp1 = s * subdiag[k] + c * diag[k + 1]; + + diag[k] = c * (c * diag[k] - s * subdiag[k]) - s * (c * subdiag[k] - s * diag[k + 1]); + diag[k + 1] = s * sdk + c * dkp1; + subdiag[k] = c * sdk - s * dkp1; + + if(k > start) + subdiag[k - 1] = c * subdiag[k - 1] - s * z; + + x = subdiag[k]; + + if(k < end - 1) + { + z = -s * subdiag[k+1]; + subdiag[k + 1] = c * subdiag[k + 1]; + } + + // apply the givens rotation to the unit matrix Q = Q * G + if(matrixQ) + q.applyOnTheRight(k, k + 1, rot); + } + } + +public: + TridiagEigen() : + m_n(0), m_computed(false), + m_near_0(TypeTraits::min() * Scalar(10)) + {} + + TridiagEigen(ConstGenericMatrix& mat) : + m_n(mat.rows()), m_computed(false), + m_near_0(TypeTraits::min() * Scalar(10)) + { + compute(mat); + } + + void compute(ConstGenericMatrix& mat) + { + using std::abs; + + m_n = mat.rows(); + if(m_n != mat.cols()) + throw std::invalid_argument("TridiagEigen: matrix must be square"); + + m_main_diag.resize(m_n); + m_sub_diag.resize(m_n - 1); + m_evecs.resize(m_n, m_n); + m_evecs.setIdentity(); + + // Scale matrix to improve stability + const Scalar scale = std::max(mat.diagonal().cwiseAbs().maxCoeff(), + mat.diagonal(-1).cwiseAbs().maxCoeff()); + // If scale=0, mat is a zero matrix, so we can early stop + if(scale < m_near_0) + { + // m_main_diag contains eigenvalues + m_main_diag.setZero(); + // m_evecs has been set identity + // m_evecs.setIdentity(); + m_computed = true; + return; + } + m_main_diag.noalias() = mat.diagonal() / scale; + m_sub_diag.noalias() = mat.diagonal(-1) / scale; + + Scalar* diag = m_main_diag.data(); + Scalar* subdiag = m_sub_diag.data(); + + Index end = m_n - 1; + Index start = 0; + Index iter = 0; // total number of iterations + int info = 0; // 0 for success, 1 for failure + + const Scalar considerAsZero = TypeTraits::min(); + const Scalar precision = Scalar(2) * Eigen::NumTraits::epsilon(); + + while(end > 0) + { + for(Index i = start; i < end; i++) + if(abs(subdiag[i]) <= considerAsZero || + abs(subdiag[i]) <= (abs(diag[i]) + abs(diag[i + 1])) * precision) + subdiag[i] = 0; + + // find the largest unreduced block + while(end > 0 && subdiag[end - 1] == Scalar(0)) + end--; + + if(end <= 0) + break; + + // if we spent too many iterations, we give up + iter++; + if(iter > 30 * m_n) + { + info = 1; + break; + } + + start = end - 1; + while(start > 0 && subdiag[start - 1] != Scalar(0)) + start--; + + tridiagonal_qr_step(diag, subdiag, start, end, m_evecs.data(), m_n); + } + + if(info > 0) + throw std::runtime_error("TridiagEigen: eigen decomposition failed"); + + // Scale eigenvalues back + m_main_diag *= scale; + + m_computed = true; + } + + const Vector& eigenvalues() const + { + if(!m_computed) + throw std::logic_error("TridiagEigen: need to call compute() first"); + + // After calling compute(), main_diag will contain the eigenvalues. + return m_main_diag; + } + + const Matrix& eigenvectors() const + { + if(!m_computed) + throw std::logic_error("TridiagEigen: need to call compute() first"); + + return m_evecs; + } +}; + + +} // namespace Spectra + +#endif // TRIDIAG_EIGEN_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergEigen.h b/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergEigen.h new file mode 100644 index 00000000..4e099f56 --- /dev/null +++ b/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergEigen.h @@ -0,0 +1,317 @@ +// The code was adapted from Eigen/src/Eigenvaleus/EigenSolver.h +// +// Copyright (C) 2008 Gael Guennebaud +// Copyright (C) 2010,2012 Jitse Niesen +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef UPPER_HESSENBERG_EIGEN_H +#define UPPER_HESSENBERG_EIGEN_H + +#include +#include +#include + +namespace Spectra { + + +template +class UpperHessenbergEigen +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + + typedef Eigen::Ref GenericMatrix; + typedef const Eigen::Ref ConstGenericMatrix; + + typedef std::complex Complex; + typedef Eigen::Matrix ComplexMatrix; + typedef Eigen::Matrix ComplexVector; + + Index m_n; // Size of the matrix + Eigen::RealSchur m_realSchur; // Schur decomposition solver + Matrix m_matT; // Schur T matrix + Matrix m_eivec; // Storing eigenvectors + ComplexVector m_eivalues; // Eigenvalues + + bool m_computed; + + void doComputeEigenvectors() + { + using std::abs; + + const Index size = m_eivec.cols(); + const Scalar eps = Eigen::NumTraits::epsilon(); + + // inefficient! this is already computed in RealSchur + Scalar norm(0); + for(Index j = 0; j < size; ++j) + { + norm += m_matT.row(j).segment((std::max)(j-1, Index(0)), size-(std::max)(j-1, Index(0))).cwiseAbs().sum(); + } + + // Backsubstitute to find vectors of upper triangular form + if(norm == Scalar(0)) + return; + + for(Index n = size - 1; n >= 0; n--) + { + Scalar p = m_eivalues.coeff(n).real(); + Scalar q = m_eivalues.coeff(n).imag(); + + // Scalar vector + if(q == Scalar(0)) + { + Scalar lastr(0), lastw(0); + Index l = n; + + m_matT.coeffRef(n,n) = Scalar(1); + for(Index i = n-1; i >= 0; i--) + { + Scalar w = m_matT.coeff(i,i) - p; + Scalar r = m_matT.row(i).segment(l,n-l+1).dot(m_matT.col(n).segment(l, n-l+1)); + + if(m_eivalues.coeff(i).imag() < Scalar(0)) + { + lastw = w; + lastr = r; + } else { + l = i; + if(m_eivalues.coeff(i).imag() == Scalar(0)) + { + if (w != Scalar(0)) + m_matT.coeffRef(i,n) = -r / w; + else + m_matT.coeffRef(i,n) = -r / (eps * norm); + } + else // Solve real equations + { + Scalar x = m_matT.coeff(i,i+1); + Scalar y = m_matT.coeff(i+1,i); + Scalar denom = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag(); + Scalar t = (x * lastr - lastw * r) / denom; + m_matT.coeffRef(i,n) = t; + if(abs(x) > abs(lastw)) + m_matT.coeffRef(i+1,n) = (-r - w * t) / x; + else + m_matT.coeffRef(i+1,n) = (-lastr - y * t) / lastw; + } + + // Overflow control + Scalar t = abs(m_matT.coeff(i,n)); + if((eps * t) * t > Scalar(1)) + m_matT.col(n).tail(size-i) /= t; + } + } + } else if(q < Scalar(0) && n > 0) { // Complex vector + Scalar lastra(0), lastsa(0), lastw(0); + Index l = n-1; + + // Last vector component imaginary so matrix is triangular + if(abs(m_matT.coeff(n,n-1)) > abs(m_matT.coeff(n-1,n))) + { + m_matT.coeffRef(n-1,n-1) = q / m_matT.coeff(n,n-1); + m_matT.coeffRef(n-1,n) = -(m_matT.coeff(n,n) - p) / m_matT.coeff(n,n-1); + } + else + { + Complex cc = Complex(Scalar(0),-m_matT.coeff(n-1,n)) / Complex(m_matT.coeff(n-1,n-1)-p,q); + m_matT.coeffRef(n-1,n-1) = Eigen::numext::real(cc); + m_matT.coeffRef(n-1,n) = Eigen::numext::imag(cc); + } + m_matT.coeffRef(n,n-1) = Scalar(0); + m_matT.coeffRef(n,n) = Scalar(1); + for(Index i = n-2; i >= 0; i--) + { + Scalar ra = m_matT.row(i).segment(l, n-l+1).dot(m_matT.col(n-1).segment(l, n-l+1)); + Scalar sa = m_matT.row(i).segment(l, n-l+1).dot(m_matT.col(n).segment(l, n-l+1)); + Scalar w = m_matT.coeff(i,i) - p; + + if(m_eivalues.coeff(i).imag() < Scalar(0)) + { + lastw = w; + lastra = ra; + lastsa = sa; + } + else + { + l = i; + if(m_eivalues.coeff(i).imag() == Scalar(0)) + { + Complex cc = Complex(-ra,-sa) / Complex(w,q); + m_matT.coeffRef(i,n-1) = Eigen::numext::real(cc); + m_matT.coeffRef(i,n) = Eigen::numext::imag(cc); + } + else + { + // Solve complex equations + Scalar x = m_matT.coeff(i,i+1); + Scalar y = m_matT.coeff(i+1,i); + Scalar vr = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag() - q * q; + Scalar vi = (m_eivalues.coeff(i).real() - p) * Scalar(2) * q; + if((vr == Scalar(0)) && (vi == Scalar(0))) + vr = eps * norm * (abs(w) + abs(q) + abs(x) + abs(y) + abs(lastw)); + + Complex cc = Complex(x*lastra-lastw*ra+q*sa,x*lastsa-lastw*sa-q*ra) / Complex(vr,vi); + m_matT.coeffRef(i,n-1) = Eigen::numext::real(cc); + m_matT.coeffRef(i,n) = Eigen::numext::imag(cc); + if(abs(x) > (abs(lastw) + abs(q))) + { + m_matT.coeffRef(i+1,n-1) = (-ra - w * m_matT.coeff(i,n-1) + q * m_matT.coeff(i,n)) / x; + m_matT.coeffRef(i+1,n) = (-sa - w * m_matT.coeff(i,n) - q * m_matT.coeff(i,n-1)) / x; + } + else + { + cc = Complex(-lastra-y*m_matT.coeff(i,n-1),-lastsa-y*m_matT.coeff(i,n)) / Complex(lastw,q); + m_matT.coeffRef(i+1,n-1) = Eigen::numext::real(cc); + m_matT.coeffRef(i+1,n) = Eigen::numext::imag(cc); + } + } + + // Overflow control + Scalar t = std::max(abs(m_matT.coeff(i,n-1)), abs(m_matT.coeff(i,n))); + if((eps * t) * t > Scalar(1)) + m_matT.block(i, n-1, size-i, 2) /= t; + + } + } + + // We handled a pair of complex conjugate eigenvalues, so need to skip them both + n--; + } + } + + // Back transformation to get eigenvectors of original matrix + Vector m_tmp(size); + for(Index j = size-1; j >= 0; j--) + { + m_tmp.noalias() = m_eivec.leftCols(j+1) * m_matT.col(j).segment(0, j+1); + m_eivec.col(j) = m_tmp; + } + } + +public: + + UpperHessenbergEigen() : + m_n(0), m_computed(false) + {} + + UpperHessenbergEigen(ConstGenericMatrix& mat) : + m_n(mat.rows()), m_computed(false) + { + compute(mat); + } + + void compute(ConstGenericMatrix& mat) + { + using std::abs; + using std::sqrt; + + if(mat.rows() != mat.cols()) + throw std::invalid_argument("UpperHessenbergEigen: matrix must be square"); + + m_n = mat.rows(); + // Scale matrix prior to the Schur decomposition + const Scalar scale = mat.cwiseAbs().maxCoeff(); + + // Reduce to real Schur form + Matrix Q = Matrix::Identity(m_n, m_n); + m_realSchur.computeFromHessenberg(mat / scale, Q, true); + if(m_realSchur.info() != Eigen::Success) + throw std::runtime_error("UpperHessenbergEigen: eigen decomposition failed"); + + m_matT = m_realSchur.matrixT(); + m_eivec = m_realSchur.matrixU(); + + // Compute eigenvalues from matT + m_eivalues.resize(m_n); + Index i = 0; + while(i < m_n) + { + // Real eigenvalue + if(i == m_n - 1 || m_matT.coeff(i+1, i) == Scalar(0)) + { + m_eivalues.coeffRef(i) = m_matT.coeff(i, i); + ++i; + } + else // Complex eigenvalues + { + Scalar p = Scalar(0.5) * (m_matT.coeff(i, i) - m_matT.coeff(i+1, i+1)); + Scalar z; + // Compute z = sqrt(abs(p * p + m_matT.coeff(i+1, i) * m_matT.coeff(i, i+1))); + // without overflow + { + Scalar t0 = m_matT.coeff(i+1, i); + Scalar t1 = m_matT.coeff(i, i+1); + Scalar maxval = std::max(abs(p), std::max(abs(t0), abs(t1))); + t0 /= maxval; + t1 /= maxval; + Scalar p0 = p / maxval; + z = maxval * sqrt(abs(p0 * p0 + t0 * t1)); + } + m_eivalues.coeffRef(i) = Complex(m_matT.coeff(i+1, i+1) + p, z); + m_eivalues.coeffRef(i+1) = Complex(m_matT.coeff(i+1, i+1) + p, -z); + i += 2; + } + } + + // Compute eigenvectors + doComputeEigenvectors(); + + // Scale eigenvalues back + m_eivalues *= scale; + + m_computed = true; + } + + const ComplexVector& eigenvalues() const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergEigen: need to call compute() first"); + + return m_eivalues; + } + + ComplexMatrix eigenvectors() + { + using std::abs; + + if(!m_computed) + throw std::logic_error("UpperHessenbergEigen: need to call compute() first"); + + Index n = m_eivec.cols(); + ComplexMatrix matV(n, n); + for(Index j = 0; j < n; ++j) + { + // imaginary part of real eigenvalue is already set to exact zero + if(Eigen::numext::imag(m_eivalues.coeff(j)) == Scalar(0) || j + 1 == n) + { + // we have a real eigen value + matV.col(j) = m_eivec.col(j).template cast(); + matV.col(j).normalize(); + } else { + // we have a pair of complex eigen values + for(Index i = 0; i < n; ++i) + { + matV.coeffRef(i,j) = Complex(m_eivec.coeff(i,j), m_eivec.coeff(i,j+1)); + matV.coeffRef(i,j+1) = Complex(m_eivec.coeff(i,j), -m_eivec.coeff(i,j+1)); + } + matV.col(j).normalize(); + matV.col(j+1).normalize(); + ++j; + } + } + + return matV; + } +}; + + +} // namespace Spectra + +#endif // UPPER_HESSENBERG_EIGEN_H diff --git a/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergQR.h b/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergQR.h new file mode 100644 index 00000000..a66d9598 --- /dev/null +++ b/src/external/Spectra/include/Spectra/LinAlg/UpperHessenbergQR.h @@ -0,0 +1,670 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef UPPER_HESSENBERG_QR_H +#define UPPER_HESSENBERG_QR_H + +#include +#include // std::sqrt +#include // std::fill, std::copy +#include // std::logic_error + +namespace Spectra { + + +/// +/// \defgroup Internals Internal Classes +/// +/// Classes for internal use. May be useful to developers. +/// + +/// +/// \ingroup Internals +/// @{ +/// + +/// +/// \defgroup LinearAlgebra Linear Algebra +/// +/// A number of classes for linear algebra operations. +/// + +/// +/// \ingroup LinearAlgebra +/// +/// Perform the QR decomposition of an upper Hessenberg matrix. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// +template +class UpperHessenbergQR +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Matrix RowVector; + typedef Eigen::Array Array; + + typedef Eigen::Ref GenericMatrix; + typedef const Eigen::Ref ConstGenericMatrix; + + Matrix m_mat_T; + +protected: + Index m_n; + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + // Q = G1 * G2 * ... * G_{n-1} + Scalar m_shift; + Array m_rot_cos; + Array m_rot_sin; + bool m_computed; + + // Given x and y, compute 1) r = sqrt(x^2 + y^2), 2) c = x / r, 3) s = -y / r + // If both x and y are zero, set c = 1 and s = 0 + // We must implement it in a numerically stable way + static void compute_rotation(const Scalar& x, const Scalar& y, Scalar& r, Scalar& c, Scalar& s) + { + using std::sqrt; + + const Scalar xsign = (x > Scalar(0)) - (x < Scalar(0)); + const Scalar ysign = (y > Scalar(0)) - (y < Scalar(0)); + const Scalar xabs = x * xsign; + const Scalar yabs = y * ysign; + if(xabs > yabs) + { + // In this case xabs != 0 + const Scalar ratio = yabs / xabs; // so that 0 <= ratio < 1 + const Scalar common = sqrt(Scalar(1) + ratio * ratio); + c = xsign / common; + r = xabs * common; + s = -y / r; + } else { + if(yabs == Scalar(0)) + { + r = Scalar(0); c = Scalar(1); s = Scalar(0); + return; + } + const Scalar ratio = xabs / yabs; // so that 0 <= ratio <= 1 + const Scalar common = sqrt(Scalar(1) + ratio * ratio); + s = -ysign / common; + r = yabs * common; + c = x / r; + } + } + +public: + /// + /// Constructor to preallocate memory. Computation can + /// be performed later by calling the compute() method. + /// + UpperHessenbergQR(Index size) : + m_n(size), + m_rot_cos(m_n - 1), + m_rot_sin(m_n - 1), + m_computed(false) + {} + + /// + /// Constructor to create an object that performs and stores the + /// QR decomposition of an upper Hessenberg matrix `mat`, with an + /// optional shift: \f$H-sI=QR\f$. Here \f$H\f$ stands for the matrix + /// `mat`, and \f$s\f$ is the shift. + /// + /// \param mat Matrix type can be `Eigen::Matrix` (e.g. + /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// Only the upper triangular and the lower subdiagonal parts of + /// the matrix are used. + /// + UpperHessenbergQR(ConstGenericMatrix& mat, const Scalar& shift = Scalar(0)) : + m_n(mat.rows()), + m_shift(shift), + m_rot_cos(m_n - 1), + m_rot_sin(m_n - 1), + m_computed(false) + { + compute(mat, shift); + } + + /// + /// Virtual destructor. + /// + virtual ~UpperHessenbergQR() {}; + + /// + /// Conduct the QR factorization of an upper Hessenberg matrix with + /// an optional shift. + /// + /// \param mat Matrix type can be `Eigen::Matrix` (e.g. + /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// Only the upper triangular and the lower subdiagonal parts of + /// the matrix are used. + /// + virtual void compute(ConstGenericMatrix& mat, const Scalar& shift = Scalar(0)) + { + m_n = mat.rows(); + if(m_n != mat.cols()) + throw std::invalid_argument("UpperHessenbergQR: matrix must be square"); + + m_shift = shift; + m_mat_T.resize(m_n, m_n); + m_rot_cos.resize(m_n - 1); + m_rot_sin.resize(m_n - 1); + + // Make a copy of mat - s * I + std::copy(mat.data(), mat.data() + mat.size(), m_mat_T.data()); + m_mat_T.diagonal().array() -= m_shift; + + Scalar xi, xj, r, c, s; + Scalar *Tii, *ptr; + const Index n1 = m_n - 1; + for(Index i = 0; i < n1; i++) + { + Tii = &m_mat_T.coeffRef(i, i); + + // Make sure mat_T is upper Hessenberg + // Zero the elements below mat_T(i + 1, i) + std::fill(Tii + 2, Tii + m_n - i, Scalar(0)); + + xi = Tii[0]; // mat_T(i, i) + xj = Tii[1]; // mat_T(i + 1, i) + compute_rotation(xi, xj, r, c, s); + m_rot_cos[i] = c; + m_rot_sin[i] = s; + + // For a complete QR decomposition, + // we first obtain the rotation matrix + // G = [ cos sin] + // [-sin cos] + // and then do T[i:(i + 1), i:(n - 1)] = G' * T[i:(i + 1), i:(n - 1)] + + // Gt << c, -s, s, c; + // m_mat_T.block(i, i, 2, m_n - i) = Gt * m_mat_T.block(i, i, 2, m_n - i); + Tii[0] = r; // m_mat_T(i, i) => r + Tii[1] = 0; // m_mat_T(i + 1, i) => 0 + ptr = Tii + m_n; // m_mat_T(i, k), k = i+1, i+2, ..., n-1 + for(Index j = i + 1; j < m_n; j++, ptr += m_n) + { + Scalar tmp = ptr[0]; + ptr[0] = c * tmp - s * ptr[1]; + ptr[1] = s * tmp + c * ptr[1]; + } + + // If we do not need to calculate the R matrix, then + // only the cos and sin sequences are required. + // In such case we only update T[i + 1, (i + 1):(n - 1)] + // m_mat_T.block(i + 1, i + 1, 1, m_n - i - 1) *= c; + // m_mat_T.block(i + 1, i + 1, 1, m_n - i - 1) += s * mat_T.block(i, i + 1, 1, m_n - i - 1); + } + + m_computed = true; + } + + /// + /// Return the \f$R\f$ matrix in the QR decomposition, which is an + /// upper triangular matrix. + /// + /// \return Returned matrix type will be `Eigen::Matrix`, depending on + /// the template parameter `Scalar` defined. + /// + virtual Matrix matrix_R() const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergQR: need to call compute() first"); + + return m_mat_T; + } + + /// + /// Overwrite `dest` with \f$Q'HQ = RQ + sI\f$, where \f$H\f$ is the input matrix `mat`, + /// and \f$s\f$ is the shift. The result is an upper Hessenberg matrix. + /// + /// \param mat The matrix to be overwritten, whose type should be `Eigen::Matrix`, + /// depending on the template parameter `Scalar` defined. + /// + virtual void matrix_QtHQ(Matrix& dest) const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergQR: need to call compute() first"); + + // Make a copy of the R matrix + dest.resize(m_n, m_n); + std::copy(m_mat_T.data(), m_mat_T.data() + m_mat_T.size(), dest.data()); + + // Compute the RQ matrix + const Index n1 = m_n - 1; + for(Index i = 0; i < n1; i++) + { + const Scalar c = m_rot_cos.coeff(i); + const Scalar s = m_rot_sin.coeff(i); + // RQ[, i:(i + 1)] = RQ[, i:(i + 1)] * Gi + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + Scalar *Yi, *Yi1; + Yi = &dest.coeffRef(0, i); + Yi1 = Yi + m_n; // RQ(0, i + 1) + const Index i2 = i + 2; + for(Index j = 0; j < i2; j++) + { + const Scalar tmp = Yi[j]; + Yi[j] = c * tmp - s * Yi1[j]; + Yi1[j] = s * tmp + c * Yi1[j]; + } + + /* Vector dest = RQ.block(0, i, i + 2, 1); + dest.block(0, i, i + 2, 1) = c * Yi - s * dest.block(0, i + 1, i + 2, 1); + dest.block(0, i + 1, i + 2, 1) = s * Yi + c * dest.block(0, i + 1, i + 2, 1); */ + } + + // Add the shift to the diagonal + dest.diagonal().array() += m_shift; + } + + /// + /// Apply the \f$Q\f$ matrix to a vector \f$y\f$. + /// + /// \param Y A vector that will be overwritten by the matrix product \f$Qy\f$. + /// + /// Vector type can be `Eigen::Vector`, depending on + /// the template parameter `Scalar` defined. + /// + // Y -> QY = G1 * G2 * ... * Y + void apply_QY(Vector& Y) const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergQR: need to call compute() first"); + + for(Index i = m_n - 2; i >= 0; i--) + { + const Scalar c = m_rot_cos.coeff(i); + const Scalar s = m_rot_sin.coeff(i); + // Y[i:(i + 1)] = Gi * Y[i:(i + 1)] + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + const Scalar tmp = Y[i]; + Y[i] = c * tmp + s * Y[i + 1]; + Y[i + 1] = -s * tmp + c * Y[i + 1]; + } + } + + /// + /// Apply the \f$Q\f$ matrix to a vector \f$y\f$. + /// + /// \param Y A vector that will be overwritten by the matrix product \f$Q'y\f$. + /// + /// Vector type can be `Eigen::Vector`, depending on + /// the template parameter `Scalar` defined. + /// + // Y -> Q'Y = G_{n-1}' * ... * G2' * G1' * Y + void apply_QtY(Vector& Y) const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergQR: need to call compute() first"); + + const Index n1 = m_n - 1; + for(Index i = 0; i < n1; i++) + { + const Scalar c = m_rot_cos.coeff(i); + const Scalar s = m_rot_sin.coeff(i); + // Y[i:(i + 1)] = Gi' * Y[i:(i + 1)] + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + const Scalar tmp = Y[i]; + Y[i] = c * tmp - s * Y[i + 1]; + Y[i + 1] = s * tmp + c * Y[i + 1]; + } + } + + /// + /// Apply the \f$Q\f$ matrix to another matrix \f$Y\f$. + /// + /// \param Y A matrix that will be overwritten by the matrix product \f$QY\f$. + /// + /// Matrix type can be `Eigen::Matrix` (e.g. + /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + // Y -> QY = G1 * G2 * ... * Y + void apply_QY(GenericMatrix Y) const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergQR: need to call compute() first"); + + RowVector Yi(Y.cols()), Yi1(Y.cols()); + for(Index i = m_n - 2; i >= 0; i--) + { + const Scalar c = m_rot_cos.coeff(i); + const Scalar s = m_rot_sin.coeff(i); + // Y[i:(i + 1), ] = Gi * Y[i:(i + 1), ] + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + Yi.noalias() = Y.row(i); + Yi1.noalias() = Y.row(i + 1); + Y.row(i) = c * Yi + s * Yi1; + Y.row(i + 1) = -s * Yi + c * Yi1; + } + } + + /// + /// Apply the \f$Q\f$ matrix to another matrix \f$Y\f$. + /// + /// \param Y A matrix that will be overwritten by the matrix product \f$Q'Y\f$. + /// + /// Matrix type can be `Eigen::Matrix` (e.g. + /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + // Y -> Q'Y = G_{n-1}' * ... * G2' * G1' * Y + void apply_QtY(GenericMatrix Y) const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergQR: need to call compute() first"); + + RowVector Yi(Y.cols()), Yi1(Y.cols()); + const Index n1 = m_n - 1; + for(Index i = 0; i < n1; i++) + { + const Scalar c = m_rot_cos.coeff(i); + const Scalar s = m_rot_sin.coeff(i); + // Y[i:(i + 1), ] = Gi' * Y[i:(i + 1), ] + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + Yi.noalias() = Y.row(i); + Yi1.noalias() = Y.row(i + 1); + Y.row(i) = c * Yi - s * Yi1; + Y.row(i + 1) = s * Yi + c * Yi1; + } + } + + /// + /// Apply the \f$Q\f$ matrix to another matrix \f$Y\f$. + /// + /// \param Y A matrix that will be overwritten by the matrix product \f$YQ\f$. + /// + /// Matrix type can be `Eigen::Matrix` (e.g. + /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + // Y -> YQ = Y * G1 * G2 * ... + void apply_YQ(GenericMatrix Y) const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergQR: need to call compute() first"); + + /*Vector Yi(Y.rows()); + for(Index i = 0; i < m_n - 1; i++) + { + const Scalar c = m_rot_cos.coeff(i); + const Scalar s = m_rot_sin.coeff(i); + // Y[, i:(i + 1)] = Y[, i:(i + 1)] * Gi + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + Yi.noalias() = Y.col(i); + Y.col(i) = c * Yi - s * Y.col(i + 1); + Y.col(i + 1) = s * Yi + c * Y.col(i + 1); + }*/ + Scalar *Y_col_i, *Y_col_i1; + const Index n1 = m_n - 1; + const Index nrow = Y.rows(); + for(Index i = 0; i < n1; i++) + { + const Scalar c = m_rot_cos.coeff(i); + const Scalar s = m_rot_sin.coeff(i); + + Y_col_i = &Y.coeffRef(0, i); + Y_col_i1 = &Y.coeffRef(0, i + 1); + for(Index j = 0; j < nrow; j++) + { + Scalar tmp = Y_col_i[j]; + Y_col_i[j] = c * tmp - s * Y_col_i1[j]; + Y_col_i1[j] = s * tmp + c * Y_col_i1[j]; + } + } + } + + /// + /// Apply the \f$Q\f$ matrix to another matrix \f$Y\f$. + /// + /// \param Y A matrix that will be overwritten by the matrix product \f$YQ'\f$. + /// + /// Matrix type can be `Eigen::Matrix` (e.g. + /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + // Y -> YQ' = Y * G_{n-1}' * ... * G2' * G1' + void apply_YQt(GenericMatrix Y) const + { + if(!m_computed) + throw std::logic_error("UpperHessenbergQR: need to call compute() first"); + + Vector Yi(Y.rows()); + for(Index i = m_n - 2; i >= 0; i--) + { + const Scalar c = m_rot_cos.coeff(i); + const Scalar s = m_rot_sin.coeff(i); + // Y[, i:(i + 1)] = Y[, i:(i + 1)] * Gi' + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + Yi.noalias() = Y.col(i); + Y.col(i) = c * Yi + s * Y.col(i + 1); + Y.col(i + 1) = -s * Yi + c * Y.col(i + 1); + } + } +}; + + + +/// +/// \ingroup LinearAlgebra +/// +/// Perform the QR decomposition of a tridiagonal matrix, a special +/// case of upper Hessenberg matrices. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// +template +class TridiagQR: public UpperHessenbergQR +{ +private: + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef const Eigen::Ref ConstGenericMatrix; + + typedef typename Matrix::Index Index; + + Vector m_T_diag; // diagonal elements of T + Vector m_T_lsub; // lower subdiagonal of T + Vector m_T_usub; // upper subdiagonal of T + Vector m_T_usub2; // 2nd upper subdiagonal of T + +public: + /// + /// Constructor to preallocate memory. Computation can + /// be performed later by calling the compute() method. + /// + TridiagQR(Index size) : + UpperHessenbergQR(size) + {} + + /// + /// Constructor to create an object that performs and stores the + /// QR decomposition of an upper Hessenberg matrix `mat`, with an + /// optional shift: \f$H-sI=QR\f$. Here \f$H\f$ stands for the matrix + /// `mat`, and \f$s\f$ is the shift. + /// + /// \param mat Matrix type can be `Eigen::Matrix` (e.g. + /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// Only the major- and sub- diagonal parts of + /// the matrix are used. + /// + TridiagQR(ConstGenericMatrix& mat, const Scalar& shift = Scalar(0)) : + UpperHessenbergQR(mat.rows()) + { + this->compute(mat, shift); + } + + /// + /// Conduct the QR factorization of a tridiagonal matrix with an + /// optional shift. + /// + /// \param mat Matrix type can be `Eigen::Matrix` (e.g. + /// `Eigen::MatrixXd` and `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// Only the major- and sub- diagonal parts of + /// the matrix are used. + /// + void compute(ConstGenericMatrix& mat, const Scalar& shift = Scalar(0)) + { + this->m_n = mat.rows(); + if(this->m_n != mat.cols()) + throw std::invalid_argument("TridiagQR: matrix must be square"); + + this->m_shift = shift; + m_T_diag.resize(this->m_n); + m_T_lsub.resize(this->m_n - 1); + m_T_usub.resize(this->m_n - 1); + m_T_usub2.resize(this->m_n - 2); + this->m_rot_cos.resize(this->m_n - 1); + this->m_rot_sin.resize(this->m_n - 1); + + m_T_diag.array() = mat.diagonal().array() - this->m_shift; + m_T_lsub.noalias() = mat.diagonal(-1); + m_T_usub.noalias() = m_T_lsub; + + // A number of pointers to avoid repeated address calculation + Scalar *c = this->m_rot_cos.data(), // pointer to the cosine vector + *s = this->m_rot_sin.data(), // pointer to the sine vector + r; + const Index n1 = this->m_n - 1; + for(Index i = 0; i < n1; i++) + { + // diag[i] == T[i, i] + // lsub[i] == T[i + 1, i] + // r = sqrt(T[i, i]^2 + T[i + 1, i]^2) + // c = T[i, i] / r, s = -T[i + 1, i] / r + this->compute_rotation(m_T_diag.coeff(i), m_T_lsub.coeff(i), r, *c, *s); + + // For a complete QR decomposition, + // we first obtain the rotation matrix + // G = [ cos sin] + // [-sin cos] + // and then do T[i:(i + 1), i:(i + 2)] = G' * T[i:(i + 1), i:(i + 2)] + + // Update T[i, i] and T[i + 1, i] + // The updated value of T[i, i] is known to be r + // The updated value of T[i + 1, i] is known to be 0 + m_T_diag.coeffRef(i) = r; + m_T_lsub.coeffRef(i) = Scalar(0); + // Update T[i, i + 1] and T[i + 1, i + 1] + // usub[i] == T[i, i + 1] + // diag[i + 1] == T[i + 1, i + 1] + const Scalar tmp = m_T_usub.coeff(i); + m_T_usub.coeffRef(i) = (*c) * tmp - (*s) * m_T_diag.coeff(i + 1); + m_T_diag.coeffRef(i + 1) = (*s) * tmp + (*c) * m_T_diag.coeff(i + 1); + // Update T[i, i + 2] and T[i + 1, i + 2] + // usub2[i] == T[i, i + 2] + // usub[i + 1] == T[i + 1, i + 2] + if(i < n1 - 1) + { + m_T_usub2.coeffRef(i) = -(*s) * m_T_usub.coeff(i + 1); + m_T_usub.coeffRef(i + 1) *= (*c); + } + + c++; + s++; + + // If we do not need to calculate the R matrix, then + // only the cos and sin sequences are required. + // In such case we only update T[i + 1, (i + 1):(i + 2)] + // T[i + 1, i + 1] = c * T[i + 1, i + 1] + s * T[i, i + 1]; + // T[i + 1, i + 2] *= c; + } + + this->m_computed = true; + } + + /// + /// Return the \f$R\f$ matrix in the QR decomposition, which is an + /// upper triangular matrix. + /// + /// \return Returned matrix type will be `Eigen::Matrix`, depending on + /// the template parameter `Scalar` defined. + /// + Matrix matrix_R() const + { + if(!this->m_computed) + throw std::logic_error("TridiagQR: need to call compute() first"); + + Matrix R = Matrix::Zero(this->m_n, this->m_n); + R.diagonal().noalias() = m_T_diag; + R.diagonal(1).noalias() = m_T_usub; + R.diagonal(2).noalias() = m_T_usub2; + + return R; + } + + /// + /// Overwrite `dest` with \f$Q'HQ = RQ + sI\f$, where \f$H\f$ is the input matrix `mat`, + /// and \f$s\f$ is the shift. The result is a tridiagonal matrix. + /// + /// \param mat The matrix to be overwritten, whose type should be `Eigen::Matrix`, + /// depending on the template parameter `Scalar` defined. + /// + void matrix_QtHQ(Matrix& dest) const + { + if(!this->m_computed) + throw std::logic_error("TridiagQR: need to call compute() first"); + + // Make a copy of the R matrix + dest.resize(this->m_n, this->m_n); + dest.setZero(); + dest.diagonal().noalias() = m_T_diag; + // The upper diagonal refers to m_T_usub + // The 2nd upper subdiagonal will be zero in RQ + + // Compute the RQ matrix + // [m11 m12] points to RQ[i:(i+1), i:(i+1)] + // [0 m22] + // + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + const Index n1 = this->m_n - 1; + for(Index i = 0; i < n1; i++) + { + const Scalar c = this->m_rot_cos.coeff(i); + const Scalar s = this->m_rot_sin.coeff(i); + const Scalar m11 = dest.coeff(i, i), + m12 = m_T_usub.coeff(i), + m22 = m_T_diag.coeff(i + 1); + + // Update the diagonal and the lower subdiagonal of dest + dest.coeffRef(i , i ) = c * m11 - s * m12; + dest.coeffRef(i + 1, i ) = - s * m22; + dest.coeffRef(i + 1, i + 1) = c * m22; + } + + // Copy the lower subdiagonal to upper subdiagonal + dest.diagonal(1).noalias() = dest.diagonal(-1); + + // Add the shift to the diagonal + dest.diagonal().array() += this->m_shift; + } +}; + +/// +/// @} +/// + + +} // namespace Spectra + +#endif // UPPER_HESSENBERG_QR_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseCholesky.h b/src/external/Spectra/include/Spectra/MatOp/DenseCholesky.h new file mode 100644 index 00000000..354c9508 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/DenseCholesky.h @@ -0,0 +1,110 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef DENSE_CHOLESKY_H +#define DENSE_CHOLESKY_H + +#include +#include +#include +#include "../Util/CompInfo.h" + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the operations related to Cholesky decomposition on a +/// positive definite matrix, \f$B=LL'\f$, where \f$L\f$ is a lower triangular +/// matrix. It is mainly used in the SymGEigsSolver generalized eigen solver +/// in the Cholesky decomposition mode. +/// +template +class DenseCholesky +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstMat; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef const Eigen::Ref ConstGenericMatrix; + + const Index m_n; + Eigen::LLT m_decomp; + int m_info; // status of the decomposition + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** matrix object, whose type can be + /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and + /// `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + DenseCholesky(ConstGenericMatrix& mat) : + m_n(mat.rows()), m_info(NOT_COMPUTED) + { + if(mat.rows() != mat.cols()) + throw std::invalid_argument("DenseCholesky: matrix must be square"); + + m_decomp.compute(mat); + m_info = (m_decomp.info() == Eigen::Success) ? + SUCCESSFUL : + NUMERICAL_ISSUE; + } + + /// + /// Returns the number of rows of the underlying matrix. + /// + Index rows() const { return m_n; } + /// + /// Returns the number of columns of the underlying matrix. + /// + Index cols() const { return m_n; } + + /// + /// Returns the status of the computation. + /// The full list of enumeration values can be found in \ref Enumerations. + /// + int info() const { return m_info; } + + /// + /// Performs the lower triangular solving operation \f$y=L^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(L) * x_in + void lower_triangular_solve(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_decomp.matrixL().solve(x); + } + + /// + /// Performs the upper triangular solving operation \f$y=(L')^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(L') * x_in + void upper_triangular_solve(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_decomp.matrixU().solve(x); + } +}; + + +} // namespace Spectra + +#endif // DENSE_CHOLESKY_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h new file mode 100644 index 00000000..7f189067 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/DenseGenComplexShiftSolve.h @@ -0,0 +1,104 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef DENSE_GEN_COMPLEX_SHIFT_SOLVE_H +#define DENSE_GEN_COMPLEX_SHIFT_SOLVE_H + +#include +#include +#include + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the complex shift-solve operation on a general real matrix \f$A\f$, +/// i.e., calculating \f$y=\mathrm{Re}\{(A-\sigma I)^{-1}x\}\f$ for any complex-valued +/// \f$\sigma\f$ and real-valued vector \f$x\f$. It is mainly used in the +/// GenEigsComplexShiftSolver eigen solver. +/// +template +class DenseGenComplexShiftSolve +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef const Eigen::Ref ConstGenericMatrix; + + typedef std::complex Complex; + typedef Eigen::Matrix ComplexMatrix; + typedef Eigen::Matrix ComplexVector; + + typedef Eigen::PartialPivLU ComplexSolver; + + ConstGenericMatrix m_mat; + const Index m_n; + ComplexSolver m_solver; + ComplexVector m_x_cache; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** matrix object, whose type can be + /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and + /// `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + DenseGenComplexShiftSolve(ConstGenericMatrix& mat) : + m_mat(mat), m_n(mat.rows()) + { + if(mat.rows() != mat.cols()) + throw std::invalid_argument("DenseGenComplexShiftSolve: matrix must be square"); + } + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_n; } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_n; } + + /// + /// Set the complex shift \f$\sigma\f$. + /// + /// \param sigmar Real part of \f$\sigma\f$. + /// \param sigmai Imaginary part of \f$\sigma\f$. + /// + void set_shift(Scalar sigmar, Scalar sigmai) + { + m_solver.compute(m_mat.template cast() - Complex(sigmar, sigmai) * ComplexMatrix::Identity(m_n, m_n)); + m_x_cache.resize(m_n); + m_x_cache.setZero(); + } + + /// + /// Perform the complex shift-solve operation + /// \f$y=\mathrm{Re}\{(A-\sigma I)^{-1}x\}\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = Re( inv(A - sigma * I) * x_in ) + void perform_op(const Scalar* x_in, Scalar* y_out) + { + m_x_cache.real() = MapConstVec(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_solver.solve(m_x_cache).real(); + } +}; + + +} // namespace Spectra + +#endif // DENSE_GEN_COMPLEX_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseGenMatProd.h b/src/external/Spectra/include/Spectra/MatOp/DenseGenMatProd.h new file mode 100644 index 00000000..6933dac0 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/DenseGenMatProd.h @@ -0,0 +1,82 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef DENSE_GEN_MAT_PROD_H +#define DENSE_GEN_MAT_PROD_H + +#include + +namespace Spectra { + + +/// +/// \defgroup MatOp Matrix Operations +/// +/// Define matrix operations on existing matrix objects +/// + +/// +/// \ingroup MatOp +/// +/// This class defines the matrix-vector multiplication operation on a +/// general real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector +/// \f$x\f$. It is mainly used in the GenEigsSolver and +/// SymEigsSolver eigen solvers. +/// +template +class DenseGenMatProd +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef const Eigen::Ref ConstGenericMatrix; + + ConstGenericMatrix m_mat; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** matrix object, whose type can be + /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and + /// `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + DenseGenMatProd(ConstGenericMatrix& mat) : + m_mat(mat) + {} + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_mat.rows(); } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_mat.cols(); } + + /// + /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = A * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_mat.cols()); + MapVec y(y_out, m_mat.rows()); + y.noalias() = m_mat * x; + } +}; + + +} // namespace Spectra + +#endif // DENSE_GEN_MAT_PROD_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h new file mode 100644 index 00000000..d7ba8f2a --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/DenseGenRealShiftSolve.h @@ -0,0 +1,90 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef DENSE_GEN_REAL_SHIFT_SOLVE_H +#define DENSE_GEN_REAL_SHIFT_SOLVE_H + +#include +#include +#include + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the shift-solve operation on a general real matrix \f$A\f$, +/// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and +/// vector \f$x\f$. It is mainly used in the GenEigsRealShiftSolver eigen solver. +/// +template +class DenseGenRealShiftSolve +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef const Eigen::Ref ConstGenericMatrix; + + ConstGenericMatrix m_mat; + const Index m_n; + Eigen::PartialPivLU m_solver; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** matrix object, whose type can be + /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and + /// `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + DenseGenRealShiftSolve(ConstGenericMatrix& mat) : + m_mat(mat), m_n(mat.rows()) + { + if(mat.rows() != mat.cols()) + throw std::invalid_argument("DenseGenRealShiftSolve: matrix must be square"); + } + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_n; } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_n; } + + /// + /// Set the real shift \f$\sigma\f$. + /// + void set_shift(Scalar sigma) + { + m_solver.compute(m_mat - sigma * Matrix::Identity(m_n, m_n)); + } + + /// + /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(A - sigma * I) * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_solver.solve(x); + } +}; + + +} // namespace Spectra + +#endif // DENSE_GEN_REAL_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseSymMatProd.h b/src/external/Spectra/include/Spectra/MatOp/DenseSymMatProd.h new file mode 100644 index 00000000..23ca36e4 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/DenseSymMatProd.h @@ -0,0 +1,75 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef DENSE_SYM_MAT_PROD_H +#define DENSE_SYM_MAT_PROD_H + +#include + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the matrix-vector multiplication operation on a +/// symmetric real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector +/// \f$x\f$. It is mainly used in the SymEigsSolver eigen solver. +/// +template +class DenseSymMatProd +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef const Eigen::Ref ConstGenericMatrix; + + ConstGenericMatrix m_mat; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** matrix object, whose type can be + /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and + /// `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + DenseSymMatProd(ConstGenericMatrix& mat) : + m_mat(mat) + {} + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_mat.rows(); } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_mat.cols(); } + + /// + /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = A * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_mat.cols()); + MapVec y(y_out, m_mat.rows()); + y.noalias() = m_mat.template selfadjointView() * x; + } +}; + + +} // namespace Spectra + +#endif // DENSE_SYM_MAT_PROD_H diff --git a/src/external/Spectra/include/Spectra/MatOp/DenseSymShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/DenseSymShiftSolve.h new file mode 100644 index 00000000..2e2c67f6 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/DenseSymShiftSolve.h @@ -0,0 +1,94 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef DENSE_SYM_SHIFT_SOLVE_H +#define DENSE_SYM_SHIFT_SOLVE_H + +#include +#include + +#include "../LinAlg/BKLDLT.h" +#include "../Util/CompInfo.h" + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the shift-solve operation on a real symmetric matrix \f$A\f$, +/// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and +/// vector \f$x\f$. It is mainly used in the SymEigsShiftSolver eigen solver. +/// +template +class DenseSymShiftSolve +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef const Eigen::Ref ConstGenericMatrix; + + ConstGenericMatrix m_mat; + const int m_n; + BKLDLT m_solver; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** matrix object, whose type can be + /// `Eigen::Matrix` (e.g. `Eigen::MatrixXd` and + /// `Eigen::MatrixXf`), or its mapped version + /// (e.g. `Eigen::Map`). + /// + DenseSymShiftSolve(ConstGenericMatrix& mat) : + m_mat(mat), m_n(mat.rows()) + { + if(mat.rows() != mat.cols()) + throw std::invalid_argument("DenseSymShiftSolve: matrix must be square"); + } + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_n; } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_n; } + + /// + /// Set the real shift \f$\sigma\f$. + /// + void set_shift(Scalar sigma) + { + m_solver.compute(m_mat, Uplo, sigma); + if(m_solver.info() != SUCCESSFUL) + throw std::invalid_argument("DenseSymShiftSolve: factorization failed with the given shift"); + } + + /// + /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(A - sigma * I) * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_solver.solve(x); + } +}; + + +} // namespace Spectra + +#endif // DENSE_SYM_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseCholesky.h b/src/external/Spectra/include/Spectra/MatOp/SparseCholesky.h new file mode 100644 index 00000000..0788596d --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/SparseCholesky.h @@ -0,0 +1,111 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPARSE_CHOLESKY_H +#define SPARSE_CHOLESKY_H + +#include +#include +#include +#include +#include "../Util/CompInfo.h" + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the operations related to Cholesky decomposition on a +/// sparse positive definite matrix, \f$B=LL'\f$, where \f$L\f$ is a lower triangular +/// matrix. It is mainly used in the SymGEigsSolver generalized eigen solver +/// in the Cholesky decomposition mode. +/// +template +class SparseCholesky +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef Eigen::SparseMatrix SparseMatrix; + typedef const Eigen::Ref ConstGenericSparseMatrix; + + const Index m_n; + Eigen::SimplicialLLT m_decomp; + int m_info; // status of the decomposition + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** sparse matrix object, whose type can be + /// `Eigen::SparseMatrix` or its mapped version + /// `Eigen::Map >`. + /// + SparseCholesky(ConstGenericSparseMatrix& mat) : + m_n(mat.rows()) + { + if(mat.rows() != mat.cols()) + throw std::invalid_argument("SparseCholesky: matrix must be square"); + + m_decomp.compute(mat); + m_info = (m_decomp.info() == Eigen::Success) ? + SUCCESSFUL : + NUMERICAL_ISSUE; + } + + /// + /// Returns the number of rows of the underlying matrix. + /// + Index rows() const { return m_n; } + /// + /// Returns the number of columns of the underlying matrix. + /// + Index cols() const { return m_n; } + + /// + /// Returns the status of the computation. + /// The full list of enumeration values can be found in \ref Enumerations. + /// + int info() const { return m_info; } + + /// + /// Performs the lower triangular solving operation \f$y=L^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(L) * x_in + void lower_triangular_solve(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_decomp.permutationP() * x; + m_decomp.matrixL().solveInPlace(y); + } + + /// + /// Performs the upper triangular solving operation \f$y=(L')^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(L') * x_in + void upper_triangular_solve(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_decomp.matrixU().solve(x); + y = m_decomp.permutationPinv() * y; + } +}; + + +} // namespace Spectra + +#endif // SPARSE_CHOLESKY_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseGenMatProd.h b/src/external/Spectra/include/Spectra/MatOp/SparseGenMatProd.h new file mode 100644 index 00000000..90881395 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/SparseGenMatProd.h @@ -0,0 +1,76 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPARSE_GEN_MAT_PROD_H +#define SPARSE_GEN_MAT_PROD_H + +#include +#include + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the matrix-vector multiplication operation on a +/// sparse real matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector +/// \f$x\f$. It is mainly used in the GenEigsSolver and SymEigsSolver +/// eigen solvers. +/// +template +class SparseGenMatProd +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef Eigen::SparseMatrix SparseMatrix; + typedef const Eigen::Ref ConstGenericSparseMatrix; + + ConstGenericSparseMatrix m_mat; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** sparse matrix object, whose type can be + /// `Eigen::SparseMatrix` or its mapped version + /// `Eigen::Map >`. + /// + SparseGenMatProd(ConstGenericSparseMatrix& mat) : + m_mat(mat) + {} + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_mat.rows(); } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_mat.cols(); } + + /// + /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = A * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_mat.cols()); + MapVec y(y_out, m_mat.rows()); + y.noalias() = m_mat * x; + } +}; + + +} // namespace Spectra + +#endif // SPARSE_GEN_MAT_PROD_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h new file mode 100644 index 00000000..df4ec6cf --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/SparseGenRealShiftSolve.h @@ -0,0 +1,95 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPARSE_GEN_REAL_SHIFT_SOLVE_H +#define SPARSE_GEN_REAL_SHIFT_SOLVE_H + +#include +#include +#include +#include + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the shift-solve operation on a sparse real matrix \f$A\f$, +/// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and +/// vector \f$x\f$. It is mainly used in the GenEigsRealShiftSolver eigen solver. +/// +template +class SparseGenRealShiftSolve +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef Eigen::SparseMatrix SparseMatrix; + typedef const Eigen::Ref ConstGenericSparseMatrix; + + ConstGenericSparseMatrix m_mat; + const int m_n; + Eigen::SparseLU m_solver; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** sparse matrix object, whose type can be + /// `Eigen::SparseMatrix` or its mapped version + /// `Eigen::Map >`. + /// + SparseGenRealShiftSolve(ConstGenericSparseMatrix& mat) : + m_mat(mat), m_n(mat.rows()) + { + if(mat.rows() != mat.cols()) + throw std::invalid_argument("SparseGenRealShiftSolve: matrix must be square"); + } + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_n; } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_n; } + + /// + /// Set the real shift \f$\sigma\f$. + /// + void set_shift(Scalar sigma) + { + SparseMatrix I(m_n, m_n); + I.setIdentity(); + + m_solver.compute(m_mat - sigma * I); + if(m_solver.info() != Eigen::Success) + throw std::invalid_argument("SparseGenRealShiftSolve: factorization failed with the given shift"); + } + + /// + /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(A - sigma * I) * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_solver.solve(x); + } +}; + + +} // namespace Spectra + +#endif // SPARSE_GEN_REAL_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseRegularInverse.h b/src/external/Spectra/include/Spectra/MatOp/SparseRegularInverse.h new file mode 100644 index 00000000..ec6614a5 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/SparseRegularInverse.h @@ -0,0 +1,102 @@ +// Copyright (C) 2017-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPARSE_REGULAR_INVERSE_H +#define SPARSE_REGULAR_INVERSE_H + +#include +#include +#include +#include + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines matrix operations required by the generalized eigen solver +/// in the regular inverse mode. For a sparse and positive definite matrix \f$B\f$, +/// it implements the matrix-vector product \f$y=Bx\f$ and the linear equation +/// solving operation \f$y=B^{-1}x\f$. +/// +/// This class is intended to be used with the SymGEigsSolver generalized eigen solver +/// in the regular inverse mode. +/// +template +class SparseRegularInverse +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef Eigen::SparseMatrix SparseMatrix; + typedef const Eigen::Ref ConstGenericSparseMatrix; + + ConstGenericSparseMatrix m_mat; + const int m_n; + Eigen::ConjugateGradient m_cg; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** sparse matrix object, whose type can be + /// `Eigen::SparseMatrix` or its mapped version + /// `Eigen::Map >`. + /// + SparseRegularInverse(ConstGenericSparseMatrix& mat) : + m_mat(mat), m_n(mat.rows()) + { + if(mat.rows() != mat.cols()) + throw std::invalid_argument("SparseRegularInverse: matrix must be square"); + + m_cg.compute(mat); + } + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_n; } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_n; } + + /// + /// Perform the solving operation \f$y=B^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(B) * x_in + void solve(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_cg.solve(x); + } + + /// + /// Perform the matrix-vector multiplication operation \f$y=Bx\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = B * x_in + void mat_prod(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_mat.template selfadjointView() * x; + } +}; + + +} // namespace Spectra + +#endif // SPARSE_REGULAR_INVERSE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseSymMatProd.h b/src/external/Spectra/include/Spectra/MatOp/SparseSymMatProd.h new file mode 100644 index 00000000..ef8f96ee --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/SparseSymMatProd.h @@ -0,0 +1,75 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPARSE_SYM_MAT_PROD_H +#define SPARSE_SYM_MAT_PROD_H + +#include +#include + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the matrix-vector multiplication operation on a +/// sparse real symmetric matrix \f$A\f$, i.e., calculating \f$y=Ax\f$ for any vector +/// \f$x\f$. It is mainly used in the SymEigsSolver eigen solver. +/// +template +class SparseSymMatProd +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef Eigen::SparseMatrix SparseMatrix; + typedef const Eigen::Ref ConstGenericSparseMatrix; + + ConstGenericSparseMatrix m_mat; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** sparse matrix object, whose type can be + /// `Eigen::SparseMatrix` or its mapped version + /// `Eigen::Map >`. + /// + SparseSymMatProd(ConstGenericSparseMatrix& mat) : + m_mat(mat) + {} + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_mat.rows(); } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_mat.cols(); } + + /// + /// Perform the matrix-vector multiplication operation \f$y=Ax\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = A * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_mat.cols()); + MapVec y(y_out, m_mat.rows()); + y.noalias() = m_mat.template selfadjointView() * x; + } +}; + + +} // namespace Spectra + +#endif // SPARSE_SYM_MAT_PROD_H diff --git a/src/external/Spectra/include/Spectra/MatOp/SparseSymShiftSolve.h b/src/external/Spectra/include/Spectra/MatOp/SparseSymShiftSolve.h new file mode 100644 index 00000000..1821cc32 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/SparseSymShiftSolve.h @@ -0,0 +1,97 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SPARSE_SYM_SHIFT_SOLVE_H +#define SPARSE_SYM_SHIFT_SOLVE_H + +#include +#include +#include +#include + +namespace Spectra { + + +/// +/// \ingroup MatOp +/// +/// This class defines the shift-solve operation on a sparse real symmetric matrix \f$A\f$, +/// i.e., calculating \f$y=(A-\sigma I)^{-1}x\f$ for any real \f$\sigma\f$ and +/// vector \f$x\f$. It is mainly used in the SymEigsShiftSolver eigen solver. +/// +template +class SparseSymShiftSolve +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef Eigen::SparseMatrix SparseMatrix; + typedef const Eigen::Ref ConstGenericSparseMatrix; + + ConstGenericSparseMatrix m_mat; + const int m_n; + Eigen::SparseLU m_solver; + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param mat An **Eigen** sparse matrix object, whose type can be + /// `Eigen::SparseMatrix` or its mapped version + /// `Eigen::Map >`. + /// + SparseSymShiftSolve(ConstGenericSparseMatrix& mat) : + m_mat(mat), m_n(mat.rows()) + { + if(mat.rows() != mat.cols()) + throw std::invalid_argument("SparseSymShiftSolve: matrix must be square"); + } + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_n; } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_n; } + + /// + /// Set the real shift \f$\sigma\f$. + /// + void set_shift(Scalar sigma) + { + SparseMatrix mat = m_mat.template selfadjointView(); + SparseMatrix identity(m_n, m_n); + identity.setIdentity(); + mat = mat - sigma * identity; + m_solver.isSymmetric(true); + m_solver.compute(mat); + if(m_solver.info() != Eigen::Success) + throw std::invalid_argument("SparseSymShiftSolve: factorization failed with the given shift"); + } + + /// + /// Perform the shift-solve operation \f$y=(A-\sigma I)^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(A - sigma * I) * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) const + { + MapConstVec x(x_in, m_n); + MapVec y(y_out, m_n); + y.noalias() = m_solver.solve(x); + } +}; + + +} // namespace Spectra + +#endif // SPARSE_SYM_SHIFT_SOLVE_H diff --git a/src/external/Spectra/include/Spectra/MatOp/internal/ArnoldiOp.h b/src/external/Spectra/include/Spectra/MatOp/internal/ArnoldiOp.h new file mode 100644 index 00000000..b79704b5 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/internal/ArnoldiOp.h @@ -0,0 +1,155 @@ +// Copyright (C) 2018-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef ARNOLDI_OP_H +#define ARNOLDI_OP_H + +#include +#include // std::sqrt + +namespace Spectra { + + +/// +/// \ingroup Internals +/// @{ +/// + +/// +/// \defgroup Operators Operators +/// +/// Different types of operators. +/// + +/// +/// \ingroup Operators +/// +/// Operators used in the Arnoldi factorization. +/// +template +class ArnoldiOp +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + + OpType& m_op; + BOpType& m_Bop; + Vector m_cache; + +public: + ArnoldiOp(OpType* op, BOpType* Bop) : + m_op(*op), m_Bop(*Bop), m_cache(op->rows()) + {} + + inline Index rows() const { return m_op.rows(); } + + // In generalized eigenvalue problem Ax=lambda*Bx, define the inner product to be = x'By. + // For regular eigenvalue problems, it is the usual inner product = x'y + + // Compute = x'By + // x and y are two vectors + template + Scalar inner_product(const Arg1& x, const Arg2& y) + { + m_Bop.mat_prod(y.data(), m_cache.data()); + return x.dot(m_cache); + } + + // Compute res = = X'By + // X is a matrix, y is a vector, res is a vector + template + void trans_product(const Arg1& x, const Arg2& y, Eigen::Ref res) + { + m_Bop.mat_prod(y.data(), m_cache.data()); + res.noalias() = x.transpose() * m_cache; + } + + // B-norm of a vector, ||x||_B = sqrt(x'Bx) + template + Scalar norm(const Arg& x) + { + using std::sqrt; + return sqrt(inner_product(x, x)); + } + + // The "A" operator to generate the Krylov subspace + inline void perform_op(const Scalar* x_in, Scalar* y_out) + { + m_op.perform_op(x_in, y_out); + } +}; + + + +/// +/// \ingroup Operators +/// +/// Placeholder for the B-operator when \f$B = I\f$. +/// +class IdentityBOp {}; + + + +/// +/// \ingroup Operators +/// +/// Partial specialization for the case \f$B = I\f$. +/// +template +class ArnoldiOp +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + + OpType& m_op; + +public: + ArnoldiOp(OpType* op, IdentityBOp* /*Bop*/) : + m_op(*op) + {} + + inline Index rows() const { return m_op.rows(); } + + // Compute = x'y + // x and y are two vectors + template + Scalar inner_product(const Arg1& x, const Arg2& y) const + { + return x.dot(y); + } + + // Compute res = = X'y + // X is a matrix, y is a vector, res is a vector + template + void trans_product(const Arg1& x, const Arg2& y, Eigen::Ref res) const + { + res.noalias() = x.transpose() * y; + } + + // B-norm of a vector. For regular eigenvalue problems it is simply the L2 norm + template + Scalar norm(const Arg& x) + { + return x.norm(); + } + + // The "A" operator to generate the Krylov subspace + inline void perform_op(Scalar* x_in, Scalar* y_out) + { + m_op.perform_op(x_in, y_out); + } +}; + +/// +/// @} +/// + + +} // namespace Spectra + +#endif // ARNOLDI_OP_H diff --git a/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h b/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h new file mode 100644 index 00000000..d7acdb2a --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsCholeskyOp.h @@ -0,0 +1,77 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SYM_GEIGS_CHOLESKY_OP_H +#define SYM_GEIGS_CHOLESKY_OP_H + +#include +#include "../DenseSymMatProd.h" +#include "../DenseCholesky.h" + +namespace Spectra { + + +/// +/// \ingroup Operators +/// +/// This class defines the matrix operation for generalized eigen solver in the +/// Cholesky decomposition mode. It calculates \f$y=L^{-1}A(L')^{-1}x\f$ for any +/// vector \f$x\f$, where \f$L\f$ is the Cholesky decomposition of \f$B\f$. +/// This class is intended for internal use. +/// +template < typename Scalar = double, + typename OpType = DenseSymMatProd, + typename BOpType = DenseCholesky > +class SymGEigsCholeskyOp +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + + OpType& m_op; + BOpType& m_Bop; + Vector m_cache; // temporary working space + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param op Pointer to the \f$A\f$ matrix operation object. + /// \param Bop Pointer to the \f$B\f$ matrix operation object. + /// + SymGEigsCholeskyOp(OpType& op, BOpType& Bop) : + m_op(op), m_Bop(Bop), m_cache(op.rows()) + {} + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_Bop.rows(); } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_Bop.rows(); } + + /// + /// Perform the matrix operation \f$y=L^{-1}A(L')^{-1}x\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(L) * A * inv(L') * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) + { + m_Bop.upper_triangular_solve(x_in, y_out); + m_op.perform_op(y_out, m_cache.data()); + m_Bop.lower_triangular_solve(m_cache.data(), y_out); + } +}; + + +} // namespace Spectra + +#endif // SYM_GEIGS_CHOLESKY_OP_H diff --git a/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h b/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h new file mode 100644 index 00000000..ac00dcb2 --- /dev/null +++ b/src/external/Spectra/include/Spectra/MatOp/internal/SymGEigsRegInvOp.h @@ -0,0 +1,74 @@ +// Copyright (C) 2017-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SYM_GEIGS_REG_INV_OP_H +#define SYM_GEIGS_REG_INV_OP_H + +#include +#include "../SparseSymMatProd.h" +#include "../SparseRegularInverse.h" + +namespace Spectra { + + +/// +/// \ingroup Operators +/// +/// This class defines the matrix operation for generalized eigen solver in the +/// regular inverse mode. This class is intended for internal use. +/// +template < typename Scalar = double, + typename OpType = SparseSymMatProd, + typename BOpType = SparseRegularInverse > +class SymGEigsRegInvOp +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + + OpType& m_op; + BOpType& m_Bop; + Vector m_cache; // temporary working space + +public: + /// + /// Constructor to create the matrix operation object. + /// + /// \param op Pointer to the \f$A\f$ matrix operation object. + /// \param Bop Pointer to the \f$B\f$ matrix operation object. + /// + SymGEigsRegInvOp(OpType& op, BOpType& Bop) : + m_op(op), m_Bop(Bop), m_cache(op.rows()) + {} + + /// + /// Return the number of rows of the underlying matrix. + /// + Index rows() const { return m_Bop.rows(); } + /// + /// Return the number of columns of the underlying matrix. + /// + Index cols() const { return m_Bop.rows(); } + + /// + /// Perform the matrix operation \f$y=B^{-1}Ax\f$. + /// + /// \param x_in Pointer to the \f$x\f$ vector. + /// \param y_out Pointer to the \f$y\f$ vector. + /// + // y_out = inv(B) * A * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) + { + m_op.perform_op(x_in, m_cache.data()); + m_Bop.solve(m_cache.data(), y_out); + } +}; + + +} // namespace Spectra + +#endif // SYM_GEIGS_REG_INV_OP_H diff --git a/src/external/Spectra/include/Spectra/SymEigsBase.h b/src/external/Spectra/include/Spectra/SymEigsBase.h new file mode 100644 index 00000000..24d5b505 --- /dev/null +++ b/src/external/Spectra/include/Spectra/SymEigsBase.h @@ -0,0 +1,447 @@ +// Copyright (C) 2018-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SYM_EIGS_BASE_H +#define SYM_EIGS_BASE_H + +#include +#include // std::vector +#include // std::abs, std::pow, std::sqrt +#include // std::min, std::copy +#include // std::invalid_argument + +#include "Util/TypeTraits.h" +#include "Util/SelectionRule.h" +#include "Util/CompInfo.h" +#include "Util/SimpleRandom.h" +#include "MatOp/internal/ArnoldiOp.h" +#include "LinAlg/UpperHessenbergQR.h" +#include "LinAlg/TridiagEigen.h" +#include "LinAlg/Lanczos.h" + +namespace Spectra { + + +/// +/// \defgroup EigenSolver Eigen Solvers +/// +/// Eigen solvers for different types of problems. +/// + +/// +/// \ingroup EigenSolver +/// +/// This is the base class for symmetric eigen solvers, mainly for internal use. +/// It is kept here to provide the documentation for member functions of concrete eigen solvers +/// such as SymEigsSolver and SymEigsShiftSolver. +/// +template < typename Scalar, + int SelectionRule, + typename OpType, + typename BOpType > +class SymEigsBase +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef Eigen::Array Array; + typedef Eigen::Array BoolArray; + typedef Eigen::Map MapMat; + typedef Eigen::Map MapVec; + typedef Eigen::Map MapConstVec; + + typedef ArnoldiOp ArnoldiOpType; + typedef Lanczos LanczosFac; + +protected: + OpType* m_op; // object to conduct matrix operation, + // e.g. matrix-vector product + const Index m_n; // dimension of matrix A + const Index m_nev; // number of eigenvalues requested + const Index m_ncv; // dimension of Krylov subspace in the Lanczos method + Index m_nmatop; // number of matrix operations called + Index m_niter; // number of restarting iterations + + LanczosFac m_fac; // Lanczos factorization + Vector m_ritz_val; // Ritz values + +private: + Matrix m_ritz_vec; // Ritz vectors + Vector m_ritz_est; // last row of m_ritz_vec, also called the Ritz estimates + BoolArray m_ritz_conv; // indicator of the convergence of Ritz values + int m_info; // status of the computation + + const Scalar m_near_0; // a very small value, but 1.0 / m_near_0 does not overflow + // ~= 1e-307 for the "double" type + const Scalar m_eps; // the machine precision, ~= 1e-16 for the "double" type + const Scalar m_eps23; // m_eps^(2/3), used to test the convergence + + // Implicitly restarted Lanczos factorization + void restart(Index k) + { + if(k >= m_ncv) + return; + + TridiagQR decomp(m_ncv); + Matrix Q = Matrix::Identity(m_ncv, m_ncv); + + for(Index i = k; i < m_ncv; i++) + { + // QR decomposition of H-mu*I, mu is the shift + decomp.compute(m_fac.matrix_H(), m_ritz_val[i]); + + // Q -> Q * Qi + decomp.apply_YQ(Q); + // H -> Q'HQ + // Since QR = H - mu * I, we have H = QR + mu * I + // and therefore Q'HQ = RQ + mu * I + m_fac.compress_H(decomp); + } + + m_fac.compress_V(Q); + m_fac.factorize_from(k, m_ncv, m_nmatop); + + retrieve_ritzpair(); + } + + // Calculates the number of converged Ritz values + Index num_converged(Scalar tol) + { + // thresh = tol * max(m_eps23, abs(theta)), theta for Ritz value + Array thresh = tol * m_ritz_val.head(m_nev).array().abs().max(m_eps23); + Array resid = m_ritz_est.head(m_nev).array().abs() * m_fac.f_norm(); + // Converged "wanted" Ritz values + m_ritz_conv = (resid < thresh); + + return m_ritz_conv.cast().sum(); + } + + // Returns the adjusted nev for restarting + Index nev_adjusted(Index nconv) + { + using std::abs; + + Index nev_new = m_nev; + for(Index i = m_nev; i < m_ncv; i++) + if(abs(m_ritz_est[i]) < m_near_0) nev_new++; + + // Adjust nev_new, according to dsaup2.f line 677~684 in ARPACK + nev_new += std::min(nconv, (m_ncv - nev_new) / 2); + if(nev_new == 1 && m_ncv >= 6) + nev_new = m_ncv / 2; + else if(nev_new == 1 && m_ncv > 2) + nev_new = 2; + + if(nev_new > m_ncv - 1) + nev_new = m_ncv - 1; + + return nev_new; + } + + // Retrieves and sorts Ritz values and Ritz vectors + void retrieve_ritzpair() + { + TridiagEigen decomp(m_fac.matrix_H()); + const Vector& evals = decomp.eigenvalues(); + const Matrix& evecs = decomp.eigenvectors(); + + SortEigenvalue sorting(evals.data(), evals.size()); + std::vector ind = sorting.index(); + + // For BOTH_ENDS, the eigenvalues are sorted according + // to the LARGEST_ALGE rule, so we need to move those smallest + // values to the left + // The order would be + // Largest => Smallest => 2nd largest => 2nd smallest => ... + // We keep this order since the first k values will always be + // the wanted collection, no matter k is nev_updated (used in restart()) + // or is nev (used in sort_ritzpair()) + if(SelectionRule == BOTH_ENDS) + { + std::vector ind_copy(ind); + for(Index i = 0; i < m_ncv; i++) + { + // If i is even, pick values from the left (large values) + // If i is odd, pick values from the right (small values) + if(i % 2 == 0) + ind[i] = ind_copy[i / 2]; + else + ind[i] = ind_copy[m_ncv - 1 - i / 2]; + } + } + + // Copy the Ritz values and vectors to m_ritz_val and m_ritz_vec, respectively + for(Index i = 0; i < m_ncv; i++) + { + m_ritz_val[i] = evals[ind[i]]; + m_ritz_est[i] = evecs(m_ncv - 1, ind[i]); + } + for(Index i = 0; i < m_nev; i++) + { + m_ritz_vec.col(i).noalias() = evecs.col(ind[i]); + } + } + +protected: + // Sorts the first nev Ritz pairs in the specified order + // This is used to return the final results + virtual void sort_ritzpair(int sort_rule) + { + // First make sure that we have a valid index vector + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + std::vector ind = sorting.index(); + + switch(sort_rule) + { + case LARGEST_ALGE: + break; + case LARGEST_MAGN: + { + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + ind = sorting.index(); + } + break; + case SMALLEST_ALGE: + { + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + ind = sorting.index(); + } + break; + case SMALLEST_MAGN: + { + SortEigenvalue sorting(m_ritz_val.data(), m_nev); + ind = sorting.index(); + } + break; + default: + throw std::invalid_argument("unsupported sorting rule"); + } + + Vector new_ritz_val(m_ncv); + Matrix new_ritz_vec(m_ncv, m_nev); + BoolArray new_ritz_conv(m_nev); + + for(Index i = 0; i < m_nev; i++) + { + new_ritz_val[i] = m_ritz_val[ind[i]]; + new_ritz_vec.col(i).noalias() = m_ritz_vec.col(ind[i]); + new_ritz_conv[i] = m_ritz_conv[ind[i]]; + } + + m_ritz_val.swap(new_ritz_val); + m_ritz_vec.swap(new_ritz_vec); + m_ritz_conv.swap(new_ritz_conv); + } + +public: + /// \cond + + SymEigsBase(OpType* op, BOpType* Bop, Index nev, Index ncv) : + m_op(op), + m_n(m_op->rows()), + m_nev(nev), + m_ncv(ncv > m_n ? m_n : ncv), + m_nmatop(0), + m_niter(0), + m_fac(ArnoldiOpType(op, Bop), m_ncv), + m_info(NOT_COMPUTED), + m_near_0(TypeTraits::min() * Scalar(10)), + m_eps(Eigen::NumTraits::epsilon()), + m_eps23(Eigen::numext::pow(m_eps, Scalar(2.0) / 3)) + { + if(nev < 1 || nev > m_n - 1) + throw std::invalid_argument("nev must satisfy 1 <= nev <= n - 1, n is the size of matrix"); + + if(ncv <= nev || ncv > m_n) + throw std::invalid_argument("ncv must satisfy nev < ncv <= n, n is the size of matrix"); + } + + /// + /// Virtual destructor + /// + virtual ~SymEigsBase() {} + + /// \endcond + + /// + /// Initializes the solver by providing an initial residual vector. + /// + /// \param init_resid Pointer to the initial residual vector. + /// + /// **Spectra** (and also **ARPACK**) uses an iterative algorithm + /// to find eigenvalues. This function allows the user to provide the initial + /// residual vector. + /// + void init(const Scalar* init_resid) + { + // Reset all matrices/vectors to zero + m_ritz_val.resize(m_ncv); + m_ritz_vec.resize(m_ncv, m_nev); + m_ritz_est.resize(m_ncv); + m_ritz_conv.resize(m_nev); + + m_ritz_val.setZero(); + m_ritz_vec.setZero(); + m_ritz_est.setZero(); + m_ritz_conv.setZero(); + + m_nmatop = 0; + m_niter = 0; + + // Initialize the Lanczos factorization + MapConstVec v0(init_resid, m_n); + m_fac.init(v0, m_nmatop); + } + + /// + /// Initializes the solver by providing a random initial residual vector. + /// + /// This overloaded function generates a random initial residual vector + /// (with a fixed random seed) for the algorithm. Elements in the vector + /// follow independent Uniform(-0.5, 0.5) distribution. + /// + void init() + { + SimpleRandom rng(0); + Vector init_resid = rng.random_vec(m_n); + init(init_resid.data()); + } + + /// + /// Conducts the major computation procedure. + /// + /// \param maxit Maximum number of iterations allowed in the algorithm. + /// \param tol Precision parameter for the calculated eigenvalues. + /// \param sort_rule Rule to sort the eigenvalues and eigenvectors. + /// Supported values are + /// `Spectra::LARGEST_ALGE`, `Spectra::LARGEST_MAGN`, + /// `Spectra::SMALLEST_ALGE` and `Spectra::SMALLEST_MAGN`, + /// for example `LARGEST_ALGE` indicates that largest eigenvalues + /// come first. Note that this argument is only used to + /// **sort** the final result, and the **selection** rule + /// (e.g. selecting the largest or smallest eigenvalues in the + /// full spectrum) is specified by the template parameter + /// `SelectionRule` of SymEigsSolver. + /// + /// \return Number of converged eigenvalues. + /// + Index compute(Index maxit = 1000, Scalar tol = 1e-10, int sort_rule = LARGEST_ALGE) + { + // The m-step Lanczos factorization + m_fac.factorize_from(1, m_ncv, m_nmatop); + retrieve_ritzpair(); + // Restarting + Index i, nconv = 0, nev_adj; + for(i = 0; i < maxit; i++) + { + nconv = num_converged(tol); + if(nconv >= m_nev) + break; + + nev_adj = nev_adjusted(nconv); + restart(nev_adj); + } + // Sorting results + sort_ritzpair(sort_rule); + + m_niter += i + 1; + m_info = (nconv >= m_nev) ? SUCCESSFUL : NOT_CONVERGING; + + return std::min(m_nev, nconv); + } + + /// + /// Returns the status of the computation. + /// The full list of enumeration values can be found in \ref Enumerations. + /// + int info() const { return m_info; } + + /// + /// Returns the number of iterations used in the computation. + /// + Index num_iterations() const { return m_niter; } + + /// + /// Returns the number of matrix operations used in the computation. + /// + Index num_operations() const { return m_nmatop; } + + /// + /// Returns the converged eigenvalues. + /// + /// \return A vector containing the eigenvalues. + /// Returned vector type will be `Eigen::Vector`, depending on + /// the template parameter `Scalar` defined. + /// + Vector eigenvalues() const + { + const Index nconv = m_ritz_conv.cast().sum(); + Vector res(nconv); + + if(!nconv) + return res; + + Index j = 0; + for(Index i = 0; i < m_nev; i++) + { + if(m_ritz_conv[i]) + { + res[j] = m_ritz_val[i]; + j++; + } + } + + return res; + } + + /// + /// Returns the eigenvectors associated with the converged eigenvalues. + /// + /// \param nvec The number of eigenvectors to return. + /// + /// \return A matrix containing the eigenvectors. + /// Returned matrix type will be `Eigen::Matrix`, + /// depending on the template parameter `Scalar` defined. + /// + virtual Matrix eigenvectors(Index nvec) const + { + const Index nconv = m_ritz_conv.cast().sum(); + nvec = std::min(nvec, nconv); + Matrix res(m_n, nvec); + + if(!nvec) + return res; + + Matrix ritz_vec_conv(m_ncv, nvec); + Index j = 0; + for(Index i = 0; i < m_nev && j < nvec; i++) + { + if(m_ritz_conv[i]) + { + ritz_vec_conv.col(j).noalias() = m_ritz_vec.col(i); + j++; + } + } + + res.noalias() = m_fac.matrix_V() * ritz_vec_conv; + + return res; + } + + /// + /// Returns all converged eigenvectors. + /// + virtual Matrix eigenvectors() const + { + return eigenvectors(m_nev); + } +}; + + +} // namespace Spectra + +#endif // SYM_EIGS_BASE_H diff --git a/src/external/Spectra/include/Spectra/SymEigsShiftSolver.h b/src/external/Spectra/include/Spectra/SymEigsShiftSolver.h new file mode 100644 index 00000000..56bd44ec --- /dev/null +++ b/src/external/Spectra/include/Spectra/SymEigsShiftSolver.h @@ -0,0 +1,205 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SYM_EIGS_SHIFT_SOLVER_H +#define SYM_EIGS_SHIFT_SOLVER_H + +#include + +#include "SymEigsBase.h" +#include "Util/SelectionRule.h" +#include "MatOp/DenseSymShiftSolve.h" + +namespace Spectra { + + +/// +/// \ingroup EigenSolver +/// +/// This class implements the eigen solver for real symmetric matrices using +/// the **shift-and-invert mode**. The background information of the symmetric +/// eigen solver is documented in the SymEigsSolver class. Here we focus on +/// explaining the shift-and-invert mode. +/// +/// The shift-and-invert mode is based on the following fact: +/// If \f$\lambda\f$ and \f$x\f$ are a pair of eigenvalue and eigenvector of +/// matrix \f$A\f$, such that \f$Ax=\lambda x\f$, then for any \f$\sigma\f$, +/// we have +/// \f[(A-\sigma I)^{-1}x=\nu x\f] +/// where +/// \f[\nu=\frac{1}{\lambda-\sigma}\f] +/// which indicates that \f$(\nu, x)\f$ is an eigenpair of the matrix +/// \f$(A-\sigma I)^{-1}\f$. +/// +/// Therefore, if we pass the matrix operation \f$(A-\sigma I)^{-1}y\f$ +/// (rather than \f$Ay\f$) to the eigen solver, then we would get the desired +/// values of \f$\nu\f$, and \f$\lambda\f$ can also be easily obtained by noting +/// that \f$\lambda=\sigma+\nu^{-1}\f$. +/// +/// The reason why we need this type of manipulation is that +/// the algorithm of **Spectra** (and also **ARPACK**) +/// is good at finding eigenvalues with large magnitude, but may fail in looking +/// for eigenvalues that are close to zero. However, if we really need them, we +/// can set \f$\sigma=0\f$, find the largest eigenvalues of \f$A^{-1}\f$, and then +/// transform back to \f$\lambda\f$, since in this case largest values of \f$\nu\f$ +/// implies smallest values of \f$\lambda\f$. +/// +/// To summarize, in the shift-and-invert mode, the selection rule will apply to +/// \f$\nu=1/(\lambda-\sigma)\f$ rather than \f$\lambda\f$. So a selection rule +/// of `LARGEST_MAGN` combined with shift \f$\sigma\f$ will find eigenvalues of +/// \f$A\f$ that are closest to \f$\sigma\f$. But note that the eigenvalues() +/// method will always return the eigenvalues in the original problem (i.e., +/// returning \f$\lambda\f$ rather than \f$\nu\f$), and eigenvectors are the +/// same for both the original problem and the shifted-and-inverted problem. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// \tparam SelectionRule An enumeration value indicating the selection rule of +/// the shifted-and-inverted eigenvalues. +/// The full list of enumeration values can be found in +/// \ref Enumerations. +/// \tparam OpType The name of the matrix operation class. Users could either +/// use the wrapper classes such as DenseSymShiftSolve and +/// SparseSymShiftSolve, or define their +/// own that implements all the public member functions as in +/// DenseSymShiftSolve. +/// +/// Below is an example that illustrates the use of the shift-and-invert mode: +/// +/// \code{.cpp} +/// #include +/// #include +/// // is implicitly included +/// #include +/// +/// using namespace Spectra; +/// +/// int main() +/// { +/// // A size-10 diagonal matrix with elements 1, 2, ..., 10 +/// Eigen::MatrixXd M = Eigen::MatrixXd::Zero(10, 10); +/// for(int i = 0; i < M.rows(); i++) +/// M(i, i) = i + 1; +/// +/// // Construct matrix operation object using the wrapper class +/// DenseSymShiftSolve op(M); +/// +/// // Construct eigen solver object with shift 0 +/// // This will find eigenvalues that are closest to 0 +/// SymEigsShiftSolver< double, LARGEST_MAGN, +/// DenseSymShiftSolve > eigs(&op, 3, 6, 0.0); +/// +/// eigs.init(); +/// eigs.compute(); +/// if(eigs.info() == SUCCESSFUL) +/// { +/// Eigen::VectorXd evalues = eigs.eigenvalues(); +/// // Will get (3.0, 2.0, 1.0) +/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; +/// } +/// +/// return 0; +/// } +/// \endcode +/// +/// Also an example for user-supplied matrix shift-solve operation class: +/// +/// \code{.cpp} +/// #include +/// #include +/// #include +/// +/// using namespace Spectra; +/// +/// // M = diag(1, 2, ..., 10) +/// class MyDiagonalTenShiftSolve +/// { +/// private: +/// double sigma_; +/// public: +/// int rows() { return 10; } +/// int cols() { return 10; } +/// void set_shift(double sigma) { sigma_ = sigma; } +/// // y_out = inv(A - sigma * I) * x_in +/// // inv(A - sigma * I) = diag(1/(1-sigma), 1/(2-sigma), ...) +/// void perform_op(double *x_in, double *y_out) +/// { +/// for(int i = 0; i < rows(); i++) +/// { +/// y_out[i] = x_in[i] / (i + 1 - sigma_); +/// } +/// } +/// }; +/// +/// int main() +/// { +/// MyDiagonalTenShiftSolve op; +/// // Find three eigenvalues that are closest to 3.14 +/// SymEigsShiftSolver eigs(&op, 3, 6, 3.14); +/// eigs.init(); +/// eigs.compute(); +/// if(eigs.info() == SUCCESSFUL) +/// { +/// Eigen::VectorXd evalues = eigs.eigenvalues(); +/// // Will get (4.0, 3.0, 2.0) +/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; +/// } +/// +/// return 0; +/// } +/// \endcode +/// +template > +class SymEigsShiftSolver: public SymEigsBase +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Array Array; + + const Scalar m_sigma; + + // First transform back the Ritz values, and then sort + void sort_ritzpair(int sort_rule) + { + Array m_ritz_val_org = Scalar(1.0) / this->m_ritz_val.head(this->m_nev).array() + m_sigma; + this->m_ritz_val.head(this->m_nev) = m_ritz_val_org; + SymEigsBase::sort_ritzpair(sort_rule); + } + +public: + /// + /// Constructor to create a eigen solver object using the shift-and-invert mode. + /// + /// \param op Pointer to the matrix operation object, which should implement + /// the shift-solve operation of \f$A\f$: calculating + /// \f$(A-\sigma I)^{-1}v\f$ for any vector \f$v\f$. Users could either + /// create the object from the wrapper class such as DenseSymShiftSolve, or + /// define their own that implements all the public member functions + /// as in DenseSymShiftSolve. + /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, + /// where \f$n\f$ is the size of matrix. + /// \param ncv Parameter that controls the convergence speed of the algorithm. + /// Typically a larger `ncv_` means faster convergence, but it may + /// also result in greater memory use and more matrix operations + /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, + /// and is advised to take \f$ncv \ge 2\cdot nev\f$. + /// \param sigma The value of the shift. + /// + SymEigsShiftSolver(OpType* op, Index nev, Index ncv, Scalar sigma) : + SymEigsBase(op, NULL, nev, ncv), + m_sigma(sigma) + { + this->m_op->set_shift(m_sigma); + } +}; + + +} // namespace Spectra + +#endif // SYM_EIGS_SHIFT_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/SymEigsSolver.h b/src/external/Spectra/include/Spectra/SymEigsSolver.h new file mode 100644 index 00000000..8ba3e509 --- /dev/null +++ b/src/external/Spectra/include/Spectra/SymEigsSolver.h @@ -0,0 +1,174 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SYM_EIGS_SOLVER_H +#define SYM_EIGS_SOLVER_H + +#include + +#include "SymEigsBase.h" +#include "Util/SelectionRule.h" +#include "MatOp/DenseSymMatProd.h" + +namespace Spectra { + + +/// +/// \ingroup EigenSolver +/// +/// This class implements the eigen solver for real symmetric matrices, i.e., +/// to solve \f$Ax=\lambda x\f$ where \f$A\f$ is symmetric. +/// +/// **Spectra** is designed to calculate a specified number (\f$k\f$) +/// of eigenvalues of a large square matrix (\f$A\f$). Usually \f$k\f$ is much +/// less than the size of the matrix (\f$n\f$), so that only a few eigenvalues +/// and eigenvectors are computed. +/// +/// Rather than providing the whole \f$A\f$ matrix, the algorithm only requires +/// the matrix-vector multiplication operation of \f$A\f$. Therefore, users of +/// this solver need to supply a class that computes the result of \f$Av\f$ +/// for any given vector \f$v\f$. The name of this class should be given to +/// the template parameter `OpType`, and instance of this class passed to +/// the constructor of SymEigsSolver. +/// +/// If the matrix \f$A\f$ is already stored as a matrix object in **Eigen**, +/// for example `Eigen::MatrixXd`, then there is an easy way to construct such +/// matrix operation class, by using the built-in wrapper class DenseSymMatProd +/// which wraps an existing matrix object in **Eigen**. This is also the +/// default template parameter for SymEigsSolver. For sparse matrices, the +/// wrapper class SparseSymMatProd can be used similarly. +/// +/// If the users need to define their own matrix-vector multiplication operation +/// class, it should implement all the public member functions as in DenseSymMatProd. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// \tparam SelectionRule An enumeration value indicating the selection rule of +/// the requested eigenvalues, for example `LARGEST_MAGN` +/// to retrieve eigenvalues with the largest magnitude. +/// The full list of enumeration values can be found in +/// \ref Enumerations. +/// \tparam OpType The name of the matrix operation class. Users could either +/// use the wrapper classes such as DenseSymMatProd and +/// SparseSymMatProd, or define their +/// own that implements all the public member functions as in +/// DenseSymMatProd. +/// +/// Below is an example that demonstrates the usage of this class. +/// +/// \code{.cpp} +/// #include +/// #include +/// // is implicitly included +/// #include +/// +/// using namespace Spectra; +/// +/// int main() +/// { +/// // We are going to calculate the eigenvalues of M +/// Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10); +/// Eigen::MatrixXd M = A + A.transpose(); +/// +/// // Construct matrix operation object using the wrapper class DenseSymMatProd +/// DenseSymMatProd op(M); +/// +/// // Construct eigen solver object, requesting the largest three eigenvalues +/// SymEigsSolver< double, LARGEST_ALGE, DenseSymMatProd > eigs(&op, 3, 6); +/// +/// // Initialize and compute +/// eigs.init(); +/// int nconv = eigs.compute(); +/// +/// // Retrieve results +/// Eigen::VectorXd evalues; +/// if(eigs.info() == SUCCESSFUL) +/// evalues = eigs.eigenvalues(); +/// +/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; +/// +/// return 0; +/// } +/// \endcode +/// +/// And here is an example for user-supplied matrix operation class. +/// +/// \code{.cpp} +/// #include +/// #include +/// #include +/// +/// using namespace Spectra; +/// +/// // M = diag(1, 2, ..., 10) +/// class MyDiagonalTen +/// { +/// public: +/// int rows() { return 10; } +/// int cols() { return 10; } +/// // y_out = M * x_in +/// void perform_op(double *x_in, double *y_out) +/// { +/// for(int i = 0; i < rows(); i++) +/// { +/// y_out[i] = x_in[i] * (i + 1); +/// } +/// } +/// }; +/// +/// int main() +/// { +/// MyDiagonalTen op; +/// SymEigsSolver eigs(&op, 3, 6); +/// eigs.init(); +/// eigs.compute(); +/// if(eigs.info() == SUCCESSFUL) +/// { +/// Eigen::VectorXd evalues = eigs.eigenvalues(); +/// // Will get (10, 9, 8) +/// std::cout << "Eigenvalues found:\n" << evalues << std::endl; +/// } +/// +/// return 0; +/// } +/// \endcode +/// +template < typename Scalar = double, + int SelectionRule = LARGEST_MAGN, + typename OpType = DenseSymMatProd > +class SymEigsSolver: public SymEigsBase +{ +private: + typedef Eigen::Index Index; + +public: + /// + /// Constructor to create a solver object. + /// + /// \param op Pointer to the matrix operation object, which should implement + /// the matrix-vector multiplication operation of \f$A\f$: + /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either + /// create the object from the wrapper class such as DenseSymMatProd, or + /// define their own that implements all the public member functions + /// as in DenseSymMatProd. + /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, + /// where \f$n\f$ is the size of matrix. + /// \param ncv Parameter that controls the convergence speed of the algorithm. + /// Typically a larger `ncv` means faster convergence, but it may + /// also result in greater memory use and more matrix operations + /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, + /// and is advised to take \f$ncv \ge 2\cdot nev\f$. + /// + SymEigsSolver(OpType* op, Index nev, Index ncv) : + SymEigsBase(op, NULL, nev, ncv) + {} + +}; + + +} // namespace Spectra + +#endif // SYM_EIGS_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/SymGEigsSolver.h b/src/external/Spectra/include/Spectra/SymGEigsSolver.h new file mode 100644 index 00000000..8e774284 --- /dev/null +++ b/src/external/Spectra/include/Spectra/SymGEigsSolver.h @@ -0,0 +1,334 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SYM_GEIGS_SOLVER_H +#define SYM_GEIGS_SOLVER_H + +#include "SymEigsBase.h" +#include "Util/GEigsMode.h" +#include "MatOp/internal/SymGEigsCholeskyOp.h" +#include "MatOp/internal/SymGEigsRegInvOp.h" + +namespace Spectra { + + +/// +/// \defgroup GEigenSolver Generalized Eigen Solvers +/// +/// Generalized eigen solvers for different types of problems. +/// + +/// +/// \ingroup GEigenSolver +/// +/// This class implements the generalized eigen solver for real symmetric +/// matrices, i.e., to solve \f$Ax=\lambda Bx\f$ where \f$A\f$ is symmetric and +/// \f$B\f$ is positive definite. +/// +/// There are two modes of this solver, specified by the template parameter +/// GEigsMode. See the pages for the specialized classes for details. +/// - The Cholesky mode assumes that \f$B\f$ can be factorized using Cholesky +/// decomposition, which is the preferred mode when the decomposition is +/// available. (This can be easily done in Eigen using the dense or sparse +/// Cholesky solver.) +/// See \ref SymGEigsSolver "SymGEigsSolver (Cholesky mode)" for more details. +/// - The regular inverse mode requires the matrix-vector product \f$Bv\f$ and the +/// linear equation solving operation \f$B^{-1}v\f$. This mode should only be +/// used when the Cholesky decomposition of \f$B\f$ is hard to implement, or +/// when computing \f$B^{-1}v\f$ is much faster than the Cholesky decomposition. +/// See \ref SymGEigsSolver "SymGEigsSolver (Regular inverse mode)" for more details. + +// Empty class template +template < typename Scalar, + int SelectionRule, + typename OpType, + typename BOpType, + int GEigsMode > +class SymGEigsSolver +{}; + + + +/// +/// \ingroup GEigenSolver +/// +/// This class implements the generalized eigen solver for real symmetric +/// matrices using Cholesky decomposition, i.e., to solve \f$Ax=\lambda Bx\f$ +/// where \f$A\f$ is symmetric and \f$B\f$ is positive definite with the Cholesky +/// decomposition \f$B=LL'\f$. +/// +/// This solver requires two matrix operation objects: one for \f$A\f$ that implements +/// the matrix multiplication \f$Av\f$, and one for \f$B\f$ that implements the lower +/// and upper triangular solving \f$L^{-1}v\f$ and \f$(L')^{-1}v\f$. +/// +/// If \f$A\f$ and \f$B\f$ are stored as Eigen matrices, then the first operation +/// can be created using the DenseSymMatProd or SparseSymMatProd classes, and +/// the second operation can be created using the DenseCholesky or SparseCholesky +/// classes. If the users need to define their own operation classes, then they +/// should implement all the public member functions as in those built-in classes. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// \tparam SelectionRule An enumeration value indicating the selection rule of +/// the requested eigenvalues, for example `LARGEST_MAGN` +/// to retrieve eigenvalues with the largest magnitude. +/// The full list of enumeration values can be found in +/// \ref Enumerations. +/// \tparam OpType The name of the matrix operation class for \f$A\f$. Users could either +/// use the wrapper classes such as DenseSymMatProd and +/// SparseSymMatProd, or define their +/// own that implements all the public member functions as in +/// DenseSymMatProd. +/// \tparam BOpType The name of the matrix operation class for \f$B\f$. Users could either +/// use the wrapper classes such as DenseCholesky and +/// SparseCholesky, or define their +/// own that implements all the public member functions as in +/// DenseCholesky. +/// \tparam GEigsMode Mode of the generalized eigen solver. In this solver +/// it is Spectra::GEIGS_CHOLESKY. +/// +/// Below is an example that demonstrates the usage of this class. +/// +/// \code{.cpp} +/// #include +/// #include +/// #include +/// #include +/// #include +/// #include +/// #include +/// +/// using namespace Spectra; +/// +/// int main() +/// { +/// // We are going to solve the generalized eigenvalue problem A * x = lambda * B * x +/// const int n = 100; +/// +/// // Define the A matrix +/// Eigen::MatrixXd M = Eigen::MatrixXd::Random(n, n); +/// Eigen::MatrixXd A = M + M.transpose(); +/// +/// // Define the B matrix, a band matrix with 2 on the diagonal and 1 on the subdiagonals +/// Eigen::SparseMatrix B(n, n); +/// B.reserve(Eigen::VectorXi::Constant(n, 3)); +/// for(int i = 0; i < n; i++) +/// { +/// B.insert(i, i) = 2.0; +/// if(i > 0) +/// B.insert(i - 1, i) = 1.0; +/// if(i < n - 1) +/// B.insert(i + 1, i) = 1.0; +/// } +/// +/// // Construct matrix operation object using the wrapper classes +/// DenseSymMatProd op(A); +/// SparseCholesky Bop(B); +/// +/// // Construct generalized eigen solver object, requesting the largest three generalized eigenvalues +/// SymGEigsSolver, SparseCholesky, GEIGS_CHOLESKY> +/// geigs(&op, &Bop, 3, 6); +/// +/// // Initialize and compute +/// geigs.init(); +/// int nconv = geigs.compute(); +/// +/// // Retrieve results +/// Eigen::VectorXd evalues; +/// Eigen::MatrixXd evecs; +/// if(geigs.info() == SUCCESSFUL) +/// { +/// evalues = geigs.eigenvalues(); +/// evecs = geigs.eigenvectors(); +/// } +/// +/// std::cout << "Generalized eigenvalues found:\n" << evalues << std::endl; +/// std::cout << "Generalized eigenvectors found:\n" << evecs.topRows(10) << std::endl; +/// +/// // Verify results using the generalized eigen solver in Eigen +/// Eigen::MatrixXd Bdense = B; +/// Eigen::GeneralizedSelfAdjointEigenSolver es(A, Bdense); +/// +/// std::cout << "Generalized eigenvalues:\n" << es.eigenvalues().tail(3) << std::endl; +/// std::cout << "Generalized eigenvectors:\n" << es.eigenvectors().rightCols(3).topRows(10) << std::endl; +/// +/// return 0; +/// } +/// \endcode + +// Partial specialization for GEigsMode = GEIGS_CHOLESKY +template < typename Scalar, + int SelectionRule, + typename OpType, + typename BOpType > +class SymGEigsSolver: + public SymEigsBase, IdentityBOp> +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + + BOpType* m_Bop; + +public: + /// + /// Constructor to create a solver object. + /// + /// \param op Pointer to the \f$A\f$ matrix operation object. It + /// should implement the matrix-vector multiplication operation of \f$A\f$: + /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either + /// create the object from the wrapper classes such as DenseSymMatProd, or + /// define their own that implements all the public member functions + /// as in DenseSymMatProd. + /// \param Bop Pointer to the \f$B\f$ matrix operation object. It + /// represents a Cholesky decomposition of \f$B\f$, and should + /// implement the lower and upper triangular solving operations: + /// calculating \f$L^{-1}v\f$ and \f$(L')^{-1}v\f$ for any vector + /// \f$v\f$, where \f$LL'=B\f$. Users could either + /// create the object from the wrapper classes such as DenseCholesky, or + /// define their own that implements all the public member functions + /// as in DenseCholesky. + /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, + /// where \f$n\f$ is the size of matrix. + /// \param ncv Parameter that controls the convergence speed of the algorithm. + /// Typically a larger `ncv` means faster convergence, but it may + /// also result in greater memory use and more matrix operations + /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, + /// and is advised to take \f$ncv \ge 2\cdot nev\f$. + /// + SymGEigsSolver(OpType* op, BOpType* Bop, Index nev, Index ncv) : + SymEigsBase, IdentityBOp>( + new SymGEigsCholeskyOp(*op, *Bop), NULL, nev, ncv + ), + m_Bop(Bop) + {} + + /// \cond + + ~SymGEigsSolver() + { + // m_op contains the constructed SymGEigsCholeskyOp object + delete this->m_op; + } + + Matrix eigenvectors(Index nvec) const + { + Matrix res = SymEigsBase, IdentityBOp>::eigenvectors(nvec); + Vector tmp(res.rows()); + const Index nconv = res.cols(); + for(Index i = 0; i < nconv; i++) + { + m_Bop->upper_triangular_solve(&res(0, i), tmp.data()); + res.col(i).noalias() = tmp; + } + + return res; + } + + Matrix eigenvectors() const + { + return SymGEigsSolver::eigenvectors(this->m_nev); + } + + /// \endcond +}; + + + +/// +/// \ingroup GEigenSolver +/// +/// This class implements the generalized eigen solver for real symmetric +/// matrices in the regular inverse mode, i.e., to solve \f$Ax=\lambda Bx\f$ +/// where \f$A\f$ is symmetric, and \f$B\f$ is positive definite with the operations +/// defined below. +/// +/// This solver requires two matrix operation objects: one for \f$A\f$ that implements +/// the matrix multiplication \f$Av\f$, and one for \f$B\f$ that implements the +/// matrix-vector product \f$Bv\f$ and the linear equation solving operation \f$B^{-1}v\f$. +/// +/// If \f$A\f$ and \f$B\f$ are stored as Eigen matrices, then the first operation +/// can be created using the DenseSymMatProd or SparseSymMatProd classes, and +/// the second operation can be created using the SparseRegularInverse class. There is no +/// wrapper class for a dense \f$B\f$ matrix since in this case the Cholesky mode +/// is always preferred. If the users need to define their own operation classes, then they +/// should implement all the public member functions as in those built-in classes. +/// +/// \tparam Scalar The element type of the matrix. +/// Currently supported types are `float`, `double` and `long double`. +/// \tparam SelectionRule An enumeration value indicating the selection rule of +/// the requested eigenvalues, for example `LARGEST_MAGN` +/// to retrieve eigenvalues with the largest magnitude. +/// The full list of enumeration values can be found in +/// \ref Enumerations. +/// \tparam OpType The name of the matrix operation class for \f$A\f$. Users could either +/// use the wrapper classes such as DenseSymMatProd and +/// SparseSymMatProd, or define their +/// own that implements all the public member functions as in +/// DenseSymMatProd. +/// \tparam BOpType The name of the matrix operation class for \f$B\f$. Users could either +/// use the wrapper class SparseRegularInverse, or define their +/// own that implements all the public member functions as in +/// SparseRegularInverse. +/// \tparam GEigsMode Mode of the generalized eigen solver. In this solver +/// it is Spectra::GEIGS_REGULAR_INVERSE. +/// + +// Partial specialization for GEigsMode = GEIGS_REGULAR_INVERSE +template < typename Scalar, + int SelectionRule, + typename OpType, + typename BOpType > +class SymGEigsSolver: + public SymEigsBase, BOpType> +{ +private: + typedef Eigen::Index Index; + +public: + /// + /// Constructor to create a solver object. + /// + /// \param op Pointer to the \f$A\f$ matrix operation object. It + /// should implement the matrix-vector multiplication operation of \f$A\f$: + /// calculating \f$Av\f$ for any vector \f$v\f$. Users could either + /// create the object from the wrapper classes such as DenseSymMatProd, or + /// define their own that implements all the public member functions + /// as in DenseSymMatProd. + /// \param Bop Pointer to the \f$B\f$ matrix operation object. It should + /// implement the multiplication operation \f$Bv\f$ and the linear equation + /// solving operation \f$B^{-1}v\f$ for any vector \f$v\f$. Users could either + /// create the object from the wrapper class SparseRegularInverse, or + /// define their own that implements all the public member functions + /// as in SparseRegularInverse. + /// \param nev Number of eigenvalues requested. This should satisfy \f$1\le nev \le n-1\f$, + /// where \f$n\f$ is the size of matrix. + /// \param ncv Parameter that controls the convergence speed of the algorithm. + /// Typically a larger `ncv` means faster convergence, but it may + /// also result in greater memory use and more matrix operations + /// in each iteration. This parameter must satisfy \f$nev < ncv \le n\f$, + /// and is advised to take \f$ncv \ge 2\cdot nev\f$. + /// + SymGEigsSolver(OpType* op, BOpType* Bop, Index nev, Index ncv) : + SymEigsBase, BOpType>( + new SymGEigsRegInvOp(*op, *Bop), Bop, nev, ncv + ) + {} + + /// \cond + ~SymGEigsSolver() + { + // m_op contains the constructed SymGEigsRegInvOp object + delete this->m_op; + } + /// \endcond +}; + + +} // namespace Spectra + +#endif // SYM_GEIGS_SOLVER_H diff --git a/src/external/Spectra/include/Spectra/Util/CompInfo.h b/src/external/Spectra/include/Spectra/Util/CompInfo.h new file mode 100644 index 00000000..b8e639d6 --- /dev/null +++ b/src/external/Spectra/include/Spectra/Util/CompInfo.h @@ -0,0 +1,37 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef COMP_INFO_H +#define COMP_INFO_H + +namespace Spectra { + + +/// +/// \ingroup Enumerations +/// +/// The enumeration to report the status of computation. +/// +enum COMPUTATION_INFO +{ + SUCCESSFUL = 0, ///< Computation was successful. + + NOT_COMPUTED, ///< Used in eigen solvers, indicating that computation + ///< has not been conducted. Users should call + ///< the `compute()` member function of solvers. + + NOT_CONVERGING, ///< Used in eigen solvers, indicating that some eigenvalues + ///< did not converge. The `compute()` + ///< function returns the number of converged eigenvalues. + + NUMERICAL_ISSUE ///< Used in Cholesky decomposition, indicating that the + ///< matrix is not positive definite. +}; + + +} // namespace Spectra + +#endif // COMP_INFO_H diff --git a/src/external/Spectra/include/Spectra/Util/GEigsMode.h b/src/external/Spectra/include/Spectra/Util/GEigsMode.h new file mode 100644 index 00000000..d03f269d --- /dev/null +++ b/src/external/Spectra/include/Spectra/Util/GEigsMode.h @@ -0,0 +1,34 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef GEIGS_MODE_H +#define GEIGS_MODE_H + +namespace Spectra { + + +/// +/// \ingroup Enumerations +/// +/// The enumeration to specify the mode of generalized eigenvalue solver. +/// +enum GEIGS_MODE +{ + GEIGS_CHOLESKY = 0, ///< Using Cholesky decomposition to solve generalized eigenvalues. + + GEIGS_REGULAR_INVERSE, ///< Regular inverse mode for generalized eigenvalue solver. + + GEIGS_SHIFT_INVERT, ///< Shift-and-invert mode for generalized eigenvalue solver. + + GEIGS_BUCKLING, ///< Buckling mode for generalized eigenvalue solver. + + GEIGS_CAYLEY ///< Cayley transformation mode for generalized eigenvalue solver. +}; + + +} // namespace Spectra + +#endif // GEIGS_MODE_H diff --git a/src/external/Spectra/include/Spectra/Util/SelectionRule.h b/src/external/Spectra/include/Spectra/Util/SelectionRule.h new file mode 100644 index 00000000..19f71dcf --- /dev/null +++ b/src/external/Spectra/include/Spectra/Util/SelectionRule.h @@ -0,0 +1,277 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SELECTION_RULE_H +#define SELECTION_RULE_H + +#include // std::vector +#include // std::abs +#include // std::sort +#include // std::complex +#include // std::pair +#include // std::invalid_argument + +namespace Spectra { + + +/// +/// \defgroup Enumerations +/// +/// Enumeration types for the selection rule of eigenvalues. +/// + +/// +/// \ingroup Enumerations +/// +/// The enumeration of selection rules of desired eigenvalues. +/// +enum SELECT_EIGENVALUE +{ + LARGEST_MAGN = 0, ///< Select eigenvalues with largest magnitude. Magnitude + ///< means the absolute value for real numbers and norm for + ///< complex numbers. Applies to both symmetric and general + ///< eigen solvers. + + LARGEST_REAL, ///< Select eigenvalues with largest real part. Only for general eigen solvers. + + LARGEST_IMAG, ///< Select eigenvalues with largest imaginary part (in magnitude). Only for general eigen solvers. + + LARGEST_ALGE, ///< Select eigenvalues with largest algebraic value, considering + ///< any negative sign. Only for symmetric eigen solvers. + + SMALLEST_MAGN, ///< Select eigenvalues with smallest magnitude. Applies to both symmetric and general + ///< eigen solvers. + + SMALLEST_REAL, ///< Select eigenvalues with smallest real part. Only for general eigen solvers. + + SMALLEST_IMAG, ///< Select eigenvalues with smallest imaginary part (in magnitude). Only for general eigen solvers. + + SMALLEST_ALGE, ///< Select eigenvalues with smallest algebraic value. Only for symmetric eigen solvers. + + BOTH_ENDS ///< Select eigenvalues half from each end of the spectrum. When + ///< `nev` is odd, compute more from the high end. Only for symmetric eigen solvers. +}; + +/// +/// \ingroup Enumerations +/// +/// The enumeration of selection rules of desired eigenvalues. Alias for `SELECT_EIGENVALUE`. +/// +enum SELECT_EIGENVALUE_ALIAS +{ + WHICH_LM = 0, ///< Alias for `LARGEST_MAGN` + WHICH_LR, ///< Alias for `LARGEST_REAL` + WHICH_LI, ///< Alias for `LARGEST_IMAG` + WHICH_LA, ///< Alias for `LARGEST_ALGE` + WHICH_SM, ///< Alias for `SMALLEST_MAGN` + WHICH_SR, ///< Alias for `SMALLEST_REAL` + WHICH_SI, ///< Alias for `SMALLEST_IMAG` + WHICH_SA, ///< Alias for `SMALLEST_ALGE` + WHICH_BE ///< Alias for `BOTH_ENDS` +}; + +/// \cond + +// Get the element type of a "scalar" +// ElemType => double +// ElemType< std::complex > => double +template +class ElemType +{ +public: + typedef T type; +}; + +template +class ElemType< std::complex > +{ +public: + typedef T type; +}; + +// When comparing eigenvalues, we first calculate the "target" +// to sort. For example, if we want to choose the eigenvalues with +// largest magnitude, the target will be -abs(x). +// The minus sign is due to the fact that std::sort() sorts in ascending order. + +// Default target: throw an exception +template +class SortingTarget +{ +public: + static typename ElemType::type get(const Scalar& val) + { + using std::abs; + throw std::invalid_argument("incompatible selection rule"); + return -abs(val); + } +}; + +// Specialization for LARGEST_MAGN +// This covers [float, double, complex] x [LARGEST_MAGN] +template +class SortingTarget +{ +public: + static typename ElemType::type get(const Scalar& val) + { + using std::abs; + return -abs(val); + } +}; + +// Specialization for LARGEST_REAL +// This covers [complex] x [LARGEST_REAL] +template +class SortingTarget, LARGEST_REAL> +{ +public: + static RealType get(const std::complex& val) + { + return -val.real(); + } +}; + +// Specialization for LARGEST_IMAG +// This covers [complex] x [LARGEST_IMAG] +template +class SortingTarget, LARGEST_IMAG> +{ +public: + static RealType get(const std::complex& val) + { + using std::abs; + return -abs(val.imag()); + } +}; + +// Specialization for LARGEST_ALGE +// This covers [float, double] x [LARGEST_ALGE] +template +class SortingTarget +{ +public: + static Scalar get(const Scalar& val) + { + return -val; + } +}; + +// Here BOTH_ENDS is the same as LARGEST_ALGE, but +// we need some additional steps, which are done in +// SymEigsSolver.h => retrieve_ritzpair(). +// There we move the smallest values to the proper locations. +template +class SortingTarget +{ +public: + static Scalar get(const Scalar& val) + { + return -val; + } +}; + +// Specialization for SMALLEST_MAGN +// This covers [float, double, complex] x [SMALLEST_MAGN] +template +class SortingTarget +{ +public: + static typename ElemType::type get(const Scalar& val) + { + using std::abs; + return abs(val); + } +}; + +// Specialization for SMALLEST_REAL +// This covers [complex] x [SMALLEST_REAL] +template +class SortingTarget, SMALLEST_REAL> +{ +public: + static RealType get(const std::complex& val) + { + return val.real(); + } +}; + +// Specialization for SMALLEST_IMAG +// This covers [complex] x [SMALLEST_IMAG] +template +class SortingTarget, SMALLEST_IMAG> +{ +public: + static RealType get(const std::complex& val) + { + using std::abs; + return abs(val.imag()); + } +}; + +// Specialization for SMALLEST_ALGE +// This covers [float, double] x [SMALLEST_ALGE] +template +class SortingTarget +{ +public: + static Scalar get(const Scalar& val) + { + return val; + } +}; + +// Sort eigenvalues and return the order index +template +class PairComparator +{ +public: + bool operator() (const PairType& v1, const PairType& v2) + { + return v1.first < v2.first; + } +}; + +template +class SortEigenvalue +{ +private: + typedef typename ElemType::type TargetType; // Type of the sorting target, will be + // a floating number type, e.g. "double" + typedef std::pair PairType; // Type of the sorting pair, including + // the sorting target and the index + + std::vector pair_sort; + +public: + SortEigenvalue(const T* start, int size) : + pair_sort(size) + { + for(int i = 0; i < size; i++) + { + pair_sort[i].first = SortingTarget::get(start[i]); + pair_sort[i].second = i; + } + PairComparator comp; + std::sort(pair_sort.begin(), pair_sort.end(), comp); + } + + std::vector index() + { + std::vector ind(pair_sort.size()); + for(unsigned int i = 0; i < ind.size(); i++) + ind[i] = pair_sort[i].second; + + return ind; + } +}; + +/// \endcond + + +} // namespace Spectra + +#endif // SELECTION_RULE_H diff --git a/src/external/Spectra/include/Spectra/Util/SimpleRandom.h b/src/external/Spectra/include/Spectra/Util/SimpleRandom.h new file mode 100644 index 00000000..7b1e6162 --- /dev/null +++ b/src/external/Spectra/include/Spectra/Util/SimpleRandom.h @@ -0,0 +1,93 @@ +// Copyright (C) 2016-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef SIMPLE_RANDOM_H +#define SIMPLE_RANDOM_H + +#include + +/// \cond + +namespace Spectra { + + +// We need a simple pseudo random number generator here: +// 1. It is used to generate initial and restarted residual vector. +// 2. It is not necessary to be so "random" and advanced. All we hope +// is that the residual vector is not in the space spanned by the +// current Krylov space. This should be met almost surely. +// 3. We don't want to call RNG in C++, since we actually want the +// algorithm to be deterministic. Also, calling RNG in C/C++ is not +// allowed in R packages submitted to CRAN. +// 4. The method should be as simple as possible, so an LCG is enough. +// 5. Based on public domain code by Ray Gardner +// http://stjarnhimlen.se/snippets/rg_rand.c + + +template +class SimpleRandom +{ +private: + typedef Eigen::Index Index; + typedef Eigen::Matrix Vector; + + const unsigned int m_a; // multiplier + const unsigned long m_max; // 2^31 - 1 + long m_rand; + + inline long next_long_rand(long seed) + { + unsigned long lo, hi; + + lo = m_a * (long)(seed & 0xFFFF); + hi = m_a * (long)((unsigned long)seed >> 16); + lo += (hi & 0x7FFF) << 16; + if(lo > m_max) + { + lo &= m_max; + ++lo; + } + lo += hi >> 15; + if(lo > m_max) + { + lo &= m_max; + ++lo; + } + return (long)lo; + } +public: + SimpleRandom(unsigned long init_seed) : + m_a(16807), + m_max(2147483647L), + m_rand(init_seed ? (init_seed & m_max) : 1) + {} + + Scalar random() + { + m_rand = next_long_rand(m_rand); + return Scalar(m_rand) / Scalar(m_max) - Scalar(0.5); + } + + // Vector of random numbers of type Scalar + // Ranging from -0.5 to 0.5 + Vector random_vec(const Index len) + { + Vector res(len); + for(Index i = 0; i < len; i++) + { + m_rand = next_long_rand(m_rand); + res[i] = Scalar(m_rand) / Scalar(m_max) - Scalar(0.5); + } + return res; + } +}; + + +} // namespace Spectra + +/// \endcond + +#endif // SIMPLE_RANDOM_H diff --git a/src/external/Spectra/include/Spectra/Util/TypeTraits.h b/src/external/Spectra/include/Spectra/Util/TypeTraits.h new file mode 100644 index 00000000..a4cc05b2 --- /dev/null +++ b/src/external/Spectra/include/Spectra/Util/TypeTraits.h @@ -0,0 +1,73 @@ +// Copyright (C) 2018-2019 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef TYPE_TRAITS_H +#define TYPE_TRAITS_H + +#include +#include + +/// \cond + +namespace Spectra { + + +// For a real value type "Scalar", we want to know its smallest +// positive value, i.e., std::numeric_limits::min(). +// However, we must take non-standard value types into account, +// so we rely on Eigen::NumTraits. +// +// Eigen::NumTraits has defined epsilon() and lowest(), but +// lowest() means negative highest(), which is a very small +// negative value. +// +// Therefore, we manually define this limit, and use eplison()^3 +// to mimic it for non-standard types. + +// Generic definition +template +struct TypeTraits +{ + static inline Scalar min() + { + return Eigen::numext::pow(Eigen::NumTraits::epsilon(), Scalar(3)); + } +}; + +// Full specialization +template <> +struct TypeTraits +{ + static inline float min() + { + return std::numeric_limits::min(); + } +}; + +template <> +struct TypeTraits +{ + static inline double min() + { + return std::numeric_limits::min(); + } +}; + +template <> +struct TypeTraits +{ + static inline long double min() + { + return std::numeric_limits::min(); + } +}; + + +} // namespace Spectra + +/// \endcond + +#endif // TYPE_TRAITS_H diff --git a/src/external/Spectra/include/Spectra/contrib/LOBPCGSolver.h b/src/external/Spectra/include/Spectra/contrib/LOBPCGSolver.h new file mode 100644 index 00000000..5ca001f6 --- /dev/null +++ b/src/external/Spectra/include/Spectra/contrib/LOBPCGSolver.h @@ -0,0 +1,501 @@ +// Written by Anna Araslanova +// Modified by Yixuan Qiu +// License: MIT + +#ifndef LOBPCG_SOLVER +#define LOBPCG_SOLVER + +#include +#include + +#include +#include +#include +#include +#include + +#include "../SymGEigsSolver.h" + + +namespace Spectra { + + /// + /// \ingroup EigenSolver + /// + + /// *** METHOD + /// The class represent the LOBPCG algorithm, which was invented by Andrew Knyazev + /// Theoretical background of the procedure can be found in the articles below + /// - Knyazev, A.V., 2001. Toward the optimal preconditioned eigensolver : Locally optimal block preconditioned conjugate gradient method.SIAM journal on scientific computing, 23(2), pp.517 - 541. + /// - Knyazev, A.V., Argentati, M.E., Lashuk, I. and Ovtchinnikov, E.E., 2007. Block locally optimal preconditioned eigenvalue xolvers(BLOPEX) in HYPRE and PETSc.SIAM Journal on Scientific Computing, 29(5), pp.2224 - 2239. + /// + /// *** CONDITIONS OF USE + /// Locally Optimal Block Preconditioned Conjugate Gradient(LOBPCG) is a method for finding the M smallest eigenvalues + /// and eigenvectors of a large symmetric positive definite generalized eigenvalue problem + /// \f$Ax=\lambda Bx,\f$ + /// where \f$A_{NxN}\f$ is a symmetric matrix, \f$B\f$ is symmetric and positive - definite. \f$A and B\f$ are also assumed large and sparse + /// \f$\textit{M}\f$ should be \f$\<< textit{N}\f$ (at least \f$\textit{5M} < \textit{N} \f$) + /// + /// *** ARGUMENTS + /// Eigen::SparseMatrix A; // N*N - Ax = lambda*Bx, lrage and sparse + /// Eigen::SparseMatrix X; // N*M - initial approximations to eigenvectors (random in general case) + /// Spectra::LOBPCGSolver solver(A, X); + /// *Eigen::SparseMatrix B; // N*N - Ax = lambda*Bx, sparse, positive definite + /// solver.setConstraints(B); + /// *Eigen::SparseMatrix Y; // N*K - constraints, already found eigenvectors + /// solver.setB(B); + /// *Eigen::SparseMatrix T; // N*N - preconditioner ~ A^-1 + /// solver.setPreconditioner(T); + /// + /// *** OUTCOMES + /// solver.solve(); // compute eigenpairs // void + /// solver.info(); // state of converjance // int + /// solver.residuals(); // get residuals to evaluate biases // Eigen::Matrix + /// solver.eigenvalues(); // get eigenvalues // Eigen::Matrix + /// solver.eigenvectors(); // get eigenvectors // Eigen::Matrix + /// + /// *** EXAMPLE + /// \code{.cpp} + /// #include + /// + /// // random A + /// Matrix a; + /// a = (Matrix::Random(10, 10).array() > 0.6).cast() * Matrix::Random(10, 10).array() * 5; + /// a = Matrix((a).triangularView()) + Matrix((a).triangularView()).transpose(); + /// for (int i = 0; i < 10; i++) + /// a(i, i) = i + 0.5; + /// std::cout << a << "\n"; + /// Eigen::SparseMatrix A(a.sparseView()); + /// // random X + /// Eigen::Matrix x; + /// x = Matrix::Random(10, 2).array(); + /// Eigen::SparseMatrix X(x.sparseView()); + /// // solve Ax = lambda*x + /// Spectra::LOBPCGSolver solver(A, X); + /// solver.compute(10, 1e-4); // 10 iterations, L2_tolerance = 1e-4*N + /// std::cout << "info\n" << solver.info() << std::endl; + /// std::cout << "eigenvalues\n" << solver.eigenvalues() << std::endl; + /// std::cout << "eigenvectors\n" << solver.eigenvectors() << std::endl; + /// std::cout << "residuals\n" << solver.residuals() << std::endl; + /// \endcode + /// + + template < typename Scalar = long double> + class LOBPCGSolver { + private: + + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + + typedef std::complex Complex; + typedef Eigen::Matrix ComplexMatrix; + typedef Eigen::Matrix ComplexVector; + + typedef Eigen::SparseMatrix SparseMatrix; + typedef Eigen::SparseMatrix SparseComplexMatrix; + + const int m_n; // dimension of matrix A + const int m_nev; // number of eigenvalues requested + SparseMatrix A, X; + SparseMatrix m_Y, m_B, m_preconditioner; + bool flag_with_constraints, flag_with_B, flag_with_preconditioner; + + public: + SparseMatrix m_residuals; + Matrix m_evectors; + Vector m_evalues; + int m_info; + + private: + + // B-orthonormalize matrix M + int orthogonalizeInPlace(SparseMatrix &M, SparseMatrix &B, \ + SparseMatrix &true_BM, bool has_true_BM = false) { + + SparseMatrix BM; + + if (has_true_BM == false) { + if (flag_with_B) { BM = B * M; } + else { BM = M; } + } + else { + BM = true_BM; + } + + Eigen::SimplicialLDLT chol_MBM(M.transpose() * BM); + + if (chol_MBM.info() != SUCCESSFUL) { + // LDLT decomposition fail + m_info = chol_MBM.info(); + return chol_MBM.info(); + } + + SparseComplexMatrix Upper_MBM = chol_MBM.matrixU().template cast(); + ComplexVector D_MBM_vec = chol_MBM.vectorD().template cast(); + + D_MBM_vec = D_MBM_vec.cwiseSqrt(); + + for (int i = 0; i < D_MBM_vec.rows(); i++) { + D_MBM_vec(i) = Complex(1.0, 0.0) / D_MBM_vec(i); + } + + SparseComplexMatrix D_MBM_mat(D_MBM_vec.asDiagonal()); + + SparseComplexMatrix U_inv(Upper_MBM.rows(), Upper_MBM.cols()); + U_inv.setIdentity(); + Upper_MBM.template triangularView().solveInPlace(U_inv); + + SparseComplexMatrix right_product = U_inv * D_MBM_mat; + M = M*right_product.real(); + if (flag_with_B) { true_BM = B * M; } + else { true_BM = M; } + + return SUCCESSFUL; + } + + void applyConstraintsInPlace(SparseMatrix &X, SparseMatrix&Y, \ + SparseMatrix&B) { + SparseMatrix BY; + if (flag_with_B) { BY = B * Y; } + else { BY = Y; } + + SparseMatrix YBY = Y.transpose() * BY; + SparseMatrix BYX = BY.transpose() * X; + + SparseMatrix YBY_XYX = (Matrix(YBY).bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(Matrix(BYX))).sparseView(); + X = X - Y * YBY_XYX; + } + + /* + return + 'AB + CD' + */ + Matrix stack_4_matricies(Matrix A, Matrix B, \ + Matrix C, Matrix D) { + Matrix result(A.rows() + C.rows(), A.cols() + B.cols()); + result.topLeftCorner(A.rows(), A.cols()) = A; + result.topRightCorner(B.rows(), B.cols()) = B; + result.bottomLeftCorner(C.rows(), C.cols()) = C; + result.bottomRightCorner(D.rows(), D.cols()) = D; + return result; + } + + Matrix stack_9_matricies(Matrix A, Matrix B, Matrix C, \ + Matrix D, Matrix E, Matrix F, \ + Matrix G, Matrix H, Matrix I) { + + Matrix result(A.rows() + D.rows() + G.rows(), A.cols() + B.cols() + C.cols()); + result.block(0, 0, A.rows(), A.cols()) = A; + result.block(0, A.cols(), B.rows(), B.cols()) = B; + result.block(0, A.cols() + B.cols(), C.rows(), C.cols()) = C; + result.block(A.rows(), 0, D.rows(), D.cols()) = D; + result.block(A.rows(), A.cols(), E.rows(), E.cols()) = E; + result.block(A.rows(), A.cols() + B.cols(), F.rows(), F.cols()) = F; + result.block(A.rows() + D.rows(), 0, G.rows(), G.cols()) = G; + result.block(A.rows() + D.rows(), A.cols(), H.rows(), H.cols()) = H; + result.block(A.rows() + D.rows(), A.cols() + B.cols(), I.rows(), I.cols()) = I; + + return result; + } + + void sort_epairs(Vector &evalues, Matrix &evectors, int SelectionRule) { + + std::function cmp; + if (SelectionRule == SMALLEST_ALGE) + cmp = std::less{}; + else + cmp = std::greater{}; + + std::map epairs(cmp); + for (int i = 0; i < m_evectors.cols(); ++i) + epairs.insert(std::make_pair(evalues(i), evectors.col(i))); + + int i = 0; + for (auto& epair : epairs) { + evectors.col(i) = epair.second; + evalues(i) = epair.first; + i++; + } + } + + void removeColumns(SparseMatrix& matrix, std::vector& colToRemove) + { + // remove columns through matrix multiplication + SparseMatrix new_matrix(matrix.cols(), matrix.cols() - int(colToRemove.size())); + int iCol = 0; + std::vector> tripletList; + tripletList.reserve(matrix.cols() - int(colToRemove.size())); + + for (int iRow = 0; iRow < matrix.cols(); iRow++) { + if (std::find(colToRemove.begin(), colToRemove.end(), iRow) == colToRemove.end()) { + tripletList.push_back(Eigen::Triplet(iRow, iCol, 1)); + iCol++; + } + } + + new_matrix.setFromTriplets(tripletList.begin(), tripletList.end()); + matrix = matrix * new_matrix; + } + + int checkConvergence_getBlocksize(SparseMatrix & m_residuals, Scalar tolerance_L2, std::vector & columnsToDelete) { + // square roots from sum of squares by column + int BlockSize = m_nev; + Scalar sum, buffer; + + for (int iCol = 0; iCol < m_nev; iCol++) { + sum = 0; + for (int iRow = 0; iRow < m_n; iRow++) { + buffer = m_residuals.coeff(iRow, iCol); + sum += buffer * buffer; + } + + if (sqrt(sum) < tolerance_L2) { + BlockSize--; + columnsToDelete.push_back(iCol); + } + } + return BlockSize; + } + + + public: + + LOBPCGSolver(const SparseMatrix& A, const SparseMatrix X) : + m_n(A.rows()), + m_nev(X.cols()), + m_info(NOT_COMPUTED), + flag_with_constraints(false), + flag_with_B(false), + flag_with_preconditioner(false), + A(A), + X(X) + { + if (A.rows() != X.rows() || A.rows() != A.cols()) + throw std::invalid_argument("Wrong size"); + + //if (m_n < 5* m_nev) + // throw std::invalid_argument("The problem size is small compared to the block size. Use standard eigensolver"); + } + + void setConstraints(const SparseMatrix& Y) { + m_Y = Y; + flag_with_constraints = true; + } + + void setB(const SparseMatrix& B) { + if (B.rows() != A.rows() || B.cols() != A.cols()) + throw std::invalid_argument("Wrong size"); + m_B = B; + flag_with_B = true; + } + + void setPreconditioner(const SparseMatrix& preconditioner) { + m_preconditioner = preconditioner; + flag_with_preconditioner = true; + } + + void compute(int maxit = 10, Scalar tol_div_n = 1e-7) { + + Scalar tolerance_L2 = tol_div_n * m_n; + int BlockSize; + int max_iter = std::min(m_n, maxit); + + SparseMatrix directions, AX, AR, BX, AD, ADD, DD, BDD, BD, XAD, RAD, DAD, XBD, RBD, BR, sparse_eVecX, sparse_eVecR, sparse_eVecD, inverse_matrix; + Matrix XAR, RAR, XBR, gramA, gramB, eVecX, eVecR, eVecD; + std::vector columnsToDelete; + + if (flag_with_constraints) { + // Apply the constraints Y to X + applyConstraintsInPlace(X, m_Y, m_B); + } + + // Make initial vectors orthonormal + // implicit BX declaration + if (orthogonalizeInPlace(X, m_B, BX) != SUCCESSFUL) { + max_iter = 0; + } + + AX = A * X; + // Solve the following NxN eigenvalue problem for all N eigenvalues and -vectors: + // first approximation via a dense problem + Eigen::EigenSolver eigs(Matrix(X.transpose() * AX)); + + if (eigs.info() != SUCCESSFUL) { + m_info = eigs.info(); + max_iter = 0; + } + else { + m_evalues = eigs.eigenvalues().real(); + m_evectors = eigs.eigenvectors().real(); + sort_epairs(m_evalues, m_evectors, SMALLEST_ALGE); + sparse_eVecX = m_evectors.sparseView(); + + X = X * sparse_eVecX; + AX = AX * sparse_eVecX; + BX = BX * sparse_eVecX; + } + + + for (int iter_num = 0; iter_num < max_iter; iter_num++) { + m_residuals.resize(m_n, m_nev); + for (int i = 0; i < m_nev; i++) { + m_residuals.col(i) = AX.col(i) - m_evalues(i) * BX.col(i); + } + BlockSize = checkConvergence_getBlocksize(m_residuals, tolerance_L2, columnsToDelete); + + if (BlockSize == 0) { + m_info = SUCCESSFUL; + break; + } + + // substitution of the original active mask + if (columnsToDelete.size() > 0) { + removeColumns(m_residuals, columnsToDelete); + if (iter_num > 0) { + removeColumns(directions, columnsToDelete); + removeColumns(AD, columnsToDelete); + removeColumns(BD, columnsToDelete); + } + columnsToDelete.clear(); // for next iteration + } + + if (flag_with_preconditioner) { + // Apply the preconditioner to the residuals + m_residuals = m_preconditioner * m_residuals; + } + + if (flag_with_constraints) { + // Apply the constraints Y to residuals + applyConstraintsInPlace(m_residuals, m_Y, m_B); + } + + if (orthogonalizeInPlace(m_residuals, m_B, BR) != SUCCESSFUL) { + break; + } + AR = A * m_residuals; + + // Orthonormalize conjugate directions + if (iter_num > 0) { + if (orthogonalizeInPlace(directions, m_B, BD, true) != SUCCESSFUL) { + break; + } + AD = A * directions; + } + + // Perform the Rayleigh Ritz Procedure + XAR = Matrix(X.transpose() * AR); + RAR = Matrix(m_residuals.transpose() * AR); + XBR = Matrix(X.transpose() * BR); + + if (iter_num > 0) { + + XAD = X.transpose() * AD; + RAD = m_residuals.transpose() * AD; + DAD = directions.transpose() * AD; + XBD = X.transpose() * BD; + RBD = m_residuals.transpose() * BD; + + gramA = stack_9_matricies(m_evalues.asDiagonal(), XAR, XAD, XAR.transpose(), RAR, RAD, XAD.transpose(), RAD.transpose(), DAD.transpose()); + gramB = stack_9_matricies(Matrix::Identity(m_nev, m_nev), XBR, XBD, XBR.transpose(), Matrix::Identity(BlockSize, BlockSize), RBD, XBD.transpose(), RBD.transpose(), Matrix::Identity(BlockSize, BlockSize)); + + } + else { + gramA = stack_4_matricies(m_evalues.asDiagonal(), XAR, XAR.transpose(), RAR); + gramB = stack_4_matricies(Matrix::Identity(m_nev, m_nev), XBR, XBR.transpose(), Matrix::Identity(BlockSize, BlockSize)); + } + + //calculate the lowest/largest m eigenpairs; Solve the generalized eigenvalue problem. + DenseSymMatProd Aop(gramA); + DenseCholesky Bop(gramB); + + SymGEigsSolver, \ + DenseCholesky, GEIGS_CHOLESKY> geigs(&Aop, &Bop, m_nev, std::min(10, int(gramA.rows()) - 1)); + + geigs.init(); + int nconv = geigs.compute(); + + //Mat evecs; + if (geigs.info() == SUCCESSFUL) { + m_evalues = geigs.eigenvalues(); + m_evectors = geigs.eigenvectors(); + sort_epairs(m_evalues, m_evectors, SMALLEST_ALGE); + } + else { + // Problem With General EgenVec + m_info = geigs.info(); + break; + } + + // Compute Ritz vectors + if (iter_num > 0) { + eVecX = m_evectors.block(0, 0, m_nev, m_nev); + eVecR = m_evectors.block(m_nev, 0, BlockSize, m_nev); + eVecD = m_evectors.block(m_nev + BlockSize, 0, BlockSize, m_nev); + + sparse_eVecX = eVecX.sparseView(); + sparse_eVecR = eVecR.sparseView(); + sparse_eVecD = eVecD.sparseView(); + + DD = m_residuals * sparse_eVecR; // new conjugate directions + ADD = AR * sparse_eVecR; + BDD = BR * sparse_eVecR; + + DD = DD + directions * sparse_eVecD; + ADD = ADD + AD * sparse_eVecD; + BDD = BDD + BD * sparse_eVecD; + } + else { + eVecX = m_evectors.block(0, 0, m_nev, m_nev); + eVecR = m_evectors.block(m_nev, 0, BlockSize, m_nev); + + sparse_eVecX = eVecX.sparseView(); + sparse_eVecR = eVecR.sparseView(); + + DD = m_residuals * sparse_eVecR; + ADD = AR * sparse_eVecR; + BDD = BR * sparse_eVecR; + } + + X = X * sparse_eVecX + DD; + AX = AX * sparse_eVecX + ADD; + BX = BX * sparse_eVecX + BDD; + + directions = DD; + AD = ADD; + BD = BDD; + + } // iteration loop + + // calculate last residuals + m_residuals.resize(m_n, m_nev); + for (int i = 0; i < m_nev; i++) { + m_residuals.col(i) = AX.col(i) - m_evalues(i) * BX.col(i); + } + BlockSize = checkConvergence_getBlocksize(m_residuals, tolerance_L2, columnsToDelete); + + if (BlockSize == 0) { + m_info = SUCCESSFUL; + } + } // compute + + Vector eigenvalues() { + return m_evalues; + } + + Matrix eigenvectors() { + return m_evectors; + } + + Matrix residuals() { + return Matrix(m_residuals); + } + + int info() { return m_info; } + + }; + + +} // namespace Spectra + +#endif // LOBPCG_SOLVER diff --git a/src/external/Spectra/include/Spectra/contrib/PartialSVDSolver.h b/src/external/Spectra/include/Spectra/contrib/PartialSVDSolver.h new file mode 100644 index 00000000..dad5b400 --- /dev/null +++ b/src/external/Spectra/include/Spectra/contrib/PartialSVDSolver.h @@ -0,0 +1,203 @@ +// Copyright (C) 2018 Yixuan Qiu +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef PARTIAL_SVD_SOLVER_H +#define PARTIAL_SVD_SOLVER_H + +#include +#include "../SymEigsSolver.h" + + +namespace Spectra { + + +// Abstract class for matrix operation +template +class SVDMatOp +{ +public: + virtual int rows() const = 0; + virtual int cols() const = 0; + + // y_out = A' * A * x_in or y_out = A * A' * x_in + virtual void perform_op(const Scalar* x_in, Scalar* y_out) = 0; + + virtual ~SVDMatOp() {} +}; + +// Operation of a tall matrix in SVD +// We compute the eigenvalues of A' * A +// MatrixType is either Eigen::Matrix or Eigen::SparseMatrix +template +class SVDTallMatOp: public SVDMatOp +{ +private: + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef const Eigen::Ref ConstGenericMatrix; + + ConstGenericMatrix m_mat; + const int m_dim; + Vector m_cache; + +public: + // Constructor + SVDTallMatOp(ConstGenericMatrix& mat) : + m_mat(mat), + m_dim(std::min(mat.rows(), mat.cols())), + m_cache(mat.rows()) + {} + + // These are the rows and columns of A' * A + int rows() const { return m_dim; } + int cols() const { return m_dim; } + + // y_out = A' * A * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) + { + MapConstVec x(x_in, m_mat.cols()); + MapVec y(y_out, m_mat.cols()); + m_cache.noalias() = m_mat * x; + y.noalias() = m_mat.transpose() * m_cache; + } +}; + +// Operation of a wide matrix in SVD +// We compute the eigenvalues of A * A' +// MatrixType is either Eigen::Matrix or Eigen::SparseMatrix +template +class SVDWideMatOp: public SVDMatOp +{ +private: + typedef Eigen::Matrix Vector; + typedef Eigen::Map MapConstVec; + typedef Eigen::Map MapVec; + typedef const Eigen::Ref ConstGenericMatrix; + + ConstGenericMatrix m_mat; + const int m_dim; + Vector m_cache; + +public: + // Constructor + SVDWideMatOp(ConstGenericMatrix& mat) : + m_mat(mat), + m_dim(std::min(mat.rows(), mat.cols())), + m_cache(mat.cols()) + {} + + // These are the rows and columns of A * A' + int rows() const { return m_dim; } + int cols() const { return m_dim; } + + // y_out = A * A' * x_in + void perform_op(const Scalar* x_in, Scalar* y_out) + { + MapConstVec x(x_in, m_mat.rows()); + MapVec y(y_out, m_mat.rows()); + m_cache.noalias() = m_mat.transpose() * x; + y.noalias() = m_mat * m_cache; + } +}; + +// Partial SVD solver +// MatrixType is either Eigen::Matrix or Eigen::SparseMatrix +template < typename Scalar = double, + typename MatrixType = Eigen::Matrix > +class PartialSVDSolver +{ +private: + typedef Eigen::Matrix Matrix; + typedef Eigen::Matrix Vector; + typedef const Eigen::Ref ConstGenericMatrix; + + ConstGenericMatrix m_mat; + const int m_m; + const int m_n; + SVDMatOp* m_op; + SymEigsSolver< Scalar, LARGEST_ALGE, SVDMatOp >* m_eigs; + int m_nconv; + Matrix m_evecs; + +public: + // Constructor + PartialSVDSolver(ConstGenericMatrix& mat, int ncomp, int ncv) : + m_mat(mat), m_m(mat.rows()), m_n(mat.cols()), m_evecs(0, 0) + { + // Determine the matrix type, tall or wide + if(m_m > m_n) + { + m_op = new SVDTallMatOp(mat); + } else { + m_op = new SVDWideMatOp(mat); + } + + // Solver object + m_eigs = new SymEigsSolver< Scalar, LARGEST_ALGE, SVDMatOp >(m_op, ncomp, ncv); + } + + // Destructor + virtual ~PartialSVDSolver() + { + delete m_eigs; + delete m_op; + } + + // Computation + int compute(int maxit = 1000, Scalar tol = 1e-10) + { + m_eigs->init(); + m_nconv = m_eigs->compute(maxit, tol); + + return m_nconv; + } + + // The converged singular values + Vector singular_values() const + { + Vector svals = m_eigs->eigenvalues().cwiseSqrt(); + + return svals; + } + + // The converged left singular vectors + Matrix matrix_U(int nu) + { + if(m_evecs.cols() < 1) + { + m_evecs = m_eigs->eigenvectors(); + } + nu = std::min(nu, m_nconv); + if(m_m <= m_n) + { + return m_evecs.leftCols(nu); + } + + return m_mat * (m_evecs.leftCols(nu).array().rowwise() / m_eigs->eigenvalues().head(nu).transpose().array().sqrt()).matrix(); + } + + // The converged right singular vectors + Matrix matrix_V(int nv) + { + if(m_evecs.cols() < 1) + { + m_evecs = m_eigs->eigenvectors(); + } + nv = std::min(nv, m_nconv); + if(m_m > m_n) + { + return m_evecs.leftCols(nv); + } + + return m_mat.transpose() * (m_evecs.leftCols(nv).array().rowwise() / m_eigs->eigenvalues().head(nv).transpose().array().sqrt()).matrix(); + } +}; + + +} // namespace Spectra + +#endif // PARTIAL_SVD_SOLVER_H diff --git a/src/external/minimum_ellipsoid/bnmin_main.h b/src/external/minimum_ellipsoid/bnmin_main.h new file mode 100644 index 00000000..a9a22a93 --- /dev/null +++ b/src/external/minimum_ellipsoid/bnmin_main.h @@ -0,0 +1,87 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +// This file is converted from BNMin1 (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) by Apostolos Chalkis + +// Original copyright notice: + +/** + Bojan Nikolic + Initial version 2008 + + This file is part of BNMin1 and is licensed under GNU General + Public License version 2. + + \file bnmin_main.cxx + +*/ +#ifndef BNMIN_MAIN_H +#define BNMIN_MAIN_H + +#include +#include + +#include + +//#include "bnmin_main1.h" +//#include "config.h" + +//namespace Minim { + + inline const char * version(void) + { + //return PACKAGE_VERSION; + return "11"; + } + + class BaseErr: + public std::runtime_error + { + public: + BaseErr(const std::string &s): + std::runtime_error(s) + { + } + + }; + + class NParsErr: + public BaseErr + { + public: + NParsErr(const std::string &fname, + size_t expected, + size_t received): + BaseErr( (boost::format("In function %s expected %i but received %i pars ") + % fname + % expected + % received).str()) + { + } + + + }; + + /*BaseErr::BaseErr(const std::string &s): + std::runtime_error(s) + { + } + + NParsErr::NParsErr(const std::string &fname, + size_t expected, + size_t received): + BaseErr( (boost::format("In function %s expected %i but received %i pars ") + % fname + % expected + % received).str()) + { + }*/ + + +#endif + +//} + + diff --git a/src/external/minimum_ellipsoid/khach.h b/src/external/minimum_ellipsoid/khach.h new file mode 100644 index 00000000..88492b8e --- /dev/null +++ b/src/external/minimum_ellipsoid/khach.h @@ -0,0 +1,220 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +// This file is converted from BNMin1 (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) by Apostolos Chalkis + +// Original copyright notice: + +/** + Bojan Nikolic + Initial version 2010 + + This file is part of BNMin1 and is licensed under GNU General + Public License version 2 + + \file ellipsoids.cxx + + Computation and use of ellipsoids releated to sets of points +*/ +#ifndef KHACH_H +#define KHACH_H + +#include +#include +#include + +//#include "khach1.h" +//#include "mcpoint1.h" +#include "mcpoint.h" +//#include "bnmin_main1.h" +//#include "bnmin_main2.h" + +//#include "../bnmin_main.hxx" + +//namespace Minim { + + template + using MTT = Eigen::Matrix; + + template + using VTT = Eigen::Matrix; + + struct KhachiyanEllipsoid + { + Eigen::Matrix Q; + Eigen::Matrix c; + }; + + template + inline bool is_nan(const Eigen::MatrixBase& x) + { + return ((x.array() == x.array())).all(); + } + + template + bool InvertMatrix(const MTT &input, + MTT &inverse) + { + inverse = input.inverse(); + return !is_nan(inverse); + } + + + inline void InvertLP(const MTT &Lambdap, + MTT &LpInv) + { + bool res = InvertMatrix(Lambdap, LpInv); + if (not res) + { + // throw an error of your choice here! + // throw MatrixErr("Could not invert matrix: ", + // Lambdap); + } + } + + inline void Lift(const MTT &A, MTT &Ap) + { + Ap.resize(A.rows()+1, A.cols()); + Ap.topLeftCorner(A.rows(), A.cols()) = A; + Ap.row(Ap.rows()-1).setConstant(1.0); + } + + inline void genDiag(const VTT &p, MTT &res) + { + res.setZero(p.size(), p.size()); + + for(size_t i=0; i &Ap, + const VTT &p, + MTT &Lambdap) + { + + MTT dp(p.size(), p.size()); + genDiag(p, dp); + + dp = dp * Ap.transpose(); + Lambdap.noalias() = Ap * dp; + } + + inline double KhachiyanIter(const MTT &Ap, VTT &p) + { + /// Dimensionality of the problem + const size_t d = Ap.rows()-1; + + MTT Lp; + MTT M; + KaLambda(Ap, p, Lp); + MTT ILp(Lp.rows(), Lp.cols()); + InvertLP(Lp, ILp); + M.noalias() = ILp * Ap; + M = Ap.transpose() * M; + + double maxval=0; + size_t maxi=0; + for(size_t i=0; i maxval) + { + maxval=M(i,i); + maxi=i; + } + } + const double step_size=(maxval -d - 1)/((d+1)*(maxval-1)); + VTT newp = p*(1-step_size); + newp(maxi) += step_size; + + const double err= (newp-p).norm(); + p = newp; + return err; + + } + + inline void KaInvertDual(const MTT &A, + const VTT &p, + MTT &Q, + VTT &c) + { + const size_t d = A.rows(); + MTT dp(p.size(), p.size()); + genDiag(p, dp); + + MTT PN; + PN.noalias() = dp * A.transpose(); + PN = A * PN; + + VTT M2; + M2.noalias() = A * p; + + MTT M3; + M3.noalias() = M2 * M2.transpose(); + + MTT invert(PN.rows(), PN.cols()); + InvertLP(PN- M3, invert); + Q.noalias() = (invert/d); + c.noalias() = A * p; + + } + + inline double KhachiyanAlgo(const MTT &A, + double eps, + size_t maxiter, + MTT &Q, + VTT &c) + { + VTT p(A.cols()); + p.setConstant(1.0/A.cols()); + + MTT Ap; + Lift(A, Ap); + + double ceps=eps*2; + for (size_t i=0; ieps; ++i) + { + ceps=KhachiyanIter(Ap, p); + } + + KaInvertDual(A, p, Q, c); + + return ceps; + + + } + + inline double KhachiyanAlgo(const std::set &ss, + double eps, + size_t maxiter, + KhachiyanEllipsoid &res) + { + const size_t d=ss.begin()->p.size(); + MTT A(d, ss.size()); + + size_t j=0; + for (std::set::const_iterator i=ss.begin(); + i != ss.end(); + ++i) + { + for(size_t k=0; k p[k]; + ++j; + } + + MTT Q(d,d); + VTT c(d); + + const double ceps=KhachiyanAlgo(A, eps, maxiter, + Q, c); + res.Q=Q; + res.c=c; + return ceps; + } + +#endif + +//} diff --git a/src/external/minimum_ellipsoid/mcpoint.h b/src/external/minimum_ellipsoid/mcpoint.h new file mode 100644 index 00000000..65176143 --- /dev/null +++ b/src/external/minimum_ellipsoid/mcpoint.h @@ -0,0 +1,477 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +// This file is converted from BNMin1 (https://www.mrao.cam.ac.uk/~bn204/oof/bnmin1.html) by Apostolos Chalkis + +// Original copyright notice: + +/** + Bojan Nikolic + Initial version 2009 + + This file is part of BNMin1 and is licensed under GNU General + Public License version 2 + + \file mcpoint.cxx +*/ +#ifndef MCPOINT_H +#define MCPOINT_H + +#include +#include +#include + +#include +#include + +//exclude gsl library Apostolos Chalkis +//#include +//#include + +//#include "mcpoint1.h" +//#include "mcpoint2.h" +#include "bnmin_main.h" +//#include "bnmin_main2.h" + +//namespace Minim { + struct MCPoint + { + /// The actual parameters + std::vector p; + /// Log-likelihood of this point + double ll; + /// A vector to store derived quantities at sample of the + /// distribution + std::vector fval; + + /// Default constructor allowed, fill in the data later + MCPoint(void): + p(0), + ll(-9999), + fval(0) + { + } + + /** \Construct with supplied position vector + */ + MCPoint(const std::vector &p): + p(p), + ll(-9999), + fval(0) + { + } + + /** \brief The parameter vector has n values + */ + MCPoint(size_t np): + p(np), + ll(-9999), + fval(0) + { + } + + MCPoint(const MCPoint &other): + p(other.p), + ll(other.ll), + fval(other.fval) + { + } + + MCPoint & operator=(const MCPoint &other) + { + p=other.p; + ll=other.ll; + fval=other.fval; + return *this; + } + + + }; + + inline bool operator< (const MCPoint &a, const MCPoint &b) + { + return a.ll < b.ll; + } + + struct WPPoint: + public MCPoint + { + /** \brief Weight factor + */ + double w; + + WPPoint(void): + w(0.0) + { + } + + WPPoint(const std::vector &p, + double w): + MCPoint(p), + w(w) + { + } + + /** \brief Construct from MCPoint and a supplied weight + */ + WPPoint(const MCPoint &mp, + double w): + MCPoint(mp), + w(w) + { + } + + }; + + /* + MCPoint::MCPoint(void): + p(0), + ll(-9999), + fval(0) + { + } + + MCPoint::MCPoint(const std::vector &p): + p(p), + ll(-9999), + fval(0) + { + } + + MCPoint::MCPoint(size_t np): + p(np), + ll(-9999), + fval(0) + { + } + + MCPoint::MCPoint(const MCPoint &other): + p(other.p), + ll(other.ll), + fval(other.fval) + { + } + + MCPoint &MCPoint::operator=(const MCPoint &other) + { + p=other.p; + ll=other.ll; + fval=other.fval; + return *this; + }*/ + + + inline void moment1(const std::list &l, + std::vector &res) + { + const size_t n=l.begin()->p.size(); + res=std::vector(n, 0.0); + for(std::list::const_iterator i=l.begin(); + i!= l.end(); + ++i) + { + for (size_t j=0; jp[j] * i->w * exp(- i->ll)); + } + } + } + + inline void moment1(const std::list &l, + double Z, + std::vector &res) + { + moment1(l,res); + for(size_t j=0; j &l, + const std::vector &m1, + std::vector &res) + { + const size_t n=m1.size(); + res=std::vector(n, 0.0); + for(std::list::const_iterator i=l.begin(); + i!= l.end(); + ++i) + { + for (size_t j=0; jp[j]-m1[j],2.0) * i->w * exp(- i->ll)); + } + } + } + + inline void moment2(const std::list &l, + const std::vector &m1, + double Z, + std::vector &res) + { + moment2(l, m1, res); + for(size_t j=0; j &s, + std::vector &res) + { + const size_t n=s.begin()->p.size(); + res=std::vector(n, 0.0); + + size_t N=0; + for(std::set::const_iterator i=s.begin(); + i!= s.end(); + ++i) + { + if(i->p.size() != n) + { + throw NParsErr("moment1", n, i->p.size()); + } + for (size_t j=0; jp[j]); + } + ++N; + } + + for(size_t j=0; j &s, + const std::vector &m1, + std::vector &res) + { + const size_t n=m1.size(); + res=std::vector(n, 0.0); + + size_t N=0; + for(std::set::const_iterator i=s.begin(); + i!= s.end(); + ++i) + { + for (size_t j=0; jp[j]-m1[j], 2); + } + ++N; + } + + for(size_t j=0; j &s, + const std::vector &m1, + std::vector &res) + { + const size_t n=m1.size(); + res=std::vector(n*n, 0.0); + + size_t N=0; + for(std::set::const_iterator i=s.begin(); + i!= s.end(); + ++i) + { + for (size_t j=0; jp[j]-m1[j])*(i->p[k]-m1[k]); + } + } + ++N; + } + + for(size_t j=0; j &s, + std::vector &res) + { + std::vector m1; + moment1(s, m1); + omoment2(s, m1, res); + } + + + inline void StdDev(const std::set &s, + std::vector &res) + { + std::vector m1, m2; + moment1(s, m1); + moment2(s, m1, m2); + res.resize(m2.size()); + for(size_t j=0; j &cv, + std::vector &eigvals, + std::vector &eigvects) + { + const size_t n=sqrt(cv.size()); + gsl_matrix_view m + = gsl_matrix_view_array (const_cast(&cv[0]), n, n); + + gsl_vector *eval = gsl_vector_alloc (n); + gsl_matrix *evec = gsl_matrix_alloc (n, n); + + gsl_eigen_symmv_workspace * w = + gsl_eigen_symmv_alloc (n); + + gsl_eigen_symmv (&m.matrix, + eval, + evec, + w); + + gsl_eigen_symmv_free (w); + + gsl_eigen_symmv_sort (eval, + evec, + GSL_EIGEN_SORT_ABS_ASC); + + eigvals.resize(n); + eigvects.resize(n*n); + for(size_t j=0; j &l, + double Z, + const std::vector &low, + const std::vector &high, + size_t nbins, + std::vector &res) + { + const size_t ndim=low.size(); + + //res.resize(pow(nbins, static_cast(ndim))); + res.resize( static_cast( pow(static_cast(nbins), static_cast(ndim)) ) ); + std::fill(res.begin(), res.end(), 0.0); + + + std::vector deltas(ndim); + for(size_t i=0; i::const_iterator i=l.begin(); + i!= l.end(); + ++i) + { + bool inside=true; + size_t k=0; + for (size_t j=0; jp[j]-low[j])/deltas[j]); + if (dimi >= 0 and dimi < (int)nbins) + { + k+= dimi * static_cast( pow(static_cast(nbins), static_cast(ndim-j-1)) ); + } + else + { + inside=false; + } + } + if (inside) + { + res[k]+= i->w * exp(- i->ll); + } + } + } + + + inline void marginHist(const std::list &l, + size_t pi, + double Z, + double low, + double high, + size_t nbins, + std::vector &res) + { + res.resize(nbins); + std::fill(res.begin(), res.end(), + 0.0); + + const double d=(high-low)/nbins; + for(std::list::const_iterator i=l.begin(); + i!= l.end(); + ++i) + { + int k=int((i->p[pi]-low)/d); + if (k > 0 and k < (int)nbins) + { + res[k]+= i->w * exp(- i->ll); + } + } + + for(size_t i=0; i &l, + double Z, + size_t i, + double ilow, + double ihigh, + size_t j, + double jlow, + double jhigh, + size_t nbins, + std::vector &res) + { + // Two dimensions only + res.resize( static_cast( pow(static_cast(nbins), static_cast(2)) ) ); + std::fill(res.begin(), res.end(), + 0.0); + const double idelta=(ihigh-ilow)/nbins; + const double jdelta=(jhigh-jlow)/nbins; + + for(std::list::const_iterator p=l.begin(); + p!= l.end(); + ++p) + { + + int dimi = int((p->p[i]-ilow)/idelta); + int dimj = int((p->p[j]-jlow)/jdelta); + + if (dimi >= 0 and dimi<((int)nbins) and dimj >= 0 and dimj < ((int)nbins)) + { + const size_t k= dimi*nbins + dimj; + res[k]+= p->w * exp(- p->ll); + } + + } + } +//} + +#endif diff --git a/src/volesti b/src/volesti deleted file mode 160000 index 5bf9188f..00000000 --- a/src/volesti +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5bf9188fb11fddab17de98fabc505e993216ca5f diff --git a/src/volesti/include/SDPAFormatManager.h b/src/volesti/include/SDPAFormatManager.h new file mode 100644 index 00000000..c38e2d59 --- /dev/null +++ b/src/volesti/include/SDPAFormatManager.h @@ -0,0 +1,271 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_SDPA_FORMAT_MANAGER_H +#define VOLESTI_SDPA_FORMAT_MANAGER_H + + +#include "convex_bodies/spectrahedra/spectrahedron.h" + +#include +#include + + +/// Reads/writes files according to the SDPA format for sdps. +/// Currently supported Format: +/// +/// +/// +/// +/// +/// For example: +/// 2 +/// 3 +/// 1 1 +/// +/// +/// \tparam NT Numerical Type +template +class SdpaFormatManager { +private: + typedef std::string::iterator string_it; + typedef std::list listVector; + + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + + /// Return the first non white space/tab character and advance the iterator one position + /// @param[in, out] it Current position + /// @param[in] end End of string + /// @return First non white space/tab character + char consumeSymbol(string_it &at, string_it & end) { + while (at != end) { + if (*at != ' ' && *at != '\t') { + char c = *at; + at++; + return c; + } + + at++; + } + + return '\0'; + } + + + /// Determine if current line is a comment + /// @param[in] line The current line + /// @return true if line is a comment, false otherwise + bool isCommentLine(std::string & line) { + string_it at = line.begin(); + string_it end = line.end(); + char c = consumeSymbol(at, end); + return c == '"' || c == '*'; + } + + + /// Get an integer from the string + /// \param[in] string + /// \return an integer + int fetchNumber(std::string &string) { + std::stringstream stream(string); + int num; + stream >> num; + return num; + } + + + /// Read a vector of the form {val1, val2, ..., valn} + /// @param string Contains the vector + /// @return a list with the n numbers + listVector readVector(std::string &string) { + std::stringstream stream(string); + listVector vector; + NT value; + + while (stream >> value) { + vector.push_back(value); + } + + return vector; + } + +public: + + /// Reads an SDPA format file + /// \param[in] is An open stram pointing to the file + /// \param[out] matrices the matrices A0, A1, A2, ..., An + /// \param[out] objectiveFunction The objective function of the sdp + void loadSDPAFormatFile(std::ifstream &is, std::vector &matrices, VT &objectiveFunction) { + std::string line; + std::string::size_type sz; + + std::getline(is, line, '\n'); + + //skip comments + while (isCommentLine(line)) { + std::getline(is, line, '\n'); + } + + //read variables number + int variablesNum = fetchNumber(line); + + if (std::getline(is, line, '\n').eof()) + throw std::runtime_error("Unexpected end of file"); + + //read number of blocks + int blocksNum = fetchNumber(line); + + if (std::getline(is, line, '\n').eof()) + throw std::runtime_error("Unexpected end of file"); + + //read block structure vector + listVector blockStructure = readVector(line); + + if (blockStructure.size() != blocksNum) + throw std::runtime_error("Wrong number of blocks"); + + if (std::getline(is, line, '\n').eof()) + throw std::runtime_error("Unexpected end of file"); + + //read constant vector + listVector constantVector = readVector(line); + + while (constantVector.size() < variablesNum) { + if (std::getline(is, line, '\n').eof()) + throw std::runtime_error("Unexpected end of file"); + + listVector t = readVector(line); + constantVector.insert(std::end(constantVector), std::begin(t), std::end(t)); + } + + matrices = std::vector(variablesNum + 1); + int matrixDim = 0; + for (auto x : blockStructure) + matrixDim += std::abs((int) x); + + //read constraint matrices + for (int atMatrix = 0; atMatrix < matrices.size(); atMatrix++) { + MT matrix; + matrix.setZero(matrixDim, matrixDim); + + int offset = 0; + + for (auto blockSize : blockStructure) { + + if (blockSize > 0) { //read a block blockSize x blockSize + int at = 0; + int i = 0, j = 0; + + while (at < blockSize * blockSize) { + if (std::getline(is, line, '\n').eof()) + throw 1; + + listVector vec = readVector(line); + + for (double value : vec) { + matrix(offset + i, offset + j) = value; + at++; + if (at % (int) blockSize == 0) { // new row + i++; + j = 0; + } else { //new column + j++; + } + } + } /* while (at0, I want it <0 + if (atMatrix == 0) //F0 has - before it in SDPA format, the rest have + + matrices[atMatrix] = matrix; + else + matrices[atMatrix] = -1 * matrix; + } + + // return lmi and objective function + objectiveFunction.setZero(variablesNum); + int at = 0; + + for (auto value : constantVector) + objectiveFunction(at++) = value; + } + + /// Create a SDPA format file + /// \param[in] os Open stream to file + /// \param[in] matrices The matrices A0, ..., An + /// \param[in] objectiveFunction The objective function of the sdp + void writeSDPAFormatFile(std::ostream &os, std::vector const & matrices, VT const & objectiveFunction) { + int dim = matrices.size() - 1; + MT A0 = matrices[0]; + + os << dim << "\n"; + os << 1 << "\n"; + os << A0.rows() << "\n"; + + os << objectiveFunction.transpose() << "\n"; + + for (int i = 0; i < A0.rows(); i++) + os << A0.row(i) << "\n"; + + for (int at=1 ; at + void loadSDPAFormatFile(std::ifstream &is, Spectrahedron &spectrahedron, Point &objectiveFunction) { + std::vector matrices; + VT coeffs; + loadSDPAFormatFile(is, matrices, coeffs); + LMI lmi(matrices); + spectrahedron = Spectrahedron(lmi); + objectiveFunction = Point(coeffs); + } + + + /// Write a spectrahedron and a vector (objective function) to a SDPA format output file + /// \tparam Point + /// \param[in] is opened stream to output file + /// \param[in] spectrahedron + /// \param[in] objectiveFunction + template + void writeSDPAFormatFile(std::ostream &os, Spectrahedron const & spectrahedron, Point const & objectiveFunction) { + writeSDPAFormatFile(os, spectrahedron.getLMI().getMatrices(), objectiveFunction.getCoefficients()); + } +}; + + +#endif //VOLESTI_SDPA_FORMAT_MANAGER_H + diff --git a/src/volesti/include/cartesian_geom/cartesian_kernel.h b/src/volesti/include/cartesian_geom/cartesian_kernel.h new file mode 100644 index 00000000..3e1049b2 --- /dev/null +++ b/src/volesti/include/cartesian_geom/cartesian_kernel.h @@ -0,0 +1,39 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2018 Vissarion Fisikopoulos, Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// VolEsti is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// VolEsti is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// See the file COPYING.LESSER for the text of the GNU Lesser General +// Public License. If you did not receive this file along with HeaDDaCHe, +// see . + + +#ifndef CARTESIAN_KERNEL_H +#define CARTESIAN_KERNEL_H + +#include "point.h" + +/// This class represents a cartesian kernel parameterized by a numerical type e.g. double +/// \tparam K Numerical Type +template +class Cartesian +{ +public: + typedef Cartesian Self; + typedef K FT; + typedef point Point; + +}; + +#endif diff --git a/src/volesti/include/cartesian_geom/point.h b/src/volesti/include/cartesian_geom/point.h new file mode 100644 index 00000000..b8c8e32b --- /dev/null +++ b/src/volesti/include/cartesian_geom/point.h @@ -0,0 +1,238 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2018-2020 Vissarion Fisikopoulos, Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef POINT_H +#define POINT_H + +#include +#include + +/// This class manipulates a point parameterized by a number type e.g. double +/// \tparam K Numerical Type +template +class point +{ +private: + unsigned int d; + + Eigen::Matrix coeffs; + typedef typename std::vector::iterator iter; +public: + typedef Eigen::Matrix Coeff; + typedef typename K::FT FT; + + point() {} + + point(const unsigned int dim) + { + d = dim; + coeffs.setZero(d); + } + + point(const unsigned int dim, iter begin, iter endit) + { + d = dim; + coeffs.resize(d); + int i = 0; + + for (iter it=begin ; it != endit ; it++) + coeffs(i++) = *it; + } + + point(const Coeff& coeffs) + { + d = coeffs.rows(); + this->coeffs = coeffs; + } + + point(const unsigned int dim, std::vector cofs) + { + d = dim; + coeffs.resize(d); + iter it = cofs.begin(); + int i=0; + + for (; it != cofs.end(); it++,i++) + coeffs(i) = *it; + + } + + void add(const Coeff& coeffs) + { + this->coeffs += coeffs; + } + + const Coeff& getCoefficients() const + { + return coeffs; + } + + int dimension() const + { + return d; + } + + void set_dimension(const unsigned int dim) + { + d = dim; + coeffs.setZero(d); + } + + void set_coord(const unsigned int i, FT coord) + { + coeffs(i) = coord; + } + + void set_coeffs (const Coeff& coeffs2) { + d = coeffs2.rows(); + coeffs = coeffs2; + } + + void set_to_origin() { + coeffs.setZero(d); + } + + FT operator[] (const unsigned int i) const + { + return coeffs(i); + } + + FT* pointerToData() + { + return coeffs.data(); + } + + FT sum() const { + return coeffs.sum(); + } + + void operator+= (const point& p) + { + coeffs += p.getCoefficients(); + } + + void operator+= (const Coeff& coeffs) + { + this->coeffs += coeffs; + } + + void operator-= (const point& p) + { + coeffs -= p.getCoefficients(); + } + + void operator-= (const Coeff& coeffs) + { + this->coeffs -= coeffs; + } + + void operator= (const Coeff& coeffs) + { + this->coeffs = coeffs; + d = coeffs.rows(); + } + + //TODO: avoid point construction in operators +,-,* + point operator+ (const point& p) const + { + point temp; + temp.d = d; + temp.coeffs = coeffs + p.getCoefficients(); + return temp; + } + + point operator- (const point& p) const + { + point temp; + temp.d = d; + temp.coeffs = coeffs - p.getCoefficients(); + return temp; + } + + point operator* (const FT k) const + { + point temp; + temp.d = d; + temp.coeffs = coeffs * k; + return temp; + } + + void operator*= (const FT k) + { + coeffs *= k; + } + + void operator/= (const FT k) + { + coeffs /= k; + } + + bool operator== (point& p) const + { + int i=0; + const Coeff & coeffs = p.getCoefficients(); + + /* degree of approximation in + "The art of computer programming" (vol II), p. 234, Donald. E. Knuth. */ + FT e = 0.00000000001; + for (i=0 ; icoeffs(i) - coeffs(i)) > e *std::abs(this->coeffs(i)) || + std::abs(this->coeffs(i) - coeffs(i)) > e *std::abs(coeffs(i))) + return false; + } + + return true; + } + + FT distance(point const & p) { + return (this->coeffs - p.coeffs).norm(); + } + + FT dot(const point& p) const + { + return coeffs.dot(p.getCoefficients()); + } + + FT dot(const Coeff& coeffs) const + { + return this->coeffs.dot(coeffs); + } + + FT squared_length() const { + FT lsq = length(); + return lsq * lsq; + } + + FT length() const { + return coeffs.norm(); + } + + void print() const + { + for(unsigned int i=0; i +point operator* (const typename K::FT& k, point const& p) +{ + return p * k; +} + +#endif diff --git a/src/volesti/include/convex_bodies/ball.h b/src/volesti/include/convex_bodies/ball.h new file mode 100644 index 00000000..51dc2647 --- /dev/null +++ b/src/volesti/include/convex_bodies/ball.h @@ -0,0 +1,176 @@ +// volesti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018-19 programs. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef BALL_H +#define BALL_H + +#include + +/// This class represents a ball parameterized by a point type +/// \tparam Point Point Type +template +class Ball{ +public: + typedef Point PointType; + typedef typename Point::FT NT; + typedef typename std::vector::iterator viterator; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + + Ball() {} + + Ball(Point cc, NT RR) : c(cc), R(RR) {} + + std::pair InnerBall() const + { + return std::pair(c, R); + } + + Point center() const + { + return c; + } + + NT squared_radius() const + { + return R; + } + + NT radius() const + { + return std::sqrt(R); + } + + int dimension() const + { + return c.dimension(); + } + + int is_in(Point const& p) const + { + if (p.squared_length() <= R) + return -1; + else return 0; + } + + std::pair line_intersect(Point const& r, Point const& v) const + { + + NT vrc(0), v2(0), rc2(0); + + vrc = v.dot(r); + v2 = v.dot(v); + rc2 = r.dot(r); + + NT disc_sqrt = std::sqrt(std::pow(vrc,2) - v2 * (rc2 - R)); + return std::pair ((NT(-1)*vrc + disc_sqrt)/v2, + (NT(-1)*vrc - disc_sqrt)/v2); + } + + std::pair line_intersect(Point const& r, + Point const& v, + const VT &Ar, + const VT &Av) const + { + return line_intersect(r, v); + } + + + std::pair line_intersect(Point const& r, + Point const& v, + const VT &Ar, + const VT &Av, + NT &lambda_prev) const + { + return line_intersect(r, v); + } + + std::pair line_positive_intersect(Point const& r, + Point const& v) const + { + return std::pair(line_intersect(r, v).first, 0); + } + + std::pair line_positive_intersect(Point const& r, + Point const& v, + const VT &Ar, + const VT &Av) const + { + return line_positive_intersect(r, v); + } + + std::pair line_positive_intersect(Point const& r, + Point const& v, + const VT &Ar, + const VT &Av, + NT &lambda_prev) const + { + return line_positive_intersect(r, v); + } + + std::pair line_intersect_coord(Point const& r, + unsigned int const& rand_coord) const + { + + NT vrc = r[rand_coord]; + NT rc2(R); + rc2 -= r.dot(r); + + + NT disc_sqrt = std::sqrt(std::pow(vrc,2) + rc2); + return std::pair (NT(-1)*vrc + disc_sqrt, NT(-1)*vrc - disc_sqrt); + + } + + std::pair line_intersect_coord(Point const& r, + unsigned int const& rand_coord, + const VT &lamdas) const + { + return line_intersect_coord(r, rand_coord); + } + + std::pair line_intersect_coord(Point const& r, + Point const& r_prev, + unsigned int const& rand_coord, + unsigned int const& rand_coord_prev, + const VT &lamdas) const + { + return line_intersect_coord(r, rand_coord); + } + + int num_of_hyperplanes() const + { + return 0; + } + + void compute_reflection (Point& v, Point const& p) const + { + Point s = p; + s *= (1.0 / std::sqrt(s.squared_length())); + s *= (-2.0 * v.dot(s)); + v += s; + } + + template + void compute_reflection (Point &v, Point const& p, update_parameters ¶ms) const { + + params.ball_inner_norm = p.length(); + params.inner_vi_ak = v.dot(p) / params.ball_inner_norm; + v += (p * (-2.0 * params.inner_vi_ak * (1.0 / params.ball_inner_norm))); + } + +private: + Point c; //center + NT R; //SQUARED radius !!! +}; + + +#endif diff --git a/src/volesti/include/convex_bodies/ballintersectconvex.h b/src/volesti/include/convex_bodies/ballintersectconvex.h new file mode 100644 index 00000000..b61ae532 --- /dev/null +++ b/src/volesti/include/convex_bodies/ballintersectconvex.h @@ -0,0 +1,401 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018-19 programs. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef BALLINTERSECTCONVEX_H +#define BALLINTERSECTCONVEX_H + +/// This class represents a polytope intersected with a ball +/// \tparam Polytope Polytope Type +/// \tparam CBall Ball Type +template +class BallIntersectPolytope { +private: + Polytope P; + CBall B; +public: + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename CBall::NT NT; + typedef typename CBall::PointType PointType; + + BallIntersectPolytope() {} + + BallIntersectPolytope(Polytope& PP, CBall &BB) : P(PP), B(BB) {}; + + Polytope first() const { return P; } + CBall second() const { return B; } + + std::pair InnerBall() const + { + return P.InnerBall(); + } + + MT get_mat() const { + return P.get_mat(); + } + + MT get_T() const { + return P.get_mat(); + } + + MT get_vec() const { + return P.get_vec(); + } + + int is_in(PointType const& p) const + { + if (B.is_in(p)==-1) + return P.is_in(p); + return 0; + } + + int num_of_hyperplanes() const { + return P.num_of_hyperplanes(); + } + + unsigned int dimension() const { + return P.dimension(); + } + + NT radius() const { + return B.radius(); + } + + std::pair line_intersect(PointType const& r, PointType const& v) const + { + + std::pair polypair = P.line_intersect(r, v); + std::pair ballpair = B.line_intersect(r, v); + return std::pair(std::min(polypair.first, ballpair.first), + std::max(polypair.second, ballpair.second)); + } + + std::pair line_intersect(PointType const& r, + PointType const& v, + VT &Ar, + VT &Av) const + { + std::pair polypair = P.line_intersect(r, v, Ar, Av); + std::pair ballpair = B.line_intersect(r, v); + return std::pair(std::min(polypair.first, ballpair.first), + std::max(polypair.second, ballpair.second)); + } + + std::pair line_intersect(PointType const& r, + PointType const& v, + VT &Ar, + VT &Av, + NT &lambda_prev) const + { + std::pair polypair = P.line_intersect(r, v, Ar, Av, lambda_prev); + std::pair ballpair = B.line_intersect(r, v); + return std::pair(std::min(polypair.first, ballpair.first), + std::max(polypair.second, ballpair.second)); + } + + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT &Ar, + VT &Av) + { + std::pair polypair = P.line_positive_intersect(r, v, Ar, Av); + std::pair ball_lambda = B.line_positive_intersect(r, v); + int facet = (polypair.first < ball_lambda.first) ? polypair.second : P.num_of_hyperplanes(); + + return std::pair(std::min(polypair.first, ball_lambda.first), facet); + } + + + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT &Ar, + VT &Av, + NT &lambda_prev) + { + std::pair polypair = P.line_positive_intersect(r, v, Ar, Av, lambda_prev); + std::pair ball_lambda = B.line_positive_intersect(r, v); + int facet = (polypair.first < ball_lambda.first) ? polypair.second : P.num_of_hyperplanes(); + + return std::pair(std::min(polypair.first, ball_lambda.first), facet); + } + + //---------------------accelerated billiard---------------------// + template + std::pair line_first_positive_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av, + update_parameters& params) + { + std::pair polypair = P.line_first_positive_intersect(r, v, Ar, Av, params); + std::pair ball_lambda = B.line_positive_intersect(r, v); + + params.hit_ball = (polypair.first < ball_lambda.first) ? false : true; + int facet = params.hit_ball ? P.num_of_hyperplanes() : polypair.second; + params.facet_prev = polypair.second; + + return std::pair(std::min(polypair.first, ball_lambda.first), facet); + } + + template + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + MT const& AA, + update_parameters& params) + { + std::pair polypair = P.line_positive_intersect(r, v, Ar, Av, lambda_prev, AA, params); + std::pair ball_lambda = B.line_positive_intersect(r, v); + + params.hit_ball = (polypair.first < ball_lambda.first) ? false : true; + int facet = params.hit_ball ? P.num_of_hyperplanes() : polypair.second; + params.facet_prev = polypair.second; + + return std::pair(std::min(polypair.first, ball_lambda.first), facet); + } + + template + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + update_parameters& params) + { + std::pair polypair = P.line_positive_intersect(r, v, Ar, Av, lambda_prev, params); + std::pair ball_lambda = B.line_positive_intersect(r, v); + + params.hit_ball = (polypair.first < ball_lambda.first) ? false : true; + int facet = params.hit_ball ? P.num_of_hyperplanes() : polypair.second; + params.facet_prev = polypair.second; + + return std::pair(std::min(polypair.first, ball_lambda.first), facet); + } +//-------------------------------------------------------------------------// + + //First coordinate ray shooting intersecting convex body + std::pair line_intersect_coord(PointType const& r, + unsigned int const& rand_coord, + VT &lamdas) const + { + + std::pair polypair = P.line_intersect_coord(r, rand_coord, lamdas); + std::pair ballpair = B.line_intersect_coord(r, rand_coord); + return std::pair(std::min(polypair.first, ballpair.first), + std::max(polypair.second, ballpair.second)); + } + + //Not the first coordinate ray shooting intersecting convex body + std::pair line_intersect_coord(PointType const& r, + PointType const& r_prev, + unsigned int const& rand_coord, + unsigned int const& rand_coord_prev, + VT &lamdas) const + { + std::pair polypair = P.line_intersect_coord(r, r_prev, rand_coord, + rand_coord_prev, lamdas); + std::pair ballpair = B.line_intersect_coord(r, rand_coord); + return std::pair(std::min(polypair.first, ballpair.first), + std::max(polypair.second, ballpair.second)); + } + + std::pair query_dual(PointType const& p, + unsigned int const& rand_coord) + { + std::pair polypair = P.query_dual(p, rand_coord); + std::pair ballpair = B.line_intersect_coord(p, rand_coord); + return std::pair(std::min(polypair.first, ballpair.first), + std::max(polypair.second, ballpair.second)); + } + + void compute_reflection (PointType& v, PointType const& p, int &facet) + { + + if (facet == P.num_of_hyperplanes()) { + B.compute_reflection(v, p); + } else { + P.compute_reflection(v, p, facet); + } + + } + + template + void compute_reflection (PointType &v, PointType const& p, update_parameters ¶ms) + { + if (params.hit_ball) { + B.compute_reflection(v, p, params); + } else { + P.compute_reflection(v, p, params); + } + } + +}; + + +/* EXPERIMENTAL +template +class PolytopeIntersectEllipsoid { +private: + T1 P; + T2 E; + typedef typename T2::K K; +public: + PolytopeIntersectEllipsoid(T1 &Pin, T2 &Ein) : P(Pin), E(Ein) {}; + + T1 first() { return P; } + T2 second() { return E; } + + int is_in(Point p){ + //std::cout << "calling is in"< line_intersect(Point r, + Point v){ + + std::pair polypair = P.line_intersect(r,v); + std::pair returnpair; + std::pair ellpair; + bool ellinter=false; + + //check the first intersection point if it is inside ball + if(E.is_in(polypair.first)){ + returnpair.first = polypair.first; + }else{ + ellinter=true; + //compute the intersection with ball + ellpair = E.line_intersect(r,v); + returnpair.first = ellpair.first; + } + //check the second intersection point + if(E.is_in(polypair.second)){ + returnpair.second = polypair.second; + }else{ + if(ellinter) //if the intersection with ball is already computed + returnpair.second = ellpair.second; + else returnpair.second = (E.line_intersect(r,v)).second; + } + return returnpair; + } + + std::pair line_intersect_coord(Point &r, + Point &r_prev, + int rand_coord, + int rand_coord_prev, + std::vector &lamdas, + bool init + ){ + + std::pair polypair = P.line_intersect_coord(r,r_prev,rand_coord,rand_coord_prev,lamdas,init); + std::pair ellpair = E.line_intersect_coord(r,rand_coord); + return std::pair (std::min(polypair.first,ellpair.first), + std::max(polypair.second,ellpair.second)); + } + +}; + + +template +class BallPolyIntersectEll { +private: + T1 BP; + T2 E; + typedef typename T2::K K; +public: + BallPolyIntersectEll(T1 &BPin, T2 &Ein) : BP(BPin), E(Ein) {}; + + T1 first() { return BP; } + T2 second() { return E; } + + int is_in(Point p){ + //std::cout << "calling is in"< line_intersect(Point r, + Point v){ + + std::pair Bpolypair = BP.line_intersect(r,v); + std::pair returnpair; + std::pair ellpair; + bool ellinter=false; + + //check the first intersection point if it is inside ball + if(E.is_in(Bpolypair.first)){ + //std::cout<<"inside ball 1, radius:"<<_B.radius()< line_intersect_coord(Point &r, + Point &r_prev, + int rand_coord, + int rand_coord_prev, + std::vector &lamdas, + bool init + ){ + + std::pair Bpolypair = BP.line_intersect_coord(r,r_prev,rand_coord,rand_coord_prev,lamdas,init); + std::pair ellpair = E.line_intersect_coord(r,rand_coord); + return std::pair (std::min(Bpolypair.first,ellpair.first), + std::max(Bpolypair.second,ellpair.second)); + } + + + +};*/ + +#endif diff --git a/src/volesti/include/convex_bodies/barriers.h b/src/volesti/include/convex_bodies/barriers.h new file mode 100644 index 00000000..977e173f --- /dev/null +++ b/src/volesti/include/convex_bodies/barriers.h @@ -0,0 +1,52 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + + +struct LogarithmicBarrierAugmenter { + + template + struct LogarithmicBarrierObjective { + typedef HPolytope Hpolytope; + typedef typename Point::FT NT; + + func f; + Hpolytope &P; + bool enable; + + LogarithmicBarrierObjective(func f_, Hpolytope P_, bool enable_) : + f(f_), P(P_), enable(enable_) {} + + NT operator() (Point &x) { + if (enable) return f(x) + P.log_barrier(x); + else return f(x); + } + + }; + + template + struct LogarithmicBarrierGradient { + typedef HPolytope Hpolytope; + typedef typename Point::FT NT; + + func f; + Hpolytope &P; + bool enable; + + LogarithmicBarrierGradient(func f_, Hpolytope P_, bool enable_) : + f(f_), P(P_), enable(enable_) {} + + Point operator() (Point &x) { + if (enable) return f(x) + P.grad_log_barrier(x); + else return f(x); + } + + }; + +}; diff --git a/src/volesti/include/convex_bodies/convex_body.h b/src/volesti/include/convex_bodies/convex_body.h new file mode 100644 index 00000000..e70e9e66 --- /dev/null +++ b/src/volesti/include/convex_bodies/convex_body.h @@ -0,0 +1,109 @@ +#ifndef CONVEX_BODY_H +#define CONVEX_BODY_H + +#include +#include +#include +#include +#include + +/// This class represents a general convex body parameterized by a point type +/// \tparam Point Point type +template +class ConvexBody { +public: + typedef Point PointType; + typedef typename Point::FT NT; + typedef typename std::vector::iterator viterator; + //using RowMatrixXd = Eigen::Matrix; + //typedef RowMatrixXd MT; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + typedef std::function func; + typedef std::function grad; +private: + unsigned int dim; //dimension + std::vector gs; // convex functions defining the convex body + std::vector grad_gs; // convex function gradients + unsigned int m; + NT tol = NT(1e-4); + + +public: + + ConvexBody() : m(0) {} + + ConvexBody(std::vector gs_, std::vector grad_gs_, unsigned int dim_) : + gs(gs_), grad_gs(grad_gs_), dim(dim_) + { + m = gs.size(); + } + + unsigned int dimension() { + return dim; + } + + // Compute positive line intersection (in [0, 1]) using Binary search + // x: starting point + // v: direction (ray) + std::pair line_positive_intersect(Point const& x, Point const &v) const { + NT t_min = NT(1); + int constraint = -1; + NT t; + for (unsigned int i = 0; i < m; i++) { + t = binary_search(x, v, gs[i]); + if (t < t_min) { + t_min = t; + constraint = i; + } + } + + return std::make_pair(t_min, constraint); + } + + NT binary_search(Point const &x, Point const &v, func const& f) const { + NT t_min = NT(0); + NT t_max = NT(1); + NT t; + NT value; + + while (t_max - t_min > tol) { + t = (t_max + t_min) / 2; + value = f(x + t * v); + if (value >= -tol && value <= 0) { + return t; + } else if (value < -tol) { + t_min = t; + } else { + t_max = t; + } + + } + + return t; + } + + + // Computes unit normal at point p of the boundary + Point unit_normal(Point const& p, int const& constraint) const { + Point n = grad_gs[constraint](p); + return (1 / n.length()) * n; + } + + // Computes reflection of v about point p on the boundary + void compute_reflection(Point &v, Point const& p, int const& constraint) const { + Point n = unit_normal(p, constraint); + v += -2 * v.dot(n) * n; + } + + // Check if point is in K + int is_in(Point const& p, NT tol=NT(0)) { + for (func g : gs) { + if (g(p) > NT(-tol)) return 0; + } + return -1; + } + +}; + +#endif diff --git a/src/volesti/include/convex_bodies/correlation_matrices/corre_matrix.hpp b/src/volesti/include/convex_bodies/correlation_matrices/corre_matrix.hpp new file mode 100755 index 00000000..23f4ecbe --- /dev/null +++ b/src/volesti/include/convex_bodies/correlation_matrices/corre_matrix.hpp @@ -0,0 +1,161 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Contributed by Huu Phuoc Le as part of Google Summer of Code 2022 program + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_CORRE_MATRIX_HPP +#define VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_CORRE_MATRIX_HPP + +/// This class handles the PointType used by CorreSpectra_MT class. +/// Every point is a correlation matrix and only the lower triangular part is stored. +/// @tparam NT Number Type +template +class CorreMatrix{ + public: + + /// The numeric/matrix/vector types we use + typedef NT FT; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix Coeff; + + MT mat; + + CorreMatrix(){} + + CorreMatrix(unsigned int n){ + mat = MT::Identity(n,n); + } + + CorreMatrix(MT const& mat){ + this->mat = mat; + } + + CorreMatrix(VT const& coeffs){ + unsigned int n = ceil(sqrt(2*coeffs.rows())); + this->mat = MT::Identity(n,n); + int ind = 0; + for(int i = 0; i < n; ++i){ + for(int j = 0; j < i; ++j){ + this->mat(i,j) = coeffs(ind); + ++ind; + } + } + } + + int dimension() const { + int n = this->mat.rows(); + return n*(n-1)/2; + } + + void operator+= (const CorreMatrix & p){ + this->mat += p.mat; + } + + void operator-= (const CorreMatrix & p){ + this->mat -= p.mat; + } + + void operator= (const CorreMatrix & p){ + this->mat = p.mat; + } + + CorreMatrix operator+ (const CorreMatrix& p) const { + CorreMatrix temp; + temp.mat = this->mat + p.mat; + return temp; + } + + + CorreMatrix operator- () const { + CorreMatrix temp; + temp.mat = - this->mat; + return temp; + } + + void operator*= (const FT k){ + this->mat *= k; + } + + CorreMatrix operator* (const FT k) const { + MT M = this->mat; + M *= k; + return CorreMatrix(M); + } + + void operator/= (const FT k){ + this->mat /= k; + } + + NT dot(MT grad){ + int i, j, n = this->mat.rows(); + NT ret = NT(0); + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + ret += this->mat(i,j) * grad(i,j); + } + } + return ret; + } + + NT dot(CorreMatrix c){ + int i, j, n = this->mat.rows(); + NT ret = NT(0); + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + ret += this->mat(i,j) * c.mat(i,j); + } + } + return ret; + } + + NT squared_length() const { + int i, j, n = this->mat.rows(); + NT ret = NT(0); + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + ret += this->mat(i,j) * this->mat(i,j); + } + } + return ret; + } + + void print() const { + int n = this->mat.rows(), i, j; + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + std::cout<< this->mat(i,j) <<" "; + } + } + std::cout<<"\n"; + } + + VT getCoefficients() const { + int n = this->mat.rows(), ind = 0, i, j; + VT coeff(n*(n-1)/2); + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + coeff(ind) = this->mat(i,j); + ++ind; + } + } + return coeff; + } +}; + +template +CorreMatrix operator* (const NT k, CorreMatrix p){ + return p * k; +} + +template +std::ostream& operator<<(std::ostream& os, const CorreMatrix& p){ + os << p.mat; + return os; +} + +#endif //VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_CORRE_MATRIX_HPP diff --git a/src/volesti/include/convex_bodies/correlation_matrices/correlation_spectrahedron.hpp b/src/volesti/include/convex_bodies/correlation_matrices/correlation_spectrahedron.hpp new file mode 100755 index 00000000..a615b33c --- /dev/null +++ b/src/volesti/include/convex_bodies/correlation_matrices/correlation_spectrahedron.hpp @@ -0,0 +1,266 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Contributed by Huu Phuoc Le as part of Google Summer of Code 2022 program + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_VOLESTI_CORRELATION_SPECTRAHEDRON_HPP +#define VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_VOLESTI_CORRELATION_SPECTRAHEDRON_HPP + +template +struct Precompute{ + + /// These flags indicate whether the corresponding matrices are computed + bool computed_A = false; + bool computed_B = false; + + /// The matrices the method positiveIntersection receives from its previous call + /// if the flag first_positive_intersection is true. + /// Matrix A is also used in coordinateIntersection + MT A, B; + + /// In method positive_intersect, the distance we are computing corresponds + /// to the minimum positive eigenvalue of a quadratic eigenvalue problem. + /// This will hold the eigenvector for that eigenvalue + VT eigenvector; + + /// Sets all flags to false + void resetFlags(){ + computed_A = computed_B = false; + } + + void set_mat_size(int const& n){ + A = -MT::Identity(n,n); + B.setZero(n, n); + eigenvector.setZero(n); + } +}; + +/// This class handles the spectrahedra of correlation matrices +/// The PointType here is stored as vector. +/// For the matrix PointType class, refer to CorrelationSpectrahedron_MT +/// @tparam Point Point Type +template +class CorrelationSpectrahedron : public Spectrahedron{ + public: + + /// The numeric/matrix/vector types we use + typedef Point PointType; + typedef typename Point::FT NT; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + typedef Precompute PrecomputationOfValues; + + /// The size of the matrix + unsigned int n; + + PrecomputationOfValues _precomputedValues; + + /// Constructor of correlation matrix spectrahedra + + CorrelationSpectrahedron(unsigned int n){ + int i,j; + this->n = n; + this->d = n*(n-1)/2; + this->_inner_ball.first = PointType(this->d); + this->_inner_ball.second = 1/std::sqrt(this->d); + _precomputedValues.set_mat_size(n); + } + + /// \returns The size of the matrix + unsigned int matrixSize() const { + return n; + } + + std::pair getInnerBall() const { + return this->_inner_ball; + } + + /// Build a correlation matrix from a vector of entries + /// \param[in] vector of coefficients + /// \param[in] the matrix to be assigned + void buildMatrix(VT const &pvector, unsigned int const n, MT &mat) const { + NT coeff; + int i, j, ind = 0; + for(i = 0; i < n ; ++i){ + mat(i,i) = -1; + } + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + coeff = -pvector[ind]; + mat(i,j) = mat(j,i) = coeff; + ++ind; + } + } + } + + /// Computes the reflected direction at a point on the boundary of the spectrahedron. + /// \param[in] r A point on the boundary of the spectrahedron + /// \param[in] v The direction of the trajectory as it hits the boundary + /// \param[out] reflectedDirection The reflected direction + template + void compute_reflection(PointType &v, PointType const &r, update_parameters&) const { + VT grad(this->d); + VT e = _precomputedValues.eigenvector; + int i, j, ind = 0; + NT sum_sq = NT(0); + + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + grad(ind) = e[i]*e[j]; + sum_sq += grad(ind)*grad(ind); + ++ind; + } + } + NT dot = v.dot(grad); + dot = 2 * dot / sum_sq; + v -= dot * PointType(grad); + } + + /// Construct the generalized eigenvalue problem \[Bt - A \] for positive_intersect. + /// \param[in] p Input vector + /// \param[in] v Input vector + /// \param[in, out] _precomputedValues Holds matrices B = I - A(v), A = A(p) + void createMatricesForPositiveLinearIntersection(VT const &p, VT const &v){ + if (true) { + VT pvector = p, vvector = v; + NT coeff; + int i, j, ind =0; + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + coeff = -pvector[ind]; + _precomputedValues.A(i,j) = _precomputedValues.A(j,i) = coeff; + coeff = -vvector[ind]; + _precomputedValues.B(i,j) = _precomputedValues.B(j,i) = coeff; + ++ind; + } + } + _precomputedValues.computed_B = true; + } + } + + NT positiveLinearIntersection(VT const &p, VT const &v){ + createMatricesForPositiveLinearIntersection(p, v); + return this->EigenvaluesProblem.minPosLinearEigenvalue_EigenSymSolver(-_precomputedValues.A, _precomputedValues.B, _precomputedValues.eigenvector); + } + + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_positive_intersect(PointType const &r, PointType const &v){ + NT pos_inter = positiveLinearIntersection(r.getCoefficients(), v.getCoefficients()); + return std::pair (pos_inter, -1); + } + + std::pair line_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT& , + NT const&){ + return line_positive_intersect(r, v); + } + + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT&){ + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT& , + NT const&, + update_parameters&){ + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT&, + NT const&, + MT const&, + update_parameters&){ + return line_positive_intersect(r, v); + } + + template + std::pair line_first_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT&, + update_parameters&){ + return line_positive_intersect(r, v); + } + + // compute intersection point of ray starting from r and pointing to v + std::pair line_intersect(PointType const &r, PointType const &v){ + createMatricesForPositiveLinearIntersection(r.getCoefficients(), v.getCoefficients()); + return this->EigenvaluesProblem.symGeneralizedProblem(_precomputedValues.A, _precomputedValues.B); + } + + + std::pair line_intersect(PointType const &r, + PointType const &v, + VT&, + VT&){ + return line_intersect(r, v); + } + + std::pair line_intersect(PointType const &r, + PointType const &v, + VT&, + VT&, + NT&){ + return line_intersect(r, v); + } + + /// Compute the gradient of the determinant of the LMI at p + /// \param[in] p Input parameter + /// \param[in] Input vector: the eigenvector A(p)*e = 0 + /// \param[out] ret The unit normal vector at p + void unit_normal(VT p, VT const &e, VT &ret) const { + int i, j, ind = 0; + NT sum_sqqrt_sq = NT(0); + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + ret(ind) = e[i]*e[j]; + sum_sqqrt_sq += ret(ind)*ret(ind); + ++ind; + } + } + ret /= std::sqrt(sum_sqqrt_sq); //normalize + } + + /// Test if a point p is in the spectrahedron + /// \param p is the current point + /// \return true if position is outside the spectrahedron + int is_in(PointType const &p, NT tol=NT(0)) const { + if(isExterior(p.getCoefficients())) return 0; + return -1; + } + + bool isExterior(VT const &pos) const { + MT mat = MT(n, n); + buildMatrix(pos, n, mat); + return isExterior(mat); + } + + bool isExterior(MT const &mat) const { + return !this->EigenvaluesProblem.isPositiveSemidefinite(-mat); + } + + MT get_mat() const { + return MT::Identity(this->d, this->d); + } +}; + +#endif //VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_VOLESTI_CORRELATION_SPECTRAHEDRON_HPP \ No newline at end of file diff --git a/src/volesti/include/convex_bodies/correlation_matrices/correlation_spectrahedron_MT.hpp b/src/volesti/include/convex_bodies/correlation_matrices/correlation_spectrahedron_MT.hpp new file mode 100755 index 00000000..d3f754a5 --- /dev/null +++ b/src/volesti/include/convex_bodies/correlation_matrices/correlation_spectrahedron_MT.hpp @@ -0,0 +1,180 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Contributed by Huu Phuoc Le as part of Google Summer of Code 2022 program + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_VOLESTI_CORRELATION_SPECTRAHEDRON_MT_HPP +#define VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_VOLESTI_CORRELATION_SPECTRAHEDRON_MT_HPP + +#include "convex_bodies/correlation_matrices/corre_matrix.hpp" + +/// This class handles the spectrahedra of correlation matrices +/// @tparam CorreMatrix The Correlation Matrix +template +class CorrelationSpectrahedron_MT : public Spectrahedron{ + public: + + /// The numeric/matrix/vector types we use + typedef CorreMatrix PointType; + typedef typename PointType::FT NT; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + + /// The size of the matrix + unsigned int n; + + VT eigenvector; + + /// Constructor of correlation matrix spectrahedra + + CorrelationSpectrahedron_MT(unsigned int n){ + int i,j; + this->n = n; + this->d = n*(n-1)/2; + this->_inner_ball.first = PointType(this->d); + this->_inner_ball.second = 1/std::sqrt(this->d); + this->eigenvector.setZero(n); + } + + /// \returns The size of the matrix + unsigned int matrixSize() const { + return n; + } + + std::pair getInnerBall() const { + return this->_inner_ball; + } + + /// Computes the reflected direction at a point on the boundary of the spectrahedron. + /// \param[in] r A point on the boundary of the spectrahedron + /// \param[in] v The direction of the trajectory as it hits the boundary + /// \param[out] reflectedDirection The reflected direction + template + void compute_reflection(PointType &v, PointType const &r, update_parameters&) const { + MT grad = MT::Zero(this->n, this->n); + int i, j; + NT sum_sq = NT(0), dot = NT(0); + + for(i = 0; i < n ; ++i){ + for(j = 0; j < i; ++j){ + grad(i,j) = eigenvector[i]*eigenvector[j]; + sum_sq += grad(i,j)*grad(i,j); + dot += grad(i,j) * v.mat(i,j); + } + } + dot = 2 * dot / sum_sq; + grad = dot*grad; + v -= PointType(grad); + } + + /// Computes the minimal positive t s.t. r+t*v intersects the boundary of the spectrahedron + /// \param[in] r + /// \param[in] v + /// \param[out] a NT value t + NT positiveLinearIntersection(PointType const &r, PointType const &v){ + + // minPosLinearEigenvalue_EigenSymSolver(A,B) computes the minimal positive eigenvalue of A-t*B + + return this->EigenvaluesProblem.minPosLinearEigenvalue_EigenSymSolver(r.mat, (-v).mat, eigenvector); + } + + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_positive_intersect(PointType const &r, + PointType const &v) { + NT pos_inter = positiveLinearIntersection(r, v); + return std::pair (pos_inter, -1); + } + + std::pair line_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT& , + NT const&){ + return line_positive_intersect(r, v); + } + + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT&){ + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT& , + NT const&, + update_parameters&){ + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT&, + NT const&, + MT const&, + update_parameters&){ + return line_positive_intersect(r, v); + } + + template + std::pair line_first_positive_intersect(PointType const &r, + PointType const &v, + VT&, + VT&, + update_parameters&){ + return line_positive_intersect(r, v); + } + + // compute intersection point of ray starting from r and pointing to v + std::pair line_intersect(PointType const &r, PointType const &v) const { + return this->EigenvaluesProblem.symGeneralizedProblem(-r.mat, -v.mat); + } + + std::pair line_intersect(PointType const &r, + PointType const &v, + VT&, + VT&) const { + return line_intersect(r, v); + } + + std::pair line_intersect(PointType const &r, + PointType const &v, + VT&, + VT&, + NT&) const { + return line_intersect(r, v); + } + + + /// Test if a point p is in the spectrahedron + /// \param p is the current point + /// \return true if position is outside the spectrahedron + int is_in(PointType const &p, NT tol=NT(0)) const { + if(this->EigenvaluesProblem.isPositiveSemidefinite(p.mat)){ + return -1; + } + return 0; + } + + bool isExterior(MT const &mat) const { + return !this->EigenvaluesProblem.isPositiveSemidefinite(mat); + } + + MT get_mat() const { + return MT::Identity(this->d, this->d); + } +}; + +#endif //VOLESTI_CONVEX_BODIES_CORRELATION_MATRICES_VOLESTI_CORRELATION_SPECTRAHEDRON_MT_HPP diff --git a/src/volesti/include/convex_bodies/ellipsoid.h b/src/volesti/include/convex_bodies/ellipsoid.h new file mode 100644 index 00000000..18c7570c --- /dev/null +++ b/src/volesti/include/convex_bodies/ellipsoid.h @@ -0,0 +1,281 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis +// Copyright (c) 2021 Vaibhav Thakkar + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +//Contributed and/or modified by Vaibhav Thakkar, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + + +#ifndef ELLIPSOIDS_H +#define ELLIPSOIDS_H + +#include +#include +#include "volume/math_helpers.hpp" + +/// This class represents an ellipsoid parameterized by a point type +/// \tparam Point Point type +template +class Ellipsoid{ +public: +typedef Point PointType; + typedef typename Point::FT NT; + typedef typename std::vector::iterator viterator; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + +private: + // representation is x'A x <= 1, i.e center is always assumed to be origin. + MT A; + + unsigned int _dim; + MT _L_cov; // LL' = inv(A) for sampling procedures + + // eigen vectors and values + VT _eigen_values; + VT _eigen_values_inv; + VT _eigen_values_inv_sqrt; + MT _Eigen_Vectors; + +public: + + Ellipsoid() {} + + // TODO(vaithak): Add a flag for telling whether the matrix passed is already inverse + Ellipsoid(MT& Ain) : A(Ain) { + Eigen::SelfAdjointEigenSolver eigensolver(A); + if (eigensolver.info() != Eigen::Success) { + throw std::runtime_error("Eigen solver returned error!"); + } + + _eigen_values = eigensolver.eigenvalues(); + _Eigen_Vectors = eigensolver.eigenvectors(); + + _eigen_values_inv = _eigen_values.array().inverse().matrix(); + _eigen_values_inv_sqrt = _eigen_values_inv.array().sqrt().matrix(); + + _dim = A.rows(); + + Eigen::LLT lltOfA(A.inverse()); // compute the Cholesky decomposition of inv(A) + if (lltOfA.info() != Eigen::Success) { + throw std::runtime_error("Cholesky decomposition failed!"); + } + _L_cov = lltOfA.matrixL(); + } + + + // Constructor for copula ellipsoid only + Ellipsoid(std::vector >& Ain) { + _dim = Ain.size(); + A.resize(_dim, _dim); + for (unsigned int i = 0; i < Ain.size(); i++) { + for (unsigned int j = 0; j < Ain.size(); j++) { + A(i,j) = Ain[i][j]; + } + } + } + + + NT radius() const { + return _eigen_values_inv_sqrt(0); + } + + + VT eigenvalues() const { + return _eigen_values; + } + + + VT eigenvalues_inv() const { + return _eigen_values_inv; + } + + + VT eigenvalues_inv_sqrt() const { + return _eigen_values_inv_sqrt; + } + + + MT eigenvectors() const { + return _Eigen_Vectors; + } + + + unsigned int dimensions() const { + return _dim; + } + + + MT Lcov() const { + return _L_cov; + } + + + // return L_cov * x + VT mult_Lcov(VT const& x) const { + return _L_cov.template triangularView() * x; + } + + + void print() const { + std::cout << "Ellipse is in the form: x' A x <= 1, (center is assumed to be origin always) \n"; + std::cout << "A = \n" << A; + } + + + + NT mat_mult(Point const& p) const { + VT x = p.getCoefficients(); + return x.transpose() * A.template selfadjointView() * x; + } + + + VT vec_mult(VT const& b) const { + return A.template selfadjointView()*b; + } + + + NT log_volume() const { + NT ball_log_vol = (NT(_dim)/NT(2) * std::log(M_PI)) - log_gamma_function(NT(_dim) / NT(2) + 1); + NT det_factor = std::log( _eigen_values_inv_sqrt.prod() ); + + return det_factor + ball_log_vol; + } + + + void scale(NT scale_factor) { + assert (scale_factor > 0); + + NT scale_factor_sq = scale_factor * scale_factor; + NT inv_scale_factor = (NT(1.0) / scale_factor); + NT inv_scale_factor_sq = (NT(1.0) / scale_factor_sq); + + _eigen_values = inv_scale_factor_sq * _eigen_values; + _eigen_values_inv = scale_factor_sq * _eigen_values_inv; + _eigen_values_inv_sqrt = scale_factor * _eigen_values_inv_sqrt; + _L_cov = scale_factor * _L_cov; + + A = inv_scale_factor_sq * A; // as volume depends on square root of it's determinant + } + + + int is_in(Point const& p) const { + return mat_mult(p) > 1 ? 0 : -1; + } + + + // compute intersection point of ray starting from r and pointing to v + std::pair line_intersect(Point const& r, Point const& v) const { + // constants of a quadratic equation + NT a_q = mat_mult(v); + NT b_q = 2 * r.getCoefficients().dot(vec_mult(v.getCoefficients())); + NT c_q = mat_mult(r) - 1; + + NT D = std::pow(b_q, 2) - 4*a_q*c_q; + return std::pair ( (-b_q + std::sqrt(D))/(2*a_q) , (-b_q - std::sqrt(D))/(2*a_q) ); + } + + + std::pair line_intersect(Point const& r, + Point const& v, + const VT &Ar, + const VT &Av) const + { + return line_intersect(r, v); + } + + + std::pair line_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT &lambda_prev) const + { + return line_intersect(r, v); + } + + + std::pair line_positive_intersect(Point const& r, + Point const& v) const + { + NT res = line_intersect(r, v).first; + return std::pair(res, 0); + } + + + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av) const + { + return line_positive_intersect(r, v); + } + + + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev) const + { + return line_positive_intersect(r, v); + } + + + // Compute the intersection of a coordinate ray + std::pair line_intersect_coord(Point const& r, const unsigned int rand_coord) const { + NT a_q = A(rand_coord, rand_coord); + NT b_q = 2 * r.getCoefficients().dot(A.col(rand_coord)); + NT c_q = mat_mult(r) - 1; + + NT D = std::pow(b_q, 2) - 4*a_q*c_q; + return std::pair ( (-b_q + std::sqrt(D))/(2*a_q) , (-b_q - std::sqrt(D))/(2*a_q) ); + } + + + std::pair line_intersect_coord(Point const& r, + unsigned int const& rand_coord, + VT& lamdas) const + { + return line_intersect_coord(r, rand_coord); + } + + + std::pair line_intersect_coord(Point const& r, + Point const& r_prev, + unsigned int const& rand_coord, + unsigned int const& rand_coord_prev, + VT& lamdas) const + { + return line_intersect_coord(r, rand_coord); + } + + + void compute_reflection (Point& v, Point const& p) const + { + // normal vector is Ap + Point s(vec_mult(p.getCoefficients())); + s *= (1.0 / s.length()); + s *= (-2.0 * v.dot(s)); + v += s; + } + + + template + void compute_reflection (Point& v, Point const& p, update_parameters ¶ms) const + { + // normal vector is Ap + Point s(vec_mult(p.getCoefficients())); + params.ball_inner_norm = s.length(); + + params.inner_vi_ak = v.dot(s) / params.ball_inner_norm; + v += (s * (-2.0 * params.inner_vi_ak * (1.0 / params.ball_inner_norm))); + } +}; + +#endif \ No newline at end of file diff --git a/src/volesti/include/convex_bodies/hpolytope.h b/src/volesti/include/convex_bodies/hpolytope.h new file mode 100644 index 00000000..49d94282 --- /dev/null +++ b/src/volesti/include/convex_bodies/hpolytope.h @@ -0,0 +1,927 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018-19 programs. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef HPOLYTOPE_H +#define HPOLYTOPE_H + +#include +#include +#include +#include "preprocess/max_inscribed_ball.hpp" +#include "root_finders/quadratic_polynomial_solvers.hpp" +#ifndef DISABLE_LPSOLVE + #include "lp_oracles/solve_lp.h" +#endif + + + +// check if an Eigen vector contains NaN or infinite values +template +bool is_inner_point_nan_inf(VT const& p) +{ + typedef Eigen::Array VTint; + VTint a = p.array().isNaN(); + for (int i = 0; i < p.rows(); i++) { + if (a(i) || std::isinf(p(i))){ + return true; + } + } + return false; +} + +/// This class describes a polytope in H-representation or an H-polytope +/// i.e. a polytope defined by a set of linear inequalities +/// \tparam Point Point type +template +class HPolytope { +public: + typedef Point PointType; + typedef typename Point::FT NT; + typedef typename std::vector::iterator viterator; + //using RowMatrixXd = Eigen::Matrix; + //typedef RowMatrixXd MT; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + +private: + unsigned int _d; //dimension + MT A; //matrix A + VT b; // vector b, s.t.: Ax<=b + std::pair _inner_ball; + +public: + //TODO: the default implementation of the Big3 should be ok. Recheck. + HPolytope() {} + + HPolytope(unsigned d_, MT const& A_, VT const& b_) : + _d{d_}, A{A_}, b{b_} + { + } + + // Copy constructor + HPolytope(HPolytope const& p) : + _d{p._d}, A{p.A}, b{p.b}, _inner_ball{p._inner_ball} + { + } + + //define matrix A and vector b, s.t. Ax<=b, + // from a matrix that contains both A and b, i.e., [A | b ] + HPolytope(std::vector> const& Pin) + { + _d = Pin[0][1] - 1; + A.resize(Pin.size() - 1, _d); + b.resize(Pin.size() - 1); + for (unsigned int i = 1; i < Pin.size(); i++) { + b(i - 1) = Pin[i][0]; + for (unsigned int j = 1; j < _d + 1; j++) { + A(i - 1, j - 1) = -Pin[i][j]; + } + } + _inner_ball.second = -1; + //_inner_ball = ComputeChebychevBall(A, b); + } + + + std::pair InnerBall() const + { + return _inner_ball; + } + + void set_InnerBall(std::pair const& innerball) //const + { + _inner_ball = innerball; + } + + void set_interior_point(Point const& r) + { + _inner_ball.first = r; + } + + //Compute Chebyshev ball of H-polytope P:= Ax<=b + //Use LpSolve library + std::pair ComputeInnerBall() + { + normalize(); + #ifndef DISABLE_LPSOLVE + _inner_ball = ComputeChebychevBall(A, b); // use lpsolve library + #else + + if (_inner_ball.second <= NT(0)) { + + NT const tol = 0.00000001; + std::tuple inner_ball = max_inscribed_ball(A, b, 150, tol); + + // check if the solution is feasible + if (is_in(Point(std::get<0>(inner_ball))) == 0 || std::get<1>(inner_ball) < NT(0) || + std::isnan(std::get<1>(inner_ball)) || std::isinf(std::get<1>(inner_ball)) || + !std::get<2>(inner_ball) || is_inner_point_nan_inf(std::get<0>(inner_ball))) + { + _inner_ball.second = -1.0; + } else + { + _inner_ball.first = Point(std::get<0>(inner_ball)); + _inner_ball.second = std::get<1>(inner_ball); + } + } + #endif + + return _inner_ball; + } + + // return dimension + unsigned int dimension() const + { + return _d; + } + + + // return the number of facets + int num_of_hyperplanes() const + { + return A.rows(); + } + + int num_of_generators() const + { + return 0; + } + + + // return the matrix A + MT get_mat() const + { + return A; + } + + + MT get_AA() const { + return A * A.transpose(); + } + + // return the vector b + VT get_vec() const + { + return b; + } + + + // change the matrix A + void set_mat(MT const& A2) + { + A = A2; + } + + + // change the vector b + void set_vec(VT const& b2) + { + b = b2; + } + + Point get_mean_of_vertices() const + { + return Point(_d); + } + + NT get_max_vert_norm() const + { + return 0.0; + } + + + // print polytope in input format + void print() { + std::cout << " " << A.rows() << " " << _d << " double" << std::endl; + for (unsigned int i = 0; i < A.rows(); i++) { + for (unsigned int j = 0; j < _d; j++) { + std::cout << A(i, j) << " "; + } + std::cout << "<= " << b(i) << std::endl; + } + } + + + // Compute the reduced row echelon form + // used to transofm {Ax=b,x>=0} to {A'x'<=b'} + // e.g. Birkhoff polytopes + /* + // Todo: change the implementation in order to use eigen matrix and vector. + int rref(){ + to_reduced_row_echelon_form(_A); + std::vector zeros(_d+1,0); + std::vector ones(_d+1,0); + std::vector zerorow(_A.size(),0); + for (int i = 0; i < _A.size(); ++i) + { + for (int j = 0; j < _d+1; ++j){ + if ( _A[i][j] == double(0)){ + ++zeros[j]; + ++zerorow[i]; + } + if ( _A[i][j] == double(1)){ + ++ones[j]; + } + } + } + for(typename stdMatrix::iterator mit=_A.begin(); mit<_A.end(); ++mit){ + int j =0; + for(typename stdCoeffs::iterator lit=mit->begin(); litend() ; ){ + if(zeros[j]==_A.size()-1 && ones[j]==1) + (*mit).erase(lit); + else{ //reverse sign in all but the first column + if(lit!=mit->end()-1) *lit = (-1)*(*lit); + ++lit; + } + ++j; + } + } + //swap last and first columns + for(typename stdMatrix::iterator mit=_A.begin(); mit<_A.end(); ++mit){ + double temp=*(mit->begin()); + *(mit->begin())=*(mit->end()-1); + *(mit->end()-1)=temp; + } + //delete zero rows + for (typename stdMatrix::iterator mit=_A.begin(); mit<_A.end(); ){ + int zero=0; + for(typename stdCoeffs::iterator lit=mit->begin(); litend() ; ++lit){ + if(*lit==double(0)) ++zero; + } + if(zero==(*mit).size()) + _A.erase(mit); + else + ++mit; + } + //update _d + _d=(_A[0]).size(); + // add unit vectors + for(int i=1;i<_d;++i){ + std::vector e(_d,0); + e[i]=1; + _A.push_back(e); + } + // _d should equals the dimension + _d=_d-1; + return 1; + }*/ + + + //Check if Point p is in H-polytope P:= Ax<=b + int is_in(Point const& p, NT tol=NT(0)) const + { + int m = A.rows(); + const NT* b_data = b.data(); + + for (int i = 0; i < m; i++) { + //Check if corresponding hyperplane is violated + if (*b_data - A.row(i) * p.getCoefficients() < NT(-tol)) + return 0; + + b_data++; + } + return -1; + } + + // compute intersection point of ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_intersect(Point const& r, Point const& v) const + { + + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_nom, sum_denom; + //unsigned int i, j; + unsigned int j; + int m = num_of_hyperplanes(); + + + sum_nom.noalias() = b - A * r.getCoefficients(); + sum_denom.noalias() = A * v.getCoefficients(); + + NT* sum_nom_data = sum_nom.data(); + NT* sum_denom_data = sum_denom.data(); + + for (int i = 0; i < m; i++) { + + if (*sum_denom_data == NT(0)) { + //std::cout<<"div0"< 0) min_plus = lamda; + if (lamda > max_minus && lamda < 0) max_minus = lamda; + } + + sum_nom_data++; + sum_denom_data++; + } + return std::make_pair(min_plus, max_minus); + } + + // compute intersection points of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + bool pos = false) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_nom; + int m = num_of_hyperplanes(), facet; + + Ar.noalias() = A * r.getCoefficients(); + sum_nom = b - Ar; + Av.noalias() = A * v.getCoefficients();; + + + NT* Av_data = Av.data(); + NT* sum_nom_data = sum_nom.data(); + + for (int i = 0; i < m; i++) { + if (*Av_data == NT(0)) { + //std::cout<<"div0"< 0) { + min_plus = lamda; + if (pos) facet = i; + }else if (lamda > max_minus && lamda < 0) max_minus = lamda; + } + + Av_data++; + sum_nom_data++; + } + if (pos) return std::make_pair(min_plus, facet); + return std::make_pair(min_plus, max_minus); + } + + std::pair line_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + bool pos = false) const + { + + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_nom; + NT mult; + //unsigned int i, j; + unsigned int j; + int m = num_of_hyperplanes(), facet; + + Ar.noalias() += lambda_prev*Av; + sum_nom = b - Ar; + Av.noalias() = A * v.getCoefficients(); + + NT* sum_nom_data = sum_nom.data(); + NT* Av_data = Av.data(); + + for (int i = 0; i < m; i++) { + if (*Av_data == NT(0)) { + //std::cout<<"div0"< 0) { + min_plus = lamda; + if (pos) facet = i; + }else if (lamda > max_minus && lamda < 0) max_minus = lamda; + } + Av_data++; + sum_nom_data++; + } + if (pos) return std::make_pair(min_plus, facet); + return std::make_pair(min_plus, max_minus); + } + + + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av) const + { + return line_intersect(r, v, Ar, Av, true); + } + + + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev) const + { + return line_intersect(r, v, Ar, Av, lambda_prev, true); + } + + + //---------------------------accelarated billiard---------------------------------- + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + template + std::pair line_first_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + update_parameters& params) const + { + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + + NT lamda = 0; + VT sum_nom; + int m = num_of_hyperplanes(), facet; + + Ar.noalias() = A * r.getCoefficients(); + sum_nom.noalias() = b - Ar; + Av.noalias() = A * v.getCoefficients(); + + NT* Av_data = Av.data(); + NT* sum_nom_data = sum_nom.data(); + + for (int i = 0; i < m; i++) { + if (*Av_data == NT(0)) { + //std::cout<<"div0"< 0) { + min_plus = lamda; + facet = i; + params.inner_vi_ak = *Av_data; + } + } + + Av_data++; + sum_nom_data++; + } + params.facet_prev = facet; + return std::pair(min_plus, facet); + } + + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + MT const& AA, + update_parameters& params) const + { + + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + + NT lamda = 0; + NT inner_prev = params.inner_vi_ak; + VT sum_nom; + int m = num_of_hyperplanes(), facet; + + Ar.noalias() += lambda_prev*Av; + if(params.hit_ball) { + Av.noalias() += (-2.0 * inner_prev) * (Ar / params.ball_inner_norm); + } else { + Av.noalias() += (-2.0 * inner_prev) * AA.col(params.facet_prev); + } + sum_nom.noalias() = b - Ar; + + NT* sum_nom_data = sum_nom.data(); + NT* Av_data = Av.data(); + + for (int i = 0; i < m; i++) { + if (*Av_data == NT(0)) { + //std::cout<<"div0"< 0) { + min_plus = lamda; + facet = i; + params.inner_vi_ak = *Av_data; + } + } + Av_data++; + sum_nom_data++; + } + params.facet_prev = facet; + return std::pair(min_plus, facet); + } + + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + update_parameters& params) const + { + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + + NT lamda = 0; + VT sum_nom; + unsigned int j; + int m = num_of_hyperplanes(), facet; + + Ar.noalias() += lambda_prev*Av; + sum_nom.noalias() = b - Ar; + Av.noalias() = A * v.getCoefficients(); + + NT* sum_nom_data = sum_nom.data(); + NT* Av_data = Av.data(); + + for (int i = 0; i < m; i++) { + if (*Av_data == NT(0)) { + //std::cout<<"div0"< 0) { + min_plus = lamda; + facet = i; + params.inner_vi_ak = *Av_data; + } + } + Av_data++; + sum_nom_data++; + } + params.facet_prev = facet; + return std::pair(min_plus, facet); + } + + //-----------------------------------------------------------------------------------// + + + //First coordinate ray intersecting convex polytope + std::pair line_intersect_coord(Point const& r, + unsigned int const& rand_coord, + VT& lamdas) const + { + + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_denom; + + int m = num_of_hyperplanes(); + + sum_denom = A.col(rand_coord); + lamdas.noalias() = b - A * r.getCoefficients(); + + NT* lamda_data = lamdas.data(); + NT* sum_denom_data = sum_denom.data(); + + for (int i = 0; i < m; i++) { + + if (*sum_denom_data == NT(0)) { + //std::cout<<"div0"< 0) min_plus = lamda; + if (lamda > max_minus && lamda < 0) max_minus = lamda; + + } + lamda_data++; + sum_denom_data++; + } + return std::make_pair(min_plus, max_minus); + } + + + //Not the first coordinate ray intersecting convex + std::pair line_intersect_coord(Point const& r, + Point const& r_prev, + unsigned int const& rand_coord, + unsigned int const& rand_coord_prev, + VT& lamdas) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + + int m = num_of_hyperplanes(); + + lamdas.noalias() += A.col(rand_coord_prev) + * (r_prev[rand_coord_prev] - r[rand_coord_prev]); + NT* data = lamdas.data(); + + for (int i = 0; i < m; i++) { + NT a = A(i, rand_coord); + + if (a == NT(0)) { + //std::cout<<"div0"< 0) min_plus = lamda; + if (lamda > max_minus && lamda < 0) max_minus = lamda; + + } + data++; + } + return std::make_pair(min_plus, max_minus); + } + + + //------------------------------oracles for exponential sampling---------------////// + + std::pair get_positive_quadratic_root(Point const& r, //current poistion + Point const& v, // current velocity + VT& Ac, // the product Ac where c is the bias vector of the exponential distribution + NT const& T, // the variance of the exponential distribution + VT& Ar, // the product Ar + VT& Av, // the product Av + int& facet_prev) const //the facet that the trajectory hit in the previous reflection + { + NT lamda = 0; + NT lamda2 =0; + NT lamda1 =0; + NT alpha; + NT min_plus = std::numeric_limits::max(); + VT sum_nom; + int m = num_of_hyperplanes(); + int facet = -1; + + sum_nom = Ar - b; + Av.noalias() = A * v.getCoefficients();; + + NT* Av_data = Av.data(); + NT* sum_nom_data = sum_nom.data(); + NT* Ac_data = Ac.data(); + + for (int i = 0; i < m; i++) + { + alpha = -((*Ac_data) / (2.0 * T)); + if (solve_quadratic_polynomial(alpha, (*Av_data), (*sum_nom_data), lamda1, lamda2)) + { + lamda = pick_first_intersection_time_with_boundary(lamda1, lamda2, i, facet_prev); + if (lamda < min_plus && lamda > 0) + { + min_plus = lamda; + facet = i; + } + } + Av_data++; + sum_nom_data++; + Ac_data++; + } + facet_prev = facet; + return std::make_pair(min_plus, facet); + } + + + // compute intersection points of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair quadratic_positive_intersect(Point const& r, //current poistion + Point const& v, // current velocity + VT& Ac, // the product Ac where c is the bias vector of the exponential distribution + NT const& T, // the variance of the exponential distribution + VT& Ar, // the product Ar + VT& Av, // the product Av + int& facet_prev) const //the facet that the trajectory hit in the previous reflection + { + Ar.noalias() = A * r.getCoefficients(); + return get_positive_quadratic_root(r, v, Ac, T, Ar, Av, facet_prev); + } + + std::pair quadratic_positive_intersect(Point const& r, //current poistion + Point const& v, // current velocity + VT& Ac, // the product Ac where c is the bias vector of the exponential distribution + NT const& T, // the variance of the exponential distribution + VT& Ar, // the product Ar + VT& Av, // the product Av + NT const& lambda_prev, // the intersection time of the previous reflection + int& facet_prev) const //the facet that the trajectory hit in the previous reflection + { + Ar.noalias() += ((lambda_prev * lambda_prev) / (-2.0*T)) * Ac + lambda_prev * Av; + return get_positive_quadratic_root(r, v, Ac, T, Ar, Av, facet_prev); + } + + NT pick_first_intersection_time_with_boundary(NT const& lamda1, NT const& lamda2, int const& current_facet, int const& previous_facet) const + { + if (lamda1 == lamda2) + { + return lamda1; + } + NT lamda; + const double tol = 1e-10; + std::pair minmax_values = std::minmax(lamda1, lamda2); + + lamda = (previous_facet == current_facet) + ? minmax_values.second < NT(tol) ? minmax_values.first : minmax_values.second + : minmax_values.second; + + if (lamda1 * lamda2 < NT(0)) + { + lamda = (previous_facet == current_facet) + ? (minmax_values.second < NT(tol)) ? minmax_values.first : minmax_values.second + : minmax_values.second; + } + else + { + lamda = (previous_facet == current_facet) + ? (minmax_values.first >= NT(0) && minmax_values.first < NT(tol)) + ? minmax_values.second : minmax_values.first + : minmax_values.first; + } + return lamda; + } + + + //------------oracle for exact hmc spherical gaussian sampling---------------// + + // compute intersection point of ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair trigonometric_positive_intersect(Point const& r, Point const& v, + NT const& omega, int &facet_prev) const + { + + NT lamda = 0, C, Phi, t1, t2, tmin; + NT min_plus = std::numeric_limits::max(), t = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_nom, sum_denom; + unsigned int j; + int m = num_of_hyperplanes(), facet = -1; + + + sum_nom.noalias() = A * r.getCoefficients(); + sum_denom.noalias() = A * v.getCoefficients(); + + NT* sum_nom_data = sum_nom.data(); + NT* sum_denom_data = sum_denom.data(); + const NT* b_data = b.data(); + + for (int i = 0; i < m; i++) { + + C = std::sqrt((*sum_nom_data) * (*sum_nom_data) + ((*sum_denom_data) * (*sum_denom_data)) / (omega * omega)); + Phi = std::atan((-(*sum_denom_data)) / ((*sum_nom_data) * omega)); + if ((*sum_denom_data) < 0.0 && Phi < 0.0) { + Phi += M_PI; + } else if ((*sum_denom_data) > 0.0 && Phi > 0.0) { + Phi -= M_PI; + } + + if (C > (*b_data)) { + NT acos_b = std::acos((*b_data) / C); + t1 = (acos_b - Phi) / omega; + if (facet_prev == i && std::abs(t1) < 1e-10){ + t1 = (2.0 * M_PI) / omega; + } + + t2 = (-acos_b - Phi) / omega; + if (facet_prev == i && std::abs(t2) < 1e-10){ + t2 = (2.0 * M_PI) / omega; + } + + t1 += (t1 < NT(0)) ? (2.0 * M_PI) / omega : NT(0); + t2 += (t2 < NT(0)) ? (2.0 * M_PI) / omega : NT(0); + + tmin = std::min(t1, t2); + + if (tmin < t && tmin > NT(0)) { + facet = i; + t = tmin; + } + } + + sum_nom_data++; + sum_denom_data++; + b_data++; + } + facet_prev = facet; + return std::make_pair(t, facet); + } + + + // Apply linear transformation, of square matrix T^{-1}, in H-polytope P:= Ax<=b + void linear_transformIt(MT const& T) + { + A = A * T; + } + + + // shift polytope by a point c + + void shift(const VT &c) + { + b -= A*c; + } + + + // return for each facet the distance from the origin + std::vector get_dists(NT const& radius) const + { + unsigned int i=0; + std::vector dists(num_of_hyperplanes(), NT(0)); + typename std::vector::iterator disit = dists.begin(); + for ( ; disit!=dists.end(); disit++, i++) + *disit = b(i) / A.row(i).norm(); + + return dists; + } + + // no points given for the rounding, you have to sample from the polytope + template + bool get_points_for_rounding (T const& /*randPoints*/) + { + return false; + } + + MT get_T() const + { + return A; + } + + void normalize() + { + NT row_norm; + for (int i = 0; i < num_of_hyperplanes(); ++i) + { + row_norm = A.row(i).norm(); + A.row(i) = A.row(i) / row_norm; + b(i) = b(i) / row_norm; + } + } + + void compute_reflection(Point& v, Point const&, int const& facet) const + { + v += -2 * v.dot(A.row(facet)) * A.row(facet); + } + + void resetFlags() {} + + NT log_barrier(Point &x, NT t = NT(100)) const { + int m = num_of_hyperplanes(); + NT total = NT(0); + NT slack; + + for (int i = 0; i < m; i++) { + slack = b(i) - x.dot(A.row(i)); + total += log(slack); + } + + return total / t; + } + + Point grad_log_barrier(Point &x, NT t = NT(100)) { + int m = num_of_hyperplanes(); + NT slack; + + Point total(x.dimension()); + + for (int i = 0; i < m; i++) { + slack = b(i) - x.dot(A.row(i)); + total = total + (1 / slack) * A.row(i); + } + total = (1.0 / t) * total; + return total; + } + + template + void compute_reflection(Point &v, const Point &, update_parameters const& params) const { + + Point a((-2.0 * params.inner_vi_ak) * A.row(params.facet_prev)); + v += a; + } + + void update_position_internal(NT&){} + + template + std::tuple curve_intersect( + NT t_prev, + NT t0, + NT eta, + std::vector &coeffs, + bfunc phi, + bfunc grad_phi, + NonLinearOracle &intersection_oracle, + int ignore_facet=-1) + { + return intersection_oracle.apply(t_prev, t0, eta, A, b, *this, + coeffs, phi, grad_phi, ignore_facet); + } +}; + +#endif diff --git a/src/volesti/include/convex_bodies/orderpolytope.h b/src/volesti/include/convex_bodies/orderpolytope.h new file mode 100644 index 00000000..2df5c661 --- /dev/null +++ b/src/volesti/include/convex_bodies/orderpolytope.h @@ -0,0 +1,755 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2021 Vaibhav Thakkar + +// Contributed and/or modified by Vaibhav Thakkar, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ORDER_POLYTOPE_H +#define ORDER_POLYTOPE_H + +#include +#include "misc/poset.h" +#include +#include "preprocess/max_inscribed_ball.hpp" +#ifndef DISABLE_LPSOLVE + #include "lp_oracles/solve_lp.h" +#endif + +/// This class represents an order polytope parameterized by a point type +/// \tparam Point Point type +template +class OrderPolytope { +public: + typedef Point PointType; + typedef typename Point::FT NT; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + +private: + Poset _poset; + unsigned int _d; // dimension + + VT b; + VT _row_norms; + MT _A; // representing as Ax <= b for ComputeInnerBall and printing + + unsigned int _num_hyperplanes; + bool _normalized; + +public: + OrderPolytope(Poset const& poset) : _poset(poset) + { + _d = _poset.num_elem(); + _num_hyperplanes = 2*_d + _poset.num_relations(); // 2*d are for >=0 and <=1 constraints + b = Eigen::MatrixXd::Zero(_num_hyperplanes, 1); + _A = Eigen::MatrixXd::Zero(_num_hyperplanes, _d); + _row_norms = Eigen::MatrixXd::Constant(_num_hyperplanes, 1, 1.0); + + // first add (ai >= 0) or (-ai <= 0) rows + _A.topLeftCorner(_d, _d) = -Eigen::MatrixXd::Identity(_d, _d); + + // next add (ai <= 1) rows + _A.block(_d, 0, _d, _d) = Eigen::MatrixXd::Identity(_d, _d); + b.block(_d, 0, _d, 1) = Eigen::MatrixXd::Constant(_d, 1, 1.0); + + // next add the relations + unsigned int num_relations = _poset.num_relations(); + for(int idx=0; idx curr_relation = _poset.get_relation(idx); + _A(2*_d + idx, curr_relation.first) = 1; + _A(2*_d + idx, curr_relation.second) = -1; + } + _row_norms.block(2*_d, 0, num_relations, 1) = Eigen::MatrixXd::Constant(num_relations, 1, sqrt(2)); + + _normalized = false; + } + + + // return dimension + unsigned int dimension() const + { + return _d; + } + + + // return number of hyperplanes + unsigned int num_of_hyperplanes() const + { + return _num_hyperplanes; + } + + + // get ith column of A + VT get_col (unsigned int i) const { + return _A.col(i); + } + + + Eigen::SparseMatrix get_mat() const + { + return _A.sparseView(); + } + + + VT get_vec() const + { + return b; + } + + + // print polytope in Ax <= b format + void print() const + { + std::cout << " " << _A.rows() << " " << _d << " double" << std::endl; + for (unsigned int i = 0; i < _A.rows(); i++) { + for (unsigned int j = 0; j < _d; j++) { + std::cout << _A(i, j) << " "; + } + std::cout << "<= " << b(i) << std::endl; + } + } + + + /** multiply the sparse matrix A of the order polytope by a vector x + * if transpose = false : return Ax + * else if transpose = true : return (A^T)x + */ + VT vec_mult(VT const& x, bool transpose=false) const + { + unsigned int rows = num_of_hyperplanes(); + unsigned int i = 0; + VT res; + if (!transpose) res = Eigen::MatrixXd::Zero(rows, 1); + else res = Eigen::MatrixXd::Zero(_d, 1); + + // ------- no effect of normalize on first 2*_d rows, norm = 1 --------- + // first _d rows of >=0 constraints + for(; i < _d; ++i) { + res(i) = (-1.0) * x(i); + } + + // next _d rows of <=1 constraints + for(; i < 2*_d; ++i) { + if (!transpose) { + res(i) = (1.0) * x(i - _d); + } + else { + res(i - _d) += (1.0) * x(i); + } + } + // ----------------------------------------------------------------- + + // next rows are for order relations + for(; i < rows; ++i) { + std::pair curr_relation = _poset.get_relation(i - 2*_d); + + if (!transpose) { + if (! _normalized) + res(i) = x(curr_relation.first) - x(curr_relation.second); + else + res(i) = (x(curr_relation.first) - x(curr_relation.second)) / _row_norms(i); + } + else { + if (! _normalized) { + res(curr_relation.first) += x(i); + res(curr_relation.second) -= x(i); + } + else { + res(curr_relation.first) += x(i) / _row_norms(i); + res(curr_relation.second) -= x(i) / _row_norms(i); + } + } + } + + return res; + } + + + //Check if Point p is in the order-polytope + int is_in(Point const& p, NT tol=NT(0)) const + { + assert(p.dimension() == _d); + + VT pt_coeffs = p.getCoefficients(); + NT diff; + + for (int i = 0; i < _d; i++) { + // DON'T JUST check violation of point between 0 and 1 + // as b will change for shifted polytope. + diff = -pt_coeffs(i) - b(i); + if (diff > NT(tol)) return 0; + + diff = pt_coeffs(i) - b(i + _d); + if (diff > NT(tol)) return 0; + } + + // check violations of order relations + // again note that b can be arbitrary because of shifting + unsigned int num_relations = _poset.num_relations(); + for(int idx=0; idx curr_relation = _poset.get_relation(idx); + diff = (pt_coeffs(curr_relation.first) - pt_coeffs(curr_relation.second)); + + if (_normalized) diff /= _row_norms(idx + 2*_d); + if((diff - b(idx + 2*_d)) > NT(tol)) + return 0; + } + + return -1; + } + + + //Compute Chebyshev ball of the polytope P:= Ax<=b + //Use LpSolve library + std::pair ComputeInnerBall() + { + normalize(); + std::pair inner_ball; + #ifndef DISABLE_LPSOLVE + inner_ball = ComputeChebychevBall(_A, b); // use lpsolve library + #else + + if (inner_ball.second <= NT(0)) { + + NT const tol = 0.00000001; + std::tuple inner_ball = max_inscribed_ball(_A, b, 150, tol); + + // check if the solution is feasible + if (is_in(Point(std::get<0>(inner_ball))) == 0 || std::get<1>(inner_ball) < NT(0) || + std::isnan(std::get<1>(inner_ball)) || std::isinf(std::get<1>(inner_ball)) || + !std::get<2>(inner_ball) || is_inner_point_nan_inf(std::get<0>(inner_ball))) + { + inner_ball.second = -1.0; + } else + { + inner_ball.first = Point(std::get<0>(inner_ball)); + inner_ball.second = std::get<1>(inner_ball); + } + } + #endif + + return inner_ball; + + } + + + // TODO: This can be removed as only modified Accelerated Billiard Walk will be used with Order Polytope + // compute intersection point of ray starting from r and pointing to v + // with the order polytope + std::pair line_intersect(Point const& r, Point const& v, bool pos = false) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_nom, sum_denom; + + int rows = num_of_hyperplanes(), facet; + + sum_nom.noalias() = b - vec_mult(r.getCoefficients()); + sum_denom.noalias() = vec_mult(v.getCoefficients()); + + NT* sum_nom_data = sum_nom.data(); + NT* sum_denom_data = sum_denom.data(); + + // iterate over all hyperplanes + for(unsigned int i = 0; i 0) { + min_plus = lamda; + if (pos) facet = i; + } + else if (lamda > max_minus && lamda < 0) { + max_minus = lamda; + } + } + + sum_nom_data++; + sum_denom_data++; + } + + if (pos) + return std::make_pair(min_plus, facet); + + return std::make_pair(min_plus, max_minus); + } + + + // TODO: This can be removed as only modified Accelerated Billiard Walk will be used with Order Polytope + // compute intersection point of ray starting from r and pointing to v + // with the order-polytope + std::pair line_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + bool pos = false) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_nom; + + int rows = num_of_hyperplanes(), facet; + + Ar.noalias() = vec_mult(r.getCoefficients()); + Av.noalias() = vec_mult(v.getCoefficients()); + + sum_nom.noalias() = b - Ar; + + NT* sum_nom_data = sum_nom.data(); + NT* sum_denom_data = Av.data(); + + // iterate over all hyperplanes + for(unsigned int i = 0; i 0) { + min_plus = lamda; + if (pos) facet = i; + } + else if (lamda > max_minus && lamda < 0) { + max_minus = lamda; + } + } + + sum_nom_data++; + sum_denom_data++; + } + + if (pos) + return std::make_pair(min_plus, facet); + + return std::make_pair(min_plus, max_minus); + } + + + // TODO: This can be removed as only modified Accelerated Billiard Walk will be used with Order Polytope + // compute intersection point of ray starting from r and pointing to v + // with the order-polytope + std::pair line_intersect(Point const& r, + Point const& v, + VT &Ar, + VT &Av, + NT const& lambda_prev, + bool pos = false) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_nom; + + int rows = num_of_hyperplanes(), facet; + + Ar.noalias() += lambda_prev*Av; + Av.noalias() = vec_mult(v.getCoefficients()); + + sum_nom.noalias() = b - Ar; + + NT* sum_nom_data = sum_nom.data(); + NT* sum_denom_data = Av.data(); + + // iterate over all hyperplanes + for(unsigned int i = 0; i 0) { + min_plus = lamda; + if (pos) facet = i; + } + else if (lamda > max_minus && lamda < 0) { + max_minus = lamda; + } + } + + sum_nom_data++; + sum_denom_data++; + } + + if (pos) + return std::make_pair(min_plus, facet); + + return std::make_pair(min_plus, max_minus); + } + + + // TODO: This can be removed as only modified Accelerated Billiard Walk will be used with Order Polytope + // compute intersection point of a ray starting from r and pointing to v + // with the order-polytope + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av) const + { + return line_intersect(r, v, Ar, Av, true); + } + + + // TODO: This can be removed as only modified Accelerated Billiard Walk will be used with Order Polytope + // compute intersection point of a ray starting from r and pointing to v + // with the order-polytope + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev) const + { + return line_intersect(r, v, Ar, Av, lambda_prev, true); + } + + + //-------------------------accelerated billiard--------------------------------// + // compute intersection point of a ray starting from r and pointing to v + // with the order-polytope + template + std::pair line_first_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + update_parameters ¶ms) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + VT sum_nom; + + int rows = num_of_hyperplanes(), facet; + + Ar.noalias() = vec_mult(r.getCoefficients()); + Av.noalias() = vec_mult(v.getCoefficients()); + + sum_nom.noalias() = b - Ar; + + NT* sum_nom_data = sum_nom.data(); + NT* sum_denom_data = Av.data(); + + // iterate over all hyperplanes + for(unsigned int i = 0; i 0) { + min_plus = lamda; + facet = i; + params.inner_vi_ak = *sum_denom_data; + } + } + + sum_nom_data++; + sum_denom_data++; + } + + params.facet_prev = facet; + return std::make_pair(min_plus, facet); + } + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + MT const& AA, + update_parameters ¶ms) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + VT sum_nom; + + int rows = num_of_hyperplanes(), facet; + NT inner_prev = params.inner_vi_ak; + + Ar.noalias() += lambda_prev*Av; + if(params.hit_ball) { + Av.noalias() += (-2.0 * inner_prev) * (Ar / params.ball_inner_norm); + } else { + Av.noalias() += (-2.0 * inner_prev) * AA.col(params.facet_prev); + } + sum_nom.noalias() = b - Ar; + + NT* sum_nom_data = sum_nom.data(); + NT* sum_denom_data = Av.data(); + + // iterate over all hyperplanes + for(unsigned int i = 0; i 0) { + min_plus = lamda; + facet = i; + params.inner_vi_ak = *sum_denom_data; + } + } + + sum_nom_data++; + sum_denom_data++; + } + + params.facet_prev = facet; + return std::make_pair(min_plus, facet); + } + + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + update_parameters ¶ms) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + VT sum_nom; + + int rows = num_of_hyperplanes(), facet; + + Ar.noalias() += lambda_prev*Av; + Av.noalias() = vec_mult(v.getCoefficients()); + + sum_nom.noalias() = b - Ar; + + NT* sum_nom_data = sum_nom.data(); + NT* sum_denom_data = Av.data(); + + // iterate over all hyperplanes + for(unsigned int i = 0; i 0) { + min_plus = lamda; + facet = i; + params.inner_vi_ak = *sum_denom_data; + } + } + + sum_nom_data++; + sum_denom_data++; + } + + params.facet_prev = facet; + return std::make_pair(min_plus, facet); + } + //------------------------------------------------------------------------------// + + + // TODO: This can be removed as only modified Accelerated Billiard Walk will be used with Order Polytope + // Compute the intersection of a coordinate ray + // with the order polytope + std::pair line_intersect_coord(Point const& r, + unsigned int const& rand_coord, + VT& lamdas) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + VT sum_denom; + + int rows = num_of_hyperplanes(); + + sum_denom = get_col(rand_coord); + lamdas = b - vec_mult(r.getCoefficients()); + + NT* sum_nom_data = lamdas.data(); + NT* sum_denom_data = sum_denom.data(); + + // iterate over all hyperplanes + for(unsigned int i = 0; i 0) { + min_plus = lamda; + } + else if (lamda > max_minus && lamda < 0) { + max_minus = lamda; + } + } + + sum_nom_data++; + sum_denom_data++; + } + + return std::make_pair(min_plus, max_minus); + } + + + // TODO: This can be removed as only modified Accelerated Billiard Walk will be used with Order Polytope + std::pair line_intersect_coord(Point const& r, + Point const& r_prev, + unsigned int const& rand_coord, + unsigned int const& rand_coord_prev, + VT& lamdas) const + { + NT lamda = 0; + NT min_plus = std::numeric_limits::max(); + NT max_minus = std::numeric_limits::lowest(); + + int rows = num_of_hyperplanes(); + + lamdas.noalias() += get_col(rand_coord_prev) + * (r_prev[rand_coord_prev] - r[rand_coord_prev]); + NT* sum_nom_data = lamdas.data(); + + // iterate over all hyperplanes + for(unsigned int i = 0; i 0) { + min_plus = lamda; + } + else if (lamda > max_minus && lamda < 0) { + max_minus = lamda; + } + } + + sum_nom_data++; + } + + return std::make_pair(min_plus, max_minus); + } + + + // no points given for the rounding, you have to sample from the polytope + template + bool get_points_for_rounding (T const& /*randPoints*/) + { + return false; + } + + + // shift polytope by a point c + void shift(VT const& c) + { + b -= vec_mult(c); + } + + + // get a point inside the order polytope, + // NOTE: the current implementation only works for non shifted order polytope + VT inner_point() + { + // get topologically sorted list of indices + std::vector sorted_list = _poset.topologically_sorted_list(); + + // vector to hold n linearly spaced values between 0-1 + std::vector lin_space_values(_d); + NT start = 0.05, end = 0.95; + NT h = (end - start)/static_cast(_d-1); + NT val = start; + for(auto x=lin_space_values.begin(); x!=lin_space_values.end(); ++x) { + *x = val; + val += h; + } + + // final result vector + VT res(_d); + for(int i=0; i<_d; ++i) { + unsigned int curr_idx = sorted_list[i]; + res(curr_idx) = lin_space_values[i]; + } + + return res; + } + + + void normalize() + { + if (_normalized == true) + return; + + // for b and _A, first 2*_d rows are already normalized, for + _normalized = true; // -> will be used to make normalization idempotent + for (unsigned int i = 0; i < _num_hyperplanes; ++i) + { + _A.row(i) /= _row_norms(i); + b(i) /= _row_norms(i); + } + } + + + // return for each facet the distance from the origin + std::vector get_dists(NT const& radius) const + { + std::vector dists(_num_hyperplanes, NT(0)); + + for (unsigned int i = 0; i < _num_hyperplanes; ++i) { + if (_normalized) + dists[i] = b(i); + else + dists[i] = b(i) / _row_norms(i); + } + + return dists; + } + + + // compute reflection given dot product and facet + void compute_reflection(Point& v, NT dot_prod, unsigned int facet) const + { + // calculating -> v += -2 * dot_prod * A.row(facet); + if (facet < _d) { + v.set_coord(facet, v[facet] - 2 * dot_prod * (-1.0)); + } + else if (facet < 2*_d) { + v.set_coord(facet-_d, v[facet-_d] - 2 * dot_prod * (1.0)); + } + else { + std::pair curr_relation = _poset.get_relation(facet - 2*_d); + v.set_coord(curr_relation.first, v[curr_relation.first] - 2 * dot_prod * (1.0 / _row_norms(facet))); + v.set_coord(curr_relation.second, v[curr_relation.second] - 2 * dot_prod * (-1.0 / _row_norms(facet))); + } + } + + + // compute reflection in O(1) time for order polytope + void compute_reflection(Point& v, Point const&, unsigned int facet) const + { + NT dot_prod; + if (facet < _d) { + dot_prod = -v[facet]; + } + else if (facet < 2*_d) { + dot_prod = v[facet - _d]; + } + else { + std::pair curr_relation = _poset.get_relation(facet - 2*_d); + dot_prod = v[curr_relation.first] - v[curr_relation.second]; + dot_prod = dot_prod / _row_norms(facet); + } + + compute_reflection(v, dot_prod, facet); + } + + + template + void compute_reflection(Point &v, Point const&, update_parameters const& params) const + { + NT dot_prod = params.inner_vi_ak; + unsigned int facet = params.facet_prev; + compute_reflection(v, dot_prod, facet); + } +}; + +#endif diff --git a/src/volesti/include/convex_bodies/spectrahedra/LMI.h b/src/volesti/include/convex_bodies/spectrahedra/LMI.h new file mode 100644 index 00000000..5ad879fc --- /dev/null +++ b/src/volesti/include/convex_bodies/spectrahedra/LMI.h @@ -0,0 +1,251 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_LMI_H +#define VOLESTI_LMI_H + +#include "matrix_operations/EigenvaluesProblems.h" + + +template +struct evaluate_lmi { + +}; + +template +struct evaluate_lmi > { +public: + typedef Eigen::Matrix MT; + /// The type for Eigen vector + typedef Eigen::Matrix VT; + + MT vectorMatrix; + + int _m, _d; + + /// Create the vectorMatrix, which has at each column the distinct elements of each A_i, i=1,...,d + void setVectorMatrix(int const& m, int const& d, std::vector &matrices) + { + _m = m; + _d = d; + int newM = m * (m + 1) / 2; + + // allocate memory + vectorMatrix.setZero(newM, d); + //a.setZero(m); + + // initialze iterator and skip A_0 + typename std::vector::iterator iter = matrices.begin(); + iter++; + + // copy elements + int atMatrix = 0; + + for (; iter != matrices.end(); iter++, atMatrix++) { + int i = 0; + + for (int at_row = 0; at_row < m; at_row++) + for (int at_col = at_row; at_col < m; at_col++) { + vectorMatrix(i++, atMatrix) = (*iter)(at_row, at_col); + } + + } + } + + /// Compute \[x_1*A_1 + ... + x_n A_n] + /// \param[in] x Input vector + /// \param[out] res Output matrix + void evaluateWithoutA0(const VT &x, MT& res, bool complete_mat = false) const { + //#define EVALUATE_WITHOUT_A0_NAIVE + #if defined(EVALUATE_WITHOUT_A0_NAIVE) + res.setZero(_m, _m); + typename std::vector::iterator it; + + int i = 0; + it = matrices.begin(); + ++it; // skip A0 + for (; it != matrices.end(); it++, i++) + res.noalias() += x(i) * (*it); + #else + + VT a = vectorMatrix * x; + + double *data = res.data(); + double *v = a.data(); + + int at = 0; + + // copy lower triangular + for (int at_col = 0; at_col < _m; at_col++) { + int col_offset = at_col * _m; + double *target = data + col_offset + at_col; + + for (int at_row = at_col; at_row < _m; at_row++) { + *(target++) = *(v++); + } + } + + if(complete_mat) { + v = a.data(); + + // copy upper triangular + for (int at_row = 0; at_row < _m; at_row++) { + double *target = data + at_row + at_row * _m; + + for (int at_col = at_row; at_col < _m; at_col++) { + *target = *(v++); + target = target + _m; + } + } + } + #endif + //return true; + } + +}; + + +/// This class handles a linear matrix inequality of the form \[A_0 + \sum x_i A_i\] +/// A template specialization for dense Eigen matrices and vectors +/// @tparam NT Numeric Type +/// @tparam MT Matrix Type +/// @tparam VT Vector Type +template +class LMI { + public: + + evaluate_lmi lmi_evaluator; + + /// The matrices A_0, A_i + std::vector matrices; + + /// The dimension of the vector x + unsigned int d; + + /// The size of the matrices A_i + unsigned int m; + + /// At each column keep the m*(m+1)/2 distinct elements of each matrix A_i, i=1,...,d + MT vectorMatrix; + + LMI(){} + + /// Creates A LMI object + /// \param[in] matrices The matrices A_0, A_i + LMI(std::vector& matrices) { + typename std::vector::iterator it = matrices.begin(); + + while (it!=matrices.end()) { + this->matrices.push_back(*it); + it++; + } + + d = matrices.size() - 1; + m = matrices[0].rows(); + + lmi_evaluator.setVectorMatrix(m, d, matrices); + } + + /// \returns The dimension of vector x + unsigned int dimension() const { + return d; + } + + /// \return The matrices A0, A1, ..., Ad + std::vector getMatrices() const { + return matrices; + } + + /// \returns The size of the matrices + unsigned int sizeOfMatrices() const { + return m; + } + + /// Evaluate A_0 + \[A_0 + \sum x_i A_i \] + /// \param[in] x The input vector + /// \param[out] ret The output matrix + void evaluate(VT const & x, MT& ret, bool complete_mat = false) const { + lmi_evaluator.evaluateWithoutA0(x, ret, complete_mat); + + // add A0 + ret += matrices[0]; + } + + /// Compute \[x_1*A_1 + ... + x_n A_n] + /// \param[in] x Input vector + /// \param[out] res Output matrix + void evaluateWithoutA0(const VT& x, MT& res, bool complete_mat = false) const { + lmi_evaluator.evaluateWithoutA0(x, res, complete_mat); + } + + /// Compute the gradient of the determinant of the LMI at p + /// \param[in] p Input parameter + /// \param[in] Input vector: lmi(p)*e = 0, e != 0 + /// \param[out] ret The normalized gradient of the determinant of the LMI at p + void normalizedDeterminantGradient(VT r, VT const& e, VT &ret) const { + NT* ret_data = ret.data(); + NT sum_sqqrt_sq = NT(0); + for (int i = 0; i < d; i++) { + // todo, use iterators + *ret_data = e.dot(matrices[i+1].template selfadjointView< Eigen::Lower >() * e); + sum_sqqrt_sq += (*ret_data) * (*ret_data); + ret_data++; + } + + //normalize + ret /= std::sqrt(sum_sqqrt_sq); + } + + /// \param i An indicator to a matrix + /// \return Pointer to A_i + MT* const getMatrix(const int i) { + return &(matrices[i]); + } + + MT get_A0() { + return matrices[0]; + } + + void set_A0(MT const& A0) { + matrices[0] = A0; + } + + /// Prints the matrices A0, ..., An + void print() const { + int i = 0; + + for (auto iter = matrices.begin(); iter != matrices.end(); iter++, i++) { + std::cout << "A" << i << "\n"; + std::cout << *iter << "\n\n"; + } + } + + /// check if the matrix is negative semidefinite + /// \param matrix a matrix + /// \return Pointer to A_i + bool isNegativeSemidefinite(MT const & matrix ) const { + EigenvaluesProblems eigs; + NT eival = eigs.findSymEigenvalue(matrix); + return eival <= 0; + } + + /// evaluate LMI(pos) and check if its negative semidefinite + /// \param pos a vector of our current position + /// \return true is LMI(pos) is negative semidefinite + bool isNegativeSemidefinite(VT const & pos) const { + MT mat; + mat.setZero(m, m); + + evaluate(pos, mat, true); + return isNegativeSemidefinite(mat); + } + +}; + +#endif //VOLESTI_LMI_H diff --git a/src/volesti/include/convex_bodies/spectrahedra/spectrahedron.h b/src/volesti/include/convex_bodies/spectrahedra/spectrahedron.h new file mode 100644 index 00000000..e8f34138 --- /dev/null +++ b/src/volesti/include/convex_bodies/spectrahedra/spectrahedron.h @@ -0,0 +1,492 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +// Modified by Huu Phuoc Le as part of Google Summer of Code 2022 program + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_SPECTRAHEDRON_H +#define VOLESTI_SPECTRAHEDRON_H + +#include "LMI.h" +#include "chrono" + + +/// Among successive calls of this class methods, we may need to pass data +/// from one call to the next, to avoid repeating computations, or to efficiently update values +/// Warning: this struct assists in many methods; perhaps for different methods use different instances +template +struct PrecomputationOfValues { + + /// These flags indicate whether the corresponding matrices are computed + /// if yes, we can use them and not compute them fro scratch + // TODO: avoid the use of flags + bool computed_A = false; + bool computed_C = false; + bool computed_XY = false; + + /// The matrices the method positiveIntersection receives from its previous call + /// if the flag first_positive_intersection is true. + /// Matrix A is also used in coordinateIntersection + MT A, B, C, X, Y; + + /// In method positive_intersect, the distance we are computing corresponds + /// to the minimum positive eigenvalue of a quadratic eigenvalue problem. + /// This will hold the eigenvector for that eigenvalue + VT eigenvector; + + /// Sets all flags to false + void resetFlags() { + computed_XY = computed_C = computed_A = false; + } + + void set_mat_size(int const& m) + { + A.setZero(m, m); + B.setZero(m, m); + C.setZero(m, m); + + X.setZero(2*m, 2*m); + Y.setZero(2*m, 2*m); + + eigenvector.setZero(m); + } +}; + + +/// This class manipulates a spectrahedron, described by a Linear Matrix Inequality i.e. LMI +/// \tparam Point Point Type +template +class Spectrahedron { +public: + + /// The numeric/matrix/vector types we use + typedef Point PointType; + typedef typename Point::FT NT; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + + double maxDouble = std::numeric_limits::max(); + + /// The type of a pair of NT + typedef std::pair pairNT; + + typedef PrecomputationOfValues _PrecomputationOfValues; + + _PrecomputationOfValues precomputedValues; + + EigenvaluesProblems EigenvaluesProblem; + + /// The dimension of the spectrahedron + unsigned int d; + VT grad; + std::pair _inner_ball; + + /// The linear matrix inequality that describes the spectrahedron + LMI lmi; + + Spectrahedron() {} + + /// Creates a spectrahedron + /// \param[in] lmi The linear matrix inequality that describes the spectrahedron + Spectrahedron(const LMI& lmi) : lmi(lmi) { + d = lmi.dimension(); + //grad.setZero(d); + precomputedValues.resetFlags(); + precomputedValues.set_mat_size(lmi.sizeOfMatrices()); + } + + void set_interior_point(PointType const& r) + { + _inner_ball.first = r; + } + + std::pair ComputeInnerBall() { + NT radius = maxDouble; + + for (unsigned int i = 0; i < dimension(); ++i) { + + std::pair min_max = coordinateIntersection(_inner_ball.first.getCoefficients(), i+1); + + if (min_max.first < radius) radius = min_max.first; + if (-min_max.second < radius) radius = -min_max.second; + } + + radius = radius / std::sqrt(NT(dimension())); + _inner_ball.second = radius; + + return std::pair(_inner_ball.first, radius); + } + + std::pair InnerBall() const + { + return _inner_ball; + } + + /// Construct the quadratic eigenvalue problem \[At^2 + Bt + C \] for positive_intersect. + /// A = lmi(c) - A0, B = lmi(b) - A0 and C = lmi(c). + /// \param[in] a Input vector + /// \param[in] b Input vector + /// \param[in] c Input vector + /// \param[in, out] precomputedValues Holds matrices A, C + void createMatricesForPositiveQuadIntersection(const VT& a, const VT& b, const VT& c) { + + // check if matrices A, C are ready + // if not compute them + if (!precomputedValues.computed_A) { + lmi.evaluateWithoutA0(a, precomputedValues.A, true); + } + + if (!precomputedValues.computed_C) { + lmi.evaluate(c, precomputedValues.C, true); + } + + // compute Matrix B + lmi.evaluateWithoutA0(b, precomputedValues.B, true); + } + + /// Construct the generalized eigenvalue problem \[Bt + C \] for positive_intersect. + /// \param[in] p Input vector + /// \param[in] v Input vector + /// \param[in, out] precomputedValues Holds matrices A, C + void createMatricesForPositiveLinearIntersection(const VT& p, const VT& v) { + // check if matrices A, C are ready + // if not compute them + if (!precomputedValues.computed_C) { + lmi.evaluate(p, precomputedValues.C); + } + + // compute Matrix B + lmi.evaluateWithoutA0(v, precomputedValues.B); + } + + + void createMatricesForPositiveIntersection(const VT& p, const VT& v) { + + // check if matrices B, C are ready if not compute them + if (!precomputedValues.computed_C) + { + lmi.evaluate(p, precomputedValues.C); + } + + lmi.evaluateWithoutA0(v, precomputedValues.B); + } + + /// Computes the distance d we must travel on the parametrized polynomial curve \[at^2 + bt + c \], + /// assuming we start at t=0, and we start increasing t. + /// We construct the quadratic eigenvalue problem \[At^2 + Bt + C \], + /// where A = lmi(c) - A0, B = lmi(b) - A0 and C = lmi(c). + /// Then we do a linearization and transform it to the generalized problem X+lY, + /// which we pass to an external library. + /// \param[in] a Input vector, the coefficient of t \[t^2\] + /// \param[in] b Input vector, the coefficient of t + /// \param[in] c Input Vector, the constant term + /// \returns The distance d + NT positiveQuadIntersection(VT const & a, VT const & b, VT const & c) { + unsigned int matrixDim = lmi.sizeOfMatrices(); + + // create matrices A, B, C + createMatricesForPositiveQuadIntersection(a, b, c); + + // get the minimum positive eigenvalue of At^2 + Bt + C + NT distance = EigenvaluesProblem.minPosQuadraticEigenvalue(precomputedValues.A, precomputedValues.B, + precomputedValues.C, precomputedValues.X, + precomputedValues.Y, + precomputedValues.eigenvector, + precomputedValues.computed_XY); + return distance; + } + + + NT positiveLinearIntersection(VT const & p, VT const & v) + { + createMatricesForPositiveLinearIntersection(p, v); + NT distance = EigenvaluesProblem.minPosLinearEigenvalue(precomputedValues.C, precomputedValues.B, + precomputedValues.eigenvector); + return distance; + } + + /// Computes the distance d one must travel on the line a + tb, + /// assuming we start at t=0 and that b has zero everywhere and 1 in its i-th coordinate. + /// We must solve the generalized eigenvalue problem A+tB, where A = lmi(a) and B=(lmi) - A0 = A_i + /// If the flag precomputedValues,computed_A is true, the matrix A is not computed. + /// \param[in] a Input vector + /// \param[in] coordinate Indicator of the i-th coordinate, 1 <= coordinate <= dimension + /// \return The pair (positive t, negative t) for which we reach the boundary + pairNT coordinateIntersection(VT const & a, int const coordinate) { + + // prepare the generalized eigenvalue problem A+lB + // we may not have to compute A! + if (!precomputedValues.computed_A) + lmi.evaluate(a, precomputedValues.A); + + return EigenvaluesProblem.symGeneralizedProblem(precomputedValues.A, *(lmi.getMatrix(coordinate))); + } + + //First coordinate ray intersecting convex polytope + std::pair line_intersect_coord(Point &r, + unsigned int const& rand_coord, + VT&) + { + return coordinateIntersection(r.getCoefficients(), rand_coord); + } + + //Not the first coordinate ray intersecting convex + std::pair line_intersect_coord(PointType &r, + PointType&, + unsigned int const& rand_coord, + unsigned int&, + VT&) + { + return coordinateIntersection(r.getCoefficients(), rand_coord); + } + + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_positive_intersect(PointType const& r, + PointType const& v) + { + NT pos_inter = positiveLinearIntersection(r.getCoefficients(), v.getCoefficients()); + return std::pair (pos_inter, -1); + } + + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT&, + VT& , + NT const&) { + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT&, + VT& , + NT const&, + update_parameters&) + { + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT&, + VT&, + NT const&, + MT const&, + update_parameters& ) + { + return line_positive_intersect(r, v); + } + + template + std::pair line_first_positive_intersect(PointType const& r, + PointType const& v, + VT&, + VT&, + update_parameters&) + { + return line_positive_intersect(r, v); + } + + // compute intersection point of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT&, + VT&) + { + return line_positive_intersect(r, v); + } + + // compute intersection point of ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair line_intersect(PointType const& r, PointType const& v) + { + NT pos_inter = positiveLinearIntersection(r.getCoefficients(), v.getCoefficients()); + NT neg_inter = -positiveLinearIntersection(r.getCoefficients(), NT(-1)*v.getCoefficients()); + + return std::make_pair(pos_inter, neg_inter); + } + + + std::pair line_intersect(PointType const& r, + PointType const& v, + VT&, + VT&) + { + return line_intersect(r, v); + } + + std::pair line_intersect(PointType const& r, + PointType const& v, + VT&, + VT&, + NT&) + { + return line_intersect(r, v); + } + + void update_position_internal(NT &t){ + precomputedValues.C += t * precomputedValues.B; + precomputedValues.computed_C = true; + } + + MT get_mat() const + { + return MT::Identity(lmi.dimension(), lmi.dimension()); + } + + void resetFlags() + { + precomputedValues.resetFlags(); + } + + void set_flags(bool bool_flag) + { + precomputedValues.computed_A = bool_flag; + precomputedValues.computed_C = bool_flag; + precomputedValues.computed_XY = bool_flag; + } + + MT get_C() const + { + return precomputedValues.C; + } + + void update_C(NT const& lambda) + { + precomputedValues.C += (lambda * lambda) * precomputedValues.A + lambda * precomputedValues.B; + } + + // return the number of facets + int num_of_hyperplanes() const + { + return 0; + } + + void shift(VT e) { + MT A0 = getLMI().get_A0(); + std::vector matrices = getLMI().getMatrices(); + + int d = matrices.size(); + + for (int i = 1; i < d; ++i) { + A0 = A0 + e(i-1)*matrices[i]; + } + + lmi.set_A0(A0); + + _inner_ball.first = PointType(dimension()); + } + + /// Computes the reflected direction at a point on the boundary of the spectrahedron. + /// \param[in] r A point on the boundary of the spectrahedron + /// \param[in] v The direction of the trajectory as it hits the boundary + /// \param[out] reflectedDirection The reflected direction + template + void compute_reflection(PointType &v, PointType const& r, update_parameters& ) const + { + VT grad(d); + lmi.normalizedDeterminantGradient(r.getCoefficients(), precomputedValues.eigenvector, grad); + + // compute reflected direction + // if v is original direction and s the surface normal, + // reflected direction = v - 2 *s + + NT dot = 2 * v.dot(grad); + v += -dot * PointType(grad); + } + + + /// \return The dimension of the spectrahedron + unsigned int dimension() const { + return d; + } + + /// \return The LMI describing this spectrahedron + LMI getLMI() const { + return lmi; + } + + /// Estimates the diameter of the spectrahedron. It samples points uniformly with coordinate directions + /// hit and run, and returns the maximum distance between them. + /// \tparam Point + /// \param[in] numPoints The number of points to sample for the estimation + /// \return An estimation of the diameter of the spectrahedron + template + NT estimateDiameter(int const numPoints, PointType const & interiorPoint, RNGType &rng) { + + std::list randPoints; + + precomputedValues.computed_A = false; + VT p = interiorPoint.getCoefficients(); + + // sample points with walk length set to 1 + for (int samplingNo=0 ; samplingNo distances = this->coordinateIntersection(p, coordinate); + + // uniformly set the new point on the segment + // defined by the intersection points + NT lambda = rng.sample_urdist(); + NT diff = distances.first + lambda * (distances.second - distances.first); + + p(coordinate - 1) = p(coordinate - 1) + diff; + + // update the precomputedValues, so we can skip + // computations in the next call + precomputedValues.computed_A = true; + precomputedValues.A += diff * *(this->getLMI().getMatrix(coordinate)); + randPoints.push_back(Point(p)); + } + + // find maximum distance among points; + NT maxDistance = 0; + typename std::list::iterator itInner, itOuter = randPoints.begin(); + + for (; itOuter!=randPoints.end() ; ++itOuter) + for (itInner=itOuter ; itInner!=randPoints.end() ; ++itInner) { + NT current = itOuter->distance(*itInner); + if (current > maxDistance) + maxDistance = current; + } + + return maxDistance; + } + + int is_in(PointType const& p, NT tol=NT(0)) const + { + if (isExterior(p.getCoefficients())) { + return 0; + } + return -1; + } + + /// Find out is lmi(current position) = mat is in the exterior of the spectrahedron + /// \param mat a matrix where mat = lmi(current position) + /// \return true if position is outside the spectrahedron + bool isExterior(MT const & mat) const { + return !lmi.isNegativeSemidefinite(mat); + } + + /// Find out is pos is in the exterior of the spectrahedron + /// \param pos a vector + /// \return true if pos is outside the spectrahedron + bool isExterior(VT const & pos) const { + return !lmi.isNegativeSemidefinite(pos); + } +}; + +#endif //VOLESTI_SPECTRAHEDRON_H diff --git a/src/volesti/include/convex_bodies/vpolyintersectvpoly.h b/src/volesti/include/convex_bodies/vpolyintersectvpoly.h new file mode 100644 index 00000000..a6dd97ca --- /dev/null +++ b/src/volesti/include/convex_bodies/vpolyintersectvpoly.h @@ -0,0 +1,429 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018-19 programs. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VPOLYINTERSECTVPOLY_H +#define VPOLYINTERSECTVPOLY_H + +#include +#include +#include +#include "sampling/sphere.hpp" + +/// This class represents the intersection of two V-polytopes +/// \tparam VPolytope VPolytope Type +/// \tparam RNGType RNGType Type +template +class IntersectionOfVpoly { +public: + typedef typename VPolytope::NT NT; + typedef typename VPolytope::PointType PointType; + typedef PointType Point; + typedef typename VPolytope::MT MT; + typedef typename VPolytope::VT VT; + unsigned seed; + std::pair _inner_ball; + NT rad; + VPolytope P1; + VPolytope P2; + + IntersectionOfVpoly(): P1(), P2() {} + + IntersectionOfVpoly(VPolytope P, VPolytope Q) : P1(P), P2(Q) { + seed = std::chrono::system_clock::now().time_since_epoch().count(); + } + + IntersectionOfVpoly(VPolytope P, VPolytope Q, unsigned _seed) : P1(P), P2(Q) { + seed = _seed; + } + + VPolytope first() { return P1; } + VPolytope second() { return P2; } + + std::pair InnerBall() const + { + return _inner_ball; + } + + int is_in(const Point &p, NT tol=NT(0)) const { + if(P1.is_in(p)==-1) + return P2.is_in(p); + return 0; + } + + + int num_of_hyperplanes() const { + return 0; + } + + unsigned int dimension() const { + return P1.dimension(); + } + + int num_of_vertices() const { + return P1.num_of_vertices() + P2.num_of_vertices(); + } + + unsigned int upper_bound_of_hyperplanes() const { + return dimension() + 1; + //return 4; + } + + int num_of_generators() const { + return 0; + } + + NT getRad() const { + return rad; + } + + MT get_mat() const { + return P1.get_mat(); + } + + VT get_vec() const { + return P1.get_vec(); + } + + MT get_T() const { + return P1.get_mat(); + } + + MT get_mat2() const { + return P2.get_mat(); + } + + Point get_mean_of_vertices() const { + return Point(P1.dimension()); + } + + + NT get_max_vert_norm() const { + return 0.0; + } + + void print() { + P1.print(); + P2.print(); + } + + bool is_feasible() { + bool empty; + int k = P1.get_mat().rows() + P2.get_mat().rows(); + RNGType rng(k); + rng.set_seed(seed); + PointInIntersection(P1.get_mat(), P2.get_mat(), + GetDirection::apply(k, rng), empty); + return !empty; + } + + std::pair ComputeInnerBall() { + + unsigned int num = 0, d = P1.dimension(); + MT V1 = P1.get_mat(), V2 = P2.get_mat(); + int k1 = V1.rows(), k2 = V2.rows(); + int k = k1 + k2; + RNGType rng(k); + rng.set_seed(seed); + Point direction(k), p(d); + std::vector vertices; + typename std::vector::iterator rvert; + bool same; + + while(num::apply(k, rng); + p = PointInIntersection(V1, V2, direction, same); + + same = false; + rvert = vertices.begin(); + for ( ; rvert!=vertices.end(); ++rvert) { + if (p==(*rvert)) { + same = true; + break; + } + } + if (same) continue; + vertices.push_back(p); + num++; + + } + + _inner_ball = P1.get_center_radius_inscribed_simplex(vertices.begin(), vertices.end()); + return _inner_ball; + + } + + void set_InnerBall(std::pair const& innerball) const + { + _inner_ball = innerball; + } + + void set_interior_point(Point const& r) + { + _inner_ball.first = r; + } + +/* + unsigned int num_of_v = 0; + unsigned int d = dimension(); + MT V(0, d); + MT V1 = P1.get_mat(); + MT V2 = P2.get_mat(); + VT itervec(d); + std::vector temp_p(d, 0.0); + typename std::vector::iterator pit; + int j; + for (int i = 0; i < V1.rows(); ++i) { + pit = temp_p.begin(); + j = 0; + for (; pit!=temp_p.end(); ++pit, ++j) { + *pit = V1(i,j); + itervec(j) = V1(i,j); + } + Point p(d, temp_p.begin(), temp_p.end()); + if (P2.is_in(p) == -1) { + V.conservativeResize(V.rows() + 1, V.cols()); + V.row(V.rows()-1) = itervec; + num_of_v++; + } + } + + for (int i = 0; i < V2.rows(); ++i) { + pit = temp_p.begin(); + j = 0; + for (; pit!=temp_p.end(); ++pit, ++j) { + *pit = V2(i,j); + itervec(j) = V2(i,j); + } + Point p(d, temp_p.begin(), temp_p.end()); + if (P1.is_in(p) == -1) { + V.conservativeResize(V.rows() + 1, V.cols()); + V.row(V.rows()-1) = itervec; + num_of_v++; + } + } + if (num_of_v <= d) { + std::cout<<"no simplex"< res; + res.second = -1.0; + return res; + } + + VPolytope Q; + Q.init(d, V, itervec); + return Q.ComputeInnerBall(); + }*/ + + // compute intersection point of ray starting from r and pointing to v + // with the V-polytope + std::pair line_intersect(const Point &r, const Point &v) const { + + std::pair P1pair = P1.line_intersect(r, v); + std::pair P2pair = P2.line_intersect(r, v); + return std::pair(std::min(P1pair.first, P2pair.first), + std::max(P1pair.second, P2pair.second)); + + } + + // compute intersection point of ray starting from r and pointing to v + // with the V-polytope + std::pair line_intersect(const Point &r, const Point &v, const VT &Ar, + const VT &Av) const { + return line_intersect(r, v); + } + + + // compute intersection point of ray starting from r and pointing to v + // with the V-polytope + std::pair line_intersect(const Point &r, const Point &v, const VT &Ar, + const VT &Av, const NT &lambda) const { + return line_intersect(r, v); + } + + std::pair line_positive_intersect(const Point &r, const Point &v) const { + + std::pair P1pair = P1.line_positive_intersect(r, v); + std::pair P2pair = P2.line_positive_intersect(r, v); + + if(P1pair.first < P2pair.first) { + return std::pair(P1pair.first, 1); + } + return std::pair(P2pair.first, 2); + } + + std::pair line_positive_intersect(const Point &r, const Point &v, const VT &Ar, + const VT &Av) const { + return line_positive_intersect(r, v); + } + + + std::pair line_positive_intersect(const Point &r, const Point &v, const VT &Ar, + const VT &Av, const NT &lambda_prev) const { + return line_positive_intersect(r, v);//, Ar, Av); + } + + //------------------------------accelarated billiard------------------------------// + template + std::pair line_first_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + update_parameters& params) const + { + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + MT const& AA, + update_parameters& params) const + { + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + update_parameters& params) const + { + return line_positive_intersect(r, v); + } + //------------------------------------------------------------------------------// + + + // Compute the intersection of a coordinate ray + // with the V-polytope + std::pair line_intersect_coord(const Point &r, + const unsigned int &rand_coord, + const VT &lamdas) const { + std::pair P1pair = P1.line_intersect_coord(r, rand_coord, lamdas); + std::pair P2pair = P2.line_intersect_coord(r, rand_coord, lamdas); + return std::pair(std::min(P1pair.first, P2pair.first), + std::max(P1pair.second, P2pair.second)); + } + + + // Compute the intersection of a coordinate ray + // with the V-polytope + std::pair line_intersect_coord(const Point &r, + const Point &r_prev, + const unsigned int &rand_coord, + const unsigned int &rand_coord_prev, + const VT &lamdas) const { + return line_intersect_coord(r, rand_coord, lamdas); + } + + + //------------------------------oracles for exponential sampling---------------////// + + // compute intersection points of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair quadratic_positive_intersect(Point const& r, + Point const& v, + VT const& Ac, + NT const& T, + VT& Ar, + VT& Av, + int& facet_prev) const + { + throw std::runtime_error("Quadratic polynomial trajectories are supported only for H-polytopes"); + } + + std::pair quadratic_positive_intersect(Point const& r, + Point const& v, + VT const& Ac, + NT const& T, + VT& Ar, + VT& Av, + NT const& lambda_prev, + int& facet_prev) const + { + throw std::runtime_error("Quadratic polynomial trajectories are supported only for H-polytopes"); + } + + //------------oracle for exact hmc spherical gaussian sampling---------------// + std::pair trigonometric_positive_intersect(Point const& r, Point const& v, + NT const& omega, int &facet_prev) const + { + return std::make_pair(0, 0); + } + + // shift polytope by a point c + void shift(const VT &c) { + P1.shift(c); + P2.shift(c); + } + + + // apply linear transformation, of square matrix T, to the V-Polytope + void linear_transformIt(const MT &T) { + P1.linear_transformIt(T); + P2.linear_transformIt(T); + } + + std::vector get_dists(const NT &radius) const { + std::vector res(upper_bound_of_hyperplanes(), radius); + return res; + } + + template + bool get_points_for_rounding (PointList &randPoints) { + if (num_of_vertices()>40*dimension()) { + return false; + } + if(!P1.get_points_for_rounding(randPoints)) { + return false; + } + if(!P2.get_points_for_rounding(randPoints)) { + return false; + } + + return true; + } + + void normalize() {} + + void resetFlags() {} + + void update_position_internal(NT&){} + + void compute_reflection (Point &v, const Point &p, const int &facet) const { + + if (facet == 1) { + P1.compute_reflection (v, p, facet); + } else { + P1.compute_reflection (v, p, facet); + } + + } + + template + void compute_reflection (Point &v, const Point &p, update_parameters const& params) const { + + if (params.facet_prev == 1) { + P1.compute_reflection (v, p, params); + } else { + P2.compute_reflection (v, p, params); + } + + } + +}; + + +#endif diff --git a/src/volesti/include/convex_bodies/vpolytope.h b/src/volesti/include/convex_bodies/vpolytope.h new file mode 100644 index 00000000..82ca6689 --- /dev/null +++ b/src/volesti/include/convex_bodies/vpolytope.h @@ -0,0 +1,648 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018-19 programs. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VPOLYTOPE_H +#define VPOLYTOPE_H + +#include +#include +#include + +#include "lp_oracles/vpolyoracles.h" +#include + + +/// This class describes a polytope in V-representation or an V-polytope +/// i.e. a polytope defined as a convex combination of points +/// \tparam Point Point type +template +class VPolytope { +public: + typedef Point PointType; + typedef typename Point::FT NT; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + +private: + unsigned int _d; //dimension + MT V; //matrix V. Each row contains a vertex + VT b; // vector b that contains first column of ine file + std::pair _inner_ball; + + // TODO: Why don't we use std::vector and std::vector for these pointers? + REAL *conv_comb, *conv_comb2, *conv_mem, *row; + int *colno, *colno_mem; + +public: + VPolytope() {} + + VPolytope(const unsigned int &dim, const MT &_V, const VT &_b): + _d{dim}, V{_V}, b{_b}, + conv_comb{new REAL[V.rows() + 1]}, + conv_comb2{new REAL[V.rows() + 1]}, + conv_mem{new REAL[V.rows()]}, + row{new REAL[V.rows() + 1]}, + colno{new int[V.rows() + 1]}, + colno_mem{new int[V.rows()]} + { + } + + // Construct matrix V which contains the vertices row-wise + // TODO: change rows; + VPolytope(std::vector> const& Pin) + { + _d = Pin[0][1] - 1; + V.resize(Pin.size() - 1, _d); + b.resize(Pin.size() - 1); + for (unsigned int i = 1; i < Pin.size(); i++) { + b(i - 1) = Pin[i][0]; + unsigned int j; + for (j = 1; j < _d + 1; j++) { + V(i - 1, j - 1) = Pin[i][j]; + } + } + conv_comb = new REAL[Pin.size()]; + conv_comb2 = new REAL[Pin.size()]; + conv_mem = new REAL[V.rows()]; + row = new REAL[V.rows() + 1]; + colno = new int[V.rows() + 1]; + colno_mem = new int[V.rows()]; + } + + template + void copy_array(T* source, T* result, size_t count) + { + T* tarray; + tarray = new T[count]; + std::copy_n(source, count, tarray); + delete [] result; + result = tarray; + } + + VPolytope& operator=(const VPolytope& other) + { + if (this != &other) { // protect against invalid self-assignment + _d = other._d; + V = other.V; + b = other.b; + + copy_array(other.conv_comb, conv_comb, V.rows() + 1); + copy_array(other.conv_comb2, conv_comb2, V.rows() + 1); + copy_array(other.conv_mem, conv_mem, V.rows()); + copy_array(other.row, row, V.rows() + 1); + copy_array(other.colno, colno, V.rows() + 1); + copy_array(other.colno_mem, colno_mem, V.rows()); + } + return *this; + } + + VPolytope& operator=(VPolytope&& other) + { + if (this != &other) { // protect against invalid self-assignment + _d = other._d; + V = other.V; + b = other.b; + + conv_comb = other.conv_comb; other.conv_comb = nullptr; + conv_comb2 = other.conv_comb2; other.conv_comb2 = nullptr; + conv_mem = other.conv_mem; other.conv_mem = nullptr; + row = other.row; other.row = nullptr; + colno = other.colno; colno = nullptr; + colno_mem = other.colno_mem; colno_mem = nullptr; + } + return *this; + } + + + VPolytope(const VPolytope& other) : + _d{other._d}, V{other.V}, b{other.b}, + conv_comb{new REAL[V.rows() + 1]}, + conv_comb2{new REAL[V.rows() + 1]}, + conv_mem{new REAL[V.rows()]}, + row{new REAL[V.rows() + 1]}, + colno{new int[V.rows() + 1]}, + colno_mem{new int[V.rows()]} + { + std::copy_n(other.conv_comb, V.rows() + 1, conv_comb); + std::copy_n(other.conv_comb2, V.rows() + 1, conv_comb2); + std::copy_n(other.conv_mem, V.rows(), conv_mem); + std::copy_n(other.row, V.rows() + 1, row); + std::copy_n(other.colno, V.rows() + 1, colno); + std::copy_n(other.colno_mem, V.rows(), colno_mem); + } + + VPolytope(VPolytope&& other) : + _d{other._d}, V{other.V}, b{other.b}, + conv_comb{nullptr}, conv_comb2{nullptr}, conv_mem{nullptr}, row{nullptr}, + colno{nullptr}, colno_mem{nullptr} + { + conv_comb = other.conv_comb; other.conv_comb = nullptr; + conv_comb2 = other.conv_comb2; other.conv_comb2 = nullptr; + conv_mem = other.conv_mem; other.conv_mem = nullptr; + row = other.row; other.row = nullptr; + colno = other.colno; colno = nullptr; + colno_mem = other.colno_mem; colno_mem = nullptr; + } + + ~VPolytope() { + delete [] conv_comb; + delete [] conv_comb2; + delete [] colno; + delete [] colno_mem; + delete [] row; + delete [] conv_mem; + } + + std::pair InnerBall() const + { + return _inner_ball; + } + + void set_InnerBall(std::pair const& innerball) //const + { + _inner_ball = innerball; + } + + void set_interior_point(Point const& r) + { + _inner_ball.first = r; + } + + // return dimension + unsigned int dimension() const { + return _d; + } + + + // this function returns 0. The main sampler requests this function to set the length of lambdas vector + int num_of_hyperplanes() const { + return 0; + } + + int num_of_generators() const { + return 0; + } + + // compute the number of facets of the cyclic polytope in dimension _d with the same number of vertices + // this is an upper bound for the number of the facets from McMullen's Upper Bound Theorem + unsigned int upper_bound_of_hyperplanes() const { + return 2 * _d; + } + + + // return the number of vertices + int num_of_vertices() const { + return V.rows(); + } + + // return the matrix V + MT get_mat() const { + return V; + } + + // return the vector b + VT get_vec() const { + return b; + } + + // change the matrix V + void set_mat(const MT &V2) { + V = V2; + } + + // change the vector b + void set_vec(const VT &b2) { + b = b2; + } + + MT get_T() const { + return V; + } + + + // print polytope in input format + void print() { + std::cout << " " << V.rows() << " " << _d << " float" << std::endl; + for (unsigned int i = 0; i < V.rows(); i++) { + for (unsigned int j = 0; j < _d; j++) { + std::cout << V(i, j) << " "; + } + std::cout<<"\n"; + } + } + + Point get_mean_of_vertices() { + Point xc(_d); + for (int i = 0; i < num_of_vertices(); ++i) { + xc.add(V.row(i)); + } + xc *= (1.0/NT(num_of_vertices())); + + return xc; + } + + + NT get_max_vert_norm() { + NT rad =0.0; + NT rad_iter; + for (int i = 0; i < num_of_vertices(); ++i) { + rad_iter = V.row(i).norm(); + if(rad_iter>rad)rad = rad_iter; + } + return rad; + } + + void normalize() {} + + // take d+1 points as input and compute the chebychev ball of the defined simplex + // done is true when the simplex is full dimensional and false if it is not + std::pair get_center_radius_inscribed_simplex(const typename std::vector::iterator it_beg, + const typename std::vector::iterator it_end) { + + Point p0 = *it_beg,p1,c; + unsigned int dim = p0.dimension(), i, j; + std::vector temp_p; + NT radius = 0.0, gi, sum = 0.0; + MT B(dim,dim), Bg(dim,dim), e(1,dim); + VT row(dim), g(dim); + std::pair result; + + for (j=1; jgetCoefficients() - p0.getCoefficients(); + } + + Bg = B; + B = B.inverse(); + for (i=0; i ComputeInnerBall() { + + std::vector verts(_d+1); + std::vector vecp(_d); + unsigned int vert_rand, pointer=0; + unsigned int i,j; + int m = num_of_vertices(); + std::vector x_vec(_d); + bool done=false; + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(seed); + boost::random::uniform_int_distribution<> uidist(1, m); + + std::pair res; + // while d+1 points do not define a full dimensional simplex repeat + while(!done){ + pointer=0; + x_vec.assign(_d+1,0); + while(pointer!=_d+1) { + vert_rand = uidist(rng); + // Check if this vertex is selected first time + if (std::find(x_vec.begin(), x_vec.begin() + pointer, vert_rand) == x_vec.begin() + pointer) { + x_vec[pointer] = vert_rand; + pointer++; + } + } + + for(i=0; i<(_d+1); i++){ + for (j=0; j<_d; j++) { + vecp[j] = V(x_vec[i] - 1, j); + } + verts[i] = Point(_d,vecp.begin(),vecp.end()); + } + res=get_center_radius_inscribed_simplex(verts.begin(), verts.end(), done); + } + return res; + }*/ + + + std::pair ComputeInnerBall() { + + NT radius = std::numeric_limits::max(), min_plus; + Point center(_d); + + std::list randPoints; + if (!get_points_for_rounding(randPoints)) { + center = get_mean_of_vertices(); + } else { + + MT Ap(_d,randPoints.size()); + typename std::list::iterator rpit=randPoints.begin(); + + unsigned int i, j = 0; + for ( ; rpit!=randPoints.end(); rpit++, j++) { + const NT* point_data = rpit->getCoefficients().data(); + + for ( i=0; i < rpit->dimension(); i++){ + Ap(i,j)=double(*point_data); + point_data++; + } + } + MT Q(_d, _d); + VT c2(_d); + size_t w=1000; + + KhachiyanAlgo(Ap,0.01,w,Q,c2); // call Khachiyan algorithm + + //Get ellipsoid matrix and center as Eigen objects + for(unsigned int i=0; i<_d; i++) center.set_coord(i, NT(c2(i))); + } + + std::pair res; + Point v(_d); + for (unsigned int i = 0; i < _d; ++i) { + v.set_to_origin(); + v.set_coord(i, 1.0); + res = intersect_double_line_Vpoly(V, center, v, row, colno); + min_plus = std::min(res.first, -1.0*res.second); + if (min_plus < radius) radius = min_plus; + } + + radius = radius / std::sqrt(NT(_d)); + _inner_ball = std::pair (center, radius); + return std::pair (center, radius); + } + + + // check if point p belongs to the convex hull of V-Polytope P + int is_in(const Point &p, NT tol=NT(0)) const { + if (memLP_Vpoly(V, p, conv_mem, colno_mem)){ + return -1; + } + return 0; + } + + + // compute intersection point of ray starting from r and pointing to v + // with the V-polytope + std::pair line_intersect(const Point &r, const Point &v) const { + + return intersect_double_line_Vpoly(V, r, v, row, colno); + } + + + // compute intersection point of ray starting from r and pointing to v + // with the V-polytope + std::pair line_intersect(const Point &r, const Point &v, const VT &Ar, + const VT &Av) const { + return intersect_double_line_Vpoly(V, r, v, row, colno); + } + + // compute intersection point of ray starting from r and pointing to v + // with the V-polytope + std::pair line_intersect(const Point &r, const Point &v, const VT &Ar, + const VT &Av, const NT &lambda_prev) const { + + return intersect_double_line_Vpoly(V, r, v, row, colno); + } + + + std::pair line_positive_intersect(const Point &r, const Point &v) const { + return std::pair (intersect_line_Vpoly(V, r, v, conv_comb, row, colno, false, false), 1); + } + + std::pair line_positive_intersect(const Point &r, const Point &v, const VT &Ar, + const VT &Av) const { + return line_positive_intersect(r, v); + } + + + std::pair line_positive_intersect(const Point &r, const Point &v, const VT &Ar, + const VT &Av, const NT &lambda_prev) const { + return line_positive_intersect(r, v); + } + + //-------------------------accelarated billiard--------------------------------// + template + std::pair line_first_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + update_parameters ¶ms) const + { + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + MT const& AA, + update_parameters ¶ms) const + { + return line_positive_intersect(r, v); + } + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + update_parameters ¶ms) const + { + return line_positive_intersect(r, v); + } + //------------------------------------------------------------------------------// + + + // Compute the intersection of a coordinate ray + // with the V-polytope + std::pair line_intersect_coord(const Point &r, + const unsigned int rand_coord, + const VT &lamdas) const { + Point v(_d); + v.set_coord(rand_coord, 1.0); + return intersect_double_line_Vpoly(V, r, v, row, colno); + } + + + // Compute the intersection of a coordinate ray + // with the V-polytope + std::pair line_intersect_coord(const Point &r, + const Point &r_prev, + const unsigned int rand_coord, + const unsigned int rand_coord_prev, + const VT &lamdas) const { + return line_intersect_coord(r, rand_coord, lamdas); + } + + + //------------------------------oracles for exponential sampling---------------////// + + // compute intersection points of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair quadratic_positive_intersect(Point const& r, + Point const& v, + VT const& Ac, + NT const& T, + VT& Ar, + VT& Av, + int& facet_prev) const + { + throw std::runtime_error("Quadratic polynomial trajectories are supported only for H-polytopes"); + } + + std::pair quadratic_positive_intersect(Point const& r, + Point const& v, + VT const& Ac, + NT const& T, + VT& Ar, + VT& Av, + NT const& lambda_prev, + int& facet_prev) const + { + throw std::runtime_error("Quadratic polynomial trajectories are supported only for H-polytopes"); + } + + + //------------oracle for exact hmc spherical gaussian sampling---------------// + std::pair trigonometric_positive_intersect(Point const& r, Point const& v, + NT const& omega, int &facet_prev) const + { + return std::make_pair(0, 0); + } + + + // shift polytope by a point c + void shift(const VT &c) { + MT V2 = V.transpose().colwise() - c; + V = V2.transpose(); + } + + + // apply linear transformation, of square matrix T, to the V-Polytope + void linear_transformIt(const MT &T) { + MT V2 = T.inverse() * V.transpose(); + V = V2.transpose(); + } + + + // consider an upper bound for the number of facets of a V-polytope + // for each facet consider a lower bound for the distance from the origin + // useful for CV algorithm to get the first gaussian + std::vector get_dists(const NT &radius) const { + std::vector res(upper_bound_of_hyperplanes(), radius); + return res; + } + + + // in number_of_vertices<=20*dimension use the vertices for the rounding + // otherwise you have to sample from the V-polytope + template + bool get_points_for_rounding (PointList &randPoints) { + if (num_of_vertices()>20*_d) { + return false; + } + unsigned int j; + + typename std::vector::iterator pointIt; + for (int i=0; i 0.0) { + Fmat2.row(count) = V.row(j); + count++; + } else { + outvert = j; + } + } + + VT a = Fmat2.colPivHouseholderQr().solve(VT::Ones(_d)); + if (a.dot(V.row(outvert)) > 1.0) a = -a; + a /= a.norm(); + + // compute reflection + a *= (-2.0 * v.dot(a)); + v += a; + } + + template + void compute_reflection(Point &v, const Point &p, update_parameters const& params) const { + + int count = 0, outvert; + MT Fmat2(_d,_d); + for (int j = 0; j < num_of_vertices(); ++j) { + if (*(conv_comb + j) > 0.0) { + Fmat2.row(count) = V.row(j); + count++; + } else { + outvert = j; + } + } + + VT a = Fmat2.colPivHouseholderQr().solve(VT::Ones(_d)); + if (a.dot(V.row(outvert)) > 1.0) a *= -1.0; + a /= a.norm(); + + // compute reflection + a *= (-2.0 * v.dot(a)); + v += a; + } + + void resetFlags() {} + + void update_position_internal(NT&){} + + template + std::tuple curve_intersect( + NT t_prev, + NT t0, + NT eta, + std::vector &coeffs, + bfunc phi, + bfunc grad_phi, + NonLinearOracle &intersection_oracle, + int ignore_facet=-1) + { + return intersection_oracle.apply(t_prev, t0, eta, V, *this, + coeffs, phi, grad_phi, ignore_facet); + } + + +}; + +#endif diff --git a/src/volesti/include/convex_bodies/zonoIntersecthpoly.h b/src/volesti/include/convex_bodies/zonoIntersecthpoly.h new file mode 100644 index 00000000..bbad43a6 --- /dev/null +++ b/src/volesti/include/convex_bodies/zonoIntersecthpoly.h @@ -0,0 +1,272 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018-19 programs. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +#ifndef ZONOINTERSECTHPOLY_H +#define ZONOINTERSECTHPOLY_H + +/// This class represents the intersection of a Zonotope with an H-polytope +/// \tparam Zonotope Zonotope Type +/// \tparam HPolytope HPolytope Type +template +class ZonoIntersectHPoly { +private: + Zonotope Z; + HPolytope HP; +public: + typedef typename HPolytope::NT NT; + typedef typename HPolytope::VT VT; + typedef typename HPolytope::MT MT; + typedef typename Zonotope::PointType PointType; + + ZonoIntersectHPoly() + {} + + ZonoIntersectHPoly(Zonotope &Z1, HPolytope &HP1) + : Z(Z1) + , HP(HP1) + {} + + Zonotope first() const { return Z; } + HPolytope second() const { return HP; } + + int is_in(const PointType &p) const { + if(HP.is_in(p)==-1) + return Z.is_in(p); + return 0; + } + + int num_of_hyperplanes() const { + return HP.num_of_hyperplanes(); + } + + unsigned int dimension() const { + return HP.dimension(); + } + + unsigned int num_of_generators() const { + return Z.num_of_generators(); + } + + MT get_mat() const { + return HP.get_mat(); + } + + MT get_T() const { + return Z.get_mat(); + } + + MT get_vec() const { + return HP.get_vec(); + } + + std::pair InnerBall() const + { + return Z.InnerBall(); + } + + std::pair line_intersect(PointType const& r, + PointType const& v) const + { + + std::pair polypair = HP.line_intersect(r, v); + std::pair zonopair = Z.line_intersect(r, v); + return std::pair(std::min(polypair.first, zonopair.first), + std::max(polypair.second, zonopair.second)); + } + + std::pair line_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av) const + { + std::pair polypair = HP.line_intersect(r, v, Ar, Av); + std::pair zonopair = Z.line_intersect(r, v); + return std::pair(std::min(polypair.first, zonopair.first), + std::max(polypair.second, zonopair.second)); + } + + std::pair line_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev) const + { + std::pair polypair = HP.line_intersect(r, v, Ar, Av, lambda_prev); + std::pair zonopair = Z.line_intersect(r, v); + return std::pair(std::min(polypair.first, zonopair.first), + std::max(polypair.second, zonopair.second)); + } + + //First coordinate ray shooting intersecting convex body + std::pair line_intersect_coord(PointType const& r, + unsigned int const& rand_coord, + VT& lamdas) const + { + std::pair polypair = HP.line_intersect_coord(r, rand_coord, lamdas); + std::pair zonopair = Z.line_intersect_coord(r, rand_coord, lamdas); + return std::pair(std::min(polypair.first, zonopair.first), + std::max(polypair.second, zonopair.second)); + } + + + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av) const + { + std::pair polypair = HP.line_positive_intersect(r, v, Ar, Av); + std::pair zonopair = Z.line_positive_intersect(r, v, Ar, Av); + int facet = HP.num_of_hyperplanes()+1; + + if (polypair.first < zonopair.first ) facet = polypair.second; + + return std::pair(std::min(polypair.first, zonopair.first), facet); + } + + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev) const + { + std::pair polypair = HP.line_positive_intersect(r, v, Ar, Av, + lambda_prev); + std::pair zonopair = Z.line_positive_intersect(r, v, Ar, Av); + int facet = HP.num_of_hyperplanes()+1; + + if (polypair.first < zonopair.first ) facet = polypair.second; + + return std::pair(std::min(polypair.first, zonopair.first), + facet); + } + + //---------------------accelarated billiard---------------------// + template + std::pair line_first_positive_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av, + update_parameters& params) const + { + std::pair polypair = HP.line_first_positive_intersect(r, v, Ar, Av, params); + std::pair zonopair = Z.line_positive_intersect(r, v, Ar, Av); + int facet = HP.num_of_hyperplanes(); + params.facet_prev = polypair.second; + + if (polypair.first < zonopair.first ) { + facet = polypair.second; + params.hit_ball = false; + } else { + params.hit_ball = true; + } + + return std::pair(std::min(polypair.first, zonopair.first), facet); + } + + template + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + MT const& AA, + update_parameters& params) const + { + std::pair polypair = HP.line_positive_intersect(r, v, Ar, Av, lambda_prev, params); + std::pair zonopair = Z.line_positive_intersect(r, v, Ar, Av); + int facet = HP.num_of_hyperplanes(); + params.facet_prev = polypair.second; + + if (polypair.first < zonopair.first ) { + facet = polypair.second; + params.hit_ball = false; + } else { + params.hit_ball = true; + } + + return std::pair(std::min(polypair.first, zonopair.first), facet); + } + + template + std::pair line_positive_intersect(PointType const& r, + PointType const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + update_parameters& params) const + { + std::pair polypair = HP.line_positive_intersect(r, v, Ar, Av, lambda_prev, params); + std::pair zonopair = Z.line_positive_intersect(r, v, Ar, Av); + int facet = HP.num_of_hyperplanes(); + params.facet_prev = polypair.second; + + if (polypair.first < zonopair.first ) { + facet = polypair.second; + params.hit_ball = false; + } else { + params.hit_ball = true; + } + + return std::pair(std::min(polypair.first, zonopair.first), facet); + } +//-------------------------------------------------------------------------// + + //Not the first coordinate ray shooting intersecting convex body + std::pair line_intersect_coord(PointType const& r, + PointType const& r_prev, + unsigned int const& rand_coord, + unsigned int const& rand_coord_prev, + VT& lamdas) const + { + + std::pair polypair = HP.line_intersect_coord(r, r_prev, + rand_coord, + rand_coord_prev, + lamdas); + std::pair zonopair = Z.line_intersect_coord(r, rand_coord, + lamdas); + return std::pair(std::min(polypair.first, zonopair.first), + std::max(polypair.second, zonopair.second)); + } + + std::pair query_dual(PointType const& p, + unsigned int const& rand_coord) const + { + std::pair polypair = HP.query_dual(p, rand_coord); + std::pair zonopair = Z.line_intersect_coord(p, rand_coord); + return std::pair(std::min(polypair.first, zonopair.first), + std::max(polypair.second, zonopair.second)); + } + + void compute_reflection (PointType &v, + PointType const& p, + int const& facet) const + { + if (facet == (HP.num_of_hyperplanes()+1)) { + Z.compute_reflection(v, p, facet); + } else { + HP.compute_reflection(v, p, facet); + } + + } + + template + void compute_reflection (PointType &v, const PointType &p, update_parameters const& params) const { + + if (params.hit_ball) { + Z.compute_reflection(v, p, params); + } else { + HP.compute_reflection(v, p, params.facet_prev); + } + + } + +}; + +#endif diff --git a/src/volesti/include/convex_bodies/zpolytope.h b/src/volesti/include/convex_bodies/zpolytope.h new file mode 100644 index 00000000..1ad8868e --- /dev/null +++ b/src/volesti/include/convex_bodies/zpolytope.h @@ -0,0 +1,619 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018-19 programs. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ZPOLYTOPE_H +#define ZPOLYTOPE_H + +#include + +#include +#include +#include "lp_oracles/vpolyoracles.h" +#include "lp_oracles/zpolyoracles.h" + +/// This class describes a zonotope i.e. the Minkowski sum of a set of line segments +/// \tparam Point Point type +template +class Zonotope { +public: + typedef Point PointType; + typedef typename Point::FT NT; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + +private: + unsigned int _d; //dimension + MT V; //matrix V. Each row contains a vertex + VT b; // vector b that contains first column of ine file + MT T; + std::pair _inner_ball; + NT maxNT = std::numeric_limits::max(); + NT minNT = std::numeric_limits::lowest(); + + REAL *conv_comb, *row_mem, *row; + int *colno, *colno_mem; + MT sigma; + MT Q0; + + +public: + + Zonotope() {} + + Zonotope(const unsigned int &dim, const MT &_V, const VT &_b): + _d{dim}, V{_V}, b{_b}, + conv_comb{new REAL[V.rows() + 1]}, + row_mem{new REAL[V.rows()]}, + row{new REAL[V.rows() + 1]}, + colno{new int[V.rows() + 1]}, + colno_mem{new int[V.rows()]} + { + compute_eigenvectors(V.transpose()); + } + + /*Zonotope(unsigned int const dim, MT const& _V, VT const& _b) + { + _d = dim; + V = _V; + b = _b; + + conv_comb = new REAL[V.rows()+1]; + row_mem = new REAL[V.rows()]; + row = new REAL[V.rows() + 1]; + colno = new int[V.rows() + 1]; + colno_mem = new int[V.rows()]; + + //conv_comb = (REAL *) malloc((V.rows()+1) * sizeof(*conv_comb)); + //colno = (int *) malloc((V.rows()+1) * sizeof(*colno)); + //row = (REAL *) malloc((V.rows()+1) * sizeof(*row)); + //colno_mem = (int *) malloc((V.rows()) * sizeof(*colno_mem)); + //row_mem = (REAL *) malloc((V.rows()) * sizeof(*row_mem)); + + compute_eigenvectors(V.transpose()); + }*/ + + Zonotope(std::vector > const& Pin) + { + _d = Pin[0][1] - 1; + V.resize(Pin.size() - 1, _d); + b.resize(Pin.size() - 1); + for (unsigned int i = 1; i < Pin.size(); i++) + { + b(i - 1) = Pin[i][0]; + for (unsigned int j = 1; j < _d + 1; j++) + { + V(i - 1, j - 1) = Pin[i][j]; + } + } + + conv_comb = new REAL[Pin.size()]; + row_mem = new REAL[V.rows()]; + row = new REAL[V.rows() + 1]; + colno = new int[V.rows() + 1]; + colno_mem = new int[V.rows()]; + + compute_eigenvectors(V.transpose()); + } + + template + void copy_array(T* source, T* result, size_t count) + { + T* tarray; + tarray = new T[count]; + std::copy_n(source, count, tarray); + delete [] result; + result = tarray; + } + + Zonotope& operator=(const Zonotope& other) + { + if (this != &other) { // protect against invalid self-assignment + _d = other._d; + V = other.V; + b = other.b; + T = other.T; + + copy_array(other.conv_comb, conv_comb, V.rows() + 1); + copy_array(other.row_mem, row_mem, V.rows()); + copy_array(other.row, row, V.rows() + 1); + copy_array(other.colno, colno, V.rows() + 1); + copy_array(other.colno_mem, colno_mem, V.rows()); + } + return *this; + } + + Zonotope& operator=(Zonotope&& other) + { + if (this != &other) { // protect against invalid self-assignment + _d = other._d; + V = other.V; + b = other.b; + T = other.T; + + conv_comb = other.conv_comb; other.conv_comb = nullptr; + row_mem = other.row_mem; other.row_mem = nullptr; + row = other.row; other.row = nullptr; + colno = other.colno; colno = nullptr; + colno_mem = other.colno_mem; colno_mem = nullptr; + } + return *this; + } + + + Zonotope(const Zonotope& other) : + _d{other._d}, V{other.V}, b{other.b}, T{other.T}, + conv_comb{new REAL[V.rows() + 1]}, + row_mem{new REAL[V.rows()]}, + row{new REAL[V.rows() + 1]}, + colno{new int[V.rows() + 1]}, + colno_mem{new int[V.rows()]} + { + std::copy_n(other.conv_comb, V.rows() + 1, conv_comb); + std::copy_n(other.row_mem, V.rows(), row_mem); + std::copy_n(other.row, V.rows() + 1, row); + std::copy_n(other.colno, V.rows() + 1, colno); + std::copy_n(other.colno_mem, V.rows(), colno_mem); + } + + Zonotope(Zonotope&& other) : + _d{other._d}, V{other.V}, b{other.b}, T{other.T}, + conv_comb{nullptr}, row_mem{nullptr}, row{nullptr}, + colno{nullptr}, colno_mem{nullptr} + { + conv_comb = other.conv_comb; other.conv_comb = nullptr; + row_mem = other.row_mem; other.row_mem = nullptr; + row = other.row; other.row = nullptr; + colno = other.colno; colno = nullptr; + colno_mem = other.colno_mem; colno_mem = nullptr; + } + + ~Zonotope() { + delete [] conv_comb; + delete [] colno; + delete [] colno_mem; + delete [] row; + delete [] row_mem; + } + + void set_interior_point(Point const& r) + { + _inner_ball.first = r; + } + + // return the dimension + unsigned int dimension() const + { + return _d; + } + + // this function returns 0. The main sampler requests this function to set the length of lambdas vector + int num_of_hyperplanes() const + { + return 0; + } + + + // return the number of parallelopipeds. Used in get_dists fnction. + unsigned int upper_bound_of_hyperplanes() const + { + return 2*_d; + } + + void compute_eigenvectors(MT const& G) + { + + int k = G.cols(); + MT ps = G; + sigma.resize(k,k); + sigma = ps.transpose()*ps; + sigma = (sigma + sigma.transpose()) / 2; + Eigen::SelfAdjointEigenSolver es(sigma); + + MT D = es.eigenvalues().asDiagonal(); + MT Q2 = es.eigenvectors(); + + Q0.resize(k,k-_d); + int count=0; + for (int i = 0; i < k; ++i) + { + if (es.eigenvalues()[i]<0.0000001) + { + for (int j = 0; j < k; ++j) + { + Q0(j, count) = Q2(j, i); + } + count++; + } + } + Eigen::JacobiSVD svd(Q0, Eigen::ComputeFullU | Eigen::ComputeFullV); + MT T2 = svd.matrixU().transpose(); + T.resize(_d,k); + for (int i = k-_d; i < k; ++i) + { + for (int j = 0; j < k; ++j) + { + T(i-k+_d,j) = T2(i,j); + } + } + + for (int i1 = 0; i1 < k; ++i1) + { + sigma(i1,i1) = sigma(i1,i1) + 0.00000001; + } + } + + MT get_T() const + { + return T; + } + + MT get_Q0() const + { + return Q0; + } + + MT get_sigma() const + { + return sigma; + } + + // return the number of vertices + int num_of_vertices() const + { + return V.rows(); + } + + std::pair InnerBall() const + { + return _inner_ball; + } + + void set_InnerBall(std::pair const& innerball) //const + { + _inner_ball = innerball; + } + + // return the number of generators + int num_of_generators() const + { + return V.rows(); + } + + // return the matrix V + MT get_mat() const + { + return V; + } + + // return the vector b + VT get_vec() const + { + return b; + } + + // change the matrix V + void set_mat(MT const& V2) + { + V = V2; + } + + // change the vector b + void set_vec(VT const& b2) + { + b = b2; + } + + Point get_mean_of_vertices() const + { + return Point(_d); + } + + + NT get_max_vert_norm() const + { + return 0.0; + } + + + // print polytope in input format + void print() + { +#ifdef VOLESTI_DEBUG + std::cout << " " << V.rows() << " " << _d << " float" << std::endl; +#endif + for (unsigned int i = 0; i < V.rows(); i++) { + for (unsigned int j = 0; j < _d; j++) { +#ifdef VOLESTI_DEBUG + std::cout << V(i, j) << " "; +#endif + } +#ifdef VOLESTI_DEBUG + std::cout<<"\n"; +#endif + } + } + + + // check if point p belongs to the convex hull of V-Polytope P + int is_in(Point const& p, NT tol=NT(0)) const + { + if(memLP_Zonotope(V, p, row_mem, colno_mem)) + { + return -1; + } + return 0; + } + + + // Compute an inner ball of the zonotope + std::pair ComputeInnerBall() + { + std::vector temp(_d,0); + NT radius = maxNT, min_plus; + Point center(_d); + + for (unsigned int i = 0; i < _d; ++i) { + temp.assign(_d,0); + temp[i] = 1.0; + Point v(_d,temp.begin(), temp.end()); + min_plus = intersect_line_Vpoly(V, center, v, conv_comb, + row, colno, false, true); + if (min_plus < radius) radius = min_plus; + } + + radius = radius / std::sqrt(NT(_d)); + _inner_ball = std::pair (center, radius); + return _inner_ball; + } + + // compute intersection point of ray starting from r and pointing to v + // with the Zonotope + std::pair line_intersect(Point const& r, Point const& v) const + { + return intersect_line_zono(V, r, v, conv_comb, colno); + } + + + // compute intersection point of ray starting from r and pointing to v + // with the Zonotope + std::pair line_intersect(Point const& r, + Point const& v, + VT const& Ar, + VT const& Av) const + { + return intersect_line_zono(V, r, v, conv_comb, colno); + } + + // compute intersection point of ray starting from r and pointing to v + // with the Zonotope + std::pair line_intersect(Point const& r, + Point const& v, + VT const& Ar, + VT const& Av, + NT const& lambda_prev) const + { + return intersect_line_zono(V, r, v, conv_comb, colno); + } + + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT const& Ar, + VT const& Av) const + { + return std::pair (intersect_line_Vpoly(V, r, v, conv_comb, + row, colno, + false, true), 1); + } + + + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT const& Ar, + VT const& Av, + NT const& lambda_prev) const + { + return line_positive_intersect(r, v, Ar, Av); + } + + //---------------------------accelarated billiard-----------------------------// + template + std::pair line_first_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + update_parameters& params) const + { + return line_positive_intersect(r, v, Ar, Av); + } + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + MT const& AA, + update_parameters& params) const + { + return line_positive_intersect(r, v, Ar, Av); + } + + template + std::pair line_positive_intersect(Point const& r, + Point const& v, + VT& Ar, + VT& Av, + NT const& lambda_prev, + update_parameters& params) const + { + return line_positive_intersect(r, v, Ar, Av); + } + //------------------------------------------------------------------------------// + + // Compute the intersection of a coordinate ray + // with the Zonotope + std::pair line_intersect_coord(Point const& r, + const unsigned int rand_coord, + VT const& lamdas) const + { + + std::vector temp(_d,0); + temp[rand_coord]=1.0; + Point v(_d,temp.begin(), temp.end()); + + return intersect_line_zono(V, r, v, conv_comb, colno); + + } + + + // Compute the intersection of a coordinate ray + // with the Zonotope + std::pair line_intersect_coord(Point const& r, + Point const& r_prev, + const unsigned int rand_coord, + const unsigned int rand_coord_prev, + VT const& lamdas) const + { + return line_intersect_coord(r, rand_coord, lamdas); + } + + + //------------------------------oracles for exponential sampling---------------////// + + // compute intersection points of a ray starting from r and pointing to v + // with polytope discribed by A and b + std::pair quadratic_positive_intersect(Point const& r, + Point const& v, + VT const& Ac, + NT const& T, + VT& Ar, + VT& Av, + int& facet_prev) const + { + throw std::runtime_error("Quadratic polynomial trajectories are supported only for H-polytopes"); + } + + std::pair quadratic_positive_intersect(Point const& r, + Point const& v, + VT const& Ac, + NT const& T, + VT& Ar, + VT& Av, + NT const& lambda_prev, + int& facet_prev) const + { + throw std::runtime_error("Quadratic polynomial trajectories are supported only for H-polytopes"); + } + + + //------------oracle for exact hmc spherical gaussian sampling---------------// + std::pair trigonometric_positive_intersect(Point const& r, Point const& v, + NT const& omega, int &facet_prev) const + { + return std::make_pair(0, 0); + } + + // shift polytope by a point c + // vector c has to be always the zero vector + void shift(VT const& c) + { + return; + } + + + // get number of parallelopipeds + // for each parallelopiped consider a lower bound for the distance from the origin + // useful for CG algorithm to get the first gaussian + std::vector get_dists(NT const& radius) const + { + std::vector res(upper_bound_of_hyperplanes(), radius); + return res; + } + + // apply linear transformation, of square matrix T, to thr Zonotope + void linear_transformIt(MT const& T) + { + MT V2 = T.inverse() * V.transpose(); + V = V2.transpose(); + } + + // return false to the rounding function + // no points are given so they have o be sampled + template + bool get_points_for_rounding (T const& randPoints) + { + return false; + } + + void normalize() {} + + void compute_reflection(Point &v, Point const& p, int const& facet) const + { + //compute_reflection(v, p, 0.0); + + int count = 0; + MT Fmat(_d-1,_d); + const NT e = 0.0000000001; + for (int j = 0; j < num_of_generators(); ++j) + { + if (((1.0 - *(conv_comb + j) ) > e || (1.0 - *(conv_comb + j) ) + > e*std::abs(*(conv_comb + j))) && + ((1.0 + *(conv_comb + j) ) > e || (1.0 + *(conv_comb + j) ) + > e*std::abs(*(conv_comb + j)))) + { + Fmat.row(count) = V.row(j); + count++; + } + } + + VT a = Fmat.fullPivLu().kernel(); + + if(p.getCoefficients().dot(a) < 0.0) a *= -1.0; + + a = a/a.norm(); + + // compute reflection + a *= (-2.0 * v.dot(a)); + v += a; + } + + void resetFlags() {} + + void update_position_internal(NT&){} + + template + void compute_reflection(Point &v, const Point &p, update_parameters const& params) const { + + int count = 0; + MT Fmat(_d-1,_d); + const NT e = 0.0000000001; + for (int j = 0; j < num_of_generators(); ++j) { + if (((1.0 - *(conv_comb + j) ) > e || (1.0 - *(conv_comb + j) ) > e*std::abs(*(conv_comb + j))) && + ((1.0 + *(conv_comb + j) ) > e || (1.0 + *(conv_comb + j) ) > e*std::abs(*(conv_comb + j)))) { + Fmat.row(count) = V.row(j); + count++; + } + } + + VT a = Fmat.fullPivLu().kernel(); + + if(p.getCoefficients().dot(a) < 0.0) a *= -1.0; + + a = a/a.norm(); + + // compute reflection + a *= (-2.0 * v.dot(a)); + v += a; + } + +}; + +#endif diff --git a/src/volesti/include/diagnostics/diagnostics.hpp b/src/volesti/include/diagnostics/diagnostics.hpp new file mode 100644 index 00000000..64905875 --- /dev/null +++ b/src/volesti/include/diagnostics/diagnostics.hpp @@ -0,0 +1,24 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef DIAGNOSTICS_DIAGNOSTICS_HPP +#define DIAGNOSTICS_DIAGNOSTICS_HPP + +#include "misc/print_table.hpp" +#include "diagnostics/multivariate_psrf.hpp" +#include "diagnostics/univariate_psrf.hpp" +#include "diagnostics/interval_psrf.hpp" +#include "diagnostics/geweke.hpp" +#include "diagnostics/raftery.hpp" +#include "diagnostics/effective_sample_size.hpp" +#include "diagnostics/thin_samples.hpp" +#include "diagnostics/print_diagnostics.hpp" + +#endif diff --git a/src/volesti/include/diagnostics/effective_sample_size.hpp b/src/volesti/include/diagnostics/effective_sample_size.hpp new file mode 100644 index 00000000..c372eb69 --- /dev/null +++ b/src/volesti/include/diagnostics/effective_sample_size.hpp @@ -0,0 +1,123 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#include + +#ifndef DIAGNOSTICS_EFFECTIVE_SAMPLE_SIZE_HPP +#define DIAGNOSTICS_EFFECTIVE_SAMPLE_SIZE_HPP + +template +void cummulative_minimum(std::vector &v) { + unsigned int N = v.size(); + for (unsigned int i = 1; i < N; i++) { + if (v[i] > v[i - 1]) v[i] = v[i - 1]; + } +} + +template +VT effective_sample_size(MT const& samples, unsigned int &min_ess) { + typedef Eigen::FFT EigenFFT; + typedef std::complex CNT; + EigenFFT fft; + + // Sample matrix is provided as d x n_samples + unsigned int d = samples.rows(); + unsigned int N = samples.cols(); + unsigned int N_even = N - N % 2; + + // Calculate effective sample size per dimension + VT ess; + ess.resize(d); + + // Autocorrelation vector + std::vector autocorrelation(N_even, NT(0)); + std::vector min_auto_correlation(N_even / 2, NT(0)); + + + // Z-normalized samples + std::vector normalized_sample_row(2 * N); + + // FFT vector + std::vector fft_vec(2 * N); + std::vector fft_inv_vec(2 * N); + std::vector psd(2 * N); + + // Helper variables + NT row_mean; + NT variance; + NT gap; + + for (unsigned int i = 0; i < d; i++) { + + // Z-normalization + row_mean = samples.row(i).mean(); + + for (int j = 0; j < N; j++) { + normalized_sample_row[j] = samples(i, j) - row_mean; + // Zero-padding + normalized_sample_row[j + N] = NT(0); + } + + variance = NT(0); + + for (int j = 0; j < N; j++) { + variance += std::pow(normalized_sample_row[j], 2); + } + + variance *= (1.0 / N); + + for (int j = 0; j < N; j++) { + normalized_sample_row[j] /= NT(1e-16 + sqrt(variance)); + } + + // Perform FFT on 2N points + fft.fwd(fft_vec, normalized_sample_row); + + // Calculate PSD which is the norm squared of the FFT of the sequence + for (int j = 0; j < 2 * N; j++) { + psd[j].real(std::norm(fft_vec[j])); + psd[j].imag(NT(0)); + } + + // Invert fft to get autocorrelation function + fft.inv(fft_inv_vec, psd); + + for (unsigned int j = 0; j < N_even; j++) { + autocorrelation[j] = std::real(fft_inv_vec[j]) / N; + } + + // Calculate minimum autocorrelation + for (int j = 0; j < N_even; j += 2) { + min_auto_correlation[j/2] = autocorrelation[j] + autocorrelation[j + 1]; + } + + gap = NT(0); + cummulative_minimum(min_auto_correlation); + + for (int j = 0; j < N_even / 2; j++) { + if (min_auto_correlation[j] > 0) gap += min_auto_correlation[j]; + } + + gap = 2 * gap - NT(1); + if (gap < NT(1)) gap = NT(1); + + ess(i) = (1.0 * N) / gap; + + // Store minimum effective sample size as integer (in general ess is not int) + // for the thinning process + if (i == 0) min_ess = (unsigned int) ceil(ess(i)); + else if ((unsigned int)ceil(ess(i)) < min_ess) min_ess = (unsigned int)ceil(ess(i)); + + } + + return ess; +} + +#endif diff --git a/src/volesti/include/diagnostics/ess_updater_autocovariance.hpp b/src/volesti/include/diagnostics/ess_updater_autocovariance.hpp new file mode 100644 index 00000000..4023a51c --- /dev/null +++ b/src/volesti/include/diagnostics/ess_updater_autocovariance.hpp @@ -0,0 +1,69 @@ +// VolEsti (volume computation and sampling library) +// Copyright (c) 2021 Vissarion Fisikopoulos +// Copyright (c) 2021 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef DIAGNOSTICS_ESS_UPDATER_AUTOCOVARIANCE_HPP +#define DIAGNOSTICS_ESS_UPDATER_AUTOCOVARIANCE_HPP + +#include +#include +#include +#include +#include +#include + +/** + Compute autocovariance estimates for every lag for the input sequence + using the Geyer's stable estimator given in + Charles J. Geyer, Practical Markov Chain Monte Carlo, Statistical Science 1992. + + * @tparam NT number type + * @tparam VT vector type + * @param samples the sequence of correlated samples + * @param auto_cov the autocovariance estimates +*/ +template +void compute_autocovariance(VT const& samples, VT &auto_cov) +{ + const NT eps = 1e-16; + typedef Eigen::FFT EigenFFT; + typedef Eigen::Matrix, Eigen::Dynamic, 1> CVT; + EigenFFT fft; + + unsigned int N = samples.size(); + NT samples_mean = samples.mean(); + auto_cov.setZero(N); + + // compute normalized samples + VT normalized_samples(2 * N); + normalized_samples.setZero(); + normalized_samples.head(N) = samples.array() - samples_mean; + + NT variance = (normalized_samples.cwiseProduct(normalized_samples)).sum(); + variance *= (1.0 / N); + variance += eps * (samples_mean*samples_mean); + normalized_samples.head(N) = normalized_samples.head(N).array() / sqrt(variance); + + // Perform FFT on 2N points + CVT frequency(2 * N); + fft.fwd(frequency, normalized_samples); + + // Invert fft to get autocorrelation function + CVT auto_cov_tmp(2 * N); + frequency = frequency.cwiseAbs2(); + fft.inv(auto_cov_tmp, frequency); + + auto_cov = auto_cov_tmp.head(N).real().array() / N; + + boost::accumulators::accumulator_set> accumulator; + for (int i = 0; i < samples.size(); ++i) + { + accumulator(samples.coeff(i)); + } + + auto_cov = auto_cov.array() * boost::accumulators::variance(accumulator); +} + +#endif diff --git a/src/volesti/include/diagnostics/ess_window_updater.hpp b/src/volesti/include/diagnostics/ess_window_updater.hpp new file mode 100644 index 00000000..99edb5aa --- /dev/null +++ b/src/volesti/include/diagnostics/ess_window_updater.hpp @@ -0,0 +1,154 @@ +// VolEsti (volume computation and sampling library) +// Copyright (c) 2021 Vissarion Fisikopoulos +// Copyright (c) 2021 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef DIAGNOSTICS_ESS_UPDATER_HPP +#define DIAGNOSTICS_ESS_UPDATER_HPP + +#include "ess_updater_autocovariance.hpp" + + +/** + This is a class that updates the effective sample size (ess) of a sample given a new chain + using Welford's algorithm to update the average values and the variance estimates where needed. + The chains has to be of the same length. The ess estimation exploits Geyer's stable estimator + for the autocovariance and the Geyer's conversion to a monotone sequence, given in, + + Charles J. Geyer, Practical Markov Chain Monte Carlo, Statistical Science 1992. + + * @tparam NT number type + * @tparam VT vector type + * @tparam MT matrix type +*/ +template +class ESSestimator { + +private: + unsigned int num_draws, max_s, s, d, num_chains, jj; + VT cm_mean, cm_var, cv_mean, draws, var_plus, ess, auto_cov; + NT oldM, rho_hat_odd, rho_hat_even, mean_var, M2, delta, new_elem; + MT acov_s_mean, rho_hat_s; + +public: + ESSestimator() {} + + ESSestimator(unsigned int const& _ndraws, unsigned int const& _dim) + { + num_draws = _ndraws; + d = _dim; + num_chains = 0; + + cm_mean.setZero(d); + cm_var.setZero(d); + cv_mean.setZero(d); + var_plus.setZero(d); + ess.setZero(d); + draws.setZero(num_draws); + acov_s_mean.setZero(num_draws-3, d); + rho_hat_s.setZero(num_draws, d); + } + + void update_estimator(MT const& samples) + { + num_chains++; + + for (int i = 0; i < d; i++) + { + draws = samples.row(i).transpose(); + compute_autocovariance(draws, auto_cov); + + new_elem = draws.mean(); + delta = new_elem - cm_mean.coeff(i); + cm_mean(i) += delta / NT(num_chains); + cm_var(i) += delta * (new_elem - cm_mean(i)); + + new_elem = auto_cov.coeff(0) * NT(num_draws) / (NT(num_draws) - 1.0); + delta = new_elem - cv_mean.coeff(i); + cv_mean(i) += delta / NT(num_chains); + + new_elem = auto_cov.coeff(1); + delta = new_elem - acov_s_mean.coeff(0, i); + acov_s_mean(0, i) += delta / NT(num_chains); + jj = 1; + while (jj < num_draws-4) + { + new_elem = auto_cov.coeff(jj+1); + delta = new_elem - acov_s_mean.coeff(jj, i); + acov_s_mean(jj, i) += delta / NT(num_chains); + + new_elem = auto_cov.coeff(jj+2); + delta = new_elem - acov_s_mean.coeff(jj+1, i); + acov_s_mean(jj+1, i) += delta / NT(num_chains); + + jj += 2; + } + } + } + + + void estimate_effective_sample_size() + { + rho_hat_s.setZero(num_draws, d); + + var_plus = cv_mean * (NT(num_draws) - 1.0) / NT(num_draws); + if (num_chains > 1) + { + VT cm_var_temp = cm_var * (1.0 / (NT(num_chains)-1.0)); + var_plus += cm_var_temp; + } + + for (int i = 0; i < d; i++) + { + rho_hat_even = 1.0; + rho_hat_s(0, i) = rho_hat_even; + rho_hat_odd = 1 - (cv_mean.coeff(i) - acov_s_mean.coeff(0, i)) / var_plus.coeff(i); + rho_hat_s(1, i) = rho_hat_odd; + + s = 1; + while (s < (num_draws - 4) && (rho_hat_even + rho_hat_odd) > 0) + { + rho_hat_even = 1.0 - (cv_mean.coeff(i) - acov_s_mean.coeff(s, i)) / var_plus.coeff(i); + rho_hat_odd = 1.0 - (cv_mean.coeff(i) - acov_s_mean.coeff(s+1, i)) / var_plus.coeff(i); + if ((rho_hat_even + rho_hat_odd) >= 0) + { + rho_hat_s(s + 1, i) = rho_hat_even; + rho_hat_s(s + 2, i) = rho_hat_odd; + } + s += 2; + } + + max_s = s; + // this is used in the improved estimate + if (rho_hat_even > 0) + { + rho_hat_s(max_s + 1, i) = rho_hat_even; + } + + // Convert Geyer's positive sequence into a monotone sequence + for (jj = 1; jj <= max_s - 3; jj += 2) + { + if (rho_hat_s(jj + 1, i) + rho_hat_s.coeff(jj + 2, i) > rho_hat_s.coeff(jj - 1, i) + rho_hat_s.coeff(jj, i)) + { + rho_hat_s(jj + 1, i) = (rho_hat_s.coeff(jj - 1, i) + rho_hat_s.coeff(jj, i)) / 2.0; + rho_hat_s(jj + 2, i) = rho_hat_s.coeff(jj + 1, i); + } + } + NT num_total_draws = NT(num_chains) * NT(num_draws); + NT tau_hat = -1.0 + 2.0 * rho_hat_s.col(i).head(max_s).sum() + rho_hat_s.coeff(max_s + 1, i); + ess(i) = num_total_draws / tau_hat; + } + } + + + VT get_effective_sample_size() + { + return ess; + } + +}; + + +#endif + diff --git a/src/volesti/include/diagnostics/geweke.hpp b/src/volesti/include/diagnostics/geweke.hpp new file mode 100644 index 00000000..7bfd291a --- /dev/null +++ b/src/volesti/include/diagnostics/geweke.hpp @@ -0,0 +1,79 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +//Licensed under GNU LGPL.3, see LICENCE file + +/* + This function implements a multivariate version of the Geweke diagnostic. + It is reduced to Hotelling's Two Sample test, which is a multivariate + extension of the common two sample Student's t-test. The null hypothesis + is that there is no difference between sample means. + + It is based on "Evaluating the accuracy of sampling-based approaches + to the calculation of posterior moments, 1992" by J. Geweke + + Inputs: samples, a matrix that contains sample points column-wise + frac_first, the portion of the first in order points in matrix samples + frac_last, the portion of the last in order points in matrix samples + alpha, the confidence level for the statistical test + + Output: A boolean to denote the result of Geweke diagnostic: + (i) false if the null hypothesis is rejected + (ii) true if the null hypothesis is not rejected +*/ + + +#ifndef DIAGNOSTICS_GEWEKE_HPP +#define DIAGNOSTICS_GEWEKE_HPP + +#include + +template +bool perform_geweke(MT const& samples, + NT frac_first = 0.1, + NT frac_last = 0.5, + NT alpha = 0.05) +{ + unsigned int d = samples.rows(), N = samples.cols(); + unsigned int N1 = N * frac_first; + unsigned int N2 = N * frac_last; + + // Compute sample means and covariances + VT mean1 = samples.block(0, 0, d, N1).rowwise().mean(); + VT mean2 = samples.block(0, N - N2, d, N2).rowwise().mean(); + + MT norm_chain1 = samples.block(0, 0, d, N1).colwise() - mean1; + MT norm_chain2 = samples.block(0, N - N2, d, N2).colwise() - mean2; + + MT sigma1 = (norm_chain1 * norm_chain1.transpose()) / (NT(N1) - 1.0); + MT sigma2 = (norm_chain2 * norm_chain2.transpose()) / (NT(N2) - 1.0); + + // Compute the pooled covariance matrix + MT S_pl = ((NT(N1) - NT(1)) * sigma1 + (NT(N2) - 1.0) * sigma2) / (NT(N1) + NT(N2) - NT(2)); + + // T2 follows Hotelling's T-squared distribution under the assumption of + // equal covariances and when the null hypothesis is true + NT T2 = (mean1 - mean2).transpose() * S_pl.inverse() * (mean1 - mean2); + T2 = ((NT(N1) * NT(N2)) / (NT(N1) + NT(N2))) * T2; + + // U follows Fischer distribution + // We use this transformation to check the null hypothesis more easily + NT U = ((NT(N1) + NT(N2) - NT(d) - 1.0) / ((NT(N1) + NT(N2) - 2.0) * NT(d))) * T2; + + boost::math::fisher_f dist(d, int(N1) + int(N2) - d - 1); + + NT F1 = boost::math::quantile(dist, alpha / 2.0); + NT F2 = boost::math::quantile(boost::math::complement(dist, alpha / 2.0)); + + if (U <= F1 || U > F2) { // reject null hypothesis + return false; + } + return true; // do not reject null hypothesis + +} + +#endif diff --git a/src/volesti/include/diagnostics/interval_psrf.hpp b/src/volesti/include/diagnostics/interval_psrf.hpp new file mode 100644 index 00000000..cbcdfd10 --- /dev/null +++ b/src/volesti/include/diagnostics/interval_psrf.hpp @@ -0,0 +1,79 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +//Licensed under GNU LGPL.3, see LICENCE file + +/* + This function implements the interval diagnostic of Brooks & Gelman. + It is based on "General Methods for Monitoring Convergence of Iterative Simulations, 1998" by S. Brooks and A. Gelman + + For each coordinate the sample is splitted into two parts. + Then the psrf of S. Brooks and A. Gelman is computed for each coordinate. + + Inputs: samples, a matrix that contains sample points column-wise + + Output: The value of interval PSRF of S. Brooks and A. Gelman for each coordinate +*/ + +#ifndef DIAGNOSTICS_INTERVAL_PSRF_HPP +#define DIAGNOSTICS_INTERVAL_PSRF_HPP + +template +VT interval_psrf(MT const& samples, NT alpha = 0.05) +{ + MT runs = samples.transpose(); + unsigned int N = samples.cols(), d = samples.rows(); + unsigned int N1 = N / 2; + unsigned int N2 = N - N1; + VT sorted_samples(N), marginal_samples(N), sorted_subsamples1(N1), sorted_subsamples2(N2), results(d); + std::vector temp_col(N); + + for (int i = 0; i < d; i++) + { + sorted_samples = runs.col(i); + marginal_samples = runs.col(i); + + temp_col.resize(N); + temp_col = std::vector(&sorted_samples[0], sorted_samples.data() + sorted_samples.cols() * + sorted_samples.rows()); + std::sort(temp_col.begin(), temp_col.end()); + sorted_samples = Eigen::Map(&temp_col[0], temp_col.size()); + + int n1 = N * (alpha / NT(2)), n2 = N - N * (alpha / NT(2)); + + NT len_total_sequence_interval = sorted_samples(n2) - sorted_samples(n1); + + sorted_subsamples1 = marginal_samples.block(0,0,N1,1); + temp_col.resize(N1); + temp_col = std::vector(&sorted_subsamples1[0], sorted_subsamples1.data() + + sorted_subsamples1.cols() * sorted_subsamples1.rows()); + std::sort(temp_col.begin(), temp_col.end()); + sorted_subsamples1 = Eigen::Map(&temp_col[0], temp_col.size()); + + sorted_subsamples2 = marginal_samples.block(N1,0,N2,1); + temp_col.resize(N2); + temp_col = std::vector(&sorted_subsamples2[0], sorted_subsamples2.data() + + sorted_subsamples2.cols() * sorted_subsamples2.rows()); + std::sort(temp_col.begin(), temp_col.end()); + sorted_subsamples2 = Eigen::Map(&temp_col[0], temp_col.size()); + + n1 = N1 * (alpha / NT(2)), n2 = N1 - N1 * (alpha / NT(2)); + NT len_sequence_interval1 = sorted_subsamples1(n2) - sorted_subsamples1(n1); + + n1 = N2 * (alpha / NT(2)), n2 = N2 - N2 * (alpha / NT(2)); + NT len_sequence_interval2 = sorted_subsamples2(n2) - sorted_subsamples2(n1); + + NT R = (len_total_sequence_interval) / + ((len_sequence_interval1 + len_sequence_interval2) / NT(2)); + + results(i) = std::abs(1.0 - R) + NT(1); + } + return results; +} + + +#endif diff --git a/src/volesti/include/diagnostics/multivariate_psrf.hpp b/src/volesti/include/diagnostics/multivariate_psrf.hpp new file mode 100644 index 00000000..883b28d6 --- /dev/null +++ b/src/volesti/include/diagnostics/multivariate_psrf.hpp @@ -0,0 +1,55 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +//Licensed under GNU LGPL.3, see LICENCE file + +/* + This function implements a multivariate version of the Rubin & Gelman diagnostic. + It is based on "Inference from iterative simulation using multiple sequences, 1992" by D. B. Rubin and A. Gelman + and "General Methods for Monitoring Convergence of Iterative Simulations, 1998" by S. Brooks and A. Gelman + + The sample is splitted into two parts. Then a multivariate psrf is computed as proposed by S. Brooks and A. Gelman + + Inputs: samples, a matrix that contains sample points column-wise + + Output: The value of multivariate PSRF by S. Brooks and A. Gelman +*/ + +#ifndef DIAGNOSTICS_PSRF_HPP +#define DIAGNOSTICS_PSRF_HPP + +template +NT multivariate_psrf(MT const& samples) +{ + unsigned int N = samples.cols(), d = samples.rows(); + unsigned int N1 = N / 2; + unsigned int N2 = N - N1; + + VT mean1 = samples.block(0, 0, d, N1).rowwise().mean(); + VT mean2 = samples.block(0, N1, d, N - N1).rowwise().mean(); + + MT norm_chain1 = samples.block(0, 0, d, N1).colwise() - mean1; + MT norm_chain2 = samples.block(0, N1, d, N - N1).colwise() - mean2; + + MT W = ((norm_chain1 * norm_chain1.transpose()) / (NT(N1) - 1.0) + + (norm_chain2 * norm_chain2.transpose()) / (NT(N2) - 1.0)) / NT(2); + + VT mean00 = (mean1 + mean2) / 2.0; + + MT B = (mean1 - mean00) * (mean1 - mean00).transpose() + + (mean2 - mean00) * (mean2 - mean00).transpose(); + + MT WB = W.inverse() * B; + Eigen::SelfAdjointEigenSolver eigensolver(WB); + NT l_max = eigensolver.eigenvalues().maxCoeff(); + + NT R = (NT(N1) - NT(1))/NT(N1) + 1.5 * l_max; + return R; +} + + +#endif diff --git a/src/volesti/include/diagnostics/print_diagnostics.hpp b/src/volesti/include/diagnostics/print_diagnostics.hpp new file mode 100644 index 00000000..b009d6d0 --- /dev/null +++ b/src/volesti/include/diagnostics/print_diagnostics.hpp @@ -0,0 +1,56 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef DIAGNOSTICS_PRINT_DIAGNOSTICS_HPP +#define DIAGNOSTICS_PRINT_DIAGNOSTICS_HPP + +template +void print_diagnostics(MT const& samples, unsigned int &min_ess, StreamType &stream) { + + unsigned int d = samples.rows(); + unsigned int N = samples.cols(); + + VariadicTable vt( + {"Dimension", + "Average", + "Standard Deviation", + "Effective Sample Size", + "Interval PSRF (50%)" + }); + + VT ess = effective_sample_size(samples, min_ess); + VT intv_psrf = interval_psrf(samples); + + NT row_mean, row_std; + + vt.setColumnPrecision({1, 3, 3, 3, 3}); + + vt.setColumnFormat({VariadicTableColumnFormat::AUTO, + VariadicTableColumnFormat::SCIENTIFIC, + VariadicTableColumnFormat::SCIENTIFIC, + VariadicTableColumnFormat::SCIENTIFIC, + VariadicTableColumnFormat::SCIENTIFIC}); + + for (unsigned int i = 0; i < d; i++) { + row_mean = samples.row(i).mean(); + row_std = NT(0); + for (int j = 0; j < N; j++) { + row_std += std::pow(samples(i, j) - row_mean, 2); + } + row_std = sqrt(row_std / N); + vt.addRow(i + 1, row_mean, row_std, ess(i), intv_psrf(i)); + } + + vt.print(stream); + std::cout << "interval_psrf = " << intv_psrf.maxCoeff() << "us" << std::endl; +} + + +#endif diff --git a/src/volesti/include/diagnostics/raftery.hpp b/src/volesti/include/diagnostics/raftery.hpp new file mode 100644 index 00000000..1fa087a5 --- /dev/null +++ b/src/volesti/include/diagnostics/raftery.hpp @@ -0,0 +1,142 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +//Licensed under GNU LGPL.3, see LICENCE file + +/* + This function implements a multivariate version of the raftery & Lewis diagnostic. + It is based on Matlab version of coda package in http://www.spatial-econometrics.com/gibbs/ + and "How many iterations in the Gibbs sampler?, 1992" by A. Raftery and S. Lewis + + Inputs: samples, a matrix that contains sample points column-wise + q, the quantile of the quantity of interest. The default value is 0.025. + r, the level of precision desired. The default value is 0.01. + s, the probability associated with r. The default value is 0.95. + + Outputs: (i) The number of draws required for burn-in + (ii) The skip parameter for 1st-order Markov chain + (iii) The skip parameter sufficient to get independence chain + (iv) The number of draws required to achieve r precision + (v) The number of draws if the chain is white noise + (vi) The I-statistic from Raftery and Lewis (1992) +*/ + +#ifndef DIAGNOSTICS_RAFTERY_HPP +#define DIAGNOSTICS_RAFTERY_HPP + +template +NT round_to_zero(NT x) +{ + return (x > 0.0) ? std::floor(x) : std::ceil(x); +} + +#include "raftery_subroutines/empquant.hpp" +#include "raftery_subroutines/indtest.hpp" +#include "raftery_subroutines/mctest.hpp" +#include "raftery_subroutines/mcest.hpp" +#include "raftery_subroutines/thin.hpp" +#include "raftery_subroutines/ppnd.hpp" + + +template +MT perform_raftery(MT const& samples, NT const& q, NT const& r, NT const& s) +{ + MT runs = samples.transpose(); + + typedef Eigen::Matrix MTint; + typedef Eigen::Matrix VTint; + + unsigned int n = runs.rows(), d = runs.cols(), kthin, kmind; + MT results(d, 6); + MTint work = MTint::Zero(n, d); + VTint tmp = VTint::Zero(n); + std::pair xy; + std::pair g2bic; + + NT cutpt, alpha, beta, g2, bic, epss; + int tcnt; + + MT sorted_samples(n, d); + VT a(n); + std::vector temp_col(n); + + for (int i = 0; i < d; i++) + { + a = runs.col(i); + temp_col = std::vector(&a[0], a.data() + a.cols() * a.rows()); + std::sort(temp_col.begin(), temp_col.end()); + sorted_samples.col(i) = Eigen::Map(&temp_col[0], temp_col.size()); + } + + for (int i = 0; i < d; i++) + { + cutpt = empquant(sorted_samples.col(i), q); + for (int j = 0; j < n; j++) + { + if (runs(j, i) <= cutpt) work(j, i) = 1; + } + kthin = 1; bic = 1.0; epss = 0.001; + + while(bic > 0.0) + { + xy = thin(work.col(i), n, kthin); + tcnt = xy.first; + tmp = xy.second; + g2bic = mctest(tmp, tcnt); + g2 = g2bic.first; + bic = g2bic.second; + kthin++; + if (kthin > n / 2) { + break; + } + } + + kthin--; + g2bic = mcest(tmp, tcnt); + alpha = g2bic.first; + beta = g2bic.second; + kmind = kthin; + g2bic = indtest(tmp, tcnt); + g2 = g2bic.first; + bic = g2bic.second; + + while (bic > 0.0) + { + xy = thin(work.col(i), n, kmind); + tcnt = xy.first; + tmp = xy.second; + g2bic = indtest(tmp, tcnt); + g2 = g2bic.first; + bic = g2bic.second; + kmind++; + if (kmind > n) { + break; + } + } + + NT psum = alpha + beta; + NT tmp1 = std::log(psum * epss / std::max(alpha, beta)) / std::log(std::abs(1.0 - psum)); + NT nburn = round_to_zero((tmp1 + 1.0) * NT(kthin)); + NT phi = ppnd((s + 1.0) / 2.0); + NT tmp2 = (2.0 - psum) * alpha * beta * (phi * phi) / (psum * psum * psum * (r * r)); + NT nprec = round_to_zero(tmp2 + 1.0) * kthin; + NT nmin = round_to_zero(((1.0 - q) * q * (phi * phi) / (r * r)) + 1.0); + NT irl = (nburn + nprec) / nmin; + NT kind = std::max(round_to_zero(irl + 1.0), NT(kmind)); + + results(i, 0) = NT(kthin); + results(i, 1) = NT(nburn); + results(i, 2) = kind; + results(i, 3) = NT(nburn) + nprec; + results(i, 4) = nmin; + results(i, 5) = irl; + } + return results; +} + + +#endif diff --git a/src/volesti/include/diagnostics/raftery_subroutines/empquant.hpp b/src/volesti/include/diagnostics/raftery_subroutines/empquant.hpp new file mode 100644 index 00000000..6604f0ad --- /dev/null +++ b/src/volesti/include/diagnostics/raftery_subroutines/empquant.hpp @@ -0,0 +1,32 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +//Based on Matlab version of coda package in http://www.spatial-econometrics.com/gibbs/ + +#ifndef EMPQUANT_HPP +#define EMPQUANT_HPP + + +template +NT empquant(VT const& sorted_samples, NT const& q) +{ + unsigned int n = sorted_samples.rows(); + + NT order = (n - 1) * q + 1.0; + NT fract = order - NT(int(order)); + int low = std::max(round_to_zero(order), 1.0); + int high = std::min(low + 1.0, NT(n)); + + NT y = (1.0 - fract) * sorted_samples(low - 1) + fract * sorted_samples(high - 1); + + return y; +} + + +#endif diff --git a/src/volesti/include/diagnostics/raftery_subroutines/indtest.hpp b/src/volesti/include/diagnostics/raftery_subroutines/indtest.hpp new file mode 100644 index 00000000..8af7a329 --- /dev/null +++ b/src/volesti/include/diagnostics/raftery_subroutines/indtest.hpp @@ -0,0 +1,43 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +//Based on Matlab version of coda package in http://www.spatial-econometrics.com/gibbs/ + +#ifndef INDTEST_HPP +#define INDTEST_HPP + + +template +std::pair indtest(VT const& d, int const& n) +{ + MT t = MT::Zero(2, 2); + int t1, t2; + NT fitted, focus; + for (int i1 = 1; i1 < n; i1++){ + t(d(i1 - 1), d(i1))=t(d(i1 - 1), d(i1)) + 1; + } + NT dcm1 = NT(n) - 1.0, g2 = 0.0; + for (int i1 = 0; i1 < 2; i1++){ + for (int i2 = 0; i2 < 2; i2++){ + if (t(i1, i2) != 0){ + t1 = t(i1, 0) + t(i1, 1); + t2 = t(0, i2) + t(1, i2); + fitted = (NT(t1) * NT(t2)) / dcm1; + focus = NT(t(i1, i2)); + g2 = g2 + std::log(focus / fitted) * focus; + } + } + } + g2 = g2 * 2.0; + NT bic = g2 - std::log(dcm1); + return std::pair(g2, bic); +} + + +#endif diff --git a/src/volesti/include/diagnostics/raftery_subroutines/mcest.hpp b/src/volesti/include/diagnostics/raftery_subroutines/mcest.hpp new file mode 100644 index 00000000..8f29021b --- /dev/null +++ b/src/volesti/include/diagnostics/raftery_subroutines/mcest.hpp @@ -0,0 +1,28 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +//Based on Matlab version of coda package in http://www.spatial-econometrics.com/gibbs/ + +#ifndef MCEST_HPP +#define MCEST_HPP + +template +std::pair mcest(VT const& d, int const& n) +{ + MT t = MT::Zero(2, 2); + for (int i1 = 1; i1 < n; i1++){ + t(d(i1 - 1), d(i1)) = t(d(i1 - 1), d(i1)) + 1; + } + NT alpha = NT(t(0, 1)) / NT((t(0, 0) + t(0, 1))), beta = NT(t(1, 0)) / NT((t(1, 0)+t(1, 1))); + + return std::pair(alpha, beta); +} + + +#endif diff --git a/src/volesti/include/diagnostics/raftery_subroutines/mctest.hpp b/src/volesti/include/diagnostics/raftery_subroutines/mctest.hpp new file mode 100644 index 00000000..f5108db8 --- /dev/null +++ b/src/volesti/include/diagnostics/raftery_subroutines/mctest.hpp @@ -0,0 +1,63 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +//Based on Matlab version of coda package in http://www.spatial-econometrics.com/gibbs/ + +#ifndef MCTEST_HPP +#define MCTEST_HPP + + +template +std::pair mctest(VT const& d, int const& n) +{ + MT m1 = MT::Zero(2,2), m2 = MT::Zero(2,2); + NT g2 = 0.0, bic = 0.0, fitted; + int i1, i2, i3, t1, t2, t3, t4, focus; + + for (int i=2; i(g2, bic); +} + + +#endif diff --git a/src/volesti/include/diagnostics/raftery_subroutines/ppnd.hpp b/src/volesti/include/diagnostics/raftery_subroutines/ppnd.hpp new file mode 100644 index 00000000..c2c8b630 --- /dev/null +++ b/src/volesti/include/diagnostics/raftery_subroutines/ppnd.hpp @@ -0,0 +1,56 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +//Based on Matlab version of coda package in http://www.spatial-econometrics.com/gibbs/ + +#ifndef PPND_HPP +#define PPND_HPP + +template +NT ppnd(NT const& p) +{ + NT split1 = 0.425, split2 = 5.0, const1 = 0.180625, const2 = 1.6, a0=3.3871327179e+00, + a1 = 5.0434271938e+01, a2 = 1.5929113202e+02, a3 = 5.9109374720e+01, b1 = 1.7895169469e+01, + b2 = 7.8757757664e+01, b3 = 6.7187563600e+01, c0 = 1.4234372777e+00, c1 = 2.7568153900e+00, + c2 = 1.3067284816e+00, c3 = 1.7023821103e-01, d1 = 7.3700164250e-01, d2 = 1.2021132975e-01, + e0 = 6.6579051150e+00, e1 = 3.0812263860e+00, e2 = 4.2868294337e-01, e3 = 1.7337203997e-02, + f1 = 2.4197894225e-01, f2 = 1.2258202635e-02; + + NT q = p - 0.5, r, y; + + if (std::abs(q) <= split1){ + r = const1 - q * q; + y = q * (((a3 * r + a2) * r + a1) * r + a0) / (((b3 * r + b2) * r + b1) * r + 1.0); + return y; + } else if (q < 0.0){ + r = p; + } else{ + r = 1 - p; + } + if (r <= 0.0){ + return 0.0; + } + + r = std::sqrt(-1.0 * std::log(r)); + + if (r <= split2){ + r = r - const2; + y = (((c3 * r + c2) * r + c1) * r + c0) / ((d2 * r + d1) * r + 1.0); + } else{ + r = r - split2; + y = (((e3 * r + e2) * r + e1) * r + e0)/((f2 * r + f1) * r + 1.0); + } + + if (q < 0.0) return -y; + + return y; +} + +#endif + diff --git a/src/volesti/include/diagnostics/raftery_subroutines/thin.hpp b/src/volesti/include/diagnostics/raftery_subroutines/thin.hpp new file mode 100644 index 00000000..c9b5ec10 --- /dev/null +++ b/src/volesti/include/diagnostics/raftery_subroutines/thin.hpp @@ -0,0 +1,32 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +//Based on Matlab version of coda package in http://www.spatial-econometrics.com/gibbs/ + +#ifndef THIN_HPP +#define THIN_HPP + +template +std::pair thin(VT const& work, unsigned int const& n, unsigned int const& kthin) +{ + VT y((n-1) / kthin + 1); + + int i = 0, j = 0; + while (i < n) + { + y(j) = work(i); + j++; + i += kthin; + } + + return std::pair((n-1) / kthin + 1, y); +} + + +#endif diff --git a/src/volesti/include/diagnostics/thin_samples.hpp b/src/volesti/include/diagnostics/thin_samples.hpp new file mode 100644 index 00000000..8e41fe2f --- /dev/null +++ b/src/volesti/include/diagnostics/thin_samples.hpp @@ -0,0 +1,37 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef DIAGNOSTICS_THIN_SAMPLES_HPP +#define DIAGNOSTICS_THIN_SAMPLES_HPP + +template +MT thin_samples(MT const& samples, NT const& min_ess) { + + // Sample matrix is provided as d x n_samples + unsigned int d = samples.rows(); + unsigned int N = samples.cols(); + unsigned int gap; + unsigned int N_gap; + + // Thin samples are the initial samples which are N / min_ess apart + gap = N / min_ess; + N_gap = N - N % gap; + + MT thin_samples; + thin_samples.resize(d, N_gap / gap); + + for (int i = 0; i < N_gap; i += gap) { + thin_samples.col(i / gap) = samples.col(i); + } + + return thin_samples; +} + +#endif diff --git a/src/volesti/include/diagnostics/univariate_psrf.hpp b/src/volesti/include/diagnostics/univariate_psrf.hpp new file mode 100644 index 00000000..3a863908 --- /dev/null +++ b/src/volesti/include/diagnostics/univariate_psrf.hpp @@ -0,0 +1,67 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +//Licensed under GNU LGPL.3, see LICENCE file + +/* + This function implements the Rubin & Gelman diagnostic. + It is based on "Inference from iterative simulation using multiple sequences, 1992" by D. B. Rubin and A. Gelman + + For each coordinate the sample is splitted into two parts. + Then the psrf of D.B. Rubin and A. Gelman is computed for each coordinate + + Inputs: samples, a matrix that contains sample points column-wise + + Output: The value of PSRF of D.B. Rubin and A. Gelman for each coordinate +*/ + +#ifndef DIAGNOSTICS_MARGINAL_PSRF_HPP +#define DIAGNOSTICS_MARGINAL_PSRF_HPP + +template +VT univariate_psrf(MT const& samples) +{ + MT runs = samples.transpose(); + unsigned int N = samples.cols(), d = samples.rows(); + unsigned int N1 = N / 2; + unsigned int N2 = N - N1; + VT coord_samples(N), results(d); + NT mean1, mean2, mean00, sum, R, W, B, sigma; + + for (int i = 0; i < d; i++) + { + coord_samples = runs.col(i); + mean1 = coord_samples.block(0,0,N1,1).mean(); + mean2 = coord_samples.block(N1,0,N2,1).mean(); + + sum = NT(0); + for (int j = 0; j < N1; j++) + { + sum += (coord_samples(j) - mean1) * (coord_samples(j) - mean1); + } + W = sum / (NT(N1) - NT(1)); + + sum = NT(0); + for (int j = N1; j < N; j++) + { + sum += (coord_samples(j) - mean2) * (coord_samples(j) - mean2); + } + W += (sum / (NT(N2) - NT(1))); + W = W / NT(2); + + mean00 = coord_samples.mean(); + + B = (mean1 - mean00) * (mean1 - mean00) + (mean2 - mean00) * (mean2 - mean00); + sigma = ((NT(N1) - NT(1)) / NT(N1)) * W + B; + R = std::sqrt(sigma / W); + + results(i) = R; + } + return results; +} + +#endif diff --git a/src/volesti/include/generators/boost_random_number_generator.hpp b/src/volesti/include/generators/boost_random_number_generator.hpp new file mode 100644 index 00000000..d15fee15 --- /dev/null +++ b/src/volesti/include/generators/boost_random_number_generator.hpp @@ -0,0 +1,95 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2020 Vissarion Fisikopoulos + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef GENERATORS_BOOST_RANDOM_NUMBER_GENERATOR_HPP +#define GENERATORS_BOOST_RANDOM_NUMBER_GENERATOR_HPP + +#include +#include + +/////////////////// Random numbers generator +/// +/// \tparam RNGType +/// \tparam NT +/// \tparam Ts + +template +struct BoostRandomNumberGenerator; + +template +struct BoostRandomNumberGenerator +{ + BoostRandomNumberGenerator(int d) + : _rng(std::chrono::system_clock::now().time_since_epoch().count()) + , _urdist(0, 1) + , _uidist(0, d-1) + , _ndist(0, 1) + {} + + NT sample_urdist() + { + return _urdist(_rng); + } + + NT sample_uidist() + { + return _uidist(_rng); + } + + NT sample_ndist() + { + return _ndist(_rng); + } + + void set_seed(unsigned rng_seed){ + _rng.seed(rng_seed); + } + +private : + RNGType _rng; + boost::random::uniform_real_distribution _urdist; + boost::random::uniform_int_distribution<> _uidist; + boost::random::normal_distribution _ndist; +}; + + +template +struct BoostRandomNumberGenerator +{ + BoostRandomNumberGenerator(int d=1) + : _rng(Seed) + , _urdist(0, 1) + , _uidist(0, d-1) + , _ndist(0, 1) + {} + + NT sample_urdist() + { + return _urdist(_rng); + } + + NT sample_uidist() + { + return _uidist(_rng); + } + + NT sample_ndist() + { + return _ndist(_rng); + } + + void set_seed(unsigned rng_seed){ + _rng.seed(rng_seed); + } + +private : + RNGType _rng; + boost::random::uniform_real_distribution _urdist; + boost::random::uniform_int_distribution<> _uidist; + boost::random::normal_distribution _ndist; +}; + +#endif // GENERATORS_BOOST_RANDOM_NUMBER_GENERATOR_HPP diff --git a/src/volesti/include/generators/convex_bodies_generator.h b/src/volesti/include/generators/convex_bodies_generator.h new file mode 100644 index 00000000..f80b98d1 --- /dev/null +++ b/src/volesti/include/generators/convex_bodies_generator.h @@ -0,0 +1,124 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef CONVEX_BODIES_GEN_H +#define CONVEX_BODIES_GEN_H + +#include + +#include "convex_bodies/convex_body.h" + +#ifndef isnan + using std::isnan; +#endif + +/// This function generates a unit ball of given dimension +/// @tparam ConvexBody Type of returned Convex Body +template +ConvexBody generate_unit_ball(unsigned int dim) { + + typedef typename ConvexBody::MT MT; + typedef typename ConvexBody::VT VT; + typedef typename ConvexBody::NT NT; + typedef typename ConvexBody::PointType Point; + typedef std::function func; + typedef std::function grad; + + func unit_ball_func = [](const Point &x) { + return x.dot(x) - 1; + }; + + grad unit_ball_grad = [](const Point &x) { + return 2 * x; + }; + + std::vector unit_ball_funcs{unit_ball_func}; + std::vector unit_ball_grads{unit_ball_grad}; + + return ConvexBody(unit_ball_funcs, unit_ball_grads, dim); +} + +/// This function generates a unit ball of given dimension intersected by a hyperplane +/// @tparam ConvexBody Type of returned Convex Body +template +ConvexBody generate_unit_ball_intersect_hyperplane(unsigned int dim) { + + typedef typename ConvexBody::MT MT; + typedef typename ConvexBody::VT VT; + typedef typename ConvexBody::NT NT; + typedef typename ConvexBody::PointType Point; + typedef std::function func; + typedef std::function grad; + + func unit_ball_func = [](const Point &x) { + return x.dot(x) - 1; + }; + + func hyperplane_func = [](const Point &x) { + return x[0] - 0.5; + }; + + grad unit_ball_grad = [](const Point &x) { + return 2 * x; + }; + + grad hyperplane_grad = [](const Point &x) { + Point v = Point(x.dimension()); + v.set_coord(0, 1); + return v; + }; + + std::vector unit_ball_funcs{unit_ball_func, hyperplane_func}; + std::vector unit_ball_grads{unit_ball_grad, hyperplane_grad}; + + return ConvexBody(unit_ball_funcs, unit_ball_grads, dim); +} + +/// This function generates a unit ball of given dimension intersected by a logsum exponential function +/// @tparam ConvexBody Type of returned Convex Body +template +ConvexBody generate_unit_ball_intersect_logsumexp(unsigned int dim) { + + typedef typename ConvexBody::MT MT; + typedef typename ConvexBody::VT VT; + typedef typename ConvexBody::NT NT; + typedef typename ConvexBody::PointType Point; + typedef std::function func; + typedef std::function grad; + + func unit_ball_func = [](const Point &x) { + return x.dot(x) - pow(x.dimension(), 2); + }; + + func logsumexp_func = [](const Point &x) { + typedef typename Point::FT NT; + NT s = 0; + for (unsigned int i = 0; i < x.dimension(); i++) { + s += exp(x[i]); + } + return log(s); + }; + + grad unit_ball_grad = [](const Point &x) { + return 2 * x; + }; + + grad logsumexp_grad = [](const Point &x) { + Point z(x.dimension()); + for (unsigned int i = 0; i < x.dimension(); i++) { + z.set_coord(i, exp(x[i])); + } + return (1 / z.sum()) * z; + }; + + std::vector unit_ball_funcs{unit_ball_func, logsumexp_func}; + std::vector unit_ball_grads{unit_ball_grad, logsumexp_grad}; + + return ConvexBody(unit_ball_funcs, unit_ball_grads, dim); +} + +#endif diff --git a/src/volesti/include/generators/h_polytopes_generator.h b/src/volesti/include/generators/h_polytopes_generator.h new file mode 100644 index 00000000..dae44bdc --- /dev/null +++ b/src/volesti/include/generators/h_polytopes_generator.h @@ -0,0 +1,61 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef H_POLYTOPES_GEN_H +#define H_POLYTOPES_GEN_H + +#include + + +#ifndef isnan + using std::isnan; +#endif + +/// This function generates a random H-polytope of given dimension and number of hyperplanes $m$ +/// @tparam Polytope Type of returned polytope +/// @tparam RNGType RNGType Type +template +Polytope random_hpoly(unsigned int dim, unsigned int m, double seed = std::numeric_limits::signaling_NaN()) { + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + typedef typename Polytope::PointType Point; + + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + if (!isnan(seed)) { + unsigned rng_seed = seed; + rng.seed(rng_seed); + } + + MT A(m, dim); + VT b(m); + Point p(dim); + + for (int i = 0; i < m; ++i) { + boost::normal_distribution<> rdist(0, 1); + NT normal = NT(0); + NT *data = p.pointerToData(); + + //RNGType rng2 = var.rng; + for (unsigned int i = 0; i < dim; ++i) { + *data = rdist(rng); + normal += *data * *data; + data++; + } + + normal = 1.0 / std::sqrt(normal); + p *= normal; + A.row(i) = p.getCoefficients(); + b(i) = 10.0; + } + + return Polytope(dim, A, b); +} + +#endif diff --git a/src/volesti/include/generators/known_polytope_generators.h b/src/volesti/include/generators/known_polytope_generators.h new file mode 100644 index 00000000..050918ad --- /dev/null +++ b/src/volesti/include/generators/known_polytope_generators.h @@ -0,0 +1,361 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef KNOWN_POLYTOPE_GENERATORS_H +#define KNOWN_POLYTOPE_GENERATORS_H + +#include + +#include "convex_bodies/hpolytope.h" +#include "convex_bodies/vpolytope.h" + +/// This function generates a hypercube of given dimension +/// The result can be either in V-representation (Vpoly=true) or in H-representation (V-poly-false) +/// @tparam Polytope Type of returned polytope +template +Polytope generate_cube(const unsigned int& dim, const bool& Vpoly) { + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + MT A; + VT b; + unsigned int m; + + if (!Vpoly) { + + A.resize(2 * dim, dim); + b.resize(2 * dim); + for (unsigned int i = 0; i < dim; ++i) { + b(i) = 1.0; + for (unsigned int j = 0; j < dim; ++j) { + if (i == j) { + A(i, j) = 1.0; + } else { + A(i, j) = 0.0; + } + } + } + for (unsigned int i = 0; i < dim; ++i) { + b(i + dim) = 1.0; + for (unsigned int j = 0; j < dim; ++j) { + if (i == j) { + A(i + dim, j) = -1.0; + } else { + A(i + dim, j) = 0.0; + } + } + } + } else { + + m = 2 << (dim - 1); + A.resize(m, dim); + b.resize(m); + for(unsigned int i=0; i> 1; + ++j; + } + for(; j +Polytope generate_cross(const unsigned int &dim, const bool &Vpoly) { + + unsigned int m; + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + + MT A; + VT b; + if (!Vpoly) { + + m = 2 << (dim - 1); + A.resize(m, dim); + b.resize(m); + for(unsigned int i=0; i> 1; + ++j; + } + for(; j +Polytope generate_simplex(const unsigned int &dim, const bool &Vpoly){ + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + + MT A; + A.resize(dim+1, dim); + VT b; + b.resize(dim+1); + + for(unsigned int i=0; i +Polytope generate_prod_simplex(const unsigned int &dim, bool Vpoly = false){ + + Polytope Perr; + try + { + if(Vpoly) throw false; + } + catch (bool e) { + #ifdef VOLESTI_DEBUG + std::cout<<"Only prod simplices in H-representation can be generated.."< +Polytope generate_skinny_cube(const unsigned int &dim, bool Vpoly = false) { + + Polytope Perr; + try + { + if(Vpoly) throw false; + } + catch (bool e) { + #ifdef VOLESTI_DEBUG + std::cout<<"Only prod simplices in H-representation can be generated.."< +Polytope generate_birkhoff(unsigned int const& n) { + + unsigned int m = n * n; + unsigned int d = n * n - 2 * n + 1; + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + + MT A = MT::Zero(m, d); + VT b(m); + + b(d) = -1.0 * int(n - 2); + + for (int i = 0; i < d; ++i) { + A(d, i) = -1; + } + + for (int i = 0; i < d; ++i) { + b(i) = 0; + A(i, i) = -1; + } + + for (int i = d+1; i < d+1+n-1; ++i) { + b(i) = 1; + for (int counter = 0; counter < n-1; ++counter) { + A(i, counter * (n-1) + (i-d-1)) = 1; + } + } + + for (int i = d+n; i < m; ++i) { + b(i) = 1; + for (int counter = 0; counter < n-1; ++counter) { + A(i, counter + (i-d-n) * (n-1)) = 1; + } + } + + Polytope P(d, A, b); + + return P; +} + +#endif diff --git a/src/volesti/include/generators/order_polytope_generator.h b/src/volesti/include/generators/order_polytope_generator.h new file mode 100644 index 00000000..bfece556 --- /dev/null +++ b/src/volesti/include/generators/order_polytope_generator.h @@ -0,0 +1,63 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2021 Vissarion Fisikopoulos +// Copyright (c) 2018-2021 Apostolos Chalkis +// Copyright (c) 2021 Vaibhav Thakkar + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ORDER_POLYTOPES_GEN_H +#define ORDER_POLYTOPES_GEN_H + +#include +#include +#include "misc.h" +#include "misc/poset.h" + + +// Instances taken from: https://github.com/ttalvitie/le-counting-practice +static const std::unordered_map instances = +{ + {"bipartite_0.5_008_0", R"(0 0 0 0 1 0 1 0 + 0 0 0 0 1 0 0 0 + 0 0 0 0 1 1 0 1 + 0 0 0 0 1 0 1 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0)"}, + + {"bayesiannetwork_andes_008_0", R"(0 0 0 0 0 0 0 0 + 1 0 0 0 0 0 0 0 + 0 1 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 1 1 0 0 0 0 + 0 0 0 0 1 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 1 1 1 0)"}, + +}; + +// generates an Order Polytope from an instance name +// Instances taken from: https://github.com/ttalvitie/le-counting-practice +/// @tparam Polytope Type of returned polytope +template +Polytope generate_orderpoly(std::string& instance_name) { + std::stringstream in_ss(instances.at(instance_name)); + Poset poset = read_poset_from_file_adj_matrix(in_ss).second; + return Polytope(poset); +} + +// Generates a cube as an Order Polytope +/// @tparam Polytope Type of returned polytope +template +Polytope generate_cube_orderpoly(unsigned int dim) { + typedef typename Poset::RV RV; + + RV order_relations; + Poset poset(dim, order_relations); + Polytope OP(poset); + return OP; +} + +#endif diff --git a/src/volesti/include/generators/sdp_generator.h b/src/volesti/include/generators/sdp_generator.h new file mode 100644 index 00000000..c2b3a154 --- /dev/null +++ b/src/volesti/include/generators/sdp_generator.h @@ -0,0 +1,151 @@ +// +// Created by panagiotis on 9/7/2019. +// + +#ifndef VOLESTI_SDP_GENERATOR_H +#define VOLESTI_SDP_GENERATOR_H + +#include /* srand, rand */ +#include /* time */ + +typedef boost::mt19937 RNGType; + +/// Generates a random matrix +/// @tparam NT Number type +template +void randomMatrixGOE(Eigen::Matrix& M) { + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + + unsigned m = M.rows(); + boost::normal_distribution<> rdist(0,1); + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();//4 if fixed for debug + RNGType rng(seed); + + for (unsigned int i=0; i +Spectrahedron, Eigen::Matrix > generateSDP(int n, int m) { + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + + MT ones = MT::Ones(m, m); + MT M = 2* Eigen::MatrixXd::Random(m,m) - ones; + + MT I = Eigen::MatrixXd::Identity(m, m); + std::vector matrices(n + 1); + matrices[0] = -(M * M.transpose()) - I; + + std::cout<<"A0 = "< lmi(matrices); + Spectrahedron spectrahedron(lmi); + return spectrahedron; + + //return optimization::sdp_problem(spectrahedron, obj); +} + +/// Generates a random spectahedron S(n, m) +/// @tparam NT Number type +template +Spectrahedron, Eigen::Matrix > generateSDP2(int n, int m) { + + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + + MT ones = MT::Ones(m, m); + MT M = 2* Eigen::MatrixXd::Random(m,m) - ones; + + MT I = Eigen::MatrixXd::Identity(m, m); + std::vector matrices(n + 1); + matrices[0] = -(M * M.transpose()) - I; + + //std::cout<<"A0 = "< lmi(matrices); + Spectrahedron spectrahedron(lmi); + return spectrahedron; + + //return optimization::sdp_problem(spectrahedron, obj); +} + + +#endif //VOLESTI_SDP_GENERATOR_H diff --git a/src/volesti/include/generators/v_polytopes_generators.h b/src/volesti/include/generators/v_polytopes_generators.h new file mode 100644 index 00000000..3977e948 --- /dev/null +++ b/src/volesti/include/generators/v_polytopes_generators.h @@ -0,0 +1,175 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef V_POLYTOPES_GEN_H +#define V_POLYTOPES_GEN_H + +#include + +#ifndef isnan + using std::isnan; +#endif + +template +void removeRow(MT &matrix, unsigned int rowToRemove) +{ + unsigned int numRows = matrix.rows()-1; + unsigned int numCols = matrix.cols(); + + if( rowToRemove < numRows ) + matrix.block(rowToRemove,0,numRows-rowToRemove,numCols) = matrix.bottomRows(numRows-rowToRemove); + + matrix.conservativeResize(numRows,numCols); +} + +/// Generates a random V-polytope +/// @tparam Polytope polytope type +/// @tparam RNGType RNGType type +template +Polytope random_vpoly(unsigned int dim, unsigned int k, double seed = std::numeric_limits::signaling_NaN()) { + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + typedef typename Polytope::PointType PointType; + typedef PointType Point; + + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + if (!isnan(seed)) { + unsigned rng_seed = seed; + rng.seed(rng_seed); + } + boost::normal_distribution<> rdist(0,1); + + typename std::vector::iterator pit; + MT V(k, dim); + unsigned int j; + + + std::vector Xs(dim,0); + NT normal = NT(0); + + for (unsigned int i = 0; i < k; ++i) { + + normal = NT(0); + for (unsigned int i=0; i +Polytope random_vpoly_incube(unsigned int d, unsigned int k, double seed = std::numeric_limits::signaling_NaN()) { + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + typedef typename Polytope::PointType PointType; + typedef PointType Point; + + REAL *conv_mem; + int *colno_mem; + + conv_mem = (REAL *) malloc(k * sizeof(*conv_mem)); + colno_mem = (int *) malloc(k * sizeof(*colno_mem)); + + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + if (!isnan(seed)) { + unsigned rng_seed = seed; + rng.seed(rng_seed); + } + boost::random::uniform_real_distribution<> urdist1(-1, 1); + + Point p(d); + typename std::vector::iterator pit; + MT V(k, d); + unsigned int j, count_row,it=0; + std::vector indices; + + VT b = VT::Ones(k); + + for (unsigned int i = 0; i < k; ++i) { + for (int j = 0; j < d; ++j) { + V(i, j) = urdist1(rng); + } + } + if(k==d+1){ + return Polytope(d, V, b); + } + + MT V2(k,d); + V2 = V; + indices.clear(); + while(it<20) { + V.resize(V2.rows(), d); + V = V2; + for (int i = 0; i < indices.size(); ++i) { + V.conservativeResize(V.rows()+1, d); + for (int j = 0; j < d; ++j) { + V(V.rows()-1, j) = urdist1(rng); + } + } + indices.clear(); + V2.resize(k, d); + V2 = V; + + for (int i = 0; i < k; ++i) { + for (int j = 0; j < d; ++j) { + p.set_coord(j, V(i, j)); + } + removeRow(V2, i); + if (memLP_Vpoly(V2, p, conv_mem, colno_mem)){ + indices.push_back(i); + } + V2.resize(k, d); + V2 = V; + } + if (indices.size()==0) { + return Polytope(d, V, b); + } + V2.resize(k - indices.size(), d); + count_row =0; + for (int i = 0; i < k; ++i) { + if(std::find(indices.begin(), indices.end(), i) != indices.end()) { + continue; + } else { + for (int j = 0; j < d; ++j) V2(count_row, j) = V(i,j); + count_row++; + } + } + it++; + } + + + free(colno_mem); + free(conv_mem); + + return Polytope(d, V2, VT::Ones(V2.rows())); +// return VP; + +} + +#endif diff --git a/src/volesti/include/generators/z_polytopes_generators.h b/src/volesti/include/generators/z_polytopes_generators.h new file mode 100644 index 00000000..8afbc6d1 --- /dev/null +++ b/src/volesti/include/generators/z_polytopes_generators.h @@ -0,0 +1,145 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef Z_POLYTOPES_GEN_H +#define Z_POLYTOPES_GEN_H + +#include + +#ifndef isnan + using std::isnan; +#endif + +/// Generates a random Zonotope with generators draw from Gaussian distribution +/// @tparam Polytope polytope type +/// @tparam RNGType RNGType type +template +Polytope gen_zonotope_gaussian(int dim, int m, double seed = std::numeric_limits::signaling_NaN()) { + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + if (!isnan(seed)) { + unsigned rng_seed = seed; + rng.seed(rng_seed); + } + boost::normal_distribution<> rdist(0, 1); + boost::normal_distribution<> rdist2(50, 33.3); + + MT A; + VT b; + A.resize(m, dim); + b.resize(m); + NT rand_gaus; + + for (unsigned int i = 0; i < m; ++i) { + b(i) = 1.0; + for (unsigned int j = 0; j < dim; ++j) { + A(i,j) = rdist(rng); + } + A.row(i)=A.row(i)/A.row(i).norm(); + while(true){ + rand_gaus = rdist2(rng); + if (rand_gaus > 0.0 && rand_gaus<100.0){ + A.row(i) = A.row(i) * rand_gaus; + break; + } + } + } + + Polytope P(dim, A, b); + return P; +} + + +/// Generates a random Zonotope with generators draw from uniform distribution +/// @tparam Polytope polytope type +/// @tparam RNGType RNGType type +template +Polytope gen_zonotope_uniform(int dim, int m, double seed = std::numeric_limits::signaling_NaN()) { + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + if (!isnan(seed)) { + unsigned rng_seed = seed; + rng.seed(rng_seed); + } + boost::normal_distribution<> rdist(0, 1); + boost::random::uniform_real_distribution<> urdist1(0, 100); + + MT A; + VT b; + A.resize(m, dim); + b.resize(m); + + for (unsigned int i = 0; i < m; ++i) { + b(i) = 1.0; + for (unsigned int j = 0; j < dim; ++j) { + A(i,j) = rdist(rng); + } + A.row(i)=A.row(i)/A.row(i).norm(); + A.row(i) = A.row(i) * urdist1(rng); + } + + Polytope P(dim, A, b); + return P; + +} + + +/// Generates a random Zonotope with generators draw from exponential distribution +/// @tparam Polytope polytope type +/// @tparam RNGType RNGType type +template +Polytope gen_zonotope_exponential(int dim, int m, double seed = std::numeric_limits::signaling_NaN()) { + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + if (!isnan(seed)) { + unsigned rng_seed = seed; + rng.seed(rng_seed); + } + boost::normal_distribution<> rdist(0, 1); + boost::normal_distribution<> expdist(1.0/30.0); + + MT A; + VT b; + A.resize(m, dim); + b.resize(m); + NT rand_exp; + + for (unsigned int i = 0; i < m; ++i) { + b(i) = 1.0; + for (unsigned int j = 0; j < dim; ++j) { + A(i,j) = rdist(rng); + } + A.row(i)=A.row(i)/A.row(i).norm(); + while(true){ + rand_exp = expdist(rng); + if (rand_exp > 0.0 && rand_exp<100.0){ + A.row(i) = A.row(i) * rand_exp; + break; + } + } + } + + Polytope P(dim, A, b); + return P; +} + +#endif diff --git a/src/volesti/include/integration/simple_MC_integration.hpp b/src/volesti/include/integration/simple_MC_integration.hpp new file mode 100644 index 00000000..58739650 --- /dev/null +++ b/src/volesti/include/integration/simple_MC_integration.hpp @@ -0,0 +1,276 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2021 Vissarion Fisikopoulos +// Copyright (c) 2018-2021 Apostolos Chalkis + +// Contributed and/or modified by Suraj Choubey, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// Monte Carlo Integration algorithm used here : https://en.wikipedia.org/wiki/Monte_Carlo_integration#Overview + +#ifndef SIMPLE_MC_INTEGRATION_HPP +#define SIMPLE_MC_INTEGRATION_HPP + +#include +#include +#include +#include +#include "convex_bodies/hpolytope.h" +#include "Eigen/Eigen" +#include "generators/known_polytope_generators.h" +#include "boost_random_number_generator.hpp" +#include "cartesian_geom/cartesian_kernel.h" +#include "random_walks/random_walks.hpp" +#include "volume/volume_sequence_of_balls.hpp" +#include "volume/volume_cooling_gaussians.hpp" +#include "volume/volume_cooling_balls.hpp" +#include "misc.h" + +typedef double NT; +typedef Cartesian Kernel; +typedef typename Kernel::Point Point; +typedef HPolytope Hpolytope; +typedef boost::mt19937 RNGType; +typedef BoostRandomNumberGenerator RandomNumberGenerator; +typedef typename HPolytope::MT MT; +typedef typename HPolytope::VT VT; + +enum volumetype {CB ,CG ,SOB}; // Volume type for polytope +typedef typename std::vector Limit; // Standard way for user to use limits +// E.g. Limits LL{0.5, 1.5, 2.5} ; Limits UL{1.2, 1.8 , 2.8 } ; + +const Limit lt{0}; // To initialize non-initialized limits +const Point pt{0}; // To initialize non-initialized points + +// To check if two n-dimensional points ensure valid limits in integration +template +< + typename Point = Point, + typename NT = NT +> +bool valid_limits(Point LL, Point UL) { + if (UL.dimension() == LL.dimension()) { + for (int i = 0; i < LL.dimension(); i++) { + if (LL[i] > UL[i]) { + std::cerr << "Invalid integration limits\n"; + return false; + } + } + return true; + } else { + std::cerr << "Invalid integration limits\n"; + return false; + } +} + +// Initialize Limit Point +template +< + typename Point = Point, + typename NT = NT +> +Point init_limit(Limit L, int dim) { + Point pt(dim); + for (int i = 0; i < dim; i++) { + pt.set_coord(i, L[i]); + } + return pt; +} + +//Initialize to [-1,1]^n +template +< + typename Point = Point, + typename NT = NT +> +void initiate_unit_limits(Point& LL, Point& UL, int dim) { + LL.set_dimension(dim); + UL.set_dimension(dim); + LL.set_to_origin(); + UL.set_to_origin(); + + for (int i = 0 ; i < dim ; i++) { + LL.set_coord(i, -1); + UL.set_coord(i, 1); + } +} + +// Simple MC Integration Over Polytopes +template +< + typename WalkType = BallWalk, + typename Polytope = Hpolytope, + typename VolumeWalkType = BallWalk, + typename RNG = RandomNumberGenerator, + typename NT = NT, + typename Functor +> +NT simple_mc_polytope_integrate(Functor Fx, + Polytope &P, + RNG &rng, + int N = 10000, + volumetype voltype = SOB, + int walk_length = 1, + NT e = 0.1, + Point Origin = pt) +{ + + int dim = P.dimension(); + // P.print(); + + // Check if ShiftPoint is shifted with accurate dimensions + if (Origin.dimension() == 0 && dim > 0) { + Origin.set_dimension(dim); + Origin.set_to_origin(); + } else if (Origin.dimension() != dim && dim > 0) { + std::cerr << "Polytope Dimension != Shiftpoint Dimension" << std::endl; + return -1; + } + + // std::cout << "Origin.dimension() = " << Origin.dimension() << std::endl; + // std::cout << "P.dimension() = " << P.dimension() << std::endl; + + // Volume calculation for HPolytope + NT volume; + + switch (voltype) { + case CB: + volume = volume_cooling_balls (P, rng, e, walk_length).second; + break; + case CG: + volume = volume_cooling_gaussians (P, rng, e, walk_length); + break; + case SOB: + volume = volume_sequence_of_balls (P, rng, e, walk_length); + break; + default: + std::cerr << "Error in volume type: CB / SOB / CG" << std::endl; + return -1; + } + + // std::cout << "Volume of the convex body = " << volume << std::endl; + + // For implementing Uniform Walks + std::pair inner_ball = P.ComputeInnerBall(); + Point x0 = inner_ball.first; + typename WalkType::template Walk walk(P, x0, rng); + + NT sum = 0; + + // Applying and walking through Uniform Walks + Storing Points in Vector + for (int i = 0; i < N; i++) { + walk.apply(P, x0, walk_length, rng); + sum += Fx(x0 + Origin); + + // (x0 + Origin).print(); + } + + // Integration Value + NT integration_value = volume * sum / N ; + return integration_value; +} + +template +< + typename WalkType = BallWalk, + typename Polytope = Hpolytope, + typename VolumeWalkType = BallWalk, + typename RNG = RandomNumberGenerator, + typename NT = NT, + typename Functor +> +NT simple_mc_polytope_integrate(Functor Fx, + Polytope &P, + int N = 10000, + volumetype voltype = SOB, + int walk_length = 1, + NT e = 0.1, + Point Origin = pt) +{ + RNG rng(P.dimension()); + return simple_mc_polytope_integrate(Fx, P, rng, N, voltype, + walk_length, e, Origin); +} + +// Simple MC Integration over Hyper-Rectangles +template +< + typename WalkType = BallWalk, + typename RNG = RandomNumberGenerator, + typename NT = NT, + typename Functor +> +NT simple_mc_integrate(Functor Fx, + int dim, + RNG &rng, + int N = 10000, + volumetype voltype = SOB, + Limit LowLimit = lt, + Limit UpLimit = lt, + int walk_length = 10, + NT e = 0.1) +{ + + // Setting up integration limits + Point LL, UL; + if (LowLimit.size() == 1 && UpLimit.size() == 1 && LowLimit[0] == 0 && UpLimit[0] == 0) { + initiate_unit_limits(LL, UL, dim); + } else if (LowLimit.size() == UpLimit.size() && LowLimit.size() == dim) { + LL = init_limit (LowLimit, dim); + UL = init_limit (UpLimit, dim); + } else { + std::cerr << "Invalid limits entered"; + return -1; + } + + NT sum = 0; + + if (valid_limits(LL, UL)) { + + // Creating an MT & VT for HPolytope(Hyper-Rectangle) for integration limits using LL & UL + MT mt(dim*2, dim); + mt.setZero(); + VT vt(dim*2); + vt.setZero(); + + for (int i=0; i (Fx, P, rng, N, voltype, walk_length, e); + return integration_value; + + } else { + std::cerr << "Invalid integration limits" << std::endl; + return -1; + } +} + +template +< + typename WalkType = BallWalk, + typename RNG = RandomNumberGenerator, + typename NT = NT, + typename Functor +> +NT simple_mc_integrate(Functor Fx, + int dim, + int N = 10000, + volumetype voltype = SOB, + Limit LowLimit = lt, + Limit UpLimit = lt, + int walk_length = 10, + NT e = 0.1) +{ + RNG rng(dim); + return simple_mc_integrate(Fx, dim, rng, N, voltype, LowLimit, UpLimit, walk_length, e); +} +#endif diff --git a/src/volesti/include/lp_oracles/misc_lp.h b/src/volesti/include/lp_oracles/misc_lp.h new file mode 100644 index 00000000..9e8e2528 --- /dev/null +++ b/src/volesti/include/lp_oracles/misc_lp.h @@ -0,0 +1,145 @@ +#ifndef MISC_LP_H +#define MISC_LP_H + + +#include +#include +#include +#undef Realloc +#undef Free +#include "lp_lib.h" + + +// Computes the Chebychev ball of an H-polytope described by a dxd matrix A and d-dimensional vector b, s.t.: Ax<=b +/// @tparam NT Number type +/// @tparam Point Point type +/// @tparam MT Matrix type +/// @tparam VT Vector type +template +std::pair ComputeChebychevBall(MT &A, VT &b){ + + lprec *lp; + int d = A.cols(); + int Ncol=d+1, j, m=A.rows(), i; + int *colno = NULL; + + REAL *row = NULL; + std::pair exception_pair(Point(1),-1.0); + + try + { + lp = make_lp(m, Ncol); + if(lp == NULL) throw false; + } + catch (bool e) { +#ifdef VOLESTI_DEBUG + std::cout<<"Could not construct Linear Program for chebychev center "< res; + + std::vector temp_p(d,0); + get_variables(lp, row); + for(j = 0; j < d; j++){ + temp_p[j]=NT(row[j]); + } + Point xc( d , temp_p.begin() , temp_p.end() ); + NT r=NT(get_objective(lp)); + res = std::pair (xc,r); + delete_lp(lp); + free(row); + free(colno); + + return res; +} + + + +#endif diff --git a/src/volesti/include/lp_oracles/solve_lp.h b/src/volesti/include/lp_oracles/solve_lp.h new file mode 100644 index 00000000..f7013e99 --- /dev/null +++ b/src/volesti/include/lp_oracles/solve_lp.h @@ -0,0 +1,296 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2018 Vissarion Fisikopoulos, Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// VolEsti is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// VolEsti is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// See the file COPYING.LESSER for the text of the GNU Lesser General +// Public License. If you did not receive this file along with HeaDDaCHe, +// see . + + +#ifndef SOLVE_LP_H +#define SOLVE_LP_H + + +#include +#include +#include +#undef Realloc +#undef Free +#include "lp_lib.h" + + +// compute the chebychev ball of an H-polytope described by a dxd matrix A and d-dimensional vector b, s.t.: Ax<=b +template +std::pair ComputeChebychevBall(const MT &A, const VT &b){ + + lprec *lp; + int d = A.cols(); + int Ncol=d+1, j, m=A.rows(), i; + int *colno = NULL; + + REAL *row = NULL; + std::pair exception_pair(Point(1),-1.0); + + try + { + lp = make_lp(m, Ncol); + if(lp == NULL) throw false; + } + catch (bool e) { + #ifdef VOLESTI_DEBUG + std::cout<<"Could not construct Linear Program for chebychev center "< res; + + std::vector temp_p(d,0); + get_variables(lp, row); + for(j = 0; j < d; j++){ + temp_p[j]=NT(row[j]); + } + Point xc( d , temp_p.begin() , temp_p.end() ); + NT r = NT(get_objective(lp)); + res = std::pair (xc,r); + delete_lp(lp); + + return res; +} + + +template +Point PointInIntersection(MT V1, MT V2, Point direction, bool &empty) { + + typedef typename Point::FT NT; + unsigned int d = V1.cols(); + unsigned int k1 = V1.rows(); + unsigned int k2 = V2.rows(); + unsigned int k = k1 + k2; + VT cb(k1); + lprec *lp; + int Ncol=k, *colno = NULL, j, i; + REAL *row = NULL; + Point p(d); + + try + { + lp = make_lp(d+2, Ncol); + if(lp == NULL) throw false; + } + catch (bool e) { +#ifdef VOLESTI_DEBUG + std::cout<<"Could not construct Linear Program for membership "<. + + +#ifndef VPOLYORACLES_H +#define VPOLYORACLES_H + + +#include +#include +#include +#undef Realloc +#undef Free +#include "lp_lib.h" + + +// return true if q belongs to the convex hull of the V-polytope described by matrix V +// otherwise return false +template +bool memLP_Vpoly(const MT &V, const Point &q, NT *row, int *colno){ + + //typedef typename Point::FT NT; + int d=q.dimension(); + lprec *lp; + int Ncol=d+1, j, i, m=V.rows(); + m++; + + try + { + lp = make_lp(m, Ncol); + if(lp == NULL) throw false; + } + catch (bool e) { +#ifdef VOLESTI_DEBUG + std::cout<<"Could not construct Linear Program for membership "<0.0){ + return false; + } + return true; +} + + + +// compute the intersection of a ray with a V-polytope +// if maxi is true compute positive lambda, when the ray is p + lambda \cdot v +// otherwise compute the negative lambda +template +NT intersect_line_Vpoly(const MT &V, const Point &p, const Point &v, + NT *conv_comb, NT *row, int *colno, bool maxi, bool zonotope){ + + int d=v.dimension(), i; + lprec *lp; + int m=V.rows(); + m++; + int Ncol=m, j, Nrows; + NT res; + if(!zonotope) { + Nrows = d+1; + } else { + Nrows = d; + } + + try + { + lp = make_lp(Nrows, Ncol); + if(lp == NULL) throw false; + } + catch (bool e) { +#ifdef VOLESTI_DEBUG + std::cout<<"Could not construct Linear Program for ray-shooting "<. + + +#ifndef ZPOLYORACLES_H +#define ZPOLYORACLES_H + + +#include +#include +#include +#undef Realloc +#undef Free +#include "lp_lib.h" + + +template +bool memLP_Zonotope(const MT &V, const Point &q, NT *row, int *colno){ + + //typedef typename Point::FT NT; + int d=q.dimension(); + lprec *lp; + int Ncol=V.rows(), j, i; + + try + { + lp = make_lp(d, Ncol); + if(lp == NULL) throw false; + } + catch (bool e) { +#ifdef VOLESTI_DEBUG + std::cout<<"Could not construct Linear Program for membership "< +std::pair intersect_line_zono(const MT &V, const Point &p, const Point &v, NT *row, int *colno){ + + std::pair pair_res; + int d=v.dimension(), i; + lprec *lp;//, *lp2; + int m=V.rows(); + m++; + int Ncol=m, j, Nrows; + NT res; + Nrows = d; + + try + { + lp = make_lp(Nrows, Ncol); + //lp2 = make_lp(Nrows, Ncol); + if(lp == NULL) throw false; + } + catch (bool e) { +#ifdef VOLESTI_DEBUG + std::cout<<"Could not construct Linear Program for ray-shooting "< +class DenseProductMatrix { +public: + /// Eigen matrix type + typedef Eigen::Matrix MT; + /// Eigen vector type + typedef Eigen::Matrix VT; + + /// The number of rows + int _rows; + /// The number of cols + int _cols; + + /// Pointer to matrix A + MT const *A; + /// Pointer to matrix B + MT const *B; + + /// The decomposition we will use + /// If PARTIAL_LU_DECOMPOSITION is defined, use the Eigen partial LU decomposition, + /// otherwise full. The partial is faster but assumes that the matrix has full rank. +#if defined(PARTIAL_LU_DECOMPOSITION) + typedef Eigen::PartialPivLU Decomposition; +#else + typedef Eigen::FullPivLU Decomposition; +#endif + + /// The LU decomposition of B + Decomposition Blu; + + /// Constructs an object of this class and computes the LU decomposition of B. + /// + /// \param[in] A The matrix A + /// \param[in] B The matrix B + DenseProductMatrix(MT const *A, MT const *B) : A(A), B(B) { + Blu = Decomposition(*B); + _rows = A->rows(); + _cols = B->cols(); + } + + ///Required by Spectra + /// \return The number of rows + int rows() { + return _rows; + } + + ///Required by Spectra + /// \return The number of columns + int cols() { + return _cols; + } + + /// Required by Spectra. + /// Computes the product Cx = y, i.e. @f[ (B^-1 A)v = y@$]. But B = LU, so Ax = LUy. + /// Let Ax = v, then LUy = v. Then Lw = v and finally Uy = w to get y; + /// \param[in] x_in + /// \param[out] y_out + void perform_op(NT const * x_in, NT* y_out) { + + // Declaring the vectors like this, we don't copy the values of x_in to v + // and next of y to y_out + Eigen::Map const x(const_cast(x_in), _rows); + VT const v = *A * x; + + Eigen::Map y(y_out, _rows); + y = Blu.solve(v); + } + + /// Required by arpack. + /// Computes the product Cx = y, i.e. @f[ (B^-1 A)v = y@$]. But B = LU, so Ax = LUy. + /// Let Ax = v, then LUy = v. Then Lw = v and finally Uy = w to get y; + /// \param[in] x_in + /// \param[out] y_out + void MultMv(NT * x_in, NT* y_out) { + + // Declaring the vectors like this, we don't copy the values of x_in to v + // and next of y to y_out + Eigen::Map const x(const_cast(x_in), _rows); + VT const v = *A * x; + + Eigen::Map y(y_out, _rows); + y = Blu.solve(v); + } +}; +#endif //VOLESTI_DENSEPRODUCTMATRIX_H diff --git a/src/volesti/include/matrix_operations/EigenDenseMatrix.h b/src/volesti/include/matrix_operations/EigenDenseMatrix.h new file mode 100644 index 00000000..2a3582e3 --- /dev/null +++ b/src/volesti/include/matrix_operations/EigenDenseMatrix.h @@ -0,0 +1,76 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_EIGENDENSEMATRIX_H +#define VOLESTI_EIGENDENSEMATRIX_H + +/// A wrap class to use Eigen dense matrices when solving Eigenvalue problems with ARPACK++ +/// \tparam NT Numeric Type +template +class EigenDenseMatrix { +public: + + /// Eigen matrix type + typedef Eigen::Matrix MT; + /// Eigen vector type + typedef Eigen::Matrix VT; + + /// The matrix + MT const * M; + + /// number of columns + int n; + /// number of rows + int m; + + /// \return Number of rows + int nrows() { return m;} + + /// \return Number of columns + int ncols() { return n;} + + /// \return Number of rows + int rows() { return m;} + + /// \return Number of columns + int cols() { return n;} + + /// Required by ARPACK++ : Multiplies the matrix with vector v + /// \param[in] v The input vector, for example double* + /// \param[out] w The result of M*v + void MultMv(NT* v, NT* w) { + // Declaring the vectors like this, we don't copy the values of v and after to w + Eigen::Map _v(v, m); + Eigen::Map _w(w, m); + + _w = *M * _v; + } + + /// Required by ARPACK++ : Multiplies the matrix with vector v + /// \param[in] v The input vector, for example double* + /// \param[out] w The result of M*v + void perform_op(NT* v, NT* w) { + // Declaring the vectors like this, we don't copy the values of v and after to w + Eigen::Map _v(v, m); + Eigen::Map _w(w, m); + + _w = *M * _v; + } + + + /// Constructs an object + /// \param[in] M An Eigen Matrix + EigenDenseMatrix(MT const * M) { + this->M = M; + n = M->cols(); + m = M->rows(); + } + +}; +#endif //VOLESTI_EIGENDENSEMATRIX_H diff --git a/src/volesti/include/matrix_operations/EigenvaluesProblems.h b/src/volesti/include/matrix_operations/EigenvaluesProblems.h new file mode 100644 index 00000000..4012177f --- /dev/null +++ b/src/volesti/include/matrix_operations/EigenvaluesProblems.h @@ -0,0 +1,450 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. +// Contributed and modified by Huu Phuoc Le as part of Google Summer of Code 2022 program + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_EIGENVALUESPROBLEMS_H +#define VOLESTI_EIGENVALUESPROBLEMS_H + +/// Uncomment the solver the function minPosGeneralizedEigenvalue uses +/// Eigen solver for generalized eigenvalue problem +//#define EIGEN_EIGENVALUES_SOLVER +/// Spectra standard eigenvalue problem +#define SPECTRA_EIGENVALUES_SOLVER +/// ARPACK++ standard eigenvalues solver +//#define ARPACK_EIGENVALUES_SOLVER + +#include +#include "DenseProductMatrix.h" +#include "EigenDenseMatrix.h" + +#include "Spectra/include/Spectra/SymGEigsSolver.h" +#include "Spectra/include/Spectra/GenEigsSolver.h" + +/// Solve eigenvalues problems +/// \tparam NT Numeric Type +/// \tparam MT Matrix Type +/// \tparam VT Vector Type +template +class EigenvaluesProblems { + +}; + + +/// A specialization of the template class EigenvaluesProblems for dense Eigen matrices and vectors. +/// \tparam NT Numer Type +template +class EigenvaluesProblems, Eigen::Matrix > { +public: + /// The type for Eigen Matrix + typedef Eigen::Matrix MT; + /// The type for Eigen vector + typedef Eigen::Matrix VT; + /// The type of a complex Eigen Vector for handling eigenvectors +#if defined(EIGEN_EIGENVALUES_SOLVER) || defined (SPECTRA_EIGENVALUES_SOLVER) + typedef typename Eigen::GeneralizedEigenSolver::ComplexVectorType CVT; +#elif defined(ARPACK_EIGENVALUES_SOLVER) + typedef Eigen::Matrix CVT; +#endif + + /// The type of a pair of NT + typedef std::pair NTpair; + + + /// Find the smallest eigenvalue of M + /// \param M a symmetric matrix + /// \return smallest eigenvalue + NT findSymEigenvalue(MT const & M) { + EigenDenseMatrix _M(&M); + +//#define NOT_WORKING +#ifdef NOT_WORKING + // Creating an eigenvalue problem and defining what we need: + // the smallest eigenvalue of M. + ARNonSymStdEig > + dprob(M.cols(), 1, &_M, &EigenDenseMatrix::MultMv, std::string ("LR"), 8, 0.0, 100*15); + + // compute + if (dprob.FindEigenvectors() == 0) { + std::cout << "Failed in findSymEigenvalue\n"; + // if failed with default (and fast) parameters, try with stable (and slow) + dprob.ChangeNcv(M.cols()/10); + if (dprob.FindEigenvectors() == 0) { + std::cout << "\tFailed Again\n"; + return NT(0); + } + } + + if (!dprob.EigenvaluesFound()) { + // if failed to find eigenvalues + return NT(0); + } + + // retrieve eigenvalue of the original system + return dprob.EigenvalueReal(0); +#elif defined(SPECTRA) + // This parameter is for Spectra. It must be larger than #(requested eigenvalues) + 2 + // and smaller than the size of matrix; + int ncv = M.cols()/10 + 5; + if (ncv > M.cols()) ncv = M.cols(); + + Spectra::SymEigsSolver > eigs(&_M, 1, ncv); + // compute + eigs.init(); + eigs.compute(50000); + if(eigs.info() == Spectra::SUCCESSFUL) { + return eigs.eigenvalues()(0); + } + else { + std::cout << "Spectra failed\n"; + return NT(0); + } +#else + Eigen::SelfAdjointEigenSolver solver; + solver.compute(M, Eigen::EigenvaluesOnly); +// typename Eigen::GeneralizedEigenSolver::ComplexVectorType eivals = solver.eigenvalues(); +// NT max = eivals(0).real(); +// +// for (int i = 1; i < eivals.rows(); i++) +// if (eivals(i).real() > max) +// max = eivals(i).real(); + + return solver.eigenvalues().maxCoeff(); +#endif + } + + /// Find the minimum positive and maximum negative eigenvalues of the generalized eigenvalue + /// problem A + lB, where A, B symmetric and A negative definite. + /// \param[in] A Input matrix + /// \param[in] B Input matrix + /// \return The pair (minimum positive, maximum negative) of eigenvalues + NTpair symGeneralizedProblem(MT const & A, MT const & B) const { + + int matrixDim = A.rows(); + + // Spectra solves Xv=lYv, where Y positive definite + // Set X = B, Y=-A. Then, the eigenvalues we want are the minimum negative + // and maximum positive eigenvalues of Xv=lYv. + + // Construct matrix operation object using the wrapper classes provided by Spectra + Spectra::DenseSymMatProd op(B); + Spectra::DenseCholesky Bop(-A); + + // Construct generalized eigen solver object + // requesting the minmum negative and largest positive eigenvalues + Spectra::SymGEigsSolver, Spectra::DenseCholesky, Spectra::GEIGS_CHOLESKY> + geigs(&op, &Bop, 2, 5 < matrixDim ? 5 : matrixDim); + + // Initialize and compute + geigs.init(); + int nconv = geigs.compute(); + + // Retrieve results + if (geigs.info() != Spectra::SUCCESSFUL) + return {NT(0), NT(0)}; + + Eigen::VectorXd evalues; + double lambdaMinPositive, lambdaMaxNegative; + + evalues = geigs.eigenvalues(); + + // get the eigenvalues of the original problem + lambdaMinPositive = 1 / evalues(0); + lambdaMaxNegative = 1 / evalues(1); + + return {lambdaMinPositive, lambdaMaxNegative}; + } + + NT minPosLinearEigenvalue(MT const & A, MT const & B, VT &eigvec) { + int matrixDim = A.rows(); + double lambdaMinPositive; + + Spectra::DenseSymMatProd op(B); + Spectra::DenseCholesky Bop(-A); + + // Construct generalized eigen solver object, requesting the largest three generalized eigenvalues + Spectra::SymGEigsSolver, Spectra::DenseCholesky, Spectra::GEIGS_CHOLESKY> + geigs(&op, &Bop, 1, 15 < matrixDim ? 15 : matrixDim); + + // Initialize and compute + geigs.init(); + int nconv = geigs.compute(); + + VT evalues; + if (geigs.info() == Spectra::SUCCESSFUL) { + evalues = geigs.eigenvalues(); + eigvec = geigs.eigenvectors().col(0); + } + + lambdaMinPositive = 1 / evalues(0); + + return lambdaMinPositive; + } + + /// Finds the minimum positive real eigenvalue of the generalized eigenvalue problem A + lB and + /// the corresponding eigenvector. + /// If the macro EIGEN_EIGENVALUES_SOLVER is defined, the Generalized Solver of Eigen is used. + /// Otherwise, we transform the generalized to a standard eigenvalue problem and use Spectra. + /// Warning: With Spectra we might get a value smaller than the minimum positive real eigenvalue (the real part + /// of a complex eigenvalue). + /// No restriction on the matrices! + /// \param[in] A Input matrix + /// \param[in] B Input matrix + /// \param[out] eigenvector The eigenvector corresponding to the minimum positive eigenvalue + /// \return The minimum positive eigenvalue + NT minPosGeneralizedEigenvalue(MT const & A, MT const & B, CVT& eigenvector) { + NT lambdaMinPositive = std::numeric_limits::max(); + +#if defined(EIGEN_EIGENVALUES_SOLVER) + // use the Generalized eigenvalue solver of Eigen + + // compute generalized eigenvalues with Eigen solver + Eigen::GeneralizedEigenSolver ges(A, -B); + + // retrieve minimum positive eigenvalue + typename Eigen::GeneralizedEigenSolver::ComplexVectorType alphas = ges.alphas(); + VT betas = ges.betas(); + int index = 0; + + for (int i = 0; i < alphas.rows(); i++) { + + if (betas(i) == 0 || alphas(i).imag() != 0) + continue; + + double lambda = alphas(i).real() / betas(i); + if (lambda > 0 && lambda < lambdaMinPositive) { + lambdaMinPositive = lambda; + index = i; + } + } + + // retrieve corresponding eigenvector + eigenvector = ges.eigenvectors().col(index); +#elif defined(SPECTRA_EIGENVALUES_SOLVER) + // Transform the problem to a standard eigenvalue problem and use the general eigenvalue solver of Spectra + + // This makes the transformation to standard eigenvalue problem. See class for more info. + // We have the generalized problem A + lB, or Av = -lBv + // This class computes the matrix product vector Mv, where M = -B * A^[-1] + MT _B = -1 * B; // TODO avoid this allocation + DenseProductMatrix M(&_B, &A); + + // This parameter is for Spectra. It must be larger than #(requested eigenvalues) + 2 + // and smaller than the size of matrix; + int ncv = 3; + + // Prepare to solve Mx = (1/l)x + // we want the smallest positive eigenvalue in the original problem, + // so in this the largest positive eigenvalue; + Spectra::GenEigsSolver > eigs(&M, 1, ncv); + + // compute + eigs.init(); + eigs.compute(); + + //retrieve result and invert to get required eigenvalue of the original problem + if (eigs.info() != Spectra::SUCCESSFUL) { + eigenvector.setZero(A.rows()); + return NT(0); + } + + lambdaMinPositive = 1/((eigs.eigenvalues())(0).real()); + + // retrieve corresponding eigenvector + int matrixDim = A.rows(); + eigenvector.resize(matrixDim); + for (int i = 0; i < matrixDim; i++) + eigenvector(i) = (eigs.eigenvectors()).col(0)(i); + +#elif defined(ARPACK_EIGENVALUES_SOLVER) + // Transform the problem to a standard eigenvalue problem and use the general eigenvalue solver of ARPACK++ + + // This makes the transformation to standard eigenvalue problem. See class for more info. + // We have the generalized problem A + lB, or Av = -lBv + // This class computes the matrix product vector Mv, where M = -B * A^[-1] + MT _B = -1 * B; // TODO avoid this allocation + DenseProductMatrix M(&_B, &A); + + // Creating an eigenvalue problem and defining what we need: + // the eigenvector of A with largest real. + ARNonSymStdEig > + + dprob(A.cols(), 1, &M, &DenseProductMatrix::MultMv, std::string ("LR"), 8 op(B); + Spectra::DenseCholesky Bop(-A); + + // Construct generalized eigen solver object, requesting the largest three generalized eigenvalues + Spectra::SymGEigsSolver, Spectra::DenseCholesky, Spectra::GEIGS_CHOLESKY> + geigs(&op, &Bop, 1, 15 < matrixDim ? 15 : matrixDim); + + // Initialize and compute + geigs.init(); + int nconv = geigs.compute(); + + // Retrieve results + VT evalues; + + if (geigs.info() == Spectra::SUCCESSFUL) { + evalues = geigs.eigenvalues(); + eigvec = geigs.eigenvectors().col(0); + } + + lambdaMinPositive = 1 / evalues(0); + + return lambdaMinPositive; + } + + /// Transform the quadratic eigenvalue problem \[At^2 + Bt + c\] to + /// the generalized eigenvalue problem X+lY. + /// If the updateOnly flag is false, compute matrices X,Y from scratch; + /// otherwise update them. + /// \param[in] A + /// \param[in] B + /// \param[in] C + /// \param[in, out] X + /// \param[in, out] Y + /// \param[in, out] updateOnly True if X,Y were previously computed and only B,C changed + void linearization(const MT &A, const MT &B, const MT &C, MT &X, MT &Y, bool &updateOnly) { + unsigned int matrixDim = A.rows(); + + // check if the matrices X,Y are computed. + //if yes, update them; otherwise compute them from scratch + if (!updateOnly) { + X.resize(2 * matrixDim, 2 * matrixDim); + Y.resize(2 * matrixDim, 2 * matrixDim); + + Y.block(matrixDim, matrixDim, matrixDim, matrixDim) = -1 * C; + Y.block(0, matrixDim, matrixDim, matrixDim) = MT::Zero(matrixDim, matrixDim); + Y.block(matrixDim, 0, matrixDim, matrixDim) = MT::Zero(matrixDim, matrixDim); + Y.block(0, 0, matrixDim, matrixDim) = A; + + X.block(0, matrixDim, matrixDim, matrixDim) = C; + X.block(0, 0, matrixDim, matrixDim) = B; + X.block(matrixDim, 0, matrixDim, matrixDim) = C; + X.block(matrixDim, matrixDim, matrixDim, matrixDim) = MT::Zero(matrixDim, matrixDim); + } else { + Y.block(matrixDim, matrixDim, matrixDim, matrixDim) = -1 * C; + + X.block(0, matrixDim, matrixDim, matrixDim) = C; + X.block(0, 0, matrixDim, matrixDim) = B; + X.block(matrixDim, 0, matrixDim, matrixDim) = C; + } + } + + /// Find the minimum positive real eigenvalue of the quadratic eigenvalue problem \[At^2 + Bt + c\]. + /// First transform it to the generalized eigenvalue problem X+lY. + /// If the updateOnly flag is false, compute matrices X,Y from scratch; + /// otherwise only update them. + /// \param[in] A Input matrix + /// \param[in] B Input matrix + /// \param[in] C Input matrix + /// \param[in, out] X + /// \param[in, out] Y + /// \param[out] eigenvector The eigenvector corresponding to the minimum positive eigenvalue + /// \param[in, out] updateOnly True if X,Y were previously computed and only B,C changed + /// \return Minimum positive eigenvalue + NT minPosQuadraticEigenvalue(MT const & A, MT const &B, MT const &C, MT &X, MT &Y, VT &eigenvector, bool &updateOnly) { + // perform linearization and create generalized eigenvalue problem X+lY + linearization(A, B, C, X, Y, updateOnly); + + // solve generalized problem + CVT eivector; + NT lambdaMinPositive = minPosGeneralizedEigenvalue(X, Y, eivector); + + if (lambdaMinPositive == 0) + return 0; + + int matrixDim = A.rows(); + + // the eivector has dimension 2*matrixDim + // while the eigenvector of the original problem has dimension matrixDim + // retrieve the eigenvector by keeping only #matrixDim coordinates. + eigenvector.resize(matrixDim); + +#if defined(EIGEN_EIGENVALUES_SOLVER) || defined (SPECTRA_EIGENVALUES_SOLVER) + for (int i = 0; i < matrixDim; i++) + eigenvector(i) = eivector(matrixDim + i).real(); +#elif defined(ARPACK_EIGENVALUES_SOLVER) + for (int i = 0; i < matrixDim; i++) + eigenvector(i) = eivector(matrixDim + i); +#endif + + return lambdaMinPositive; + } + + // Using LDLT decomposition to check membership + // Faster than computing the largest eigenvalue with Spectra + // more numerically stable for singular matrices + bool isPositiveSemidefinite(MT const &A) const { + Eigen::LDLT A_ldlt(A); + if (A_ldlt.info() != Eigen::NumericalIssue && A_ldlt.isPositive()) + return true; + return false; + } + + /// Minimum positive eigenvalue of the generalized eigenvalue problem A - lB + /// Use Eigen::GeneralizedSelfAdjointEigenSolver ges(B,A) (faster) + /// \param[in] A: symmetric positive definite matrix + /// \param[in] B: symmetric matrix + /// \return The minimum positive eigenvalue and the corresponding eigenvector + NT minPosLinearEigenvalue_EigenSymSolver(MT const & A, MT const & B, VT &eigvec) const { + NT lambdaMinPositive = NT(0); + Eigen::GeneralizedSelfAdjointEigenSolver ges(B,A); + lambdaMinPositive = 1/ges.eigenvalues().reverse()[0]; + eigvec = ges.eigenvectors().reverse().col(0).reverse(); + return lambdaMinPositive; + } +}; + +#endif //VOLESTI_EIGENVALUESPROBLEMS_H diff --git a/src/volesti/include/misc/linear_extensions.h b/src/volesti/include/misc/linear_extensions.h new file mode 100644 index 00000000..13866816 --- /dev/null +++ b/src/volesti/include/misc/linear_extensions.h @@ -0,0 +1,92 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2018 Vissarion Fisikopoulos + +// Licensed under GNU LGPL.3, see LICENCE file + +inline void linear_extensions_to_order_polytope(std::istream &is, + std::ostream &os){ + + std::string ns; + //read n: the number of elements in the poset + std::getline(is, ns, ' '); + std::istringstream buffer(ns); + int n; + buffer >> n; + //read m: the number of edges/relations in the poset + std::getline(is, ns, '\n'); + std::istringstream bufferm(ns); + int m; + bufferm >> m; + + os << "order_"< ipoint; + point.erase( std::remove( point.begin(), point.end(), ' ' ), + point.end() ); + point.erase( std::remove( point.begin(), point.end(), '[' ), + point.end() ); + std::string coord; + if (point[0]==',') + point.erase(0,1); + std::stringstream stream(point); + if (!point.empty()){ + int i=1; + os<<" 0 "; + //read point 1 + std::getline(stream, coord, ','); + std::istringstream buffer(coord); + int temp; + buffer >> temp; + ipoint.push_back(temp); + //os << temp << " "; + //write inequality 1 + for(;i> temp2; + ipoint.push_back(temp2); + //os << temp2 << "\n"; + //write inequality 2 + for(;i +#include +#include +#include +#include "poset.h" + +//function to print rounding to double coordinates +template +void round_print(T p) { + std::cout<<"test version.."< Vpolys; + +int Minkowski_sum_naive(V_polytope &P1, V_polytope &P2, V_polytope &Msum){ + std::cout<<(!P1.empty() && !P2.empty())< ep(P1[0].dimension()); + ep.insert(Msum_all.begin(),Msum_all.end()); + //std::vector extreme_points; + ep.get_extreme_points(std::back_inserter(Msum)); + return Msum.size(); + } + return -1; +} +*/ + +// polymake file to compute exact volume +template +void print_polymake_volfile(T &P, + std::ostream& os){ + std::cout<<"test version.."<;\n"; + os << "$p->POINTS=<<'.';\n"; + for (typename T::iterator vit = P.begin(); vit != P.end(); vit++){ + os << "1 "; + for (Point::Cartesian_const_iterator cit=vit->cartesian_begin(); + cit != vit->cartesian_end(); + cit++){ + os << *cit; + if (cit - vit->cartesian_begin() != vit->dimension()-1) + os << " "; + } + //os << "|" << vit->point().index(); + os << "\n"; + } + os << ".\n"; + os << "print ' ';\n"; + os << "print $p->N_POINTS;\n"; + os << "print ' ';\n"; + os << "print $p->N_VERTICES;\n"; + os << "print ' ';\n"; + os << "print $p->DIM;\n"; + os << "print ' ';\n"; + os << "my $t0 = [gettimeofday];\n"; + os << "my $f=$p->VOLUME;\n"; + os << "print $f;\n"; + os << "print ' ';\n"; + os << "print tv_interval($t0,[gettimeofday]);\n"; + os << "print \"\n\";\n"; + os << std::endl; +}*/ + +// polymake file to compute exact volume +template +void print_polymake_volfile2(T &P, + std::ostream& os){ + std::cout<<"test version.."<INEQUALITIES=<<'.';\n"; + //os << "my $p=new Polytope;\n"; + //os << "$p->POINTS=<<'.';\n"; + for (typename T::iterator vit = P.begin(); vit != P.end(); vit++){ + Hyperplane::Coefficient_const_iterator cit_end = vit->coefficients_end(); + os << *(--cit_end)<<" "; + //os << "0 "; + Hyperplane::Coefficient_const_iterator cit = vit->coefficients_begin(); + //++cit; + for (; cit != cit_end; cit++){ + //std::cout<<*cit<<" "; + os <<(*cit)<<" "; + if (cit - vit->coefficients_begin() != vit->dimension()-1) + os << " "; + } + //os << "|" << vit->point().index(); + os << "\n"; + } + os << ".\n"; + //$p=new Polytope(INEQUALITIES=>$inequalities); + + os << "print ' ';\n"; + os << "print $p->N_POINTS;\n"; + os << "print ' ';\n"; + os << "print $p->N_VERTICES;\n"; + os << "print ' ';\n"; + os << "print $p->DIM;\n"; + os << "print ' ';\n"; + os << "my $t0 = [gettimeofday];\n"; + os << "my $f=$p->VOLUME;\n"; + os << "print $f;\n"; + os << "print ' ';\n"; + os << "print tv_interval($t0,[gettimeofday]);\n"; + os << "print \"\n\";\n"; + os << std::endl; +}*/ +template +void read_pointset(std::istream &is, + std::vector > &Input){ + + std::string point; + + while(!std::getline(is, point, '\n').eof()) { + //std::cout< input; + while (found2!=std::string::npos || point[found]=='-') + { + //std::cout<<"*"<<(point[found]!='-')<<"*"< +void read_objective(std::istream &is, std::vector &obj) { + NT element; + while (is >> element) obj.push_back(element); + +} + +template +std::pair read_inner_ball(std::istream &is) { + std::vector obj; + read_objective(is, obj); + unsigned int dim = obj.size() - 1; + Point center(dim); + NT radius = obj[dim]; + for (unsigned int i = 0; i < dim; i++) { + center.set_coord(i, obj[i]); + } + + return std::make_pair(center, radius); +} + +/* read a poset given in the following format: + - First line contains a single positive integer 'n' - number of elements + - Next `m` lines follow containing a pair 'i j' in each line to signify A_i <= A_j + i.e i_th element is less than or equal to the j_th element +*/ +Poset read_poset_from_file(std::istream &data_file) { + typedef typename Poset::RT RT; + typedef typename Poset::RV RV; + + // read number of elements + unsigned int n; + data_file >> n; + + // read relations line by line + RT curr_relation; + RV relations; + while(data_file >> curr_relation.first >> curr_relation.second) + relations.push_back(curr_relation); + + return Poset(n, relations); +} + + +// read a poset given as an adjacency matrix +std::pair read_poset_from_file_adj_matrix(std::istream &in) { + typedef typename Poset::RV RV; + + RV edges; + unsigned int x, n = 0; + + // read a single line + std::string line; + std::getline(in, line); + std::stringstream line_ss(line); + while(line_ss >> x) { + if(x) { + edges.emplace_back(0, n); + } + ++n; + } + + // read rest of the lines + for(unsigned int a = 1; a < n; ++a) { + for(unsigned int b = 0; b < n; ++b) { + if(!(in >> x)) { + std::cerr << "Invalid adjacency matrix"; + return std::pair(false, Poset()); + } + + if(x) { + edges.emplace_back(a, b); + } + } + } + + return std::pair(true, Poset(n, edges)); +} + +#endif //MISC_H diff --git a/src/volesti/include/misc/poset.h b/src/volesti/include/misc/poset.h new file mode 100644 index 00000000..69ba02ec --- /dev/null +++ b/src/volesti/include/misc/poset.h @@ -0,0 +1,131 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2021 Vaibhav Thakkar + +// Contributed and/or modified by Vaibhav Thakkar, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef POSET_H +#define POSET_H + +#include +#include +#include + +/// A class to represent a partial order set aka Poset +class Poset { +public: + typedef std::pair RT; // relation type + typedef std::vector RV; // relations vector + +private: + unsigned int n; // elements will be from 0 to n-1 + RV order_relations; // pairs of form a <= b + +public: + Poset() {} + + Poset(unsigned int _n, RV& _order_relations) : + n(_n), order_relations(verify(_order_relations, n)) + {} + + + // verify if the relations are valid + static RV verify(const RV& relations, unsigned int n) + { + for (int i = 0; i < relations.size(); i++) { + if (relations[i].first < 0 || relations[i].first >= n) + throw "invalid elements in order relations"; + + if (relations[i].second < 0 || relations[i].second >= n) + throw "invalid elements in order relations"; + } + + // TODO: Check if corresponding DAG is actually acyclic + + return relations; + } + + + void print() + { + std::cout << "Number of elements: " << num_elem() << std::endl; + + unsigned int count = num_relations(); + for(int i=0; i + bool is_in(const std::vector& pt_coeffs, NT tol=NT(0)) const + { + for (int i = 0; i < order_relations.size(); i++) { + unsigned int a = order_relations[i].first; + unsigned int b = order_relations[i].second; + + if (! (pt_coeffs[a] - pt_coeffs[b] <= tol) ) { + return false; + } + } + + return true; + } + + + std::vector topologically_sorted_list() const + { + std::vector > adjList(n); + std::vector indegree(n, 0); + + for(auto x: order_relations) { + adjList[x.first].push_back(x.second); + indegree[x.second] += 1; + } + + std::queue q; + for(unsigned int i=0; i res; + while(!q.empty()) { + unsigned int curr = q.front(); + res.push_back(curr); + q.pop(); + + for(unsigned int i=0; i +#include +#include +#include +#include +#include +#include +#include + +/** + * Used to specify the column format + */ +enum class VariadicTableColumnFormat +{ + AUTO, + SCIENTIFIC, + FIXED, + PERCENT +}; + +/** + * A class for "pretty printing" a table of data. + * + * Requries C++11 (and nothing more) + * + * It's templated on the types that will be in each column + * (all values in a column must have the same type) + * + * For instance, to use it with data that looks like: "Fred", 193.4, 35, "Sam" + * with header names: "Name", "Weight", "Age", "Brother" + * + * You would invoke the table like so: + * VariadicTable vt("Name", "Weight", "Age", "Brother"); + * + * Then add the data to the table: + * vt.addRow("Fred", 193.4, 35, "Sam"); + * + * And finally print it: + * vt.print(); + */ +template +class VariadicTable +{ +public: + /// The type stored for each row + typedef std::tuple DataTuple; + + /** + * Construct the table with headers + * + * @param headers The names of the columns + * @param static_column_size The size of columns that can't be found automatically + */ + VariadicTable(std::vector headers, + unsigned int static_column_size = 0, + unsigned int cell_padding = 1) + : _headers(headers), + _num_columns(std::tuple_size::value), + _static_column_size(static_column_size), + _cell_padding(cell_padding) + { + assert(headers.size() == _num_columns); + } + + /** + * Add a row of data + * + * Easiest to use like: + * table.addRow({data1, data2, data3}); + * + * @param data A Tuple of data to add + */ + void addRow(Ts... entries) { _data.emplace_back(std::make_tuple(entries...)); } + + /** + * Pretty print the table of data + */ + template + void print(StreamType & stream) + { + size_columns(); + + // Start computing the total width + // First - we will have _num_columns + 1 "|" characters + unsigned int total_width = _num_columns + 1; + + // Now add in the size of each colum + for (auto & col_size : _column_sizes) + total_width += col_size + (2 * _cell_padding); + + // Print out the top line + stream << std::string(total_width, '-') << "\n"; + + // Print out the headers + stream << "|"; + for (unsigned int i = 0; i < _num_columns; i++) + { + // Must find the center of the column + auto half = _column_sizes[i] / 2; + half -= _headers[i].size() / 2; + + stream << std::string(_cell_padding, ' ') << std::setw(_column_sizes[i]) << std::left + << std::string(half, ' ') + _headers[i] << std::string(_cell_padding, ' ') << "|"; + } + + stream << "\n"; + + // Print out the line below the header + stream << std::string(total_width, '-') << "\n"; + + // Now print the rows of the table + for (auto & row : _data) + { + stream << "|"; + print_each(row, stream); + stream << "\n"; + } + + // Print out the line below the header + stream << std::string(total_width, '-') << "\n"; + } + + /** + * Set how to format numbers for each column + * + * Note: this is ignored for std::string columns + * + * @column_format The format for each column: MUST be the same length as the number of columns. + */ + void setColumnFormat(const std::vector & column_format) + { + assert(column_format.size() == std::tuple_size::value); + + _column_format = column_format; + } + + /** + * Set how many digits of precision to show for floating point numbers + * + * Note: this is ignored for std::string columns + * + * @column_format The precision for each column: MUST be the same length as the number of columns. + */ + void setColumnPrecision(const std::vector & precision) + { + assert(precision.size() == std::tuple_size::value); + _precision = precision; + } + +protected: + // Just some handy typedefs for the following two functions + typedef decltype(&std::right) right_type; + typedef decltype(&std::left) left_type; + + // Attempts to figure out the correct justification for the data + // If it's a floating point value + template ::type>::value>::type> + static right_type justify(int /*firstchoice*/) + { + return std::right; + } + + // Otherwise + template + static left_type justify(long /*secondchoice*/) + { + return std::left; + } + + /** + * These three functions print out each item in a Tuple into the table + * + * Original Idea From From https://stackoverflow.com/a/26908596 + * + * BTW: This would all be a lot easier with generic lambdas + * there would only need to be one of this sequence and then + * you could pass in a generic lambda. Unfortunately, that's C++14 + */ + + /** + * This ends the recursion + */ + template + void print_each(TupleType &&, + StreamType & /*stream*/, + std::integral_constant< + size_t, + std::tuple_size::type>::value>) + { + } + + /** + * This gets called on each item + */ + template ::type>::value>::type> + void print_each(TupleType && t, StreamType & stream, std::integral_constant) + { + auto & val = std::get(t); + + // Set the precision + if (!_precision.empty()) + { + assert(_precision.size() == + std::tuple_size::type>::value); + + stream << std::setprecision(_precision[I]); + } + + // Set the format + if (!_column_format.empty()) + { + assert(_column_format.size() == + std::tuple_size::type>::value); + + if (_column_format[I] == VariadicTableColumnFormat::SCIENTIFIC) + stream << std::scientific; + + if (_column_format[I] == VariadicTableColumnFormat::FIXED) + stream << std::fixed; + + if (_column_format[I] == VariadicTableColumnFormat::PERCENT) + stream << std::fixed << std::setprecision(2); + } + + stream << std::string(_cell_padding, ' ') << std::setw(_column_sizes[I]) + << justify(0) << val << std::string(_cell_padding, ' ') << "|"; + + // Unset the format + if (!_column_format.empty()) + { + // Because "stream << std::defaultfloat;" won't compile with old GCC or Clang + stream.unsetf(std::ios_base::floatfield); + } + + // Recursive call to print the next item + print_each(std::forward(t), stream, std::integral_constant()); + } + + /** + * his is what gets called first + */ + template + void print_each(TupleType && t, StreamType & stream) + { + print_each(std::forward(t), stream, std::integral_constant()); + } + + /** + * Try to find the size the column will take up + * + * If the datatype has a size() member... let's call it + */ + template + size_t sizeOfData(const T & data, decltype(((T *)nullptr)->size()) * /*dummy*/ = nullptr) + { + return data.size(); + } + + /** + * Try to find the size the column will take up + * + * If the datatype is an integer - let's get it's length + */ + template + size_t sizeOfData(const T & data, + typename std::enable_if::value>::type * /*dummy*/ = nullptr) + { + if (data == 0) + return 1; + + return std::log10(data) + 1; + } + + /** + * If it doesn't... let's just use a statically set size + */ + size_t sizeOfData(...) { return _static_column_size; } + + /** + * These three functions iterate over the Tuple, find the printed size of each element and set it + * in a vector + */ + + /** + * End the recursion + */ + template + void size_each(TupleType &&, + std::vector & /*sizes*/, + std::integral_constant< + size_t, + std::tuple_size::type>::value>) + { + } + + /** + * Recursively called for each element + */ + template ::type>::value>::type> + void + size_each(TupleType && t, std::vector & sizes, std::integral_constant) + { + sizes[I] = sizeOfData(std::get(t)); + + // Override for Percent + if (!_column_format.empty()) + if (_column_format[I] == VariadicTableColumnFormat::PERCENT) + sizes[I] = 6; // 100.00 + + // Continue the recursion + size_each(std::forward(t), sizes, std::integral_constant()); + } + + /** + * The function that is actually called that starts the recursion + */ + template + void size_each(TupleType && t, std::vector & sizes) + { + size_each(std::forward(t), sizes, std::integral_constant()); + } + + /** + * Finds the size each column should be and set it in _column_sizes + */ + void size_columns() + { + _column_sizes.resize(_num_columns); + + // Temporary for querying each row + std::vector column_sizes(_num_columns); + + // Start with the size of the headers + for (unsigned int i = 0; i < _num_columns; i++) + _column_sizes[i] = _headers[i].size(); + + // Grab the size of each entry of each row and see if it's bigger + for (auto & row : _data) + { + size_each(row, column_sizes); + + for (unsigned int i = 0; i < _num_columns; i++) + _column_sizes[i] = std::max(_column_sizes[i], column_sizes[i]); + } + } + + /// The column headers + std::vector _headers; + + /// Number of columns in the table + unsigned int _num_columns; + + /// Size of columns that we can't get the size of + unsigned int _static_column_size; + + /// Size of the cell padding + unsigned int _cell_padding; + + /// The actual data + std::vector _data; + + /// Holds the printable width of each column + std::vector _column_sizes; + + /// Column Format + std::vector _column_format; + + /// Precision For each column + std::vector _precision; +}; diff --git a/src/volesti/include/nlp_oracles/nlp_hpolyoracles.hpp b/src/volesti/include/nlp_oracles/nlp_hpolyoracles.hpp new file mode 100644 index 00000000..d7394ca2 --- /dev/null +++ b/src/volesti/include/nlp_oracles/nlp_hpolyoracles.hpp @@ -0,0 +1,572 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +/* This file contains the implementation of boundary oracles between an + d-dimensional H-polytope P: Ax <= b with a curve p(t) = sum c_j phi_j(t) + where c_j are d-dimensional coefficients and phi_j(t) are basis functions + (e.g. polynomials, rational functions, splines). The boundary oracle + returns one root (in general multiple roots exist) assuming that for t >= t_p + the curve p(t) penetrates the H-polytope. The problem reduces to the following + non-linear optimization problem for finding the maximum t such that p(t) penetrates + the polytope where t lies inside [t0, t0 + eta] for some eta > 0 + + max t + + subject to + t >= t0 + t <= t0 + eta + A p(t) <= b + + The second constraint can be rewritten as (A*C) * Phi <= b which is eventually + the optimization problem we solve where the vector Phi contains all the basis + functions and C has c_j's as columns. + + The second optimization problem that can be solved is the following + + min_i min t_i + + subject to + + t0 <= t_i <= t0 + eta + A_i^T p(t_i) = b_i + A_j^T p(t_i) <= b_i j neq i + + + + We use interior-point methods to solve the non-linear optimization program + using COIN-OR ipopt and the ifopt interface for Eigen + ipopt. + +*/ + +#ifndef NLP_HPOLYORACLES_HPP +#define NLP_HPOLYORACLES_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "root_finders/root_finders.hpp" +#define MAX_NR_TRIES 10000 + +#define POLYTOL 1e-7 + +#ifndef isnan + using std::isnan; +#endif + +#ifndef isinf + using std::isinf; +#endif + + +using namespace ifopt; + +/// Define the variable t we use in the optimization +/// \tparam VT Vector Type +/// \tparam NT Numeric Type +template +class HPolyOracleVariables : public VariableSet { +public: + NT t, tb, eta; + + HPolyOracleVariables(NT t_prev, NT tb_, NT eta_=-1): + VariableSet(1, "t"), t(t_prev), tb(tb_), eta(eta_) {}; + + void SetVariables(const VT& T) override { + t = T(0); + } + + VectorXd GetValues() const override { + VectorXd T(1); + T(0) = t; + return T; + } + + VecBound GetBounds() const override { + VecBound bounds(GetRows()); + NT tu = eta > 0 ? NT(tb + eta) : NT(inf); + + bounds.at(0) = Bounds(tb, tu); + return bounds; + }; + +}; + +/// Define the cost function f(t) = t (ipopt takes minimization so it is -t) +/// \tparam VT Vector Type +/// \tparam NT Numeric Type +template +class HPolyOracleCost : public CostTerm { +public: + std::string method; + + HPolyOracleCost(std::string method_) : + CostTerm("h_poly_cost"), method(method_) {}; + + NT GetCost() const override { + VectorXd T = GetVariables()->GetComponent("t")->GetValues(); + if (method == "max_pos") return - T(0); + else if (method == "min_pos") return T(0); + } + + void FillJacobianBlock (std::string var_set, Jacobian& jac) const override { + if (var_set == "t") { + if (method == "max_pos") jac.coeffRef(0, 0) = (NT) (-1.0); + else if (method == "min_pos") jac.coeffRef(0, 0) = (NT) (1.0); + } + } + +}; + +/// Define the feasibility constraint A p(t) <= b which we translate +/// to (A * C) * Phi <= b +/// \tparam MT Matrix Type +/// \tparam VT Vector Type +/// \tparam NT Numeric Type +/// \tparam bfunc feasibility constraint type +template +class HPolyOracleFeasibility : public ConstraintSet { +public: + MT &C; + VT &b; + bfunc phi, grad_phi; + NT t0; + int m, M; + std::string method; + int index; + int ignore_facet; + + HPolyOracleFeasibility( + MT &C_, + VT &b_, + NT t0_, + bfunc basis, + bfunc basis_grad, + std::string method_, + int i, + int ignore_facet_=-1) : + C(C_), b(b_), t0(t0_), phi(basis), grad_phi(basis_grad), + ConstraintSet(C_.rows(), "h_poly_feasibility"), + method(method_), index(i), ignore_facet(ignore_facet_) { + m = C_.rows(); + M = C_.cols(); + }; + + // Define bounds for feasibility + VecBound GetBounds() const override { + VecBound bounds(GetRows()); + for (int i = 0; i < m; i++) { + if (method == "min_pos" && i == index) { + bounds.at(i) = Bounds(b(i), b(i)); + } + else { + bounds.at(i) = Bounds((NT) (-inf), b(i)); + } + + if (i == ignore_facet) { + bounds.at(i) = Bounds((NT) (-inf), (NT) (inf)); + } + + } + return bounds; + } + + VectorXd GetValues() const override { + VectorXd T = GetVariables()->GetComponent("t")->GetValues(); + NT t = T(0); + VectorXd phis; + phis.resize(M); + + for (int i = 0; i < M; i++) { + phis(i) = (NT) (phi(t, t0, i, M)); + } + return C * phis; + } + + // Calculate jacobian matrix of constraints + void FillJacobianBlock (std::string var_set, Jacobian& jac_block) const override { + + if (var_set == "t") { + VectorXd T = GetVariables()->GetComponent("t")->GetValues(); + NT t = T(0); + NT temp; + + for (int i = 0; i < m; i++) { + jac_block.coeffRef(i, 0) = NT(0); + for (int j = 0; j < M; j++) { + temp = grad_phi(t, t0, j, M); + if (isinf(temp)) continue; + else jac_block.coeffRef(i, 0) += temp; + } + } + } + } + + +}; + +/// Oracle that uses the COIN-OR ipopt solver +/// \tparam Polytope Polytope Type +/// \tparam bfunc feasibility constraint type +template +struct IpoptHPolyoracle { + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + typedef typename Polytope::PointType Point; + + + std::tuple apply( + NT t_prev, + NT t0, + NT eta, + MT &A, + VT &b, + Polytope &P, + std::vector &coeffs, + bfunc phi, + bfunc grad_phi, + int ignore_facet=-1, + std::string solution="max_pos") + { + + MT C, C_tmp; + C_tmp.resize(coeffs[0].dimension(), coeffs.size()); + + + for (int i = 0; i < coeffs.size(); i++) { + C_tmp.col(i) = coeffs[i].getCoefficients(); + } + + // C_tmp: dimension x num_coeffs + // A: constraints x dimension + // C: constraints x num_coeffs + C = A * C_tmp; + + // Initialize COIN-OR ipopt solver + IpoptSolver ipopt; + ipopt.SetOption("linear_solver", "mumps"); + ipopt.SetOption("jacobian_approximation", "exact"); + ipopt.SetOption("tol", POLYTOL); + ipopt.SetOption("acceptable_tol", 100 * POLYTOL); + ipopt.SetOption("max_iter", 1000000); + + ipopt.SetOption("print_level", 0); + ipopt.SetOption("sb", "yes"); + + NT t, t_tmp; + + if (solution == "max_pos") { + + Problem nlp; + std::shared_ptr> + hpolyoraclevariables (new HPolyOracleVariables(t_prev, t0, eta)); + std::shared_ptr> + hpolyoraclecost (new HPolyOracleCost(solution)); + std::shared_ptr> + hpolyoraclefeasibility (new HPolyOracleFeasibility + (C, b, t0, phi, grad_phi, "max_pos", 0, ignore_facet)); + + nlp.AddVariableSet (hpolyoraclevariables); + nlp.AddCostSet (hpolyoraclecost); + nlp.AddConstraintSet(hpolyoraclefeasibility); + + ipopt.Solve(nlp); + + t = nlp.GetOptVariables()->GetValues()(0); + + } + + if (solution == "min_pos") { + int m = A.rows(); + + t = eta > 0 ? t0 + eta : std::numeric_limits::max(); + + for (int i = 0; i < m; i++) { + + if (i == ignore_facet) continue; + + Problem nlp; + std::shared_ptr> + hpolyoraclevariables (new HPolyOracleVariables(t_prev, t0, eta)); + std::shared_ptr> + hpolyoraclecost (new HPolyOracleCost(solution)); + std::shared_ptr> + hpolyoraclefeasibility (new HPolyOracleFeasibility + (C, b, t0, phi, grad_phi, "min_pos", i)); + + nlp.AddVariableSet (hpolyoraclevariables); + nlp.AddCostSet (hpolyoraclecost); + nlp.AddConstraintSet(hpolyoraclefeasibility); + + t_tmp = nlp.GetOptVariables()->GetValues()(0); + + std::cout << "t is " << t_tmp << std::endl; + + if (t_tmp < t && t_tmp > t0) t = t_tmp; + + + } + } + + Point p(coeffs[0].dimension()); + + for (unsigned int i = 0; i < coeffs.size(); i++) { + p += phi(t, t0, i, coeffs.size()) * coeffs[i]; + } + + const NT* b_data = b.data(); + + int f_min = -1; + NT dist_min = std::numeric_limits::max(); + + for (int i = 0; i < A.rows(); i++) { + if (*b_data == 0 && std::abs(*b_data - A.row(i) * p.getCoefficients()) < dist_min) { + f_min = i; + dist_min = std::abs(*b_data - A.row(i) * p.getCoefficients()); + } + else if (*b_data != 0 && std::abs(*b_data - A.row(i) * p.getCoefficients()) / *b_data < dist_min) { + f_min = i; + dist_min = std::abs(*b_data - A.row(i) * p.getCoefficients()) / *b_data; + } + b_data++; + } + + return std::make_tuple(t, p, f_min); + }; + +}; + +/// Compute intersection of H-polytope P := Ax <= b +/// with polynomial curve p(t) = sum a_j (t - t0)^j +/// Uses the MPsolve library +/// \tparam Polytope Polytope Type +/// \tparam bfunc feasibility constraint type +template +struct MPSolveHPolyoracle { + + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + typedef typename Polytope::PointType Point; + + std::tuple apply( + NT t_prev, + NT t0, + NT eta, + MT &A, + VT &b, + Polytope &P, + std::vector &coeffs, + bfunc phi, + bfunc grad_phi, + int ignore_facet=-1, + bool positive_real=true) + { + NT maxNT = std::numeric_limits::max(); + NT minNT = std::numeric_limits::lowest(); + + NT tu = eta > 0 ? t0 + eta : NT(maxNT); + NT t = tu; + Point dummy(coeffs[0].dimension()); + + for (unsigned int j = 0; j < coeffs.size(); j++) { + dummy = dummy + pow(tu - t0, NT(j)) * coeffs[j]; + } + + std::tuple result = std::make_tuple(tu, dummy, -1); + + int m = A.rows(); + + // Keeps constants A_i^T C_j + std::vector Z(coeffs.size(), NT(0)); + + // std::vector> solutions; + + // Iterate over all hyperplanes + for (int i = 0; i < m; i++) { + + if (i == ignore_facet) continue; + + for (unsigned int j = 0; j < coeffs.size(); j++) { + Z[j] = A.row(i) * coeffs[j].getCoefficients(); + + #ifdef VOLESTI_DEBUG + std::cout << "Z [ " << j << " ] = " << Z[j] << std::endl; + #endif + } + + // Find point projection on m-th hyperplane + Z[0] -= b(i); + + std::vector> solutions = mpsolve(Z, positive_real); + + for(std::pair sol: solutions) { + + #ifdef VOLESTI_DEBUG + std::cout << "Facet: " << i << " Candidate root is " << sol.first + t0 << std::endl; + #endif + + // Check if solution is in the desired range [t0, t0 + eta] and if it is the current minimum + if (t0 + sol.first <= tu && t0 + sol.first < std::get<0>(result)) { + t = t0 + sol.first; + + // Calculate point from this root + Point p = Point(coeffs[0].dimension()); + + for (unsigned int j = 0; j < coeffs.size(); j++) { + p += pow(t - t0, NT(j)) * coeffs[j] ; + } + + #ifdef VOLESTI_DEBUG + std::cout << "Calculcated point is " << std::endl << p.getCoefficients() << std::endl; + #endif + + // Check if point satisfies Ax <= b up to some tolerance and change current solution + if (P.is_in(p, 1e-6)) { + result = std::make_tuple(t, p, i); + } + } + } + + } + + return result; + }; + +}; + +/// Compute intersection of H-polytope P := Ax <= b +/// with curve p(t) = sum a_j phi_j(t) where phi_j are basis +/// functions (e.g. polynomials) +/// Uses Newton-Raphson to solve the transcendental equation +/// \tparam Polytope Polytope Type +/// \tparam bfunc feasibility constraint type +template +struct NewtonRaphsonHPolyoracle { + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + typedef typename Polytope::PointType Point; + + std::tuple apply( + NT t_prev, + NT t0, + NT eta, + MT &A, + VT &b, + Polytope &P, + std::vector &coeffs, + bfunc phi, + bfunc grad_phi, + int ignore_facet=-1) + { + + // Keep results in a vector (in case of multiple roots) + // The problem has O(m * len(coeffs)) solutions if phi's are polys + // due to the Fundamental Theorem of Algebra + // Some roots may be common for more than one hyperplanes + NT maxNT = std::numeric_limits::max(); + NT minNT = std::numeric_limits::lowest(); + + // Root + NT t = t_prev; + NT tu = eta > 0 ? t0 + eta : NT(maxNT); + + Point dummy(coeffs[0].dimension()); + + for (unsigned int j = 0; j < coeffs.size(); j++) { + dummy += coeffs[j] * phi(tu, t0, j, coeffs.size()); + } + + + std::tuple result = std::make_tuple(tu, dummy, -1); + + // Helper variables for Newton-Raphson + NT dot_u, num, den, den_tmp; + + // Regularization for NR (e.g. at critical points where grad = 0) + NT reg = (NT) 1e-7; + VT Z; + int m = A.rows(); + + // Keeps constants A_i^T C_j + Z.resize(coeffs.size()); + + // Iterate over all hyperplanes + for (int i = 0; i < m; i++) { + + if (i == ignore_facet) continue; + + // Calculate constants + start_iter: t_prev = t0 + reg; + + + for (unsigned int j = 0; j < coeffs.size(); j++) { + Z(j) = A.row(i) * coeffs[j].getCoefficients(); + } + + + for (int tries = 0; tries < MAX_NR_TRIES; tries++) { + + num = - b(i); + den = (NT) 0; + + // Calculate numerator f(t) and denominator f'(t) + for (int j = 0; j < coeffs.size(); j++) { + num += Z(j) * phi(t_prev, t0, j, coeffs.size()); + + // Avoid ill-posed derivative (e.g. 0^{-1}) + if (j > 0) den += Z(j) * grad_phi(t_prev, t0, j, coeffs.size()); + } + + // Regularize denominator if near 0 + if (std::abs(den) < 10 * reg) den += reg; + + // Newton-Raphson Iteration t = t_old - f(t) / f'(t) + t = t_prev - num / den; + + if (t < 0 && t_prev < 0) continue; + + if (std::abs(t - t_prev) < 1e-6 && t > t0) { + // Add root (as t) and facet + + Point p = Point(coeffs[0].dimension()); + + for (unsigned int j = 0; j < coeffs.size(); j++) { + p += coeffs[j] * phi(t, t0, j, coeffs.size()); + } + + if (P.is_in(p) && t < std::get<0>(result)) + result = std::make_tuple(t, p, i); + + } + + t_prev = t; + + } + + } + + return result; + + }; + +}; + + +#endif diff --git a/src/volesti/include/nlp_oracles/nlp_oracles.hpp b/src/volesti/include/nlp_oracles/nlp_oracles.hpp new file mode 100644 index 00000000..11f978e8 --- /dev/null +++ b/src/volesti/include/nlp_oracles/nlp_oracles.hpp @@ -0,0 +1,22 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#include +#include +#include +#include + +#include "nlp_hpolyoracles.hpp" +#include "nlp_vpolyoracles.hpp" + +#ifndef NLP_POLYORACLES_HPP +#define NLP_POLYORACLES_HPP + +#endif diff --git a/src/volesti/include/nlp_oracles/nlp_vpolyoracles.hpp b/src/volesti/include/nlp_oracles/nlp_vpolyoracles.hpp new file mode 100644 index 00000000..9e921d9c --- /dev/null +++ b/src/volesti/include/nlp_oracles/nlp_vpolyoracles.hpp @@ -0,0 +1,314 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +/* This file contains the implementation of boundary oracles between an + d-dimensional V-polytope given by V with a curve p(t) = sum c_j phi_j(t) + where c_j are d-dimensional coefficients and phi_j(t) are basis functions + (e.g. polynomials, rational functions, splines). The boundary oracle + returns one root (in general multiple roots exist) assuming that for t >= t_p + the curve p(t) penetrates the V-polytope. The problem reduces to the following + non-linear optimization problem + + max t + + subject to + t >= 0 + lambda_i >= 0 i \in [V] + \sum_{i = 1}^m lambda_i = 1 + \sum_{i = 1}^m lambda_i v_i - \sum_{i = 1}^M c_j phi_j(t) = 0 + + We use interior-point methods to solve the non-linear optimization program + using COIN-OR ipopt and the ifopt interface for Eigen + ipopt. + +*/ + +#ifndef NLP_VPOLYORACLES_HPP +#define NLP_VPOLYORACLES_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ifopt; + +/// Define the variable t we use in the optimization +/// \tparam VT Vector Type +/// \tparam MT Matrix Type +template +class VPolyOracleVariableT : public VariableSet { +public: + NT t, tb, eta; + + VPolyOracleVariableT(NT t_prev, NT tb_, NT eta_=-1): VariableSet(1, "t"), t(t_prev), tb(tb_), eta(eta_) {}; + + void SetVariables(const VT& T) override { + t = T(0); + } + + VectorXd GetValues() const override { + VectorXd T(1); + T(0) = t; + return T; + } + + VecBound GetBounds() const override { + VecBound bounds(GetRows()); + NT tu = eta > 0 ? NT(tb + eta) : NT(inf); + + bounds.at(0) = Bounds(tb, tu); + return bounds; + }; + +}; + + +/// Define the variable t we use in the optimization +/// \tparam VT Vector Type +/// \tparam MT Matrix Type +template +class VPolyOracleVariableLambdas : public VariableSet { +public: + VectorXd lambdas; + int m; + + VPolyOracleVariableLambdas(int m_): m(m_), VariableSet(m_, "lambdas") { + lambdas.resize(m_); + for (int i = 0; i < m_; i++) lambdas(i) = NT(0); + }; + + void SetVariables(const VectorXd& lambdas_) override { + // for (int i = 0; i < m; i++) lambdas(i) = lambdas_(i); + lambdas = lambdas_; + } + + VectorXd GetValues() const override { + return lambdas; + } + + VecBound GetBounds() const override { + VecBound bounds(GetRows()); + for (int i = 0; i < m; i++) bounds.at(i) = Bounds(NT(0), NT(inf)); + return bounds; + }; + +}; + +/// Define the cost function f(t) = t (ipopt takes minimization so it is -t) +/// \tparam VT Vector Type +/// \tparam MT Matrix Type +template +class VPolyOracleCost : public CostTerm { +public: + int m; + + VPolyOracleCost(int m_) : CostTerm("v_poly_cost"), m(m_) {}; + + NT GetCost() const override { + VectorXd T = GetVariables()->GetComponent("t")->GetValues(); + return - T(0); + } + + void FillJacobianBlock (std::string var_set, Jacobian& jac) const override { + if (var_set == "t") jac.coeffRef(0, 0) = NT(-1.0); + if (var_set == "lambdas") { + for (int i = 0; i < m; i++) { + jac.coeffRef(0, i) = NT(0.0); + } + } + } + + +}; + +/// Define the feasibility lambdas +/// \tparam VT Vector Type +/// \tparam MT Matrix Type +template +class VPolyoracleFeasibilityLambdas : public ConstraintSet { +public: + int m; + + VPolyoracleFeasibilityLambdas(int m_) : ConstraintSet(1, "lambdas_simplex"), m(m_) {}; + + + VectorXd GetValues() const override { + VectorXd lambdas = GetVariables()->GetComponent("lambdas")->GetValues(); + VectorXd S(1); + S(0) = NT(0); + for (int i = 0; i < m; i++) S(0) += lambdas(i); + return S; + } + + VecBound GetBounds() const override { + VecBound bounds(GetRows()); + bounds.at(0) = Bounds(NT(1.0), NT(1.0)); + return bounds; + } + + void FillJacobianBlock (std::string var_set, Jacobian& jac) const override { + if (var_set == "lambdas") + for (int i = 0; i < m; i++) { + jac.coeffRef(0, i) = NT(1.0); + } + if (var_set == "t") jac.coeffRef(0, 0) = NT(0); + } + + +}; + +template +class VPolyOracleFeasibilityCurve : public ConstraintSet { +public: + int m; // number of lambdas + int M; // number of coefficients + int d_; // dimension + std::vector &coeffs; + MT &V; + NT t0; + + bfunc phi, grad_phi; + +// V, coeffs, t0, phi, grad_phi + + VPolyOracleFeasibilityCurve(MT &V_, std::vector &coeffs_, NT t0_, bfunc basis, bfunc basis_grad) : + V(V_), coeffs(coeffs_), t0(t0_), phi(basis), grad_phi(basis_grad), ConstraintSet(V_.cols() ,"curve_feasibility") { + m = V.rows(); + M = (int) (coeffs.size()); + d_ = V.cols(); + } + + VecBound GetBounds() const override { + VecBound bounds(GetRows()); + for (int i = 0; i < d_; i++) bounds.at(i) = Bounds(NT(0), NT(0)); + return bounds; + } + + VectorXd GetValues() const override { + VectorXd values_(d_); + NT t = GetVariables()->GetComponent("t")->GetValues()(0); + VectorXd lambdas = GetVariables()->GetComponent("lambdas")->GetValues(); + + for (int i = 0; i < d_; i++) { + values_(i) = NT(0); + + for (int j = 0; j < m; j++) { + values_(i) += lambdas(j) * V(j, i); + } + + for (int j = 0; j < M; j++) { + values_(i) -= phi(t, t0, j, M) * coeffs[j][i]; + } + + } + + return values_; + + } + + void FillJacobianBlock (std::string var_set, Jacobian& jac) const override { + if (var_set == "t") { + NT t = GetVariables()->GetComponent("lambdas")->GetValues()(0); + + for (int i = 0; i < d_; i++) { + for (int j = 0; j < M; j++) { + jac.coeffRef(i, 0) -= grad_phi(t, t0, j, M) * coeffs[j][i]; + } + } + } + + if (var_set == "lambdas") { + for (int i = 0; i < d_; i++) { + for (int j = 0; j < m; j++) { + jac.coeffRef(i, j) = V(j, i); + } + } + } + + } + +}; + +/// Oracle for V-polytopes +/// \tparam Polytope Polytope Type +/// \tparam bfunc feasibility constraint type +template +struct IpoptVPolyoracle { + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::NT NT; + typedef typename Polytope::PointType Point; + + std::tuple apply( + NT t_prev, + NT t0, + NT eta, + MT &V, + Polytope &P, + std::vector &coeffs, + bfunc phi, + bfunc grad_phi, + int ignore_facet=-1) { // ignore facet not supported + + Problem nlp; + + int m = V.rows(); + + std::shared_ptr> vpolyoraclevariablet (new VPolyOracleVariableT(t_prev, t0, eta)); + std::shared_ptr> vpolyoraclevariable_lambdas (new VPolyOracleVariableLambdas(m)); + + nlp.AddVariableSet(vpolyoraclevariablet); + nlp.AddVariableSet(vpolyoraclevariable_lambdas); + + std::shared_ptr> vpolyoraclecost (new VPolyOracleCost(m)); + + nlp.AddCostSet(vpolyoraclecost); + + std::shared_ptr> vpolyoraclefeasibility_lambdas (new VPolyoracleFeasibilityLambdas(m)); + std::shared_ptr> vpolyoraclefeasibility_curve (new VPolyOracleFeasibilityCurve(V, coeffs, t0, phi, grad_phi)); + + nlp.AddConstraintSet(vpolyoraclefeasibility_lambdas); + nlp.AddConstraintSet(vpolyoraclefeasibility_curve); + + nlp.PrintCurrent(); + + IpoptSolver ipopt; + ipopt.SetOption("linear_solver", "mumps"); + + // TODO fix exact jacobian + ipopt.SetOption("jacobian_approximation", "finite-difference-values"); + ipopt.SetOption("tol", 1e-7); + ipopt.SetOption("print_level", 0); + ipopt.SetOption("sb", "yes"); + + ipopt.Solve(nlp); + + NT t = nlp.GetOptVariables()->GetValues()(0); + + Point p(coeffs[0].dimension()); + + for (unsigned int i = 0; i < coeffs.size(); i++) { + p += phi(t, t0, i, coeffs.size()) * coeffs[i]; + } + + return std::make_tuple(t, p, NT(-1)); + + } + + + +}; + +#endif diff --git a/src/volesti/include/ode_solvers/basis.hpp b/src/volesti/include/ode_solvers/basis.hpp new file mode 100644 index 00000000..f51e92f7 --- /dev/null +++ b/src/volesti/include/ode_solvers/basis.hpp @@ -0,0 +1,186 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_BASIS_HPP +#define ODE_SOLVERS_BASIS_HPP + +enum BasisType { + DERIVATIVE = 0, + FUNCTION = 1, + INTEGRAL = 2 +}; + + +template +struct PolynomialBasis { + BasisType basis_type; + + PolynomialBasis(BasisType basis_type_) : basis_type(basis_type_) {} + + NT operator() (NT t, const NT t0, unsigned int j, const unsigned int ord) const { + switch (basis_type) { + case FUNCTION: + return pow(t - t0, NT(j)); + case DERIVATIVE: + return NT(j) * pow(t - t0, NT(j - 1)); + case INTEGRAL: + return pow(t - t0, NT(j + 1)) / NT(j + 1); + } + } + +}; + +template +struct Polynomial { + + BasisType basis_type; + PolynomialBasis basis; + std::vector coeffs; + NT result; + unsigned int ord; + + Polynomial(std::vector coeffs_, BasisType basis_type_) : + basis_type(basis_type_), coeffs(coeffs_), basis(basis_type_) { + ord = coeffs.size(); + } + + NT operator() (NT t, NT t0, unsigned int j=-1, unsigned int ord=-1) { + result = NT(0); + + for (unsigned int i = 0; i < ord; i++) { + result += coeffs[i] * basis(t, t0, i, ord); + } + + return result; + } + + static std::vector convolve(std::vector const& p, std::vector const& q) { + // Performs direct convolution of p and q (less round-off error than FFT) + unsigned int n = p.size(); + unsigned int m = q.size(); + unsigned int r = n + m - 1; + + std::vector result(r, NT(0)); + + unsigned int j, k; + + for (unsigned int i = 0; i < r; i++) { + j = (i >= m - 1)? i - (m - 1) : 0; + k = (i < n - 1)? i : n - 1; + for (unsigned int z = j; z <= k; z++) result[i] += (p[z] * q[i - z]); + } + + return result; + } + + static std::vector multi_convolve(std::vector> &seq) { + + std::vector result; + result = seq[0]; + + for (unsigned int i = 1; i < seq.size(); i++) { + result = convolve(result, seq[i]); + } + + return result; + + } + + +}; + +template +struct RationalFunctionBasis { + bfunc p, q; + bfunc grad_p, grad_q; + NT reg = 1e-6; + BasisType basis_type; + NT num, den, grad_num, grad_den; + + RationalFunctionBasis(bfunc num, bfunc grad_num, bfunc den, bfunc grad_den, BasisType basis_type_) : + p(num), grad_p(grad_num), q(den), grad_q(grad_den), basis_type(basis_type_) {}; + + NT operator()(NT t, NT t0, unsigned int j, unsigned int ord) { + + switch (basis_type) { + case FUNCTION: + num = p(t, t0, j, ord); + den = q(t, t0, j, ord); + if (std::abs(den) < reg) den += reg; + return num / den; + case DERIVATIVE: + num = p(t, t0, j, ord); + grad_num = grad_p(t, t0, j, ord); + den = q(t, t0, j, ord); + grad_den = grad_q(t, t0, j, ord); + if (std::abs(den * den) < reg) den += reg; + return (grad_num / den) - (grad_den * num) / den; + case INTEGRAL: + throw true; + } + } + +}; + +template +struct LagrangePolynomial { + + VT coeffs; + VT nodes; + int basis = -1; + + LagrangePolynomial() {} + + int order() const { + return nodes.rows(); + } + + void set_nodes(VT nodes_) { + nodes = nodes_; + } + + void set_basis(int basis_) { + basis = basis_; + } + + void set_coeffs(VT coeffs_) { + coeffs = coeffs_; + } + + NT operator() (NT t) const { + + int j = (int) round((order() * acos(t)) / M_PI - 0.5); + NT temp = cos((j+0.5) * M_PI / order()); + + if (abs(temp - nodes(j)) < 1e-6) { + if (basis == -1) return coeffs(j); + else return NT(1); + } else { + throw true; + return NT(-1); + } + + } +}; + +template +void degree_doubling_chebyshev(std::vector &coeffs, + std::vector &result) { + unsigned int N = coeffs.size() - 1; + + for (int i = 0; i <= 2 * N; i++) { + if (i > N) result[i] = coeffs[i - N]; + else if (i == N) result[i] = 2 * coeffs[0]; + else result[i] = coeffs[N - i]; + } + +} + +#endif diff --git a/src/volesti/include/ode_solvers/collocation.hpp b/src/volesti/include/ode_solvers/collocation.hpp new file mode 100644 index 00000000..3b025289 --- /dev/null +++ b/src/volesti/include/ode_solvers/collocation.hpp @@ -0,0 +1,298 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_COLLOCATION_HPP +#define ODE_SOLVERS_COLLOCATION_HPP + +#include "nlp_oracles/nlp_hpolyoracles.hpp" +#include "nlp_oracles/nlp_vpolyoracles.hpp" + +template < + typename Point, + typename NT, + typename Polytope, + typename bfunc, + typename func, + typename NontLinearOracle=MPSolveHPolyoracle< + Polytope, + bfunc + > +> +struct CollocationODESolver { + + + // Vectors of points + typedef std::vector pts; + typedef std::vector ptsv; + + // typedef from existing templates + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef std::vector MTs; + typedef std::vector bounds; + typedef std::vector coeffs; + + unsigned int dim; + + NT eta; + NT t, t_prev, dt, t_temp; + const NT tol = 1e-3; + + // If set to true the solver assumes linearity of the field + // Otherwise it approximates the constant vector with Euler method + const bool exact = false; + + // If set to true it enables precomputation (does not recompute A and b at every step) + const bool precompute = true; + + bool precompute_flag = false; + + // Function oracles x'(t) = F(x, t) + func F; + + // Basis functions + bfunc phi, grad_phi; + + bounds Ks; + + // Contains the sub-states + pts xs, xs_prev, zs; + Point y; + + NT temp_grad; + NT temp_func; + + // Basis coefficients + ptsv as; + + // Temporal coefficients + coeffs cs; + + // Matrices for collocation methods + MTs As, Bs; + + // Keeps the solution to Ax = b temporarily + MTs temps; + + VT Ar, Av; + + NontLinearOracle oracle; + + int prev_facet = -1; + Point prev_point; + + CollocationODESolver(NT initial_time, NT step, pts initial_state, func oracle, + bounds boundaries, coeffs c_coeffs, bfunc basis, bfunc grad_basis) : + t(initial_time), xs(initial_state), F(oracle), eta(step), Ks(boundaries), + cs(c_coeffs), phi(basis), grad_phi(grad_basis) { + dim = xs[0].dimension(); + initialize_matrices(); + }; + + unsigned int order() const { + return cs.size(); + } + + void initialize_matrices() { + As = MTs(xs.size()); + Bs = MTs(xs.size()); + temps = MTs(xs.size()); + as = ptsv(xs.size(), pts(order(), Point(xs[0].dimension()))); + for (unsigned int i = 0; i < xs.size(); i++) { + // Gradient matrix is of size (order - 1) x (order - 1) + As[i].resize(order()-1, order()-1); + // Constants matrix is of size (order - 1) x dim + Bs[i].resize(order()-1, xs[0].dimension()); + temps[i].resize(order()-1, xs[0].dimension()); + } + + zs = pts{Point(1)}; + } + + void step() { + t_prev = t; + xs_prev = xs; + + for (unsigned int ord = 0; ord < order(); ord++) { + // Calculate t_ord + t = t_prev + cs[ord] * eta; + + for (int i = 0; i < xs.size(); i++) { + // a0 = F(x0, t0) + y = F(i,xs, t); + + if (ord == 0) { + as[i][0] = xs_prev[i]; + + if (exact) { + for (unsigned int j = 0; j < order() - 1; j++) { + Bs[i].row(j) = y.getCoefficients(); + } + } + + } + // Construct matrix A + else { + if (exact) { + + if (!precompute || (precompute && !precompute_flag)) { + for (unsigned int j = 0; j < order() - 1; j++) { + temp_grad = grad_phi(t, t_prev, order() - j - 1, order()); + zs[0].set_coord(0, phi(t, t_prev, order() - j - 1, order())); + temp_func = F(i,zs, t)[0]; + As[i](ord-1, j) = temp_grad - temp_func; + } + } + } + else { + + // Compute new derivative (inter-point) + dt = (cs[ord] - cs[ord-1]) * eta; + y = dt * y; + + // Do not take into account reflections + xs[i] += y; + + // Keep grads for matrix B + Bs[i].row(ord-1) = y.getCoefficients().transpose(); + + // Construct matrix A that contains the gradients of the basis functions + if (!precompute || (precompute && !precompute_flag)) { + for (unsigned int j = 0; j < order() - 1; j++) { + As[i](ord-1, j) = grad_phi(t, t_prev, order() - j - 1, order()); + } + } + } + } + + } + } + + // Solve linear systems + for (int i = 0; i < xs.size(); i++) { + // temp contains solution in decreasing order of bases + temps[i] = As[i].colPivHouseholderQr().solve(Bs[i]); + + for (int j = 0; j < order() - 1; j++) { + // TODO Add vectorized implementation + // as[i][order() - j - 1] += temp(j); + for (int k = 0; k < xs[0].dimension(); k++) { + as[i][order() - j - 1].set_coord(k, temps[i](j, k)); + } + } + } + + if (!exact) { + for (int r = 0; r < (int) (eta / tol); r++) { + for (unsigned int i = 0; i < xs.size(); i++) { + xs[i] = Point(xs[i].dimension()); + for (unsigned int ord = 0; ord < order(); ord++) { + xs[i] += as[i][ord] * phi(t_prev + eta, t_prev, ord, order()); + } + } + + for (unsigned int i = 0; i < xs.size(); i++) { + for (int ord = 1; ord < order(); ord++) { + t_temp = cs[ord] * eta; + y = F(i,xs, t_temp); + Bs[i].row(ord-1) = y.getCoefficients().transpose(); + } + } + + + // Solve linear systems + for (int i = 0; i < xs.size(); i++) { + // temp contains solution in decreasing order of bases + temps[i] = As[i].colPivHouseholderQr().solve(Bs[i]); + + for (int j = 0; j < order() - 1; j++) { + // TODO Add vectorized implementation + // as[i][order() - j - 1] += temp(j); + for (int k = 0; k < xs[0].dimension(); k++) { + as[i][order() - j - 1].set_coord(k, temps[i](j, k)); + } + } + } + + } + + + } + + + // Compute next point + for (unsigned int i = 0; i < xs.size(); i++) { + if (Ks[i] == NULL) { + // xs[i] = Point(xs[i].dimension()); + // for (unsigned int ord = 0; ord < order(); ord++) { + // xs[i] += as[i][ord] * phi(t_prev + eta, t_prev, ord, order()); + // } + + if (prev_facet != -1 && i > 0) Ks[i-1]->compute_reflection(xs[i], prev_point, prev_facet); + prev_facet = -1; + + + } else { + std::tuple result = Ks[i]->curve_intersect(t_prev, t_prev, + eta, as[i], phi, grad_phi, oracle); + + // Point is inside polytope + if (std::get<2>(result) == -1 && Ks[i]->is_in(std::get<1>(result))) { + // std::cout << "Inside" << std::endl; + xs[i] = Point(xs[i].dimension()); + for (unsigned int ord = 0; ord < order(); ord++) { + xs[i] += as[i][ord] * phi(t_prev + eta, t_prev, ord, order()); + } + + prev_facet = -1; + + } + else { + // std::cout << "outside" << std::endl; + // Stick to the boundary + xs[i] = 0.99 * std::get<1>(result); + prev_point = xs[i]; + prev_facet = std::get<2>(result); + + + } + } + } + + t += eta; + + precompute_flag = true; + + } + + + void print_state() { + for (int j = 0; j < xs.size(); j++) { + for (unsigned int i = 0; i < xs[j].dimension(); i++) { + std::cout << xs[j][i] << " "; + } + } + std::cout << std::endl; + } + + void steps(int num_steps) { + for (int i = 0; i < num_steps; i++) step(); + } + + Point get_state(int index) { + return xs[index]; + } + + void set_state(int index, Point p) { + xs[index] = p; + } +}; + +#endif diff --git a/src/volesti/include/ode_solvers/euler.hpp b/src/volesti/include/ode_solvers/euler.hpp new file mode 100644 index 00000000..2e240742 --- /dev/null +++ b/src/volesti/include/ode_solvers/euler.hpp @@ -0,0 +1,130 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_EULER_HPP +#define ODE_SOLVERS_EULER_HPP + +template < + typename Point, + typename NT, + typename Polytope, + typename func +> +struct EulerODESolver { + + typedef std::vector pts; + typedef std::vector bounds; + typedef typename Polytope::VT VT; + + unsigned int dim; + + NT eta; + NT t; + VT Ar, Av; + + func F; + bounds Ks; + + // Contains the sub-states + pts xs; + pts xs_prev; + + // Previous state boundary point + Point x_prev_bound; + + // Previous state boundary facet + int prev_facet = -1; + + EulerODESolver(NT initial_time, NT step, pts initial_state, func oracle, + bounds boundaries) : + eta(step), t(initial_time), F(oracle), Ks(boundaries), xs(initial_state) { + dim = xs[0].dimension(); + }; + + + void step(int k, bool accepted) { + xs_prev = xs; + t += eta; + for (unsigned int i = 0; i < xs.size(); i++) { + Point y = F(i, xs_prev, t); + y = eta * y; + + if (Ks[i] == NULL) { + xs[i] = xs_prev[i] + y; + if (prev_facet != -1 && i > 0) { + Ks[i-1]->compute_reflection(xs[i], x_prev_bound, prev_facet); + } + + } + else { + + // Find intersection (assuming a line trajectory) between x and y + do { + // Find line intersection between xs[i] (new position) and y + std::pair pbpair = Ks[i]->line_positive_intersect(xs_prev[i], y, Ar, Av); + + if (pbpair.first >= 0 && pbpair.first <= 1) { + // Advance to point on the boundary + xs_prev[i] += (pbpair.first * 0.95) * y; + + // Update facet for reflection of derivative + prev_facet = pbpair.second; + x_prev_bound = xs_prev[i]; + + // Reflect ray y on the boundary point y now is the reflected ray + Ks[i]->compute_reflection(y, xs_prev[i], pbpair.second); + // Add it to the existing (boundary) point and repeat + xs[i] = xs_prev[i] + y; + + } + else { + prev_facet = -1; + xs[i] = xs_prev[i] + y; + } + } while (!Ks[i]->is_in(xs[i])); + + } + + } + + } + + void print_state() { + for (int j = 0; j < xs.size(); j++) { + for (unsigned int i = 0; i < xs[j].dimension(); i++) { + std::cout << xs[j][i] << " "; + } + } + std::cout << std::endl; + } + + void steps(int num_steps, bool accepted) { + for (int i = 0; i < num_steps; i++) step(i, accepted); + } + + Point get_state(int index) { + return xs[index]; + } + + void set_state(int index, Point p) { + xs[index] = p; + } + + void disable_adaptive() { + // TODO Implement + } + + void enable_adaptive() { + // TODO Implement + } + +}; + +#endif diff --git a/src/volesti/include/ode_solvers/generalized_leapfrog.hpp b/src/volesti/include/ode_solvers/generalized_leapfrog.hpp new file mode 100644 index 00000000..098ea035 --- /dev/null +++ b/src/volesti/include/ode_solvers/generalized_leapfrog.hpp @@ -0,0 +1,172 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_GENERALIZED_LEAPFROG_HPP +#define ODE_SOLVERS_GENERALIZED_LEAPFROG_HPP + +template < +typename Point, +typename NT, +typename ConvexBody, +typename func +> +struct GeneralizedLeapfrogODESolver { + + typedef std::vector pts; + + typedef Eigen::Matrix MT; + + typedef std::vector bounds; + typedef typename ConvexBody::VT VT; + + unsigned int dim; + std::vector lambda_prev; + + + NT eta; + NT eta0; + NT t; + NT dl = 0.995; + + func F; + bounds Ks; + + // Contains the sub-states + pts xs; + pts xs_prev; + + MT _AA; + + std::pair pbpair; + + unsigned long long num_reflections = 0; + unsigned long long num_steps = 0; + + bool adaptive = true; + + GeneralizedLeapfrogODESolver(NT initial_time, NT step, pts initial_state, func oracle, bounds boundaries, bool adaptive_=true) : + eta(step), eta0(step), t(initial_time), F(oracle), Ks(boundaries), xs(initial_state), adaptive(adaptive_) { + dim = xs[0].dimension(); + initialize(); + }; + + void initialize() { + for (unsigned int i = 0; i < xs.size(); i++) { + lambda_prev.push_back(NT(0)); + } + } + + void disable_adaptive() { + adaptive = false; + } + + void enable_adaptive() { + adaptive = true; + } + + void step(int k, bool accepted) { + num_steps++; + + if (adaptive) eta = (eta0 * num_steps) / (num_steps + num_reflections); + + xs_prev = xs; + unsigned int x_index, v_index, it; + t += eta; + for (unsigned int i = 1; i < xs.size(); i += 2) { + //pbpair.second = -1; + x_index = i - 1; + v_index = i; + + // v' <- v + eta / 2 F(x) + Point z = F(v_index, xs_prev, t); + z = (eta / 2) * z; + xs[v_index] = xs[v_index] + z; + + // x <- x + eta v' + Point y = xs[v_index]; + + if (Ks[x_index] == NULL) { + xs[x_index] = xs_prev[x_index] + eta*y; + } + else { + // Find intersection (assuming a line trajectory) between x and y + bool step_completed = false; + NT T = eta; + if (k == 0 && !accepted) { + lambda_prev[x_index] = 0.0; + } + + pbpair = Ks[x_index]->line_positive_intersect(xs_prev[x_index], y); + if (T <= pbpair.first) { + xs[x_index] = xs_prev[x_index] + T * y; + xs[v_index] = y; + lambda_prev[x_index] = T; + step_completed = true; + } + + if (!step_completed) { + lambda_prev[x_index] = dl * pbpair.first; + xs_prev[x_index] = xs_prev[x_index] + lambda_prev[x_index] * y; + T -= lambda_prev[x_index]; + Ks[x_index]->compute_reflection(y, xs_prev[x_index], pbpair.second); + num_reflections++; + + while (true) + { + pbpair = Ks[x_index]->line_positive_intersect(xs_prev[x_index], y); + if (T <= pbpair.first) { + xs[x_index] = xs_prev[x_index] + T * y; + xs[v_index] = y; + lambda_prev[x_index] = T; + break; + } + lambda_prev[x_index] = dl * pbpair.first; + xs_prev[x_index] = xs_prev[x_index] + lambda_prev[x_index] * y; + T -= lambda_prev[x_index]; + Ks[x_index]->compute_reflection(y, xs_prev[x_index], pbpair.second); + num_reflections++; + } + } + } + + // tilde v <- v + eta / 2 F(tilde x) + z = F(v_index, xs, t); + z = (eta / 2) * z; + xs[v_index] = xs[v_index] + z; + + } + + } + + void print_state() { + for (int j = 0; j < xs.size(); j ++) { + for (unsigned int i = 0; i < xs[j].dimension(); i++) { + std::cout << xs[j][i] << " "; + } + } + std::cout << std::endl; + } + + void steps(int num_steps, bool accepted) { + for (int i = 0; i < num_steps; i++) step(i, accepted); + } + + Point get_state(int index) { + return xs[index]; + } + + void set_state(int index, Point p) { + xs[index] = p; + } + +}; + + +#endif diff --git a/src/volesti/include/ode_solvers/implicit_midpoint.hpp b/src/volesti/include/ode_solvers/implicit_midpoint.hpp new file mode 100644 index 00000000..786734d8 --- /dev/null +++ b/src/volesti/include/ode_solvers/implicit_midpoint.hpp @@ -0,0 +1,177 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" + +#ifndef IMPLICIT_MIDPOINT_HPP +#define IMPLICIT_MIDPOINT_HPP +#include "preprocess/crhmc/opts.h" +#include "random_walks/crhmc/hamiltonian.hpp" +#ifdef TIME_KEEPING +#include +#endif + +template +inline std::vector operator+(const std::vector &v1, + const std::vector &v2) +{ + std::vector result(v1.size()); + for (int i = 0; i < v1.size(); i++) { + result[i] = v1[i] + v2[i]; + } + return result; +} +template +inline std::vector operator*(const std::vector &v, const Type alpha) +{ + std::vector result(v.size()); + for (int i = 0; i < v.size(); i++) { + result[i] = v[i] * alpha; + } + return result; +} +template +inline std::vector operator/(const std::vector &v, const Type alpha) +{ + return v * (1 / alpha); +} +template +inline std::vector operator-(const std::vector &v1, + const std::vector &v2) +{ + + return v1 + v2 * (-1.0); +} +template +struct ImplicitMidpointODESolver { + using VT = typename Polytope::VT; + using MT = typename Polytope::MT; + using pts = std::vector; + using hamiltonian = Hamiltonian; + using Opts = opts; + + unsigned int dim; + NT eta; + int num_steps = 0; + NT t; + + // Contains the sub-states + pts xs; + pts xs_prev; + + // Function oracle + func F; + Polytope &P; + Opts &options; + MT nu; + int num_runs = 0; + hamiltonian ham; + bool done; +#ifdef TIME_KEEPING + std::chrono::time_point start, end; + std::chrono::duration DU_duration = std::chrono::duration::zero(); + std::chrono::duration approxDK_duration = std::chrono::duration::zero(); +#endif + ImplicitMidpointODESolver(NT initial_time, + NT step, + pts initial_state, + func oracle, + Polytope &boundaries, + Opts &user_options) : + eta(step), + t(initial_time), + xs(initial_state), + F(oracle), + P(boundaries), + options(user_options), + ham(hamiltonian(boundaries)) + { + dim = xs[0].rows(); + }; + + void step(int k, bool accepted) { + num_runs++; + pts partialDerivatives; +#ifdef TIME_KEEPING + start = std::chrono::system_clock::now(); +#endif + partialDerivatives = ham.DU(xs); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + DU_duration += end - start; +#endif + xs = xs + partialDerivatives * (eta / 2); + xs_prev = xs; + done = false; + nu = MT::Zero(P.equations(), simdLen); + for (int i = 0; i < options.maxODEStep; i++) { + pts xs_old = xs; + pts xmid = (xs_prev + xs) / 2.0; +#ifdef TIME_KEEPING + start = std::chrono::system_clock::now(); +#endif + partialDerivatives = ham.approxDK(xmid, nu); + +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + approxDK_duration += end - start; +#endif + xs = xs_prev + partialDerivatives * (eta); + VT dist = ham.x_norm(xmid, xs - xs_old) / eta; + NT maxdist = dist.maxCoeff(); + //If the estimate does not change terminate + if (maxdist < options.implicitTol) { + done = true; + num_steps = i; + break; + //If the estimate is very bad, sample another velocity + } else if (maxdist > options.convergence_bound) { + xs = xs * std::nan("1"); + done = true; + num_steps = i; + break; + } + } +#ifdef TIME_KEEPING + start = std::chrono::system_clock::now(); +#endif + partialDerivatives = ham.DU(xs); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + DU_duration += end - start; +#endif + xs = xs + partialDerivatives * (eta / 2); + ham.project(xs); + } + + void steps(int num_steps, bool accepted) { + for (int i = 0; i < num_steps; i++) { + step(i, accepted); + } + } + + MT get_state(int index) { return xs[index]; } + + void set_state(int index, MT p) { xs[index] = p; } + template + void print_state(StreamType &stream) { + for (int j = 0; j < xs.size(); j++) { + stream << "state " << j << ": \n"; + stream << xs[j]; + stream << '\n'; + } + } +}; + +#endif diff --git a/src/volesti/include/ode_solvers/integral_collocation.hpp b/src/volesti/include/ode_solvers/integral_collocation.hpp new file mode 100644 index 00000000..10db9be9 --- /dev/null +++ b/src/volesti/include/ode_solvers/integral_collocation.hpp @@ -0,0 +1,289 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// Refers to the integral collocation method with Lagrange Polynomials +// from Lee, Yin Tat, Zhao Song, and Santosh S. Vempala. +//"Algorithmic theory of ODEs and sampling from well-conditioned +// logconcave densities." arXiv preprint arXiv:1812.06243 (2018). + + +#ifndef ODE_SOLVERS_INTEGRAL_COLLOCATION_HPP +#define ODE_SOLVERS_INTEGRAL_COLLOCATION_HPP + +#include "nlp_oracles/nlp_hpolyoracles.hpp" +#include "nlp_oracles/nlp_vpolyoracles.hpp" +#include "boost/numeric/ublas/vector.hpp" +#include "boost/numeric/ublas/io.hpp" +#include "boost/math/special_functions/chebyshev.hpp" +#include "boost/math/special_functions/chebyshev_transform.hpp" + +template < + typename Point, + typename NT, + typename Polytope, + typename func +> +struct IntegralCollocationODESolver { + + + // Vectors of points + typedef std::vector pts; + typedef std::vector ptsv; + + // typedef from existing templates + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef std::vector MTs; + + typedef std::vector bounds; + typedef std::vector coeffs; + typedef boost::numeric::ublas::vector boost_vector; + typedef boost::math::chebyshev_transform chebyshev_transform_boost; + + unsigned int dim; + + NT eta; + NT t, t_prev, dt, temp_node, a, b; + const NT tol = 1e-6; + + // Function oracles x'(t) = F(x, t) + func F; + bounds Ks; + + // Contains the sub-states + pts xs, xs_prev, X_temp; + Point y; + + // Temporal coefficients + coeffs cs; + + VT Ar, Av, X_op, nodes; + + MT A_phi, X0, X, X_prev, F_op; + + unsigned int _order; + + LagrangePolynomial lagrange_poly; + + int prev_facet = -1; + Point prev_point; + + IntegralCollocationODESolver(NT initial_time, NT step, pts initial_state, + func oracle, bounds boundaries, unsigned int order_=4) : + t(initial_time), xs(initial_state), X_temp(initial_state), F(oracle), eta(step), Ks(boundaries), + _order(order_) { + dim = xs[0].dimension(); + initialize_matrices(); + }; + + unsigned int order() const { + return _order; + } + + void initialize_matrices() { + + A_phi.resize(order(), order()); + nodes.resize(order()); + + std::vector temp; + + for (unsigned int j = 0; j < order(); j++) { + nodes(j) = cos((j+0.5) * M_PI / order()); + } + + lagrange_poly.set_nodes(nodes); + + // Calculate integrals of basis functions based on the Discrete Chebyshev Transform + for (unsigned int i = 0; i < order(); i++) { + + lagrange_poly.set_basis((int) i); + + for (unsigned int j = 0; j <= i; j++) { + if (nodes(j) < NT(0)) { + a = nodes(j); + b = NT(0); + } else { + a = NT(0); + b = nodes(j); + } + + chebyshev_transform_boost transform(lagrange_poly, a, b); + A_phi(i, j) = NT(transform.integrate()); + A_phi(j, i) = A_phi(i, j); + } + } + + #ifdef VOLESTI_DEBUG + std::cout << "A_phi" << std::endl; + std::cout << A_phi << std::endl; + #endif + + X.resize(xs.size() * dim, order()); + X0.resize(xs.size() * dim, order()); + X_prev.resize(xs.size() * dim, order()); + X_op.resize(xs.size() * dim); + + F_op.resize(xs.size() * dim, order()); + + lagrange_poly.set_basis(-1); + } + + void initialize_fixed_point() { + for (unsigned int ord = 0; ord < order(); ord++) { + for (unsigned int i = 0; i < xs.size(); i++) { + for (unsigned int j = i * dim; j < (i + 1) * dim; j++) { + X0(j, ord) = xs_prev[i][j % dim]; + } + } + } + } + + void step() { + xs_prev = xs; + initialize_fixed_point(); + + std::vector transforms; + + X = X0; + X_prev = 100 * X0; + NT err; + + do { + for (unsigned int ord = 0; ord < order(); ord++) { + for (unsigned int i = 0; i < xs.size(); i++) { + for (unsigned int j = i * dim; j < (i + 1) * dim; j++) { + X_temp[i].set_coord(j % dim, X(j, ord)); + } + } + + for (unsigned int i = 0; i < xs.size(); i++) { + // std::cout << "pre y" << std::endl; + temp_node = nodes(ord) * eta; + y = F(i, X_temp, temp_node); + + for (int j = i * dim; j < (i + 1) * dim; j++) { + F_op(j, ord) = y[j % dim]; + } + + } + } + + X = X0 + F_op * A_phi; + + X_prev = X; + + err = sqrt((X - X_prev).squaredNorm()); + + } while (err > 1e-10); + + X_op = X0.col(0); + + unsigned int max_transform_coeffs_length = 0; + + + for (unsigned int i = 0; i < xs.size(); i++) { + for (unsigned int j = i * dim; j < (i + 1) * dim; j++) { + lagrange_poly.set_coeffs(F_op.row(j).transpose()); + chebyshev_transform_boost transform(lagrange_poly, 0, eta, 1e-5, 5); + transforms.push_back(transform); + // Keep max transform length for zero-padding + if (max_transform_coeffs_length < transform.coefficients().size()) { + max_transform_coeffs_length = transform.coefficients().size(); + } + + X_op(j) += NT(transform.integrate()); + } + } + + + for (unsigned int i = 0; i < xs.size(); i++) { + if (Ks[i] == NULL) { + for (unsigned int j = i * dim; j < (i + 1) * dim; j++) { + xs[i].set_coord(j % dim, X_op(j)); + } + } + else { + throw true; + // TODO Implement oracle. It requires doing chebyshev transforms + // with the same #of points at each dimension + // 1. Temporarily store coefficients as points + // 2. Initialize everything to Points at origin (for zero-padding) + // pts temp_pts(max_transform_coeffs_length, Point(dim)); + + // 3. Store transformation to polynomial of twice the degree + // pts transformed_pts(2 * max_transform_coeffs_length, Point(dim)); + // std::vector temp_coeffs; + + // 4. Invoke chebyshev transform coefficients + // Precomputed from the final step of the fixed point iterator + // for (unsigned int j = i * dim; j < (i + 1) * dim; j++) { + // temp_coeffs = transforms[j].coefficients(); + // for (unsigned int k = 0; k < temp_coeffs.size(); k++) { + // temp_pts[k].set_coord(j % dim, temp_coeffs[k]); + // } + // } + + // 5. Apply degree-doubling transformation + // The transformation takes the n Chebyshev transform coefficients c[i] + // and creates a complex polynomial of order 2n with coefficients a[i] + // such that a[n] = 2 * c[0], a[i] = c[i - n] for i > n and a[i] = c[n - i] for i < n + // This polynomial h(z) is defined on the complex plane. + // Its roots are related to the chebyshev transform as: z is a root of + // the degree-doubled polynomial h(z) then Re(z) is a + // root for the chebyshev transfrom. This transformation is domain_name + // in order to be able to use MPSolve to compute the boundary oracle + // degree_doubling_chebyshev(temp_pts, transformed_pts); + + // 6. Integrate coefficients + // We want the integral of the function instead of the function itself. + // Integrating the polynomial is fairly easy. + // transformed_pts.insert(transformed_pts.begin(), Point(dim)); + + // for (unsigned int k = 1; k < transformed_pts.size(); k++) { + // transformed_pts[k] = (1.0 / k) * transformed_pts[k]; + // } + + // 7. Find roots using MPSolve + // Project the computed coefficients to each of the polytopes normals and + // calculate the complex roots of the resulting equations. + // Then keep the smallest positive rational part. + } + + } + + + + } + + + void print_state() { + for (int j = 0; j < xs.size(); j++) { + for (unsigned int i = 0; i < xs[j].dimension(); i++) { + std::cout << xs[j][i] << " "; + } + } + std::cout << std::endl; + } + + void steps(int num_steps) { + for (int i = 0; i < num_steps; i++) step(); + } + + Point get_state(int index) { + return xs[index]; + } + + void set_state(int index, Point p) { + xs[index] = p; + } +}; + + +#endif diff --git a/src/volesti/include/ode_solvers/leapfrog.hpp b/src/volesti/include/ode_solvers/leapfrog.hpp new file mode 100644 index 00000000..b7f2724b --- /dev/null +++ b/src/volesti/include/ode_solvers/leapfrog.hpp @@ -0,0 +1,221 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_LEAPFROG_HPP +#define ODE_SOLVERS_LEAPFROG_HPP + +template < +typename Point, +typename NT, +typename Polytope, +typename func +> +struct LeapfrogODESolver { + + struct update_parameters +{ + update_parameters() + : facet_prev(0), hit_ball(false), inner_vi_ak(0.0), ball_inner_norm(0.0) + {} + int facet_prev; + bool hit_ball; + double inner_vi_ak; + double ball_inner_norm; +}; + + update_parameters _update_parameters; + + typedef std::vector pts; + + typedef Eigen::Matrix MT; + + typedef std::vector bounds; + typedef typename Polytope::VT VT; + + unsigned int dim; + + std::vector Ar, Av; + std::vector lambda_prev; + + NT eta; + NT eta0; + NT t; + NT dl = 0.995; + + func F; + bounds Ks; + + // Contains the sub-states + pts xs; + pts xs_prev; + + Point grad_x; + + MT _AA; + + std::pair pbpair; + + unsigned long long num_reflections = 0; + unsigned long long num_steps = 0; + + bool adaptive = true; + + LeapfrogODESolver(NT initial_time, NT step, pts initial_state, func oracle, bounds boundaries, bool adaptive_=true) : + eta(step), eta0(step), t(initial_time), F(oracle), Ks(boundaries), xs(initial_state), adaptive(adaptive_) { + dim = xs[0].dimension(); + _update_parameters = update_parameters(); + grad_x.set_dimension(dim); + initialize(); + }; + + + void initialize() { + for (unsigned int i = 0; i < xs.size(); i++) { + VT ar, av; + if (Ks[i] != NULL) { + ar.setZero(Ks[i]->num_of_hyperplanes()); + ar = Ks[i]->get_mat() * xs[i].getCoefficients(); + av.setZero(Ks[i]->num_of_hyperplanes()); + _AA.noalias() = Ks[i]->get_mat() * Ks[i]->get_mat().transpose(); + Ks[i]->resetFlags(); + } + Ar.push_back(ar); + Av.push_back(av); + lambda_prev.push_back(NT(0)); + } + } + + void disable_adaptive() { + adaptive = false; + } + + void enable_adaptive() { + adaptive = true; + } + + void step(int k, bool accepted) { + num_steps++; + if (adaptive) eta = (eta0 * num_steps) / (num_steps + num_reflections); + + xs_prev = xs; + unsigned int x_index, v_index, it; + t += eta; + Point y; + for (unsigned int i = 1; i < xs.size(); i += 2) { + + x_index = i - 1; + v_index = i; + + // v' <- v + eta / 2 F(x) + if (k == 0 && !accepted) { + grad_x = F(v_index, xs_prev, t); + } + xs[v_index] += (eta / 2) * grad_x; + + // x <- x + eta v' + y = xs[v_index]; + + if (Ks[x_index] == NULL) { + xs[x_index] += eta*y; + } + else { + // Find intersection (assuming a line trajectory) between x and y + bool step_completed = false; + NT T = eta; + if (k == 0 && !accepted) { + Ar[x_index] = Ks[x_index]->get_mat() * xs_prev[x_index].getCoefficients(); + lambda_prev[x_index] = 0.0; + Ks[x_index]->resetFlags(); + } + + pbpair = Ks[x_index]->line_positive_intersect(xs_prev[x_index], y, Ar[x_index], Av[x_index], + lambda_prev[x_index], _update_parameters); + if (T <= pbpair.first) { + xs[x_index] = xs_prev[x_index] + T * y; + xs[v_index] = y; + lambda_prev[x_index] = T; + Ks[x_index]->update_position_internal(T); + step_completed = true; + } + + if (!step_completed) { + lambda_prev[x_index] = dl * pbpair.first; + xs_prev[x_index] = xs_prev[x_index] + lambda_prev[x_index] * y; + T -= lambda_prev[x_index]; + Ks[x_index]->update_position_internal(lambda_prev[x_index]); + Ks[x_index]->compute_reflection(y, xs_prev[x_index], _update_parameters); + num_reflections++; + + while (true) + { + pbpair = Ks[x_index]->line_positive_intersect(xs_prev[x_index], y, Ar[x_index], Av[x_index], + lambda_prev[x_index], _AA, _update_parameters); + if (T <= pbpair.first) { + xs[x_index] = xs_prev[x_index] + T * y; + xs[v_index] = y; + lambda_prev[x_index] = T; + Ks[x_index]->update_position_internal(T); + break; + } + lambda_prev[x_index] = dl * pbpair.first; + xs_prev[x_index] = xs_prev[x_index] + lambda_prev[x_index] * y; + T -= lambda_prev[x_index]; + Ks[x_index]->update_position_internal(lambda_prev[x_index]); + Ks[x_index]->compute_reflection(y, xs_prev[x_index], _update_parameters); + num_reflections++; + } + } + } + + // tilde v <- v + eta / 2 F(tilde x) + grad_x = F(v_index, xs, t); + xs[v_index] += (eta / 2) * grad_x; + } + + } + + void print_state() { + for (int j = 0; j < xs.size(); j ++) { + for (unsigned int i = 0; i < xs[j].dimension(); i++) { + std::cout << xs[j][i] << " "; + } + } + std::cout << std::endl; + } + + void steps(int num_steps, bool accepted) { + for (int i = 0; i < num_steps; i++) step(i, accepted); + } + + Point get_state(int index) { + return xs[index]; + } + + void set_state(int index, Point p) { + xs[index] = p; + } + + NT get_eta() { + return eta; + } + + void set_eta(NT &eta_) { + eta = eta_; + eta0 = eta_; + } + + bounds get_bounds() { + return Ks; + } + +}; + + +#endif diff --git a/src/volesti/include/ode_solvers/ode_solvers.hpp b/src/volesti/include/ode_solvers/ode_solvers.hpp new file mode 100644 index 00000000..68e730fc --- /dev/null +++ b/src/volesti/include/ode_solvers/ode_solvers.hpp @@ -0,0 +1,64 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +/* + Each solver solves an ODE of the form d^k x/dt^k = F(x, t) + The vector x can be written as x = (x_1, ...., x_n) where each + x_i is conditioned on a convex polytope K_i with bounary reflections + on the boundary of K_i for each 1 <= i <= n. Each sub-state is determined + by the oracle function F_i(x, t) which has range over x = (x_1, ..., x_n). + + Some general parameter notations for the solvers + + Templates + 1. Polytope: The polytope type (H-Polytope, V-Polytope, Z-Polytope) K_i + 2. NT: Number type (double, float) + 3. Point: Point type + 4. func: Function type for the oracle collection K_i + 5. bfunc: Basis function type (e.g. polynomial) for non-linear trajectory + methods (such as collocation) + + Variables + 1. Ks: A vector of domains K_i for 1 <= i <= n + 2. xs: A vector of substates x_i for 1 <= i <= n + 3. xs_prev: The previous state of the solver + 4. F: A functor of oracles F_i for 1 <= i <= n + 5. eta: Step size + 6. t: Temporal variable + + TODO: + 1. Change datastructure of boundaries (std::vector) + +*/ + +#include +#include +#include +#include + +#include "ode_solvers/euler.hpp" +#include "ode_solvers/implicit_midpoint.hpp" +#include "ode_solvers/runge_kutta.hpp" +#include "ode_solvers/leapfrog.hpp" +#include "ode_solvers/richardson_extrapolation.hpp" +#include "ode_solvers/oracle_functors.hpp" +#include "ode_solvers/randomized_midpoint.hpp" +#include "ode_solvers/generalized_leapfrog.hpp" + +#ifndef DISABLE_NLP_ORACLES +#include "ode_solvers/collocation.hpp" +#include "ode_solvers/basis.hpp" +#include "ode_solvers/integral_collocation.hpp" +#endif + +#ifndef ODE_SOLVERS_ODE_SOLVERS_HPP +#define ODE_SOLVERS_ODE_SOLVERS_HPP + +#endif diff --git a/src/volesti/include/ode_solvers/oracle_functors.hpp b/src/volesti/include/ode_solvers/oracle_functors.hpp new file mode 100644 index 00000000..35fc8887 --- /dev/null +++ b/src/volesti/include/ode_solvers/oracle_functors.hpp @@ -0,0 +1,397 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_ORACLE_FUNCTORS_HPP +#define ODE_SOLVERS_ORACLE_FUNCTORS_HPP + +struct OptimizationFunctor { + template < + typename NT, + typename Functor, + typename GradFunctor + > + struct parameters { + NT T; // Temperature + unsigned int dim; // Dimension + Functor f; + GradFunctor neg_grad_f; + NT L; + NT m; + NT kappa; + unsigned int order; + + parameters( + NT T_, + unsigned int dim_, + Functor f_, + GradFunctor neg_grad_f_) : + T(T_), + dim(dim_), + f(f_), + neg_grad_f(neg_grad_f_), + L(1), + m(1), + kappa(1), + order(2) + {}; + + void update_temperature(NT k, NT l) { + T = T * pow(1.0 + 1.0 / pow(dim, k), l); + } + }; + + template + < + typename Point, + typename Functor, + typename GradFunctor + > + struct GradientFunctor { + typedef typename Point::FT NT; + typedef std::vector pts; + + parameters ¶ms; + + GradientFunctor(parameters ¶ms_) : params(params_) {}; + + // The index i represents the state vector index + Point operator() (unsigned int const& i, pts const& xs, NT const& t) const { + if (i == params.order - 1) { + return params.neg_grad_f(i, xs, t) * params.T; // returns - a*x + } else { + return xs[i + 1]; // returns derivative + } + } + }; + + template + < + typename Point, + typename Functor, + typename GradFunctor + > + struct FunctionFunctor { + typedef typename Point::FT NT; + parameters ¶ms; + + FunctionFunctor(parameters ¶ms_) : params(params_) {}; + + NT operator() (Point const& x) const { + return params.f(x) * params.T; + } + }; +}; + +struct IsotropicQuadraticFunctor { + + // Holds function oracle and gradient oracle for the function 1/2 a ||x||^2 + template < + typename NT + > + struct parameters { + NT alpha; + unsigned int order; + NT L; // Lipschitz constant of gradient + NT m; // Strong-convexity parameter + NT kappa; // Condition number + + parameters() : + alpha(NT(1)), + order(2), + L(NT(1)), + m(NT(1)), + kappa(1) + {}; + + parameters( + NT alpha_, + unsigned int order_) : + alpha(alpha_), + order(order_), + L(alpha_), + m(alpha_), + kappa(1) + {}; + }; + + + template + < + typename Point + > + struct GradientFunctor { + typedef typename Point::FT NT; + typedef std::vector pts; + + parameters ¶ms; + + GradientFunctor(parameters ¶ms_) : params(params_) {}; + + // The index i represents the state vector index + Point operator() (unsigned int const& i, pts const& xs, NT const& t) const { + if (i == params.order - 1) { + return (-params.alpha) * xs[0]; // returns - a*x + } else { + return xs[i + 1]; // returns derivative + } + } + + Point operator()(Point const &x){ + Point y = (-params.alpha) * x; + return y; + } + }; + + + template + < + typename Point + > + struct FunctionFunctor { + typedef typename Point::FT NT; + + parameters ¶ms; + + FunctionFunctor(parameters ¶ms_) : params(params_) {}; + + NT operator() (Point const& x) const { + return 0.5 * params.alpha * x.dot(x); + } + }; + +}; + +struct IsotropicLinearFunctor { + + // Exponential Density + template < + typename NT + > + struct parameters { + NT alpha; + unsigned int order; + NT L; // Lipschitz constant of gradient + NT m; // Strong-convexity constant + NT kappa; // Condition number + + parameters() : + alpha(NT(1)), + order(1), + L(0), + m(0), + kappa(1) + {}; + + parameters(NT alpha_, unsigned int order_) : + alpha(alpha_), + order(order), + L(0), + m(0), + kappa(1) + {} + }; + + template + < + typename Point + > + struct GradientFunctor { + typedef typename Point::FT NT; + typedef std::vector pts; + + parameters ¶ms; + + GradientFunctor(parameters ¶ms_) : params(params_) {}; + + // The index i represents the state vector index + Point operator() (unsigned int const& i, pts const& xs, NT const& t) const { + if (i == params.order - 1) { + Point y = Point::all_ones(xs[0].dimension()); + y = (- params.alpha) * y; + return y; + } else { + return xs[i + 1]; // returns derivative + } + } + + }; + + template + < + typename Point + > + struct FunctionFunctor { + typedef typename Point::FT NT; + + parameters ¶ms; + + FunctionFunctor(parameters ¶ms_) : params(params_) {}; + + NT operator() (Point const& x) const { + return params.alpha * x.sum(); + } + + }; + +}; + + +struct ExponentialFunctor { + + // Sample from linear program c^T x (exponential density) + template < + typename NT, + typename Point + > + struct parameters { + unsigned int order; + NT L; // Lipschitz constant for gradient + NT m; // Strong convexity constant + NT kappa; // Condition number + Point c; // Coefficients of LP objective + NT a; // Inverse variance + + parameters(Point c_) : order(2), L(1), m(1), kappa(1), c(c_), a(1.0) {}; + parameters(Point c_, NT a_) : order(2), L(1), m(1), kappa(1), c(c_), a(a_) {}; + + }; + + template + < + typename Point + > + struct GradientFunctor { + typedef typename Point::FT NT; + typedef std::vector pts; + + parameters ¶ms; + + GradientFunctor(parameters ¶ms_) : params(params_) {}; + + // The index i represents the state vector index + Point operator() (unsigned int const& i, pts const& xs, NT const& t) const { + if (i == params.order - 1) { + Point y(params.c); + return (-params.a) * y; + } else { + return xs[i + 1]; // returns derivative + } + } + + }; + + template + < + typename Point + > + struct FunctionFunctor { + typedef typename Point::FT NT; + + parameters ¶ms; + + FunctionFunctor(parameters ¶ms_) : params(params_) {}; + + // The index i represents the state vector index + NT operator() (Point const& x) const { + return params.a * x.dot(params.c); + } + + }; + +}; + + +struct GaussianFunctor { + + template < + typename NT, + typename Point + > + struct parameters { + Point x0; + NT a; + NT eta; + unsigned int order; + NT L; // Lipschitz constant for gradient + NT m; // Strong convexity constant + NT kappa; // Condition number + + parameters(Point x0_, NT a_, NT eta_) : + x0(x0_), a(a_), eta(eta_), order(2), L(2 * a_), m(2 * a_), kappa(1) {}; + + }; + + template + < + typename Point + > + struct GradientFunctor { + typedef typename Point::FT NT; + typedef std::vector pts; + + parameters ¶ms; + + GradientFunctor(parameters ¶ms_) : params(params_) {}; + + // The index i represents the state vector index + Point operator() (unsigned int const& i, pts const& xs, NT const& t) const { + if (i == params.order - 1) { + Point y = (-2.0 * params.a) * (xs[0] - params.x0); + return y; + } else { + return xs[i + 1]; // returns derivative + } + } + Point operator()(Point const&x){ + Point y = (-2.0 * params.a) * (x - params.x0); + return y; + } + }; + + template + < + typename Point + > + struct FunctionFunctor { + typedef typename Point::FT NT; + + parameters ¶ms; + + FunctionFunctor(parameters ¶ms_) : params(params_) {}; + + // The index i represents the state vector index + NT operator() (Point const& x) const { + Point y = x - params.x0; + return params.a * y.dot(y); + } + + }; + + template +< + typename Point +> +struct HessianFunctor { + typedef typename Point::FT NT; + + parameters ¶ms; + + HessianFunctor(parameters ¶ms_) : params(params_) {}; + + // The index i represents the state vector index + Point operator() (Point const& x) const { + return (2.0 * params.a) * Point::all_ones(x.dimension()); + } + +}; + +}; + +#endif diff --git a/src/volesti/include/ode_solvers/oracle_functors_rcpp.hpp b/src/volesti/include/ode_solvers/oracle_functors_rcpp.hpp new file mode 100644 index 00000000..853dcb3c --- /dev/null +++ b/src/volesti/include/ode_solvers/oracle_functors_rcpp.hpp @@ -0,0 +1,159 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_ORACLE_FUNCTORS_RCPP_HPP +#define ODE_SOLVERS_ORACLE_FUNCTORS_RCPP_HPP + +enum ode_solvers { + no_solver, + leapfrog, + euler, + runge_kutta, + richardson, + collocation +}; + +// Holds Oracle Functor that wraps an R function via RCpp +// The R function is provided as an Rcpp::Function object +// The functor uses Rcpp::as and Rcpp::wrap to do the conversion, +// call the oracle, and convert the results back to C++ +struct RcppFunctor { + + template < + typename NT + > + struct parameters { + NT L; // Lipschitz constant of gradient + NT m; // Strong-convexity parameter + NT eta; // Step-size (if defined by user) + NT kappa; // Condition number + unsigned int order; // Order of ODE functor + + parameters( + NT L_, + NT m_, + NT eta_, + unsigned int order_=2 + ) : + L(L_), + m(m_), + eta(eta_), + kappa(L_ / m_), + order(order_) + {} + }; + + // Log-probability gradient functor + template + < + typename Point + > + struct GradientFunctor { + typedef typename Point::FT NT; + typedef typename Point::Coeff VT; + typedef std::vector pts; + + parameters params; + Rcpp::Function neg_grad_f; // Negative gradient as an Rcpp::Function + bool negate; + + GradientFunctor( + parameters params_, + Rcpp::Function neg_grad_f_, + bool negate_=true): + params(params_), + neg_grad_f(neg_grad_f_), + negate(negate_) + {}; + + // The index i represents the state vector index + Point operator() (unsigned int const& i, pts const& xs, NT const& t) const { + if (i == params.order - 1) { + // Convert point to Rcpp::NumericMatrix + + VT y = Rcpp::as(neg_grad_f(Rcpp::wrap(xs[0].getCoefficients()))); + + Point z(y); + + if (negate) z = (-1.0) * z; + + // Return result as Point + return z; + } else { + return xs[i + 1]; // returns derivative + } + } + + Point operator() (Point const& x) const { + VT y = Rcpp::as(neg_grad_f(Rcpp::wrap(x.getCoefficients()))); + + Point z(y); + + if (negate) z = (-1.0) * z; + + // Return result as Point + return z; + } + + }; + + // Negative log-probability functor + template + < + typename Point + > + struct FunctionFunctor { + typedef typename Point::FT NT; + typedef typename Point::Coeff VT; + + parameters params; + Rcpp::Function negative_logprob; + + FunctionFunctor( + parameters params_, + Rcpp::Function negative_logprob_) : + params(params_), + negative_logprob(negative_logprob_) + {}; + + NT operator() (Point const& x) const { + return Rcpp::as(negative_logprob(Rcpp::wrap(x.getCoefficients()))); + } + + }; + + // Log-probability hessian functor + template + < + typename Point + > + struct HessianFunctor { + typedef typename Point::FT NT; + typedef typename Point::Coeff VT; + + parameters params; + Rcpp::Function hessian; // Negative hessian as an Rcpp::Function + + HessianFunctor( + parameters params_, + Rcpp::Function hessian_) : + params(params_), + hessian(hessian_) + {}; + + Point operator() (Point const& x) const { + VT y= Rcpp::as(hessian(Rcpp::wrap(x.getCoefficients()))); + return Point(y); + } + + }; +}; + +#endif diff --git a/src/volesti/include/ode_solvers/randomized_midpoint.hpp b/src/volesti/include/ode_solvers/randomized_midpoint.hpp new file mode 100644 index 00000000..d2a58e3c --- /dev/null +++ b/src/volesti/include/ode_solvers/randomized_midpoint.hpp @@ -0,0 +1,226 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// Based on https://papers.nips.cc/paper/8483-the-randomized-midpoint-method-for-log-concave-sampling.pdf + +#ifndef ODE_SOLVERS_RANDOMIZED_MIDPOINT_HPP +#define ODE_SOLVERS_RANDOMIZED_MIDPOINT_HPP + +template < +typename Point, +typename NT, +typename Polytope, +typename func, +typename RandomNumberGenerator +> +struct RandomizedMipointSDESolver { + + typedef std::vector pts; + + typedef std::vector bounds; + typedef typename Polytope::VT VT; + typedef typename Polytope::MT MT; + + unsigned int dim; + + VT Ar, Av; + + NT eta; + NT t; + NT u; + + func F; + bounds Ks; + + // Contains the sub-states + pts xs; + pts xs_prev; + Point y, w, z; + + Point W1, W2, W3; + VT Y, Z; + MT C, L, D, M; + + std::pair pbpair; + + RandomizedMipointSDESolver(NT initial_time, NT step, pts initial_state, func oracle, bounds boundaries, NT u_=NT(1)) : + eta(step), t(initial_time), u(u_), F(oracle), Ks(boundaries), xs(initial_state) { + dim = xs[0].dimension(); + Y.resize(2 * dim); + Z.resize(2 * dim); + C.resize(2 * dim, 2 * dim); + L.resize(2 * dim, 2 * dim); + D.resize(2 * dim, 2 * dim); + M.resize(2 * dim, 2 * dim); + W1 = Point(dim); + W2 = Point(dim); + W3 = Point(dim); + + for (unsigned int i = 0; i < 2 * dim; i++) { + Y(i) = NT(0); + Z(i) = NT(0); + for (unsigned int j = 0; j < 2 * dim; j++) { + C(i, j) = NT(0); + D(i, j) = NT(0); + } + } + }; + + void step(RandomNumberGenerator &rng) { + xs_prev = xs; + unsigned int x_index, v_index, it; + NT a; + t += eta; + for (unsigned int i = 1; i < xs.size(); i += 2) { + + a = rng.sample_urdist(); + + x_index = i - 1; + v_index = i; + + calculate_Ws(a, rng); + + z = xs_prev[x_index]; + z = z + (0.5 * (1 - exp(- 2 * a * eta))) * xs_prev[v_index]; + z = z - (0.5 * u * (a * eta - 0.5 * (1 - exp(-2 * a * eta)))) * F(v_index, xs_prev, t); + z = z + sqrt(u) * W1; + + w = xs_prev[x_index]; + xs[x_index] = xs_prev[x_index]; + y = (-1.0) * xs_prev[x_index]; + xs_prev[x_index] = z; + xs[x_index] = xs[x_index] + (0.5 * (1 - exp(-2 * eta))) * xs[v_index]; + xs[x_index] = xs[x_index] + (0.5 * u * eta * (1 - exp(-2 * (eta - a * eta)))) * F(v_index, xs_prev, t); + xs[x_index] = xs[x_index] + sqrt(u) * W2; + + xs[v_index] = exp(- 2 * eta) * xs_prev[v_index]; + xs[v_index] = xs[v_index] + u * eta * exp(-2 * (eta - a * eta)) * F(v_index, xs_prev, t); + xs[v_index] = xs[v_index] + (2 * sqrt(u)) * W3; + + xs_prev[x_index] = w; + + y = y + xs[x_index]; + + if (Ks[x_index] == NULL) { + xs[x_index] = xs_prev[x_index] + y; + } + else { + // Find intersection (assuming a line trajectory) between x and y + do { + + pbpair = Ks[x_index]->line_positive_intersect(xs_prev[x_index], y, Ar, Av); + + if (pbpair.first >= 0 && pbpair.first <= 1) { + xs_prev[x_index] += (pbpair.first * 0.95) * y; + Ks[x_index]->compute_reflection(y, xs_prev[x_index], pbpair.second); + xs[x_index] = xs_prev[x_index] + y; + + // Reflect velocity + Ks[x_index]->compute_reflection(xs[v_index], xs[x_index], pbpair.second); + } + else { + xs[x_index] = xs_prev[x_index] + y; + } + } while (!Ks[x_index]->is_in(xs[x_index])); + } + + } + } + + void print_state() { + for (int j = 0; j < xs.size(); j ++) { + for (unsigned int i = 0; i < xs[j].dimension(); i++) { + std::cout << xs[j][i] << " "; + } + } + std::cout << std::endl; + } + + void steps(int num_steps, RandomNumberGenerator &rng) { + for (int i = 0; i < num_steps; i++) step(rng); + } + + Point get_state(int index) { + return xs[index]; + } + + void set_state(int index, Point p) { + xs[index] = p; + } + + void calculate_Ws(NT &a, RandomNumberGenerator &rng) { + // Initialize matrices to zero + Y = 0 * Y; + Z = 0 * Z; + C = 0 * C; + D = 0 * D; + + // Helper variables + NT temp_y, temp_z, h1, h2, g1, g2; + + // Variance of variable G1, G2 + temp_y = 0.25 * (exp(4 * a * eta) - 1); + temp_z = 0.25 * (exp(4 * eta) - exp(4 * a * eta)); + for (unsigned int i = 0; i < dim; i++) { + C(i, i) = temp_y; + D(i, i) = temp_z; + } + + // Variance of H1, H2 + temp_y = a * eta; + temp_z = (eta - a * eta); + for (unsigned int i = dim; i < 2 * dim; i++) { + C(i, i) = temp_y; + D(i, i) = temp_z; + } + + // Covariances of Hi, Gi + temp_y = 0.5 * (exp(2 * a * eta) - 1); + temp_z = 0.5 * (exp(2 * eta) - exp(2 * a * eta)); + for (unsigned int i = 0; i < dim; i++) { + C(i, i + dim) = temp_y; + C(i + dim, i) = temp_y; + D(i, i + dim) = temp_z; + D(i + dim, i) = temp_z; + } + + // Cholesky Decomposition + Eigen::LLT lltofC(C); + L = lltofC.matrixL(); + Eigen::LLT lltofD(D); + M = lltofD.matrixL(); + + // Normal Vectors + for (unsigned int i = 0; i < 2 * dim; i++) { + Y(i) = rng.sample_ndist(); + Z(i) = rng.sample_ndist(); + } + + // Transformed vectors + Y = L * Y; + Z = M * Z; + + // Calculate Brownian Integrals W1, W2, W3 (Appendix A) + for (int i = 0; i < dim; i++) { + h1 = Y(i + dim); + g1 = Y(i); + h2 = Z(i + dim); + g2 = Z(i); + W1.set_coord(i, h1 - exp(- 2 * a * eta) * g1); + W2.set_coord(i, h1 + h2 - exp(- 2 * a * eta) * (g1 + g2)); + W3.set_coord(i, exp(- 2 * eta) * (g1 + g2)); + } + + } + +}; + + +#endif diff --git a/src/volesti/include/ode_solvers/richardson_extrapolation.hpp b/src/volesti/include/ode_solvers/richardson_extrapolation.hpp new file mode 100644 index 00000000..5d6eb887 --- /dev/null +++ b/src/volesti/include/ode_solvers/richardson_extrapolation.hpp @@ -0,0 +1,188 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_RICHARDSON_EXTRAPOLATION_HPP +#define ODE_SOLVERS_RICHARDSON_EXTRAPOLATION_HPP + + +template < + typename Point, + typename NT, + typename Polytope, + typename func +> +struct RichardsonExtrapolationODESolver { + + typedef std::vector pts; + + typedef std::vector bounds; + typedef std::vector coeffs; + typedef std::vector scoeffs; + typedef std::vector ptsv; + typedef std::vector ptsm; + + typedef typename Polytope::VT VT; + + unsigned int dim; + const unsigned int MAX_TRIES = 5; + + NT eta, eta_temp; + NT t, t_prev; + NT tol = 1e-7; + NT error = NT(-1); + NT den; + Point num, y; + VT Ar, Av; + + RKODESolver *solver; + + func F; + bounds Ks; + + // Contains the sub-states + pts xs; + pts xs_prev; + + ptsm A; + bool flag; + + // Previous state boundary point + Point x_prev_bound; + + // Previous state boundary facet + int prev_facet = -1; + + RichardsonExtrapolationODESolver(NT initial_time, NT step, pts initial_state, + func oracle, bounds boundaries) : + eta(step), t(initial_time), F(oracle), Ks(boundaries), xs(initial_state) { + dim = xs[0].dimension(); + A = ptsm(MAX_TRIES+1, ptsv(MAX_TRIES+1, pts(xs.size()))); + initialize_solver(); + }; + + void initialize_solver() { + solver = new RKODESolver(t, eta, xs, F, bounds{NULL}); + } + + void step(int k, bool accepted) { + xs_prev = xs; + eta_temp = eta; + flag = true; + + // Use RK4 solver + solver->xs = xs_prev; + solver->t = t; + solver->eta = eta_temp; + solver->steps(1, false); + A[1][1] = solver->xs; + + + for (unsigned int j = 1; j <= MAX_TRIES-1; j++) { + // Reduce step size by two + eta_temp /= 2; + + // Find solution with half stepsize and twice the num of steps + solver->xs = xs_prev; + solver->t = t; + solver->eta = eta_temp; + solver->steps(2*j, false); + A[j+1][1] = solver->xs; + + // Perform Richardson extrapolation + for (unsigned int k = 1; k <= j; k++) { + den = 1.0 * ((4 << k) - 1); + for (unsigned int i = 0; i < xs.size(); i++) { + num = (1.0 * (4 << k)) * A[j+1][k][i]; + num = num - A[j][k][i]; + A[j+1][k+1][i] = (1 / den) * num; + } + } + + error = NT(-1); + + for (unsigned int i = 0; i < xs.size(); i++) { + y = A[j+1][j+1][i] - A[j][j][i]; + if (sqrt(y.dot(y)) > error) error = sqrt(y.dot(y)); + } + + if (error < tol || j == MAX_TRIES - 1) { + for (unsigned int i = 0; i < xs.size(); i++) { + y = A[j+1][j+1][i] - xs[i]; + + if (Ks[i] == NULL) { + xs[i] = xs_prev[i] + y; + if (prev_facet != -1 && i > 0) { + Ks[i-1]->compute_reflection(xs[i], x_prev_bound, prev_facet); + } + prev_facet = -1; + } + else { + + // Find intersection (assuming a line trajectory) between x and y + do { + // Find line intersection between xs[i] (new position) and y + std::pair pbpair = Ks[i]->line_positive_intersect(xs_prev[i], y, Ar, Av); + // If point is outside it would yield a negative param + if (pbpair.first >= 0 && pbpair.first <= 1) { + + xs_prev[i] += (pbpair.first * 0.95) * y; + + // Update facet for reflection of derivative + prev_facet = pbpair.second; + x_prev_bound = xs_prev[i]; + + // Reflect ray y on the boundary point y now is the reflected ray + Ks[i]->compute_reflection(y, xs_prev[i], pbpair.second); + // Add it to the existing (boundary) point and repeat + xs[i] = xs_prev[i] + y; + + } + else { + prev_facet = -1; + xs[i] = xs_prev[i] + y; + } + } while (!Ks[i]->is_in(xs[i])); + + } + + } + break; + } + } + + t += eta; + + } + + void print_state() { + for (int j = 0; j < xs.size(); j++) { + for (unsigned int i = 0; i < xs[j].dimension(); i++) { + std::cout << xs[j][i] << " "; + } + } + std::cout << std::endl; + } + + void steps(int num_steps, bool accepted) { + for (int i = 0; i < num_steps; i++) step(i, accepted); + } + + Point get_state(int index) { + return xs[index]; + } + + void set_state(int index, Point p) { + xs[index] = p; + } + +}; + + +#endif diff --git a/src/volesti/include/ode_solvers/runge_kutta.hpp b/src/volesti/include/ode_solvers/runge_kutta.hpp new file mode 100644 index 00000000..5d8793ca --- /dev/null +++ b/src/volesti/include/ode_solvers/runge_kutta.hpp @@ -0,0 +1,172 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ODE_SOLVERS_RUNGE_KUTTA_H +#define ODE_SOLVERS_RUNGE_KUTTA_H + + +template < + typename Point, + typename NT, + typename Polytope, + typename func +> +struct RKODESolver { + + typedef std::vector pts; + typedef std::vector ptsv; + + typedef std::vector bounds; + typedef std::vector coeffs; + typedef std::vector scoeffs; + + typedef typename Polytope::VT VT; + + unsigned int dim; + + NT eta; + NT t, t_prev; + + VT Ar, Av; + + func F; + bounds Ks; + + // Contains the sub-states + pts xs; + ptsv ks; + Point y; + + // Previous state boundary point + Point x_prev_bound; + + // Previous state boundary facet + int prev_facet = -1; + + scoeffs as; + coeffs cs, bs; + + + RKODESolver(NT initial_time, NT step, pts initial_state, func oracle, + bounds boundaries) : + eta(step), t(initial_time), F(oracle), Ks(boundaries), xs(initial_state) { + dim = xs[0].dimension(); + // If no coefficients are given the RK4 method is assumed + cs = coeffs{0, 0.5, 0.5, 1}; + bs = coeffs{1.0/6, 1.0/3, 1.0/3, 1.0/6}; + as = scoeffs{ + coeffs{}, + coeffs{0.5}, + coeffs{0, 0.5}, + coeffs{0, 0, 1.0} + }; + }; + + + // RKODESolver(NT initial_time, NT step, pts initial_state, func oracle, + // bounds boundaries, scoeffs a_coeffs, coeffs b_coeffs, coeffs c_coeffs) : + // t(initial_time), xs(initial_state), F(oracle), eta(step), Ks(boundaries), + // as(a_coeffs), bs(b_coeffs), cs(c_coeffs) { + // dim = xs[0].dimension(); + // }; + + unsigned int order() { + return bs.size(); + } + + void step(int k, bool accepted) { + ks = ptsv(order(), xs); + t_prev = t; + + for (unsigned int ord = 0; ord < order(); ord++) { + // Initialize t to previous + t = t_prev + cs[ord] * eta; + + // Initialize ks to previous solution we use + // Initialize argument + for (int j = 0; j < ord; j++) { + for (int r = 0; r < xs.size(); r++) { + y = ks[j][r]; + y = (eta * as[ord][j]) * y; + ks[ord][r] = ks[ord][r] + y; + } + } + + for (unsigned int i = 0; i < xs.size(); i++) { + // Calculate k_i s + y = F(i,ks[ord], t); + ks[ord][i] = y; + y = (eta * bs[i]) * y; + + if (Ks[i] == NULL) { + xs[i] = xs[i] + y; + if (prev_facet != -1 && i > 0) + Ks[i-1]->compute_reflection(xs[i], x_prev_bound, prev_facet); + prev_facet = -1; + } + else { + + // Find intersection (assuming a line trajectory) between x and y + do { + // Find line intersection between xs[i] (new position) and y + std::pair pbpair = Ks[i]->line_positive_intersect(xs[i], y, Ar, Av); + // If point is outside it would yield a negative param + if (pbpair.first >= 0 && pbpair.first <= 1) { + // Advance to point on the boundary + xs[i] += (pbpair.first * 0.99) * y; + + // Update facet for reflection of derivative + prev_facet = pbpair.second; + x_prev_bound = xs[i]; + + // Reflect ray y on the boundary point y now is the reflected ray + Ks[i]->compute_reflection(y, xs[i], pbpair.second); + // Add it to the existing (boundary) point and repeat + xs[i] += y; + + } + else { + prev_facet = -1; + xs[i] += y; + } + } while (!Ks[i]->is_in(xs[i])); + + } + + } + } + + } + + void print_state() { + for (int j = 0; j < xs.size(); j++) { + for (unsigned int i = 0; i < xs[j].dimension(); i++) { + std::cout << xs[j][i] << " "; + } + } + std::cout << std::endl; + } + + void steps(int num_steps, bool accepted) { + for (int i = 0; i < num_steps; i++) step(i, accepted); + } + + Point get_state(int index) { + return xs[index]; + } + + void set_state(int index, Point p) { + xs[index] = p; + } + +}; + + +#endif diff --git a/src/volesti/include/optimization/simulated_annealing.hpp b/src/volesti/include/optimization/simulated_annealing.hpp new file mode 100644 index 00000000..77e15b12 --- /dev/null +++ b/src/volesti/include/optimization/simulated_annealing.hpp @@ -0,0 +1,152 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_SIMULATED_ANNEALING_HPP +#define VOLESTI_SIMULATED_ANNEALING_HPP + +#include + +#include "optimization/sliding_window.hpp" +#include "random_walks/boltzmann_hmc_walk.hpp" + + +/// A magic number! +/// when estimating the diameter of the spectrahedron, +/// sample 20 + sqrt(dimension) points to estimate it +#define CONSTANT_1 20 + +/// Holds parameters of the algorithm +/// \tparam Point Point Type +template +struct SimulatedAnnealingSettings { + /// The numeric type + typedef typename Point::FT NT; + + /// Desired accuracy (relative error) + NT error; + /// The walk length of the HMC random walk + int walkLength; + /// A bound to the number of steps; if negative it is unbounded + int maxNumSteps; + + /// Starting from an initial temperature, at each step it will decrease by a factor of + /// \[ 1 - 1 / (dimension^k) \]. Default is 0.5 + NT k; + + SimulatedAnnealingSettings(NT const error, int const walkLength = 1, int const maxNumSteps = -1, NT const k = 0.5) : error(error), + walkLength(walkLength), maxNumSteps(maxNumSteps), k(k) {} +}; + + +/// Simulated Annealing algorithm for a semidefinite program +/// Minimize \[ c^T x \], s.t. LMI(x) <= 0 +/// \param[in] spectrahedron A spectrahedron described by a linear matrix inequality +/// \param[in] objectiveFunction The function we minimize +/// \param[in] settings Parameters of the algorithm +/// \param[in] interiorPoint An initial feasible solution to start the algorithm +/// \param[out] solution The vector minimizing the objective function +/// \param[in] verbose True to print messages. Default is false +/// \return The best approximation to the optimal solution +template +double solve_sdp(_Spectrahedron & spectrahedron, Point const & objectiveFunction, _Settings const & settings, + Point const & interiorPoint, Point& solution, bool verbose = false) { + + // fetch the data types we will use + typedef typename _Spectrahedron::NT NT; + typedef typename _Spectrahedron::MT MT; + typedef typename _Spectrahedron::VT VT; + typedef BoostRandomNumberGenerator RNGType; + typedef BoltzmannHMCWalk::Walk<_Spectrahedron, RNGType > HMC; + + // the algorithm requires the objective function to be normalized + // we will need to remember the norm + VT _objectiveFunctionNormed = objectiveFunction.getCoefficients(); + NT objectiveFunctionNorm = _objectiveFunctionNormed.norm(); + _objectiveFunctionNormed.normalize(); + Point objectiveFunctionNormed = Point(_objectiveFunctionNormed); + + RNGType rng(spectrahedron.dimension()); + + // Estimate the diameter of the spectrahedron + // needed for the random walk and for the simulated annealing algorithm + NT diameter = spectrahedron.estimateDiameter(CONSTANT_1 + std::sqrt(spectrahedron.dimension()), interiorPoint, rng); + + /******** initialization *********/ + solution = interiorPoint; + // the minimum till last iteration + NT currentMin = objectiveFunction.dot(solution); + int stepsCount = 0; + // initial temperature must be the diameter of the body + NT temperature = diameter; + // after each iteration, temperature = temperature * tempDecreaseFactor + NT tempDecreaseFactor = 1.0 - static_cast(1.0 / std::pow(spectrahedron.dimension(), settings.k)); + + // initialize random walk; + typename HMC::Settings hmc_settings = typename HMC::Settings(settings.walkLength, rng, objectiveFunction, temperature, diameter); + HMC hmcRandomWalk = HMC(hmc_settings); + NT previous_min = objectiveFunction.dot(solution); + + /******** solve *********/ + // if settings.maxNumSteps is negative there is no + // bound to the number of steps - stop + // when desired relative error is achieved + while (stepsCount < settings.maxNumSteps || settings.maxNumSteps < 0) { + + // sample one point with current temperature + std::list randPoints; + + // get a sample under the Boltzmann distribution + // using the HMC random walk + while (1) { + hmcRandomWalk.apply(spectrahedron, solution, settings.walkLength, randPoints); + + // if the sampled point is not inside the spectrahedron (error in boundary oracle), + // get a new one + if (spectrahedron.isExterior(spectrahedron.get_C())) { + if (verbose) std::cout << "Sampled point outside the spectrahedron.\n"; + randPoints.clear(); + spectrahedron.resetFlags(); + } + else { + // update values; + solution = randPoints.back(); + randPoints.clear(); + break; + } + } + + // update current value + currentMin = objectiveFunction.dot(solution); + ++stepsCount; + + // compute relative error + NT relError = relativeError(previous_min, currentMin); + previous_min = currentMin; + + if (verbose) + std::cout << "Step: " << stepsCount << ", Temperature: " << temperature << ", Min: " << currentMin + << ", Relative error: " << relError << "\n"; + + // check if we reached desired accuracy + if (relError < settings.error) + break; + + // decrease the temperature + temperature *= tempDecreaseFactor; + hmcRandomWalk.setTemperature(temperature); + + } /* while (stepsCount < settings.maxNumSteps || settings.maxNumSteps < 0) { */ + + // return the minimum w.r.t. the original objective function + return currentMin*objectiveFunctionNorm; +} + + + +#endif //VOLESTI_SIMULATED_ANNEALING_HPP diff --git a/src/volesti/include/optimization/sliding_window.hpp b/src/volesti/include/optimization/sliding_window.hpp new file mode 100644 index 00000000..daab137a --- /dev/null +++ b/src/volesti/include/optimization/sliding_window.hpp @@ -0,0 +1,66 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_SLIDING_WINDOW_HPP +#define VOLESTI_SLIDING_WINDOW_HPP + + +/// Computes the relative error +/// \tparam NT Numeric type +/// \param[in] approx The approximated value +/// \param[in] exact The exact value +/// \return The relative error +template +NT relativeError(NT approx, NT exact) { + return std::fabs((exact - approx) / exact); +} + + +/// A sliding window, which allows to get the relative error of approximations +/// \tparam NT Numeric type +template +class SlidingWindow { +public: + + /// The stored approximations + std::list approximations; + /// The size of the window + int windowSize; + /// The number of entries in list approximations + int numEntries; + + /// Constructor + /// \param[in] windowSize The size of the window + SlidingWindow(int windowSize) : windowSize(windowSize) { + numEntries = 0; + } + + /// Adds an approximation in the window + /// \param[in] approximation The new approximation + void push(NT approximation) { + // if window is full, remove the oldest value + if (numEntries >= windowSize) { + approximations.pop_back(); + } + else + numEntries++; + + approximation.push_front(approximation); + } + + /// \return The relative error between the youngest and oldest approximations + double getRelativeError() { + if (numEntries < windowSize) + return 1; + + return relativeError(approximations.back(), approximations.front()); + } +}; + +#endif //VOLESTI_SLIDING_WINDOW_HPP diff --git a/src/volesti/include/preprocess/crhmc/analytic_center.h b/src/volesti/include/preprocess/crhmc/analytic_center.h new file mode 100644 index 00000000..7124626b --- /dev/null +++ b/src/volesti/include/preprocess/crhmc/analytic_center.h @@ -0,0 +1,173 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef ANALYTIC_CENTER_H +#define ANALYTIC_CENTER_H +#include "Eigen/Eigen" +#include "PackedCSparse/PackedChol.h" +#include "preprocess/crhmc/crhmc_utils.h" +#include "preprocess/crhmc/opts.h" +#include +#include +#include +#ifndef SIMD_LEN +#define SIMD_LEN 0 +#endif +const size_t chol_k2 = (SIMD_LEN == 0) ? 1 : SIMD_LEN; + +/*This function computes the analytic center of the polytope*/ +//And detects additional constraint that need to be added +// x - It outputs the minimizer of min f(x) subjects to {Ax=b} +// C - detected constraint matrix +// If the domain ({Ax=b} intersect dom(f)) is not full dimensional in {Ax=b} +// because of the dom(f), the algorithm will detect the collapsed dimension +// and output the detected constraint C x = d +// d - detected constraint vector +template +std::tuple analytic_center(SpMat const &A, VT const &b, Polytope &f, Opts const &options, VT x = VT::Zero(0, 1)) +{ + using CholObj = typename Polytope::CholObj; + using Triple = typename Polytope::Triple; + using Tx = typename Polytope::Tx; + // initial conditions + int n = A.cols(); + int m = A.rows(); + if (x.rows() == 0 || !f.barrier.feasible(x)) + { + x = f.barrier.center; + } + + VT lambda = VT::Zero(n, 1); + int fullStep = 0; + NT tConst = 0; + NT primalErr = std::numeric_limits::max(); + NT dualErr = std::numeric_limits::max(); + NT primalErrMin = std::numeric_limits::max(); + NT primalFactor = 1; + NT dualFactor = 1 + b.norm(); + std::vector idx; + + CholObj solver = CholObj(transform_format(A)); + solver.accuracyThreshold = 0; + for (int iter = 0; iter < options.ipmMaxIter; iter++) + { + std::pair pair_analytic_oracle = f.analytic_center_oracle(x); + VT grad = pair_analytic_oracle.first; + VT hess = pair_analytic_oracle.second; + + // compute the residual + VT rx = lambda - grad; + VT rs = b - A * x; + + // check stagnation + primalErrMin = std::min(primalErr, primalErrMin); + primalErr = rx.norm() / primalFactor; + NT dualErrLast = dualErr; + dualErr = rs.norm() / dualFactor; + bool feasible = f.barrier.feasible(x); + //Compare the dual and primal error to the last and minimum so far + if ((dualErr > (1 - 0.9 * tConst) * dualErrLast) || + (primalErr > 10 * primalErrMin) || !feasible) + { + VT dist = f.barrier.boundary_distance(x); + NT th = options.ipmDistanceTol; + visit_lambda(dist, [&idx, th](double v, int i, int j) + { + if (v < th) + idx.push_back(i); }); + if (idx.size() > 0) + { + break; + } + } + + // compute the step direction + VT Hinv = hess.cwiseInverse(); + solver.decompose((Tx *)Hinv.data()); + VT out(m, 1); + solver.solve((Tx *)rs.data(), (Tx *)out.data()); + VT dr1 = A.transpose() * out; + VT in = A * Hinv.cwiseProduct(rx); + solver.solve((Tx *)in.data(), (Tx *)out.data()); + + VT dr2 = A.transpose() * out; + VT dx1 = Hinv.cwiseProduct(dr1); + VT dx2 = Hinv.cwiseProduct(rx - dr2); + + // compute the step size + VT dx = dx1 + dx2; + NT tGrad = std::min(f.barrier.step_size(x, dx), 1.0); + dx = dx1 + tGrad * dx2; + NT tConst = std::min(0.99 * f.barrier.step_size(x, dx), 1.0); + tGrad = tGrad * tConst; + + // make the step + x = x + tConst * dx; + lambda = lambda - dr2; + + if (!f.barrier.feasible(x)) + { + break; + } + //If we have have converged + if (tGrad == 1) + { + //do some more fullStep + fullStep = fullStep + 1; + if (fullStep > log(dualErr / options.ipmDualTol) && fullStep > options.min_convergence_steps) + { + break; + } + } + else + { + fullStep = 0; + } + } + SpMat C; + VT d; + if (idx.size() == 0) + { + VT dist = f.barrier.boundary_distance(x); + NT th = options.ipmDistanceTol; + visit_lambda(dist, [&idx, th](double v, int i, int j) + { + if (v < th) + idx.push_back(i); }); + } + + if (idx.size() > 0) + { + C.resize(idx.size(), n); + std::pair pboundary = f.barrier.boundary(x); + VT A_ = pboundary.first; + VT b_ = pboundary.second; + std::vector sparseIdx; + for (int i = 0; i < idx.size(); i++) + { + sparseIdx.push_back(Triple(i, idx[i], A_(idx[i]))); + } + C.setFromTriplets(sparseIdx.begin(), sparseIdx.end()); + d.resize(idx.size(), 1); + copy_indicies(d, b_, idx); + } + else + { + C = MT::Zero(0, n).sparseView(); + d = VT::Zero(0, 1); + } + return std::make_tuple(x, C, d); +} +#endif diff --git a/src/volesti/include/preprocess/crhmc/constraint_problem.h b/src/volesti/include/preprocess/crhmc/constraint_problem.h new file mode 100644 index 00000000..82931505 --- /dev/null +++ b/src/volesti/include/preprocess/crhmc/constraint_problem.h @@ -0,0 +1,81 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef CONSTRAINT_PROBLEM_H +#define CONSTRAINT_PROBLEM_H +#include "Eigen/Eigen" +/*Input structure: With this the user can define a polytope sampling problem*/ +template +class constraint_problem { +public: + using Type = typename Point::FT; + using PointType = Point; + using VT = Eigen::Matrix; + using MT = MatrixType; +private: + unsigned int num_vars; // num_vars of the original problem + MatrixType Aeq; // Matrix of coefficients for the equality constraints + VT beq; // Right hand side of the equality constraints + MatrixType Aineq; // Matrix of coefficients for the inequality constraints + VT bineq; // Right hand side of the inequality constraints + VT lb; // lb on the output coordinates preset to -1e9 + VT ub; // ub on the output coordinates preset to +1e9 + Type inf = 1e9; +public: + /*Constructors for different input instances*/ + constraint_problem(const int dim, MT const &Aeq_, VT const &beq_, MT const &Aineq_, + VT const &bineq_, VT const &lb_, VT const &ub_) + : num_vars(dim), Aeq(Aeq_), beq(beq_), Aineq(Aineq_), bineq(bineq_), + lb(lb_), ub(ub_) { + } + + constraint_problem(const int dim) { + num_vars = dim; + init(num_vars); + } + + + void init(int num_vars) { + Aineq.resize(0, num_vars); + Aeq.resize(0, num_vars); + bineq.resize(0, 1); + beq.resize(0, 1); + lb = -VT::Ones(num_vars) * inf; + ub = VT::Ones(num_vars) * inf; + } + void set_equality_constraints(MT const &Aeq_, VT const &beq_){ + Aeq = Aeq_; + beq = beq_; + } + void set_inequality_constraints(MT const &Aineq_, VT const &bineq_){ + Aineq = Aineq_; + bineq = bineq_; + } + void set_bounds(VT const &lb_, VT const &ub_){ + lb = lb_; + ub = ub_; + } + std::pair get_equations(){ + return std::make_pair(Aeq,beq); + } + std::pair get_inequalities(){ + return std::make_pair(Aineq,bineq); + } + std::pair get_bounds(){ + return std::make_pair(lb,ub); + } + unsigned int dimension(){ + return num_vars; + } + +}; + +#endif diff --git a/src/volesti/include/preprocess/crhmc/crhmc_input.h b/src/volesti/include/preprocess/crhmc/crhmc_input.h new file mode 100644 index 00000000..05f61c4b --- /dev/null +++ b/src/volesti/include/preprocess/crhmc/crhmc_input.h @@ -0,0 +1,172 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef CRHMC_INPUT_H +#define CRHMC_INPUT_H +#include "Eigen/Eigen" +#include "opts.h" +#include "convex_bodies/hpolytope.h" +#include "preprocess/crhmc/constraint_problem.h" +/*0 funciton handles are given as a reference in case the user gives no +function. Then the uniform function is implied*/ +template +struct ZeroFunctor +{ + using Type = typename Point::FT; + Point operator()(Point const &x) const { return Point(x.dimension()); } + struct parameters { + Type L=1; + Type eta=1; + }; + struct parameters params; +}; +template +struct ZeroScalarFunctor +{ + using Type = typename Point::FT; + Type operator()(Point const &x) const { return 0; } +}; + +/// +/// Input structure: With this the user can define the input for a crhmc polytope sampling problem +template , + typename grad = ZeroFunctor, + typename hess = ZeroFunctor> +class crhmc_input +{ + using Type = typename Point::FT; + using VT = Eigen::Matrix; + ZeroFunctor zerof; + ZeroScalarFunctor zerosf; + +public: + using MT = MatrixType; + using Func = func; + using Grad = grad; + using Hess = hess; + using point= Point; + MatrixType Aineq; // Matrix of coefficients for the inequality constraints + VT bineq; // Right hand side of the inequality constraints + MatrixType Aeq; // Matrix of coefficients for the equality constraints + VT beq; // Right hand side of the equality constraints + opts options; // structure of the parameters of the problem + VT lb; // lb on the output coordinates preset to -1e7 + VT ub; // ub on the output coordinates preset to +1e7 + func &f; // Negative log density function handle + grad &df; // Negative log density gradient function handle + hess &ddf; // Negative log density hessian function handle + bool fZero; // whether f is completely zero + bool fHandle; // whether f is handle or not + bool dfHandle; // whether df is handle or not + bool ddfHandle; // whether ddf is handle or not + unsigned int dimension; // dimension of the original problem + const Type inf = options.max_coord + 1; // helper for barrier handling + /*Constructors for different input instances*/ + crhmc_input(int dim, func &function, grad &g, hess &h) + : f(function), df(g), ddf(h) + { dimension=dim; + fZero = false; + fHandle = true; + dfHandle = true; + ddfHandle = true; + init(dimension); + } + crhmc_input(int dim, func &function) + : f(function), df(zerof), ddf(zerof) + { dimension=dim; + fZero = false; + fHandle = true; + dfHandle = false; + ddfHandle = false; + init(dimension); + } + crhmc_input(int dim, func &function, grad &g) + : f(function), df(g), ddf(zerof) + { dimension=dim; + fZero = false; + fHandle = true; + dfHandle = true; + ddfHandle = false; + init(dimension); + } + crhmc_input(int dim) : f(zerosf), df(zerof), ddf(zerof) + { dimension=dim; + fZero = true; + fHandle = false; + dfHandle = false; + ddfHandle = false; + init(dimension); + } + + void init(int dimension) + { + Aineq.resize(0, dimension); + Aeq.resize(0, dimension); + bineq.resize(0, 1); + beq.resize(0, 1); + lb = -VT::Ones(dimension) * inf; + ub = VT::Ones(dimension) * inf; + } +}; +#include + +template < + typename Input, typename Polytope, typename Func, typename Grad, typename Hess, + typename std::enable_if>::value>::type * = nullptr> +inline Input convert2crhmc_input(Polytope &P, Func &f, Grad &g, Hess &h) { + int dimension = P.dimension(); + Input input = Input(dimension, f, g, h); + if (std::is_same< + Hess, ZeroFunctor>::value){ + input.ddfHandle=false; + } + input.Aineq = P.get_mat(); + input.bineq = P.get_vec(); + return input; +} + +template >::value>::type + * = nullptr> +inline Input convert2crhmc_input(Polytope &P, Func &f, Grad &g, Hess &h) { + int dimension = P.dimension(); + Input input = Input(dimension, f, g, h); + if (std::is_same< + Hess, ZeroFunctor>::value){ + input.ddfHandle=false; + } + std::tie(input.Aineq, input.bineq) = P.get_inequalities(); + std::tie(input.Aeq, input.beq) = P.get_equations(); + std::tie(input.lb, input.ub) = P.get_bounds(); + return input; +} +template >::value && + !std::is_same>::value>:: + type * = nullptr> +inline Input convert2crhmc_input(Polytope &P, Func &f, Grad &g, Hess &h) { + /*CRHMC works only for H-polytopes and constraint_problems for now*/ + int dimension = 0; + Input input = Input(dimension, f, g, h); + return input; +} +#endif diff --git a/src/volesti/include/preprocess/crhmc/crhmc_problem.h b/src/volesti/include/preprocess/crhmc/crhmc_problem.h new file mode 100644 index 00000000..c501bce6 --- /dev/null +++ b/src/volesti/include/preprocess/crhmc/crhmc_problem.h @@ -0,0 +1,703 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef CRHMC_PROBLEM_H +#define CRHMC_PROBLEM_H +#include "Eigen/Eigen" +#include "PackedCSparse/PackedChol.h" +#include "cartesian_geom/cartesian_kernel.h" +#include "convex_bodies/hpolytope.h" +#include "preprocess/crhmc/analytic_center.h" +#include "preprocess/crhmc/crhmc_input.h" +#include "preprocess/crhmc/crhmc_utils.h" +#include "preprocess/crhmc/lewis_center.h" +#include "preprocess/crhmc/opts.h" +#include "preprocess/crhmc/two_sided_barrier.h" +#include +#include +#include +#include +#include + +#ifndef SIMD_LEN +#define SIMD_LEN 0 +#endif +const size_t chol_k = (SIMD_LEN == 0) ? 1 : SIMD_LEN; + +/// +/// Crhmc sampling problem: With this the user can define a crhmc polytope sampling problem +/// @tparam Point Point type +/// @tparam Input Input format +template +class crhmc_problem { +public: + using NT = double; + using PolytopeType = HPolytope; + using MT = Eigen::Matrix; + using VT = Eigen::Matrix; + using IVT = Eigen::Matrix; + using SpMat = Eigen::SparseMatrix; + using PM = Eigen::PermutationMatrix; + using IndexVector = Eigen::Matrix; + using CholObj = PackedChol; + using Triple = Eigen::Triplet; + using Barrier = two_sided_barrier; + using Tx = FloatArray; + using Opts = opts; + using Diagonal_MT = Eigen::DiagonalMatrix; + using Func = typename Input::Func; + using Grad = typename Input::Grad; + using Hess = typename Input::Hess; + using Crhmc_problem=crhmc_problem; + + unsigned int _d; // dimension + // Problem variables Ax=b st lb<=x<=ub + MT A; // matrix A input matrix + SpMat Asp; // problem matrix A in Sparse form + VT b; // vector b, s.t.: Ax=b + VT lb; // Lower bound for output coordinates + VT ub; // Upper bound for output coordinates + Barrier barrier; // Class that holds functions that handle the log barrier for + // lb and ub + Opts options; // problem parameters + // Transformation (T,y) such that the new variables x + // can be tranform to the original z (z= Tx+y) + SpMat T; + VT y; + // Non zero indices and values for fast tranform + std::vector Tidx; // T x = x(Tidx) .* Ta + VT Ta; // T x = x(Tidx) .* Ta + bool isempty_center = true; + VT center = VT::Zero(0, 1); // Resulting polytope Lewis or Analytic center + VT analytic_ctr; //analytic center vector (for testing) + VT w_center;// weights of the lewis center + + VT width; // width of the varibles + int nP;//input dimension + + Func &func; // function handle + Grad &df; // gradient handle + Hess &ddf; // hessian handle + bool fZero; // whether f is completely zero + bool fHandle; // whether f is handle or not + bool dfHandle; // whether df is handle or not + bool ddfHandle; // whether ddf is handle or not + /*Invalid polytope variables*/ + bool terminate=false; + std::string terminate_message; +#ifdef TIME_KEEPING +//Timing information + std::chrono::duration rescale_duration, sparsify_duration, + reordering_duration, rm_rows_duration, rm_fixed_vars_duration, + ex_collapsed_vars_duration, shift_barrier_duration, lewis_center_duration; +#endif + const NT inf = options.max_coord; // helper for barrier handling + const NT barrier_bound = 1e7; + int equations() const { return Asp.rows(); } + int dimension() const { return Asp.cols(); } + int nnz() const { return Asp.nonZeros(); } + + // Remove varibles that have width under some tolerance + int remove_fixed_variables(const NT tol = 1e-12) { + int m = Asp.rows(); + int n = Asp.cols(); + VT d = estimate_width(); + CholObj solver = CholObj(transform_format(Asp)); + solver.accuracyThreshold = 0; + VT w = VT::Ones(n, 1); + solver.decompose((Tx *)w.data()); + VT out_vector = VT(m, 1); + solver.solve((Tx *)b.data(), (Tx *)out_vector.data()); + VT x = Asp.transpose() * out_vector; + + x = ((x.array()).abs() < tol).select(0., x); + std::vector freeIndices; + std::vector indices; + int nFreeVars = 0; + for (int i = 0; i < n; i++) { + if (d(i) < tol * (1 + abs(x(i)))) { + } else { + freeIndices.push_back(Triple(i, nFreeVars, 1)); + nFreeVars++; + indices.push_back(i); + x(i) = 0.0; + } + } + + if (freeIndices.size() != n) { + SpMat S = SpMat(n, freeIndices.size()); + S.setFromTriplets(freeIndices.begin(), freeIndices.end()); + append_map(S, x); + copy_indicies(barrier.lb, barrier.lb, indices); + copy_indicies(barrier.ub, barrier.ub, indices); + barrier.lb.conservativeResize(indices.size()); + barrier.ub.conservativeResize(indices.size()); + + barrier.set_bound(barrier.lb, barrier.ub); + if (!isempty_center) { + copy_indicies(center, center, indices); + center.conservativeResize(indices.size()); + } + return 1; + } + return 0; + } + + int extract_collapsed_variables() { + SpMat Ac; + VT bc; + if (isempty_center) { + std::tie(center, Ac, bc) = analytic_center(Asp, b, *this, options); + isempty_center = false; + } else { + std::tie(center, Ac, bc) = + analytic_center(Asp, b, *this, options, center); + analytic_ctr=center; + } + if (Ac.rows() == 0) { + return 0; + } + SpMat _A = SpMat(Asp); + sparse_stack_v(_A, Ac, Asp); + b.conservativeResize(b.rows() + bc.rows(), 1); + b.bottomRows(bc.rows()) = bc; + return 1; + } + // Rescale the polytope for numerical stability + void rescale(const VT x = VT::Zero(0, 1)) { + if (std::min(equations(), dimension()) <= 1) { + return; + } + VT hess; + if (x.rows() == 0) { + hess = VT::Ones(dimension(), 1); + } else { + std::tie(std::ignore, hess) = analytic_center_oracle(x); + hess = hess + (width.cwiseProduct(width)).cwiseInverse(); + } + VT scale = (hess.cwiseSqrt()).cwiseInverse(); + SpMat Ain = Asp * scale.asDiagonal(); + VT cscale; + VT rscale; + + std::tie(cscale, rscale) = gmscale(Ain, 0.9); + Asp = (rscale.cwiseInverse()).asDiagonal() * Asp; + b = b.cwiseQuotient(rscale); + barrier.set_bound(barrier.lb.cwiseProduct(cscale), + barrier.ub.cwiseProduct(cscale)); + append_map((cscale.cwiseInverse()).asDiagonal(), VT::Zero(dimension(), 1)); + if (!isempty_center) { + center = center.cwiseProduct(cscale); + } + } + + // Rewrite P so that each cols has no more than maxNZ non-zeros + void splitDenseCols(const int maxnz) { + int m = Asp.rows(); + int n = Asp.cols(); + if (m <= maxnz) { + return; + } + if (Asp.nonZeros() > maxnz * n) { + return; + } + int numBadCols = 1; + lb = barrier.lb; + ub = barrier.ub; + //until there are columns with more than maxNZ elements + while (numBadCols > 0) { + m = Asp.rows(); + n = Asp.cols(); + std::vector colCounts(n); + std::vector badCols; + numBadCols = 0; + //find the columns with count larger than maxNZ + std::tie(colCounts, badCols) = nnzPerColumn(Asp, maxnz); + numBadCols = badCols.size(); + if (numBadCols == 0) { + break; + } + //create a new variable for each one and update Asp, b, lb, ub, T, y accordingly + SpMat A_; + SpMat Aj(m, numBadCols); + SpMat Ai(numBadCols, n + numBadCols); + std::vector newColumns; + std::vector newRows; + b.conservativeResize(m + numBadCols, 1); + lb.conservativeResize(n + numBadCols, 1); + ub.conservativeResize(n + numBadCols, 1); + + for (int j = 0; j < numBadCols; j++) { + int i = badCols[j]; + int k = 0; + for (SpMat::InnerIterator it(Asp, i); it; ++it) { + if (k >= colCounts[i] / 2) { + newColumns.push_back(Triple(it.row(), j, it.value())); + it.valueRef() = 0; + } + k++; + } + newRows.push_back(Triple(j, i, 1)); + newRows.push_back(Triple(j, j + n, -1)); + lb(n + j) = lb(i); + ub(n + j) = ub(i); + b(m + j) = 0; + } + Ai.setFromTriplets(newRows.begin(), newRows.end()); + Aj.setFromTriplets(newColumns.begin(), newColumns.end()); + Asp.prune(0, 0); + sparse_stack_h_inplace(Asp, Aj); + sparse_stack_v(Asp, Ai, A_); + Asp = A_; + } + SpMat _T = MT::Zero(T.rows(), ub.rows() - T.cols()).sparseView(); + sparse_stack_h_inplace(T, _T); + updateT(); + barrier.set_bound(lb, ub); + } + // Change A and the correpsonding Transformation + template + void append_map(MatrixType const &S, VT const &z) { + b = b - Asp * z; + Asp = Asp * S; + y = y + T * z; + T = T * S; + updateT(); + } + // Shift the problem with respect to x + void shift_barrier(VT const &x) { + int size = x.rows(); + b = b - Asp * x; + y = y + T * x; + barrier.set_bound(barrier.lb - x, barrier.ub - x); + if (!isempty_center) { + center = center - x; + } + } + // Reorder the polytope accordint to the AMD Reordering for better sparsity + // pattern in the Cholesky decomposition + void reorder() { + if (!options.EnableReordering || Asp.rows()*Asp.cols() < options.maxNZ) { + return; + } + fillin_reduce(Asp,b); + } +//Using the Cholesky decomposition remove dependent rows in the systen Asp*x=b + int remove_dependent_rows(NT tolerance = 1e-12, NT infinity = 1e+64) { + //this approach does not work with 0 rows + remove_zero_rows(Asp, b); + int m = Asp.rows(); + int n = Asp.cols(); + VT v = VT(m); + VT w = VT::Ones(n, 1); + CholObj solver = CholObj(transform_format(Asp)); + solver.accuracyThreshold = 0; + solver.decompose((Tx *)w.data()); + solver.diagL((Tx *)v.data()); + std::vector indices(m, false); + std::vector idx; + bool changed = false; + for (int i = 0; i < m; i++) { + if ((v(i) > tolerance) && (v(i) < infinity)) { + indices[i] = true; + idx.push_back(i); + }else{ + changed=true; + } + } + if (!changed) { + return 0; + } + + remove_rows(Asp, indices); + copy_indicies(b, b, idx); + b.conservativeResize(idx.size(), 1); + return 1; + } +//Apply number of operations that simplify the problem + void simplify() { +#ifdef TIME_KEEPING + std::chrono::time_point start, end; + start = std::chrono::system_clock::now(); +#endif + rescale(); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + rescale_duration += end - start; + start = std::chrono::system_clock::now(); +#endif + splitDenseCols(options.maxNZ); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + sparsify_duration += end - start; + start = std::chrono::system_clock::now(); +#endif + reorder(); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + reordering_duration += end - start; +#endif + int changed = 1; + while (changed) { + while (changed) { + changed = 0; +#ifdef TIME_KEEPING + start = std::chrono::system_clock::now(); +#endif + changed += remove_dependent_rows(); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + rm_rows_duration += end - start; + start = std::chrono::system_clock::now(); +#endif + changed += remove_fixed_variables(); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + rm_fixed_vars_duration += end - start; + start = std::chrono::system_clock::now(); +#endif + reorder(); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + reordering_duration += end - start; +#endif + } +#ifdef TIME_KEEPING + start = std::chrono::system_clock::now(); +#endif + changed += extract_collapsed_variables(); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + ex_collapsed_vars_duration += end - start; +#endif + } + } + + VT estimate_width(bool use_center=false) { + int n = Asp.cols(); + VT hess = VT::Ones(n, 1); + if(use_center){ + std::tie(std::ignore, hess)=analytic_center_oracle(center); + } + CholObj solver = CholObj(transform_format(Asp)); + solver.accuracyThreshold = 0; + solver.decompose((Tx *)hess.data()); + VT w_vector(n, 1); + solver.leverageScoreComplement((Tx *)w_vector.data()); + w_vector = (w_vector.cwiseMax(0)).cwiseProduct(hess.cwiseInverse()); + VT tau = w_vector.cwiseSqrt(); + + return tau; + } + + template + void print(StreamType &stream, std::string const message = "Printing Sparse problem") { + stream << "----------------" << message << "--------------" << '\n'; + stream << "(m,n) = " << equations() << " , " << dimension() + << " nnz= " << Asp.nonZeros() << "\n"; + if (equations() > 20 || dimension() > 20) { + stream << "too big for complete visulization\n"; + return; + } + stream << "A=\n"; + + stream << MT(Asp); + stream << "\n"; + + stream << "b=\n"; + stream << b; + stream << "\n"; + + stream << "lb=\n"; + stream << barrier.lb; + stream << "\n"; + + stream << "ub=\n"; + stream << barrier.ub; + stream << "\n"; + + stream << "T=\n"; + stream << MT(T); + stream << "\n"; + + stream << "y=\n"; + stream << y; + stream << "\n"; + + stream << "center=\n"; + stream << center; + stream << "\n"; + } + + void make_format(Input const &input, MT const &S) { + nP = input.Aeq.cols(); + int nIneq = input.Aineq.rows(); + int nEq = input.Aeq.rows(); + A.resize(nEq + nIneq, nP + nIneq); + A << input.Aeq, MT::Zero(nEq, nIneq), input.Aineq, MT::Identity(nIneq, nIneq); + b.resize(nEq + nIneq, 1); + b << input.beq, input.bineq; + lb.resize(nP + nIneq, 1); + ub.resize(nP + nIneq, 1); + lb << input.lb, MT::Zero(nIneq, 1); + ub << input.ub, MT::Ones(nIneq, 1) * inf; + Asp.resize(nEq + nIneq, nP + nIneq); + int n = dimension(); + /*Move lb=ub to Ax=b*/ + for (int i = 0; i < n; i++) { + if (doubleVectorEqualityComparison(lb(i), ub(i))) { + MT temp = MT::Zero(1, n); + temp(i) = 1; + A.conservativeResize(A.rows() + 1, A.cols()); + A.row(A.rows() - 1) = temp; + b.conservativeResize(b.rows() + 1); + b(b.rows() - 1) = (lb(i) + ub(i)) / 2; + lb(i) = -inf; + ub(i) = inf; + } + } + Asp = A.sparseView(); + } + void make_format(Input const &input, SpMat const &S) { + nP = input.Aeq.cols(); + int nIneq = input.Aineq.rows(); + int nEq = input.Aeq.rows(); + Asp.resize(nEq + nIneq, nP + nIneq); + SpMat B = SpMat(input.Aeq); + SpMat C = SpMat(input.Aineq); + B.conservativeResize(nEq, nIneq + nP); + SpMat temp = SpMat(nIneq, nIneq); + temp.setIdentity(); + sparse_stack_h_inplace(C, temp); + sparse_stack_v(B, C, Asp); + b.resize(nEq + nIneq, 1); + b << input.beq, input.bineq; + lb.resize(nP + nIneq, 1); + ub.resize(nP + nIneq, 1); + lb << input.lb, MT::Zero(nIneq, 1); + ub << input.ub, MT::Ones(nIneq, 1) * inf; + int n = dimension(); + /*Move lb=ub to Ax=b*/ + for (int i = 0; i < n; i++) { + if (doubleVectorEqualityComparison(lb(i), ub(i))) { + B.resize(Asp.rows(), Asp.cols()); + B = SpMat(Asp); + MT temp = MT::Zero(1, n); + temp(i) = 1; + C = temp.sparseView(); + sparse_stack_v(B, C, Asp); + b.conservativeResize(b.rows() + 1); + b(b.rows() - 1) = (lb(i) + ub(i)) / 2; + lb(i) = -inf; + ub(i) = inf; + } + } + Asp.makeCompressed(); + } +//Class constructor + crhmc_problem(Input const &input, Opts _options = Opts()) + : options(_options), func(input.f), df(input.df), ddf(input.ddf), + fZero(input.fZero), fHandle(input.fHandle), dfHandle(input.dfHandle), + ddfHandle(input.ddfHandle) { +#ifdef TIME_KEEPING + rescale_duration = sparsify_duration = reordering_duration = + rm_rows_duration = rm_fixed_vars_duration = ex_collapsed_vars_duration = + shift_barrier_duration = lewis_center_duration = + std::chrono::duration::zero(); +#endif + + make_format(input, input.Aeq); + PreproccessProblem(); + } + // Initialization funciton + void PreproccessProblem() { + int n = dimension(); + barrier.set_bound(lb.cwiseMax(-barrier_bound), ub.cwiseMin(barrier_bound)); + NT tol = std::numeric_limits::epsilon(); + Asp.prune(tol, tol); + /*Update the transformation Tx + y*/ + T = SpMat(nP, n); + std::vector indices; + for (int i = 0; i < nP; i++) { + indices.push_back(Triple(i, i, 1)); + } + T.setFromTriplets(indices.begin(), indices.end()); + Tidx = std::vector(T.rows()); + updateT(); + y = VT::Zero(nP, 1); + /*Simplify*/ + if (!fZero) { + fZero = true; + simplify(); + fZero = false; + } + simplify(); +#ifdef TIME_KEEPING + std::chrono::time_point start, end; + start = std::chrono::system_clock::now(); +#endif + if (isempty_center) { + std::tie(center, std::ignore, std::ignore) = + analytic_center(Asp, b, *this, options); + isempty_center = false; + } + shift_barrier(center); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + shift_barrier_duration += end - start; +#endif + reorder(); + + width = estimate_width(true); + // Recenter again and make sure it is feasible + VT hess; +#ifdef TIME_KEEPING + start = std::chrono::system_clock::now(); +#endif + std::tie(center, std::ignore, std::ignore, w_center) = + lewis_center(Asp, b, *this, options, center); + std::tie(std::ignore, hess) = lewis_center_oracle(center, w_center); + CholObj solver = CholObj(transform_format(Asp)); + solver.accuracyThreshold = 0; + VT Hinv = hess.cwiseInverse(); + solver.decompose((Tx *)Hinv.data()); + VT out(equations(), 1); + VT input = (b - Asp * center); + solver.solve((Tx *)input.data(), (Tx *)out.data()); + center = center + (Asp.transpose() * out).cwiseProduct(Hinv); +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + lewis_center_duration += end - start; +#endif + if ((center.array() > barrier.ub.array()).any() || + (center.array() < barrier.lb.array()).any()) { + terminate = true; + terminate_message = "Polytope:Infeasible. The algorithm cannot find a feasible point.\n"; + return; + } + } +#ifdef TIME_KEEPING +template +void print_preparation_time(StreamType& stream){ + stream << "---Preparation Timing Information"<< std::endl; + stream << "Rescale completed in time, "; + stream << rescale_duration.count() << " secs " << std::endl; + stream << "Split dense columns completed in time, "; + stream << sparsify_duration.count() << " secs " << std::endl; + stream << "Reordering completed in time, "; + stream << reordering_duration.count() << " secs " << std::endl; + stream << "Removing dependent rows completed in time, "; + stream << rm_rows_duration.count() << " secs " << std::endl; + stream << "Removing fixed variables completed in time, "; + stream << rm_fixed_vars_duration.count() << " secs " << std::endl; + stream << "Extracting collapsed variables completed in time, "; + stream << ex_collapsed_vars_duration.count() << " secs " << std::endl; + stream << "Shift_barrier completed in time, "; + stream << shift_barrier_duration.count() << " secs " << std::endl; + stream << "Finding Center completed in time, "; + stream << lewis_center_duration.count() << " secs " << std::endl; +} +#endif + // Gradient and hessian of for the analytic center + std::pair analytic_center_oracle(VT const &x) { + MT g, h; + std::tie(std::ignore, g, h) = f_oracle(x); + return std::make_pair(g + barrier.gradient(x), h + barrier.hessian(x)); + } + // Gradient and hessian of for the lewis center + std::pair lewis_center_oracle(VT const &x, VT const &w) { + MT g, h; + std::tie(std::ignore, g, h) = f_oracle(x); + return std::make_pair(g + w.cwiseProduct(barrier.gradient(x)), + h + w.cwiseProduct(barrier.hessian(x))); + } + // Function that uses the transformation (T,y) to apply the function to the + // original variables + std::tuple f_oracle(MT const &x) { + int n = x.rows(); + int m=x.cols(); + VT f=VT(m); + MT g=MT(n,m); + MT h=MT(n,m); + if (fZero) { + f = VT::Zero(m); + g = MT::Zero(n,m); + h = MT::Zero(n,m); + return std::make_tuple(f, g, h); + } + // Take the correpsonding point in the original space + MT z = MT::Zero(y.rows(), m); + if (fHandle || dfHandle || ddfHandle) { + for(int k=0;k +#include "PackedCSparse/SparseMatrix.h" +#include +#include + +template +struct lambda_as_visitor_wrapper : Func +{ + lambda_as_visitor_wrapper(const Func &f) : Func(f) {} + template + void init(const S &v, I i, I j) + { + return Func::operator()(v, i, j); + } +}; + +template +void visit_lambda(const Mat &m, const Func &f) +{ + lambda_as_visitor_wrapper visitor(f); + m.visit(visitor); +} + +template +void sparse_stack_v(const SparseMatrixType &top, const SparseMatrixType &bottom, + SparseMatrixType &stacked) +{ + assert(top.cols() == bottom.cols()); + stacked.resize(top.rows() + bottom.rows(), top.cols()); + stacked.resizeNonZeros(top.nonZeros() + bottom.nonZeros()); + + int i = 0; + + for (int col = 0; col < top.cols(); col++) + { + stacked.outerIndexPtr()[col] = i; + + for (int j = top.outerIndexPtr()[col]; j < top.outerIndexPtr()[col + 1]; + j++, i++) + { + stacked.innerIndexPtr()[i] = top.innerIndexPtr()[j]; + stacked.valuePtr()[i] = top.valuePtr()[j]; + } + + for (int j = bottom.outerIndexPtr()[col]; + j < bottom.outerIndexPtr()[col + 1]; j++, i++) + { + stacked.innerIndexPtr()[i] = (int)top.rows() + bottom.innerIndexPtr()[j]; + stacked.valuePtr()[i] = bottom.valuePtr()[j]; + } + } + stacked.outerIndexPtr()[top.cols()] = i; +} + +template +void sparse_stack_h(const SparseMatrixType &left, const SparseMatrixType &right, + SparseMatrixType &stacked) +{ + assert(left.rows() == right.rows()); + + stacked.resize(left.rows(), left.cols() + right.cols()); + stacked.resizeNonZeros(left.nonZeros() + right.nonZeros()); + + std::copy(left.innerIndexPtr(), left.innerIndexPtr() + left.nonZeros(), + stacked.innerIndexPtr()); + std::copy(right.innerIndexPtr(), right.innerIndexPtr() + right.nonZeros(), + stacked.innerIndexPtr() + left.nonZeros()); + + std::copy(left.valuePtr(), left.valuePtr() + left.nonZeros(), + stacked.valuePtr()); + std::copy(right.valuePtr(), right.valuePtr() + right.nonZeros(), + stacked.valuePtr() + left.nonZeros()); + + std::copy(left.outerIndexPtr(), left.outerIndexPtr() + left.cols(), + stacked.outerIndexPtr()); // dont need the last entry of + // A.outerIndexPtr() -- total length is + // AB.cols() + 1 = A.cols() + B.cols() + 1 + std::transform(right.outerIndexPtr(), + right.outerIndexPtr() + right.cols() + 1, + stacked.outerIndexPtr() + left.cols(), + [&](int i) + { return i + left.nonZeros(); }); +} +#include + +template +void sparse_stack_h_inplace(SparseMatrixType &left, + const SparseMatrixType &right) +{ + assert(left.rows() == right.rows()); + + const int leftcol = (int)left.cols(); + const int leftnz = (int)left.nonZeros(); + + left.conservativeResize(left.rows(), left.cols() + right.cols()); + left.resizeNonZeros(left.nonZeros() + right.nonZeros()); + + std::copy(right.innerIndexPtr(), right.innerIndexPtr() + right.nonZeros(), + left.innerIndexPtr() + leftnz); + std::copy(right.valuePtr(), right.valuePtr() + right.nonZeros(), + left.valuePtr() + leftnz); + std::transform( + right.outerIndexPtr(), right.outerIndexPtr() + right.cols() + 1, + left.outerIndexPtr() + leftcol, [&](int i) + { return i + leftnz; }); +} + +template +void remove_zero_rows(SparseMatrixType &A, VectorType &b) { + std::vector> tripletList; + unsigned Ndata = A.cols(); + unsigned Nbins = A.rows(); + for (int k = 0; k < A.outerSize(); ++k) { + for (typename SparseMatrixType::InnerIterator it(A, k); it; ++it) { + tripletList.push_back( + Eigen::Triplet(it.row(), it.col(), it.value())); + } + } + // get which rows are empty + std::vector has_value(Nbins, false); + for (auto tr : tripletList) + has_value[tr.row()] = true; + if (std::all_of(has_value.begin(), has_value.end(), + [](bool v) { return v; })) { + return; + } + // create map from old to new indices + std::map row_map; + unsigned new_idx = 0; + for (unsigned old_idx = 0; old_idx < Nbins; old_idx++) + if (has_value[old_idx]) { + row_map[old_idx] = new_idx; + b(new_idx) = b(old_idx); + new_idx++; + } + // make new triplet list, dropping empty rows + std::vector> newTripletList; + newTripletList.reserve(Ndata); + for (auto tr : tripletList) + newTripletList.push_back( + Eigen::Triplet(row_map[tr.row()], tr.col(), tr.value())); + + // form new matrix and return + A.resize(new_idx, Ndata); + A.setFromTriplets(newTripletList.begin(), newTripletList.end()); + b.conservativeResize(new_idx); +} + +template +void remove_rows(SparseMatrixType &A, std::vector ¬Removed) { + unsigned Ndata = A.cols(); + unsigned Nbins = A.rows(); + // create map from old to new indices + std::map row_map; + unsigned new_idx = 0; + for (unsigned old_idx = 0; old_idx < Nbins; old_idx++) + if (notRemoved[old_idx]) + row_map[old_idx] = new_idx++; + + std::vector> tripletList; + for (int k = 0; k < A.outerSize(); ++k) { + for (typename SparseMatrixType::InnerIterator it(A, k); it; ++it) { + if (notRemoved[it.row()]) { + tripletList.push_back( + Eigen::Triplet(row_map[it.row()], it.col(), it.value())); + } + } + } + // form new matrix and return + A.resize(new_idx, Ndata); + A.setFromTriplets(tripletList.begin(), tripletList.end()); +} + +template +std::pair colwiseMinMax(SparseMatrixType const &A) +{ + int n = A.cols(); + VectorType cmax(n); + VectorType cmin(n); + for (int k = 0; k < A.outerSize(); ++k) + { + Type minv = +std::numeric_limits::max(); + Type maxv = std::numeric_limits::lowest(); + for (typename SparseMatrixType::InnerIterator it(A, k); it; ++it) + { + minv = std::min(minv, it.value()); + maxv = std::max(maxv, it.value()); + } + cmin(k) = minv; + cmax(k) = maxv; + } + return std::make_pair(cmin, cmax); +} +template +void nextpow2(VectorType &a) +{ + a = (a.array() == 0).select(1, a); + a = (((a.array().log()) / std::log(2)).ceil()).matrix(); + a = pow(2, a.array()).matrix(); +} +template +std::pair gmscale(SparseMatrixType &Asp, + const Type scltol) +{ + using Diagonal_MT = Eigen::DiagonalMatrix; + int m = Asp.rows(); + int n = Asp.cols(); + SparseMatrixType A = Asp.cwiseAbs(); + A.makeCompressed(); + int maxpass = 10; + Type aratio = 1e+50; + Type sratio; + Type damp = 1e-4; + Type small = 1e-8; + VectorType rscale = VectorType ::Ones(m, 1); + VectorType cscale = VectorType ::Ones(n, 1); + VectorType cmax; + VectorType cmin; + VectorType rmax; + VectorType rmin; + VectorType eps = VectorType ::Ones(n, 1) * 1e-12; + SparseMatrixType SA; + for (int npass = 0; npass < maxpass; npass++) + { + + rscale = (rscale.array() == 0).select(1, rscale); + Diagonal_MT Rinv = (rscale.cwiseInverse()).asDiagonal(); + SA = Rinv * A; + std::tie(cmin, cmax) = + colwiseMinMax(SA); + + // cmin = (cmin + eps).cwiseInverse(); + sratio = (cmax.cwiseQuotient(cmin)).maxCoeff(); + + if (npass > 0) + { + cscale = ((cmin.cwiseMax(damp * cmax)).cwiseProduct(cmax)).cwiseSqrt(); + } + + if (npass >= 2 && sratio >= aratio * scltol) + { + break; + } + aratio = sratio; + nextpow2(cscale); + Diagonal_MT Cinv = (cscale.cwiseInverse()).asDiagonal(); + SA = A * Cinv; + std::tie(rmin, rmax) = + colwiseMinMax(SA.transpose()); + // rmin = (rmin + eps).cwiseInverse(); + rscale = ((rmin.cwiseMax(damp * rmax)).cwiseProduct(rmax)).cwiseSqrt(); + nextpow2(rscale); + } + rscale = (rscale.array() == 0).select(1, rscale); + Diagonal_MT Rinv = (rscale.cwiseInverse()).asDiagonal(); + SA = Rinv * A; + std::tie(std::ignore, cscale) = + colwiseMinMax(SA); + nextpow2(cscale); + return std::make_pair(cscale, rscale); +} +template +int doubleVectorEqualityComparison( + const Type a, const Type b, + const Type tol = std::numeric_limits::epsilon()) +{ + return (abs(a - b) < tol * (1 + abs(a) + abs(b))); +} + +template +std::pair, std::vector> +nnzPerColumn(SparseMatrixType const &A, const int threashold) +{ + int n = A.cols(); + std::vector colCounts(n); + std::vector badCols; + for (int k = 0; k < A.outerSize(); ++k) + { + int nnz = 0; + for (typename SparseMatrixType::InnerIterator it(A, k); it; ++it) + { + if (it.value() != 0) + { + nnz++; + } + } + colCounts[k] = nnz; + if (nnz > threashold) + { + badCols.push_back(k); + } + } + return std::make_pair(colCounts, badCols); +} +using PM = Eigen::PermutationMatrix; +template +PM permuteMatAMD(SparseMatrixType const &A) +{ + Eigen::AMDOrdering ordering; + PM perm; + ordering(A, perm); + return perm; +} +template +PM postOrderPerm(SparseMatrixType const &A) +{ + using IndexVector = Eigen::Matrix; + int n = A.rows(); + IndexVector m_etree; + IndexVector firstRowElt; + Eigen::internal::coletree(A, m_etree, firstRowElt); + IndexVector post; + Eigen::internal::treePostorder(int(A.cols()), m_etree, post); + PM post_perm(n); + for (int i = 0; i < n; i++) + post_perm.indices()(i) = post(i); + return post_perm; +} + +template +void fillin_reduce(SparseMatrixType &X,VectorType& b){ + SparseMatrixType I = SparseMatrixType(Eigen::VectorXd::Ones(X.rows()).asDiagonal()); + SparseMatrixType XX = X * X.transpose() + I; + XX.makeCompressed(); + Eigen::SimplicialLDLT> cholesky; + cholesky.analyzePattern(XX); + X = cholesky.permutationP() * X; + b = cholesky.permutationP() *b; +} +template +PackedCSparse::SparseMatrix transform_format(SparseMatrixType const &mat) { + PackedCSparse::SparseMatrix A = PackedCSparse::SparseMatrix(mat.rows(), mat.cols(), mat.nonZeros()); + IndexType nnz = 0; + for (IndexType outeindex = 0; outeindex < mat.outerSize(); ++outeindex) { + A.p[outeindex] = nnz; + for (typename SparseMatrixType::InnerIterator it(mat, outeindex); it; ++it) { + A.i[nnz] = it.row(); + A.x[nnz] = it.value(); + nnz++; + } + } + A.p[A.n] = nnz; + return A; +} +template +void copy_indicies(MatrixType& a, MatrixType& b, std::vectorconst & a_idx, std::vectorconst & b_idx){ +for(int i=0;i +void copy_indicies(MatrixType& a, MatrixType b, std::vectorconst & b_idx){ +for(int i=0;i +void set(MatrixType &a, std::vectorconst & idx, const Type c){ + for(int i=0;i +void saxpy(MatrixType &a,MatrixType const &b,MatrixType const& c, std::vectorconst & a_idx, std::vectorconst & b_idx){ +for(int i=0;i +void load_problem(SpMat &A, VT &b, VT &lb, VT &ub, int &dimension, + std::string problem_name) { + { + std::string fileName(problem_name); + fileName.append(".mm"); + SpMat X; + loadMarket(X, fileName); + int m = X.rows(); + dimension = X.cols() - 1; + A = X.leftCols(dimension); + b = VT(X.col(dimension)); + } + { + std::string fileName(problem_name); + fileName.append("_bounds.mm"); + SpMat bounds; + loadMarket(bounds, fileName); + lb = VT(bounds.col(0)); + ub = VT(bounds.col(1)); + } +} +#endif diff --git a/src/volesti/include/preprocess/crhmc/lewis_center.h b/src/volesti/include/preprocess/crhmc/lewis_center.h new file mode 100644 index 00000000..5bf89d15 --- /dev/null +++ b/src/volesti/include/preprocess/crhmc/lewis_center.h @@ -0,0 +1,188 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef LEWIS_CENTER_H +#define LEWIS_CENTER_H +#include "Eigen/Eigen" +#include "PackedCSparse/PackedChol.h" +#include "preprocess/crhmc/crhmc_utils.h" +#include "preprocess/crhmc/opts.h" +#include "preprocess/crhmc/two_sided_barrier.h" +#include +#include +#include +#ifndef SIMD_LEN +#define SIMD_LEN 0 +#endif +const size_t chol_k3 = (SIMD_LEN == 0) ? 1 : SIMD_LEN; + +/*This function computes the Lewis center of the polytope*/ +//And detects additional constraint that need to be added +// x - It outputs the minimizer of min f(x) subjects to {Ax=b} +// w - Output weights that correspond to the Lewis center, they are gone be used in the sampler to reduce the conditon number +// C - detected constraint matrix +// If the domain ({Ax=b} intersect dom(f)) is not full dimensional in {Ax=b} +// because of the dom(f), the algorithm will detect the collapsed dimension +// and output the detected constraint C x = d +// d - detected constraint vector +template +std::tuple lewis_center(SpMat const &A, VT const &b, Polytope &f, Opts const &options, VT x = VT::Zero(0, 1)) +{ + using CholObj = typename Polytope::CholObj; + using Triple = typename Polytope::Triple; + using Tx = typename Polytope::Tx; + NT epsilon = 1e-8; + // initial conditions + int n = A.cols(); + int m = A.rows(); + //If it is given use starting point + if (x.rows() == 0 || !f.barrier.feasible(x)) + { + x = f.barrier.center; + } + VT lambda = VT::Zero(n, 1); + int fullStep = 0; + NT tConst = 0; + NT primalErr = std::numeric_limits::max(); + NT dualErr = std::numeric_limits::max(); + NT primalErrMin = std::numeric_limits::max(); + NT primalFactor = 1; + NT dualFactor = 1 + b.norm(); + std::vector idx; + + CholObj solver = CholObj(transform_format(A)); + VT w = VT::Ones(n, 1); + VT wp = w; + for (int iter = 0; iter < options.ipmMaxIter; iter++) + { + std::pair pair_analytic_oracle = f.lewis_center_oracle(x, wp); + VT grad = pair_analytic_oracle.first; + VT hess = pair_analytic_oracle.second; + + // compute the residual + VT rx = lambda - grad; + VT rs = b - A * x; + + // check stagnation + primalErrMin = std::min(primalErr, primalErrMin); + primalErr = rx.norm() / primalFactor; + NT dualErrLast = dualErr; + dualErr = rs.norm() / dualFactor; + bool feasible = f.barrier.feasible(x); + if ((dualErr > (1 - 0.9 * tConst) * dualErrLast) || + (primalErr > 10 * primalErrMin) || !feasible) + { + VT dist = f.barrier.boundary_distance(x); + NT th = options.ipmDistanceTol; + visit_lambda(dist, [&idx, th](double v, int i, int j) + { + if (v < th) + idx.push_back(i); }); + + if (idx.size() > 0) + { + break; + } + } + + // compute the step direction + VT Hinv = hess.cwiseInverse(); + solver.decompose((Tx *)Hinv.data()); + VT out(m, 1); + solver.solve((Tx *)rs.data(), (Tx *)out.data()); + VT dr1 = A.transpose() * out; + VT in = A * Hinv.cwiseProduct(rx); + solver.solve((Tx *)in.data(), (Tx *)out.data()); + + VT dr2 = A.transpose() * out; + VT dx1 = Hinv.cwiseProduct(dr1); + VT dx2 = Hinv.cwiseProduct(rx - dr2); + + // compute the step size + VT dx = dx1 + dx2; + NT tGrad = std::min(f.barrier.step_size(x, dx), 1.0); + dx = dx1 + tGrad * dx2; + NT tConst = std::min(0.99 * f.barrier.step_size(x, dx), 1.0); + tGrad = tGrad * tConst; + + // make the step + x = x + tConst * dx; + lambda = lambda - dr2; + + // update weight + VT w_vector(n, 1); + solver.leverageScoreComplement((Tx *)w_vector.data()); + + VT wNew = w_vector.cwiseMax(0) + VT::Ones(n, 1) * epsilon; + w = (w + wNew) / 2; + wp = Eigen::pow(w.array(), 0.875).matrix(); + + if (!f.barrier.feasible(x)) + { + break; + } + + // stop if converged + if (tGrad == 1) + { + fullStep = fullStep + 1; + if (fullStep > log(dualErr / options.ipmDualTol) && + fullStep > options.min_convergence_steps) + { + break; + } + } + else + { + fullStep = 0; + } + } + + SpMat C; + VT d; + if (idx.size() == 0) + { + VT dist = f.barrier.boundary_distance(x); + NT th = options.ipmDistanceTol; + visit_lambda(dist, [&idx, th](double v, int i, int j) + { + if (v < th) + idx.push_back(i); }); + } + + if (idx.size() > 0) + { + C.resize(idx.size(), n); + std::pair pboundary = f.barrier.boundary(x); + VT A_ = pboundary.first; + VT b_ = pboundary.second; + std::vector sparseIdx; + for (int i = 0; i < idx.size(); i++) + { + sparseIdx.push_back(Triple(i, idx[i], A_(idx[i]))); + } + C.setFromTriplets(sparseIdx.begin(), sparseIdx.end()); + d.resize(idx.size(), 1); + copy_indicies(d, b_, idx); + } + else + { + C = MT::Zero(0, n).sparseView(); + d = VT::Zero(0, 1); + } + + return std::make_tuple(x, C, d, wp); +} +#endif diff --git a/src/volesti/include/preprocess/crhmc/opts.h b/src/volesti/include/preprocess/crhmc/opts.h new file mode 100644 index 00000000..573a69db --- /dev/null +++ b/src/volesti/include/preprocess/crhmc/opts.h @@ -0,0 +1,62 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" + +#ifndef OPTS_H +#define OPTS_H + +/// @brief Crhmc options +/// @tparam Type Numer type +template class opts { +public: + /*Preprocess options*/ + const int ipmMaxIter = 200; //Maximum number of iterations for finding the analytic and lewis center + const Type ipmDistanceTol = 1e-8; + const Type ipmDualTol = 1e-12; + int maxNZ = 30; + Type max_coord = 1e9; + bool EnableReordering = true; + const int min_convergence_steps=8; + + /*ODE options*/ + const Type implicitTol = 1e-5; + const int maxODEStep = 30; + Type initialStep = 0.2; + Type convergence_bound = 1e16; + + /*PackedCS Solver Options*/ + Type solver_accuracy_threshold=1e-2; + int simdLen=1; + + /*Sampler options*/ + bool DynamicWeight = true; //Enable the use of dynamic weights for each variable when sampling + bool DynamicStepSize = true; // Enable adaptive step size that avoids low acceptance probability + bool DynamicRegularizer = true; //Enable the addition of a regularization term + Type regularization_factor=1e-20; + /*Dynamic step choices*/ + Type warmUpStep = 10; + int maxConsecutiveBadStep = 10; + Type targetODEStep = 10; + Type shrinkFactor = 1.1; + Type minStepSize = 1e-5; + Type effectiveStepSize = 1; + + opts() {} + void operator=(const opts &rhs) { + EnableReordering = rhs.EnableReordering; + maxNZ = rhs.maxNZ; + } +}; +#endif diff --git a/src/volesti/include/preprocess/crhmc/two_sided_barrier.h b/src/volesti/include/preprocess/crhmc/two_sided_barrier.h new file mode 100644 index 00000000..7e279ab7 --- /dev/null +++ b/src/volesti/include/preprocess/crhmc/two_sided_barrier.h @@ -0,0 +1,173 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" + +// The log barrier for the domain {lu <= x <= ub}: +// phi(x) = - sum log(x - lb) - sum log(ub - x). +#ifndef TWO_SIDED_BARIER_H +#define TWO_SIDED_BARIER_H + +#include "Eigen/Eigen" +#include "cartesian_geom/cartesian_kernel.h" +#include "preprocess/crhmc/crhmc_utils.h" +#include + +/// @brief A two sided barrier used by crhmc sampler +/// @tparam Point Point Type +template class two_sided_barrier { + + using NT = typename Point::FT; + using MT = Eigen::Matrix; + using VT = Eigen::Matrix; + +public: + VT lb; + VT ub; + int vdim; + int n; + std::vector upperIdx; + std::vector lowerIdx; + std::vector freeIdx; + VT center; + const NT max_step = 1e16; // largest step size + const NT regularization_constant = 1e-20; // small regularization to not have a large inverse + const NT unbounded_center_coord = 1e6; + MT extraHessian; + + const NT inf = std::numeric_limits::infinity(); + + void set_bound(VT const &_lb, VT const &_ub) { + n = _lb.rows(); + extraHessian.resize(n, 1); + lb.resize(n); + ub.resize(n); + lb = _lb; + ub = _ub; + extraHessian = regularization_constant * MT::Ones(n, 1); + int x1 = 0, x2 = 0, x3 = 0; + for (int i = 0; i < n; i++) { + if (lb(i) == -inf) { + upperIdx.push_back(i); + x1++; + } + if (ub(i) == inf) { + lowerIdx.push_back(i); + x2++; + } + if (ub(i) == inf && lb(i) == -inf) { + freeIdx.push_back(i); + } + } + + VT c = (ub + lb) / 2; + VT bias1=VT::Ones(x2, 1) * unbounded_center_coord; + saxpy(c,lb,bias1,lowerIdx,lowerIdx); + VT bias2=-VT::Ones(x1, 1) * unbounded_center_coord; + saxpy(c,ub,bias2,upperIdx,upperIdx); + set(c, freeIdx, 0.0); + + center = c; + } + two_sided_barrier(VT const &_lb, VT const &_ub, int _vdim = 1) { + set_bound(_lb, _ub); + vdim = _vdim; + extraHessian = regularization_constant * MT::Ones(n,1); + } + two_sided_barrier() { vdim = 1; } + + VT gradient(VT const &x) { + return (ub - x).cwiseInverse() - (x - lb).cwiseInverse(); + } + + VT hessian(VT const &x) { + VT d = ((ub - x).cwiseProduct((ub - x))).cwiseInverse() + + ((x - lb).cwiseProduct((x - lb))).cwiseInverse(); + return d + extraHessian; + } + MT hessian(MT const &x){ + MT d = (((- x).colwise()+ub).cwiseProduct(((- x).colwise()+ub))).cwiseInverse() + + ((x.colwise() - lb).cwiseProduct((x.colwise() - lb))).cwiseInverse(); + return d + extraHessian; + } + MT tensor(MT const &x) { + MT d = 2 * ((((-x).colwise()+ub).cwiseProduct(((-x).colwise()+ub))).cwiseProduct(((-x).colwise()+ub))).cwiseInverse() - + 2 * (((x.colwise() - lb).cwiseProduct(( x.colwise() - lb))).cwiseProduct(( x.colwise() - lb))).cwiseInverse(); + return d; + } + MT quadratic_form_gradient(MT const &x, MT const &u) { + // Output the -grad of u' (hess phi(x)) u. + return (u.cwiseProduct(u)).cwiseProduct(tensor(x)); + } + VT quadratic_form_gradient(VT const &x, VT const &u) { + // Output the -grad of u' (hess phi(x)) u. + + return (u.cwiseProduct(u)).cwiseProduct(tensor(x)); + } + NT step_size(VT const &x, VT const &v) { + // Output the maximum step size from x with direction v. + + // check positive direction + VT temp = (v.array() > 0).select((ub - x).cwiseQuotient(v), max_step); + NT t1 = temp.minCoeff(); + + // check negative direction + temp = (v.array() < 0).select((lb - x).cwiseQuotient(v), max_step); + NT t2 = temp.minCoeff(); + + return std::min(t1, t2); + } + VT boundary_distance(VT const &x) { + // Output the distance of x with its closest boundary for each + // coordinate + return ((x - lb).cwiseMin(ub - x)).cwiseAbs(); + } + + bool feasible(VT const &x) { + return (x.array() > lb.array() && x.array() < ub.array()).all(); + } + VT feasible(MT const &x) { + VT result=VT::Ones(x.cols()); + for(int i=0;i lb.array() && x.col(i).array() < ub.array()).all(); + } + return result; + } + + std::pair analytic_center_oracle(VT const &x) { + VT g = VT::Zero(n, 1); + VT h = VT::Zero(n, 1); + return std::make_pair(g + gradient(x), h + hessian(x)); + } + + std::pair lewis_center_oracle(VT const &x, VT const &w) { + VT g = VT::Zero(n, 1); + VT h = VT::Zero(n, 1); + return std::make_pair(g + w.cwiseProduct(gradient(x)),h + w.cwiseProduct(hessian(x))); + } + + std::pair boundary(VT const &x) { + // Output the normal at the boundary around x for each barrier. + // Assume: only 1 vector is given + + VT A = VT::Ones(x.rows(), 1); + VT b = ub; + + b = (x.array() < center.array()).select(-lb, b); + A = (x.array() < center.array()).select(-A, A); + + return std::make_pair(A, b); + } +}; +#endif diff --git a/src/volesti/include/preprocess/crhmc/weighted_two_sided_barrier.h b/src/volesti/include/preprocess/crhmc/weighted_two_sided_barrier.h new file mode 100644 index 00000000..09f5eb79 --- /dev/null +++ b/src/volesti/include/preprocess/crhmc/weighted_two_sided_barrier.h @@ -0,0 +1,166 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" + +// The log barrier for the domain {lu <= x <= ub}: +// phi(x) = - sum log(x - lb) - sum log(ub - x). +#ifndef WEIGHTED_TWO_SIDED_BARIER_H +#define WEIGHTED_TWO_SIDED_BARIER_H + +#include "Eigen/Eigen" +#include "cartesian_geom/cartesian_kernel.h" +#include "preprocess/crhmc/crhmc_utils.h" +#include + +/// @brief A weighted two sided barrier used by crhmc sampler +/// @tparam Point Point Type +template class weighted_two_sided_barrier { + + using NT = typename Point::FT; + using MT = Eigen::Matrix; + using VT = Eigen::Matrix; + +public: + VT lb; + VT ub; + int vdim; + int n; + std::vector upperIdx; + std::vector lowerIdx; + std::vector freeIdx; + VT center; + const NT max_step = 1e16; // largest step size + const NT regularization_constant = 1e-20; // small regularization to not have a large inverse + const NT unbounded_center_coord = 1e6; + MT extraHessian; + const NT inf = std::numeric_limits::infinity(); + + VT w; + + weighted_two_sided_barrier(VT const &_lb, VT const &_ub, VT const &_w, + int _vdim = 1) { + set_bound(_lb, _ub); + w = _w; + vdim = _vdim; + extraHessian = regularization_constant * VT::Ones(n,1); + } + weighted_two_sided_barrier() { vdim = 1; } + + VT gradient(VT const &x) { + return w.cwiseQuotient(ub - x) - w.cwiseQuotient(x - lb); + } + + VT hessian(VT const &x) { + VT d = w.cwiseQuotient((ub - x).cwiseProduct((ub - x))) + + w.cwiseQuotient((x - lb).cwiseProduct((x - lb))); + return d + extraHessian; + } + MT hessian(MT const &x){ + MT d = (((- x).colwise()+ub).cwiseProduct(((- x).colwise()+ub))).cwiseInverse() + + ((x.colwise() - lb).cwiseProduct((x.colwise() - lb))).cwiseInverse(); + return w.asDiagonal()*d + extraHessian; + } + VT tensor(VT const &x) { + VT d = 2 * w.cwiseQuotient(((ub - x).cwiseProduct((ub - x))).cwiseProduct((ub - x))) - + 2 * w.cwiseQuotient(((x - lb).cwiseProduct((x - lb))).cwiseProduct((x - lb))); + return d; + } + MT tensor(MT const &x) { + MT d = 2 * ((((-x).colwise()+ub).cwiseProduct(((-x).colwise()+ub))).cwiseProduct(((-x).colwise()+ub))).cwiseInverse() - + 2 * (((x.colwise() - lb).cwiseProduct(( x.colwise() - lb))).cwiseProduct(( x.colwise() - lb))).cwiseInverse(); + return w.asDiagonal()*d; + } + MT quadratic_form_gradient(MT const &x, MT const &u) { + // Output the -grad of u' (hess phi(x)) u. + return (u.cwiseProduct(u)).cwiseProduct(tensor(x)); + } + VT quadratic_form_gradient(VT const &x, VT const &u) { + // Output the -grad of u' (hess phi(x)) u. + + return (u.cwiseProduct(u)).cwiseProduct(tensor(x)); + } + NT step_size(VT const &x, VT const &v) { + // Output the maximum step size from x with direction v or -v. + + // check positive direction + VT temp = (v.array() > 0).select((ub - x).cwiseQuotient(v), max_step); + NT t1 = temp.minCoeff(); + + // check negative direction + temp = (v.array() < 0).select((lb - x).cwiseQuotient(v), max_step); + NT t2 = temp.minCoeff(); + + return std::min(t1, t2); + } + VT boundary_distance(VT const &x) { + // Output the distance of x with its closest boundary for each + // coordinate + return ((x - lb).cwiseMin(ub - x)).cwiseAbs(); + } + + bool feasible(VT const &x) { + return (x.array() > lb.array() && x.array() < ub.array()).all(); + } + VT feasible(MT const &x) { + VT result=VT::Ones(x.cols()); + for(int i=0;i lb.array() && x.col(i).array() < ub.array()).all(); + } + return result; + } + void set_bound(VT const &_lb, VT const &_ub) { + + lb = _lb; + ub = _ub; + n = lb.rows(); + extraHessian = regularization_constant * VT::Ones(n); + int x1 = 0, x2 = 0, x3 = 0; + for (int i = 0; i < n; i++) { + if (lb(i) == -inf) { + upperIdx.push_back(i); + x1++; + } + if (ub(i) == inf) { + lowerIdx.push_back(i); + x2++; + } + if (ub(i) == inf && lb(i) == -inf) { + freeIdx.push_back(i); + } + } + + VT c = (ub + lb) / 2; + VT bias1=VT::Ones(x2, 1) * unbounded_center_coord; + saxpy(c,lb,bias1,lowerIdx,lowerIdx); + VT bias2=-VT::Ones(x1, 1) * unbounded_center_coord; + saxpy(c,ub,bias2,upperIdx,upperIdx); + set(c, freeIdx, 0.0); + + center = c; + } + + std::pair boundary(VT const &x) { + // Output the normal at the boundary around x for each barrier. + // Assume: only 1 vector is given + VT A = VT::Ones(x.rows(), 1); + VT b = ub; + + b = (x.array() < center.array()).select(-lb, b); + A = (x.array() < center.array()).select(-A, A); + + return std::make_pair(A, b); + } +}; +#endif diff --git a/src/volesti/include/preprocess/estimate_L_smooth_parameter.hpp b/src/volesti/include/preprocess/estimate_L_smooth_parameter.hpp new file mode 100644 index 00000000..8f5a1e89 --- /dev/null +++ b/src/volesti/include/preprocess/estimate_L_smooth_parameter.hpp @@ -0,0 +1,73 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + + +#ifndef ESTIMATE_L_SMOOTH_PARAMETER_HPP +#define ESTIMATE_L_SMOOTH_PARAMETER_HPP + +#include "random_walks/random_walks.hpp" + +template +< + typename WalkTypePolicy = AcceleratedBilliardWalk, + typename Polytope, + typename Point, + typename NegativeGradientFunctor, + typename RandomNumberGenerator +> +double estimate_L_smooth(Polytope &P, Point &p, unsigned int const& walk_length, + NegativeGradientFunctor F, RandomNumberGenerator &rng) +{ + typedef typename Point::FT NT; + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > RandomWalk; + + P.ComputeInnerBall(); + + unsigned int d = P.dimension(); + unsigned int rnum = 5 * d; + std::vector randPoints(1); + std::vector vecPoint1; + std::vector vecPoint2; + std::vector< std::vector > listOfPoints; + Point F1; + + RandomWalk walk(P, p, rng); + for (unsigned int i=0; i::lowest(), Ltemp; + + for (auto pit=listOfPoints.begin(); pit!=(listOfPoints.end()-1); ++pit) + { + F1 = F(1, *pit, 0); + + for (auto qit=(pit+1); qit!=listOfPoints.end(); ++qit) + { + vecPoint2 = *qit; + Ltemp = (F1 - F(1, *qit, 0)).length() / ((*pit)[0] - (*qit)[0]).length(); + + if (Ltemp > L) + { + L = Ltemp; + } + } + } + return L; +} + + +#endif diff --git a/src/volesti/include/preprocess/max_inscribed_ball.hpp b/src/volesti/include/preprocess/max_inscribed_ball.hpp new file mode 100644 index 00000000..6fe70e9a --- /dev/null +++ b/src/volesti/include/preprocess/max_inscribed_ball.hpp @@ -0,0 +1,216 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + + +#ifndef MAX_INNER_BALL +#define MAX_INNER_BALL + +/* + This implmentation computes the largest inscribed ball in a given convex polytope P. + The polytope has to be given in H-representation P = {x | Ax <= b} and the rows of A + has to be normalized. It solves the Linear program: max t, s.t. Ax + t*e <= b, where + e is the vector of ones. + + The implementation is based on Yin Zhang's Matlab implementation in https://github.com/Bounciness/Volume-and-Sampling/blob/1c7adfb46c2c01037e625db76ff00e73616441d4/external/mve11/mve_cobra/mve_presolve_cobra.m + + Input: matrix A, vector b such that the polytope P = {x | Ax<=b} + tolerance parameter tol + + Output: center of the ball x + radius r +*/ + +template +void calcstep(MT const& A, MT const& A_trans, MT const& R, VT &s, + VT &y, VT &r1, VT const& r2, NT const& r3, VT &r4, + VT &dx, VT &ds, NT &dt, VT &dy, VT &tmp, VT &rhs) +{ + int m = A.rows(), n = A.cols(); + NT *vec_iter1 = tmp.data(), *vec_iter2 = y.data(), *vec_iter3 = s.data(), + *vec_iter4 = r1.data(), *vec_iter5 = r4.data(); + for (int i = 0; i < m; ++i) { + *vec_iter1 = ((*vec_iter4) / (*vec_iter2) - (*vec_iter5)) / (*vec_iter3); + vec_iter1++; vec_iter2++; vec_iter3++; vec_iter4++; vec_iter5++; + } + + rhs.block(0,0,n,1).noalias() = r2 + A_trans * tmp; + rhs(n) = r3 + tmp.sum(); + VT dxdt = R.colPivHouseholderQr().solve(R.transpose().colPivHouseholderQr().solve(rhs)); + + dx = dxdt.block(0,0,n,1); + dt = dxdt(n); + ds.noalias() = r1 - A*dx - VT::Ones(m) * dt; + vec_iter1 = dy.data(); vec_iter2 = r4.data(); vec_iter3 = y.data(); + vec_iter4 = ds.data(); vec_iter5 = s.data(); + + for (int i = 0; i < m; ++i) { + *vec_iter1 = ((*vec_iter2) - (*vec_iter3) * (*vec_iter4)) / (*vec_iter5); + vec_iter1++; vec_iter2++; vec_iter3++; vec_iter4++; vec_iter5++; + } +} + + +template +std::tuple max_inscribed_ball(MT const& A, VT const& b, unsigned int maxiter, NT tol) +{ + int m = A.rows(), n = A.cols(); + bool converge; + + NT bnrm = b.norm(); + VT o_m = VT::Zero(m), o_n = VT::Zero(n), e_m = VT::Ones(m); + + VT x = o_n, y = e_m / m; + NT t = b.minCoeff() - 1.0; + VT s = b - e_m * t; + + VT dx = o_n; + VT dxc = dx, ds = o_m; + VT dsc = ds, dy = o_m, mu_ds_dy(m), tmp(m), rhs(n + 1); + VT dyc = dy, r1(m), r2(n), r4(m), r23(n + 1), AtDe(n), d(m); + + NT dt = NT(0), dtc = NT(0), tau, sigma0 = 0.2, r3, gap, + prif, drif, rgap, total_err, alphap, alphad, + ratio, sigma, mu, t_prev = 1000.0 * t + 100.0; + + NT const tau0 = 0.995, power_num = 5.0 * std::pow(10.0, 15.0); + NT *vec_iter1, *vec_iter2, *vec_iter3, *vec_iter4; + + MT B(n + 1, n + 1), AtD(n, m), R(n + 1, n + 1), + eEye = std::pow(10.0, -14.0) * MT::Identity(n + 1, n + 1), + A_trans = A.transpose(); + + for (unsigned int i = 0; i < maxiter; ++i) { + + // KKT residuals + r1.noalias() = b - (A * x + s + t * e_m); + r2.noalias() = -A_trans * y; + r3 = 1.0 - y.sum(); + r4 = -s.cwiseProduct(y); + + r23.block(0, 0, n, 1) = r2; + r23(n) = r3; + gap = -r4.sum(); + + // relative residual norms and gap + prif = r1.norm() / (1.0 + bnrm); + drif = r23.norm() / 10.0; + rgap = std::abs(b.dot(y) - t) / (1.0 + std::abs(t)); + total_err = std::max(prif, drif); + total_err = std::max(total_err, rgap); + + // progress output & check stopping + if (total_err < tol || ( t > 0 && ( (std::abs(t - t_prev) <= tol * t && std::abs(t - t_prev) <= tol * t_prev) + || (t_prev >= (1.0 - tol) * t && i > 0) + || (t <= (1.0 - tol) * t_prev && i > 0) ) ) ) + { + //converged + converge = true; + break; + } + + if (dt > 1000.0 * bnrm || t > 1000000.0 * bnrm) + { + //unbounded + converge = false; + break; + } + + // Shur complement matrix + vec_iter1 = d.data(); + vec_iter3 = s.data(); + vec_iter2 = y.data(); + for (int j = 0; j < m; ++j) { + *vec_iter1 = std::min(power_num, (*vec_iter2) / (*vec_iter3)); + AtD.col(j).noalias() = A_trans.col(j) * (*vec_iter1); + vec_iter1++; + vec_iter3++; + vec_iter2++; + } + + AtDe.noalias() = AtD * e_m; + B.block(0, 0, n, n).noalias() = AtD * A; + B.block(0, n, n, 1).noalias() = AtDe; + B.block(n, 0, 1, n).noalias() = AtDe.transpose(); + B(n, n) = d.sum(); + B.noalias() += eEye; + + // Cholesky decomposition + Eigen::LLT lltOfB(B); + R = lltOfB.matrixL().transpose(); + + // predictor step & length + calcstep(A, A_trans, R, s, y, r1, r2, r3, r4, dx, ds, dt, dy, tmp, rhs); + + alphap = -1.0; + alphad = -1.0; + vec_iter1 = ds.data(); + vec_iter2 = s.data(); + vec_iter3 = dy.data(); + vec_iter4 = y.data(); + + for (int j = 0; j < m; ++j) { + alphap = std::min(alphap, (*vec_iter1) / (*vec_iter2)); + alphad = std::min(alphad, (*vec_iter3) / (*vec_iter4)); + vec_iter1++; + vec_iter2++; + vec_iter3++; + vec_iter4++; + } + alphap = -1.0 / alphap; + alphad = -1.0 / alphad; + + // determine mu + ratio = (s + alphap * ds).dot((y + alphad * dy)) / gap; + sigma = std::min(sigma0, ratio * ratio); + mu = (sigma * gap) / NT(m); + + // corrector and combined step & length + mu_ds_dy.noalias() = e_m * mu - ds.cwiseProduct(dy); + calcstep(A, A_trans, R, s, y, o_m, o_n, 0.0, mu_ds_dy, dxc, dsc, dtc, dyc, tmp, rhs); + + dx += dxc; + ds += dsc; + dt += dtc; + dy += dyc; + + alphap = -0.5; + alphad = -0.5; + vec_iter1 = ds.data(); + vec_iter2 = s.data(); + vec_iter3 = dy.data(); + vec_iter4 = y.data(); + + for (int j = 0; j < m; ++j) { + alphap = std::min(alphap, (*vec_iter1) / (*vec_iter2)); + alphad = std::min(alphad, (*vec_iter3) / (*vec_iter4)); + vec_iter1++; + vec_iter2++; + vec_iter3++; + vec_iter4++; + } + alphap = -1.0 / alphap; + alphad = -1.0 / alphad; + + // update iterates + tau = std::max(tau0, 1.0 - gap / NT(m)); + alphap = std::min(1.0, tau * alphap); + alphad = std::min(1.0, tau * alphad); + + x += alphap * dx; + s += alphap * ds; + t_prev = t; + t += alphap * dt; + y += alphad * dy; + } + + std::tuple result = std::make_tuple(x, t, converge); + return result; +} + +#endif diff --git a/src/volesti/include/preprocess/max_inscribed_ellipsoid.hpp b/src/volesti/include/preprocess/max_inscribed_ellipsoid.hpp new file mode 100644 index 00000000..ebdb261e --- /dev/null +++ b/src/volesti/include/preprocess/max_inscribed_ellipsoid.hpp @@ -0,0 +1,236 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2021 Vaibhav Thakkar + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. +//Contributed and/or modified by Vaibhav Thakkar, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + + +#ifndef MVE_COMPUTATION_HPP +#define MVE_COMPUTATION_HPP + +#include +#include + + +/* + Implementation of the interior point method to compute the largest inscribed ellipsoid in a + given convex polytope by "Yin Zhang, An Interior-Point Algorithm for the Maximum-Volume Ellipsoid + Problem (1999)". + + This C++ implementation is based on the Matlab implementation in https://github.com/Bounciness/Volume-and-Sampling/blob/1c7adfb46c2c01037e625db76ff00e73616441d4/external/mve11/mve_cobra/mve_solver_cobra.m + + The implmentation computes the largest inscribed ellipsoid {x | x = y + Es, ||s|| = 1} + + Input: matrix A, vector b such that the polytope P = {x | Ax<=b} + interior point x0 + tolerance parameters tol, reg + + Output: center of the ellipsoid y + matrix V = E_transpose * E +*/ + +// using Custom_MT as to deal with both dense and sparse matrices, MT will be the type of result matrix +template +std::pair, bool> max_inscribed_ellipsoid(Custom_MT A, VT b, VT const& x0, + unsigned int const& maxiter, + NT const& tol, NT const& reg) +{ + typedef Eigen::DiagonalMatrix Diagonal_MT; + + int m = A.rows(), n = A.cols(); + bool converged = false; + + NT bnrm = b.norm(), + last_r1 = std::numeric_limits::lowest(), + last_r2 = std::numeric_limits::lowest(), + prev_obj = std::numeric_limits::lowest(), + gap, rmu, res, objval, r1, r2 ,r3, rel, Rel, + astep, ax, ay, az, tau; + + NT const reg_lim = std::pow(10.0, -10.0), tau0 = 0.75, minmu = std::pow(10.0, -8.0); + + NT *vec_iter1, *vec_iter2, *vec_iter3; + + VT x = VT::Zero(n), y = VT::Ones(m), bmAx = VT::Ones(m), + h(m), z(m), yz(m), yh(m), R1(n), R2(m), R3(m), y2h(m), y2h_z(m), h_z(m), + R3Dy(m), R23(m), dx(n), Adx(m), dyDy(m), dy(m), dz(m); + + VT const bmAx0 = b - A * x0, ones_m = VT::Ones(m); + + MT Q(m, m), E2(n, n), YQ(m,m), G(m,m), T(m,n), ATP(n,m), ATP_A(n,n); + Diagonal_MT Y(m); + Custom_MT YA(m, n); + + A = (ones_m.cwiseProduct(bmAx0.cwiseInverse())).asDiagonal() * A, b = ones_m; + Custom_MT A_trans = A.transpose(); + + int i = 1; + while (i <= maxiter) { + + Y = y.asDiagonal(); + + E2.noalias() = MT(A_trans * Y * A).inverse(); + + Q.noalias() = A * E2 * A_trans; + h = Q.diagonal(); + h = h.cwiseSqrt(); + + if (i == 1) { + // perform those computations only during the first iteration + NT t = bmAx.cwiseProduct(h.cwiseInverse()).minCoeff(); + y *= (1.0 / (t * t)); + h *= t; + vec_iter1 = bmAx.data(); + vec_iter2 = h.data(); + vec_iter3 = z.data(); + for (int j = 0; j < m; ++j) { + *vec_iter3 = std::max(0.1, (*vec_iter1 - (*vec_iter2))); + vec_iter1++; + vec_iter2++; + vec_iter3++; + } + Q *= (t * t); + Y = Y * (1.0 / (t * t)); + } + + yz = y.cwiseProduct(z); + yh = y.cwiseProduct(h); + + gap = yz.sum() / NT(m); // compute the gap between primal and dual solution + rmu = std::min(0.5, gap) * gap; + rmu = std::max(rmu, minmu); + + R1.noalias() = - A_trans * yh; + R2 = bmAx - h - z; + R3.noalias() = rmu * ones_m - yz; + + r1 = R1.template lpNorm(); + r2 = R2.template lpNorm(); + r3 = R3.template lpNorm(); + + res = std::max(r1, r2); + res = std::max(res, r3); + objval = std::log(E2.determinant()) / 2.0; + + Eigen::SelfAdjointEigenSolver eigensolver(E2); // E2 is positive definite matrix + // computing eigenvalues of E2 + rel = eigensolver.eigenvalues().minCoeff(); + Rel = eigensolver.eigenvalues().maxCoeff(); + + if (i % 10 == 0) { + + if (std::abs((last_r1 - r1) / std::min(NT(std::abs(last_r1)), NT(std::abs(r1)))) < 0.01 && + std::abs((last_r2 - r2) / std::min(NT(abs(last_r2)), NT(std::abs(r2)))) < 0.01 && + Rel / rel > 100.0 && + reg > reg_lim) { + converged = false; + //Stopped making progress + break; + } + last_r2 = r2; + last_r1 = r1; + } + + // stopping criterion + if ((res < tol * (1.0 + bnrm) && rmu <= minmu) || + (i > 4 && prev_obj != std::numeric_limits::lowest() && + ((std::abs(objval - prev_obj) <= tol * objval && std::abs(objval - prev_obj) <= tol * prev_obj) || + (prev_obj >= (1.0 - tol) * objval || objval <= (1.0 - tol) * prev_obj) ) ) ) { + //converged + x += x0; + converged = true; + break; + } + + prev_obj = objval; // storing the objective value of the previous iteration + YQ.noalias() = Y * Q; + G = YQ.cwiseProduct(YQ.transpose()); + y2h = 2.0 * yh; + YA = Y * A; + + vec_iter1 = y2h.data(); + vec_iter2 = z.data(); + vec_iter3 = y2h_z.data(); + for (int j = 0; j < m; ++j) { + *vec_iter3 = std::max(reg, (*vec_iter1) * (*vec_iter2)); + vec_iter1++; + vec_iter2++; + vec_iter3++; + } + + G.diagonal() += y2h_z; + h_z = h + z; + + for (int j = 0; j < n; ++j) { + T.col(j) = G.colPivHouseholderQr().solve( VT(YA.col(j).cwiseProduct(h_z)) ); + } + ATP.noalias() = MT(y2h.asDiagonal()*T - YA).transpose(); + + vec_iter1 = R3.data(); + vec_iter2 = y.data(); + vec_iter3 = R3Dy.data(); + for (int j = 0; j < m; ++j) { + *vec_iter3 = (*vec_iter1) / (*vec_iter2); + vec_iter1++; + vec_iter2++; + vec_iter3++; + } + + R23 = R2 - R3Dy; + ATP_A.noalias() = ATP * A; + ATP_A.diagonal() += ones_m * reg; + dx = ATP_A.colPivHouseholderQr().solve(R1 + ATP * R23); // predictor step + + // corrector and combined step & length + Adx.noalias() = A * dx; + dyDy = G.colPivHouseholderQr().solve(y2h.cwiseProduct(Adx-R23)); + + dy = y.cwiseProduct(dyDy); + dz = R3Dy - z.cwiseProduct(dyDy); + + vec_iter1 = Adx.data(); + vec_iter2 = bmAx.data(); + ax = -0.5; + for (int j = 0; j < m; ++j) { + ax = std::min(ax, - (*vec_iter1) / (*vec_iter2)); + vec_iter1++; + vec_iter2++; + } + + ax = -1.0 / ax; + ay = -1.0 / std::min(dyDy.minCoeff(), -0.5); + + vec_iter1 = dz.data(); + vec_iter2 = z.data(); + az = -0.5; + for (int j = 0; j < m; ++j) { + az = std::min(az, (*vec_iter1) / (*vec_iter2)); + vec_iter1++; + vec_iter2++; + } + + az = -1.0 / az; + tau = std::max(tau0, 1.0 - res); + astep = tau * std::min(std::min(1.0, ax), std::min(ay, az)); // compute the step + + // update iterates + x += astep * dx; + y += astep * dy; + z += astep * dz; + + bmAx -= astep * Adx; + + i++; + } + + return std::pair, bool>(std::pair(E2, x), converged); + +} + + +#endif diff --git a/src/volesti/include/preprocess/max_inscribed_ellipsoid_rounding.hpp b/src/volesti/include/preprocess/max_inscribed_ellipsoid_rounding.hpp new file mode 100644 index 00000000..c13d53f7 --- /dev/null +++ b/src/volesti/include/preprocess/max_inscribed_ellipsoid_rounding.hpp @@ -0,0 +1,77 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + + +#ifndef MAX_ELLIPSOID_ROUNDING_HPP +#define MAX_ELLIPSOID_ROUNDING_HPP + +#include "max_inscribed_ellipsoid.hpp" + +template +< + typename MT, + typename VT, + typename NT, + typename Polytope, + typename Point +> +std::tuple max_inscribed_ellipsoid_rounding(Polytope &P, + Point const& InnerPoint) +{ + std::pair, bool> iter_res; + iter_res.second = false; + + VT x0 = InnerPoint.getCoefficients(); + MT E, L; + unsigned int maxiter = 150, iter = 1, d = P.dimension(); + + NT R = 100.0, r = 1.0, tol = std::pow(10, -6.0), reg = std::pow(10, -4.0), round_val = 1.0; + + MT T = MT::Identity(d, d); + VT shift = VT::Zero(d); + + while (true) + { + // compute the largest inscribed ellipsoid in P centered at x0 + iter_res = max_inscribed_ellipsoid(P.get_mat(), P.get_vec(), x0, maxiter, tol, reg); + E = iter_res.first.first; + E = (E + E.transpose()) / 2.0; + E = E + MT::Identity(d, d)*std::pow(10, -8.0); //normalize E + + Eigen::LLT lltOfA(E); // compute the Cholesky decomposition of E + L = lltOfA.matrixL(); + + Eigen::SelfAdjointEigenSolver eigensolver(L); + r = eigensolver.eigenvalues().minCoeff(); + R = eigensolver.eigenvalues().maxCoeff(); + + // check the roundness of the polytope + if(((std::abs(R / r) <= 2.3 && iter_res.second) || iter >= 20) && iter>2){ + break; + } + + // shift polytope and apply the linear transformation on P + P.shift(iter_res.first.second); + shift += T * iter_res.first.second; + T = T * L; + round_val *= L.transpose().determinant(); + P.linear_transformIt(L); + + reg = std::max(reg / 10.0, std::pow(10, -10.0)); + P.normalize(); + x0 = VT::Zero(d); + + iter++; + } + + std::tuple result = std::make_tuple(T, shift, std::abs(round_val)); + return result; +} + +#endif diff --git a/src/volesti/include/preprocess/min_sampling_covering_ellipsoid_rounding.hpp b/src/volesti/include/preprocess/min_sampling_covering_ellipsoid_rounding.hpp new file mode 100644 index 00000000..1e97835d --- /dev/null +++ b/src/volesti/include/preprocess/min_sampling_covering_ellipsoid_rounding.hpp @@ -0,0 +1,123 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef MIN_ELLIPSOID_ROUNDING_HPP +#define MIN_ELLIPSOID_ROUNDING_HPP + +#include +#include "khach.h" +#include "sampling/random_point_generators.hpp" +#include "volume/sampling_policies.hpp" + +template +< + typename WalkTypePolicy, + typename MT, + typename VT, + typename Polytope, + typename Point, + typename NT, + typename RandomNumberGenerator +> +std::tuple min_sampling_covering_ellipsoid_rounding(Polytope &P, + std::pair &InnerBall, + const unsigned int &walk_length, + RandomNumberGenerator &rng) +{ + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > WalkType; + typedef RandomPointGenerator RandomPointGenerator; + + unsigned int d = P.dimension(); + std::list randPoints; //ds for storing rand points + NT ratio = 10, round_val = 1.0; + unsigned int iter = 0, j, i; + const unsigned int num_of_samples = 10*d;//this is the number of sample points will used to compute min_ellipoid + PushBackWalkPolicy push_back_policy; + + MT T = MT::Identity(d,d); + VT shift = VT::Zero(d); + + while (ratio > 6.0 && iter < 3) + { + randPoints.clear(); + if (!P.get_points_for_rounding(randPoints)) + { // If P is a V-polytope then it will store its vertices in randPoints + // If P is not a V-Polytope or number_of_vertices>20*domension + // 2. Generate the first random point in P + // Perform random walk on random point in the Chebychev ball + Point c = InnerBall.first; + NT radius = InnerBall.second; + Point p = GetPointInDsphere::apply(d, radius, rng); + p += c; + RandomPointGenerator::apply(P, p, num_of_samples, walk_length, + randPoints, push_back_policy, rng); + } + + // Store points in a matrix to call Khachiyan algorithm for the minimum volume enclosing ellipsoid + MT Ap(d, randPoints.size()); + typename std::list::iterator rpit=randPoints.begin(); + + j = 0; + for ( ; rpit!=randPoints.end(); rpit++, j++) { + for (i=0 ; idimension(); i++){ + Ap(i,j)=double((*rpit)[i]); + } + } + MT Q(d,d); //TODO: remove dependence on ublas and copy to eigen + VT c2(d); + size_t w=1000; + KhachiyanAlgo(Ap,0.01,w,Q,c2); // call Khachiyan algorithm + + MT E(d, d); + VT e(d); + + //Get ellipsoid matrix and center as Eigen objects + for(i=0; i eigensolver(E); + NT rel = std::real(eigensolver.eigenvalues()[0]); + NT Rel = std::real(eigensolver.eigenvalues()[0]); + for(i=1; i Rel) Rel = std::real(eigensolver.eigenvalues()[i]); + } + + Eigen::LLT lltOfA(E); // compute the Cholesky decomposition of E + MT L = lltOfA.matrixL(); // retrieve factor L in the decomposition + + //Shift polytope in order to contain the origin (center of the ellipsoid) + P.shift(e); + + MT L_1 = L.inverse(); + shift = shift + T * e; + T = T * L_1.transpose(); + + P.linear_transformIt(L_1.transpose()); + InnerBall = P.ComputeInnerBall(); + round_val *= L_1.determinant(); + ratio = Rel / rel; + iter++; + } + + std::tuple result = std::make_tuple(T, shift, std::abs(round_val)); + return result; +} + + +#endif // MIN_ELLIPSOID_ROUNDING_HPP diff --git a/src/volesti/include/preprocess/svd_rounding.hpp b/src/volesti/include/preprocess/svd_rounding.hpp new file mode 100644 index 00000000..e62c03d2 --- /dev/null +++ b/src/volesti/include/preprocess/svd_rounding.hpp @@ -0,0 +1,176 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + + +#ifndef SVD_ROUNDING_HPP +#define SVD_ROUNDING_HPP + + +template +< + typename WalkTypePolicy, + typename Polytope, + typename Point, + typename MT, + typename VT, + typename RandomNumberGenerator +> +void svd_on_sample(Polytope &P, Point &p, unsigned int const& num_rounding_steps, MT &V, VT &s, VT &Means, + unsigned int const& walk_length, RandomNumberGenerator &rng) +{ + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + typedef RandomPointGenerator RandomPointGenerator; + PushBackWalkPolicy push_back_policy; + + unsigned int N = num_rounding_steps; + + std::list randPoints; + MT RetMat(N, P.dimension()); + RandomPointGenerator::apply(P, p, N, walk_length, randPoints, + push_back_policy, rng); + + int jj = 0; + for (typename std::list::iterator rpit = randPoints.begin(); rpit!=randPoints.end(); rpit++, jj++) + { + RetMat.row(jj) = (*rpit).getCoefficients().transpose(); + } + + for (int i = 0; i < P.dimension(); ++i) { + Means(i) = RetMat.col(i).mean(); + } + + for (int i = 0; i < N; ++i) { + RetMat.row(i) = RetMat.row(i) - Means.transpose(); + } + + Eigen::BDCSVD svd(RetMat, Eigen::ComputeFullV); + s = svd.singularValues() / svd.singularValues().minCoeff(); + + if (s.maxCoeff() >= 2.0) { + for (int i = 0; i < s.size(); ++i) { + if (s(i) < 2.0) { + s(i) = 1.0; + } + } + V = svd.matrixV(); + } else { + s = VT::Ones(P.dimension()); + V = MT::Identity(P.dimension(), P.dimension()); + } +} + + +template +< + typename WalkTypePolicy, + typename MT, + typename VT, + typename Polytope, + typename Point, + typename NT, + typename RandomNumberGenerator +> + +std::tuple svd_rounding(Polytope &P, + std::pair &InnerBall, + const unsigned int &walk_length, + RandomNumberGenerator &rng) +{ + NT tol = 0.00000001; + NT R = std::pow(10,10), r = InnerBall.second; + + int n = P.dimension(), m = P.num_of_hyperplanes(); + + Polytope P_old(P); + + MT old_A(m,n), A = P.get_mat(), T = MT::Identity(n,n), round_mat, r_inv; + VT T_shift = VT::Zero(n), shift(n), s(n); + + Point p(n); + + bool done = false, last_round_under_p, fail; + + unsigned int tries=0, num_rounding_steps = 10 * n, rounding_samples = 0, round_it; + NT max_s, s_cutof, p_cutof, num_its, prev_max_s = std::numeric_limits::max(), + s_cutoff, p_cutoff; + MT V(n,n), S(n,n); + + while (!done) { + + T = MT::Identity(n, n); + T_shift = VT::Zero(n); + + round_it = 1; + max_s = std::numeric_limits::max(); + s_cutoff = 2.3; + p_cutoff = 10.0; + last_round_under_p = false; + fail = false; + num_its = 20; + + while (max_s > s_cutoff && round_it <= num_its) { + + p = InnerBall.first; + svd_on_sample(P, p, num_rounding_steps, V, s, + shift, walk_length, rng); + + rounding_samples = rounding_samples + num_rounding_steps; + max_s = s.maxCoeff(); + + if (max_s <= p_cutoff && max_s > s_cutoff) { + if (last_round_under_p) { + num_rounding_steps = num_rounding_steps * 2; + p = InnerBall.first; + svd_on_sample(P, p, num_rounding_steps, V, s, + shift, walk_length, rng); + max_s = s.maxCoeff(); + } else { + last_round_under_p = true; + } + } else { + last_round_under_p = false; + } + S = s.asDiagonal(); + round_mat = V * S; + r_inv = VT::Ones(n).cwiseProduct(s.cwiseInverse()).asDiagonal() * V.transpose(); + + if (round_it != 1 && max_s >= NT(4) * prev_max_s) { + fail = true; + break; + } + + round_it++; + prev_max_s = max_s; + + P.shift(shift); + P.linear_transformIt(round_mat); + InnerBall = P.ComputeInnerBall(); + T_shift += T * shift; + T = T * round_mat; + } + if (round_it <= num_its && !fail) { + done = true; + } else { + tries = tries + 1; + num_rounding_steps = num_rounding_steps * 2.0; + P = P_old; + } + } + + std::tuple result = std::make_tuple(T, shift, std::abs(T.determinant())); + return result; +} + + +#endif diff --git a/src/volesti/include/random_walks/boltzmann_hmc_walk.hpp b/src/volesti/include/random_walks/boltzmann_hmc_walk.hpp new file mode 100644 index 00000000..e69816e9 --- /dev/null +++ b/src/volesti/include/random_walks/boltzmann_hmc_walk.hpp @@ -0,0 +1,216 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLESTI_BOLTZMANN_HMC_WALK_HPP +#define VOLESTI_BOLTZMANN_HMC_WALK_HPP + +#include "generators/boost_random_number_generator.hpp" +#include "../sampling/sphere.hpp" + +/// The Hamiltonian Monte Carlo random walk, to sample from the Boltzmann distribution, i.e. e^(-c*x/T). +struct BoltzmannHMCWalk { +public: + + struct parameters {}; + parameters param; + + + /// The implementation of the walk + /// Currently implemented only for spectrahedra + /// with template specialization + ///@tparam ConvexBody a convex body + ///@tparam RandomNumberGenerator + template + struct Walk { + + /// The matrix/vector types we use + typedef typename ConvexBody::PointType Point; + typedef typename ConvexBody::MT MT; + typedef typename ConvexBody::VT VT; + typedef typename Point::FT NT; + + /// A struct containing the parameters for the random walk + struct Settings { + /// The number of points to "burn", before keeping the following as a sample + int walk_length; + /// For generating random numbers + RandomNumberGenerator randomNumberGenerator; + /// The c in the distribution + VT c; + /// The T in the distribution + NT temperature; + /// The diameter of the body + NT diameter; + /// Set the number of allowed reflections at each step: #reflections < reflectionsBound * dimension + unsigned int reflectionsBound; + /// When determining we can move d long till we reach the boundary, we walk d*dl, for numerical stability + NT dl; + + /// Constructs an object of Settings + /// \param[in] walkLength The number of points to "burn", before keeping the following as a sample + /// \param[in] rng For generating random numbers + /// \param[in] c The c in the distribution + /// \param[in] temperature The T in the distribution + /// \param[in] diameter The diameter of the convexbody + /// \param[in] reflectionsBound at each iteration allow reflectionsBound*dimension reflections at most + /// \param[in] dl approach the boundary with a factor of dl, for numerical stability + /// \return An instance of this struct + template + Settings(const int walkLength, const RandomNumberGenerator &randomNumberGenerator, const Point &c, const NT temperature, const NT diameter, + unsigned int reflectionsBound = 100, NT dl = 0.995) : walk_length(walkLength), randomNumberGenerator(randomNumberGenerator), + c(c.getCoefficients()), + temperature(temperature), + diameter(diameter), + reflectionsBound(reflectionsBound), dl(dl) {} + + Settings() {} + }; + + /// The parameters of the random walk + Settings settings; + + Walk() {} + + /// Constructor + /// \param[in] settings The settings of the random walk + Walk(Settings &settings) : settings(settings) {} + + /// Change the settings + /// \param[in] settings The settings of the random walk + void setSettings(Settings &settings) { + this->settings = settings; + } + + /// Samples random points from the convexbody from the Boltzmann distribution + /// \param[in] convexbody A convexbody + /// \param[in] interiorPoint A point in the interior of the convexbody + /// \param[in] pointsNum The number of points to sample + /// \param[out] points The list of the sampled points + /// \tparam Point class Point with NT and VT as declared above in this class + template + void apply(ConvexBody &convexbody, Point const & interiorPoint, const unsigned int pointsNum, + std::list &points) { + // store intermediate results between successive calls of methods + // of the class convexbody, to avoid repeating computations + + VT p = interiorPoint.getCoefficients(); + + // sample #pointsNum points + for (unsigned int i = 1; i <= pointsNum; ++i) { + // burn #walk_length points to get one sample + for (unsigned int j = 0; j < settings.walk_length; ++j) { + getNextPoint(convexbody, p); + } + + // add the sample in the return list + points.push_back(Point(p)); + } + } + + + /// A single step of the HMC random walk: choose a direction and walk on the trajectory for a random distance. + /// If it hits the boundary, the trajectory is reflected. If #reflections < reflectionsBound * dimension, it returns the same point + /// \param[in] convexbody A convexbody + /// \param[in, out] p An interior point, and the next point in the random walk + /// \tparam Point + template + void getNextPoint(ConvexBody &convexbody, VT &p) { + + // initialize + RandomNumberGenerator &rng = settings.randomNumberGenerator; + boost::random::uniform_real_distribution<> urdist(0, 1); + const NT dl = settings.dl; + unsigned int n = convexbody.dimension(); + int reflectionsNum = 0; + int reflectionsNumBound = settings.reflectionsBound * n; + VT previousPoint; + VT p0 = p; + + // choose a distance to walk + NT T = rng.sample_urdist() * settings.diameter; + + // The trajectory will be of the form a*t^2 + vt + p + // where a = -c / 2*temperature + // and at each reflection, v and p will change + + // crate vector a + VT a = -settings.c / (2 * settings.temperature); + + // The vector v will be a random a direction + VT v = GetDirection::apply(n, rng).getCoefficients(); + + // Begin a step of the random walk + // Also, count the reflections and respect the bound + while (reflectionsNum++ < reflectionsNumBound) { + + // we are at point p and the trajectory a*t^2 + vt + p + // find how long we can walk till we hit the boundary + NT lambda = convexbody.positiveQuadIntersection(a, v, p); + + // We just solved a quadratic polynomial eigenvalue system At^2 + Bt + C, + // where A = lmi(a) - A0, B = lmi(v) - A0 and C = lmi(p) + // and also did a linearization creating two matrices X, Y. + // For the subsequent calls, we don't have to compute these matrices from scratch, + // but can efficiently update them. + // A remains the same + // C := A*lambda^2 + B*lambda + C + // X, Y will be updated in class convexbody + // Set the flags + convexbody.set_flags(true); + + // if we can walk the remaining distance without reaching he boundary + if (T <= lambda) { + // set the new point p:= (T^2)*a + T*V + p + p += (T * T) * a + T * v; + + // update matrix C + convexbody.update_C(T); + return; + } + + // we hit the boundary and still have to walk + // don't go all the way to the boundary, for numerical stability + lambda *= dl; + + // save current and set new point + previousPoint = p; + p += (lambda * lambda) * a + lambda * v; + + // update remaining distance we must walk + T -= lambda; + + // update matrix C + convexbody.update_C(lambda); + //precomputedValues.C += (lambda * lambda) * precomputedValues.A + lambda * precomputedValues.B; + + // Set v to have the direction of the trajectory at t = lambda + // i.e. the gradient of at^2 + vt + p, for t = lambda + v += (lambda * 2) * a; + + // compute reflected direction + convexbody.compute_reflection(v, p); + } + + // if the #reflections exceeded the limit, don't move + if (reflectionsNum == reflectionsNumBound) { + p = p0; + convexbody.set_flags(false); + } + } + + /// Sets the temperature in the distribution + /// \param[in] temperature New value of temperature + void setTemperature(NT temperature) { + settings.temperature = temperature; + } + }; + +}; + +#endif //VOLESTI_BOLTZMANN_HMC_WALK_HPP diff --git a/src/volesti/include/random_walks/boundary_cdhr_walk.hpp b/src/volesti/include/random_walks/boundary_cdhr_walk.hpp new file mode 100644 index 00000000..5e6b5224 --- /dev/null +++ b/src/volesti/include/random_walks/boundary_cdhr_walk.hpp @@ -0,0 +1,91 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_BOUNDARY_CDHR_WALK_HPP +#define RANDOM_WALKS_BOUNDARY_CDHR_WALK_HPP + +#include "sampling/sphere.hpp" + +// random directions hit-and-run walk with uniform target distribution +// from boundary + +struct BCDHRWalk +{ + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope& P, Point const& p, RandomNumberGenerator& rng) + { + initialize(P, p, rng); + } + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + Point& p1, // a point to start + Point& p2, + unsigned int const& walk_length, + RandomNumberGenerator& rng) + { + std::pair bpair; + for (auto j = 0u; j < walk_length; ++j) + { + auto rand_coord_prev = _rand_coord; + _rand_coord = rng.sample_uidist(); + NT kapa = rng.sample_urdist(); + bpair = P.line_intersect_coord(_p, + _p_prev, + _rand_coord, + rand_coord_prev, + _lamdas); + _p_prev = _p; + _p.set_coord(_rand_coord, _p[_rand_coord] + bpair.first + kapa + * (bpair.second - bpair.first)); + } + p1 = _p_prev; + p2 = _p_prev; + p1.set_coord(_rand_coord, _p_prev[_rand_coord] + bpair.first); + p2.set_coord(_rand_coord, _p_prev[_rand_coord] + bpair.second); + } + + private : + + template + inline void initialize(GenericBody const& P, + Point const& p, + RandomNumberGenerator& rng) + { + _lamdas.setZero(P.num_of_hyperplanes()); + _rand_coord = rng.sample_uidist(); + NT kapa = rng.sample_urdist(); + _p = p; + std::pair bpair = P.line_intersect_coord(_p, _rand_coord, + _lamdas); + _p_prev = _p; + _p.set_coord(_rand_coord, _p[_rand_coord] + bpair.first + kapa + * (bpair.second - bpair.first)); + } + + unsigned int _rand_coord; + Point _p; + Point _p_prev; + typename Point::Coeff _lamdas; + }; + +}; + +#endif // RANDOM_WALKS_BOUNDARY_CDHR_WALK_HPP diff --git a/src/volesti/include/random_walks/boundary_rdhr_walk.hpp b/src/volesti/include/random_walks/boundary_rdhr_walk.hpp new file mode 100644 index 00000000..29d500b6 --- /dev/null +++ b/src/volesti/include/random_walks/boundary_rdhr_walk.hpp @@ -0,0 +1,85 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_BOUNDARY_RDHR_WALK_HPP +#define RANDOM_WALKS_BOUNDARY_RDHR_WALK_HPP + +#include "sampling/sphere.hpp" + +// Random directions hit-and-run walk with uniform target distribution +// from the boundary + +struct BRDHRWalk +{ + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope& P, Point const& p, RandomNumberGenerator& rng) + { + initialize(P, p, rng); + } + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + Point& p1, // a point to start + Point& p2, + unsigned int const& walk_length, + RandomNumberGenerator& rng) + { + for (auto j=0u; j::apply(P.dimension(), rng); + std::pair bpair = P.line_intersect(_p, v, _lamdas, _Av, + _lambda); + _lambda = rng.sample_urdist() * (bpair.first - bpair.second) + + bpair.second; + p1 = (bpair.first * v); + p1 += _p; + p2 = (bpair.second * v); + p2 += _p; + _p += (_lambda * v); + } + } + + private : + + template + inline void initialize(GenericBody const& P, + Point const& p, + RandomNumberGenerator& rng) + { + _lamdas.setZero(P.num_of_hyperplanes()); + _Av.setZero(P.num_of_hyperplanes()); + + Point v = GetDirection::apply(P.dimension(), rng); + std::pair bpair = P.line_intersect(p, v, _lamdas, _Av); + _lambda = rng.sample_urdist() * (bpair.first - bpair.second) + bpair.second; + _p = (_lambda * v) + p; + } + + Point _p; + NT _lambda; + typename Point::Coeff _lamdas; + typename Point::Coeff _Av; + }; + +}; + + +#endif // RANDOM_WALKS_BOUNDARY_RDHR_WALK_HPP diff --git a/src/volesti/include/random_walks/compute_diameter.hpp b/src/volesti/include/random_walks/compute_diameter.hpp new file mode 100644 index 00000000..b20aa839 --- /dev/null +++ b/src/volesti/include/random_walks/compute_diameter.hpp @@ -0,0 +1,225 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2022 Vissarion Fisikopoulos +// Copyright (c) 2018-2022 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_COMPUTE_DIAMETER_HPP +#define RANDOM_WALKS_COMPUTE_DIAMETER_HPP + +#include "convex_bodies/ball.h" +#include "convex_bodies/ballintersectconvex.h" +#include "convex_bodies/hpolytope.h" +#include "convex_bodies/spectrahedra/spectrahedron.h" +#ifndef DISABLE_LPSOLVE + #include "convex_bodies/vpolytope.h" + #include "convex_bodies/vpolyintersectvpoly.h" + #include "convex_bodies/zpolytope.h" + #include "convex_bodies/zonoIntersecthpoly.h" +#endif +#include "convex_bodies/orderpolytope.h" +#include "convex_bodies/ellipsoid.h" + + +template +struct compute_diameter +{ + template + static NT compute(GenericPolytope) {return NT(0);} +}; + + +template +struct compute_diameter> +{ + template + static NT compute(HPolytope &P) + { + return NT(2) * std::sqrt(NT(P.dimension())) * P.InnerBall().second; + } +}; + +template +struct compute_diameter> +{ + template + static NT compute(Spectrahedron &P) + { + std::pair inner_ball = P.ComputeInnerBall(); + return NT(6) * NT(P.dimension()) * inner_ball.second; + } +}; + +template +struct compute_diameter> +{ + template + static NT compute(CorrelationSpectrahedron &P) + { + std::pair inner_ball = P.getInnerBall(); + return NT(P.dimension()) * inner_ball.second; + } +}; + +template +struct compute_diameter> +{ + template + static NT compute(CorrelationSpectrahedron_MT &P) + { + std::pair inner_ball = P.getInnerBall(); + return NT(P.dimension()) * inner_ball.second; + } +}; + +#ifndef DISABLE_LPSOLVE +template +struct compute_diameter> +{ + template + static NT compute(VPolytope &P) + { + typedef typename VPolytope::MT MT; + NT diameter = NT(0), diam_iter; + MT V = P.get_mat(); + for (int i = 0; i < V.rows(); ++i) { + for (int j = 0; j < V.rows(); ++j) { + if (i != j) { + diam_iter = (V.row(i) - V.row(j)).norm(); + if (diam_iter > diameter) diameter = diam_iter; + } + } + } + return diameter; + } +}; + +template +struct compute_diameter> +{ + template + static NT compute(Zonotope &P) + { + typedef typename Zonotope::MT MT; + typedef typename Zonotope::VT VT; + + MT V = P.get_mat(); + int k = V.rows(), max_index = -1; + MT D = V.transpose() * V; + D = (D + D.transpose()) / 2.0; + Eigen::SelfAdjointEigenSolver es(D); + MT D2 = es.eigenvalues().asDiagonal(), Q = es.eigenvectors(); + + NT max_eig = NT(0); + for (int i = 0; i < P.dimension(); ++i) { + if (es.eigenvalues()[i] > max_eig) { + max_eig = es.eigenvalues()[i]; + max_index = i; + } + } + + VT max_eigvec = -1.0 * Q.col(max_index); + VT obj_fun = max_eigvec.transpose() * V.transpose(), x0(k); + + for (int j = 0; j < k; ++j) x0(j) = (obj_fun(j) < 0.0) ? -1.0 : 1.0; + + return NT(2) * (V.transpose() * x0).norm(); + } +}; + +template +struct compute_diameter, RandomNumberGenerator>> +{ + template + static NT compute(IntersectionOfVpoly, RandomNumberGenerator> &P) + { + return NT(2) * NT(P.dimension()) * P.InnerBall().second; + } +}; + +template +struct compute_diameter, HPolytope>> +{ + template + static NT compute(ZonoIntersectHPoly, HPolytope> &P) + { + typedef typename ZonoIntersectHPoly, HPolytope>::VT VT; + typedef typename ZonoIntersectHPoly, HPolytope>::MT MT; + typedef HPolytope Hpolytope; + + typedef BoostRandomNumberGenerator RandomNumberGenerator; + PushBackWalkPolicy push_back_policy; + typedef typename BCDHRWalk::template Walk + < + Hpolytope, + RandomNumberGenerator + > BCdhrWalk; + typedef BoundaryRandomPointGenerator BCdhrRandomPointGenerator; + + MT G = P.get_T().transpose(); + MT AG = P.get_mat()*G; + int k = G.cols(), d = P.dimension(); + MT eyes1(k, 2*k); + eyes1 << MT::Identity(k,k), NT(-1) * MT::Identity(k,k); + MT M1(k, 4*k); + M1 << AG.transpose(), eyes1; + MT M = M1.transpose(); + VT b = P.get_vec(); + + VT bb(4*k); + for (int i = 0; i < 4*k; ++i) bb(i) = (i < 2*k) ? b(i) : 1.0; + + Hpolytope HP(d, M, bb); + + RandomNumberGenerator rng(HP.dimension()); + + std::list randPoints; + std::pair InnerBall = HP.ComputeInnerBall(); + Point q = InnerBall.first; + BCdhrRandomPointGenerator::apply(HP, q, 4*d*d, 1, + randPoints, push_back_policy, rng); + typename std::list::iterator rpit=randPoints.begin(); + NT max_norm = NT(0), iter_norm; + for ( ; rpit!=randPoints.end(); rpit++) { + iter_norm = (G*(*rpit).getCoefficients()).norm(); + if (iter_norm > max_norm) max_norm = iter_norm; + } + return NT(2) * max_norm; + } +}; + +#endif + +template +struct compute_diameter>> +{ + template + static NT compute(BallIntersectPolytope> &P) + { + return NT(2) * P.radius(); + } +}; + +template +struct compute_diameter> +{ + template + static NT compute(OrderPolytope& P) + { + return std::sqrt(NT(P.dimension())); + } +}; + +template +struct compute_diameter, Ellipsoid > > +{ + template + static NT compute(BallIntersectPolytope, Ellipsoid>& P) + { + NT polytope_diameter = std::sqrt(NT(P.dimension())); + return std::min(polytope_diameter, (NT(2) * P.radius())); + } +}; + +#endif // RANDOM_WALKS_COMPUTE_DIAMETER_HPP diff --git a/src/volesti/include/random_walks/crhmc/additional_units/auto_tuner.hpp b/src/volesti/include/random_walks/crhmc/additional_units/auto_tuner.hpp new file mode 100644 index 00000000..66a8d51a --- /dev/null +++ b/src/volesti/include/random_walks/crhmc/additional_units/auto_tuner.hpp @@ -0,0 +1,64 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef AUTO_TUNER_HPP +#define AUTO_TUNER_HPP +#include "random_walks/crhmc/additional_units/dynamic_regularizer.hpp" +#include "random_walks/crhmc/additional_units/dynamic_step_size.hpp" +#include "random_walks/crhmc/additional_units/dynamic_weight.hpp" + +// This class is responsible for calling the additional crhmc modules for: +// modifying the weights, ode step size and regularizer factor addaptively. + +template +class auto_tuner { + using weight_tuner = dynamic_weight; + using regularizion_tuner = + dynamic_regularizer; + using step_size_tuner = dynamic_step_size; + + using Opts = typename Sampler::Opts; + +public: + Opts options; + std::unique_ptr tune_weights; + std::unique_ptr tune_regularization; + std::unique_ptr tune_step_size; + auto_tuner(Sampler &s) : + options(s.params.options) + { + if (options.DynamicWeight) { + tune_weights = std::unique_ptr(new weight_tuner(s)); + } + if (options.DynamicRegularizer) { + tune_regularization = std::unique_ptr(new regularizion_tuner(s)); + } + if (options.DynamicStepSize) { + tune_step_size = std::unique_ptr(new step_size_tuner(s)); + } + } + void updateModules(Sampler &s, RandomNumberGenerator &rng) { + if (options.DynamicWeight) { + tune_weights->update_weights(s, rng); + } + if (options.DynamicRegularizer) { + tune_regularization->update_regularization_factor(s, rng); + } + if (options.DynamicStepSize) { + tune_step_size->update_step_size(s); + } + } +}; +#endif diff --git a/src/volesti/include/random_walks/crhmc/additional_units/dynamic_regularizer.hpp b/src/volesti/include/random_walks/crhmc/additional_units/dynamic_regularizer.hpp new file mode 100644 index 00000000..81449836 --- /dev/null +++ b/src/volesti/include/random_walks/crhmc/additional_units/dynamic_regularizer.hpp @@ -0,0 +1,59 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef DYNAMIC_REGULARIZER_HPP +#define DYNAMIC_REGULARIZER_HPP +#include "Eigen/Eigen" + +/// Module for updating the extra term we add to the barrier +/// This is nessecary for any polytope with free variables +/// Part of crhmc sampler +template +class dynamic_regularizer { +public: + using NT = typename Sampler::NT; + using Point = typename Sampler::point; + using MT = Eigen::Matrix; + using Opts = typename Sampler::Opts; + int n; + int simdLen; + MT bound; + Opts &options; + MT &extraHessian; + dynamic_regularizer(Sampler &s) : + simdLen(s.simdLen), + options(s.params.options), + extraHessian(options.DynamicWeight + ? s.solver->ham.weighted_barrier->extraHessian + : s.solver->ham.barrier->extraHessian) + { + n = s.dim; + bound = MT::Ones(n, simdLen); + extraHessian = MT::Ones(n, simdLen); + } + + void update_regularization_factor(Sampler &s, RandomNumberGenerator &rng) { + MT x = s.x; + x = (x.cwiseAbs()).cwiseMax(1); + bound = bound.cwiseMax(x); + if ((2 / (bound.array() * bound.array()) < n * extraHessian.array()).any()) { + extraHessian = (0.5 / n) * (bound.cwiseProduct(bound)).cwiseInverse(); + s.solver->ham.forceUpdate = true; + s.solver->ham.move({s.x, s.v}); + s.v = s.get_direction_with_momentum(n, rng, s.x, MT::Zero(n, simdLen), 0, false); + } + } +}; +#endif diff --git a/src/volesti/include/random_walks/crhmc/additional_units/dynamic_step_size.hpp b/src/volesti/include/random_walks/crhmc/additional_units/dynamic_step_size.hpp new file mode 100644 index 00000000..36721196 --- /dev/null +++ b/src/volesti/include/random_walks/crhmc/additional_units/dynamic_step_size.hpp @@ -0,0 +1,118 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef DYNAMIC_STEP_SIZE_HPP +#define DYNAMIC_STEP_SIZE_HPP + +/// Module for dynamically choosing the ODE step size and the velocity momentum +/// Part of crhmc sampler +template +class dynamic_step_size { + using NT = typename Sampler::NT; + using Opts = typename Sampler::Opts; + using IVT = Eigen::Array; + using VT = Eigen::Array; + +public: + int simdLen; + IVT consecutiveBadStep; + int iterSinceShrink = 0; + VT rejectSinceShrink; + int ODEStepSinceShrink = 0; + int effectiveStep = 0; + bool warmupFinished = false; + Opts &options; + NT η + NT &momentum; + VT acceptedStep; + VT nEffectiveStep; // number of effective steps + NT accumulatedMomentum = 0; + dynamic_step_size(Sampler &s) : + simdLen(s.simdLen), + options(s.params.options), + eta(s.solver->eta), + momentum(s.params.momentum) + { + nEffectiveStep = VT::Zero(simdLen); + acceptedStep = VT::Zero(simdLen); + consecutiveBadStep = IVT::Zero(simdLen); + rejectSinceShrink = VT::Zero(simdLen); + + if (options.warmUpStep > 0) { + eta = 1e-3; + } else { + warmupFinished = true; + } + } + void update_step_size(Sampler &s) { + acceptedStep = acceptedStep + s.prob.array(); + accumulatedMomentum = s.prob.mean() * momentum * accumulatedMomentum + eta; + Eigen::Matrix accept = (s.accept.template cast()); + nEffectiveStep = nEffectiveStep + eta * accumulatedMomentum * accept.array(); + IVT bad_step = IVT::Zero(simdLen); + if (s.solver->num_steps == options.maxODEStep) { + bad_step += 1; + } else { + bad_step = (s.prob.array() < 0.5).select(1, IVT::Zero(simdLen)); + } + consecutiveBadStep = bad_step * consecutiveBadStep + bad_step; + + NT warmupRatio = nEffectiveStep.mean() / options.warmUpStep; + if (warmupRatio < 1 && !warmupFinished && + consecutiveBadStep.maxCoeff() < options.maxConsecutiveBadStep) { + eta = options.initialStep * std::min(warmupRatio + 1e-2, 1.0); + momentum = 1 - std::min(1.0, eta / options.effectiveStepSize); + return; + } + + if (!warmupFinished) { + acceptedStep = VT::Zero(simdLen); + nEffectiveStep = VT::Zero(simdLen); + warmupFinished = true; + } + + iterSinceShrink++; + rejectSinceShrink += 1 - s.prob.array(); + ODEStepSinceShrink += s.solver->num_steps; + + int shrink = 0; + NT shiftedIter = iterSinceShrink + 20 / (1 - momentum); + + NT targetProbability = std::pow((1.0 - momentum), (2 / 3)) / 4; + if (rejectSinceShrink.maxCoeff() > targetProbability * shiftedIter|| + consecutiveBadStep.maxCoeff() > options.maxConsecutiveBadStep || + ODEStepSinceShrink > options.targetODEStep * shiftedIter) { + shrink = 1; + } + + if (shrink == 1) { + iterSinceShrink = 0; + ODEStepSinceShrink = 0; + rejectSinceShrink = VT::Zero(simdLen); + consecutiveBadStep = IVT::Zero(simdLen); + + eta /= options.shrinkFactor; + momentum = 1 - std::min(0.999, eta / options.effectiveStepSize); + + if (eta < options.minStepSize) { + s.P.terminate=true; + s.P.terminate_message="Algorithm fails to converge even with step size h = "+std::to_string(eta)+"\n"; + } + } + + iterSinceShrink = iterSinceShrink + 1; + } +}; +#endif diff --git a/src/volesti/include/random_walks/crhmc/additional_units/dynamic_weight.hpp b/src/volesti/include/random_walks/crhmc/additional_units/dynamic_weight.hpp new file mode 100644 index 00000000..6b943377 --- /dev/null +++ b/src/volesti/include/random_walks/crhmc/additional_units/dynamic_weight.hpp @@ -0,0 +1,76 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef DYNAMIC_WEIGHT_HPP +#define DYNAMIC_WEIGHT_HPP + +/// Class responsible for updating the weights of the barrier +/// Part of crhmc sampler +template +class dynamic_weight { + using NT = typename Sampler::NT; + using Point = typename Sampler::point; + using VT = Eigen::Matrix; + using MT = Eigen::Matrix; + using IVT = Eigen::Array; + using Opts = typename Sampler::Opts; + +public: + int simdLen; + IVT consecutiveBadStep; + int n; + VT &w; + Opts options; + dynamic_weight(Sampler &s) + : simdLen(s.simdLen), w(s.solver->ham.weighted_barrier->w), options(s.params.options) + { + n = s.dim; + consecutiveBadStep = IVT::Zero(simdLen); + } + // If we have consecutive bad steps update the weights with + // the help of the leverage scores. + void update_weights(Sampler &s, RandomNumberGenerator &rng) + { + IVT bad_step = IVT::Zero(simdLen); + if (s.solver->num_steps == options.maxODEStep) { + bad_step += 1; + } else { + bad_step = (s.prob.array() < 0.5).select(1, IVT::Zero(simdLen)); + } + NT threshold; + consecutiveBadStep = bad_step * consecutiveBadStep + bad_step; + + if (s.accept.sum() < simdLen) { + VT lsc = s.solver->ham.lsc.colwise().maxCoeff().transpose(); + /*The more bad steps in a row we have the higher we want the threshold to be + In order to change w more drasticaly according to the leverage scores. + So if we have more than 2 bad steps in a row we elect to set the threshold to 4 + else to 16. Not many changes will be possible as the w should be upperbounded by 1*/ + if (consecutiveBadStep.maxCoeff() > 2) { + threshold = 4; + } else { + threshold = 16; + } + bool changed = (lsc.array() > threshold * w.array()).any(); + if (changed) { + w = (lsc.array() > threshold * w.array()).select((w * threshold).cwiseMin(1), w); + s.solver->ham.forceUpdate = true; + s.solver->ham.move({s.x, s.v}); + s.v = s.get_direction_with_momentum(n, rng, s.x, MT::Zero(n, simdLen), false); + } + } + } +}; +#endif diff --git a/src/volesti/include/random_walks/crhmc/crhmc_walk.hpp b/src/volesti/include/random_walks/crhmc/crhmc_walk.hpp new file mode 100644 index 00000000..21ed8d6b --- /dev/null +++ b/src/volesti/include/random_walks/crhmc/crhmc_walk.hpp @@ -0,0 +1,245 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef CRHMC_WALK_HPP +#define CRHMC_WALK_HPP +#include "generators/boost_random_number_generator.hpp" +#include "ode_solvers/ode_solvers.hpp" +#include "random_walks/crhmc/additional_units/auto_tuner.hpp" +#include "random_walks/gaussian_helpers.hpp" +#include +struct CRHMCWalk { + template + < + typename NT, + typename OracleFunctor + > + struct parameters { + using Opts = opts; + NT epsilon; // tolerance in mixing + NT eta = 0.2; // step size + NT momentum; + NT effectiveStepSize = 1; + Opts &options; + parameters(OracleFunctor const &F, + unsigned int dim, + Opts &user_options, + NT epsilon_ = 2) : + options(user_options) + { + epsilon = epsilon_; + eta = 1.0 / (dim * sqrt(F.params.L)); + momentum = 1 - std::min(1.0, eta / effectiveStepSize); + } + }; + + template + < + typename Point, + typename Polytope, + typename RandomNumberGenerator, + typename NegativeGradientFunctor, + typename NegativeLogprobFunctor, + typename Solver + > + struct Walk { + using point = Point; + using pts = std::vector; + using NT = typename Point::FT; + using VT = Eigen::Matrix; + using MT = Eigen::Matrix; + using Sampler = CRHMCWalk::Walk; + + using Opts = typename Polytope::Opts; + using IVT = Eigen::Matrix; + + // Hyperparameters of the sampler + parameters ¶ms; + + // Numerical ODE solver + std::unique_ptr solver; + + // Dimension + unsigned int dim; + + // Polytope + Polytope &P; + + // Discarded Samples + long total_discarded_samples = 0; + long num_runs = 0; + float discard_ratio = 0; + + // Average acceptance probability + float total_acceptance_prob = 0; + float average_acceptance_prob = 0; + + // Acceptance probability + VT prob; + bool accepted; + IVT accept; + bool update_modules; + int simdLen; + // References to xs + MT x, v; + + // Proposal points + MT x_tilde, v_tilde; + + // Gradient function + NegativeGradientFunctor &F; + + // Auto tuner + std::unique_ptr>module_update; + + // Helper variables + VT H, H_tilde; + // Density exponent + NegativeLogprobFunctor &f; +#ifdef TIME_KEEPING + std::chrono::time_point start, end; + std::chrono::duration H_duration = std::chrono::duration::zero(); +#endif + Walk(Polytope &Problem, + Point &p, + NegativeGradientFunctor &neg_grad_f, + NegativeLogprobFunctor &neg_logprob_f, + parameters ¶m) : + params(param), + P(Problem), + F(neg_grad_f), + f(neg_logprob_f) + { + + dim = p.dimension(); + simdLen = params.options.simdLen; + // Starting point is provided from outside + x = p.getCoefficients() * MT::Ones(1, simdLen); + accepted = false; + // Initialize solver + solver = std::unique_ptr(new Solver(0.0, params.eta, {x, x}, F, Problem, params.options)); + v = MT::Zero(dim, simdLen); + module_update = std::unique_ptr>(new auto_tuner(*this)); + update_modules = params.options.DynamicWeight || + params.options.DynamicRegularizer || + params.options.DynamicStepSize; + }; + // Sample a new velocity with momentum + MT get_direction_with_momentum(unsigned int const &dim, + RandomNumberGenerator &rng, MT const &x, MT v, + NT momentum = 0, bool normalize = true) + { + MT z = MT(dim, simdLen); + for (int i = 0; i < simdLen; i++) + { + z.col(i) = GetDirection::apply(dim, rng, normalize).getCoefficients(); + } + solver->ham.move({x, v}); + MT sqrthess = (solver->ham.hess).cwiseSqrt(); + z = sqrthess.cwiseProduct(z); + return v * std::sqrt(momentum) + z * std::sqrt(1 - momentum); + } + // Returns the current point in the tranformed in the original space + inline MT getPoints() { return (P.T * x).colwise() + P.y; } + // Returns the current point in the tranformed in the original space + inline Point getPoint() { return Point(P.T * x.col(0) + P.y); } + + inline MT masked_choose(MT &x, MT &x_tilde, IVT &accept) { + return accept.transpose().replicate(x.rows(), 1).select(x_tilde, x); + } + inline void disable_adaptive(){ + update_modules=false; + } + inline void apply(RandomNumberGenerator &rng, + int walk_length = 1, + bool metropolis_filter = true) + { + num_runs++; + // Pick a random velocity with momentum + v = get_direction_with_momentum(dim, rng, x, v, params.momentum, false); + solver->set_state(0, x); + solver->set_state(1, v); + // Get proposals + solver->steps(walk_length, accepted); + x_tilde = solver->get_state(0); + v_tilde = solver->get_state(1); + if (metropolis_filter) { +#ifdef TIME_KEEPING + start = std::chrono::system_clock::now(); +#endif + // Calculate initial Hamiltonian + H = solver->ham.hamiltonian(x, v); + + // Calculate new Hamiltonian + H_tilde = solver->ham.hamiltonian(x_tilde, -v_tilde); + +#ifdef TIME_KEEPING + end = std::chrono::system_clock::now(); + H_duration += end - start; +#endif + VT feasible = solver->ham.feasible(x_tilde, + v_tilde); + prob = (1.0 < exp((H - H_tilde).array())).select(1.0, exp((H - H_tilde).array())); + prob = (feasible.array() > 0.5).select(prob, 0); + + total_acceptance_prob += prob.sum(); + VT rng_vector = VT(simdLen); + for (int i = 0; i < simdLen; i++) + { + rng_vector(i) = rng.sample_urdist(); + } + accept = (rng_vector.array() < prob.array()).select(1 * IVT::Ones(simdLen), 0 * IVT::Ones(simdLen)); + + x = masked_choose(x, x_tilde, accept); + v = -v; + v = masked_choose(v, v_tilde, accept); + total_discarded_samples += simdLen - accept.sum(); + discard_ratio = (1.0 * total_discarded_samples) / (num_runs * simdLen); + average_acceptance_prob = total_acceptance_prob / (num_runs * simdLen); + } else { + x = x_tilde; + v = v_tilde; + } + if (update_modules) { + module_update->updateModules(*this, rng); + } + } +#ifdef TIME_KEEPING + void initialize_timers() { + H_duration = std::chrono::duration::zero(); + solver->DU_duration = std::chrono::duration::zero(); + solver->approxDK_duration = std::chrono::duration::zero(); + } + template + void print_timing_information(StreamType &stream) { + stream << "---Sampling Timing Information" << std::endl; + double DU_time = solver->DU_duration.count(); + double DK_time = solver->approxDK_duration.count(); + double H_time = H_duration.count(); + double total_time = H_time + DK_time + DU_time; + stream << "Computing the Hamiltonian in time, " << H_time << " secs\n"; + stream << "Computing DU partial derivatives in time, " << DU_time + << " secs\n"; + stream << "Computing DK partial derivatives in time, " << DK_time + << " secs\n"; + stream << "H_time + DK_time + DU_time: " << total_time << "\n"; + } +#endif + }; +}; + +#endif diff --git a/src/volesti/include/random_walks/crhmc/hamiltonian.hpp b/src/volesti/include/random_walks/crhmc/hamiltonian.hpp new file mode 100644 index 00000000..0d9ef546 --- /dev/null +++ b/src/volesti/include/random_walks/crhmc/hamiltonian.hpp @@ -0,0 +1,249 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2022-2022 Ioannis Iakovidis + +// Contributed and/or modified by Ioannis Iakovidis, as part of Google Summer of +// Code 2022 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Yunbum Kook, Yin Tat Lee, Ruoqi Shen, Santosh S. Vempala. "Sampling with +// Riemannian Hamiltonian +// Monte Carlo in a Constrained Space" +#ifndef HAMILTONIAN_HPP +#define HAMILTONIAN_HPP +#include "preprocess/crhmc/two_sided_barrier.h" +#include "preprocess/crhmc/weighted_two_sided_barrier.h" +#include "PackedCSparse/PackedChol.h" +#include "preprocess/crhmc/crhmc_utils.h" +#include + +template +class Hamiltonian { + using VT = typename Polytope::VT; + using IVT = Eigen::Array; + using BVT = Eigen::Matrix; + using NT = typename Polytope::NT; + using MT = typename Polytope::MT; + using SpMat = typename Polytope::SpMat; + using Tx = FloatArray; + using CholObj = PackedChol; + using Opts = typename Polytope::Opts; + using pts = std::vector; + using Barrier = two_sided_barrier; + using WeightedBarrier = weighted_two_sided_barrier; + +public: + bool prepared = false; + bool forceUpdate = true; // Update function oracle temporary varibles + Polytope &P; + MT hess; + bool dUDx_empty = true; + MT last_dUdx; + CholObj solver; + pts xs; + MT x; + MT dfx; + MT lsc; + VT fx; + int n; + int m; + int num_runs = 0; + Barrier *barrier; + std::unique_ptr weighted_barrier; + Opts &options; + Hamiltonian(Polytope &boundaries) : + P(boundaries), + solver(CholObj(transform_format(boundaries.Asp))), + options(boundaries.options) + { + n = P.dimension(); + m = P.equations(); + x = MT::Zero(n, simdLen); + xs = {x, x}; + lsc = MT::Zero(simdLen, n); + solver.accuracyThreshold = options.solver_accuracy_threshold; + if (options.DynamicWeight) + { + weighted_barrier = + std::unique_ptr(new WeightedBarrier(P.barrier.lb, P.barrier.ub, P.w_center)); + weighted_barrier->extraHessian.resize(n, simdLen); + weighted_barrier->extraHessian = MT::Ones(n, simdLen) * options.regularization_factor; + } + barrier = &P.barrier; + barrier->extraHessian.resize(n, simdLen); + barrier->extraHessian = MT::Ones(n, simdLen) * options.regularization_factor; + } + + // Compute H(x,v) + VT hamiltonian(MT x, MT v) + { + prepare({x, v}); + pts pd = DK({x, v}); + VT K = 0.5 * (v.cwiseProduct(pd[0])).colwise().sum(); + Tx out=solver.logdet(); + VT logdet=VT(simdLen); + for (int i = 0; i < simdLen; i++) + logdet(i) = get(out, i); + VT U = ((hess.array()).log()).colwise().sum(); + U = (U + logdet) * 0.5 + fx; + VT E = U + K; + return E; + } + // Helper is nan function for vectors + template + IVT is_not_nan(MatrixType x) + { + IVT result = IVT::Ones(x.cols()); + for (int i = 0; i < x.rows(); i++) { + for (int j = 0; j < x.cols(); j++) { + if (std::isnan(x(i, j))) { + result(j) = 0; + } + } + } + return result; + } + // Test if the values of x and v are valid and if x is feasible + VT feasible(MT x, MT v) + { + VT feasible_coordinate = VT::Ones(x.cols()); + if (options.DynamicWeight) { + feasible_coordinate = weighted_barrier->feasible(x); + } else { + feasible_coordinate = barrier->feasible(x); + } + VT r = feasible_coordinate.cwiseProduct((is_not_nan(x) * is_not_nan(v)).matrix()); + return r; + } + // prepare the solver weighted by the hessian + void prepare(pts const &xs) + { + move(xs); + if (!prepared) { + MT Hinv = (hess.cwiseInverse()).transpose(); + solver.decompose((Tx *)Hinv.data()); + dUDx_empty = true; + } + prepared = true; + } + // Computation of the partial derivatives of the K term + pts DK(pts const &x_bar) + { + MT x = x_bar[0]; + MT v = x_bar[1]; + move(x_bar); + MT invHessV = v.cwiseQuotient(hess); + MT input_vector = P.Asp * invHessV; + input_vector.transposeInPlace(); + MT out_vector = MT::Zero(simdLen, m); + solver.solve((Tx *)input_vector.data(), (Tx *)out_vector.data()); + out_vector.transposeInPlace(); + MT dKdv = + invHessV - (P.Asp.transpose() * out_vector).cwiseQuotient(hess); + + MT dKdx = MT::Zero(n, simdLen); + if (options.DynamicWeight) { + dKdx = + weighted_barrier->quadratic_form_gradient(x, dKdv) / + 2; + } else { + dKdx = barrier->quadratic_form_gradient(x, dKdv) / + 2; + } + + return {dKdv, dKdx}; + } + // Approximate computation of the partial derivatives of the K term + pts approxDK(pts const &x_bar, MT &nu) + { + MT x = x_bar[0]; + MT v = x_bar[1]; + move(x_bar); + MT dUdv_b = P.Asp * (v - P.Asp.transpose() * nu).cwiseQuotient(hess); + dUdv_b.transposeInPlace(); + MT out_solver = MT(nu.cols(), nu.rows()); + solver.solve((Tx *)dUdv_b.data(), (Tx *)out_solver.data()); + nu = nu + out_solver.transpose(); + + MT dKdv = (v - P.Asp.transpose() * nu).cwiseQuotient(hess); + MT dKdx = MT::Zero(n, simdLen); + if (options.DynamicWeight) { + dKdx = + weighted_barrier->quadratic_form_gradient(x, dKdv) / + 2; + } else { + dKdx = barrier->quadratic_form_gradient(x, dKdv) / + 2; + } + return {dKdv, dKdx}; + } + // Compute the partial derivatives of one term + // This is only dependent on x and so DU/Dv=0 + pts DU(pts const &x_bar) + { + MT x = x_bar[0]; + move(x_bar); + if (!prepared || dUDx_empty) { + prepare(x_bar); + solver.leverageScoreComplement((Tx *)lsc.data()); + + if (options.DynamicWeight) { + last_dUdx = (weighted_barrier->tensor(x).cwiseProduct(lsc.transpose())) + .cwiseQuotient(2 * hess) + + dfx; + } else { + last_dUdx = + (barrier->tensor(x).cwiseProduct(lsc.transpose())).cwiseQuotient(2 * hess) + + dfx; + } + dUDx_empty = false; + } + + return {MT::Zero(n, simdLen), -last_dUdx}; + } + // Compute the computations involving only x iff x has been changed + // Else they are stored + void move(pts const &y) + { + if (y[0] == xs[0] && !forceUpdate) { + return; + } + xs = y; + x = xs[0]; + MT h; + std::tie(fx, dfx, h) = P.f_oracle(x); + if (options.DynamicWeight) { + hess = weighted_barrier->hessian(x) + h; + } else { + hess = barrier->hessian(x) + h; + } + forceUpdate = false; + prepared = false; + } + // Project x to the polytope + void project(pts &xs) { + move(xs); + MT x = xs[0]; + int m = P.Asp.rows(); + MT out_vector = MT(simdLen, m); + MT in_vector = (-P.Asp * x).colwise() + P.b; + in_vector.transposeInPlace(); + solver.solve((Tx *)in_vector.data(), (Tx *)out_vector.data()); + out_vector.transposeInPlace(); + out_vector = P.Asp.transpose() * out_vector; + xs[0] = xs[0] + (out_vector).cwiseQuotient(hess); + } + // Get the inner product of x and ds weighted by the hessian + VT x_norm(pts const &xs, pts const &dx) + { + move(xs); + MT dx_x = dx[0]; + MT r = (dx_x.cwiseProduct(dx_x)).cwiseProduct(hess); + return r.colwise().sum(); + } +}; +#endif diff --git a/src/volesti/include/random_walks/ellipsoid_walks/README.md b/src/volesti/include/random_walks/ellipsoid_walks/README.md new file mode 100644 index 00000000..ec1e5785 --- /dev/null +++ b/src/volesti/include/random_walks/ellipsoid_walks/README.md @@ -0,0 +1,7 @@ +1) This folder contains the C++ implementations -with several modifications- of the Dikin walk, + the Vaidya walk and the John walk from https://github.com/rzrsk/vaidya-walk by Raaz Dwivedi. + +2) The implemented random walks are presented in the paper of + Y. Chen, R. Dwivedi, M. J. Wainwright and B. Yu, + "Fast MCMC Sampling Algorithms on Polytopes", + Journal of Machine Learning Research, 2018. diff --git a/src/volesti/include/random_walks/ellipsoid_walks/dikin_walker.h b/src/volesti/include/random_walks/ellipsoid_walks/dikin_walker.h new file mode 100644 index 00000000..b16d997b --- /dev/null +++ b/src/volesti/include/random_walks/ellipsoid_walks/dikin_walker.h @@ -0,0 +1,157 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Original C++ code from https://github.com/rzrsk/vaidya-walk by Raaz Dwivedi. + +// Modified by Alexandros Manochis to be integrated in volesti, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// The implemented random walk is presented in the paper of +// Y. Chen, R. Dwivedi, M. J. Wainwright and B. Yu, +// "Fast MCMC Sampling Algorithms on Polytopes", +// Journal of Machine Learning Research, 2018. + +#ifndef PWALK_DIKIN_WALKER_HPP_ +#define PWALK_DIKIN_WALKER_HPP_ + +#include +#include "math_functions.h" + +/// @brief Class that defines the Dikin walk sampler +/// @tparam Dtype Number Type +template +class DikinWalker { +public: + + DikinWalker() {} + + DikinWalker(const Eigen::Matrix &initialization, const Eigen::Matrix &cons_A, const Eigen::Matrix &cons_b, const Dtype r){ + nb_dim_ = cons_A.cols(); + nb_cons_ = cons_A.rows(); + nb_curr_samples_ = 1; + initialization_ = initialization; + cons_A_ = cons_A; + cons_b_ = cons_b; + curr_sample_ = initialization; + r_ = r; + } + + // getter for radius + Dtype getRadius() { + return r_; + } + + // check whether a given point is in th polytope + bool checkInPolytope(const Eigen::Matrix &new_sample) { + return (cons_A_ * new_sample - cons_b_).maxCoeff() < 0; + } + + // getter for dimension + int getNbDim() { + return nb_dim_; + } + + // getter for nb current samples + int getNbCurrSamples() { + return nb_curr_samples_; + } + + // getter for current sample + Eigen::Matrix &getCurrSample() { + return curr_sample_; + } + + void proposal(Eigen::Matrix& new_sample){ + Eigen::Matrix gaussian_step = + Eigen::Matrix::Zero(this->nb_dim_); + sample_gaussian(this->nb_dim_, 0., 1., gaussian_step); + + // get hessian + Eigen::Matrix new_sqrt_inv_hess = + Eigen::Matrix::Zero(this->nb_dim_, this->nb_dim_); + sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess); + + new_sample = this->curr_sample_ + r_ / std::sqrt(Dtype(this->nb_dim_)) * (new_sqrt_inv_hess * gaussian_step); + } + + bool acceptRejectReverse(const Eigen::Matrix& new_sample){ + // get hessian on x + Eigen::Matrix new_sqrt_inv_hess_x = + Eigen::Matrix::Zero(this->nb_dim_, this->nb_dim_); + sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess_x); + // get hessian on y + Eigen::Matrix new_sqrt_inv_hess_y = + Eigen::Matrix::Zero(this->nb_dim_, this->nb_dim_); + sqrtInvHessBarrier(new_sample, new_sqrt_inv_hess_y); + + Dtype scale = r_/std::sqrt(Dtype(this->nb_dim_)); + Dtype p_y_to_x = gaussian_density(this->curr_sample_, new_sample, new_sqrt_inv_hess_y.inverse()/scale); + Dtype p_x_to_y = gaussian_density(new_sample, this->curr_sample_, new_sqrt_inv_hess_x.inverse()/scale); + + Dtype ar_ratio = std::min(1., p_y_to_x/p_x_to_y); + + Dtype random_num = rng_uniform(0., 1.); + // lazy version of the walk + if (random_num > ar_ratio) { + return false; + } + + return true; + } + + bool doSample(Eigen::Matrix& new_sample, const Dtype lazy = Dtype(0.5)){ + proposal(new_sample); + this->nb_curr_samples_ += 1; + // for lazy markov chain + Dtype random_num = rng_uniform(0., 1.); + // check balance and check in polytope + if (random_num < lazy && this->checkInPolytope(new_sample) && acceptRejectReverse(new_sample)){ + this->curr_sample_ = new_sample; + return true; + } else { + new_sample = this->curr_sample_; + return false; + } + } + + void sqrtInvHessBarrier(const Eigen::Matrix& new_sample, Eigen::Matrix& new_sqrt_inv_hess) + { + Eigen::Matrix inv_slack = (this->cons_b_ - this->cons_A_ * new_sample).cwiseInverse(); + + Eigen::Matrix half_hess = inv_slack.asDiagonal()* this->cons_A_; + Eigen::Matrix new_hess = half_hess.transpose() * half_hess; + + // compute eigenvectors and eigenvalues + Eigen::SelfAdjointEigenSolver > es(new_hess); + + Eigen::Matrix V = es.eigenvectors(); + Eigen::Matrix Dv = es.eigenvalues(); + new_sqrt_inv_hess = V * Dv.cwiseInverse().cwiseSqrt().asDiagonal() * V.transpose(); + } + +private: + Dtype r_; + + // Dimension + int nb_dim_; + // number of constraints + int nb_cons_; + // current sample size + int nb_curr_samples_; + // Initial vector + Eigen::Matrix initialization_; + // constraints matrix A + Eigen::Matrix cons_A_; + // constraint vector b + Eigen::Matrix cons_b_; + // Current vector + Eigen::Matrix curr_sample_; +}; + + +#endif // PWALK_DIKIN_WALKER_HPP_ diff --git a/src/volesti/include/random_walks/ellipsoid_walks/john_walker.h b/src/volesti/include/random_walks/ellipsoid_walks/john_walker.h new file mode 100644 index 00000000..fd1d25f9 --- /dev/null +++ b/src/volesti/include/random_walks/ellipsoid_walks/john_walker.h @@ -0,0 +1,204 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Original C++ code from https://github.com/rzrsk/vaidya-walk by Raaz Dwivedi. + +// Modified by Alexandros Manochis to be integrated in volesti, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// The implemented random walk is presented in the paper of +// Y. Chen, R. Dwivedi, M. J. Wainwright and B. Yu, +// "Fast MCMC Sampling Algorithms on Polytopes", +// Journal of Machine Learning Research, 2018. + +#ifndef PWALK_JOHN_WALKER_HPP_ +#define PWALK_JOHN_WALKER_HPP_ + +#include +#include +#include "math_functions.h" + + +/// @brief Class that defines the John walk sampler +/// @tparam Dtype Number Type +template +class JohnWalker { +public: + + JohnWalker() {} + + JohnWalker(const Eigen::Matrix &initialization, const Eigen::Matrix &cons_A, const Eigen::Matrix &cons_b, const Dtype r) + { + nb_dim_ = cons_A.cols(); + nb_cons_ = cons_A.rows(); + nb_curr_samples_ = 1; + initialization_ = initialization; + cons_A_ = cons_A; + cons_b_ = cons_b; + curr_sample_ = initialization; + r_ = r; + alpha_ = 1. - 1. / std::log2(2. * Dtype(cons_A.rows()) / Dtype(cons_A.cols())); + beta_ = Dtype(cons_A.cols()) / 2. / Dtype(cons_A.rows()); + curr_weight_ = Eigen::Matrix::Ones(cons_A.rows()); + } + + // getter for radius + Dtype getRadius() { + return r_; + } + + // check whether a given point is in th polytope + bool checkInPolytope(const Eigen::Matrix &new_sample) { + return (cons_A_ * new_sample - cons_b_).maxCoeff() < 0; + } + + // getter for dimension + int getNbDim() { + return nb_dim_; + } + + // getter for nb current samples + int getNbCurrSamples() { + return nb_curr_samples_; + } + + // getter for current sample + Eigen::Matrix &getCurrSample() { + return curr_sample_; + } + + void proposal(Eigen::Matrix &new_sample) { + Eigen::Matrix gaussian_step = Eigen::Matrix::Zero( + this->nb_dim_); + sample_gaussian(this->nb_dim_, 0., 1., gaussian_step); + + // get hessian + Eigen::Matrix new_sqrt_inv_hess = + Eigen::Matrix::Zero( + this->nb_dim_, this->nb_dim_); + sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess); + + new_sample = this->curr_sample_ + r_ / std::sqrt(Dtype(this->nb_dim_)) * (new_sqrt_inv_hess * gaussian_step); + } + + bool acceptRejectReverse(const Eigen::Matrix &new_sample) { + // get hessian on y + Eigen::Matrix new_sqrt_inv_hess_y = + Eigen::Matrix::Zero( + this->nb_dim_, this->nb_dim_); + + sqrtInvHessBarrier(new_sample, new_sqrt_inv_hess_y); + // get hessian on x + Eigen::Matrix new_sqrt_inv_hess_x = + Eigen::Matrix::Zero( + this->nb_dim_, this->nb_dim_); + sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess_x); + + Dtype scale = r_ / std::sqrt(Dtype(this->nb_dim_)); + Dtype p_y_to_x = gaussian_density(this->curr_sample_, new_sample, + new_sqrt_inv_hess_y.inverse() / scale); + Dtype p_x_to_y = gaussian_density(new_sample, this->curr_sample_, + new_sqrt_inv_hess_x.inverse() / scale); + + Dtype ar_ratio = std::min(1., p_y_to_x / p_x_to_y); + + Dtype random_num = rng_uniform(0., 1.); + // lazy version of the walk + if (random_num > ar_ratio) { + return false; + } + + return true; + } + + bool doSample(Eigen::Matrix &new_sample, const Dtype lazy = Dtype(0.5)) { + proposal(new_sample); + this->nb_curr_samples_ += 1; + // for lazy markov chain + Dtype random_num = rng_uniform(0., 1.); + // check balance and check in polytope + if (random_num < lazy && this->checkInPolytope(new_sample) && acceptRejectReverse(new_sample)) { + this->curr_sample_ = new_sample; + return true; + } else { + new_sample = this->curr_sample_; + return false; + } + } + + void sqrtInvHessBarrier(const Eigen::Matrix &new_sample, + Eigen::Matrix &new_sqrt_inv_hess) { + Eigen::Matrix inv_slack = (this->cons_b_ - this->cons_A_ * new_sample).cwiseInverse(); + + Eigen::Matrix half_hess = inv_slack.asDiagonal() * this->cons_A_; + + Eigen::Matrix gradient; + Eigen::Matrix score; + Eigen::Matrix weight_half_alpha; + Eigen::Matrix weight_half_hess; + Eigen::Matrix new_hess; + Eigen::Matrix new_hess_inv; + Eigen::Matrix beta_ones = + beta_ * Eigen::Matrix::Ones(this->nb_cons_); + + Eigen::Matrix next_weight = curr_weight_; + // compute scores using gradient descent + do { + curr_weight_ = next_weight; + weight_half_alpha = curr_weight_.array().pow(alpha_ / 2.); + + weight_half_hess = (weight_half_alpha.cwiseProduct(inv_slack)).asDiagonal() * this->cons_A_; + + new_hess = weight_half_hess.transpose() * weight_half_hess; + + new_hess_inv = new_hess.inverse(); + + score = ((weight_half_hess * new_hess_inv).cwiseProduct(weight_half_hess)).rowwise().sum(); + + next_weight = (0.5 * (curr_weight_ + score + beta_ones)).cwiseMax(beta_ones); + + } while ((next_weight - curr_weight_).template lpNorm() > Dtype(0.00001)); + + // compute john hessian + Eigen::Matrix john_new_hess = + half_hess.transpose() * curr_weight_.asDiagonal() * half_hess; + + // compute eigenvectors and eigenvalues + Eigen::SelfAdjointEigenSolver > es(john_new_hess); + + Eigen::Matrix V = es.eigenvectors(); + Eigen::Matrix Dv = es.eigenvalues(); + new_sqrt_inv_hess = V * Dv.cwiseInverse().cwiseSqrt().asDiagonal() * V.transpose(); + } + +private: + + Dtype alpha_; + Dtype beta_; + + Dtype r_; + + // Dimension + int nb_dim_; + // number of constraints + int nb_cons_; + // current sample size + int nb_curr_samples_; + // Initial vector + Eigen::Matrix initialization_; + // constraints matrix A + Eigen::Matrix cons_A_; + // constraint vector b + Eigen::Matrix cons_b_; + // Current vector + Eigen::Matrix curr_sample_; + + Eigen::Matrix curr_weight_; +}; + +#endif // PWALK_JOHN_WALKER_HPP_ + diff --git a/src/volesti/include/random_walks/ellipsoid_walks/math_functions.h b/src/volesti/include/random_walks/ellipsoid_walks/math_functions.h new file mode 100644 index 00000000..c1c9566f --- /dev/null +++ b/src/volesti/include/random_walks/ellipsoid_walks/math_functions.h @@ -0,0 +1,54 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Original C++ code from https://github.com/rzrsk/vaidya-walk by Raaz Dwivedi. + +// Modified by Alexandros Manochis to be integrated in volesti, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef PWALK_UTIL_MATH_FUNCTIONS_HPP_ +#define PWALK_UTIL_MATH_FUNCTIONS_HPP_ + +#include +#include +#include + +// Define random number generator type +typedef boost::mt19937 rng_t; + +template +void sample_gaussian(const int n, const Dtype a, + const Dtype sigma, Eigen::Matrix& r) { + static rng_t gen(1234567); + static boost::normal_distribution random_distribution(a, sigma); + static boost::variate_generator > + variate_generator(gen, random_distribution); + + for (int i = 0; i < n; ++i) { + r[i] = variate_generator(); + } +} + +template +Dtype rng_uniform(const Dtype a, const Dtype b) { + static rng_t gen(1234567); + static boost::uniform_real random_distribution(a, b); + static boost::variate_generator > + variate_generator(gen, random_distribution); + + return variate_generator(); +} + +template +Dtype gaussian_density(const Eigen::Matrix& x, + const Eigen::Matrix& mu, + const Eigen::Matrix& sqrt_cov) { + Eigen::Matrix c = sqrt_cov * (x - mu); + return std::exp(-0.5*c.dot(c)) * sqrt_cov.determinant(); +} + + +#endif // PWALK_UTIL_MATH_FUNTIONS_HPP_ diff --git a/src/volesti/include/random_walks/ellipsoid_walks/vaidya_walker.h b/src/volesti/include/random_walks/ellipsoid_walks/vaidya_walker.h new file mode 100644 index 00000000..6da8b5a7 --- /dev/null +++ b/src/volesti/include/random_walks/ellipsoid_walks/vaidya_walker.h @@ -0,0 +1,171 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Original C++ code from https://github.com/rzrsk/vaidya-walk by Raaz Dwivedi. + +// Modified by Alexandros Manochis to be integrated in volesti, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// The implemented random walk is presented in the paper of +// Y. Chen, R. Dwivedi, M. J. Wainwright and B. Yu, +// "Fast MCMC Sampling Algorithms on Polytopes", +// Journal of Machine Learning Research, 2018. + +#ifndef PWALK_VAIDYA_WALKER_HPP_ +#define PWALK_VAIDYA_WALKER_HPP_ + +#include +#include "math_functions.h" + + +/// @brief Class that defines the Vaidya walk sampler +/// @tparam Dtype Number Type +template +class VaidyaWalker { +public: + + VaidyaWalker() {} + + VaidyaWalker(const Eigen::Matrix &initialization, const Eigen::Matrix &cons_A, const Eigen::Matrix &cons_b, const Dtype r) + { + nb_dim_ = cons_A.cols(); + nb_cons_ = cons_A.rows(); + nb_curr_samples_ = 1; + initialization_ = initialization; + cons_A_ = cons_A; + cons_b_ = cons_b; + curr_sample_ = initialization; + r_ = r; + } + + // getter for radius + Dtype getRadius() { + return r_; + } + + // check whether a given point is in th polytope + bool checkInPolytope(const Eigen::Matrix &new_sample) { + return (cons_A_ * new_sample - cons_b_).maxCoeff() < 0; + } + + // getter for dimension + int getNbDim() { + return nb_dim_; + } + + // getter for nb current samples + int getNbCurrSamples() { + return nb_curr_samples_; + } + + // getter for current sample + Eigen::Matrix &getCurrSample() { + return curr_sample_; + } + + void proposal(Eigen::Matrix &new_sample) { + Eigen::Matrix gaussian_step = Eigen::Matrix::Zero( + this->nb_dim_); + sample_gaussian(this->nb_dim_, 0., 1., gaussian_step); + + // get hessian + Eigen::Matrix new_sqrt_inv_hess = Eigen::Matrix::Zero(this->nb_dim_, this->nb_dim_); + sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess); + + new_sample = this->curr_sample_ + r_ / std::sqrt(std::sqrt(Dtype(this->nb_dim_) * Dtype(this->nb_cons_))) + * (new_sqrt_inv_hess * gaussian_step); + } + + bool acceptRejectReverse(const Eigen::Matrix &new_sample) { + // get hessian on x + Eigen::Matrix new_sqrt_inv_hess_x = Eigen::Matrix::Zero(this->nb_dim_, this->nb_dim_); + sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess_x); + // get hessian on y + Eigen::Matrix new_sqrt_inv_hess_y = Eigen::Matrix::Zero(this->nb_dim_, this->nb_dim_); + sqrtInvHessBarrier(new_sample, new_sqrt_inv_hess_y); + + Dtype scale = r_ / std::sqrt(std::sqrt(Dtype(this->nb_dim_) * Dtype(this->nb_cons_))); + Dtype p_y_to_x = gaussian_density(this->curr_sample_, new_sample, new_sqrt_inv_hess_y.inverse() / scale); + Dtype p_x_to_y = gaussian_density(new_sample, this->curr_sample_, new_sqrt_inv_hess_x.inverse() / scale); + + Dtype ar_ratio = std::min(1., p_y_to_x / p_x_to_y); + + Dtype random_num = rng_uniform(0., 1.); + // lazy version of the walk + if (random_num > ar_ratio) { + return false; + } + + return true; + } + + bool doSample(Eigen::Matrix &new_sample, const Dtype lazy = Dtype(0.5)) { + proposal(new_sample); + this->nb_curr_samples_ += 1; + // for lazy markov chain + Dtype random_num = rng_uniform(0., 1.); + // check balance and check in polytope + if (random_num < lazy && this->checkInPolytope(new_sample) && acceptRejectReverse(new_sample)) { + this->curr_sample_ = new_sample; + return true; + } else { + new_sample = this->curr_sample_; + return false; + } + } + + void sqrtInvHessBarrier(const Eigen::Matrix &new_sample, + Eigen::Matrix &new_sqrt_inv_hess) { + Eigen::Matrix inv_slack = (this->cons_b_ - this->cons_A_ * new_sample).cwiseInverse(); + + Eigen::Matrix half_hess = inv_slack.asDiagonal() * this->cons_A_; + Eigen::Matrix new_hess = half_hess.transpose() * half_hess; + + Eigen::Matrix new_hess_inv = new_hess.inverse(); + + // compute leverage scores + // Eigen::Matrix score = ((this->cons_A_ * new_hess_inv).cwiseProduct(this->cons_A_)).rowwise().sum().cwiseProduct(inv_slack).cwiseProduct(inv_slack); + Eigen::Matrix score = ((half_hess * new_hess_inv).cwiseProduct( + half_hess)).rowwise().sum(); + + // compute vaidya hessian + Eigen::Matrix vaidya_new_hess = + half_hess.transpose() * score.asDiagonal() * half_hess + + Dtype(this->nb_dim_) / Dtype(this->nb_cons_) * new_hess; + + // compute eigenvectors and eigenvalues + Eigen::SelfAdjointEigenSolver > es(vaidya_new_hess); + + Eigen::Matrix V = es.eigenvectors(); + Eigen::Matrix Dv = es.eigenvalues(); + new_sqrt_inv_hess = V * Dv.cwiseInverse().cwiseSqrt().asDiagonal() * V.transpose(); + } + +private: + Dtype r_; + + // Dimension + int nb_dim_; + // number of constraints + int nb_cons_; + // current sample size + int nb_curr_samples_; + // Initial vector + Eigen::Matrix initialization_; + // constraints matrix A + Eigen::Matrix cons_A_; + // constraint vector b + Eigen::Matrix cons_b_; + // Current vector + Eigen::Matrix curr_sample_; +}; + +#endif // PWALK_VAIDYA_WALKER_HPP_ + diff --git a/src/volesti/include/random_walks/exponential_hamiltonian_monte_carlo_exact_walk.hpp b/src/volesti/include/random_walks/exponential_hamiltonian_monte_carlo_exact_walk.hpp new file mode 100644 index 00000000..6e5d2e00 --- /dev/null +++ b/src/volesti/include/random_walks/exponential_hamiltonian_monte_carlo_exact_walk.hpp @@ -0,0 +1,300 @@ +// VolEsti (volume computation and sampling library) +// Copyright (c) 2021 Vissarion Fisikopoulos +// Copyright (c) 2021 Apostolos Chalkis + + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_EXPONENTIAL_EXACT_HMC_WALK_HPP +#define RANDOM_WALKS_EXPONENTIAL_EXACT_HMC_WALK_HPP + +#define INSIDE_BODY_TOLLERANCE 1e-10 + +#include "sampling/sphere.hpp" + + +// Exact HMC for sampling from the Exponential distribution restricted to a convex polytope +struct ExponentialHamiltonianMonteCarloExactWalk +{ + ExponentialHamiltonianMonteCarloExactWalk(double L) + : param(L, true, 0, false) + {} + + ExponentialHamiltonianMonteCarloExactWalk(double L, unsigned int rho) + : param(L, true, rho, true) + {} + + ExponentialHamiltonianMonteCarloExactWalk() + : param(0, false, 0, false) + {} + + struct parameters + { + parameters(double L, bool set, unsigned int _rho, bool _set_rho) + : m_L(L), set_L(set), rho(_rho), set_rho(_set_rho) + {} + double m_L; + bool set_L; + unsigned int rho; + bool set_rho; + }; + + parameters param; + + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + + template + Walk(GenericPolytope &P, Point const& p, Point const& c, NT const& T, RandomNumberGenerator &rng) + { + _Len = compute_diameter + ::template compute(P); + _Ac = P.get_mat() * c.getCoefficients(); + _Temp = T; + _c = c; + _rho = 100 * P.dimension(); // upper bound for the number of reflections (experimental) + initialize(P, p, rng); + } + + template + Walk(GenericPolytope &P, Point const& p, Point const& c, NT const& T, RandomNumberGenerator &rng, + parameters const& params) + { + _Len = params.set_L ? params.m_L + : compute_diameter + ::template compute(P); + _Ac = P.get_mat() * c.getCoefficients(); + _Temp = T; + _c = c; + _rho = 100 * P.dimension(); // upper bound for the number of reflections (experimental) + initialize(P, p, rng); + } + + template + < + typename GenericPolytope + > + inline bool apply(GenericPolytope& P, + Point& p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT T; + int failures = 0, it; + Point p0 = _p; + + for (auto j=0u; j::apply(n, rng, false); + + it = 0; + while (it < _rho) + { + auto pbpair = P.quadratic_positive_intersect(_p, _v, _Ac, _Temp, _lambdas, + _Av, _lambda_prev, _facet_prev); + if (T <= pbpair.first) { + _p += ((T * T) / (-2.0*_Temp)) *_c + (T * _v); + _lambda_prev = T; + break; + } + _lambda_prev = pbpair.first; + _p += ((_lambda_prev * _lambda_prev) / (-2.0*_Temp)) *_c + (_lambda_prev * _v); + T -= _lambda_prev; + _v += (-_lambda_prev/_Temp) * _c; + P.compute_reflection(_v, _p, pbpair.second); + it++; + } + + } while (P.is_in(_p, INSIDE_BODY_TOLLERANCE) == 0); + if (it == _rho) { + _p = p0; + } + } + p = _p; + return true; + } + + + template + < + typename GenericPolytope + > + inline void get_starting_point(GenericPolytope& P, + Point const& center, + Point &q, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT radius = P.InnerBall().second; + + q = GetPointInDsphere::apply(n, radius, rng); + q += center; + initialize(P, q, rng); + + apply(P, q, walk_length, rng); + } + + + template + < + typename GenericPolytope + > + inline void parameters_burnin(GenericPolytope& P, + Point const& center, + unsigned int const& num_points, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + Point p(n); + std::vector pointset; + pointset.push_back(center); + pointset.push_back(_p); + NT rad = NT(0), max_dist, Lmax = NT(0), radius = P.InnerBall().second; + + for (int i = 0; i < num_points; i++) + { + p = GetPointInDsphere::apply(n, radius, rng); + p += center; + initialize(P, p, rng); + + apply(P, p, walk_length, rng); + max_dist = get_max_distance(pointset, p, rad); + if (max_dist > Lmax) + { + Lmax = max_dist; + } + if (2.0 * rad > Lmax) + { + Lmax = 2.0 * rad; + } + pointset.push_back(p); + } + + if (Lmax > _Len) + { + update_delta(Lmax); + } + pointset.clear(); + } + + + inline void update_delta(NT L) + { + _Len = L; + } + + +private : + + template + < + typename GenericPolytope + > + inline void initialize(GenericPolytope& P, + Point const& p, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT T; + _lambdas.setZero(P.num_of_hyperplanes()); + _Av.setZero(P.num_of_hyperplanes()); + _v = GetDirection::apply(n, rng, false); + + do { + _p = p; + T = rng.sample_urdist() * _Len; + int it = 0; + + std::pair pbpair + = P.quadratic_positive_intersect(_p, _v, _Ac, _Temp, _lambdas, _Av, _facet_prev); + if (T <= pbpair.first || pbpair.second < 0) { + _p += ((T * T) / (-2.0*_Temp)) *_c + (T * _v); + _lambda_prev = T; + return; + } + _lambda_prev = pbpair.first; + _p += ((_lambda_prev * _lambda_prev) / (-2.0*_Temp)) *_c + (_lambda_prev * _v); + _v += (-_lambda_prev/_Temp) * _c; + T -= _lambda_prev; + P.compute_reflection(_v, _p, pbpair.second); + + while (it <= _rho) + { + std::pair pbpair + = P.quadratic_positive_intersect(_p, _v, _Ac, _Temp, _lambdas, _Av, _lambda_prev, _facet_prev); + if (T <= pbpair.first || pbpair.second < 0) { + _p += ((T * T) / (-2.0*_Temp)) *_c + (T * _v); + _lambda_prev = T; + break; + } else if (it == _rho) { + _lambda_prev = rng.sample_urdist() * pbpair.first; + _p += ((_lambda_prev * _lambda_prev) / (-2.0*_Temp)) *_c + (_lambda_prev * _v); + break; + } + _lambda_prev = pbpair.first; + _p += ((_lambda_prev * _lambda_prev) / (-2.0*_Temp)) *_c + (_lambda_prev * _v); + _v += (-_lambda_prev/_Temp) * _c; + T -= _lambda_prev; + P.compute_reflection(_v, _p, pbpair.second); + it++; + } + } while (P.is_in(_p, INSIDE_BODY_TOLLERANCE) == 0); + } + + + inline double get_max_distance(std::vector &pointset, Point const& q, double &rad) + { + double dis = -1.0, temp_dis; + int jj = 0; + for (auto vecit = pointset.begin(); vecit!=pointset.end(); vecit++, jj++) + { + temp_dis = (q.getCoefficients() - (*vecit).getCoefficients()).norm(); + if (temp_dis > dis) { + dis = temp_dis; + } + if (jj == 0 && temp_dis > rad) { + rad = temp_dis; + } + } + return dis; + } + + + NT _Len; + VT _Ac; + Point _p; + Point _v; + Point _c; + NT _Temp; + NT _lambda_prev; + int _facet_prev; + unsigned int _rho; + typename Point::Coeff _lambdas; + typename Point::Coeff _Av; +}; + +}; + + +#endif // RANDOM_WALKS_EXPONENTIAL_HMC_WALK_HPP + diff --git a/src/volesti/include/random_walks/gaussian_accelerated_billiard_walk.hpp b/src/volesti/include/random_walks/gaussian_accelerated_billiard_walk.hpp new file mode 100644 index 00000000..fbf43f44 --- /dev/null +++ b/src/volesti/include/random_walks/gaussian_accelerated_billiard_walk.hpp @@ -0,0 +1,248 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2021 Vaibhav Thakkar + +// Contributed and/or modified by Vaibhav Thakkar, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_GAUSSIAN_ACCELERATED_BILLIARD_WALK_HPP +#define RANDOM_WALKS_GAUSSIAN_ACCELERATED_BILLIARD_WALK_HPP + +#include "convex_bodies/orderpolytope.h" +#include "convex_bodies/ellipsoid.h" +#include "convex_bodies/ballintersectconvex.h" +#include "generators/boost_random_number_generator.hpp" +#include "sampling/ellipsoid.hpp" +#include "random_walks/uniform_billiard_walk.hpp" + +#include "random_walks/compute_diameter.hpp" + +// Billiard walk which accelarates each step for uniform distribution and also takes into account +// the shape of the polytope for generating directions. + +struct GaussianAcceleratedBilliardWalk +{ + GaussianAcceleratedBilliardWalk(double L) + : param(L, true) + {} + + GaussianAcceleratedBilliardWalk() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_L(set) + {} + double m_L; + bool set_L; + }; + + struct update_parameters + { + update_parameters() + : facet_prev(0), hit_ball(false), inner_vi_ak(0.0), ball_inner_norm(0.0) + {} + int facet_prev; + bool hit_ball; + double inner_vi_ak; + double ball_inner_norm; + }; + + parameters param; + + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + // typedef typename Polytope::MT MT; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope& P, + Point const& p, + Ellipsoid const& E, // ellipsoid representing the Gaussian distribution + RandomNumberGenerator &rng) + { + _update_parameters = update_parameters(); + _Len = compute_diameter + ::template compute(P); + + // Removed as will be used for sparse matrices only + // _AA.noalias() = P.get_mat() * P.get_mat().transpose(); + initialize(P, p, E, rng); + } + + template + Walk(GenericPolytope& P, + Point const& p, + Ellipsoid const& E, // ellipsoid representing the Gaussian distribution + RandomNumberGenerator &rng, + parameters const& params) + { + _update_parameters = update_parameters(); + _Len = params.set_L ? params.m_L + : compute_diameter + ::template compute(P); + + // Removed as will be used for sparse matrices only + // _AA.noalias() = P.get_mat() * P.get_mat().transpose(); + initialize(P, p, E, rng); + } + + template + < + typename GenericPolytope, + typename Ellipsoid + > + inline void apply(GenericPolytope& P, + Point &p, // a point to return the result + Ellipsoid const& E, // ellipsoid representing the Gaussian distribution + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT T; + const NT dl = 0.995; + int it; + + for (auto j=0u; j::apply(n, E, rng); + NT norm_v = _v.length(); + _v /= norm_v; + + Point p0 = _p; + Point v0 = _v; + typename Point::Coeff lambdas0 = _lambdas; + typename Point::Coeff Av0 = _Av; + NT lambda_prev0 = _lambda_prev; + + it = 0; + while (it < 100*n) + { + std::pair pbpair + = P.line_positive_intersect(_p, _v, _lambdas, _Av, _lambda_prev, _update_parameters); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + break; + } + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, _update_parameters); + it++; + } + if (it == 100*n) + { + _p = p0; + _lambdas = lambdas0; + _Av = Av0; + _lambda_prev = lambda_prev0; + } + else + { + // metropolis filter + NT u_logprob = log(rng.sample_urdist()); + NT accept_logprob = 0.5 * (norm_v * norm_v) * (E.mat_mult(v0) - E.mat_mult(_v)); + // std::cout << "diff: " << (accept_logprob - u_logprob) << std::endl; + if(u_logprob > accept_logprob) { + // reject + _p = p0; + _lambdas = lambdas0; + _Av = Av0; + _lambda_prev = lambda_prev0; + } + } + } + p = _p; + } + + inline void update_delta(NT L) + { + _Len = L; + } + + private : + + template + < + typename GenericPolytope, + typename Ellipsoid + > + inline void initialize(GenericPolytope& P, + Point const& p, // a point to start + Ellipsoid const& E, // ellipsoid representing the Gaussian distribution + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + const NT dl = 0.995; + _lambdas.setZero(P.num_of_hyperplanes()); + _Av.setZero(P.num_of_hyperplanes()); + _p = p; + _v = GetGaussianDirection::apply(n, E, rng); + NT norm_v = _v.length(); + _v /= norm_v; + + NT T = -std::log(rng.sample_urdist()) * _Len; + Point p0 = _p; + int it = 0; + + std::pair pbpair + = P.line_first_positive_intersect(_p, _v, _lambdas, _Av, _update_parameters); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + return; + } + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, _update_parameters); + + while (it <= 100*n) + { + std::pair pbpair + = P.line_positive_intersect(_p, _v, _lambdas, _Av, _lambda_prev, _update_parameters); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + break; + } else if (it == 100*n) { + _lambda_prev = rng.sample_urdist() * pbpair.first; + _p += (_lambda_prev * _v); + break; + } + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, _update_parameters); + it++; + } + } + + NT _Len; + Point _p; + Point _v; + NT _lambda_prev; + // MT _AA; // Removed as will be used for sparse matrices only + update_parameters _update_parameters; + typename Point::Coeff _lambdas; + typename Point::Coeff _Av; + }; + +}; + + +#endif diff --git a/src/volesti/include/random_walks/gaussian_ball_walk.hpp b/src/volesti/include/random_walks/gaussian_ball_walk.hpp new file mode 100644 index 00000000..8c267e0f --- /dev/null +++ b/src/volesti/include/random_walks/gaussian_ball_walk.hpp @@ -0,0 +1,107 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_GAUSSIAN_BALL_WALK_HPP +#define RANDOM_WALKS_GAUSSIAN_BALL_WALK_HPP + +#include "sampling/sphere.hpp" +#include "random_walks/gaussian_helpers.hpp" + +// Ball walk with spherical Gaussian target distribution + +struct GaussianBallWalk +{ + + GaussianBallWalk(double L) + : param(L, true) + {} + + GaussianBallWalk() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_delta(set) + {} + double m_L; + bool set_delta; + }; + + parameters param; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + template + static inline NT compute_delta(GenericPolytope& P, NT const& a) + { + //return ((P.InnerBall()).second * NT(4)) / NT(P.dimension()); + return (NT(4) * (P.InnerBall()).second) / std::sqrt(std::max(NT(1), a) * NT(P.dimension())); + } + + Walk (Polytope& P, Point const& p, NT const& a, + RandomNumberGenerator &rng) + { + _delta = compute_delta(P, a); + } + + Walk (Polytope& P, + Point const& p, + NT const& a, + RandomNumberGenerator &rng, + parameters const& params) + { + _delta = params.set_delta ? params.m_L + : compute_delta(P, a); + } + + template + inline void apply(BallPolytope const& P, + Point &p, // a point to start + NT const& a_i, + unsigned int const& walk_length, + RandomNumberGenerator& rng) + { + for (auto j = 0u; j < walk_length; ++j) + { + Point y = GetPointInDsphere::apply(P.dimension(), + _delta, + rng); + y += p; + if (P.is_in(y) == -1) + { + NT f_x = eval_exp(p, a_i); + NT f_y = eval_exp(y, a_i); + NT rnd = rng.sample_urdist(); + if (rnd <= f_y / f_x) { + p = y; + } + } + } + } + + inline void update_delta(NT delta) + { + _delta = delta; + } + +private : + NT _delta; +}; + +}; + +#endif // RANDOM_WALKS_GAUSSIAN_BALL_WALK_HPP diff --git a/src/volesti/include/random_walks/gaussian_cdhr_walk.hpp b/src/volesti/include/random_walks/gaussian_cdhr_walk.hpp new file mode 100644 index 00000000..04ebedbc --- /dev/null +++ b/src/volesti/include/random_walks/gaussian_cdhr_walk.hpp @@ -0,0 +1,151 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_GAUSSIAN_CDHR_WALK_HPP +#define RANDOM_WALKS_GAUSSIAN_CDHR_WALK_HPP + +#include "sampling/sphere.hpp" +#include "generators/boost_random_number_generator.hpp" +#include "random_walks/gaussian_helpers.hpp" + +// Pick a point from the distribution exp(-a_i||x||^2) on the coordinate chord +template +< + typename NT, + typename RandomNumberGenerator +> +NT chord_random_point_generator_exp_coord(const NT &l, + const NT &u, + const NT &a_i, + RandomNumberGenerator& rng) +{ + NT r, r_val, fn, dis; + // pick from 1-dimensional gaussian if enough weight is inside polytope P + if (a_i > EXP_CHORD_TOLERENCE && u - l >= 2.0 / std::sqrt(2.0 * a_i)) + { + while (true) { + r = rng.sample_ndist();//rdist(rng2); + r = r / std::sqrt(2.0 * a_i); + if (r >= l && r <= u) { + break; + } + } + dis = r; + + // select using rejection sampling from a bounding rectangle + } + else + { + NT M = get_max_coord(l, u, a_i); + while (true) + { + r = rng.sample_urdist(); + dis = (1.0 - r) * l + r * u; + r_val = M * rng.sample_urdist(); + fn = std::exp(-a_i * dis * dis); + if (r_val < fn) + { + break; + } + } + } + return dis; +} + + +// Coordinate directions hit-and-run walk with spherical Gaussian target distribution + +struct GaussianCDHRWalk +{ + + struct parameters {}; + parameters param; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + Walk(Polytope& P, + Point const& p, + NT const& a_i, + RandomNumberGenerator &rng) + { + initialize(P, p, a_i, rng); + } + + Walk(Polytope& P, + Point const& p, + NT const& a_i, + RandomNumberGenerator& rng, + parameters&) + { + initialize(P, p, a_i, rng); + } + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + Point &p, // a point to start + NT const& a_i, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + for (auto j = 0u; j < walk_length; ++j) + { + auto rand_coord_prev = _rand_coord; + _rand_coord = rng.sample_uidist(); + std::pair bpair = + P.line_intersect_coord(_p, _p_prev, _rand_coord, + rand_coord_prev, _lamdas); + NT dis = chord_random_point_generator_exp_coord + (_p[_rand_coord] + bpair.second, + _p[_rand_coord] + bpair.first, + a_i, + rng); + _p_prev = _p; + _p.set_coord(_rand_coord, dis); + } + p = _p; + } + +private : + + template + inline void initialize(BallPolytope const& P, + Point const& p, + NT const& a_i, + RandomNumberGenerator &rng) + { + _lamdas.setZero(P.num_of_hyperplanes()); + _rand_coord = rng.sample_uidist(); + _p = p; + std::pair bpair = P.line_intersect_coord(_p, _rand_coord, _lamdas); + NT dis = chord_random_point_generator_exp_coord + (_p[_rand_coord] + bpair.second, + _p[_rand_coord] + bpair.first, + a_i, rng); + _p_prev = p; + _p.set_coord(_rand_coord, dis); + } + + unsigned int _rand_coord; + Point _p; + Point _p_prev; + typename Point::Coeff _lamdas; +}; + +}; + +#endif // RANDOM_WALKS_GAUSSIAN_CDHR_WALK_HPP diff --git a/src/volesti/include/random_walks/gaussian_hamiltonian_monte_carlo_exact_walk.hpp b/src/volesti/include/random_walks/gaussian_hamiltonian_monte_carlo_exact_walk.hpp new file mode 100644 index 00000000..8b83b053 --- /dev/null +++ b/src/volesti/include/random_walks/gaussian_hamiltonian_monte_carlo_exact_walk.hpp @@ -0,0 +1,275 @@ +// VolEsti (volume computation and sampling library) +// Copyright (c) 2021 Vissarion Fisikopoulos +// Copyright (c) 2021 Apostolos Chalkis + + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_GAUSSIAN_EXACT_HMC_WALK_HPP +#define RANDOM_WALKS_GAUSSIAN_EXACT_HMC_WALK_HPP + +#include "sampling/sphere.hpp" + + + +// Exact HMC for sampling from the spherical Gaussian distribution + +struct GaussianHamiltonianMonteCarloExactWalk +{ + GaussianHamiltonianMonteCarloExactWalk(double L, unsigned int _rho) + : param(L, true, _rho, true) + {} + + GaussianHamiltonianMonteCarloExactWalk(double L) + : param(L, true, 0, false) + {} + + GaussianHamiltonianMonteCarloExactWalk() + : param(0, false, 0, false) + {} + + + struct parameters + { + parameters(double L, bool set, unsigned int _rho, bool _set_rho) + : m_L(L), set_L(set), rho(_rho), set_rho(_set_rho) + {} + double m_L; + bool set_L; + unsigned int rho; + bool set_rho; + }; + + parameters param; + + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef typename Polytope::VT VT; + + template + Walk(GenericPolytope &P, Point const& p, NT const& a_i, RandomNumberGenerator &rng) + { + _Len = compute_diameter + ::template compute(P); + _omega = std::sqrt(NT(2) * a_i); + _rho = 100 * P.dimension(); // upper bound for the number of reflections (experimental) + initialize(P, p, a_i, rng); + } + + template + Walk(GenericPolytope &P, Point const& p, NT const& a_i, RandomNumberGenerator &rng, + parameters const& params) + { + _Len = params.set_L ? params.m_L + : compute_diameter + ::template compute(P); + _omega = std::sqrt(NT(2) * a_i); + _rho = 100 * P.dimension(); // upper bound for the number of reflections (experimental) + initialize(P, p, a_i, rng); + } + + template + < + typename GenericPolytope + > + inline void apply(GenericPolytope& P, + Point& p, + NT const& a_i, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT T; + + for (auto j=0u; j::apply(n, rng, false); + Point p0 = _p; + int it = 0; + while (it < _rho) + { + auto pbpair = P.trigonometric_positive_intersect(_p, _v, _omega, _facet_prev); + if (T <= pbpair.first) { + update_position(_p, _v, T, _omega); + break; + } + _lambda_prev = pbpair.first; + T -= _lambda_prev; + update_position(_p, _v, _lambda_prev, _omega); + P.compute_reflection(_v, _p, pbpair.second); + it++; + } + if (it == _rho){ + _p = p0; + } + } + p = _p; + } + + + template + < + typename GenericPolytope + > + inline void get_starting_point(GenericPolytope& P, + Point const& center, + Point &q, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT radius = P.InnerBall().second; + + q = GetPointInDsphere::apply(n, radius, rng); + q += center; + initialize(P, q, rng); + + apply(P, q, walk_length, rng); + } + + + template + < + typename GenericPolytope + > + inline void parameters_burnin(GenericPolytope& P, + Point const& center, + unsigned int const& num_points, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + Point p(n); + std::vector pointset; + pointset.push_back(center); + pointset.push_back(_p); + NT rad = NT(0), max_dist, Lmax = NT(0), radius = P.InnerBall().second; + + for (int i = 0; i < num_points; i++) + { + p = GetPointInDsphere::apply(n, radius, rng); + p += center; + initialize(P, p, rng); + + apply(P, p, walk_length, rng); + max_dist = get_max_distance(pointset, p, rad); + if (max_dist > Lmax) + { + Lmax = max_dist; + } + if (2.0*rad > Lmax) + { + Lmax = 2.0 * rad; + } + pointset.push_back(p); + } + + if (Lmax > _Len) + { + update_delta(Lmax); + } + pointset.clear(); + } + + inline void update_delta(NT L) + { + _Len = L; + } + +private : + + template + < + typename GenericPolytope + > + inline void initialize(GenericPolytope& P, + Point const& p, + NT const& a_i, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + _facet_prev = -1; + _p = p; + _v = GetDirection::apply(n, rng, false); + + NT T = rng.sample_urdist() * _Len; + int it = 0; + + while (it <= _rho) + { + auto pbpair + = P.trigonometric_positive_intersect(_p, _v, _omega, _facet_prev); + if (T <= pbpair.first) { + update_position(_p, _v, T, _omega); + break; + } else if (it == _rho) { + _lambda_prev = rng.sample_urdist() * pbpair.first; + update_position(_p, _v, _lambda_prev, _omega); + break; + } + _lambda_prev = pbpair.first; + update_position(_p, _v, _lambda_prev, _omega); + T -= _lambda_prev; + P.compute_reflection(_v, _p, pbpair.second); + it++; + } + } + + inline void update_position(Point &p, Point &v, NT const& T, NT const& omega) + { + NT C, Phi; + for (size_t i = 0; i < p.dimension(); i++) + { + C = std::sqrt(p[i] * p[i] + (v[i] * v[i]) / (omega * omega)); + Phi = std::atan((-v[i]) / (p[i] * omega)); + if (v[i] < 0.0 && Phi < 0.0) { + Phi += M_PI; + } else if (v[i] > 0.0 && Phi > 0.0) { + Phi -= M_PI; + } + p.set_coord(i, C * std::cos(omega * T + Phi)); + v.set_coord(i, -C * omega * std::sin(omega * T + Phi)); + } + + } + + inline double get_max_distance(std::vector &pointset, Point const& q, double &rad) + { + double dis = -1.0, temp_dis; + int jj = 0; + for (auto vecit = pointset.begin(); vecit!=pointset.end(); vecit++, jj++) + { + temp_dis = (q.getCoefficients() - (*vecit).getCoefficients()).norm(); + if (temp_dis > dis) { + dis = temp_dis; + } + if (jj == 0 && temp_dis > rad) { + rad = temp_dis; + } + } + return dis; + } + + int _facet_prev; + unsigned int _rho; + NT _Len; + Point _p; + Point _v; + NT _omega; + NT _lambda_prev; +}; + +}; + + +#endif // RANDOM_WALKS_GAUSSIAN_HMC_WALK_HPP + diff --git a/src/volesti/include/random_walks/gaussian_helpers.hpp b/src/volesti/include/random_walks/gaussian_helpers.hpp new file mode 100644 index 00000000..d1ab5429 --- /dev/null +++ b/src/volesti/include/random_walks/gaussian_helpers.hpp @@ -0,0 +1,48 @@ +#ifndef GAUSSIAN_HELPERS_HPP +#define GAUSSIAN_HELPERS_HPP + +#define EXP_CHORD_TOLERENCE 0.00000001 + +// evaluate the pdf of point p +template +NT eval_exp(Point const& p, NT const& a) +{ + return std::exp(-a * p.squared_length()); +} + + +template +NT get_max(Point const& l, Point const& u, NT const& a_i) +{ + NT res; + Point a = -1.0 * l; + Point bef = u - l; + Point b = (1.0 / std::sqrt((bef).squared_length())) * bef; + Point z = (a.dot(b) * b) + l; + NT low_bd = (l[0] - z[0]) / b[0], up_bd = (u[0] - z[0]) / b[0]; + if (low_bd * up_bd > 0) + { + //if(std::signbit(low_bd)==std::signbit(up_bd)){ + res = std::max(eval_exp(u, a_i), eval_exp(l, a_i)); + } + else + { + res = eval_exp(z, a_i); + } + + return res; +} + + +template +NT get_max_coord(NT const& l, NT const& u, NT const& a_i) +{ + const NT zero = 0; + if (l < zero && u > zero) + { + return NT(1); + } + return std::max(std::exp(-a_i * l * l), std::exp(-a_i * u * u)); +} + +#endif // GAUSSIAN_HELPERS_HPP diff --git a/src/volesti/include/random_walks/gaussian_rdhr_walk.hpp b/src/volesti/include/random_walks/gaussian_rdhr_walk.hpp new file mode 100644 index 00000000..b7a53abe --- /dev/null +++ b/src/volesti/include/random_walks/gaussian_rdhr_walk.hpp @@ -0,0 +1,118 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_GAUSSIAN_RDHR_WALK_HPP +#define RANDOM_WALKS_GAUSSIAN_RDHR_WALK_HPP + +#include "random_walks/gaussian_helpers.hpp" +#include "generators/boost_random_number_generator.hpp" + + +// Pick a point from the distribution exp(-a_i||x||^2) on the chord +template +< + typename Point, + typename NT, + typename RandomNumberGenerator +> +void chord_random_point_generator_exp(Point &lower, + Point & upper, + const NT &a_i, + Point &p, + RandomNumberGenerator& rng) +{ + NT r, r_val, fn; + Point bef = upper - lower; + // pick from 1-dimensional gaussian if enough weight is inside polytope P + if (a_i > EXP_CHORD_TOLERENCE && std::sqrt(bef.squared_length()) >= (2.0 / std::sqrt(2.0 * a_i))) + { + Point a = -1.0 * lower; + Point b = (1.0 / std::sqrt(bef.squared_length())) * bef; + Point z = (a.dot(b) * b) + lower; + NT low_bd = (lower[0] - z[0]) / b[0]; + NT up_bd = (upper[0] - z[0]) / b[0]; + while (true) { + r = rng.sample_ndist();//rdist(rng2); + r = r / std::sqrt(2.0 * a_i); + if (r >= low_bd && r <= up_bd) { + break; + } + } + p = (r * b) + z; + + // select using rejection sampling from a bounding rectangle + } else { + NT M = get_max(lower, upper, a_i); + while (true) { + r = rng.sample_urdist();//urdist(rng2); + Point pef = r * upper; + p = ((1.0 - r) * lower) + pef; + r_val = M * rng.sample_urdist();//urdist(var.rng); + fn = eval_exp(p, a_i); + if (r_val < fn) { + break; + } + } + } +} + +// Random directions hit-and-run walk with spherical Gaussian target distribution + +struct GaussianRDHRWalk +{ + + struct parameters {}; + parameters param; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + Walk(Polytope&, Point const&, NT const&, RandomNumberGenerator&) + {} + + Walk(Polytope&, Point const&, NT const&, RandomNumberGenerator&, + parameters&) + {} + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + Point &p, // a point to start + NT const& a_i, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + for (auto j = 0u; j < walk_length; ++j) + { + Point v = GetDirection::apply(p.dimension(), rng); + std::pair dbpair = P.line_intersect(p, v); + + NT min_plus = dbpair.first; + NT max_minus = dbpair.second; + Point upper = (min_plus * v) + p; + Point lower = (max_minus * v) + p; + + chord_random_point_generator_exp(lower, upper, a_i, p, rng); + } + } +}; + +}; + + +#endif // RANDOM_WALKS_GAUSSIAN_RDHR_WALK_HPP diff --git a/src/volesti/include/random_walks/hamiltonian_monte_carlo_walk.hpp b/src/volesti/include/random_walks/hamiltonian_monte_carlo_walk.hpp new file mode 100644 index 00000000..23342ea5 --- /dev/null +++ b/src/volesti/include/random_walks/hamiltonian_monte_carlo_walk.hpp @@ -0,0 +1,179 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Lee, Yin Tat, Ruoqi Shen, and Kevin Tian. "Logsmooth Gradient Concentration +// and Tighter Runtimes for Metropolized Hamiltonian Monte Carlo." +#ifndef HAMILTONIAN_MONTE_CARLO_WALK_HPP +#define HAMILTONIAN_MONTE_CARLO_WALK_HPP + + +#include "generators/boost_random_number_generator.hpp" +#include "random_walks/gaussian_helpers.hpp" +#include "ode_solvers/ode_solvers.hpp" + +struct HamiltonianMonteCarloWalk { + + template + < + typename NT, + typename OracleFunctor + > + struct parameters { + NT epsilon; // tolerance in mixing + NT eta; // step size + + parameters( + OracleFunctor const& F, + unsigned int dim, + NT epsilon_=2) + { + epsilon = epsilon_; + eta = 1.0 / (dim * sqrt(F.params.L)); + // eta = 1.0 / + // (sqrt(20 * F.params.L * pow(dim, 3))); + } + }; + + template + < + typename Point, + typename Polytope, + typename RandomNumberGenerator, + typename NegativeGradientFunctor, + typename NegativeLogprobFunctor, + typename Solver + > + struct Walk { + + typedef std::vector pts; + typedef typename Point::FT NT; + typedef std::vector bounds; + + // Hyperparameters of the sampler + parameters ¶ms; + + // Numerical ODE solver + Solver *solver; + + // Dimension + unsigned int dim; + + // Discarded Samples + long total_discarded_samples = 0; + long num_runs = 0; + float discard_ratio = 0; + + // Average acceptance probability + float total_acceptance_log_prob = 0; + float average_acceptance_log_prob = 0; + + // References to xs + Point x, v; + + // Proposal points + Point x_tilde, v_tilde; + + // Gradient function + NegativeGradientFunctor &F; + + bool accepted; + + // Helper variables + NT H, H_tilde, log_prob, u_logprob; + + // Density exponent + NegativeLogprobFunctor &f; + + Walk(Polytope *P, + Point &p, + NegativeGradientFunctor &neg_grad_f, + NegativeLogprobFunctor &neg_logprob_f, + parameters ¶m) : + params(param), + F(neg_grad_f), + f(neg_logprob_f) + { + + dim = p.dimension(); + + // Starting point is provided from outside + x = p; + + accepted = false; + + // Initialize solver + solver = new Solver(0, params.eta, pts{x, x}, F, bounds{P, NULL}); + + }; + + inline void apply(RandomNumberGenerator &rng, + int walk_length=1, + bool metropolis_filter=true) + { + num_runs++; + + // Pick a random velocity + v = GetDirection::apply(dim, rng, false); + + solver->set_state(0, x); + solver->set_state(1, v); + + // Get proposals + solver->steps(walk_length, accepted); + x_tilde = solver->get_state(0); + v_tilde = solver->get_state(1); + + if (metropolis_filter) { + // Calculate initial Hamiltonian + H = hamiltonian(x, v); + + // Calculate new Hamiltonian + H_tilde = hamiltonian(x_tilde, v_tilde); + + // Log-sum-exp trick + log_prob = H - H_tilde < 0 ? H - H_tilde : 0; + + // Decide to switch + u_logprob = log(rng.sample_urdist()); + total_acceptance_log_prob += log_prob; + if (u_logprob < log_prob) { + x = x_tilde; + accepted = true; + } + else { + total_discarded_samples++; + accepted = false; + } + } else { + x = x_tilde; + accepted = true; + } + + discard_ratio = (1.0 * total_discarded_samples) / num_runs; + average_acceptance_log_prob = total_acceptance_log_prob / num_runs; + + } + + inline NT hamiltonian(Point &pos, Point &vel) const { + return f(pos) + 0.5 * vel.dot(vel); + } + + void disable_adaptive() { + solver->disable_adaptive(); + } + + void enable_adaptive() { + solver->enable_adaptive(); + } + }; +}; + +#endif // HAMILTONIAN_MONTE_CARLO_WALK_HPP diff --git a/src/volesti/include/random_walks/langevin_walk.hpp b/src/volesti/include/random_walks/langevin_walk.hpp new file mode 100644 index 00000000..df23dbac --- /dev/null +++ b/src/volesti/include/random_walks/langevin_walk.hpp @@ -0,0 +1,153 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Shen, Ruoqi, and Yin Tat Lee. "The randomized midpoint method for +// log-concave sampling." Advances in Neural Information Processing Systems. 2019. + +#ifndef LANGEVIN_WALK_HPP +#define LANGEVIN_WALK_HPP + +#include "generators/boost_random_number_generator.hpp" +#include "random_walks/gaussian_helpers.hpp" + +struct UnderdampedLangevinWalk { + + template + < + typename NT, + typename OracleFunctor + > + struct parameters { + NT epsilon; // tolerance in mixing + NT eta; // step size + NT u; + parameters( + OracleFunctor const& F, + unsigned int dim, + NT epsilon_=1e-4) + { + epsilon = epsilon_; + u = 1.0 / F.params.L; + eta = 1.0 / (sqrt(20 * F.params.L)); + + // eta = std::min(pow(epsilon, 1.0 / 3) / + // pow(F.params.kappa, 1.0 / 6) * + // pow(log(1.0 / epsilon), - 1.0 / 6), + // pow(epsilon, 2.0 / 3) * + // pow(log(1.0 / epsilon), - 1.0 / 3)); + } + }; + + template + < + typename Point, + typename Polytope, + typename RandomNumberGenerator, + typename NegativeGradientFunctor, + typename NegativeLogprobFunctor, + typename Solver_ // Dummy template argument + > + struct Walk { + + typedef std::vector pts; + typedef typename Point::FT NT; + typedef std::vector bounds; + typedef RandomizedMipointSDESolver Solver; + + parameters ¶ms; + + // Numerical ODE solver + Solver *solver; + + unsigned int dim; + + // References to xs + Point x, v; + // Proposal points + Point x_tilde, v_tilde; + + // Gradient Function + NegativeGradientFunctor &F; + + // Density exponent + NegativeLogprobFunctor &f; + + NT H_tilde, H, log_prob, u_logprob; + + Walk(Polytope *P, + Point &initial_x, + NegativeGradientFunctor &neg_grad_f, + NegativeLogprobFunctor &neg_logprob_f, + parameters ¶m) : + params(param), + F(neg_grad_f), + f(neg_logprob_f) + { + // Starting point is provided from outside + x = initial_x; + dim = initial_x.dimension(); + v = Point(dim); + + solver = new Solver(0, params.eta, pts{x, v}, F, bounds{P, NULL}, params.u); + + }; + + inline void apply( + RandomNumberGenerator &rng, + int walk_length=1, + bool metropolis_filter=false) + { + solver->set_state(0, x); + solver->set_state(1, v); + + // Get proposals + solver->steps(walk_length, rng); + x_tilde = solver->get_state(0); + v_tilde = solver->get_state(1); + + if (metropolis_filter) { + // Calculate initial Hamiltonian + H = hamiltonian(x, v); + + // Calculate new Hamiltonian + H_tilde = hamiltonian(x_tilde, v_tilde); + + // Log-sum-exp trick + log_prob = H - H_tilde < 0 ? H - H_tilde : 0; + + // Decide to switch + u_logprob = log(rng.sample_urdist()); + if (u_logprob < log_prob) { + x = x_tilde; + v = v_tilde; + } + } else { + x = x_tilde; + v = v_tilde; + } + + } + + inline NT hamiltonian(Point &pos, Point &vel) const { + return f(pos) + 1.0 / (2 * params.u) * vel.dot(vel); + } + + void disable_adaptive() { + // TODO Implement + } + + void enable_adaptive() { + // TODO Implement + } + }; +}; + +#endif // LANGEVIN_WALK_HPP diff --git a/src/volesti/include/random_walks/multithread_walks.hpp b/src/volesti/include/random_walks/multithread_walks.hpp new file mode 100644 index 00000000..c42fba1f --- /dev/null +++ b/src/volesti/include/random_walks/multithread_walks.hpp @@ -0,0 +1,739 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Konstantinos Pallikaris, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +// THIS IS A TEMPORAL FILE. IT CONTAINS A FEW PROTOTYPES + +#ifndef RANDOM_WALKS_MULTITHREAD_WALKS_HPP +#define RANDOM_WALKS_MULTITHREAD_WALKS_HPP + +#include "sampling/sphere.hpp" +#include "generators/boost_random_number_generator.hpp" +#include "random_walks/gaussian_helpers.hpp" +#include "random_walks/gaussian_cdhr_walk.hpp" +#include "random_walks/gaussian_rdhr_walk.hpp" + + +// random directions hit-and-run walk with uniform target distribution +// from boundary + +struct BCDHRWalk_multithread +{ + template + struct thread_parameters + { + thread_parameters(unsigned int d, unsigned int m) + { + p = Point(d); + p1 = Point(d); + p2 = Point(d); + p_prev = Point(d); + lambdas.setZero(m); + } + + Point p; + Point p1; + Point p2; + Point p_prev; + unsigned int rand_coord_prev; + unsigned int rand_coord; + typename Point::Coeff lambdas; + }; + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef thread_parameters thread_parameters_; + //typedef thread_params thread_parameters_; + + template + Walk(GenericPolytope& P, thread_parameters_ ¶meters, RandomNumberGenerator& rng) + { + initialize(P, parameters, rng); + } + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + unsigned int const& walk_length, + RandomNumberGenerator& rng) + { + std::pair bpair; + for (auto j = 0u; j < walk_length; ++j) + { + params.rand_coord_prev = params.rand_coord; + params.rand_coord = rng.sample_uidist(); + NT kapa = rng.sample_urdist(); + bpair = P.line_intersect_coord(params.p, + params.p_prev, + params.rand_coord, + params.rand_coord_prev, + params.lambdas); + params.p_prev = params.p; + params.p.set_coord(params.rand_coord, params.p[params.rand_coord] + bpair.first + kapa + * (bpair.second - bpair.first)); + } + params.p1 = params.p_prev; + params.p2 = params.p_prev; + params.p1.set_coord(params.rand_coord, params.p_prev[params.rand_coord] + bpair.first); + params.p2.set_coord(params.rand_coord, params.p_prev[params.rand_coord] + bpair.second); + } + + private : + + template + inline void initialize(GenericBody const& P, + thread_parameters_ ¶ms, // parameters + RandomNumberGenerator& rng) + { + params.lambdas.setZero(P.num_of_hyperplanes()); + params.rand_coord = rng.sample_uidist(); + NT kapa = rng.sample_urdist(); + + std::pair bpair = P.line_intersect_coord(params.p, params.rand_coord, + params.lambdas); + params.p_prev = params.p; + params.p.set_coord(params.rand_coord, params.p[params.rand_coord] + bpair.first + kapa + * (bpair.second - bpair.first)); + } + + }; + +}; + + +// Random directions hit-and-run walk with uniform target distribution +// from the boundary, multithread version + +struct BRDHRWalk_multithread +{ + + template + struct thread_parameters + { + thread_parameters(unsigned int d, unsigned int m) + { + p = Point(d); + p1 = Point(d); + p2 = Point(d); + v = Point(d); + lambdas.setZero(m); + Av.setZero(m); + lambda_prev = NT(0); + } + + Point p; + Point p1; + Point p2; + Point v; + NT lambda_prev; + typename Point::Coeff lambdas; + typename Point::Coeff Av; + }; + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef thread_parameters thread_parameters_; + //typedef thread_params thread_parameters_; + + template + Walk(GenericPolytope& P, thread_parameters_ ¶meters, RandomNumberGenerator& rng) + { + initialize(P, parameters, rng); + } + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + unsigned int const& walk_length, + RandomNumberGenerator& rng) + { + for (auto j=0u; j::apply(P.dimension(), rng); + std::pair bpair = P.line_intersect(params.p, params.v, params.lambdas, params.Av, + params.lambda_prev); + params.lambda_prev = rng.sample_urdist() * (bpair.first - bpair.second) + + bpair.second; + params.p1 = (bpair.first * params.v); + params.p1 += params.p; + params.p2 = (bpair.second * params.v); + params.p2 += params.p; + params.p += (params.lambda_prev * params.v); + } + } + + private : + + template + inline void initialize(GenericBody const& P, + thread_parameters_ ¶ms, // parameters + RandomNumberGenerator& rng) + { + params.lambdas.setZero(P.num_of_hyperplanes()); + params.Av.setZero(P.num_of_hyperplanes()); + + params.v = GetDirection::apply(P.dimension(), rng); + std::pair bpair = P.line_intersect(params.p, params.v, params.lambdas, params.Av); + params.lambda_prev = rng.sample_urdist() * (bpair.first - bpair.second) + bpair.second; + params.p = (params.lambda_prev * params.v) + params.p; + } + + }; + +}; + + +// Coordinate directions hit-and-run walk with spherical Gaussian target distribution +// Multithred version + +struct GaussianCDHRWalk_multithread +{ + + template + struct thread_parameters + { + thread_parameters(unsigned int d, unsigned int m) + { + p = Point(d); + p_prev = Point(d); + lambdas.setZero(m); + } + + Point p; + Point p_prev; + unsigned int rand_coord_prev; + unsigned int rand_coord; + typename Point::Coeff lambdas; + }; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef thread_parameters thread_parameters_; + //typedef thread_params thread_parameters_; + + Walk(Polytope& P, + thread_parameters_ ¶ms, + NT const& a_i, + RandomNumberGenerator &rng) + { + initialize(P, params, a_i, rng); + } + + template + Walk(Polytope& P, + thread_parameters_ ¶ms, + NT const& a_i, + RandomNumberGenerator &rng, + parameters&) + { + initialize(P, params, a_i, rng); + } + + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + NT const& a_i, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + for (auto j = 0u; j < walk_length; ++j) + { + params.rand_coord_prev = params.rand_coord; + params.rand_coord = rng.sample_uidist(); + std::pair bpair = + P.line_intersect_coord(params.p, params.p_prev, params.rand_coord, + params.rand_coord_prev, params.lambdas); + NT dis = chord_random_point_generator_exp_coord + (params.p[params.rand_coord] + bpair.second, + params.p[params.rand_coord] + bpair.first, + a_i, + rng); + params.p_prev = params.p; + params.p.set_coord(params.rand_coord, dis); + } + } + +private : + + template + inline void initialize(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + NT const& a_i, + RandomNumberGenerator &rng) + { + params.lambdas.setZero(P.num_of_hyperplanes()); + params.rand_coord = rng.sample_uidist(); + + std::pair bpair = P.line_intersect_coord(params.p, params.rand_coord, params.lambdas); + NT dis = chord_random_point_generator_exp_coord + (params.p[params.rand_coord] + bpair.second, + params.p[params.rand_coord] + bpair.first, + a_i, rng); + params.p_prev = params.p; + params.p.set_coord(params.rand_coord, dis); + } + +}; + +}; + + +// Random directions hit-and-run walk with spherical Gaussian target distribution +// multithread version + +struct GaussianRDHRWalk_multithread +{ + + template + struct thread_parameters + { + thread_parameters(unsigned int d, unsigned int m) + { + p = Point(d); + v = Point(d); + } + + Point p; + Point v; + }; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef thread_parameters thread_parameters_; + //typedef thread_params thread_parameters_; + + Walk(Polytope&, thread_parameters_ &, NT const&, RandomNumberGenerator&) + {} + + template + Walk(Polytope&, thread_parameters_ &, NT const&, RandomNumberGenerator&, + parameters&) + {} + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + NT const& a_i, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + for (auto j = 0u; j < walk_length; ++j) + { + params.v = GetDirection::apply(P.dimension(), rng); + std::pair dbpair = P.line_intersect(params.p, params.v); + + NT min_plus = dbpair.first; + NT max_minus = dbpair.second; + Point upper = (min_plus * params.v) + params.p; + Point lower = (max_minus * params.v) + params.p; + + chord_random_point_generator_exp(lower, upper, a_i, params.p, rng); + } + } +}; + +}; + + +// Billiard walk for uniform distribution + +struct BilliardWalk_multithread +{ + + template + struct thread_parameters + { + thread_parameters(unsigned int d, unsigned int m) + { + p = Point(d); + p0 = Point(d); + v = Point(d); + lambdas.setZero(m); + Av.setZero(m); + lambda_prev = NT(0); + } + + Point p; + Point p0; + Point v; + NT lambda_prev; + typename Point::Coeff lambdas; + typename Point::Coeff Av; + }; + + BilliardWalk_multithread(double L) + : param(L, true) + {} + + BilliardWalk_multithread() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_L(set) + {} + double m_L; + bool set_L; + }; + + parameters param; + + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef thread_parameters thread_parameters_; + //typedef thread_params thread_parameters_; + + template + Walk(GenericPolytope& P, thread_parameters_ ¶meters, RandomNumberGenerator &rng) + { + _Len = compute_diameter + ::template compute(P); + initialize(P, parameters, rng); + } + + template + Walk(GenericPolytope& P, thread_parameters_ ¶meters, RandomNumberGenerator &rng, + parameters_ const& params) + { + _Len = params.set_L ? params.m_L + : compute_diameter + ::template compute(P); + initialize(P, parameters, rng); + } + + template + < + typename GenericPolytope + > + inline void apply(GenericPolytope& P, + thread_parameters_ ¶meters, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT T = rng.sample_urdist() * _Len; + const NT dl = 0.995; + + for (auto j=0u; j::apply(n, rng); + parameters.p0 = parameters.p; + int it = 0; + while (it < 50*n) + { + auto pbpair = P.line_positive_intersect(parameters.p, parameters.v, parameters.lambdas, + parameters.Av, parameters.lambda_prev); + if (T <= pbpair.first) { + parameters.p += (T * parameters.v); + parameters.lambda_prev = T; + break; + } + parameters.lambda_prev = dl * pbpair.first; + parameters.p += (parameters.lambda_prev * parameters.v); + T -= parameters.lambda_prev; + P.compute_reflection(parameters.v, parameters.p, pbpair.second); + it++; + } + if (it == 50*n) + { + parameters.p = parameters.p0; + } + } + } + + inline void update_delta(NT L) + { + _Len = L; + } + +private : + + template + < + typename GenericPolytope + > + inline void initialize(GenericPolytope& P, + thread_parameters_ ¶meters, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + const NT dl = 0.995; + parameters.lambdas.setZero(P.num_of_hyperplanes()); + parameters.Av.setZero(P.num_of_hyperplanes()); + + parameters.v = GetDirection::apply(n, rng); + NT T = rng.sample_urdist() * _Len; + int it = 0; + + std::pair pbpair + = P.line_positive_intersect(parameters.p, parameters.v, parameters.lambdas, parameters.Av); + if (T <= pbpair.first) + { + parameters.p += (T * parameters.v); + parameters.lambda_prev = T; + return; + } + parameters.lambda_prev = dl * pbpair.first; + parameters.p += (parameters.lambda_prev * parameters.v); + T -= parameters.lambda_prev; + P.compute_reflection(parameters.v, parameters.p, pbpair.second); + + while (it <= 50*n) + { + std::pair pbpair + = P.line_positive_intersect(parameters.p, parameters.v, parameters.lambdas, + parameters.Av, parameters.lambda_prev); + if (T <= pbpair.first) + { + parameters.p += (T * parameters.v); + parameters.lambda_prev = T; + break; + }else if (it == 50*n) { + parameters.lambda_prev = rng.sample_urdist() * pbpair.first; + parameters.p += (parameters.lambda_prev * parameters.v); + break; + } + parameters.lambda_prev = dl * pbpair.first; + parameters.p += (parameters.lambda_prev * parameters.v); + T -= parameters.lambda_prev; + P.compute_reflection(parameters.v, parameters.p, pbpair.second); + it++; + } + } + + NT _Len; +}; + +}; + + +// coordinate directions hit-and-run walk with uniform target distribution +// Parallel version + +struct CDHRWalk_multithread +{ + template + struct thread_parameters + { + thread_parameters(unsigned int d, unsigned int m) + { + p = Point(d); + p_prev = Point(d); + lambdas.setZero(m); + } + + Point p; + Point p_prev; + unsigned int rand_coord_prev; + unsigned int rand_coord; + typename Point::Coeff lambdas; + }; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef thread_parameters thread_parameters_; + //typedef thread_params thread_parameters_; + + template + Walk(GenericPolytope& P, thread_parameters_ ¶meters, RandomNumberGenerator& rng) + { + initialize(P, parameters, rng); + } + + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + for (auto j=0u; j bpair = P.line_intersect_coord(params.p, + params.p_prev, + params.rand_coord, + params.rand_coord_prev, + params.lambdas); + params.p_prev = params.p; + params.p.set_coord(params.rand_coord, params.p[params.rand_coord] + bpair.first + kapa + * (bpair.second - bpair.first)); + } + } + +private : + + template + inline void initialize(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + RandomNumberGenerator &rng) + { + params.lambdas.setZero(P.num_of_hyperplanes()); + params.rand_coord = rng.sample_uidist(); + NT kapa = rng.sample_urdist(); + + std::pair bpair = P.line_intersect_coord(params.p, params.rand_coord, params.lambdas); + params.p_prev = params.p; + params.p.set_coord(params.rand_coord, params.p[params.rand_coord] + bpair.first + kapa + * (bpair.second - bpair.first)); + } + +}; + +}; + + +// Random directions hit-and-run walk with uniform target distribution +// Parallel version + +struct RDHRWalk_multithread +{ + + template + struct thread_parameters + { + thread_parameters(unsigned int d, unsigned int m) + { + p = Point(d); + v = Point(d); + lambdas.setZero(m); + Av.setZero(m); + lambda_prev = NT(0); + } + + Point p; + Point v; + NT lambda_prev; + typename Point::Coeff lambdas; + typename Point::Coeff Av; + }; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef thread_parameters thread_parameters_; + //typedef thread_params thread_parameters_; + + template + Walk(GenericPolytope& P, thread_parameters_ ¶meters, RandomNumberGenerator& rng) + { + initialize(P, parameters, rng); + } + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + unsigned int const& walk_length, + RandomNumberGenerator& rng) + { + for (auto j=0u; j::apply(P.dimension(), rng); + std::pair bpair = P.line_intersect(params.p, params.v, params.lambdas, params.Av, + params.lambda_prev); + params.lambda_prev = rng.sample_urdist() * (bpair.first - bpair.second) + + bpair.second; + params.p += (params.lambda_prev * params.v); + } + } + +private : + + template + inline void initialize(BallPolytope const& P, + thread_parameters_ ¶ms, // parameters + RandomNumberGenerator &rng) + { + params.lambdas.setZero(P.num_of_hyperplanes()); + params.Av.setZero(P.num_of_hyperplanes()); + + params.v = GetDirection::apply(P.dimension(), rng); + std::pair bpair = P.line_intersect(params.p, params.v, params.lambdas, params.Av); + params.lambda_prev = rng.sample_urdist() * (bpair.first - bpair.second) + bpair.second; + params.p += (params.lambda_prev * params.v); + } + +}; + +}; + + +#endif // RANDOM_WALKS_MULTITHREAD_WALKS_HPP diff --git a/src/volesti/include/random_walks/nuts_hmc_walk.hpp b/src/volesti/include/random_walks/nuts_hmc_walk.hpp new file mode 100644 index 00000000..857064c0 --- /dev/null +++ b/src/volesti/include/random_walks/nuts_hmc_walk.hpp @@ -0,0 +1,380 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2022 Vissarion Fisikopoulos +// Copyright (c) 2018-2022 Apostolos Chalkis +// Copyright (c) 2020-2022 Elias Tsigaridas +// Copyright (c) 2020-2022 Marios Papachristou + +// Licensed under GNU LGPL.3, see LICENCE file + +// References +// Matthew D. Hoffman, Andrew Gelman. "The No-U-Turn Sampler: +// Adaptively Setting Path Lengths in Hamiltonian Monte Carlo", 2011. + +// Comment: Compared to [Matthew D. Hoffman, Andrew Gelman, 2011] +// we modify the step of Nesterov's algorithm in the burn in phase. + +#ifndef NUTS_HAMILTONIAN_MONTE_CARLO_WALK_HPP +#define NUTS_HAMILTONIAN_MONTE_CARLO_WALK_HPP + + +#include "generators/boost_random_number_generator.hpp" +#include "random_walks/gaussian_helpers.hpp" +#include "ode_solvers/ode_solvers.hpp" +#include "preprocess/estimate_L_smooth_parameter.hpp" + +struct NutsHamiltonianMonteCarloWalk { + + template + < + typename NT, + typename OracleFunctor + > + struct parameters { + NT epsilon; // tolerance in mixing + NT eta; // step size + + parameters( + OracleFunctor const& F, + unsigned int dim, + NT epsilon_=2) + { + epsilon = epsilon_; + eta = F.params.L > 0 ? 10.0 / (dim * sqrt(F.params.L)) : 0.005; + } + }; + + template + < + typename Point, + typename Polytope, + typename RandomNumberGenerator, + typename NegativeGradientFunctor, + typename NegativeLogprobFunctor, + typename Solver + > + struct Walk { + + typedef std::vector pts; + typedef typename Point::FT NT; + typedef std::vector bounds; + + // Hyperparameters of the sampler + parameters ¶ms; + + // Numerical ODE solver + Solver *solver; + + // Dimension + unsigned int dim; + + // Discarded Samples + long num_runs = 0; + long total_acceptance = 0; + + // Average acceptance probability + NT average_acceptance = 0; + + // References to xs + Point x, v; + + // Helper points + Point v_pl, v_min, v_min_j, v_pl_j, X_pl, X_pl_j, X_min, X, X_rnd_j, X_min_j, x_pl_min; + + // Gradient function + NegativeGradientFunctor &F; + + bool accepted; + + // Burnin parameters + NT eps_step, mu, log_tilde_eps, H_tilde, alpha, na; + const NT delta = NT(0.65), Delta_max = NT(1000), gamma = NT(0.05), t0 = NT(10), kk = NT(0.85); + + // Density exponent + NegativeLogprobFunctor &f; + + Walk(Polytope *P, + Point &p, + NegativeGradientFunctor &neg_grad_f, + NegativeLogprobFunctor &neg_logprob_f, + parameters ¶m, + bool burn_in_phase = true) : + params(param), + F(neg_grad_f), + f(neg_logprob_f) + { + dim = p.dimension(); + + v_pl.set_dimension(dim); + v_min.set_dimension(dim); + v_min_j.set_dimension(dim); + v_pl_j.set_dimension(dim); + X_pl.set_dimension(dim); + X_pl_j.set_dimension(dim); + X_min.set_dimension(dim); + X.set_dimension(dim); + X_rnd_j.set_dimension(dim); + X_min_j.set_dimension(dim); + x_pl_min.set_dimension(dim); + + eps_step = params.eta; + mu = std::log(10*eps_step); + log_tilde_eps = NT(0); + H_tilde = NT(0); + alpha = NT(0); + na = NT(0); + + // Starting point is provided from outside + x = p; + + accepted = false; + + // Initialize solver + solver = new Solver(0, params.eta, pts{x, x}, F, bounds{P, NULL}); + disable_adaptive(); + + if (burn_in_phase) + { + RandomNumberGenerator rng(dim); + burnin(rng); + } + }; + + + inline void burnin(RandomNumberGenerator &rng, + unsigned int N = 1000, + unsigned int walk_length=1) + { + reset_num_runs(); + Point p = x; + NT L; + + if ((solver->get_bounds())[0] == NULL) + { + L = (NT(100) / NT(dim)) * (NT(100) / NT(dim)); + } + else + { + Polytope K = *(solver->get_bounds())[0]; + L = estimate_L_smooth(K, p, walk_length, F, rng); + } + + eps_step = NT(5) / (NT(dim) * std::sqrt(L)); + solver->set_eta(eps_step); + + for (int i = 0; i < N; i++) + { + apply(rng, walk_length, true); + solver->set_eta(eps_step); + } + reset_num_runs(); + } + + + inline void apply(RandomNumberGenerator &rng, + unsigned int walk_length=1, + bool burnin = false) + { + num_runs++; + + int x_counting_total = 0; + + // Pick a random velocity + v = GetDirection::apply(dim, rng, false); + + v_pl = v; + v_min = NT(-1) * v; + X_pl = x; + X_min = x; + + NT h1 = hamiltonian(x, v); + + NT uu = std::log(rng.sample_urdist()) - h1; + int j = -1; + bool s = true; + bool updated = false; + bool pos_state_single = false; + + if (burnin) + { + alpha = NT(0); + } + + while (s) + { + j++; + + if (burnin) + { + na = std::pow(NT(2), NT(j)); + } + + NT dir = rng.sample_urdist(); + + if (dir > 0.5) + { + v = v_pl; + X = X_pl; + } + else + { + v = v_min; + X = X_min; + } + X_rnd_j = X; + + int x_counting = 0; + int num_samples = int(std::pow(NT(2), NT(j))); + accepted = false; + + for (int k = 1; k <= num_samples; k++) + { + if (!accepted) + { + solver->set_state(0, X); + solver->set_state(1, v); + } + + // Get proposals + solver->steps(walk_length, accepted); + accepted = true; + + X = solver->get_state(0); + v = solver->get_state(1); + + NT hj = hamiltonian(X, v); + + if (burnin) + { + alpha += std::min(NT(1), std::exp(-hj + h1)); + } + + if (uu > Delta_max - hj) + { + s = false; + break; + } + + bool pos_state = false; + if (uu < -hj) + { + pos_state = true; + pos_state_single = true; + x_counting = x_counting + 1; + x_counting_total = x_counting_total + 1; + } + + if (k == 1) + { + if (dir > 0.5) + { + X_min_j = X; + v_min_j = v; + } + else + { + X_pl_j = X; + v_pl_j = v; + } + } + if (k == num_samples) + { + if (dir > 0.5) + { + x_pl_min = X - X_min_j; + if ((x_pl_min.dot(v) < 0) || (x_pl_min.dot(v_min_j) < 0)) + { + s = false; + } + } + else + { + x_pl_min = X_pl_j - X; + if ((x_pl_min.dot(v) < 0) || (x_pl_min.dot(v_pl_j) < 0)) + { + s = false; + } + } + } + if ((rng.sample_urdist() < (1/NT(x_counting))) && pos_state) + { + X_rnd_j = X; + } + } + + if (dir > 0.5) + { + X_pl = X; + v_pl = v; + } + else + { + X_min = X; + v_min = v; + } + + if (s && (rng.sample_urdist() < (NT(x_counting) / NT(x_counting_total)))) + { + x = X_rnd_j; + if (pos_state_single) + { + updated = true; + } + } + + if (s) + { + x_pl_min = X_pl - X_min; + if ((x_pl_min.dot(v_min) < 0) || (x_pl_min.dot(v_pl) < 0)) + { + s = false; + } + } + } + + if (updated) + { + total_acceptance++; + } + average_acceptance = NT(total_acceptance) / NT(num_runs); + + if (burnin) + { + H_tilde = (NT(1) - NT(1) / (NT(num_runs) + t0)) * H_tilde + (NT(1) / (NT(num_runs) + t0)) * (delta - alpha / na); + NT log_eps = mu - (std::sqrt(NT(num_runs)) / gamma) * H_tilde; + + // TODO: use the following to generalize Nesterov's algorithm + //log_tilde_eps = std::pow(mu,-kk) * log_eps + (NT(1) - std::pow(mu,-kk))*log_tilde_eps; + + eps_step = std::exp(log_eps); + } + } + + inline NT hamiltonian(Point &pos, Point &vel) const { + return f(pos) + 0.5 * vel.dot(vel); + } + + inline NT get_eta_solver() { + return solver->get_eta(); + } + + void disable_adaptive() { + solver->disable_adaptive(); + } + + void enable_adaptive() { + solver->enable_adaptive(); + } + + void reset_num_runs() { + num_runs = 0; + total_acceptance = 0; + } + + NT get_ratio_acceptance() { + return average_acceptance; + } + }; +}; + +#endif // HAMILTONIAN_MONTE_CARLO_WALK_HPP diff --git a/src/volesti/include/random_walks/random_walks.hpp b/src/volesti/include/random_walks/random_walks.hpp new file mode 100644 index 00000000..6c36457c --- /dev/null +++ b/src/volesti/include/random_walks/random_walks.hpp @@ -0,0 +1,34 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2020-2021 Vissarion Fisikopoulos +// Copyright (c) 2020-2021 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_RANDOM_WALKS_HPP +#define RANDOM_WALKS_RANDOM_WALKS_HPP + +#include + +#include "random_walks/boundary_cdhr_walk.hpp" +#include "random_walks/boundary_rdhr_walk.hpp" +#include "random_walks/gaussian_ball_walk.hpp" +#include "random_walks/gaussian_cdhr_walk.hpp" +#include "random_walks/gaussian_rdhr_walk.hpp" +#include "random_walks/uniform_ball_walk.hpp" +#include "random_walks/uniform_billiard_walk.hpp" +#include "random_walks/uniform_cdhr_walk.hpp" +#include "random_walks/uniform_rdhr_walk.hpp" +#include "random_walks/uniform_dikin_walk.hpp" +#include "random_walks/uniform_john_walk.hpp" +#include "random_walks/uniform_vaidya_walk.hpp" +#include "random_walks/uniform_accelerated_billiard_walk.hpp" +#include "random_walks/gaussian_accelerated_billiard_walk.hpp" +#include "random_walks/gaussian_hamiltonian_monte_carlo_exact_walk.hpp" +#include "random_walks/exponential_hamiltonian_monte_carlo_exact_walk.hpp" +#include "random_walks/uniform_accelerated_billiard_walk_parallel.hpp" +#include "random_walks/hamiltonian_monte_carlo_walk.hpp" +#include "random_walks/nuts_hmc_walk.hpp" +#include "random_walks/langevin_walk.hpp" +#include "random_walks/crhmc/crhmc_walk.hpp" +#endif // RANDOM_WALKS_RANDOM_WALKS_HPP diff --git a/src/volesti/include/random_walks/uniform_accelerated_billiard_walk.hpp b/src/volesti/include/random_walks/uniform_accelerated_billiard_walk.hpp new file mode 100644 index 00000000..980c8e5d --- /dev/null +++ b/src/volesti/include/random_walks/uniform_accelerated_billiard_walk.hpp @@ -0,0 +1,309 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_ACCELERATED_IMPROVED_BILLIARD_WALK_HPP +#define RANDOM_WALKS_ACCELERATED_IMPROVED_BILLIARD_WALK_HPP + +#include "sampling/sphere.hpp" + + +// Billiard walk which accelarates each step for uniform distribution + +struct AcceleratedBilliardWalk +{ + AcceleratedBilliardWalk(double L) + : param(L, true) + {} + + AcceleratedBilliardWalk() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_L(set) + {} + double m_L; + bool set_L; + }; + + struct update_parameters + { + update_parameters() + : facet_prev(0), hit_ball(false), inner_vi_ak(0.0), ball_inner_norm(0.0) + {} + int facet_prev; + bool hit_ball; + double inner_vi_ak; + double ball_inner_norm; + }; + + parameters param; + + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Polytope::MT MT; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope &P, Point const& p, RandomNumberGenerator &rng) + { + _update_parameters = update_parameters(); + _L = compute_diameter + ::template compute(P); + _AA.noalias() = P.get_mat() * P.get_mat().transpose(); + _rho = 1000 * P.dimension(); // upper bound for the number of reflections (experimental) + initialize(P, p, rng); + } + + template + Walk(GenericPolytope &P, Point const& p, RandomNumberGenerator &rng, + parameters const& params) + { + _update_parameters = update_parameters(); + _L = params.set_L ? params.m_L + : compute_diameter + ::template compute(P); + _AA.noalias() = P.get_mat() * P.get_mat().transpose(); + _rho = 1000 * P.dimension(); // upper bound for the number of reflections (experimental) + initialize(P, p, rng); + } + + template + < + typename GenericPolytope + > + inline void apply(GenericPolytope &P, + Point &p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT T; + const NT dl = 0.995; + int it; + + for (auto j=0u; j::apply(n, rng); + Point p0 = _p; + + it = 0; + std::pair pbpair = P.line_positive_intersect(_p, _v, _lambdas, _Av, + _lambda_prev, _update_parameters); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + continue; + } + + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, _update_parameters); + it++; + + while (it < _rho) + { + std::pair pbpair + = P.line_positive_intersect(_p, _v, _lambdas, _Av, _lambda_prev, + _AA, _update_parameters); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + break; + } + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, _update_parameters); + it++; + } + if (it == _rho) _p = p0; + } + p = _p; + } + + + template + < + typename GenericPolytope + > + inline void get_starting_point(GenericPolytope &P, + Point const& center, + Point &q, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT radius = P.InnerBall().second; + + q = GetPointInDsphere::apply(n, radius, rng); + q += center; + initialize(P, q, rng); + + apply(P, q, walk_length, rng); + } + + + template + < + typename GenericPolytope + > + inline void parameters_burnin(GenericPolytope &P, + Point const& center, + unsigned int const& num_points, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + Point p(P.dimension()); + std::vector pointset; + pointset.push_back(center); + pointset.push_back(_p); + NT rad = NT(0), max_dist, Lmax = get_delta(), radius = P.InnerBall().second; + + for (int i = 0; i < num_points; i++) + { + Point p = GetPointInDsphere::apply(P.dimension(), radius, rng); + p += center; + initialize(P, p, rng); + + apply(P, p, walk_length, rng); + max_dist = get_max_distance(pointset, p, rad); + if (max_dist > Lmax) + { + Lmax = max_dist; + } + if (2.0*rad > Lmax) { + Lmax = 2.0 * rad; + } + pointset.push_back(p); + } + + if (Lmax > _L) { + if (P.dimension() <= 2500) + { + update_delta(Lmax); + } + else{ + update_delta(2.0 * get_delta()); + } + } + pointset.clear(); + } + + + + inline void update_delta(NT L) + { + _L = L; + } + + NT get_delta() + { + return _L; + } + + private : + + template + < + typename GenericPolytope + > + inline void initialize(GenericPolytope &P, + Point const& p, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + const NT dl = 0.995; + _lambdas.setZero(P.num_of_hyperplanes()); + _Av.setZero(P.num_of_hyperplanes()); + _p = p; + _v = GetDirection::apply(n, rng); + + NT T = -std::log(rng.sample_urdist()) * _L; + Point p0 = _p; + int it = 0; + + std::pair pbpair + = P.line_first_positive_intersect(_p, _v, _lambdas, _Av, _update_parameters); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + return; + } + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, _update_parameters); + + while (it <= _rho) + { + std::pair pbpair + = P.line_positive_intersect(_p, _v, _lambdas, _Av, _lambda_prev, _AA, _update_parameters); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + break; + } else if (it == _rho) { + _lambda_prev = rng.sample_urdist() * pbpair.first; + _p += (_lambda_prev * _v); + break; + } + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, _update_parameters); + it++; + } + } + + inline double get_max_distance(std::vector &pointset, Point const& q, double &rad) + { + double dis = -1.0, temp_dis; + int jj = 0; + for (auto vecit = pointset.begin(); vecit!=pointset.end(); vecit++, jj++) + { + temp_dis = (q.getCoefficients() - (*vecit).getCoefficients()).norm(); + if (temp_dis > dis) { + dis = temp_dis; + } + if (jj == 0) { + if (temp_dis > rad) { + rad = temp_dis; + } + } + } + return dis; + } + + double _L; + Point _p; + Point _v; + NT _lambda_prev; + MT _AA; + unsigned int _rho; + update_parameters _update_parameters; + typename Point::Coeff _lambdas; + typename Point::Coeff _Av; + }; + +}; + + +#endif diff --git a/src/volesti/include/random_walks/uniform_accelerated_billiard_walk_parallel.hpp b/src/volesti/include/random_walks/uniform_accelerated_billiard_walk_parallel.hpp new file mode 100644 index 00000000..1c2630f9 --- /dev/null +++ b/src/volesti/include/random_walks/uniform_accelerated_billiard_walk_parallel.hpp @@ -0,0 +1,311 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_ACCELERATED_IMPROVED_BILLIARD_WALK_PARALLEL_HPP +#define RANDOM_WALKS_ACCELERATED_IMPROVED_BILLIARD_WALK_PARALLEL_HPP + +#include "sampling/sphere.hpp" + + +// Billiard walk which accelarates each step for uniform distribution and can be used for a parallel use by threads + +struct AcceleratedBilliardWalkParallel +{ + AcceleratedBilliardWalkParallel() + {} + + + struct update_parameters + { + update_parameters() + : facet_prev(0), hit_ball(false), inner_vi_ak(0.0), ball_inner_norm(0.0) + {} + int facet_prev; + bool hit_ball; + double inner_vi_ak; + double ball_inner_norm; + }; + + template + struct thread_parameters + { + thread_parameters(unsigned int d, unsigned int m) + { + update_step_parameters = update_parameters(); + p = Point(d); + v = Point(d); + lambdas.setZero(m); + Av.setZero(m); + lambda_prev = NT(0); + } + + update_parameters update_step_parameters; + Point p; + Point v; + NT lambda_prev; + typename Point::Coeff lambdas; + typename Point::Coeff Av; + }; + + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Polytope::MT MT; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope &P) + { + _L = compute_diameter + ::template compute(P); + _AA.noalias() = P.get_mat() * P.get_mat().transpose(); + _p0 = Point(P.dimension()); + _rho = 1000 * P.dimension(); + } + + template + Walk(GenericPolytope &P, NT const& L) + { + _L = L > NT(0) ? L + : compute_diameter + ::template compute(P); + _AA.noalias() = P.get_mat() * P.get_mat().transpose(); + _p0 = Point(P.dimension()); + _rho = 1000 * P.dimension(); + } + + template + < + typename GenericPolytope, + typename thread_params + > + inline void apply(GenericPolytope &P, + thread_params ¶ms, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT T; + const NT dl = 0.995; + int it; + + for (auto j=0u; j::apply(n, rng); + _p0 = params.p; + + it = 0; + std::pair pbpair = P.line_positive_intersect(params.p, params.v, params.lambdas, params.Av, + params.lambda_prev, params.update_step_parameters); + if (T <= pbpair.first) + { + params.p += (T * params.v); + params.lambda_prev = T; + continue; + } + + params.lambda_prev = dl * pbpair.first; + params.p += (params.lambda_prev * params.v); + T -= params.lambda_prev; + P.compute_reflection(params.v, params.p, params.update_step_parameters); + it++; + + while (it < _rho) + { + std::pair pbpair + = P.line_positive_intersect(params.p, params.v, params.lambdas, params.Av, + params.lambda_prev, _AA, params.update_step_parameters); + if (T <= pbpair.first) { + params.p += (T * params.v); + params.lambda_prev = T; + break; + } + params.lambda_prev = dl * pbpair.first; + params.p += (params.lambda_prev * params.v); + T -= params.lambda_prev; + P.compute_reflection(params.v, params.p, params.update_step_parameters); + it++; + } + if (it == _rho) params.p = _p0; + } + } + + + template + < + typename GenericPolytope, + typename thread_params + > + inline void get_starting_point(GenericPolytope &P, + Point const& center, + thread_params ¶ms, + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT radius = P.InnerBall().second; + + params.p = GetPointInDsphere::apply(n, radius, rng); + params.p += center; + initialize(P, params, rng); + + apply(P, params, walk_length, rng); + } + + + template + < + typename GenericPolytope, + typename thread_params + > + inline void parameters_burnin(GenericPolytope &P, + Point const& center, + unsigned int const& num_points, + unsigned int const& walk_length, + RandomNumberGenerator &rng, + thread_params ¶ms) + { + std::vector pointset; + pointset.push_back(center); + + params.p = Point(P.dimension()); + NT rad = NT(0), max_dist, Lmax = get_delta(), radius = P.InnerBall().second; + + for (int i = 0; i < num_points; i++) + { + params.p = GetPointInDsphere::apply(P.dimension(), radius, rng); + params.p += center; + initialize(P, params, rng); + + apply(P, params, walk_length, rng); + max_dist = get_max_distance(pointset, params.p, rad); + if (max_dist > Lmax) + { + Lmax = max_dist; + } + if (2.0*rad > Lmax) { + Lmax = 2.0 * rad; + } + pointset.push_back(params.p); + } + + if (Lmax > _L) + { + if (P.dimension() <= 2500) + { + update_delta(Lmax); + } + else{ + update_delta(2.0 * get_delta()); + } + } + pointset.clear(); + } + + + + inline void update_delta(NT L) + { + _L = L; + } + + NT get_delta() + { + return _L; + } + + private : + + template + < + typename GenericPolytope, + typename thread_params + > + inline void initialize(GenericPolytope &P, + thread_params ¶ms, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + const NT dl = 0.995; + params.v = GetDirection::apply(n, rng); + + NT T = -std::log(rng.sample_urdist()) * _L; + int it = 0; + + std::pair pbpair + = P.line_first_positive_intersect(params.p, params.v, params.lambdas, + params.Av, params.update_step_parameters); + if (T <= pbpair.first) { + params.p += (T * params.v); + params.lambda_prev = T; + return; + } + params.lambda_prev = dl * pbpair.first; + params.p += (params.lambda_prev * params.v); + T -= params.lambda_prev; + P.compute_reflection(params.v, params.p, params.update_step_parameters); + + while (it <= _rho) + { + std::pair pbpair + = P.line_positive_intersect(params.p, params.v, params.lambdas, params.Av, + params.lambda_prev, _AA, params.update_step_parameters); + if (T <= pbpair.first) { + params.p += (T * params.v); + params.lambda_prev = T; + break; + } else if (it == _rho) { + params.lambda_prev = rng.sample_urdist() * pbpair.first; + params.p += (params.lambda_prev * params.v); + break; + } + params.lambda_prev = dl * pbpair.first; + params.p += (params.lambda_prev * params.v); + T -= params.lambda_prev; + P.compute_reflection(params.v, params.p, params.update_step_parameters); + it++; + } + } + + inline double get_max_distance(std::vector &pointset, Point const& q, double &rad) + { + double dis = -1.0, temp_dis; + int jj = 0; + for (auto vecit = pointset.begin(); vecit!=pointset.end(); vecit++, jj++) + { + temp_dis = (q.getCoefficients() - (*vecit).getCoefficients()).norm(); + if (temp_dis > dis) { + dis = temp_dis; + } + if (jj == 0) { + if (temp_dis > rad) { + rad = temp_dis; + } + } + } + return dis; + } + + NT _L; + MT _AA; + Point _p0; + unsigned int _rho; + }; + +}; + + +#endif + + diff --git a/src/volesti/include/random_walks/uniform_ball_walk.hpp b/src/volesti/include/random_walks/uniform_ball_walk.hpp new file mode 100644 index 00000000..77c608b7 --- /dev/null +++ b/src/volesti/include/random_walks/uniform_ball_walk.hpp @@ -0,0 +1,96 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_UNIFORM_BALL_WALK_HPP +#define RANDOM_WALKS_UNIFORM_BALL_WALK_HPP + +#include "generators/boost_random_number_generator.hpp" + +// Ball walk with uniform target distribution + +struct BallWalk +{ + BallWalk(double L) + : param(L, true) + {} + + BallWalk() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_delta(set) + {} + double m_L; + bool set_delta; + }; + + parameters param; + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope& P, Point const& /*p*/, + RandomNumberGenerator& /*rng*/) + { + _delta = compute_delta(P); + } + + template + Walk(GenericPolytope& P, Point const& /*p*/, + RandomNumberGenerator& /*rng*/, parameters const& params) + { + _delta = params.set_delta ? params.m_L + : compute_delta(P); + } + + template + static inline NT compute_delta(GenericPolytope& P) + { + //return ((P.InnerBall()).second * NT(4)) / NT(P.dimension()); + return (NT(4) * (P.InnerBall()).second) / std::sqrt(NT(P.dimension())); + } + + template + inline void apply(BallPolytope const& P, + Point &p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + for (auto j = 0u; j < walk_length; ++j) + { + Point y = GetPointInDsphere::apply(P.dimension(), + _delta, + rng); + y += p; + if (P.is_in(y) == -1) p = y; + } + } + + inline void update_delta(NT delta) + { + _delta = delta; + } + + private: + double _delta; + }; +}; + +#endif // RANDOM_WALKS_UNIFORM_BALL_WALK_HPP diff --git a/src/volesti/include/random_walks/uniform_billiard_walk.hpp b/src/volesti/include/random_walks/uniform_billiard_walk.hpp new file mode 100644 index 00000000..dcbc96b1 --- /dev/null +++ b/src/volesti/include/random_walks/uniform_billiard_walk.hpp @@ -0,0 +1,200 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2019 program. +// Contributed and modified by Huu Phuoc Le as part of Google Summer of Code 2022 program + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_UNIFORM_BILLIARD_WALK_HPP +#define RANDOM_WALKS_UNIFORM_BILLIARD_WALK_HPP + +#include + +#include "convex_bodies/ball.h" +#include "convex_bodies/ballintersectconvex.h" +#include "convex_bodies/hpolytope.h" +#include "convex_bodies/spectrahedra/spectrahedron.h" +#include "convex_bodies/correlation_matrices/correlation_spectrahedron.hpp" +#include "convex_bodies/correlation_matrices/correlation_spectrahedron_MT.hpp" +#ifndef DISABLE_LPSOLVE + #include "convex_bodies/vpolytope.h" + #include "convex_bodies/vpolyintersectvpoly.h" + #include "convex_bodies/zpolytope.h" + #include "convex_bodies/zonoIntersecthpoly.h" +#endif +#include "sampling/sphere.hpp" +#include "random_walks/boundary_cdhr_walk.hpp" +#include "generators/boost_random_number_generator.hpp" +#include "sampling/random_point_generators.hpp" +#include "volume/sampling_policies.hpp" +#include "random_walks/compute_diameter.hpp" + +// Billiard walk for uniform distribution + +struct BilliardWalk +{ + BilliardWalk(double L) + : param(L, true) + {} + + BilliardWalk() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_L(set) + {} + double m_L; + bool set_L; + }; + + parameters param; + + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope &P, Point const& p, RandomNumberGenerator &rng) + { + _Len = compute_diameter + ::template compute(P); + initialize(P, p, rng); + } + + template + Walk(GenericPolytope &P, Point const& p, RandomNumberGenerator &rng, + parameters const& params) + { + _Len = params.set_L ? params.m_L + : compute_diameter + ::template compute(P); + initialize(P, p, rng); + } + + template + < + typename GenericPolytope + > + inline void apply(GenericPolytope &P, + Point& p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + NT T = rng.sample_urdist() * _Len; + const NT dl = 0.995; + + for (auto j=0u; j::apply(n, rng); + + Point p0 = _p; + int it = 0; + while (it < 50*n) + { + auto pbpair = P.line_positive_intersect(_p, _v, _lambdas, + _Av, _lambda_prev); + + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + break; + } + + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + + P.compute_reflection(_v, _p, pbpair.second); + + it++; + } + if (it == 50*n){ + _p = p0; + } + } + p = _p; + } + + inline void update_delta(NT L) + { + _Len = L; + } + +private : + + template + < + typename GenericPolytope + > + inline void initialize(GenericPolytope &P, + Point const& p, + RandomNumberGenerator &rng) + { + unsigned int n = P.dimension(); + const NT dl = 0.995; + _lambdas.setZero(P.num_of_hyperplanes()); + _Av.setZero(P.num_of_hyperplanes()); + _p = p; + _v = GetDirection::apply(n, rng); + + NT T = rng.sample_urdist() * _Len; + Point p0 = _p; + int it = 0; + std::pair pbpair + = P.line_positive_intersect(_p, _v, _lambdas, _Av); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + return; + } + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, pbpair.second); + while (it <= 50*n) + { + std::pair pbpair + = P.line_positive_intersect(_p, _v, _lambdas, _Av, _lambda_prev); + if (T <= pbpair.first) { + _p += (T * _v); + _lambda_prev = T; + break; + }else if (it == 50*n) { + _lambda_prev = rng.sample_urdist() * pbpair.first; + _p += (_lambda_prev * _v); + break; + } + _lambda_prev = dl * pbpair.first; + _p += (_lambda_prev * _v); + T -= _lambda_prev; + P.compute_reflection(_v, _p, pbpair.second); + it++; + } + } + + NT _Len; + Point _p; + Point _v; + NT _lambda_prev; + typename Point::Coeff _lambdas; + typename Point::Coeff _Av; +}; + +}; + +#endif // RANDOM_WALKS_UNIFORM_BILLIARD_WALK_HPP diff --git a/src/volesti/include/random_walks/uniform_cdhr_walk.hpp b/src/volesti/include/random_walks/uniform_cdhr_walk.hpp new file mode 100644 index 00000000..12d2ff78 --- /dev/null +++ b/src/volesti/include/random_walks/uniform_cdhr_walk.hpp @@ -0,0 +1,97 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_UNIFORM_CDHR_WALK_HPP +#define RANDOM_WALKS_UNIFORM_CDHR_WALK_HPP + +#include "sampling/sphere.hpp" + +// coordinate directions hit-and-run walk with uniform target distribution + +struct CDHRWalk +{ + struct parameters {}; + parameters param; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope& P, Point const& p, RandomNumberGenerator& rng) + { + initialize(P, p, rng); + } + + template + Walk(GenericPolytope& P, Point const& p, + RandomNumberGenerator& rng, parameters const& params) + { + initialize(P, p, rng); + } + + template + < + typename BallPolytope + > + inline void apply(BallPolytope const& P, + Point &p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &rng) + { + for (auto j=0u; j bpair = P.line_intersect_coord(_p, + _p_prev, + _rand_coord, + rand_coord_prev, + _lamdas); + _p_prev = _p; + _p.set_coord(_rand_coord, _p[_rand_coord] + bpair.first + kapa + * (bpair.second - bpair.first)); + } + p = _p; + } + +private : + + template + inline void initialize(BallPolytope const& P, + Point const& p, + RandomNumberGenerator &rng) + { + _lamdas.setZero(P.num_of_hyperplanes()); + _rand_coord = rng.sample_uidist(); + NT kapa = rng.sample_urdist(); + _p = p; + std::pair bpair = P.line_intersect_coord(_p, _rand_coord, _lamdas); + _p_prev = _p; + _p.set_coord(_rand_coord, _p[_rand_coord] + bpair.first + kapa + * (bpair.second - bpair.first)); + } + + unsigned int _rand_coord; + Point _p; + Point _p_prev; + typename Point::Coeff _lamdas; +}; + +}; + + +#endif // RANDOM_WALKS_UNIFORM_CDHR_WALK_HPP diff --git a/src/volesti/include/random_walks/uniform_dikin_walk.hpp b/src/volesti/include/random_walks/uniform_dikin_walk.hpp new file mode 100644 index 00000000..e791b254 --- /dev/null +++ b/src/volesti/include/random_walks/uniform_dikin_walk.hpp @@ -0,0 +1,102 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_DIKIN_WALK_HPP +#define RANDOM_WALKS_DIKIN_WALK_HPP + +#include "convex_bodies/ball.h" +#include "convex_bodies/ballintersectconvex.h" +#include "convex_bodies/hpolytope.h" +#ifndef DISABLE_LPSOLVE + #include "convex_bodies/vpolytope.h" + #include "convex_bodies/vpolyintersectvpoly.h" + #include "convex_bodies/zpolytope.h" +#endif +#include "convex_bodies/zonoIntersecthpoly.h" +#include "ellipsoid_walks/dikin_walker.h" + + +// Dikin walk for uniform distribution + +struct DikinWalk +{ + DikinWalk(double L) + : param(L, true) + {} + + DikinWalk() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_L(set) + {} + double m_L; + bool set_L; + }; + + parameters param; + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Point::FT NT; + + Walk(Polytope &P, Point &p, RandomNumberGenerator &) + { + MT A = P.get_mat(); + VT b = P.get_vec(), _vec_point = VT::Zero(P.dimension()), p0 = p.getCoefficients(); + NT r = P.ComputeInnerBall().second; + dikinw = DikinWalker(p0, A, b, r); + } + + Walk(Polytope &P, Point & p, RandomNumberGenerator &, parameters const& params) + { + MT A = P.get_mat(); + VT b = P.get_vec(), _vec_point = VT::Zero(P.dimension()), p0 = p.getCoefficients(); + NT r = params.set_L ? params.m_L + : P.ComputeInnerBall().second; + dikinw = DikinWalker(p0, A, b, r); + } + + template + < + typename GenericPolytope + > + inline void apply(GenericPolytope &, + Point &p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &) + { + for (auto j=0u; j dikinw; + VT _vec_point; + }; + +}; + + +#endif diff --git a/src/volesti/include/random_walks/uniform_john_walk.hpp b/src/volesti/include/random_walks/uniform_john_walk.hpp new file mode 100644 index 00000000..2c6bc42e --- /dev/null +++ b/src/volesti/include/random_walks/uniform_john_walk.hpp @@ -0,0 +1,94 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_JOHN_WALK_HPP +#define RANDOM_WALKS_JOHN_WALK_HPP + + +#include "ellipsoid_walks/john_walker.h" + + +// John walk for uniform distribution + +struct JohnWalk +{ + JohnWalk(double L) + : param(L, true) + {} + + JohnWalk() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_L(set) + {} + double m_L; + bool set_L; + }; + + parameters param; + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Point::FT NT; + + Walk(Polytope &P, Point &p, RandomNumberGenerator &) + { + MT A = P.get_mat(); + VT b = P.get_vec(), _vec_point = VT::Zero(P.dimension()), p0 = p.getCoefficients(); + NT r = P.ComputeInnerBall().second; + johnw = JohnWalker(p0, A, b, r); + } + + Walk(Polytope &P, Point & p, RandomNumberGenerator &, parameters const& params) + { + MT A = P.get_mat(); + VT b = P.get_vec(), _vec_point = VT::Zero(P.dimension()), p0 = p.getCoefficients(); + NT r = params.set_L ? params.m_L + : P.ComputeInnerBall().second; + johnw = JohnWalker(p0, A, b, r); + } + + template + < + typename GenericPolytope + > + inline void apply(GenericPolytope &, + Point &p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &) + { + for (auto j=0u; j johnw; + VT _vec_point; + }; + +}; + + +#endif diff --git a/src/volesti/include/random_walks/uniform_rdhr_walk.hpp b/src/volesti/include/random_walks/uniform_rdhr_walk.hpp new file mode 100644 index 00000000..5e0eb475 --- /dev/null +++ b/src/volesti/include/random_walks/uniform_rdhr_walk.hpp @@ -0,0 +1,92 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_UNIFORM_RDHR_WALK_HPP +#define RANDOM_WALKS_UNIFORM_RDHR_WALK_HPP + + +#include "sampling/sphere.hpp" + +// Random directions hit-and-run walk with uniform target distribution + +struct RDHRWalk +{ + struct parameters {}; + parameters param; + +template +< + typename Polytope, + typename RandomNumberGenerator +> +struct Walk +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + + template + Walk(GenericPolytope& P, Point const& p, RandomNumberGenerator& rng) + { + initialize(P, p, rng); + } + + template + Walk(GenericPolytope& P, Point const& p, + RandomNumberGenerator& rng, parameters const& params) + { + initialize(P, p, rng); + } + + template + < + typename BallPolytope + > + inline void apply(BallPolytope& P, + Point& p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator& rng) + { + for (auto j=0u; j::apply(p.dimension(), rng); + std::pair bpair = P.line_intersect(_p, v, _lamdas, _Av, + _lambda); + _lambda = rng.sample_urdist() * (bpair.first - bpair.second) + + bpair.second; + _p += (_lambda * v); + } + p = _p; + } + +private : + + template + inline void initialize(BallPolytope& P, + Point const& p, + RandomNumberGenerator &rng) + { + _lamdas.setZero(P.num_of_hyperplanes()); + _Av.setZero(P.num_of_hyperplanes()); + + Point v = GetDirection::apply(p.dimension(), rng); + std::pair bpair = P.line_intersect(p, v, _lamdas, _Av); + _lambda = rng.sample_urdist() * (bpair.first - bpair.second) + bpair.second; + _p = (_lambda * v) + p; + } + + Point _p; + NT _lambda; + typename Point::Coeff _lamdas; + typename Point::Coeff _Av; +}; + +}; + + +#endif // RANDOM_WALKS_UNIFORM_RDHR_WALK_HPP diff --git a/src/volesti/include/random_walks/uniform_vaidya_walk.hpp b/src/volesti/include/random_walks/uniform_vaidya_walk.hpp new file mode 100644 index 00000000..5bbbe991 --- /dev/null +++ b/src/volesti/include/random_walks/uniform_vaidya_walk.hpp @@ -0,0 +1,95 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Contributed and/or modified by Alexandros Manochis, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef RANDOM_WALKS_VAIDYA_WALK_HPP +#define RANDOM_WALKS_VAIDYA_WALK_HPP + + +#include "ellipsoid_walks/vaidya_walker.h" + + +// Vaidya walk for uniform distribution + +struct VaidyaWalk +{ + VaidyaWalk(double L) + : param(L, true) + {} + + VaidyaWalk() + : param(0, false) + {} + + struct parameters + { + parameters(double L, bool set) + : m_L(L), set_L(set) + {} + double m_L; + bool set_L; + }; + + parameters param; + + + template + < + typename Polytope, + typename RandomNumberGenerator + > + struct Walk + { + typedef typename Polytope::PointType Point; + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Point::FT NT; + + Walk(Polytope &P, Point &p, RandomNumberGenerator &) + { + MT A = P.get_mat(); + VT b = P.get_vec(), _vec_point = VT::Zero(P.dimension()), p0 = p.getCoefficients(); + NT r = P.ComputeInnerBall().second; + vaidyaw = VaidyaWalker(p0, A, b, r); + } + + Walk(Polytope &P, Point & p, RandomNumberGenerator &, parameters const& params) + { + MT A = P.get_mat(); + VT b = P.get_vec(), _vec_point = VT::Zero(P.dimension()), p0 = p.getCoefficients(); + NT r = params.set_L ? params.m_L + : P.ComputeInnerBall().second; + vaidyaw = VaidyaWalker(p0, A, b, r); + } + + template + < + typename GenericPolytope + > + inline void apply(GenericPolytope &, + Point &p, // a point to start + unsigned int const& walk_length, + RandomNumberGenerator &) + { + for (auto j=0u; j vaidyaw; + VT _vec_point; + }; + +}; + + +#endif diff --git a/src/volesti/include/root_finders/mp_solve_wrapper.hpp b/src/volesti/include/root_finders/mp_solve_wrapper.hpp new file mode 100644 index 00000000..1193f07c --- /dev/null +++ b/src/volesti/include/root_finders/mp_solve_wrapper.hpp @@ -0,0 +1,75 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef MP_SOLVE_WRAPPER_HPP +#define MP_SOLVE_WRAPPER_HPP + +template +std::vector> mpsolve(std::vector &coeffs, bool positive_real=false) { + + long n = (long) coeffs.size(); + + while (std::abs(coeffs[n-1]) < NT(1e-9)) { + n--; + + } + + + mps_monomial_poly *p; + mps_context *s; + + s = mps_context_new (); + p = mps_monomial_poly_new (s, n-1); + + mps_context_select_algorithm(s, MPS_ALGORITHM_SECULAR_GA); + + for (long i = 0; i < n; i++) { + mps_monomial_poly_set_coefficient_d (s, p, i, coeffs[i], 0); + } + + + /* Set the input polynomial */ + mps_context_set_input_poly (s, MPS_POLYNOMIAL (p)); + + /* Allocate space to hold the results. We check only floating point results + * in here */ + cplx_t *results = cplx_valloc (n-1); + + /* Actually solve the polynomial */ + mps_mpsolve (s); + + /* Save roots computed in the vector results */ + mps_context_get_roots_d (s, &results, NULL); + + std::vector> results_vector; + + NT real, im; + + for (long i = 0; i < n - 1; i++) { + real = (NT) cplx_Re(*results); + im = (NT) cplx_Im(*results); + + #ifdef VOLESTI_DEBUG + std::cout << real << " + " << im << "i" << std::endl; + #endif + results++; + if (positive_real) { + if (real > 0 && std::abs(im) < 1e-8) { + results_vector.push_back(std::make_pair(real, im)); + } + } else { + results_vector.push_back(std::make_pair(real, im)); + } + } + + return results_vector; +} + +#endif diff --git a/src/volesti/include/root_finders/newton_raphson.hpp b/src/volesti/include/root_finders/newton_raphson.hpp new file mode 100644 index 00000000..27411d76 --- /dev/null +++ b/src/volesti/include/root_finders/newton_raphson.hpp @@ -0,0 +1,49 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef NEWTON_RAPHSON_HPP +#define NEWTON_RAPHSON_HPP + +/// @brief Function implementing the Newton-Raphson numerical method +/// @tparam NT Number type +/// @tparam func Function type +template +std::pair newton_raphson(NT t0, func f, func grad_f, const NT rtol, + const NT reg=0, const unsigned int max_tries=1000000) { + NT t, t_prev, err; + NT y, y_prime; + t = t0; + unsigned int tries = 0; + + do { + tries++; + y = f(t_prev); + y_prime = grad_f(t_prev); + + if (std::abs(y_prime) < rtol) y_prime += reg; + + t = t_prev - y / y_prime; + if (t_prev != 0) { + err = std::abs(t - t_prev) / t_prev; + } else { + err = std::abs(t - t_prev); + } + + t_prev = t; + + if (tries > max_tries) break; + + } while (err > rtol); + + return std::make_pair(t, tries > max_tries); + +} + +#endif diff --git a/src/volesti/include/root_finders/quadratic_polynomial_solvers.hpp b/src/volesti/include/root_finders/quadratic_polynomial_solvers.hpp new file mode 100644 index 00000000..ad39c2ef --- /dev/null +++ b/src/volesti/include/root_finders/quadratic_polynomial_solvers.hpp @@ -0,0 +1,42 @@ +// VolEsti (volume computation and sampling library) +// Copyright (c) 2021 Vissarion Fisikopoulos +// Copyright (c) 2021 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + + +#ifndef QUADRATIC_POLYNOMIAL_SOLVERS_H +#define QUADRATIC_POLYNOMIAL_SOLVERS_H + +// The function compute the roots of a quadratic polynomial equation +template +bool solve_quadratic_polynomial(NT const& a, NT const& b, NT const& c, NT &x1, NT &x2) +{ + if (a == NT(0)) { + x1 = -c / b; + x2 = x1; + return true; + } + + NT Delta = b * b - 4.0 * a * c; + if (Delta < NT(0)) + { + return false; + } + + if (b >= NT(0)) + { + x1 = (- b - std::sqrt(Delta)) / (2.0 * a); + x2 = (2.0 * c) / (- b - std::sqrt(Delta)); + } + else + { + x1 = (2.0 * c) / (- b + std::sqrt(Delta)); + x2 = (- b + std::sqrt(Delta)) / (2.0 * a); + } + return true; +} + + +#endif + diff --git a/src/volesti/include/root_finders/root_finders.hpp b/src/volesti/include/root_finders/root_finders.hpp new file mode 100644 index 00000000..d9b36f17 --- /dev/null +++ b/src/volesti/include/root_finders/root_finders.hpp @@ -0,0 +1,28 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2020-2020 Marios Papachristou + +// Contributed and/or modified by Marios Papachristou, as part of Google Summer of Code 2020 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "newton_raphson.hpp" +#include "mp_solve_wrapper.hpp" + +#ifndef ROOT_FINDERS_HPP +#define ROOT_FINDERS_HPP + +#endif diff --git a/src/volesti/include/sampling/ellipsoid.hpp b/src/volesti/include/sampling/ellipsoid.hpp new file mode 100644 index 00000000..7507b989 --- /dev/null +++ b/src/volesti/include/sampling/ellipsoid.hpp @@ -0,0 +1,82 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2021 Vissarion Fisikopoulos +// Copyright (c) 2018-2021 Apostolos Chalkis +// Copyright (c) 2021 Vaibhav Thakkar + +//Contributed and/or modified by Vaibhav Thakkar, as part of Google Summer of Code 2021 program. + + +#ifndef SAMPLERS_ELLIPSOID_HPP +#define SAMPLERS_ELLIPSOID_HPP + +#include "sphere.hpp" +#include "Eigen/Eigen" + + +template +struct GetPointInDellipsoid +{ + typedef typename Point::FT NT; + + template + inline static Point apply(unsigned int const& dim, + Ellipsoid const& E, + RandomNumberGenerator& rng) + { + // Generate a point inside a sphere of radius 1.0 + Point p = GetPointInDsphere::apply(dim, NT(1.0), rng); + + // transform it to a point inside an ellipsoid + return Point(E.mult_Lcov(p.getCoefficients())); + } +}; + + +template +struct GetGaussianDirection +{ + typedef typename Point::FT NT; + typedef typename Eigen::Matrix VT; + + template + inline static Point apply(unsigned int const& dim, + Ellipsoid const& E, // ellipsoid representing the Gaussian distribution + RandomNumberGenerator &rng) + { + // Generate a point inside a sphere of radius 1.0 + Point p = GetDirection::apply(dim, rng, false); + + // Multiply with cholesky matrix + VT gaussian_vec = E.mult_Lcov(p.getCoefficients()); + + // convert to point + return Point(gaussian_vec); + } +}; + + +template +struct GetPointOnDellipsoid +{ + typedef typename Point::FT NT; + typedef typename Eigen::Matrix VT; + + template + inline static Point apply(unsigned int const& dim, + Ellipsoid const& E, // ellipsoid representing the Gaussian distribution + RandomNumberGenerator &rng) + { + // Generate a point inside a sphere of radius 1.0 + Point p = GetDirection::apply(dim, rng, true); + + // Multiply with cholesky matrix + VT gaussian_vec = E.mult_Lcov(p.getCoefficients()); + + // convert to point + return Point(gaussian_vec); + } +}; + + +#endif // ELLIPSOID_HPP \ No newline at end of file diff --git a/src/volesti/include/sampling/mmcs.hpp b/src/volesti/include/sampling/mmcs.hpp new file mode 100644 index 00000000..07529ad6 --- /dev/null +++ b/src/volesti/include/sampling/mmcs.hpp @@ -0,0 +1,128 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2021 Vissarion Fisikopoulos +// Copyright (c) 2021 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef MMCS_HPP +#define MMCS_HPP + +#include "diagnostics/ess_window_updater.hpp" + +/** + * The class implements a single step of the Multiphase Monte Carlo Sampling algorithm + * given in, + * + * A. Chalkis, V. Fisikopoulos, E. Tsigaridas, H. Zafeiropoulos, Geometric algorithms for sampling the flux space of metabolic networks, SoCG 21. + * + * @tparam Polytope convex polytope type + * @tparam RandomNumberGenerator random number generator type + * @tparam MT matrix type + * @tparam Point cartensian point type + * @tparam WalkTypePolicy random walk type +*/ +template +< + typename Polytope, + typename RandomNumberGenerator, + typename MT, + typename Point, + typename WalkTypePolicy +> +bool perform_mmcs_step(Polytope &P, + RandomNumberGenerator &rng, + unsigned int const& walk_length, + unsigned int const& target_ess, + unsigned int const& max_num_samples, + unsigned int const& window, + unsigned int &Neff_sampled, + unsigned int &total_samples, + unsigned int const& num_rounding_steps, + MT &TotalRandPoints, + const Point &starting_point, + unsigned int const& nburns, + bool request_rounding, + WalkTypePolicy &WalkType) +{ + typedef typename Polytope::NT NT; + typedef typename Polytope::VT VT; + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > Walk; + + bool done = false; + unsigned int points_to_sample = target_ess; + int min_eff_samples; + total_samples = 0; + MT winPoints(P.dimension(), window); + Point q(P.dimension()); + + Point p = starting_point; + + if (request_rounding) + { + TotalRandPoints.setZero(num_rounding_steps, P.dimension()); + } + else + { + TotalRandPoints.setZero(max_num_samples, P.dimension()); + } + + Walk walk(P, p, rng, WalkType.param); + ESSestimator estimator(window, P.dimension()); + + walk.template parameters_burnin(P, p, 10 + int(std::log(NT(P.dimension()))), 10, rng); + + while (!done) + { + walk.template get_starting_point(P, p, q, 10, rng); + for (int i = 0; i < window; i++) + { + walk.template apply(P, q, walk_length, rng); + winPoints.col(i) = q.getCoefficients(); + } + estimator.update_estimator(winPoints); + total_samples += window; + if (total_samples >= TotalRandPoints.rows()) + { + if (total_samples > TotalRandPoints.rows()) + { + TotalRandPoints.conservativeResize(total_samples, P.dimension()); + } + if (request_rounding || total_samples >= max_num_samples) + { + done = true; + } + } + TotalRandPoints.block(total_samples - window, 0, window, P.dimension()) = winPoints.transpose(); + if (done || total_samples >= points_to_sample) + { + estimator.estimate_effective_sample_size(); + min_eff_samples = int(estimator.get_effective_sample_size().minCoeff()); + if (done && min_eff_samples < target_ess) + { + Neff_sampled = min_eff_samples; + return false; + } + if (min_eff_samples >= target_ess) + { + Neff_sampled = min_eff_samples; + return true; + } + if (min_eff_samples > 0) + { + points_to_sample += (total_samples / min_eff_samples) * (target_ess - min_eff_samples) + 100; + } + else + { + points_to_sample = total_samples + 100; + } + } + } + return false; +} + +#endif diff --git a/src/volesti/include/sampling/parallel_mmcs.hpp b/src/volesti/include/sampling/parallel_mmcs.hpp new file mode 100644 index 00000000..a8308f4c --- /dev/null +++ b/src/volesti/include/sampling/parallel_mmcs.hpp @@ -0,0 +1,203 @@ +// VolEsti (volume computation and sampling library) +// Copyright (c) 2021 Vissarion Fisikopoulos +// Copyright (c) 2021 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef PARALLEL_MMCS_HPP +#define PARALLEL_MMCS_HPP + + +#include +#include +#include +#include "diagnostics/ess_window_updater.hpp" + +/** + * The class implements a single step of the Parallel Multiphase Monte Carlo Sampling algorithm + * given in, + * + * A. Chalkis, V. Fisikopoulos, E. Tsigaridas, H. Zafeiropoulos, Geometric algorithms for sampling the flux space of metabolic networks, SoCG 21. + * + * @tparam WalkTypePolicy random walk type + * @tparam Polytope convex polytope type + * @tparam RandomNumberGenerator random number generator type + * @tparam MT matrix type + * @tparam Point cartensian point type + * @tparam NT number type +*/ +template +< + typename WalkTypePolicy, + typename Polytope, + typename RandomNumberGenerator, + typename MT, + typename Point, + typename NT +> +bool perform_parallel_mmcs_step(Polytope &P, + RandomNumberGenerator &rng, + unsigned int const& walk_length, + unsigned int const& target_ess, + unsigned int const& max_num_samples, + unsigned int const& window, + unsigned int &Neff_sampled, + unsigned int &total_samples, + unsigned int const& num_rounding_steps, + MT &TotalRandPoints, + const Point &starting_point, + unsigned int const& nburns, + unsigned int const& num_threads, + bool request_rounding, + NT L) +{ + typedef typename Polytope::VT VT; // vector type + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > Walk; + + typedef typename WalkTypePolicy::template thread_parameters + < + NT, + Point + > _thread_parameters; + + omp_set_num_threads(num_threads); + std::vector num_starting_points_per_thread(num_threads, 0); + std::vector bound_on_num_points_per_thread(num_threads, 0); + std::vector num_generated_points_per_thread(num_threads, 0); + unsigned int jj = 0, d = P.dimension(), m = P.num_of_hyperplanes(); + bool complete = false; + + while (jj < nburns) + { + for (unsigned int i = 0; i < num_threads; i++) + { + num_starting_points_per_thread[i]++; + bound_on_num_points_per_thread[i] += window; + jj++; + } + } + + std::vector winPoints_per_thread(num_threads, MT::Zero(d, window)); + std::vector TotalRandPoints_per_thread(num_threads); + + ESSestimator estimator(window, P.dimension()); + + bool done = false, done_all = false; + unsigned int points_to_sample = target_ess; + int min_eff_samples; + total_samples = 0; + + Point pp = starting_point; + for (unsigned int i = 0; i < num_threads; i++) + { + TotalRandPoints_per_thread[i].setZero(bound_on_num_points_per_thread[i], d); + } + unsigned int upper_bound_on_total_num_of_samples; + if (request_rounding) + { + upper_bound_on_total_num_of_samples = num_rounding_steps; + } + else + { + upper_bound_on_total_num_of_samples = max_num_samples; + } + TotalRandPoints.resize(0, 0); + Walk walk(P, L); + + _thread_parameters random_walk_parameters(d, m); + walk.template parameters_burnin(P, pp, 10 + int(std::log(NT(d))), 10, rng, random_walk_parameters); + Point const p = pp; + + #pragma omp parallel + { + int thread_index = omp_get_thread_num(); + _thread_parameters thread_random_walk_parameters(d, m); + + for (unsigned int it = 0; it < num_starting_points_per_thread[thread_index]; it++) + { + if (done_all) + { + break; + } + walk.template get_starting_point(P, p, thread_random_walk_parameters, 10, rng); + for (int i = 0; i < window; i++) + { + walk.template apply(P, thread_random_walk_parameters, walk_length, rng); + winPoints_per_thread[thread_index].col(i) = thread_random_walk_parameters.p.getCoefficients(); + } + + #pragma omp critical + { + estimator.update_estimator(winPoints_per_thread[thread_index]); + } + + num_generated_points_per_thread[thread_index] += window; + + #pragma omp critical + { + total_samples += window; + } + #pragma omp single + { + if (total_samples >= upper_bound_on_total_num_of_samples) + { + done = true; + } + } + TotalRandPoints_per_thread[thread_index].block(num_generated_points_per_thread[thread_index] - window, 0, window, d) = winPoints_per_thread[thread_index].transpose(); + #pragma omp single + { + if (done || (total_samples >= points_to_sample)) + { + estimator.estimate_effective_sample_size(); + + min_eff_samples = int(estimator.get_effective_sample_size().minCoeff()); + if (done && min_eff_samples < target_ess) + { + Neff_sampled = min_eff_samples; + done_all = true; + } + if (min_eff_samples >= target_ess) + { + complete = true; + Neff_sampled = min_eff_samples; + done_all = true; + } + if (min_eff_samples > 0 && !done_all) + { + points_to_sample += (total_samples / min_eff_samples) * (target_ess - min_eff_samples) + 100; + } + else if (!done_all) + { + points_to_sample = total_samples + 100; + } + } + } + } + } + + estimator.estimate_effective_sample_size(); + min_eff_samples = int(estimator.get_effective_sample_size().minCoeff()); + Neff_sampled = min_eff_samples; + if (min_eff_samples >= target_ess) + { + complete = true; + } + + unsigned int current_num_samples = 0; + for (unsigned int i = 0; i < num_threads; i++) + { + TotalRandPoints.conservativeResize(TotalRandPoints.rows() + num_generated_points_per_thread[i], d); + TotalRandPoints.block(current_num_samples, 0, num_generated_points_per_thread[i], d) = TotalRandPoints_per_thread[i].block(0, 0, num_generated_points_per_thread[i], d); + current_num_samples += num_generated_points_per_thread[i]; + TotalRandPoints_per_thread[i].resize(0, 0); + } + + return complete; +} + +#endif diff --git a/src/volesti/include/sampling/random_point_generators.hpp b/src/volesti/include/sampling/random_point_generators.hpp new file mode 100644 index 00000000..e70e1461 --- /dev/null +++ b/src/volesti/include/sampling/random_point_generators.hpp @@ -0,0 +1,395 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SAMPLERS_RANDOM_POINT_GENERATORS_HPP +#define SAMPLERS_RANDOM_POINT_GENERATORS_HPP + +template +< + typename Walk +> +struct RandomPointGenerator +{ + template + < + typename Polytope, + typename Point, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator, + typename Parameters + > + static void apply(Polytope& P, + Point &p, // a point to start + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng, + Parameters const& parameters) + { + Walk walk(P, p, rng, parameters); + for (unsigned int i=0; i + static void apply(Polytope& P, + Point &p, // a point to start + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng) + { + Walk walk(P, p, rng); + for (unsigned int i=0; i +struct MultivariateGaussianRandomPointGenerator +{ + template + < + typename Polytope, + typename Point, + typename Ellipsoid, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator, + typename Parameters + > + static void apply(Polytope& P, + Point &p, // a point to start + Ellipsoid const& E, // ellipsoid representing the Gaussian distribution + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng, + Parameters const& parameters) + { + Walk walk(P, p, E, rng, parameters); + for (unsigned int i=0; i + static void apply(Polytope& P, + Point &p, // a point to start + Ellipsoid const& E, // ellipsoid representing the Gaussian distribution + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng) + { + Walk walk(P, p, E, rng); + for (unsigned int i=0; i +struct GaussianRandomPointGenerator +{ + template + < + typename Polytope, + typename Point, + typename NT, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator + > + static void apply(Polytope& P, + Point &p, // a point to start + NT const& a_i, + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng) + { + Walk walk(P, p, a_i, rng); + for (unsigned int i=0; i + static void apply(Polytope& P, + Point &p, // a point to start + NT const& a_i, + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng, + Parameters const& parameters) + { + Walk walk(P, p, a_i, rng, parameters); + + for (unsigned int i=0; i +struct BoundaryRandomPointGenerator +{ + template + < + typename Polytope, + typename Point, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator + > + static void apply(Polytope& P, + Point &p, // a point to start + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng) + { + Walk walk(P, p, rng); + Point p1(P.dimension()), p2(P.dimension()); + for (unsigned int i=0; i +struct LogconcaveRandomPointGenerator +{ + + template + < typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator + > + static void apply(unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng, + Walk &walk) + { + typedef double NT; + + for (unsigned int i = 0; i < rnum; ++i) + { + // Gather one sample + walk.apply(rng, walk_length); + + // Use PushBackWalkPolicy + policy.apply(randPoints, walk.x); + } + } +}; + +template +< + typename Walk +> +struct CrhmcRandomPointGenerator +{ + + template + < + typename Polytope, + typename Point, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator, + typename NegativeGradientFunctor, + typename NegativeLogprobFunctor, + typename Parameters + > + static void apply(Polytope &P, + Point &p, // a point to start + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng, + NegativeGradientFunctor &F, + NegativeLogprobFunctor &f, + Parameters ¶meters, + Walk &walk, + int simdLen=1, + bool raw_output= false) + { + typedef typename Walk::MT MT; + for (unsigned int i = 0; i < std::ceil((float)rnum/simdLen); ++i) + { + // Gather one sample + walk.apply(rng, walk_length); + if(walk.P.terminate){return;} + MT x; + if(raw_output){ + x=walk.x; + }else{ + x=walk.getPoints(); + } + if((i + 1) * simdLen > rnum){ + for(int j = 0; j < rnum-simdLen*i; j++){ + Point p = Point(x.col(j)); + policy.apply(randPoints, p); + } + break; + } + // Use PushBackWalkPolicy + for(int j=0; j +struct ExponentialRandomPointGenerator +{ + template + < + typename Polytope, + typename Point, + typename NT, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator + > + static void apply(Polytope& P, + Point &p, // a point to start + Point const& c, // bias function + NT const& T, // temperature/variance + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng) + { + Walk walk(P, p, c, T, rng); + bool success; + for (unsigned int i=0; i + static void apply(Polytope& P, + Point &p, // a point to start + Point const& c, // bias function + NT const& T, // temperature/variance + unsigned int const& rnum, + unsigned int const& walk_length, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng, + Parameters const& parameters) + { + Walk walk(P, p, c, T, rng, parameters); + bool success; + + for (unsigned int i=0; i +#include +#include + + +template +struct policy_storing +{ + template + static void store(WalkPolicy &policy, PointList &randPoints, ThreadParameters &thread_random_walk_parameters) + { + policy.apply(randPoints, thread_random_walk_parameters.p); + } +}; + + +template <> +struct policy_storing +{ + template + static void store(WalkPolicy &policy, PointList &randPoints, ThreadParameters &thread_random_walk_parameters) + { + policy.apply(randPoints, thread_random_walk_parameters.p1); + policy.apply(randPoints, thread_random_walk_parameters.p2); + } +}; + +template <> +struct policy_storing : policy_storing +{}; + + +template +< + typename Walk +> +struct RandomPointGeneratorMultiThread +{ + template + < + typename Polytope, + typename Point, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator, + typename Parameters + > + static void apply(Polytope& P, + Point &p, // a point to start + unsigned int const& rnum, + unsigned int const& walk_length, + unsigned int const& num_threads, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng, + Parameters const& parameters) + { + typedef typename Point::FT NT; + typedef typename Walk::thread_parameters_ _thread_parameters; + + omp_set_num_threads(num_threads); + unsigned int d = P.dimension(), m = P.num_of_hyperplanes(); + + std::vector num_points_per_thread(rnum%num_threads, rnum/num_threads+1); + std::vector a(num_threads - rnum%num_threads, rnum/num_threads); + num_points_per_thread.insert(num_points_per_thread.end(), a.begin(), a.end()); + + _thread_parameters thread_random_walk_parameters_temp(d, m); + Walk walk(P, thread_random_walk_parameters_temp, rng, parameters); + + #pragma omp parallel + { + int thread_index = omp_get_thread_num(); + _thread_parameters thread_random_walk_parameters(d, m); + thread_random_walk_parameters = thread_random_walk_parameters_temp; + + for (unsigned int it = 0; it < num_points_per_thread[thread_index]; it++) + { + walk.template apply(P, thread_random_walk_parameters, walk_length, rng); + #pragma omp critical + { + policy_storing::template store(policy, randPoints, thread_random_walk_parameters); + } + } + } + } + + template + < + typename Polytope, + typename Point, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator + > + static void apply(Polytope& P, + Point &p, // a point to start + unsigned int const& rnum, + unsigned int const& walk_length, + unsigned int const& num_threads, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng) + { + typedef typename Point::FT NT; + typedef typename Walk::thread_parameters_ _thread_parameters; + + omp_set_num_threads(num_threads); + unsigned int d = P.dimension(), m = P.num_of_hyperplanes(); + + std::vector num_points_per_thread(rnum%num_threads, rnum/num_threads+1); + std::vector a(num_threads - rnum%num_threads, rnum/num_threads); + num_points_per_thread.insert(num_points_per_thread.end(), a.begin(), a.end()); + + _thread_parameters thread_random_walk_parameters_temp(d, m); + Walk walk(P, thread_random_walk_parameters_temp, rng); + + #pragma omp parallel + { + int thread_index = omp_get_thread_num(); + _thread_parameters thread_random_walk_parameters(d, m); + thread_random_walk_parameters = thread_random_walk_parameters_temp; + + for (unsigned int it = 0; it < num_points_per_thread[thread_index]; it++) + { + walk.template apply(P, thread_random_walk_parameters, walk_length, rng); + #pragma omp critical + { + policy_storing::template store(policy, randPoints, thread_random_walk_parameters); + } + } + } + } +}; + + + +template +< + typename Walk +> +struct GaussianPointGeneratorMultiThread +{ + template + < + typename Polytope, + typename Point, + typename NT, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator, + typename Parameters + > + static void apply(Polytope& P, + Point &p, // a point to start + NT const& a_i, + unsigned int const& rnum, + unsigned int const& walk_length, + unsigned int const& num_threads, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng, + Parameters const& parameters) + { + typedef typename Walk::thread_parameters_ _thread_parameters; + + omp_set_num_threads(num_threads); + unsigned int d = P.dimension(), m = P.num_of_hyperplanes(); + + std::vector num_points_per_thread(rnum%num_threads, rnum/num_threads+1); + std::vector a(num_threads - rnum%num_threads, rnum/num_threads); + num_points_per_thread.insert(num_points_per_thread.end(), a.begin(), a.end()); + + _thread_parameters thread_random_walk_parameters_temp(d, m); + Walk walk(P, thread_random_walk_parameters_temp, a_i, rng, parameters); + + #pragma omp parallel + { + int thread_index = omp_get_thread_num(); + _thread_parameters thread_random_walk_parameters(d, m); + thread_random_walk_parameters = thread_random_walk_parameters_temp; + + for (unsigned int it = 0; it < num_points_per_thread[thread_index]; it++) + { + walk.template apply(P, thread_random_walk_parameters, a_i, walk_length, rng); + #pragma omp critical + { + policy_storing::template store(policy, randPoints, thread_random_walk_parameters); + } + } + } + } + + template + < + typename Polytope, + typename Point, + typename NT, + typename PointList, + typename WalkPolicy, + typename RandomNumberGenerator + > + static void apply(Polytope& P, + Point &p, // a point to start + NT const& a_i, + unsigned int const& rnum, + unsigned int const& walk_length, + unsigned int const& num_threads, + PointList &randPoints, + WalkPolicy &policy, + RandomNumberGenerator &rng) + { + typedef typename Walk::thread_parameters_ _thread_parameters; + + omp_set_num_threads(num_threads); + unsigned int d = P.dimension(), m = P.num_of_hyperplanes(); + + std::vector num_points_per_thread(rnum%num_threads, rnum/num_threads+1); + std::vector a(num_threads - rnum%num_threads, rnum/num_threads); + num_points_per_thread.insert(num_points_per_thread.end(), a.begin(), a.end()); + + _thread_parameters thread_random_walk_parameters_temp(d, m); + Walk walk(P, thread_random_walk_parameters_temp, a_i, rng); + + #pragma omp parallel + { + int thread_index = omp_get_thread_num(); + _thread_parameters thread_random_walk_parameters(d, m); + thread_random_walk_parameters = thread_random_walk_parameters_temp; + + for (unsigned int it = 0; it < num_points_per_thread[thread_index]; it++) + { + walk.template apply(P, thread_random_walk_parameters, a_i, walk_length, rng); + #pragma omp critical + { + policy_storing::template store(policy, randPoints, thread_random_walk_parameters); + } + } + } + } +}; + + +#endif // SAMPLERS_RANDOM_POINT_GENERATORS_MULTITHREAD_HPP diff --git a/src/volesti/include/sampling/sample_correlation_matrices.hpp b/src/volesti/include/sampling/sample_correlation_matrices.hpp new file mode 100644 index 00000000..9a1de231 --- /dev/null +++ b/src/volesti/include/sampling/sample_correlation_matrices.hpp @@ -0,0 +1,153 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2020 Apostolos Chalkis + +// Contributed by Huu Phuoc Le as part of Google Summer of Code 2022 program + +// Licensed under GNU LGPL.3, see LICENCE file + +/// Functions to sample correlation matrices w.r.t. a truncated density + +#ifndef VOLESTI_SAMPLING_SAMPLE_CORRELATION_MATRICES_HPP +#define VOLESTI_SAMPLING_SAMPLE_CORRELATION_MATRICES_HPP + +#include + +// New implementations for sampling correlation matrices with CorrelationSpectrahedron and CorrelationSpectrahedron_MT + +template +< + typename WalkTypePolicy, + typename PointType, + typename RNGType, + typename PointList +> +void uniform_correlation_sampling(const unsigned int &n, + PointList &randPoints, + const unsigned int &walkL, + const unsigned int &num_points, + unsigned int const& nburns){ + CorrelationSpectrahedron P(n); + const unsigned int d = P.dimension(); + PointType startingPoint(d); + RNGType rng(d); + + uniform_sampling(randPoints, P, rng, walkL, num_points, startingPoint, nburns); +} + +template +< + typename WalkTypePolicy, + typename PointType, + typename RNGType, + typename PointList +> +void uniform_correlation_sampling_MT( const unsigned int &n, + PointList &randPoints, + const unsigned int &walkL, + const unsigned int &num_points, + unsigned int const& nburns){ + CorrelationSpectrahedron_MT P(n); + const unsigned int d = P.dimension(); + PointType startingPoint(n); + RNGType rng(d); + + uniform_sampling(randPoints, P, rng, walkL, num_points, startingPoint, nburns); +} + +template +< + typename WalkTypePolicy, + typename PointType, + typename RNGType, + typename PointList, + typename NT +> +void gaussian_correlation_sampling( const unsigned int &n, + PointList &randPoints, + const unsigned int &walkL, + const unsigned int &num_points, + const NT &a, + unsigned int const& nburns = 0){ + CorrelationSpectrahedron P(n); + const unsigned int d = P.dimension(); + PointType startingPoint(d); + RNGType rng(d); + + gaussian_sampling(randPoints, P, rng, walkL, num_points, a, startingPoint, nburns); +} + +template +< + typename WalkTypePolicy, + typename PointType, + typename RNGType, + typename PointList, + typename NT +> +void gaussian_correlation_sampling_MT( const unsigned int &n, + PointList &randPoints, + const unsigned int &walkL, + const unsigned int &num_points, + const NT &a, + unsigned int const& nburns = 0){ + CorrelationSpectrahedron_MT P(n); + const unsigned int d = P.dimension(); + PointType startingPoint(n); + RNGType rng(d); + + gaussian_sampling(randPoints, P, rng, walkL, num_points, a, startingPoint, nburns); +} + +template +< + typename WalkTypePolicy, + typename PointType, + typename RNGType, + typename PointList, + typename NT, + typename VT +> +void exponential_correlation_sampling( const unsigned int &n, + PointList &randPoints, + const unsigned int &walkL, + const unsigned int &num_points, + const VT &c, + const NT &T, + unsigned int const& nburns = 0){ + CorrelationSpectrahedron P(n); + const unsigned int d = P.dimension(); + PointType startingPoint(d); + RNGType rng(d); + PointType _c(c); + + exponential_sampling(randPoints, P, rng, walkL, num_points, _c, T, startingPoint, nburns); +} + +template +< + typename WalkTypePolicy, + typename PointType, + typename RNGType, + typename PointList, + typename NT, + typename VT +> +void exponential_correlation_sampling_MT( const unsigned int &n, + PointList &randPoints, + const unsigned int &walkL, + const unsigned int &num_points, + const VT &c, + const NT &T, + unsigned int const& nburns = 0){ + CorrelationSpectrahedron_MT P(n); + const unsigned int d = P.dimension(); + PointType startingPoint(n); + RNGType rng(d); + PointType _c(c); + + exponential_sampling(randPoints, P, rng, walkL, num_points, _c, T, startingPoint, nburns); +} + +#endif //VOLESTI_SAMPLING_SAMPLE_CORRELATION_MATRICES_HPP diff --git a/src/volesti/include/sampling/sampling.hpp b/src/volesti/include/sampling/sampling.hpp new file mode 100644 index 00000000..69618609 --- /dev/null +++ b/src/volesti/include/sampling/sampling.hpp @@ -0,0 +1,603 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2018 Vissarion Fisikopoulos, Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// VolEsti is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// VolEsti is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// See the file COPYING.LESSER for the text of the GNU Lesser General +// Public License. If you did not receive this file along with HeaDDaCHe, +// see . + + +#ifndef SAMPLE_ONLY_H +#define SAMPLE_ONLY_H + +template +void uniform_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + const unsigned int &walk_len, + const unsigned int &rnum, + const Point &starting_point, + unsigned int const& nburns) +{ + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + //RandomNumberGenerator rng(P.dimension()); + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + typedef RandomPointGenerator RandomPointGenerator; + if (nburns > 0) { + RandomPointGenerator::apply(P, p, nburns, walk_len, randPoints, + push_back_policy, rng); + randPoints.clear(); + } + RandomPointGenerator::apply(P, p, rnum, walk_len, randPoints, + push_back_policy, rng); + + +} + +template < + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename WalkTypePolicy, + typename Point +> +void uniform_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + WalkTypePolicy &WalkType, + const unsigned int &walk_len, + const unsigned int &rnum, + const Point &starting_point, + unsigned int const& nburns) +{ + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + //RandomNumberGenerator rng(P.dimension()); + PushBackWalkPolicy push_back_policy; + typedef RandomPointGenerator RandomPointGenerator; + + Point p = starting_point; + if (nburns > 0) { + RandomPointGenerator::apply(P, p, nburns, walk_len, randPoints, + push_back_policy, rng, WalkType.param); + randPoints.clear(); + } + RandomPointGenerator::apply(P, p, rnum, walk_len, randPoints, + push_back_policy, rng, WalkType.param); +} + + +template +< + typename WalkTypePolicy, + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename Point +> +void uniform_sampling_boundary(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + const unsigned int &walk_len, + const unsigned int &rnum, + const Point &starting_point, + unsigned int const& nburns) +{ + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + //RandomNumberGenerator rng(P.dimension()); + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + typedef BoundaryRandomPointGenerator BoundaryRandomPointGenerator; + if (nburns > 0) { + BoundaryRandomPointGenerator::apply(P, p, nburns, walk_len, + randPoints, push_back_policy, rng); + randPoints.clear(); + } + unsigned int n = rnum / 2; + BoundaryRandomPointGenerator::apply(P, p, rnum / 2, walk_len, + randPoints, push_back_policy, rng); + +} + + +template +< + typename WalkTypePolicy, + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename NT, + typename Point +> +void gaussian_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + const unsigned int &walk_len, + const unsigned int &rnum, + const NT &a, + const Point &starting_point, + unsigned int const& nburns) +{ + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + //RandomNumberGenerator rng(P.dimension()); + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + typedef GaussianRandomPointGenerator RandomPointGenerator; + if (nburns > 0) { + RandomPointGenerator::apply(P, p, a, nburns, walk_len, randPoints, + push_back_policy, rng); + randPoints.clear(); + } + RandomPointGenerator::apply(P, p, a, rnum, walk_len, randPoints, + push_back_policy, rng); + + +} + + +template < + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename WalkTypePolicy, + typename NT, + typename Point + > +void gaussian_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + WalkTypePolicy &WalkType, + const unsigned int &walk_len, + const unsigned int &rnum, + const NT &a, + const Point &starting_point, + unsigned int const& nburns) +{ + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + //RandomNumberGenerator rng(P.dimension()); + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + typedef GaussianRandomPointGenerator RandomPointGenerator; + if (nburns > 0) { + RandomPointGenerator::apply(P, p, a, nburns, walk_len, randPoints, + push_back_policy, rng, WalkType.param); + randPoints.clear(); + } + RandomPointGenerator::apply(P, p, a, rnum, walk_len, randPoints, + push_back_policy, rng, WalkType.param); +} + +template < + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename WalkTypePolicy, + typename NT, + typename Point, + typename NegativeGradientFunctor, + typename NegativeLogprobFunctor, + typename Solver + > +void logconcave_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + const unsigned int &walk_len, + const unsigned int &rnum, + const Point &starting_point, + unsigned int const& nburns, + NegativeGradientFunctor &F, + NegativeLogprobFunctor &f) +{ + typedef typename WalkTypePolicy::template Walk + < + Point, + Polytope, + RandomNumberGenerator, + NegativeGradientFunctor, + NegativeLogprobFunctor, + Solver + > walk; + + typedef typename WalkTypePolicy::template parameters + < + NT, + NegativeGradientFunctor + > walk_params; + + // Initialize random walk parameters + unsigned int dim = starting_point.dimension(); + walk_params params(F, dim); + + if (F.params.eta > 0) { + params.eta = F.params.eta; + } + + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + walk logconcave_walk(&P, p, F, f, params); + + typedef LogconcaveRandomPointGenerator RandomPointGenerator; + + if (nburns > 0) { + RandomPointGenerator::apply(nburns, walk_len, randPoints, + push_back_policy, rng, logconcave_walk); + } + logconcave_walk.disable_adaptive(); + randPoints.clear(); + + RandomPointGenerator::apply(rnum, walk_len, randPoints, + push_back_policy, rng, logconcave_walk); +} +#include "preprocess/crhmc/crhmc_input.h" +#include "preprocess/crhmc/crhmc_problem.h" +template + < + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename WalkTypePolicy, + typename NT, + typename Point, + typename NegativeGradientFunctor, + typename NegativeLogprobFunctor, + typename HessianFunctor, + typename Solver + > +void crhmc_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + const int walk_len, + const unsigned int rnum, + const unsigned int nburns, + NegativeGradientFunctor &F, + NegativeLogprobFunctor &f, + HessianFunctor &h, + int simdLen = 1, + bool raw_output=false) { + typedef typename Polytope::MT MatrixType; + typedef crhmc_input + < + MatrixType, + Point, + NegativeLogprobFunctor, + NegativeGradientFunctor, + HessianFunctor + > Input; + Input input = convert2crhmc_input(P, f, F, h); + typedef crhmc_problem CrhmcProblem; + CrhmcProblem problem = CrhmcProblem(input); + if(problem.terminate){return;} + typedef typename WalkTypePolicy::template Walk + < + Point, + CrhmcProblem, + RandomNumberGenerator, + NegativeGradientFunctor, + NegativeLogprobFunctor, + Solver + > walk; + typedef typename WalkTypePolicy::template parameters + < + NT, + NegativeGradientFunctor + > walk_params; + Point p = Point(problem.center); + problem.options.simdLen=simdLen; + walk_params params(input.df, p.dimension(), problem.options); + + if (input.df.params.eta > 0) { + params.eta = input.df.params.eta; + } + + PushBackWalkPolicy push_back_policy; + + walk crhmc_walk = walk(problem, p, input.df, input.f, params); + + typedef CrhmcRandomPointGenerator RandomPointGenerator; + + RandomPointGenerator::apply(problem, p, nburns, walk_len, randPoints, + push_back_policy, rng, F, f, params, crhmc_walk); + //crhmc_walk.disable_adaptive(); + randPoints.clear(); + RandomPointGenerator::apply(problem, p, rnum, walk_len, randPoints, + push_back_policy, rng, F, f, params, crhmc_walk, simdLen, raw_output); +} +#include "ode_solvers/ode_solvers.hpp" +template < + typename Polytope, + typename RNGType, + typename PointList, + typename NegativeGradientFunctor, + typename NegativeLogprobFunctor, + typename HessianFunctor, + typename CRHMCWalk, + int simdLen=1 +> +void execute_crhmc(Polytope &P, RNGType &rng, PointList &randPoints, + unsigned int const& walkL, unsigned int const& numpoints, + unsigned int const& nburns, NegativeGradientFunctor *F=NULL, + NegativeLogprobFunctor *f=NULL, HessianFunctor *h=NULL, bool raw_output= false){ +typedef typename Polytope::MT MatrixType; +typedef typename Polytope::PointType Point; +typedef typename Point::FT NT; +if(h!=NULL){ +typedef crhmc_input + < + MatrixType, + Point, + NegativeLogprobFunctor, + NegativeGradientFunctor, + HessianFunctor + > Input; +typedef crhmc_problem CrhmcProblem; +crhmc_sampling < + PointList, + Polytope, + RNGType, + CRHMCWalk, + NT, + Point, + NegativeGradientFunctor, + NegativeLogprobFunctor, + HessianFunctor, + ImplicitMidpointODESolver < + Point, + NT, + CrhmcProblem, + NegativeGradientFunctor, + simdLen + > +>(randPoints, P, rng, walkL, numpoints, nburns, *F, *f, *h, simdLen, raw_output); +}else{ + typedef crhmc_input + < + MatrixType, + Point, + NegativeLogprobFunctor, + NegativeGradientFunctor, + ZeroFunctor + > Input; + typedef crhmc_problem CrhmcProblem; + ZeroFunctor zerof; +crhmc_sampling < + PointList, + Polytope, + RNGType, + CRHMCWalk, + NT, + Point, + NegativeGradientFunctor, + NegativeLogprobFunctor, + ZeroFunctor, + ImplicitMidpointODESolver < + Point, + NT, + CrhmcProblem, + NegativeGradientFunctor, + simdLen + > +>(randPoints, P, rng, walkL, numpoints, nburns, *F, *f, zerof, simdLen, raw_output); +} +} +template +< + typename WalkTypePolicy, + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename NT, + typename Point +> +void exponential_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + const unsigned int &walk_len, + const unsigned int &rnum, + const Point &c, + const NT &a, + const Point &starting_point, + unsigned int const& nburns) +{ + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + typedef ExponentialRandomPointGenerator RandomPointGenerator; + if (nburns > 0) { + RandomPointGenerator::apply(P, p, c, a, nburns, walk_len, randPoints, + push_back_policy, rng); + randPoints.clear(); + } + RandomPointGenerator::apply(P, p, c, a, rnum, walk_len, randPoints, + push_back_policy, rng); +} + + +template < + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename WalkTypePolicy, + typename NT, + typename Point + > +void exponential_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + WalkTypePolicy &WalkType, + const unsigned int &walk_len, + const unsigned int &rnum, + const Point &c, + const NT &a, + const Point &starting_point, + unsigned int const& nburns) +{ + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + typedef ExponentialRandomPointGenerator RandomPointGenerator; + if (nburns > 0) { + RandomPointGenerator::apply(P, p, c, a, nburns, walk_len, randPoints, + push_back_policy, rng, WalkType.param); + randPoints.clear(); + } + RandomPointGenerator::apply(P, p, c, a, rnum, walk_len, randPoints, + push_back_policy, rng, WalkType.param); +} + + +template +< + typename WalkTypePolicy, + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename NT, + typename Point +> +void exponential_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + const unsigned int &walk_len, + const unsigned int &rnum, + const Point &c, + const NT &a, + const NT &eta, + const Point &starting_point, + unsigned int const& nburns) +{ + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + typedef ExponentialRandomPointGenerator RandomPointGenerator; + if (nburns > 0) { + RandomPointGenerator::apply(P, p, c, a, eta, nburns, walk_len, randPoints, + push_back_policy, rng); + randPoints.clear(); + } + RandomPointGenerator::apply(P, p, c, a, eta, rnum, walk_len, randPoints, + push_back_policy, rng); +} + + +template < + typename PointList, + typename Polytope, + typename RandomNumberGenerator, + typename WalkTypePolicy, + typename NT, + typename Point + > +void exponential_sampling(PointList &randPoints, + Polytope &P, + RandomNumberGenerator &rng, + WalkTypePolicy &WalkType, + const unsigned int &walk_len, + const unsigned int &rnum, + const Point &c, + const NT &a, + const NT &eta, + const Point &starting_point, + unsigned int const& nburns) +{ + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + PushBackWalkPolicy push_back_policy; + + Point p = starting_point; + + typedef ExponentialRandomPointGenerator RandomPointGenerator; + if (nburns > 0) { + RandomPointGenerator::apply(P, p, c, a, eta, nburns, walk_len, randPoints, + push_back_policy, rng, WalkType.param); + randPoints.clear(); + } + RandomPointGenerator::apply(P, p, c, a, eta, rnum, walk_len, randPoints, + push_back_policy, rng, WalkType.param); +} + + +#endif diff --git a/src/volesti/include/sampling/simplex.hpp b/src/volesti/include/sampling/simplex.hpp new file mode 100644 index 00000000..92534993 --- /dev/null +++ b/src/volesti/include/sampling/simplex.hpp @@ -0,0 +1,392 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2018-2020 Vissarion Fisikopoulos, Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// VolEsti is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// VolEsti is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// See the file COPYING.LESSER for the text of the GNU Lesser General +// Public License. If you did not receive this file along with HeaDDaCHe, +// see . + +#ifndef SAMPLERS_SIMPLEX_HPP +#define SAMPLERS_SIMPLEX_HPP + +#ifndef isnan + using std::isnan; +#endif + +template +void Sam_Unit(unsigned int dim, + unsigned int num, + std::list &points, + double seed = std::numeric_limits::signaling_NaN()) +{ + + unsigned int j,i,x_rand,M=2147483647,pr,divisors,pointer; // M is the largest possible integer + std::vector x_vec; + std::vector y; + + boost::random::uniform_int_distribution<> uidist(1,M); + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + if (!isnan(seed)) { + unsigned rng_seed2 = seed; + rng.seed(rng_seed2); + } + //unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + //RNGType rng(seed); + + if (dim<=60){ + + x_vec.assign(dim+1,0); + + for (i=0; i filter; + bool t1=true,t2=true; + pr=3*dim+1; + if(pr%2==0) pr+=1; + + while(t1){ + t2=true; + divisors=(int)floor(sqrt((NT)pr))+1; + for (i=3; i x_vec2; + NT Ti,sum; + + x_vec2.assign(dim+1,0.0); + + // Generate the number of points requested + for (i=0; i +void Sam_Canon_Unit(unsigned int dim, + unsigned int num, + std::list &points, + double seed = std::numeric_limits::signaling_NaN()) +{ + + unsigned int j,i,x_rand,M=2147483647,pointer; // M is the largest possible integer + std::vector y; + dim--; + boost::random::uniform_int_distribution<> uidist(1,M); + + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + if (!isnan(seed)) { + unsigned rng_seed2 = seed; + rng.seed(rng_seed2); + } + + std::vector x_vec2; + NT Ti,sum; + + x_vec2.assign(dim+1,0.0); + + // Generate the number of points requested + for (i=0; i +void Sam_arb_simplex(const Vpolytope &P, unsigned int num, PointList &points){ + + typedef typename Vpolytope::MT MT; + typedef typename Vpolytope::NT NT; + typedef typename Vpolytope::rngtype RNGType; + typedef typename Vpolytope::PolytopePoint Point; + + MT V = P.get_mat(); + std::vector vec_point; + + unsigned int n=V.rows(),j,i,k,x_rand,M=2147483647,pr,divisors,pointer; // M is the largest possible integer + unsigned int dim = V.cols(); + std::vector temp_p(dim, 0.0); + std::vector x_vec; + std::vector y; + + for (int i = 0; i < V.rows(); ++i) { + for (int j = 0; j < V.cols(); ++j) { + temp_p[j] = V(i,j); + } + vec_point.push_back(Point(dim, temp_p.begin(), temp_p.end())); + } + typename std::vector::iterator it_beg = vec_point.begin(); + + NT Xj; + Point p0=*it_beg; + + boost::random::uniform_int_distribution<> uidist(1,M); + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(seed); + + if (dim<=60){ + + x_vec.assign(dim+2,0); x_vec[dim+1]=M; + + for (i=0; i filter; + x_vec.assign(dim+2,0); x_vec[dim+1]=M; + + bool t1=true,t2=true; + pr=3*dim+1; + if(pr%2==0) pr+=1; + + while(t1){ + t2=true; + divisors=(int)floor(sqrt((NT)pr))+1; + for (i=3; i x_vec2; + NT Ti,sum; + x_vec2.assign(dim+1,0.0); + + // Generate the number of points requested + for (i=0; i +struct GetDirection +{ + typedef typename Point::FT NT; + + template + inline static Point apply(unsigned int const& dim, + RandomNumberGenerator &rng, + bool normalize=true) + { + NT normal = NT(0); + Point p(dim); + NT* data = p.pointerToData(); + + if(normalize) + { + for (unsigned int i=0; i +struct GetDirection> +{ + template + inline static CorreMatrix apply(unsigned int const& dim, + RandomNumberGenerator &rng, + bool normalize=true) + { + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + + unsigned int n = std::ceil(std::sqrt(2*dim)); + MT mat = MT::Zero(n,n); + NT normal = NT(0), coeff; + + int i, j; + + if(normalize) + { + for(i = 0; i < n ; ++i) + { + for(j = 0; j < i; ++j) + { + coeff = rng.sample_ndist(); + mat(i,j) = coeff; + normal += coeff * coeff; + } + } + normal = NT(1)/std::sqrt(normal); + mat *= normal; + }else + { + for(i = 0; i < n ; ++i) + { + for(j = 0; j < i; ++j) + { + coeff = rng.sample_ndist(); + mat(i,j) = coeff; + } + } + } + return CorreMatrix(mat); + } +}; + +template +struct GetPointInDsphere +{ + template + inline static Point apply(unsigned int const& dim, + NT const& radius, + RandomNumberGenerator &rng) + { + Point p = GetDirection::apply(dim, rng); + NT U = rng.sample_urdist(); + U = std::pow(U, NT(1)/(NT(dim))); + p *= radius * U; + return p; + } +}; + +template +struct GetPointOnDsphere +{ + template + inline static Point apply(unsigned int const& dim, + NT const& radius, + RandomNumberGenerator &rng) + { + Point p = GetDirection::apply(dim, rng); + if (radius != 0) p *= radius; + return p; + } +}; + + + +#endif // SPHERE_HPP diff --git a/src/volesti/include/sos/NonSymmetricIPM.h b/src/volesti/include/sos/NonSymmetricIPM.h new file mode 100644 index 00000000..b5c5b95e --- /dev/null +++ b/src/volesti/include/sos/NonSymmetricIPM.h @@ -0,0 +1,333 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file +#ifndef SOS_NONSYMMETRICIPM_H +#define SOS_NONSYMMETRICIPM_H + +#include "barriers/LHSCB.h" +#include "barriers/ProductBarrier.h" +#include +#include +#include + + +template +class Instance { +public: + Constraints constraints; + LHSCB *barrier; +}; + +template +class DirectionDecomposition { +public: + DirectionDecomposition(Vector v, unsigned const n, unsigned const m) { + assert(v.rows() == m + n + 1 + n + 1); + y = v.block(0, 0, m, 1); + x = v.block(m, 0, n, 1); + tau = v.block(m + n, 0, 1, 1).sum(); + s = v.block(m + n + 1, 0, n, 1); + kappa = v.block(m + n + 1 + n, 0, 1, 1).sum(); + } + + friend std::ostream &operator<<(std::ostream &os, const DirectionDecomposition &dir); + + Vector y, x, s; + T kappa, tau; +}; + +template +class ErrorConstants { +public: + + ErrorConstants() {}; + + template + ErrorConstants(const ErrorConstants other) { + //Note: Boost Dependency + primal = boost::numeric_cast(other.primal); + dual = boost::numeric_cast(other.dual); + complementary = boost::numeric_cast(other.complementary); + } + + void set(Matrix A, Vector b, Vector c) { + //Constant are used to conform with the implementation by Papp & Yildiz. + primal = std::max(T(1), sqrt(pow(A.norm(), 2) + pow(b.norm(), 2))); + Matrix Id = Matrix::Identity(A.cols(), A.cols()); + dual = std::max(T(1), sqrt(pow(A.transpose().norm(), 2) + pow(Id.norm(), 2) + pow(c.norm(), 2))); + complementary = sqrt(pow(c.norm(), 2) + pow(b.norm(), 2) + 1); + } + + T primal; + T dual; + T complementary; +}; + +template +class NonSymmetricIPM { + + typedef Matrix Matrix; + typedef Vector Vector; + +public: + + NonSymmetricIPM(Matrix &A_, Vector &b_, Vector &c_, LHSCB *barrier_); + + NonSymmetricIPM(Instance &instance) : NonSymmetricIPM(instance.constraints.A, + instance.constraints.b, instance.constraints.c, + instance.barrier) { + _logger->set_level(spdlog::level::info); + }; + + NonSymmetricIPM(Instance &, std::string); + + + template + void cast_members_from(const NonSymmetricIPM &other) { + A_sparse = other.A_sparse.template cast(); + _basis_ker_A = other._basis_ker_A.template cast(); + _config = other._config; + + _num_predictor_steps = other._num_predictor_steps; + _num_corrector_steps = other._num_corrector_steps; + _param_step_length_predictor = boost::numeric_cast(other._param_step_length_predictor); + _step_length_predictor = boost::numeric_cast(other._step_length_predictor); + _step_length_corrector = boost::numeric_cast(other._step_length_corrector); + _epsilon = boost::numeric_cast(other._epsilon); + + _large_neighborhood = boost::numeric_cast(other._large_neighborhood); + _small_neighborhood = boost::numeric_cast(other._small_neighborhood); + //copy current solution + + x = other.x.template cast(); + y = other.y.template cast(); + s = other.s.template cast(); + kappa = boost::numeric_cast(other.kappa); + tau = boost::numeric_cast(other.tau); + + _last_predictor_direction = other._last_predictor_direction.template cast(); + + _err_consts = ErrorConstants(other._err_consts); + + //copy configuration; + + _use_line_search = other._use_line_search; + } + + template + NonSymmetricIPM *cast_with_product_barrier() { + + //cast initialisatino data + Eigen::Matrix A_ = A.template cast(); + Eigen::Matrix b_ = b.template cast(); + Eigen::Matrix c_ = c.template cast(); + + //TODO: High priority. Figure out how to undo the cast to the ProductBarrier. + ProductBarrier *barrier_ = static_cast *>(_barrier)->template cast(); + + assert(barrier_ != nullptr); + + NonSymmetricIPM *nonSymmetricIPM = new NonSymmetricIPM(A_, b_, c_, barrier_); + + //cast members + + nonSymmetricIPM->template cast_members_from(*this); + + //copy configuration; + + assert(nonSymmetricIPM->x.rows() == nonSymmetricIPM->s.rows()); + assert(nonSymmetricIPM->x.rows() == nonSymmetricIPM->c.rows()); + + return nonSymmetricIPM; + } + + IPMDouble calc_step_length_predictor() { + IPMDouble const epsilon = .5; + IPMDouble const eta = _large_neighborhood * pow(epsilon, _num_corrector_steps); + IPMDouble const k_x = eta + sqrt(2 * eta * eta + _barrier->concordance_parameter(x) + 1); + return _param_step_length_predictor / k_x; + } + + int run_solver(); + + enum Termination { + SUCCESS, + FAILURE + }; + + inline IPMDouble primal_error() { + return (A * x - tau * b).norm() / _err_consts.primal; + } + + inline IPMDouble dual_error() { + return (A.transpose() * y + s - tau * c).norm() / _err_consts.dual; + } + + inline IPMDouble primal_error_rescaled() { + return (A * x / tau - b).norm(); + } + + inline IPMDouble dual_error_rescaled() { + return (A.transpose() * y / tau + s / tau - c).norm(); + } + + inline IPMDouble duality_gap() { + return x.dot(s) / _barrier->concordance_parameter(x); + + } + + inline IPMDouble complementarity() { + return abs(c.dot(x) - b.dot(y)) / _err_consts.complementary; + } + + bool verify_solution(IPMDouble precision = 10e-5) { + if (not _barrier->in_interior(x)) { + return false; + } + if (primal_error_rescaled() > precision) { + return false; + } + if (dual_error_rescaled() > precision) { + return false; + } + + //TODO: Add duality gap check. In general we don't have strict duality as exploited below. + if (duality_gap() > precision) { + return false; + } + return true; + } + + Solution get_solution() { + Solution sol; + assert(tau != 0); + sol.x = x / tau; + sol.s = s / tau; + sol.centrality = centrality(); + sol.gap = mu(); + return sol; + } + + std::shared_ptr _logger; + std::shared_ptr _benchmark_logger; + + + //TODO:Figure out how to make these variables while being able to compile it. + + Matrix A; + Vector b; + Vector c; + + Eigen::SparseMatrix A_sparse; + + //Matrix whose columns are a basis of the kernel of A. Currently unused. + Matrix _basis_ker_A; + + Vector x; + Vector y; + Vector s; + + pt::ptree _config; + + unsigned _num_predictor_steps = 500; + unsigned _num_corrector_steps; + + IPMDouble _param_step_length_predictor = 0.02; + + IPMDouble _step_length_predictor; + IPMDouble _step_length_corrector; + + IPMDouble _epsilon = 10e-5; + + unsigned _total_num_line_steps; + + //Large neighborhood + IPMDouble _large_neighborhood; + //Small neighborhood + IPMDouble _small_neighborhood; + + bool _check_centrality_in_every_segment = true; + + bool _type_cast_if_unsuccessful = true; + + bool _use_line_search = true; + + IPMDouble kappa, tau; + + Vector _last_predictor_direction; + + ErrorConstants _err_consts; + + void initialize(); + +private: + + cxxtimer::Timer _predictor_timer; + cxxtimer::Timer _corrector_timer; + cxxtimer::Timer _andersen_sys_timer; + cxxtimer::Timer _centrality_timer; + + std::vector _custom_timers; + + cxxtimer::Timer _general_method_timer; + cxxtimer::Timer _total_runtime_timer; + + + void set_configuration_variables(); + + bool terminate(); + + bool terminate_successfully_wrapper(); + + bool terminate_successfully(); + + bool terminate_infeasible_wrapper(); + + bool terminate_infeasible(); + + + Vector _stored_x_centrality; + Vector _stored_s_centrality; + IPMDouble _stored_centrality_error; + + + IPMDouble mu(); + + Vector psi(IPMDouble t); + + LHSCB *_barrier; + + std::vector > + solve_andersen_andersen_subsystem(std::vector > &); + + Vector andersen_andersen_solve(Vector const); + + Vector solve(Matrix &, Vector const); + + Vector create_predictor_RHS(); + + Vector create_corrector_RHS(); + + Vector solve_predictor_system(); + + Vector solve_corrector_system(); + + IPMDouble centrality(); + + + void print_status(); + + void apply_update(Vector concat); + + Vector build_update_vector(); + + void test_gradient(); + + void test_hessian(); +}; + +#include "NonSymmetricIPM.hpp" + +#endif //SOS_NONSYMMETRICIPM_H diff --git a/src/volesti/include/sos/NonSymmetricIPM.hpp b/src/volesti/include/sos/NonSymmetricIPM.hpp new file mode 100644 index 00000000..a634c545 --- /dev/null +++ b/src/volesti/include/sos/NonSymmetricIPM.hpp @@ -0,0 +1,781 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#include +#include "NonSymmetricIPM.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/fmt/ostr.h" +#include "barriers/ProductBarrier.h" +#include "barriers/SumBarrier.h" +#include "barriers/InterpolantDualSOSBarrier.h" + +template +std::ostream &operator<<(std::ostream &os, const DirectionDecomposition &dir) { + os << "x " << dir.x.transpose() << std::endl; + os << "s: " << dir.s.transpose() << std::endl; + os << "y: " << dir.y.transpose() << std::endl; + os << "kappa: " << dir.kappa << ", tau: " << dir.tau << std::endl; + return os; +} + + +template +NonSymmetricIPM::NonSymmetricIPM(Instance &instance, std::string config_json) : NonSymmetricIPM( + instance) { + pt::read_json(config_json, _config); + _config = _config.get_child("IPM"); + std::cout << "NonSymmetricIPM configuration..." << std::endl; + pt::write_json(std::cout, _config); + initialize(); + set_configuration_variables(); +} + +template +void NonSymmetricIPM::set_configuration_variables() { + _epsilon = _config.get("epsilon"); + _logger->info("epsilon set to {}", _epsilon); + _num_corrector_steps = _config.get("num_corrector_steps"); + _large_neighborhood = _config.get("large_neighborhood"); + _small_neighborhood = _config.get("small_neighborhood"); + _param_step_length_predictor = _config.get("scale_predictor_step"); + _step_length_predictor = calc_step_length_predictor(); + _step_length_corrector = _config.get("length_corrector_step"); + _check_centrality_in_every_segment = _config.get("check_centrality_in_every_segment"); + _type_cast_if_unsuccessful = _config.get("type_cast_if_unsuccessful"); + + _logger->info("Set log level to {}", spdlog::level::level_enum(_config.get("logger_level"))); + _logger->set_level(spdlog::level::level_enum(_config.get("logger_level"))); + + _use_line_search = _config.get("use_line_search"); +} + +template +Vector NonSymmetricIPM::solve(Matrix &M_, Vector const v_) { + Vector sol = M_.colPivHouseholderQr().solve(v_); + return sol; +} + +//TODO:For sparse systems we might create a dense matrix in the LLT decomposition. Implement separate solver for this case +//TODO:make sure all memory is preallocated. + +template +std::vector, Vector > > +NonSymmetricIPM::solve_andersen_andersen_subsystem( + std::vector > &v) { + + _custom_timers[8].start(); + _custom_timers[4].start(); + //TODO: double transposition. Figure out how to multiply solve from RHS. +// Matrix A_H_inv = LLT.solve(A.transpose()).transpose() / mu(); + Matrix A_H_inv = _barrier->llt_solve(x, A.transpose()).transpose() / mu(); + A_H_inv.eval(); + _custom_timers[4].stop(); + _custom_timers[5].start(); + + //TODO: Use better method to sparsify A. + Matrix A_H_inv_A_top = A_H_inv * A_sparse.transpose(); + A_H_inv_A_top.eval(); + + _custom_timers[5].stop(); + _custom_timers[6].start(); + Eigen::LLT A_H_inv_A_top_LLT = A_H_inv_A_top.llt(); + _custom_timers[6].stop(); + _custom_timers[8].stop(); + _custom_timers[9].start(); + + std::vector > results; + //TODO: might be possible to solve these system in batches! + //TODO: check whether Conjugate Gradient Method solves Normal Equations more efficiently. + for (unsigned i = 0; i < v.size(); i++) { + Vector &r1 = v[i].first; + Vector &r2 = v[i].second; + Vector r2_solve = A * _barrier->llt_solve(x, r2) / mu(); +// Vector new_s_intermediate = A_H_inv_A_top_LLT.matrixL().solve(-(r2_solve - r1)); +// Vector new_s = A_H_inv_A_top_LLT.matrixU().solve(new_s_intermediate); + Vector new_s = A_H_inv_A_top_LLT.solve(-(r2_solve - r1)); +// Vector new_t_intermediate = LLT.matrixL().solve((r2 + A.transpose() * new_s) / mu()); +// Vector new_t = LLT.matrixU().solve(new_t_intermediate); + Vector new_t = _barrier->llt_solve(x, (r2 + A.transpose() * new_s) / mu()); + results.emplace_back(std::pair(new_s, new_t)); + } + _custom_timers[9].stop(); + return results; +} + +template +Vector NonSymmetricIPM::andersen_andersen_solve(Vector const rhs) { + + _andersen_sys_timer.start(); + + const int m = A.rows(); + const int n = A.cols(); + + //TODO: figure out whether rescaling makes sense. + Vector const r_p = rhs.segment(0, m); + Vector const r_d = rhs.segment(m, n); + Vector const r_g = rhs.segment(m + n, 1); + Vector const r_xs = rhs.segment(m + n + 1, n); + Vector const r_tk = rhs.segment(m + n + 1 + n, 1); + + Matrix mu_H_x = mu() * _barrier->hessian(x); + IPMDouble mu_H_tau = mu() / (tau * tau); + + std::vector > new_rhs_vectors; + new_rhs_vectors.emplace_back(std::pair(b, -c)); + new_rhs_vectors.emplace_back(std::pair(r_p, r_d + r_xs)); + + std::vector > ret = solve_andersen_andersen_subsystem(new_rhs_vectors); + + Vector p = ret[0].first; + Vector q = ret[0].second; + + Vector u = ret[1].first; + Vector v = ret[1].second; + + Vector pq(m + n); + pq << p, q; + + Vector uv(m + n); + uv << u, v; + + SPDLOG_TRACE("{}", pq.segment(m, n).transpose()); + SPDLOG_TRACE("{}", q.transpose()); + + Vector rhs_uv(m + n); + rhs_uv << r_p, r_d + r_xs; + + IPMDouble d_tau = (r_g.sum() + r_tk.sum() - b.dot(u) + c.dot(v)) / (mu() / (tau * tau) + b.dot(p) - c.dot(q)); + + Vector d_yx = uv + d_tau * pq; + Vector d_x = d_yx.segment(m, n); + Vector d_s = r_xs - mu_H_x * d_x; + IPMDouble d_kappa = r_tk.sum() - mu_H_tau * d_tau; + + Vector d_tau_vec(1); + d_tau_vec(0) = d_tau; + Vector d_kappa_vec(1); + d_kappa_vec(0) = d_kappa; + + Vector sol(m + n + 1 + n + 1); + sol << d_yx, d_tau_vec, d_s, d_kappa_vec; + + _andersen_sys_timer.stop(); + + //Check for dual error in this solution and potentially run iterative refinement. + + Vector dual_err = -A.transpose() * d_yx.segment(0, y.rows()) + d_tau * c - d_s - r_d; + IPMDouble rel_dual_error = dual_err.norm() / r_d.norm(); + + _logger->debug("relative dual error is {}", rel_dual_error); + if (rel_dual_error > 10e-2) { + _logger->warn("relative dual error is {}", rel_dual_error); + } + return sol; +} + +template +Vector NonSymmetricIPM::build_update_vector() { + Vector concat(y.rows() + x.rows() + 1 + s.rows() + 1); + concat << y, x, tau * Matrix::Identity(1, 1), s, kappa * Matrix::Identity(1, 1); + return concat; +} + +template +void NonSymmetricIPM::apply_update(Vector concat) { + y = concat.block(0, 0, y.rows(), 1); + x = concat.block(y.rows(), 0, x.rows(), 1); + tau = concat.block(y.rows() + x.rows(), 0, 1, 1).sum(); + s = concat.block(y.rows() + x.rows() + 1, 0, s.rows(), 1); + kappa = concat.block(y.rows() + x.rows() + 1 + s.rows(), 0, 1, 1).sum(); +} + +//Heuristic for steplength. +template +IPMDouble calc_new_alpha(int num_steps, IPMDouble step_length) { + IPMDouble scale_fac = pow(2, num_steps) * step_length; + IPMDouble const threshold = .4; + if (scale_fac > threshold) { + IPMDouble ratio = log2(scale_fac / threshold); + scale_fac = 1. - (1 - threshold) / pow(2, ratio); + } + return scale_fac; +} + +template +int NonSymmetricIPM::run_solver() { + + _logger->info("Solver started."); + + _logger->trace("b: {}", b.transpose()); + _logger->trace("c: {}", c.transpose()); + _logger->trace("gradient: {}", _barrier->gradient(x).transpose()); + + print_status(); + + _total_num_line_steps = 0; + _total_runtime_timer.start(); + unsigned pred_iteration = 0; + for (; pred_iteration < _num_predictor_steps; ++pred_iteration) { + Vector vec_begin_predictor_direction = build_update_vector(); + _logger->debug("Begin predictor iteration {}", pred_iteration); + _predictor_timer.start(); + if (terminate_successfully_wrapper()) { + _logger->info("Interior point method terminated successfully with required proximity."); + break; + } + + if (terminate_infeasible_wrapper()) { + _logger->info("Interior point method terminated with infeasible solution."); + break; + } + +#ifndef NDEBUG + if (centrality() > _large_neighborhood) { + _logger->warn("Centrality at beginning of predictor step is {}, large neighborhood is {}", centrality(), + _large_neighborhood); + } +#endif + + _logger->trace("Solve predictor system..."); + Vector predictor_direction = solve_predictor_system(); + _logger->trace("Finished solving predictor system"); + + + DirectionDecomposition pred_dir(predictor_direction, x.rows(), y.rows()); + + //Check predictor direction + + IPMDouble err_primal = (A * pred_dir.x - b * pred_dir.tau + A * x - b * tau).norm() + / (A * x - b * tau).norm(); + IPMDouble err_dual = (-A.transpose() * pred_dir.y + c * pred_dir.tau - pred_dir.s + - A.transpose() * y + c * tau - s).norm() / (A.transpose() * y - c * tau + s).norm(); + IPMDouble err_opt = abs(b.dot(pred_dir.y) - c.dot(pred_dir.x) - pred_dir.kappa + + b.dot(y) - c.dot(x) - kappa) / abs(-b.dot(y) + c.dot(x) + kappa); + IPMDouble err_cent = (pred_dir.s + mu() * _barrier->hessian(x) * pred_dir.x + s).norm() / s.norm(); + IPMDouble err_cent2 = abs(pred_dir.kappa + mu() / (tau * tau) * pred_dir.tau + kappa) / kappa; + + IPMDouble err_sum = err_primal + err_dual + err_opt + err_cent + err_cent2; + + if (_logger->level() <= spdlog::level::debug) { + _logger->debug("Errors in predictor direction {} {} {} {} {}", err_primal, err_dual, err_opt, err_cent, + err_cent2); + } + + const IPMDouble ERR_THRESHOLD = .1; + if (err_sum > ERR_THRESHOLD) { + _logger->warn("Error in predictor direction too big. Terminate. (Error is {}. Threshold is {})", + err_sum, ERR_THRESHOLD); + _logger->info("Predictor direction: "); + std::cout << pred_dir.x.transpose() << std::endl << pred_dir.s.transpose() << std::endl; + //TODO: Iterative refinement. + return Termination::FAILURE; + } + + + Vector curr_vec = build_update_vector(); + Vector orig_vector = curr_vec; + unsigned num_line_steps = 0; + if (not _use_line_search) { + curr_vec += _step_length_predictor * predictor_direction; + apply_update(curr_vec); + + } else { + //find longest step length such that we remain in beta environment. + //TODO: find sophisticated way of computing this efficiently. Currently we do simple repeated squaring, + // irrespective of the barrier function. + + Vector fallback_vec = orig_vector; + curr_vec = orig_vector + calc_new_alpha(num_line_steps, _step_length_predictor) * predictor_direction; + apply_update(curr_vec); + while (kappa > 0 and tau > 0 and _barrier->in_interior(x) + and (not _check_centrality_in_every_segment or (centrality() < _large_neighborhood))) { + _logger->debug("Another iteration in line search..."); + fallback_vec = curr_vec; + curr_vec = orig_vector + calc_new_alpha(num_line_steps, _step_length_predictor) * predictor_direction; + num_line_steps++; + apply_update(curr_vec); + } + + //Dynamically adjust step length + if (num_line_steps == 0) { + _step_length_predictor /= 2; + } + + if (num_line_steps > 3) { + _step_length_predictor *= 2; + } + + if (num_line_steps == 0) { + _logger->info("Reason for stopping: "); + if (not _barrier->in_interior(x)) { + _logger->info("Not in interior"); + } else if (centrality() >= _large_neighborhood) { + _logger->info("Centrality {} is worse than neighborhood {}", centrality(), _large_neighborhood); + } + } + _logger->debug("Applied {} line steps in iteration {}", num_line_steps, pred_iteration); + apply_update(fallback_vec); + +#ifndef NDEBUG + if(not terminate_successfully() and num_line_steps == 0){ + _logger->warn("Could not perform a single predictor step"); + } +#endif + } + + _total_num_line_steps += num_line_steps; + _logger->info("End of predictor step {} with {} line steps and total num line steps {}:", + pred_iteration, num_line_steps, _total_num_line_steps); + if (_logger->level() <= spdlog::level::info) { + print_status(); + } + + _predictor_timer.stop(); + _corrector_timer.start(); + + for (unsigned corr_iteration = 0; corr_iteration < _num_corrector_steps; ++corr_iteration) { + //TODO: figure out if this is already as expensive as running another corrector step. (probably not, as the crrent value can be used for next predictor step if true). + + IPMDouble cur_centrality = centrality(); + if (cur_centrality < _small_neighborhood) { + _logger->info("Central enough to skip corrector iteration {}", corr_iteration); + break; + } + + Vector psi_full = psi(mu()); + + //TODO: Use Woodburry matrix identity for corrector step. + + Vector corrector_direction = solve_corrector_system(); + + DirectionDecomposition dir(corrector_direction, x.rows(), y.rows()); + Vector concat = build_update_vector(); + concat += _step_length_corrector * corrector_direction; + apply_update(concat); + + if (centrality() > cur_centrality / 2) { + //In this case we are too far away from the central path. Revert back to and shrink large neighborhood + _logger->info("Shrink large neighborhood and use stored iterate, " + "because centering step converges too slowly."); + apply_update(vec_begin_predictor_direction); + _large_neighborhood = _small_neighborhood + (_large_neighborhood - _small_neighborhood) / 2; + break; + } + + _logger->info("End of corrector step {} :", corr_iteration); + if (_logger->level() <= spdlog::level::info) { + print_status(); + } + + if (_logger->level() <= spdlog::level::debug) { + assert(kappa > 0); + assert(tau > 0); + assert(_barrier->in_interior(x)); + assert(centrality() < _large_neighborhood); + } + } + _corrector_timer.stop(); + } + + _total_runtime_timer.stop(); + _benchmark_logger->info("{} {} {} {} {}", (_barrier->getNumVariables() / 2 - 1) / 2, pred_iteration + 1, + _total_runtime_timer.count() / 1000., + _total_runtime_timer.count() / (1000. * _total_num_line_steps), + _epsilon); + return Termination::SUCCESS; +} + +template +NonSymmetricIPM::NonSymmetricIPM(Matrix &A_, Vector &b_, Vector &c_, LHSCB *barrier_) : + A(A_), b(b_), c(c_), kappa(1.), tau(1.), _barrier(barrier_) { + y = Matrix::Zero(A.rows(), 1); + + //TODO: use proper tolerance / reference. + A_sparse = A.sparseView(IPMDouble(10e-10), 1e-10); + + _stored_x_centrality.resize(c.rows()); + _stored_s_centrality.resize(c.rows()); + + _custom_timers.resize(10); + + _logger = spdlog::get("NonSymmetricIPM"); + + + if (_logger == nullptr) { + std::vector sinks; + sinks.push_back(std::make_shared()); + sinks.push_back(std::make_shared("logs/logfile.txt")); + _logger = std::make_shared("NonSymmetricIPM", begin(sinks), end(sinks)); + _logger->set_level(spdlog::level::info); + + sinks.push_back(std::make_shared("logs/benchmark.txt")); + _benchmark_logger = std::make_shared("", begin(sinks) + 2, end(sinks)); + } + + _logger->info("A has dimensions {} x {}", A.rows(), A.cols()); + _logger->info("The number of non-zero entries is {}", A_sparse.nonZeros()); + +} + +template +void NonSymmetricIPM::initialize() { + + //Rescale instance for stability/conditioning (See Papp & Yildiz paper) + + x = _barrier->initialize_x(); + s = _barrier->initialize_s(); + + _err_consts.set(A,b,c); + + IPMDouble scaling_delta_primal = 0; + for (int i = 0; i < A.rows(); i++) { + IPMDouble const row_ratio = (1. + abs(b(i))) / (1. + abs(A.row(i).sum())); + scaling_delta_primal = std::max(scaling_delta_primal, row_ratio); + } + + IPMDouble scaling_delta_dual = 0; + for (int i = 0; i < s.rows(); i++) { + IPMDouble const entry_ratio = (1 + abs(s(i))) / (1 + abs(c(i))); + scaling_delta_dual = std::max(scaling_delta_dual, entry_ratio); + } + + IPMDouble const scaling_delta = sqrt(scaling_delta_dual * scaling_delta_primal); + + _logger->debug("Norm of x is {} and norm of s is {} before rescaling.", x.norm(), s.norm()); + + x = _barrier->initialize_x(scaling_delta); + s = _barrier->initialize_s(scaling_delta); + + _logger->debug("Rescaled initial point by {}", scaling_delta); + _logger->debug("Norm of c is {}, Norm of b is {} and norm of A is {} ", c.norm(), b.norm(), A.norm()); + + assert(x.rows() == _barrier->getNumVariables()); + + //TODO: switch to dense operation based on number of nonzeros. +// Matrix QR = A.transpose().householderQr().householderQ(); + + Eigen::SparseQR, Eigen::COLAMDOrdering > QR_sparse; + + Eigen::SparseMatrix tmp_sparse = A_sparse.transpose(); + tmp_sparse.makeCompressed(); + QR_sparse.compute(tmp_sparse); + +// _basis_ker_A = QR.block(0, A.rows(), QR.rows(), QR.cols() - A.rows()); + _basis_ker_A = Matrix(QR_sparse.matrixQ()).block(0, A.rows(), QR_sparse.rows(), QR_sparse.cols() - A.rows()); + + _logger->trace("Matrix A is: \n {}", A); + _logger->trace("Basis of ker A is: \n {}", _basis_ker_A); + _logger->trace("Check correctness: \n {}", A * _basis_ker_A); + + _num_corrector_steps = 3; + _large_neighborhood = .99; + _small_neighborhood = 0.1; + + _step_length_predictor = calc_step_length_predictor(); + _step_length_corrector = 1; + + + +} + +template +Vector NonSymmetricIPM::create_predictor_RHS() { + Vector v(y.rows() + x.rows() + 1 + s.rows() + 1); + v << y, x, tau * Matrix::Identity(1, 1), s, kappa * Matrix::Identity(1, 1); + DirectionDecomposition cur_sol(v, x.rows(), y.rows()); + Vector v1 = -(A * x - b * tau); + Vector v2 = -(-A.transpose() * y + c * tau - s); + Vector v3 = -(b.dot(y) - c.dot(x) - kappa) * Matrix::Identity(1, 1); + Vector rhs(v1.rows() + v2.rows() + v3.rows() + s.rows() + 1); + rhs << v1, v2, v3, -s, -kappa * Matrix::Identity(1, 1); + return rhs; +} + +template +Vector NonSymmetricIPM::create_corrector_RHS() { + Vector v1 = Vector::Zero(A.rows() + A.cols() + 1); + Vector v2 = -psi(mu()); + Vector rhs(v1.rows() + s.rows() + 1); + rhs << v1, v2; + _logger->trace("Corrector RHS is \n {}", rhs.transpose()); + return rhs; +}; + +template +IPMDouble NonSymmetricIPM::mu() { + return (x.dot(s) + (tau * kappa)) / (_barrier->concordance_parameter(x) + 1); +}; + +template +Vector NonSymmetricIPM::psi(IPMDouble t) { + Vector v(s.rows()); + v = s + t * _barrier->gradient(x); + Vector v_aux = (kappa - t / tau) * Matrix::Identity(1, 1); + Vector res(s.rows() + 1); + res << v, v_aux; + return res; +} + +template +Vector NonSymmetricIPM::solve_predictor_system() { + Vector rhs = create_predictor_RHS(); + SPDLOG_LOGGER_DEBUG(_logger, "System RHS for predictor step is {}", rhs.transpose()); + Vector andersen_direction = andersen_andersen_solve(rhs); + return andersen_direction; +} + +template +Vector NonSymmetricIPM::solve_corrector_system() { + Vector rhs = create_corrector_RHS(); + SPDLOG_LOGGER_DEBUG(_logger, "Psi is {}", psi(mu()).transpose()); + SPDLOG_LOGGER_DEBUG(_logger, "Barrier is {}", _barrier->gradient(x).transpose()); + SPDLOG_LOGGER_DEBUG(_logger, "s is {}", s.transpose()); + SPDLOG_LOGGER_DEBUG(_logger, "System RHS for corrector step is {}", rhs.transpose()); + SPDLOG_LOGGER_TRACE(_logger, "Corrector matrix is: \n{}", _M); + + //TODO: check if iterative refinement makes sense + auto andersen_dir = andersen_andersen_solve(rhs); + + return andersen_dir; +} + +template +void NonSymmetricIPM::print_status() { + + std::string format_ = "{:<25}:{:20.2}"; + + Double const step_length_predictor = static_cast(_step_length_predictor); + Double const step_length_corrector = static_cast(_step_length_corrector); + + _logger->debug(format_, "alpha_predictor", step_length_predictor); + _logger->debug(format_, "alpha_corrector", step_length_corrector); + + Matrix aux(2, x.rows()); + aux.block(0, 0, 1, x.rows()) = x.transpose(); + aux.block(1, 0, 1, s.rows()) = s.transpose(); + + + if (_logger->level() <= spdlog::level::trace) { + _logger->trace("Current primal/dual x, s pair :\n{}", aux); + _logger->trace("Current primal/dual rescaled x, s pair :\n{}", aux / tau); + } + + _logger->info(format_, "kappa", static_cast(kappa)); + _logger->info(format_, "tau", static_cast(tau)); + + IPMDouble mu_ipm_scaled = mu() / (tau * tau); + Double const mu_scaled = static_cast(mu_ipm_scaled); + _logger->debug(format_, "mu scaled", mu_scaled); + + IPMDouble mu_ipm = mu(); + Double const mu_ = static_cast(mu_ipm); + _logger->info(format_, "mu", boost::numeric_cast(mu())); + + if (_logger->level() <= spdlog::level::debug) { + IPMDouble centrality_ipm_ = centrality(); + Double const centrality_ = static_cast(centrality_ipm_); + _logger->debug(format_, "centrality error", centrality_); + } + + IPMDouble duality_gap_ipm_ = kappa / tau; + Double duality_gap_ = static_cast(duality_gap_ipm_); + _logger->info(format_, "duality gap", duality_gap_); + + IPMDouble primal_inf_ipm_ = primal_error(); + Double primal_inf_ = static_cast(primal_inf_ipm_); + _logger->info(format_, "primal infeas. ", primal_inf_); + + IPMDouble primal_inf_unscaled_ipm_ = primal_error_rescaled(); + Double primal_inf_unscaled_ = static_cast(primal_inf_unscaled_ipm_); + _logger->debug(format_, "primal infeas. unscaled", primal_inf_unscaled_); + + IPMDouble dual_inf_ipm_ = dual_error(); + Double dual_inf_ = static_cast(dual_inf_ipm_); + _logger->info(format_, "dual infeas.", dual_inf_); + + IPMDouble dual_inf_unscaled_ipm_ = dual_error_rescaled(); + Double dual_inf_unscaled_ = static_cast(dual_inf_unscaled_ipm_); + _logger->debug(format_, "dual infeas. unscaled", dual_inf_unscaled_); + + _logger->trace("last predictor direction: {}", _last_predictor_direction.transpose()); + + _logger->info(format_, "Predictor time (s)", _predictor_timer.count() / 1000.); + _logger->info(format_, "Corrector time (s)", _corrector_timer.count() / 1000.); + + _logger->info(format_, "Total andersen time (s)", + _andersen_sys_timer.count() / 1000.); + _logger->info(format_, "Total runtime (s)", + _total_runtime_timer.count() / 1000.); + _logger->info(format_, "Calc centrality time (s)", + _centrality_timer.count() / 1000.); + _logger->info(format_, "Time checking interior(s)", + _barrier->_in_interior_timer.template count() / 1000.); + + + _logger->info(format_, "Time per step: ", + _total_runtime_timer.count() / (1000. * _total_num_line_steps)); + if (_logger->level() <= spdlog::level::debug) { + for (unsigned idx = 0; idx < _custom_timers.size(); idx++) { + std::string s = "Custom timer " + std::to_string(idx); + _logger->info(format_, s, + _custom_timers[idx].template count() / 1000.); + } + ProductBarrier *productBarrier = static_cast *>(_barrier); + SumBarrier *sumBarrier = static_cast *>(productBarrier->get_barriers()[0]); + InterpolantDualSOSBarrier *interpBarrier = static_cast *>(sumBarrier->get_barriers()[0]); + _logger->info("Runtimes for updating gradient/hessian/LLT"); + for (unsigned idx = 0; idx < interpBarrier->_custom_timers.size(); idx++) { + std::string s = "Custom timer " + std::to_string(idx); + _logger->info(format_, s, + interpBarrier->_custom_timers[idx].template count() / 1000.); + } + } + _logger->info("--------------------------------------------------------------------------------------"); + +} + +template +bool NonSymmetricIPM::terminate_successfully_wrapper() { +// Eigen::internal::set_is_malloc_allowed(true); + bool result = terminate_successfully(); +// Eigen::internal::set_is_malloc_allowed(false); + return result; +} + +template +bool NonSymmetricIPM::terminate_successfully() { + if (primal_error() > _epsilon) { + return false; + } + + if (dual_error() > _epsilon) { + return false; + } + + if (complementarity() > _epsilon){ + return false; + } + + return true; +} + +// Termination criteria taken from Skajaa - Ye "A Homogeneous Interior-Point Algorithm for +// Nonsymmetric Convex Conic Optimization" https://web.stanford.edu/~yyye/nonsymmhsdimp.pdf page 15. + +template +bool NonSymmetricIPM::terminate_infeasible_wrapper() { + +// Eigen::internal::set_is_malloc_allowed(true); + bool result = terminate_infeasible(); +// Eigen::internal::set_is_malloc_allowed(false); + return result; +} + +template +bool NonSymmetricIPM::terminate_infeasible() { + + //TODO: Figure out if initialization scaling (delta) should influence the termination criteria. + + //Primal feasibility + IPMDouble const ipm_1 = IPMDouble(1.); + IPMDouble const primal_error = (A * x - b * tau).template lpNorm(); + IPMDouble const A_norm = A.template lpNorm(); + IPMDouble const b_norm = b.template lpNorm(); + if (primal_error > _epsilon * std::max(ipm_1, A_norm + b_norm)) { + return false; + } + + IPMDouble const dual_error = (A.transpose() * y + s - c * tau).template lpNorm(); + IPMDouble const c_norm = c.template lpNorm(); + //Dual feasibility + if (dual_error > _epsilon * std::max(ipm_1, A_norm + c_norm)) { + return false; + } + + //Duality gap + if (abs(-c.dot(x) + b.dot(y) - kappa) + > + _epsilon * std::max(ipm_1, static_cast(c_norm + b_norm))) { + return false; + }; + + //tiny tau + if (tau > _epsilon * 10e-2 * std::max(ipm_1, kappa)) { + return false; + } + return true; + +} + +template +bool NonSymmetricIPM::terminate() { + return terminate_successfully_wrapper() or terminate_infeasible_wrapper(); +} + + +template +IPMDouble NonSymmetricIPM::centrality() { + + _centrality_timer.start(); + if (_stored_x_centrality == x and _stored_s_centrality == s) { + return _stored_centrality_error; + } + IPMDouble const mu_d = mu(); + _custom_timers[0].start(); + Vector const psi_vec = psi(mu_d); + + _logger->trace("Vector Psi is: {}", psi_vec.transpose()); + + _custom_timers[0].stop(); + _custom_timers[2].start(); + IPMDouble tau_kappa_entry = tau * psi_vec.segment(psi_vec.rows() - 1, 1).sum(); + + Vector LLT_sol = _barrier->llt_L_solve(x, psi_vec.segment(0, psi_vec.rows() - 1)); + Vector err_L(psi_vec.rows()); + err_L << LLT_sol, tau_kappa_entry; + + if (_logger->level() <= spdlog::level::debug) { + ProductBarrier *pb = static_cast * >(_barrier); + auto &segs = pb->get_segments(); + Vector seg_norms(segs.size() + 1); + for (int i = 0; i < segs.size(); i++) { + seg_norms(i) = err_L.segment(segs[i].first, segs[i].second - segs[i].first).norm(); + } + seg_norms(segs.size()) = tau_kappa_entry; + seg_norms /= mu_d; + _logger->info("Segments error norms are: {}", seg_norms.transpose()); + } + + if (_logger->level() <= spdlog::level::trace) { + _logger->trace("Linear system error vector is {}", err_L.transpose()); + } + _custom_timers[2].stop(); + + IPMDouble centr_err_L = err_L.norm() / mu_d; + + _stored_x_centrality = x; + _stored_s_centrality = s; + _stored_centrality_error = centr_err_L; + _centrality_timer.stop(); + return centr_err_L; +} + +template +void NonSymmetricIPM::test_hessian() { + for (int i = 0; i < x.rows(); i++) { + Vector dir = Vector::Zero(x.rows()); + dir(i) = 10e-4; + _logger->debug("Test hessian at: {}", x.transpose()); + _logger->debug("with update {}", dir.transpose()); + Matrix M(2, x.rows()); + M.block(0, 0, 1, x.rows()) = _barrier->gradient(x + dir).transpose() + - _barrier->gradient(x).transpose(); + M.block(1, 0, 1, x.rows()) = (_barrier->hessian(x) * dir).transpose(); + _logger->debug("Comparison of gradient and hessian for {}: \n{}", dir.transpose(), M); + } + return; +}; + diff --git a/src/volesti/include/sos/README.md b/src/volesti/include/sos/README.md new file mode 100644 index 00000000..6c9eef4c --- /dev/null +++ b/src/volesti/include/sos/README.md @@ -0,0 +1,104 @@ +## Sum of Squares optimization + +This subproject implements the algorithm(s) in [1,2,3]. +Supplementary material describing the mathematical theory behind the code can be found [here](http://personal.lse.ac.uk/natura/gsoc2020/supplementary.pdf) + +#### Usage the SOS-solver for Polynomial Envelope problems + +For precise computation of Chebyshev Points and Lagrange Polynomials boost::multiprecision is used. Boost is also +used for the Property Tree in instances and configuration as well as for typecasting templated classes. The boost headers in this project do not contain the needed header files. Please provide the link to the boost files (version 1.67 or higher) via thet `-DBOOST_DIR` flag. Also [spdlog](https://github.com/gabime/spdlog) is used. Please provide link to installed package via `-DSPDLOG_DIR` flag. (The link also provides manuals for installation with various package managers.) +Navigate to the SOS envelope example and and compile: + +``` +cd examples/EnvelopeProblemSOS +cmake -DCMAKE_BUILD_TYPE=Release_double -DBOOST_DIR=your_boost_include_directory -DSPDLOG_DIR=your_spdlog_include_dir . +make +``` + +Run + +``` +./EnvelopeProblemSOS +``` + +See an example plot below. +![image](plot_saved.png "Example envelope") + +To parse a custom file invoke with added file argument: + +``` +./NonSymmetricConicOptimization file.json +``` + +where `file.json` has format + +``` json +{ + "max_degree": 30, + "num_variables": 1, + "polynomials": [ + [1,-1, 3, -4, 7], + [0.5,2, 8, -3, 5] + ] +} + +``` + +Each array in "polynomials" stands for a polynomial. The entries of its array a are the coefficients of the first length(a) coefficients in standard monomial basis or Chebyshev basis. The basis choice can be adjusted in the configuration json file. + + +#### Implemented + +* The generic implementation of the algorithm in [1] with barrier methods for the following cones: + * Non-negative orthant + * SDP cone + * Dual of SOS cone with following bases: + * monomial + * interpolant +* Generation of Lagrange polynomials for Chebyshev points of the second kind +* Tool that approximates the polynomial lower envelope of any (stable to degree approx. 100) set of univariate polynomials +on the interval [-1,1]. +* Visualisation via Matplotlib plot. +* Support for several floating-point precision data types for the IPM +(Primitive Floating Point and boost:multiprecision floats) +* Added multivariate support +* Added multithreading. +* Added LAPACK and BLAS support +* Added MKL Support (system-dependent configuration necessary) +* Dynamically switch between float -> double -> long double -> multiprecision, when Matrices become ill-conditioned. + Note that this feature currenlty only works for the InterpolantDualSOSBarrier, SumBarrier and ProductBarrier. +* Dynamic step-length for predictor step and size of large neighborhood. +* Input and configuration files in JSON format + +#### Roadmap + +* Add switch to use sparse computation (via counting nonzeros in A or other existing funcionality) +* Parameter tuning +* Choose Method for QR Decompositions dynamically. Benchmarks can be found [here](https://eigen.tuxfamily.org/dox/group__DenseDecompositionBenchmark.html). +* Use Eigen funcionality to avoid dynamical heap memory is allocation. +* Implement Lagrange polynomials more efficiently (or find C++ library with sufficient precision.) +* Benchmarking with alfonso, SOSTOOLS, MOSEK (SDP and the new Nonsymmetric cone solver), SeDuMi +* Implementation/inspiration from [4]. In particular interesting are: + * Positive definite rescaling matrix for added stability and monotonicity + * Combining Predictor- and Corrector steps. +* Higher order corrector steps as in [1] +* Write more tests +* Test different central path neighborhoods, e.g. measuring \psi in \infty norm. +* Interfaces for R (and Python) +* Store the gradient and Hessian of the previous iterate (for failing next step in predictor direction) + * Even better: For all the line steps, compute the new predictor/corrector direction. This is cheap, + as we already have gradient and hessian, but might drastically improve performance. +* Fix SDP solver. It returns feasible solutions but no optimal solutions yet. + + +#### References + +[1] A. Skajaa and Y. Ye, [A homogeneous interior-point algorithm for nonsymmetric convex conic optimization](https://web.stanford.edu/~yyye/nonsymmhsdimp.pdf), Mathematical Programming Ser. A, 150 (2015), pp. 391-422. + +[2] D. Papp and S. Yildiz, On “A homogeneous interior-point algorithm for nonsymmetric convex conic optimizationâ€. https://arxiv.org/abs/1712.00492 + +[3] D. Papp and S. Yildiz, [Sum-of-squares optimization without semidefinite programming](https://arxiv.org/abs/1712.01792). SIAM Journal on Optimization 29(1), 2019, pp. 822-851. + +[4] R. Badenbroek and J. Dahl, [An Algorithm for Nonsymmetric Conic Optimization Inspired by MOSEK](https://arxiv.org/pdf/2003.01546.pdf). https://arxiv.org/pdf/2003.01546.pdf + + diff --git a/src/volesti/include/sos/barriers/DualSOSConeStandardBarrier.h b/src/volesti/include/sos/barriers/DualSOSConeStandardBarrier.h new file mode 100644 index 00000000..d4ff9b7d --- /dev/null +++ b/src/volesti/include/sos/barriers/DualSOSConeStandardBarrier.h @@ -0,0 +1,49 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file +#ifndef SOS_DUALSOSCONESTANDARDBARRIER_H +#define SOS_DUALSOSCONESTANDARDBARRIER_H + +#include "LHSCB.h" + +//Implementation for Standard Monomial Basis for the Dual SOS cone. +template +class DualSOSConeStandardBarrier : public LHSCB { + + using LHSCB = LHSCB; + + typedef Vector Vector; + typedef Matrix Matrix; + +public: + DualSOSConeStandardBarrier() : LHSCB() {}; + + DualSOSConeStandardBarrier(unsigned max_polynomial_degree_) : _max_polynomial_degree(max_polynomial_degree_) { + }; + + Vector gradient(Vector x) override; + + Matrix hessian(Vector x) override; + + //Matrix inverse_hessian(Vector x) override; + bool in_interior(Vector x) override; + + IPMDouble concordance_parameter(Vector x) override; + + Vector initialize_x() override; + Vector initialize_s() override; + + //Lambda is the linear operator used in Proposition 1.1 in Papp & Yildiz "SOS + //without semidefinite programming. In particular, for the monomial basis it takes + //the form listed in section 3.1 of the same paper. + Matrix Lambda(Vector x); + +private: + unsigned _max_polynomial_degree; +}; + +#include "DualSOSConeStandardBarrier.hpp" + +#endif //SOS_DUALSOSCONESTANDARDBARRIER_H diff --git a/src/volesti/include/sos/barriers/DualSOSConeStandardBarrier.hpp b/src/volesti/include/sos/barriers/DualSOSConeStandardBarrier.hpp new file mode 100644 index 00000000..7e4f5106 --- /dev/null +++ b/src/volesti/include/sos/barriers/DualSOSConeStandardBarrier.hpp @@ -0,0 +1,81 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#include "DualSOSConeStandardBarrier.h" + + +template +Vector DualSOSConeStandardBarrier::gradient(Vector x) { + assert(in_interior(x)); + Matrix X = Lambda(x); + Matrix Z = X.inverse(); + Vector g(x.rows()); + for (int i = 0; i < g.rows(); ++i) { + Matrix E_i = Matrix::Zero(X.rows(), X.cols()); + for (int j = 0; j <= i; ++j) { + E_i(j, i - j) = 1; + } + g(i) = -Z.cwiseProduct(E_i).sum(); + } + return g; +} + +template +Matrix DualSOSConeStandardBarrier::hessian(Vector x) { + assert(in_interior(x)); + Matrix X = Lambda(x); + Matrix Z = X.inverse(); + Matrix H = Matrix::Zero(Z.rows(), Z.cols()); + for (int u = 0; u < H.rows(); ++u) { + for (int v = 0; v < H.cols(); ++v) { + IPMDouble H_uv = 0; + for (int a = 0; a <= u; ++a) { + for (int k = 0; k <= v; ++k) { + H_uv += Z(a, u - a) + Z(k, v - k); + } + } + } + } + return H; +} + +template +bool DualSOSConeStandardBarrier::in_interior(Vector x) { + Matrix X = Lambda(x); + CustomLLT llt_check; + llt_check.compute(X); + return llt_check.info() != Eigen::NumericalIssue; +} + +template +IPMDouble DualSOSConeStandardBarrier::concordance_parameter(Vector x) { + return x.rows(); +} + +template +Vector DualSOSConeStandardBarrier::initialize_x() { + //TODO: find centered initialization + return Vector(); +} + +template +Vector DualSOSConeStandardBarrier::initialize_s() { + //TODO: find centered initialization + return Vector(); +} + +template +Matrix DualSOSConeStandardBarrier::Lambda(Vector x) { + assert(x.rows() == _max_polynomial_degree + 1); + Matrix M(_max_polynomial_degree + 1, _max_polynomial_degree + 1); + for (unsigned i = 0; i < _max_polynomial_degree + 1; ++i) { + for (unsigned j = 0; j < _max_polynomial_degree + 1; ++j) { + M(i, j) = x(i + j); + } + } + return M; +} + diff --git a/src/volesti/include/sos/barriers/FullSpaceBarrier.h b/src/volesti/include/sos/barriers/FullSpaceBarrier.h new file mode 100644 index 00000000..c8221a88 --- /dev/null +++ b/src/volesti/include/sos/barriers/FullSpaceBarrier.h @@ -0,0 +1,46 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SOS_FULLSPACEBARRIER_H +#define SOS_FULLSPACEBARRIER_H + +#include "LHSCB.h" + +template +class FullSpaceBarrier final : public LHSCB{ + + using LHSCB = LHSCB; + + typedef Vector Vector; + typedef Matrix Matrix; + + Vector gradient(Vector x) override; + + Matrix hessian(Vector x) override; + + Matrix inverse_hessian(Vector x) override; + + bool in_interior(Vector x) override; + + Matrix llt_solve(Vector x, const Matrix &rhs) override; + Vector llt_L_solve(Vector x, Vector rhs) override; + + + IPMDouble concordance_parameter(Vector x) override; + + Vector initialize_x() override; + + Vector initialize_s() override; + +public: + FullSpaceBarrier(unsigned num_variables_) { + this->_num_variables = num_variables_; + } +}; + +#include "FullSpaceBarrier.hpp" + +#endif //SOS_FULLSPACEBARRIER_H diff --git a/src/volesti/include/sos/barriers/FullSpaceBarrier.hpp b/src/volesti/include/sos/barriers/FullSpaceBarrier.hpp new file mode 100644 index 00000000..3e0f29cb --- /dev/null +++ b/src/volesti/include/sos/barriers/FullSpaceBarrier.hpp @@ -0,0 +1,66 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#include "FullSpaceBarrier.h" + +//This barrier function exists for convenience. +//An instance should be reformulated instead of using the barrier. + +template +Vector FullSpaceBarrier::gradient(Vector) { + return Vector::Zero(this->_num_variables); +} + +template +Matrix FullSpaceBarrier::hessian(Vector) { + return Matrix::Zero(this->_num_variables, this->_num_variables); +} + +template +Matrix FullSpaceBarrier::inverse_hessian(Vector) { + return Matrix::Zero(this->_num_variables, this->_num_variables); +} + +template +bool FullSpaceBarrier::in_interior(Vector) { + return true; +} + +template +Matrix FullSpaceBarrier::llt_solve(Vector x, const Matrix &rhs) { + if(rhs.norm() > 1e-10){ + spdlog::warn("Exit because RHS of matrix is"); + std::cout << rhs << std::endl; + assert(false); + } + return Matrix::Zero(rhs.rows(),rhs.cols()); +} + +template +Vector FullSpaceBarrier::llt_L_solve(Vector x, Vector rhs) { + if(rhs.norm() > 1e-10){ + spdlog::warn("Exit because RHS of matrix is"); + std::cout << rhs << std::endl; + assert(false); + } + return Vector::Zero(x.rows()); +} + + +template +IPMDouble FullSpaceBarrier::concordance_parameter(Vector) { + return 0; +} + +template +Vector FullSpaceBarrier::initialize_x() { + return Vector::Zero(this->_num_variables); +} + +template +Vector FullSpaceBarrier::initialize_s() { + return Vector::Zero(this->_num_variables); +} diff --git a/src/volesti/include/sos/barriers/InterpolantDualSOSBarrier.h b/src/volesti/include/sos/barriers/InterpolantDualSOSBarrier.h new file mode 100644 index 00000000..76781e52 --- /dev/null +++ b/src/volesti/include/sos/barriers/InterpolantDualSOSBarrier.h @@ -0,0 +1,113 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SOS_INTERPOLANTDUALSOSBARRIER_H +#define SOS_INTERPOLANTDUALSOSBARRIER_H + +#include "LHSCB.h" + +template +class InterpolantDualSOSBarrier : public LHSCB { + + using LHSCB = LHSCB; + + typedef Vector Vector; + typedef Matrix Matrix; + +public: + InterpolantDualSOSBarrier() : LHSCB() {}; + + InterpolantDualSOSBarrier(unsigned max_polynomial_degree_, unsigned num_variables_ = 1) : + InterpolantDualSOSBarrier(max_polynomial_degree_, Vector::Ones(1), num_variables_) {}; + + InterpolantDualSOSBarrier(unsigned max_polynomial_degree_, Vector poly_g, unsigned num_variable_symbols_ = 1); + + template + InterpolantDualSOSBarrier * cast(){ + InterpolantDualSOSBarrier * int_bar = new InterpolantDualSOSBarrier(); + int_bar->_custom_timers.resize(10); + int_bar->_max_polynomial_degree = _max_polynomial_degree; + int_bar->_num_variable_symbols = _num_variable_symbols; + int_bar->_unisolvent_basis = _unisolvent_basis; + int_bar->_intermediate_matrix = _intermediate_matrix.template cast(); + int_bar->_preintermediate_matrix = _preintermediate_matrix.template cast(); +// int_bar->_intermediate_LLT = _intermediate_LLT.template cast(); + int_bar->_Q = _Q.template cast(); + int_bar->_V = _V.template cast(); + int_bar->_L = _L; + int_bar->_U = _U; + int_bar->_g = _g.template cast(); + int_bar->_g_g_transpose = _g_g_transpose.template cast(); + int_bar->_P = _P.template cast(); + int_bar->use_low_rank_updates = use_low_rank_updates; + + return int_bar; + }; + + bool update_gradient_hessian_LLT(Vector x, bool check_interior_only = false); + + Vector gradient(Vector x) override; + + Matrix hessian(Vector x) override; + + Eigen::LLT llt(Vector x, bool symmetrize = 0) override; + + Matrix inverse_hessian(Vector x) override; + + bool in_interior(Vector x) override; + + //TODO: better solution for implementation concordance parameter; + IPMDouble concordance_parameter(Vector x) override; + + Vector initialize_x() override; + + Vector initialize_s() override; + + std::vector > &get_basis() { + return _unisolvent_basis; + } + + Matrix get_P() { + return _P; + } + + void configure(pt::ptree & config); + + unsigned _max_polynomial_degree; + unsigned _num_variable_symbols; + //Turn _unisolvent_basis into Matrix + std::vector > _unisolvent_basis; + Matrix _intermediate_matrix; + Matrix _preintermediate_matrix; + CustomLLT _intermediate_LLT; + Matrix _Q; + Matrix _V; + unsigned _L, _U; + + //Weighted polynomials + Vector _g; + Matrix _g_g_transpose; + Matrix _P; + + //Usage of Woodburry matrix identity and simple heuristic for choice of variables + //to update. + bool use_low_rank_updates = true; + +private: + + + void compute_V_transpose_V(); + + void construct_univariate(Vector poly_g); + void construct_bivariate(Vector poly_g); + void construct_multivariate(Vector poly_g); + + +}; + +#include "InterpolantDualSOSBarrier.hpp" + +#endif //SOS_INTERPOLANTDUALSOSBARRIER_H diff --git a/src/volesti/include/sos/barriers/InterpolantDualSOSBarrier.hpp b/src/volesti/include/sos/barriers/InterpolantDualSOSBarrier.hpp new file mode 100644 index 00000000..c08046de --- /dev/null +++ b/src/volesti/include/sos/barriers/InterpolantDualSOSBarrier.hpp @@ -0,0 +1,493 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#include "InterpolantDualSOSBarrier.h" +#include +#include "Padua/padua.h" + +template +InterpolantDualSOSBarrier::InterpolantDualSOSBarrier( + unsigned max_polynomial_degree_, Vector poly_g,unsigned num_variable_symbols_) + : _max_polynomial_degree(max_polynomial_degree_), _num_variable_symbols(num_variable_symbols_) { + + //TODO: Check if still true for multivariate case. + assert(poly_g.rows() <= max_polynomial_degree_ + 1); + + this->_custom_timers.resize(10); + + //poly_g.rows() is degree + 1 of the polynomial g; + //TODO: setting _L using the number of rows only works for univariate polynomials + // + + //For now no weights for multivariate case. + assert(_num_variable_symbols == 1 or poly_g == Vector::Ones(poly_g.rows())); + + //TODO: check if type cast is safe. + _L = static_cast(boost::math::binomial_coefficient( + _num_variable_symbols + _max_polynomial_degree + 1 - (unsigned) poly_g.rows(), _num_variable_symbols)); + _U = static_cast(boost::math::binomial_coefficient( + 2 * _max_polynomial_degree + _num_variable_symbols, _num_variable_symbols)); + + _preintermediate_matrix = Matrix(_U, _L); + _intermediate_matrix = Matrix(_L, _L); + _intermediate_LLT = CustomLLT(_L); + _V = Matrix(_L, _U); + _Q = Matrix(_V.cols(), _V.cols()); + + this->_num_variables = _U; + _unisolvent_basis.resize(_U); + + if (_num_variable_symbols == 1) { + construct_univariate(poly_g); + } else if (_num_variable_symbols == 2) { + construct_bivariate(poly_g); + } else { + construct_multivariate(poly_g); + } + +}; + +template +void InterpolantDualSOSBarrier::construct_univariate(Vector poly_g) { + for (unsigned i = 0; i < _unisolvent_basis.size(); ++i) { + BoostDouble cos_i = boost::multiprecision::cos(i * boost::math::constants::pi() / (_U - 1)); + InterpolantDouble dummy_ipm; + InterpolantDouble cos_val = static_cast(cos_i); + _unisolvent_basis[i].push_back(cos_val); + } + + //TODO: Figure out how choice of P could influence condition / stability of maps. + + //Use monomial standard basis to orthogonalize + + //_P is used in the Interior Point Method. Therefore we need to convert the multi-precision + // floating-point into the IPM floating point precision + + //TODO: This interpolant Matrix only needs to be found once and can then be reused; + + //Alternative approach of finding _P via Chebyshev basis + + //TODO:Make this library more precise + + //Computing _g should be done with transformation matrix. + _g = Vector::Zero(_U); + for (int p = 0; p < _U; ++p) { + _g(p) = poly_g(0); + for (int i = 1; i < poly_g.rows(); i++) { + _g(p) += poly_g(i) * pow(_unisolvent_basis[p][0], i).template convert_to(); + } + } + _g_g_transpose = _g * _g.transpose(); + + //TODO: Option to compute cheb_P via InterpolantDouble; + + //TODO: Figure out whether orthogonalization could be done in double precision to speed up initialisation. + spdlog::info("Construct orthogonal interpolant point Matrix P..."); + cxxtimer::Timer orth_timer; + orth_timer.start(); + _P = orthogonal_P_Matrix_library.get(_L,_U); + orth_timer.stop(); + std::cout << "Orthogonalization done in " << orth_timer.count() / 1000. + << " seconds." << std::endl; +} + +//Untested +template +void InterpolantDualSOSBarrier::construct_bivariate(Vector poly_g) { + + //For now no weighted polynomials + assert(poly_g == Vector::Ones(1)); + unsigned const corrected_d = 2 * _max_polynomial_degree + 1 - poly_g.size(); + unsigned const corrected_d_plus_1 = corrected_d + 1; + + double *pd_pts = padua::padua_points(_max_polynomial_degree + 1); + + for (int i = 0; i < _U; i++) { + _unisolvent_basis[i] = {pd_pts[2 * i], pd_pts[2 * i + 1]}; + } + + //Set weight vector _g; + //TODO: do properly for weighted case. + _g = Vector::Ones(_U); + _g_g_transpose = _g * _g.transpose(); + + std::cout << "Construct Matrix P" << std::endl; + + _P.resize(_U, _L); + + //TODO: Make following loops more efficient OR find mathematical theory that simplifies expressions. + unsigned col_idx = 0; + for (int i = 0; i <= _max_polynomial_degree; i++) { + for (int j = 0; j + i <= _max_polynomial_degree; j++) { + Eigen::VectorXd vec_i = Eigen::VectorXd::Zero(i + 1); + vec_i(i) = 1; + Eigen::VectorXd vec_j = Eigen::VectorXd::Zero(j + 1); + vec_j(j) = 1; + ChebTools::ChebyshevExpansion cheb_i(vec_i); + ChebTools::ChebyshevExpansion cheb_j(vec_j); + for (int k = 0; k < _U; k++) { + double first_eval = cheb_i.y_recurrence(static_cast(_unisolvent_basis[k][0])); + double second_eval = cheb_j.y_recurrence(static_cast(_unisolvent_basis[k][1])); + _P(k, col_idx) = first_eval * second_eval; + } + col_idx++; + } + } + + std::cout << "P before orthogonolisation: " << std::endl << _P << std::endl; + + Matrix P_ortho = _P.householderQr().householderQ(); + P_ortho.colwise().hnormalized(); + _P = P_ortho.block(0, 0, _U, _L); + //end +} + +//TODO: test +template +void InterpolantDualSOSBarrier::construct_multivariate(Vector poly_g) { + assert(poly_g == Vector::Ones(1)); + + //Set weight vector _g; + //TODO: do properly for weighted case. + _g = Vector::Ones(_U); + _g_g_transpose = _g * _g.transpose(); + + _P.resize(_U, _L); + + //just for testing. + + int num_candidates = 1; + for (int i = 2 * _max_polynomial_degree + 1 + 1; + i <= 2 * _max_polynomial_degree + _num_variable_symbols + 1; i++) { + num_candidates *= i; + } + + //generate Fekete candidates + + + std::vector > candidates; + std::vector comb_bound; + for (int i = 2 * _max_polynomial_degree + 1; i <= 2 * _max_polynomial_degree + _num_variable_symbols; i++) { + comb_bound.push_back(i); + } + AllCombinationTuple comb_tuple(comb_bound); + + do { + std::vector &cheb_v = comb_tuple.get_combination(); + std::vector cand; + for (int i = 0; i < cheb_v.size(); i++) { + cand.push_back(cos((double) cheb_v[i] * boost::math::constants::pi() / (double) comb_bound[i])); + } + candidates.push_back(cand); + } while (comb_tuple.next()); + + for (auto cand : candidates) { + for (auto c : cand) { + std::cout << c << " "; + } + std::cout << std::endl; + } + + assert(num_candidates == candidates.size()); + + //generate Fekete polynomials + + Matrix candidate_matrix(_U, candidates.size()); + + DegreeTuple dt(_num_variable_symbols, 2 * _max_polynomial_degree); + unsigned tup_idx = 0; + do { + //Compute chebyshev polynomial evaluation + std::vector &tup = dt.get_tuple(); + for (int i = 0; i < candidates.size(); i++) { + double cheb_eval = 1.; + for (int j = 0; j < candidates[i].size(); j++) { + Eigen::VectorXd vec_j = Eigen::VectorXd::Zero(tup[j] + 1); + vec_j(tup[j]) = 1; + ChebTools::ChebyshevExpansion cheb_j(vec_j); + double eval_j = cheb_j.y_recurrence(static_cast(candidates[i][j])); + cheb_eval *= eval_j; + } + candidate_matrix(tup_idx, i) = cheb_eval; + } + tup_idx++; + } while (dt.next_valid()); + + + assert(tup_idx == _U); + +// std::cout << "Candidate matrix is \n"; +// std::cout << candidate_matrix << std::endl; + + + Matrix col_permutated_cand_matrix = candidate_matrix * candidate_matrix.colPivHouseholderQr().colsPermutation(); +// std::cout << "And after permutation \n"; +// std::cout << col_permutated_cand_matrix << std::endl; + _P = col_permutated_cand_matrix.block(0, 0, _U, _U).transpose(); +} + +//For profiling purposes +template +void InterpolantDualSOSBarrier::compute_V_transpose_V() { + _Q.noalias() = _V.transpose() * _V; +} + +template +void InterpolantDualSOSBarrier::configure(pt::ptree & config){ + if(config.find("use_low_rank_updates") != config.not_found()){ + use_low_rank_updates = config.get("use_low_rank_updates"); + } +} + +template +bool InterpolantDualSOSBarrier::update_gradient_hessian_LLT(Vector x, bool check_interior_only) { + + Matrix Q; + CustomLLT LLT; + IPMDouble stored_gx_norm; + Vector new_stored_x; + +// if(!this->_stored_gradients.empty()){ +// //This is to test whether lower ran updates make sense +// +// Vector stored_x = this->_stored_gradients[0].first; +// stored_gx_norm = _g.cwiseProduct(stored_x).norm(); +// Matrix aux(2, x.rows()); +// IPMDouble relative_norm = stored_x.norm()/x.norm(); +//// std::cout << "relative norm is " << relative_norm << std::endl; +// aux << stored_x.transpose(), x.transpose() * relative_norm; +//// std::cout << "aux is\n" << aux << std::endl; +// Vector rel = stored_x.cwiseProduct(x.cwiseInverse()); +// std::vector relative_vec(rel.data(), rel.data() + rel.rows()); +// sort(relative_vec.begin(), relative_vec.end()); +// IPMDouble mean = relative_vec[relative_vec.size() / 2]; +// +// std::cout << "Relative mean vector is: \n"; +// +// unsigned small_variation_count_05 = 0; +// unsigned small_variation_count_01 = 0; +// for(IPMDouble i : relative_vec){ +// i/=mean; +// std::cout << i << ", "; +// if(abs(i-1) < .05){ +// small_variation_count_05++; +// } +// if(abs(i-1) < .01){ +// small_variation_count_01++; +// } +// } +// std::cout << std::endl; +// std::cout << "small variation ratio: " << double(small_variation_count_01) / relative_vec.size() << std::endl; +// std::cout << "small variation ratio: " << double(small_variation_count_05) / relative_vec.size() << std::endl; +// } + + bool do_exact_computation = true; + + if (use_low_rank_updates and not this->_stored_gradients.empty()) { + do_exact_computation = false; + this->_custom_timers[9].start(); + Vector stored_x = this->_stored_gradients[0].first; + stored_gx_norm = _g.cwiseProduct(stored_x).norm(); + Matrix aux(2, x.rows()); + aux << stored_x.transpose(), x.transpose(); + std::cout << "aux is\n" << aux << std::endl; + Vector stored_scaled_gx = _g.cwiseProduct(stored_x).normalized(); + //TODO: Find best scaling to minimize number of adjusted variables. + Vector scaled_gx = _g.cwiseProduct(x).normalized(); + Vector relative = scaled_gx - stored_scaled_gx; + Vector relative_abs = relative.cwiseAbs(); +// TODO: Check if adding diagonal here makes sense. This corresponds to the gradient and the term occurs for the Sherman-Morrison update. + + std::vector relative_vec(relative_abs.data(), relative_abs.data() + relative_abs.rows()); + std::vector relative_vec_sorted = sort_indexes(relative_vec); + std::reverse(relative_vec_sorted.begin(), relative_vec_sorted.end()); + + Q = _Q; + LLT = _intermediate_LLT; + assert(_U == relative_vec_sorted.size()); + + new_stored_x = this->_stored_gradients[0].first; + for (int i = 0; i < _U; i++) { + + //rank one updates + + unsigned idx = relative_vec_sorted[i]; + + //TODO: find good threshold + if (relative_abs(idx) < 1e-10 * relative_abs(relative_vec_sorted[0])) { + std::cout << "break after " << i << " of " << _U << " updates" + << std::endl; + break; + } + + //Q update + new_stored_x(idx) = x(idx); + + this->_custom_timers[7].start(); + Vector P_idx = _P.row(idx).transpose(); + Vector tmp = LLT.solve(P_idx); + Vector rank_one_factor = _P * tmp; + Matrix Q_update = + -rank_one_factor * rank_one_factor.transpose() / + (1 / (stored_gx_norm * relative(idx)) + tmp.dot(P_idx)); +// std::cout << "Q_update normed is \n" << Q_update << std::endl; + + Q += Q_update; + this->_custom_timers[7].stop(); + + //L update + this->_custom_timers[8].start(); + LLT.rankUpdate(P_idx, stored_gx_norm * relative(idx)); + if (LLT.info() == Eigen::NumericalIssue) { + //This means we should either change the order in which we apply the rank-1 updates + //or revert back to exact computation. Currently we just jump to exact computation. + + do_exact_computation = true; + break; + } + this->_custom_timers[8].stop(); + } + this->_custom_timers[9].stop(); + + if (not do_exact_computation) { +// std::cout << "Old matrix LLT \n" << LLT.matrixL().toDenseMatrix() << std::endl; + _intermediate_LLT.copy_and_scale(LLT, sqrt(_g.cwiseProduct(x).norm() / stored_gx_norm)); +// std::cout << "Newly copied matrix \n" +// << _intermediate_LLT.matrixL().toDenseMatrix() / sqrt(_g.cwiseProduct(x).norm() / stored_gx_norm) << std::endl; + _Q = Q * stored_gx_norm / _g.cwiseProduct(x).norm(); + } + } + + if (do_exact_computation) { + new_stored_x = x; + + if(this->_stored_intermediate_LLT.empty()){ + this->_stored_intermediate_LLT.resize(1); + this->_stored_intermediate_LLT[0].first = Vector::Zero(new_stored_x.rows()); + } + + if(this->_stored_intermediate_LLT[0].first == new_stored_x){ + _intermediate_LLT = this->_stored_intermediate_LLT[0].second; + } else { + + this->_custom_timers[1].start(); + _preintermediate_matrix.noalias() = _g.cwiseProduct(x).asDiagonal() * _P; + _intermediate_matrix.noalias() = _P.transpose() * _preintermediate_matrix; + this->_custom_timers[1].stop(); + this->_custom_timers[2].start(); + _intermediate_LLT.compute(_intermediate_matrix); + this->_custom_timers[2].stop(); + + if (_intermediate_LLT.info() == Eigen::NumericalIssue) { + return false; + } else { + this->_stored_intermediate_LLT[0].first = new_stored_x; + this->_stored_intermediate_LLT[0].second = _intermediate_LLT; + } + } + + if (check_interior_only) { + return true; + } + + this->_custom_timers[3].start(); + _V.noalias() = _intermediate_LLT.matrixL().solve(_P.transpose()); + this->_custom_timers[3].stop(); + + //Experiments showed that using the triangularView instead would slow down the program. + //So we use the full Matrix _V to compute its product with the transpose. + + this->_custom_timers[4].start(); + compute_V_transpose_V(); + this->_custom_timers[4].stop(); + } + + //TODO: store hessian as self-adjoint + if (this->_stored_hessians.empty()) { + this->_stored_hessians.resize(1); + } + this->_stored_hessians[0].first = new_stored_x; + this->_stored_hessians[0].second.noalias() = _g_g_transpose.cwiseProduct(_Q.cwiseProduct(_Q)); + + if (this->_stored_gradients.empty()) { + this->_stored_gradients.resize(1); + } + this->_stored_gradients[0].first = new_stored_x; + this->_stored_gradients[0].second.noalias() = -_Q.diagonal().cwiseProduct(_g); + + if (this->_stored_LLT.empty()) { + this->_stored_LLT.resize(1); + } + + this->_stored_LLT[0].first = new_stored_x; + this->_custom_timers[5].start(); + this->_stored_LLT[0].second.compute(this->_stored_hessians[0].second.template selfadjointView()); + this->_custom_timers[5].stop(); + + return true; +} + +template +Vector InterpolantDualSOSBarrier::gradient(Vector x) { + auto *grad_ptr = this->find_gradient(x); + if (grad_ptr) { + return *grad_ptr; + } + update_gradient_hessian_LLT(x); + return this->_stored_gradients[0].second; +} + +template +Matrix InterpolantDualSOSBarrier::hessian(Vector x) { + auto *hess_ptr = this->find_hessian(x); + if (hess_ptr) { + return *hess_ptr; + } + update_gradient_hessian_LLT(x); + return this->_stored_hessians[0].second; +} + +template +Eigen::LLT > InterpolantDualSOSBarrier::llt(Vector x, bool) { + auto *llt_ptr = this->find_LLT(x); + if (llt_ptr) { + return *llt_ptr; + } + update_gradient_hessian_LLT(x); + return this->_stored_LLT[0].second; +} + +//Should not be invoked as it is slow. +template +Matrix InterpolantDualSOSBarrier::inverse_hessian(Vector x) { + Matrix L_inv = llt(x).matrixL().toDenseMatrix().inverse(); + Matrix inv = L_inv.transpose() * L_inv; + return inv; +} + +template +bool InterpolantDualSOSBarrier::in_interior(Vector x) { + //The computational effort to calculate whether x is in the interior is nearly as high + //as computing gradient and hessian. Therefore we just calculate them here as well. + bool check_interior_only = true; + return update_gradient_hessian_LLT(x, check_interior_only); +} + +template +IPMDouble InterpolantDualSOSBarrier::concordance_parameter(Vector) { + return _L; +} + +template +Vector InterpolantDualSOSBarrier::initialize_x() { + return Vector::Ones(_U); +} + +template +Vector InterpolantDualSOSBarrier::initialize_s() { + return -gradient(initialize_x()); +} + diff --git a/src/volesti/include/sos/barriers/LHSCB.h b/src/volesti/include/sos/barriers/LHSCB.h new file mode 100644 index 00000000..dd19d099 --- /dev/null +++ b/src/volesti/include/sos/barriers/LHSCB.h @@ -0,0 +1,89 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SOS_LHSCB_H +#define SOS_LHSCB_H + +#include +#include +#include "utils.h" + +template +class LHSCB { + typedef Vector Vector; + typedef Matrix Matrix; +public: + LHSCB() : _num_variables(0) {}; + + template + void cast_members_from(const LHSCB & other){ + _num_variables = other._num_variables; + } + + template + LHSCB * cast(){ + LHSCB * lhscb; + (*lhscb).template cast_members_from(*this); + return lhscb; + } + + virtual ~LHSCB() {}; + + virtual Vector gradient(Vector x) = 0; + + virtual Matrix hessian(Vector x) = 0; + + virtual Eigen::LLT llt(Vector x, bool symmetrize = 0); + + virtual Matrix llt_solve(Vector x, const Matrix &rhs); + + virtual Vector llt_L_solve(Vector x, Vector rhs); + + Vector *find_gradient(Vector x); + + Matrix *find_hessian(Vector x); + + Eigen::LLT *find_LLT(Vector x); + + virtual Matrix inverse_hessian(Vector x); + + virtual bool in_interior(Vector x) = 0; + + virtual IPMDouble concordance_parameter(Vector x) = 0; + + virtual Vector initialize_x(IPMDouble parameter) { + return parameter * initialize_x(); + } + + //TODO: figure out if initializing the dual is in general just - 1/mu * g(x) (i.e. whether this is in the dual cone) + virtual Vector initialize_s(IPMDouble parameter) { + return initialize_s() / parameter; + } + + virtual Vector initialize_x() = 0; + + virtual Vector initialize_s() = 0; + + cxxtimer::Timer _in_interior_timer; + std::vector _custom_timers; + + unsigned _num_variables; + + +protected: + + std::vector > _stored_gradients; + std::vector > _stored_hessians; + std::vector > > _stored_LLT; + std::vector > > _stored_intermediate_LLT; + +public: + unsigned int getNumVariables() const; +}; + +#include "LHSCB.hpp" + +#endif //SOS_LHSCB_H diff --git a/src/volesti/include/sos/barriers/LHSCB.hpp b/src/volesti/include/sos/barriers/LHSCB.hpp new file mode 100644 index 00000000..bf209a92 --- /dev/null +++ b/src/volesti/include/sos/barriers/LHSCB.hpp @@ -0,0 +1,89 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#include +#include "LHSCB.h" + +template +Matrix LHSCB::inverse_hessian(Vector x) { + Eigen::LLT LLT = llt(x); + Matrix L_inv = LLT.matrixL().toDenseMatrix().inverse(); + return L_inv.transpose() * L_inv; +} + +template +unsigned int LHSCB::getNumVariables() const { + return _num_variables; +} + +//TODO: use short queue instead. +template +Vector *LHSCB::find_gradient(Vector x) { + for (int i = _stored_gradients.size() - 1; i >= 0; i--) { + if (x == _stored_gradients[i].first) { + return &_stored_gradients[i].second; + } + } + return nullptr; +} + +template +Matrix *LHSCB::find_hessian(Vector x) { + for (int i = _stored_hessians.size() - 1; i >= 0; i--) { + if (x == _stored_hessians[i].first) { + return &_stored_hessians[i].second; + } + } + return nullptr; +} + +template +Eigen::LLT > *LHSCB::find_LLT(Vector x) { + for (int i = _stored_LLT.size() - 1; i >= 0; i--) { + if (x == _stored_LLT[i].first) { + return &_stored_LLT[i].second; + } + } + return nullptr; +} + +template +Eigen::LLT > LHSCB::llt(Vector x, bool symmetrize) { + + Eigen::LLT *llt_ptr = nullptr; + + if (not symmetrize) { + llt_ptr = find_LLT(x); + } + + if (llt_ptr) { + return *llt_ptr; + } + + if (_stored_LLT.empty()) { + _stored_LLT.resize(1); + } + + _stored_LLT[0].first = x; + if (not symmetrize) { + _stored_LLT[0].second = Eigen::LLT(hessian(x).llt()); + } else { + Matrix hess_tmp = hessian(x); + _stored_LLT[0].second = Eigen::LLT(((hess_tmp+ hess_tmp.transpose()) / 2).llt()); + } + + return _stored_LLT[0].second; +} + +template +Matrix LHSCB::llt_solve(Vector x, const Matrix &rhs) { + return llt(x).solve(rhs); +} + +template +Vector LHSCB::llt_L_solve(Vector x, Vector rhs) { + return llt(x).matrixL().solve(rhs); +} diff --git a/src/volesti/include/sos/barriers/LPStandardBarrier.h b/src/volesti/include/sos/barriers/LPStandardBarrier.h new file mode 100644 index 00000000..a0de7a48 --- /dev/null +++ b/src/volesti/include/sos/barriers/LPStandardBarrier.h @@ -0,0 +1,47 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SOS_LPSTANDARDBARRIER_H +#define SOS_LPSTANDARDBARRIER_H + +#include "LHSCB.h" + +template +class LPStandardBarrier final : public LHSCB { + using LHSCB = LHSCB; + + typedef Vector Vector; + typedef Matrix Matrix; + +public: + LPStandardBarrier() : LHSCB() {}; + + LPStandardBarrier(unsigned num_variables_) { + this->_num_variables = num_variables_; + }; + + Vector gradient(Vector x) override; + + Matrix hessian(Vector x) override; + + Matrix inverse_hessian(Vector x) override; + + bool in_interior(Vector x) override; + + //TODO: better solution for concordance parameter; + IPMDouble concordance_parameter(Vector x) override; + + Vector initialize_x() override; + + Vector initialize_s() override; + +private: + +}; + +#include "LPStandardBarrier.hpp" + +#endif //SOS_LPSTANDARDBARRIER_H diff --git a/src/volesti/include/sos/barriers/LPStandardBarrier.hpp b/src/volesti/include/sos/barriers/LPStandardBarrier.hpp new file mode 100644 index 00000000..9a6815a9 --- /dev/null +++ b/src/volesti/include/sos/barriers/LPStandardBarrier.hpp @@ -0,0 +1,43 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#include "LPStandardBarrier.h" + +template +bool LPStandardBarrier::in_interior(Vector x) { + return (x.minCoeff() > 0); +} + +template +Vector LPStandardBarrier::gradient(Vector x) { + assert(in_interior(x)); + return -x.array().inverse(); +} + +template +Matrix LPStandardBarrier::hessian(Vector x) { + return x.array().pow(2).inverse().matrix().asDiagonal(); +} + +template +Matrix LPStandardBarrier::inverse_hessian(Vector x) { + return hessian(x).inverse(); +} + +template +IPMDouble LPStandardBarrier::concordance_parameter(Vector x) { + return x.rows(); +} + +template +Vector LPStandardBarrier::initialize_x() { + return Vector::Ones(this->_num_variables); +} + +template +Vector LPStandardBarrier::initialize_s() { + return Vector::Ones(this->_num_variables); +} diff --git a/src/volesti/include/sos/barriers/ProductBarrier.h b/src/volesti/include/sos/barriers/ProductBarrier.h new file mode 100644 index 00000000..be5e4329 --- /dev/null +++ b/src/volesti/include/sos/barriers/ProductBarrier.h @@ -0,0 +1,113 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SOS_PRODUCTBARRIER_H +#define SOS_PRODUCTBARRIER_H + +#include "LHSCB.h" +#include "SumBarrier.h" +#include "InterpolantDualSOSBarrier.h" + + +template +class ProductBarrier : public LHSCB { + + typedef Vector Vector; + typedef Matrix Matrix; + + typedef Vector (LHSCB::*VectorFunc)(Vector); + typedef Matrix (LHSCB::*MatrixFunc)(Vector); + typedef Vector (LHSCB::*VoidFunc)(); + +public: + ProductBarrier(unsigned num_threads = 1) : LHSCB() { + _num_threads = num_threads; + } + + ProductBarrier(std::vector *> barriers_, std::vector num_variables_, unsigned num_threads = 1) { + assert(barriers_.size() == num_variables_.size()); + _num_threads = num_threads; + for (unsigned j = 0; j < barriers_.size(); ++j) { + _barriers.push_back(barriers_[j]); + _num_vars_per_barrier.push_back(num_variables_[j]); + } + } + + template + ProductBarrier * cast(){ + ProductBarrier * pb = new ProductBarrier(); + pb->_num_threads = _num_threads; + pb->_segments = _segments; + for(LHSCB * barrier : _barriers){ + //TODO: High priority. Figure out how to undo the cast to the SumBarrier. + LHSCB * new_barr; + if(dynamic_cast*>(barrier)){ + new_barr = static_cast*>(barrier)->template cast(); + } else { + if(dynamic_cast*>(barrier)){ + new_barr = static_cast*>(barrier)->template cast(); + } else { + exit(1); + } + } + pb->add_barrier(new_barr); + } + return pb; + }; + + void add_barrier(LHSCB *lhscb) { + _barriers.push_back(lhscb); + _num_vars_per_barrier.push_back(lhscb->getNumVariables()); + this->_num_variables += lhscb->getNumVariables(); + } + + Vector gradient(Vector x) override; + + Matrix hessian(Vector x) override; + +// Matrix inverse_hessian(Vector x) override; + bool in_interior(Vector x) override; + + //TODO: better solution for concordance parameter; + IPMDouble concordance_parameter(Vector x) override; + + Vector initialize_x() override; + + Vector initialize_s() override; + + Eigen::LLT llt(Vector x, bool symmetrize = 0) override; + + Matrix llt_solve(Vector x, const Matrix &rhs) override; + + Vector llt_L_solve(Vector x, Vector rhs) override; + + Matrix inverse_hessian(Vector x) override; + + void update_segments(); + std::vector > & get_segments(){ + return _segments; + }; + + Vector evaluate(Vector x, VectorFunc func); + Matrix evaluate(Vector x, MatrixFunc func); + Vector evaluate(VoidFunc func); + + std::vector *> & get_barriers(){ + return _barriers; + } + + std::vector *> _barriers; + std::vector > _segments; + std::vector _num_vars_per_barrier; + unsigned _num_threads; + +private: + +}; + +#include "ProductBarrier.hpp" + +#endif //SOS_PRODUCTBARRIER_H diff --git a/src/volesti/include/sos/barriers/ProductBarrier.hpp b/src/volesti/include/sos/barriers/ProductBarrier.hpp new file mode 100644 index 00000000..722a0c47 --- /dev/null +++ b/src/volesti/include/sos/barriers/ProductBarrier.hpp @@ -0,0 +1,173 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + + +#include "ProductBarrier.h" + +//TODO: Figure out how to compute llt decomposition with diagonal blocks. There +// does not seem to be a straightforward way with Eigen. +template +Eigen::LLT > ProductBarrier::llt(Vector x, bool symmetrize) { + return LHSCB::llt(x, symmetrize); +} + +//TODO: figure out how these methods can be even more abstracted. +template +Vector ProductBarrier::llt_L_solve(Vector x, Vector rhs) { + update_segments(); + Vector product_llt_solve(this->_num_variables); +#ifdef PARALLELIZE_BARRIERS +#pragma omp parallel for +#endif + for (unsigned i = 0; i < _barriers.size(); ++i) { + std::pair & seg = _segments[i]; + LHSCB *barrier = _barriers[i]; + Vector x_seg = x.segment(seg.first, seg.second - seg.first); + Vector rhs_seg = rhs.segment(seg.first, seg.second - seg.first); + Vector lls_solve_seg = rhs_seg.nonZeros() ? barrier->llt_L_solve(x_seg, rhs_seg) + : Vector::Zero(rhs_seg.rows()); + product_llt_solve.segment(seg.first , seg.second - seg.first) = lls_solve_seg; + } + return product_llt_solve; +} + +template +Matrix ProductBarrier::llt_solve(Vector x, const Matrix &rhs) { + update_segments(); + Matrix product_llt_solve = Matrix::Zero(rhs.rows(), rhs.cols()); +#ifdef PARALLELIZE_BARRIERS +#pragma omp parallel for +#endif + for (unsigned i = 0; i < _barriers.size(); ++i) { + std::pair & seg = _segments[i]; + LHSCB *barrier = _barriers[i]; + Vector x_seg = x.segment(seg.first, seg.second - seg.first); + Matrix rhs_block = rhs.block(seg.first, 0, seg.second-seg.first, rhs.cols()); + Matrix lls_solve_block = barrier->llt_solve(x_seg, rhs_block); + product_llt_solve.block(seg.first, 0, seg.second - seg.first, rhs.cols()) = lls_solve_block; + } + return product_llt_solve; +} + +//Should not be used, as immense storage is needed. +template +Matrix ProductBarrier::hessian(Vector x) { + return evaluate(x, &LHSCB::hessian); +} + +template +bool ProductBarrier::in_interior(Vector x) { + this->_in_interior_timer.start(); + update_segments(); + std::vector in_interior_vec(_barriers.size()); +#ifdef PARALLELIZE_BARRIERS +#pragma omp parallel for +#endif + for (unsigned i = 0; i < _barriers.size(); ++i) { + std::pair & seg = _segments[i]; + LHSCB *barrier = _barriers[i]; + Vector x_seg = x.segment(seg.first, seg.second - seg.first); + in_interior_vec[i] = barrier->in_interior(x_seg); + } + this->_in_interior_timer.stop(); + return std::all_of(in_interior_vec.begin(), in_interior_vec.end(), [](bool b){return b;}); +} + +template +IPMDouble ProductBarrier::concordance_parameter(Vector x) { + unsigned idx = 0; + IPMDouble concordance_par = 0; + for (unsigned i = 0; i < _barriers.size(); ++i) { + LHSCB *barrier = _barriers[i]; + unsigned num_variables = _num_vars_per_barrier[i]; + Vector v_segment = x.segment(idx, num_variables); + concordance_par += barrier->concordance_parameter(v_segment); + idx += num_variables; + } + return concordance_par; +} + +template +Vector ProductBarrier::initialize_x() { + return evaluate(&LHSCB::initialize_x); +} + +template +Vector ProductBarrier::initialize_s() { + return evaluate(&LHSCB::initialize_s); +} + +template +Matrix ProductBarrier::inverse_hessian(Vector x) { + return evaluate(x, &LHSCB::inverse_hessian); +} + +template +Vector ProductBarrier::gradient(Vector x) { + return evaluate(x, &LHSCB::gradient); +} + +template +void ProductBarrier::update_segments() { + _segments.resize(0); + unsigned idx = 0; + std::vector > segments; + for (unsigned i = 0; i < _barriers.size(); ++i) { + unsigned num_variables = _num_vars_per_barrier[i]; + _segments.push_back(std::pair(idx, idx + num_variables)); + idx += num_variables; + } +} + +template +Vector ProductBarrier::evaluate(Vector x, VectorFunc func) { + update_segments(); + Vector product_vector(this->_num_variables); +#ifdef PARALLELIZE_BARRIERS +#pragma omp parallel for +#endif + for (unsigned i = 0; i < _barriers.size(); ++i) { + std::pair &seg = _segments[i]; + LHSCB *barrier = _barriers[i]; + Vector x_seg = x.segment(seg.first, seg.second - seg.first); + Vector vec_seg = (barrier->*func)(x_seg); + product_vector.segment(seg.first, seg.second - seg.first) = vec_seg; + } + return product_vector; +} + +template +Matrix ProductBarrier::evaluate(Vector x, MatrixFunc func) { + update_segments(); + Matrix product_matrix = Matrix::Zero(this->_num_variables, this->_num_variables); +#ifdef PARALLELIZE_BARRIERS +#pragma omp parallel for +#endif + for (unsigned i = 0; i < _barriers.size(); ++i) { + std::pair &seg = _segments[i]; + LHSCB *barrier = _barriers[i]; + Vector x_seg = x.segment(seg.first, seg.second - seg.first); + Matrix matrix_block = (barrier->*func)(x_seg); + product_matrix.block(seg.first, seg.first, seg.second - seg.first, seg.second - seg.first) = matrix_block; + } + return product_matrix; +} + +template +Vector ProductBarrier::evaluate(VoidFunc func) { + update_segments(); + Vector product_vector(this->_num_variables); +#ifdef PARALLELIZE_BARRIERS +#pragma omp parallel for +#endif + for (unsigned i = 0; i < _barriers.size(); ++i) { + std::pair &seg = _segments[i]; + LHSCB *barrier = _barriers[i]; + Vector vec_seg = (barrier->*func)(); + product_vector.segment(seg.first, seg.second - seg.first) = vec_seg; + } + return product_vector; +} diff --git a/src/volesti/include/sos/barriers/SumBarrier.h b/src/volesti/include/sos/barriers/SumBarrier.h new file mode 100644 index 00000000..7252cab5 --- /dev/null +++ b/src/volesti/include/sos/barriers/SumBarrier.h @@ -0,0 +1,59 @@ +// +// Created by test Bento Natura on 30/07/2020. +// + +#ifndef SOS_SUMBARRIER_H +#define SOS_SUMBARRIER_H + +#include "LHSCB.h" +#include "InterpolantDualSOSBarrier.h" + +//corresponds to intersection of cones. +template +class SumBarrier : public LHSCB { + + typedef Vector Vector; + typedef Matrix Matrix; + +public: + SumBarrier(unsigned num_variables_); + + SumBarrier(std::vector *> barriers_, unsigned num_variables_); + + template + SumBarrier * cast(){ + SumBarrier * sb = new SumBarrier(this->_num_variables); + for(LHSCB * barrier : _barriers){ + //TODO: High priority. Figure out how to undo the cast to the InterpolantBarrier. + InterpolantDualSOSBarrier * new_barr = static_cast*>(barrier)->template cast(); +// LHSCB * new_barr = (*barrier).template cast(); + sb->add_barrier(new_barr); + } + return sb; + }; + + void add_barrier(LHSCB *lhscb); + + Vector gradient(Vector x) override; + + Matrix hessian(Vector x) override; + + bool in_interior(Vector x) override; + + //TODO: better solution for concordance parameter; + IPMDouble concordance_parameter(Vector x) override; + + //TODO: verify that initialisation works for both primal and dual side in general. + Vector initialize_x() override; + + Vector initialize_s() override; + + std::vector *> & get_barriers(){ + return _barriers; + } +private: + std::vector *> _barriers; +}; + +#include "SumBarrier.hpp" +#endif //SOS_SUMBARRIER_H diff --git a/src/volesti/include/sos/barriers/SumBarrier.hpp b/src/volesti/include/sos/barriers/SumBarrier.hpp new file mode 100644 index 00000000..40cb48ba --- /dev/null +++ b/src/volesti/include/sos/barriers/SumBarrier.hpp @@ -0,0 +1,101 @@ +// +// Created by test Bento Natura on 30/07/2020. +// + +#include "SumBarrier.h" + +template +SumBarrier::SumBarrier(unsigned num_variables_) : LHSCB() { + this->_num_variables = num_variables_; +} + +template +SumBarrier::SumBarrier(std::vector *> barriers_, unsigned num_variables_) { + this->_num_variables = num_variables_; + for (unsigned j = 0; j < barriers_.size(); ++j) { + assert(barriers_[j]->getNumVariables() == num_variables_); + _barriers.push_back(barriers_[j]); + } +} + +template +void SumBarrier::add_barrier(LHSCB *lhscb) { + _barriers.push_back(lhscb); +} + +template +Vector SumBarrier::gradient(Vector x) { + Vector grad_vec = Vector::Zero(x.rows()); + std::vector barrier_vec(_barriers.size()); +#ifdef PARALLELIZE_BARRIERS +#pragma omp parallel for +#endif + for (int i = 0; i < _barriers.size(); i++){ + barrier_vec[i] = _barriers[i]->gradient(x); + } + for(int i = 0; i < _barriers.size(); i++){ + grad_vec += barrier_vec[i]; + } + return grad_vec; +} + +template +Matrix SumBarrier::hessian(Vector x) { + Matrix hess_mat = Matrix::Zero(x.rows(), x.rows()); + std::vector barrier_vec(_barriers.size()); +#ifdef PARALLELIZE_BARRIERS +#pragma omp parallel for +#endif + for (int i = 0; i < _barriers.size(); i++){ + barrier_vec[i] = _barriers[i]->hessian(x); + } + for(int i = 0; i < _barriers.size(); i++){ + hess_mat += barrier_vec[i]; + } + + return hess_mat; +} + +template +bool SumBarrier::in_interior(Vector x) { + std::vector barrier_vec(_barriers.size()); +#pragma omp parallel for + for (int i = 0; i < _barriers.size(); i++){ + barrier_vec[i] = _barriers[i]->in_interior(x); + } + return std::all_of(barrier_vec.begin(), barrier_vec.end(), [](bool b){return b;}); +} + +//TODO: better solution for concordance parameter; +template +IPMDouble SumBarrier::concordance_parameter(Vector x) { + IPMDouble conc_par = 0.; + for ( + auto barrier + : _barriers) { + conc_par += barrier-> + concordance_parameter(x); + } + return conc_par; +} + +//TODO: verify that initialisation works for both primal and dual side in general. +template +Vector SumBarrier::initialize_x() { + assert(!_barriers.empty()); + Vector rs = _barriers[0]->initialize_x(); + return rs; + +} + +template +Vector SumBarrier::initialize_s() { + Vector s_init = Vector::Zero(this->_num_variables); + for ( + auto barrier + : _barriers) { + s_init += barrier->initialize_s(); + } + return s_init; +} + diff --git a/src/volesti/include/sos/barriers/ZeroSpaceBarrier.h b/src/volesti/include/sos/barriers/ZeroSpaceBarrier.h new file mode 100644 index 00000000..e2a11929 --- /dev/null +++ b/src/volesti/include/sos/barriers/ZeroSpaceBarrier.h @@ -0,0 +1,44 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SOS_ZEROSPACEBARRIER_H +#define SOS_ZEROSPACEBARRIER_H + +#include "LHSCB.h" + +//This class models the 0-cone. Should not be used, so reformulate your +//instance. +template +class ZeroSpaceBarrier final : public LHSCB { + + using LHSCB = LHSCB; + + typedef Vector Vector; + typedef Matrix Matrix; + + Vector gradient(Vector x) override; + + Matrix hessian(Vector x) override; + + Matrix inverse_hessian(Vector x) override; + + bool in_interior(Vector x) override; + + IPMDouble concordance_parameter(Vector x) override; + + Vector initialize_x() override; + + Vector initialize_s() override; + +public: + ZeroSpaceBarrier(unsigned num_variables_) { + this->_num_variables = num_variables_; + } +}; + +#include "ZeroSpaceBarrier.hpp" + +#endif //SOS_ZEROSPACEBARRIER_H diff --git a/src/volesti/include/sos/barriers/ZeroSpaceBarrier.hpp b/src/volesti/include/sos/barriers/ZeroSpaceBarrier.hpp new file mode 100644 index 00000000..1381fbcd --- /dev/null +++ b/src/volesti/include/sos/barriers/ZeroSpaceBarrier.hpp @@ -0,0 +1,46 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#include "ZeroSpaceBarrier.h" + +template +Vector ZeroSpaceBarrier::gradient(Vector x) { + //should not be used + assert(false); + return -std::numeric_limits::infinity() * Vector::Ones(x.cols()); +} + +template +Matrix ZeroSpaceBarrier::hessian(Vector x) { + //should not be used + assert(false); + return std::numeric_limits::infinity() * Matrix::Identity(x.cols(), x.cols()); +} + +template +Matrix ZeroSpaceBarrier::inverse_hessian(Vector) { + return Matrix::Zero(this->_num_variables, this->_num_variables); +} + +template +bool ZeroSpaceBarrier::in_interior(Vector) { + return true; +} + +template +IPMDouble ZeroSpaceBarrier::concordance_parameter(Vector) { + return 0; +} + +template +Vector ZeroSpaceBarrier::initialize_x() { + return Vector::Zero(this->_num_variables); +} + +template +Vector ZeroSpaceBarrier::initialize_s() { + return Vector::Zero(this->_num_variables); +} diff --git a/src/volesti/include/sos/config/config.json b/src/volesti/include/sos/config/config.json new file mode 100644 index 00000000..f3b150f5 --- /dev/null +++ b/src/volesti/include/sos/config/config.json @@ -0,0 +1,21 @@ +{ + "IPM": { + "epsilon": 1e-6, + "num_corrector_steps": 4, + "large_neighborhood": 5, + "small_neighborhood": 0.05, + "scale_predictor_step": 1, + "length_corrector_step": 1, + "logger_level": 2, + "use_line_search": true, + "check_centrality_in_every_segment": true, + "type_cast_if_unsuccessful": true + }, + "InterpolantBarrier": { + "use_low_rank_updates": false + }, + + "use_weighted_polynomials": true, + "input_in_interpolant_basis": true, + "plot_threshold": 20 +} diff --git a/src/volesti/include/sos/gsoc_history.md b/src/volesti/include/sos/gsoc_history.md new file mode 100644 index 00000000..df4354de --- /dev/null +++ b/src/volesti/include/sos/gsoc_history.md @@ -0,0 +1,15 @@ +#### Weekly Progress + +* Week 1: Implementation of [1,2]. +* Week 2: Testing implementation with LP and SDP barrier, Debugging, Refactoring. +* Week 3: Implementation of Interpolant-Barrier, Focus on speeding up implementation with dedicated libraries. +* Week 4: Implementation of Toy Problem: Polynomial Envelope. +* Week 5: Debugging, Refactoring Code. +* Week 6: Debugging, Refactoring Code, Speeding up initialisation of "Polynomial Envelope" Problem + (non-trivial: properties of Chebyshev polynomials, Clenshaw-Curtis algorithm) + * Week 7: Runtime speedups, more efficient (including experimentally) implementation. + * Week 8: Further speedup, Big code refactoring, Add Polynomial minimization, Create Benchmarks with alfonso. + * Week 9: Writing of documentation / report, more refactoring, add multivariate support. + * Week 10: Improve multivariate support, Added multithreading. + * Week 11: Added low-rank updates to both Hessian and intermediate Cholesky decomposition, templating algorithm. + * Week 12: MKL Support, JSON parsing of configuration, templating algorithm diff --git a/src/volesti/include/sos/include/cxxtimer.hpp b/src/volesti/include/sos/include/cxxtimer.hpp new file mode 100644 index 00000000..c3f0b722 --- /dev/null +++ b/src/volesti/include/sos/include/cxxtimer.hpp @@ -0,0 +1,184 @@ +/* + +MIT License + +Copyright (c) 2017 André L. Maravilha + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#ifndef CXX_TIMER_HPP +#define CXX_TIMER_HPP + +#include + + +namespace cxxtimer { + +/** + * This class works as a stopwatch. + */ +class Timer { + +public: + + /** + * Constructor. + * + * @param start + * If true, the timer is started just after construction. + * Otherwise, it will not be automatically started. + */ + Timer(bool start = false); + + /** + * Copy constructor. + * + * @param other + * The object to be copied. + */ + Timer(const Timer& other) = default; + + /** + * Transfer constructor. + * + * @param other + * The object to be transfered. + */ + Timer(Timer&& other) = default; + + /** + * Destructor. + */ + virtual ~Timer() = default; + + /** + * Assignment operator by copy. + * + * @param other + * The object to be copied. + * + * @return A reference to this object. + */ + Timer& operator=(const Timer& other) = default; + + /** + * Assignment operator by transfer. + * + * @param other + * The object to be transferred. + * + * @return A reference to this object. + */ + Timer& operator=(Timer&& other) = default; + + /** + * Start/resume the timer. + */ + void start(); + + /** + * Stop/pause the timer. + */ + void stop(); + + /** + * Reset the timer. + */ + void reset(); + + /** + * Return the elapsed time. + * + * @param duration_t + * The duration type used to return the time elapsed. If not + * specified, it returns the time as represented by + * std::chrono::milliseconds. + * + * @return The elapsed time. + */ + template + typename duration_t::rep count() const; + +private: + + bool started_; + bool paused_; + std::chrono::steady_clock::time_point reference_; + std::chrono::duration accumulated_; +}; + +} + + +inline cxxtimer::Timer::Timer(bool start) : + started_(false), paused_(false), + reference_(std::chrono::steady_clock::now()), + accumulated_(std::chrono::duration(0)) { + if (start) { + this->start(); + } +} + +inline void cxxtimer::Timer::start() { + if (!started_) { + started_ = true; + paused_ = false; + accumulated_ = std::chrono::duration(0); + reference_ = std::chrono::steady_clock::now(); + } else if (paused_) { + reference_ = std::chrono::steady_clock::now(); + paused_ = false; + } +} + +inline void cxxtimer::Timer::stop() { + if (started_ && !paused_) { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + accumulated_ = accumulated_ + std::chrono::duration_cast< std::chrono::duration >(now - reference_); + paused_ = true; + } +} + +inline void cxxtimer::Timer::reset() { + if (started_) { + started_ = false; + paused_ = false; + reference_ = std::chrono::steady_clock::now(); + accumulated_ = std::chrono::duration(0); + } +} + +template +typename duration_t::rep cxxtimer::Timer::count() const { + if (started_) { + if (paused_) { + return std::chrono::duration_cast(accumulated_).count(); + } else { + return std::chrono::duration_cast( + accumulated_ + (std::chrono::steady_clock::now() - reference_)).count(); + } + } else { + return duration_t(0).count(); + } +} + + +#endif diff --git a/src/volesti/include/sos/include/matplotlib-cpp/matplotlibcpp.h b/src/volesti/include/sos/include/matplotlib-cpp/matplotlibcpp.h new file mode 100644 index 00000000..4b2b241f --- /dev/null +++ b/src/volesti/include/sos/include/matplotlib-cpp/matplotlibcpp.h @@ -0,0 +1,2366 @@ +//The MIT License (MIT) +// +//Copyright (c) 2014 Benno Evers +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//SOFTWARE. + +//Credit to https://github.com/lava/matplotlib-cpp/ + +#pragma once + +// Python headers must be included before any system headers, since +// they define _POSIX_C_SOURCE +#include + +#include +#include +#include +#include +#include +#include +#include +#include // requires c++11 support +#include + +#ifndef WITHOUT_NUMPY +# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +# include + +# ifdef WITH_OPENCV +# include +# endif // WITH_OPENCV + +/* + * A bunch of constants were removed in OpenCV 4 in favour of enum classes, so + * define the ones we need here. + */ +# if CV_MAJOR_VERSION > 3 +# define CV_BGR2RGB cv::COLOR_BGR2RGB +# define CV_BGRA2RGBA cv::COLOR_BGRA2RGBA +# endif +#endif // WITHOUT_NUMPY + +#if PY_MAJOR_VERSION >= 3 +# define PyString_FromString PyUnicode_FromString +# define PyInt_FromLong PyLong_FromLong +# define PyString_FromString PyUnicode_FromString +#endif + + +namespace matplotlibcpp { +namespace detail { + +static std::string s_backend; + +struct _interpreter { + PyObject *s_python_function_show; + PyObject *s_python_function_close; + PyObject *s_python_function_draw; + PyObject *s_python_function_pause; + PyObject *s_python_function_save; + PyObject *s_python_function_figure; + PyObject *s_python_function_fignum_exists; + PyObject *s_python_function_plot; + PyObject *s_python_function_quiver; + PyObject *s_python_function_semilogx; + PyObject *s_python_function_semilogy; + PyObject *s_python_function_loglog; + PyObject *s_python_function_fill; + PyObject *s_python_function_fill_between; + PyObject *s_python_function_hist; + PyObject *s_python_function_imshow; + PyObject *s_python_function_scatter; + PyObject *s_python_function_boxplot; + PyObject *s_python_function_subplot; + PyObject *s_python_function_subplot2grid; + PyObject *s_python_function_legend; + PyObject *s_python_function_xlim; + PyObject *s_python_function_ion; + PyObject *s_python_function_ginput; + PyObject *s_python_function_ylim; + PyObject *s_python_function_title; + PyObject *s_python_function_axis; + PyObject *s_python_function_axvline; + PyObject *s_python_function_xlabel; + PyObject *s_python_function_ylabel; + PyObject *s_python_function_gca; + PyObject *s_python_function_xticks; + PyObject *s_python_function_yticks; + PyObject *s_python_function_tick_params; + PyObject *s_python_function_grid; + PyObject *s_python_function_clf; + PyObject *s_python_function_errorbar; + PyObject *s_python_function_annotate; + PyObject *s_python_function_tight_layout; + PyObject *s_python_colormap; + PyObject *s_python_empty_tuple; + PyObject *s_python_function_stem; + PyObject *s_python_function_xkcd; + PyObject *s_python_function_text; + PyObject *s_python_function_suptitle; + PyObject *s_python_function_bar; + PyObject *s_python_function_colorbar; + PyObject *s_python_function_subplots_adjust; + + + /* For now, _interpreter is implemented as a singleton since its currently not possible to have + multiple independent embedded python interpreters without patching the python source code + or starting a separate process for each. + http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program + */ + + static _interpreter& get() { + static _interpreter ctx; + return ctx; + } + + PyObject* safe_import(PyObject* module, std::string fname) { + PyObject* fn = PyObject_GetAttrString(module, fname.c_str()); + + if (!fn) + throw std::runtime_error(std::string("Couldn't find required function: ") + fname); + + if (!PyFunction_Check(fn)) + throw std::runtime_error(fname + std::string(" is unexpectedly not a PyFunction.")); + + return fn; + } + +private: + +#ifndef WITHOUT_NUMPY +# if PY_MAJOR_VERSION >= 3 + + void *import_numpy() { + import_array(); // initialize C-API + return NULL; + } + +# else + + void import_numpy() { + import_array(); // initialize C-API + } + +# endif +#endif + + _interpreter() { + + // optional but recommended +#if PY_MAJOR_VERSION >= 3 + wchar_t name[] = L"plotting"; +#else + char name[] = "plotting"; +#endif + Py_SetProgramName(name); + Py_Initialize(); + +#ifndef WITHOUT_NUMPY + import_numpy(); // initialize numpy C-API +#endif + + PyObject* matplotlibname = PyString_FromString("matplotlib"); + PyObject* pyplotname = PyString_FromString("matplotlib.pyplot"); + PyObject* cmname = PyString_FromString("matplotlib.cm"); + PyObject* pylabname = PyString_FromString("pylab"); + if (!pyplotname || !pylabname || !matplotlibname || !cmname) { + throw std::runtime_error("couldnt create string"); + } + + PyObject* matplotlib = PyImport_Import(matplotlibname); + Py_DECREF(matplotlibname); + if (!matplotlib) { + PyErr_Print(); + throw std::runtime_error("Error loading module matplotlib!"); + } + + // matplotlib.use() must be called *before* pylab, matplotlib.pyplot, + // or matplotlib.backends is imported for the first time + if (!s_backend.empty()) { + PyObject_CallMethod(matplotlib, const_cast("use"), const_cast("s"), s_backend.c_str()); + } + + PyObject* pymod = PyImport_Import(pyplotname); + Py_DECREF(pyplotname); + if (!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); } + + s_python_colormap = PyImport_Import(cmname); + Py_DECREF(cmname); + if (!s_python_colormap) { throw std::runtime_error("Error loading module matplotlib.cm!"); } + + PyObject* pylabmod = PyImport_Import(pylabname); + Py_DECREF(pylabname); + if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); } + + s_python_function_show = safe_import(pymod, "show"); + s_python_function_close = safe_import(pymod, "close"); + s_python_function_draw = safe_import(pymod, "draw"); + s_python_function_pause = safe_import(pymod, "pause"); + s_python_function_figure = safe_import(pymod, "figure"); + s_python_function_fignum_exists = safe_import(pymod, "fignum_exists"); + s_python_function_plot = safe_import(pymod, "plot"); + s_python_function_quiver = safe_import(pymod, "quiver"); + s_python_function_semilogx = safe_import(pymod, "semilogx"); + s_python_function_semilogy = safe_import(pymod, "semilogy"); + s_python_function_loglog = safe_import(pymod, "loglog"); + s_python_function_fill = safe_import(pymod, "fill"); + s_python_function_fill_between = safe_import(pymod, "fill_between"); + s_python_function_hist = safe_import(pymod,"hist"); + s_python_function_scatter = safe_import(pymod,"scatter"); + s_python_function_boxplot = safe_import(pymod,"boxplot"); + s_python_function_subplot = safe_import(pymod, "subplot"); + s_python_function_subplot2grid = safe_import(pymod, "subplot2grid"); + s_python_function_legend = safe_import(pymod, "legend"); + s_python_function_ylim = safe_import(pymod, "ylim"); + s_python_function_title = safe_import(pymod, "title"); + s_python_function_axis = safe_import(pymod, "axis"); + s_python_function_axvline = safe_import(pymod, "axvline"); + s_python_function_xlabel = safe_import(pymod, "xlabel"); + s_python_function_ylabel = safe_import(pymod, "ylabel"); + s_python_function_gca = safe_import(pymod, "gca"); + s_python_function_xticks = safe_import(pymod, "xticks"); + s_python_function_yticks = safe_import(pymod, "yticks"); + s_python_function_tick_params = safe_import(pymod, "tick_params"); + s_python_function_grid = safe_import(pymod, "grid"); + s_python_function_xlim = safe_import(pymod, "xlim"); + s_python_function_ion = safe_import(pymod, "ion"); + s_python_function_ginput = safe_import(pymod, "ginput"); + s_python_function_save = safe_import(pylabmod, "savefig"); + s_python_function_annotate = safe_import(pymod,"annotate"); + s_python_function_clf = safe_import(pymod, "clf"); + s_python_function_errorbar = safe_import(pymod, "errorbar"); + s_python_function_tight_layout = safe_import(pymod, "tight_layout"); + s_python_function_stem = safe_import(pymod, "stem"); + s_python_function_xkcd = safe_import(pymod, "xkcd"); + s_python_function_text = safe_import(pymod, "text"); + s_python_function_suptitle = safe_import(pymod, "suptitle"); + s_python_function_bar = safe_import(pymod,"bar"); + s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); + s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust"); +#ifndef WITHOUT_NUMPY + s_python_function_imshow = safe_import(pymod, "imshow"); +#endif + s_python_empty_tuple = PyTuple_New(0); + } + + ~_interpreter() { + Py_Finalize(); + } +}; + +} // end namespace detail + +/// Select the backend +/// +/// **NOTE:** This must be called before the first plot command to have +/// any effect. +/// +/// Mainly useful to select the non-interactive 'Agg' backend when running +/// matplotlibcpp in headless mode, for example on a machine with no display. +/// +/// See also: https://matplotlib.org/2.0.2/api/matplotlib_configuration_api.html#matplotlib.use +inline void backend(const std::string& name) +{ + detail::s_backend = name; +} + +inline bool annotate(std::string annotation, double x, double y) +{ + detail::_interpreter::get(); + + PyObject * xy = PyTuple_New(2); + PyObject * str = PyString_FromString(annotation.c_str()); + + PyTuple_SetItem(xy,0,PyFloat_FromDouble(x)); + PyTuple_SetItem(xy,1,PyFloat_FromDouble(y)); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "xy", xy); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, str); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if(res) Py_DECREF(res); + + return res; +} + +namespace detail { + +#ifndef WITHOUT_NUMPY +// Type selector for numpy array conversion +template struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default +template <> struct select_npy_type { const static NPY_TYPES type = NPY_DOUBLE; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_FLOAT; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_BOOL; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT8; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_SHORT; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT8; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_USHORT; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_ULONG; }; +template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; + +// Sanity checks; comment them out or change the numpy type below if you're compiling on +// a platform where they don't apply +//static_assert(sizeof(long long) == 8); +//template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; +//static_assert(sizeof(unsigned long long) == 8); +//template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; +// TODO: add int, long, etc. + +template +PyObject* get_array(const std::vector& v) +{ + npy_intp vsize = v.size(); + NPY_TYPES type = select_npy_type::type; + if (type == NPY_NOTYPE) { + size_t memsize = v.size()*sizeof(double); + double* dp = static_cast(::malloc(memsize)); + for (size_t i=0; i(varray), NPY_ARRAY_OWNDATA); + return varray; + } + + PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data())); + return varray; +} + + +template +PyObject* get_2darray(const std::vector<::std::vector>& v) +{ + if (v.size() < 1) throw std::runtime_error("get_2d_array v too small"); + + npy_intp vsize[2] = {static_cast(v.size()), + static_cast(v[0].size())}; + + PyArrayObject *varray = + (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE); + + double *vd_begin = static_cast(PyArray_DATA(varray)); + + for (const ::std::vector &v_row : v) { + if (v_row.size() != static_cast(vsize[1])) + throw std::runtime_error("Missmatched array size"); + std::copy(v_row.begin(), v_row.end(), vd_begin); + vd_begin += vsize[1]; + } + + return reinterpret_cast(varray); +} + +#else // fallback if we don't have numpy: copy every element of the given vector + +template +PyObject* get_array(const std::vector& v) +{ + PyObject* list = PyList_New(v.size()); + for(size_t i = 0; i < v.size(); ++i) { + PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i))); + } + return list; +} + +#endif // WITHOUT_NUMPY + +// sometimes, for labels and such, we need string arrays +inline PyObject * get_array(const std::vector& strings) +{ + PyObject* list = PyList_New(strings.size()); + for (std::size_t i = 0; i < strings.size(); ++i) { + PyList_SetItem(list, i, PyString_FromString(strings[i].c_str())); + } + return list; +} + +// not all matplotlib need 2d arrays, some prefer lists of lists +template +PyObject* get_listlist(const std::vector>& ll) +{ + PyObject* listlist = PyList_New(ll.size()); + for (std::size_t i = 0; i < ll.size(); ++i) { + PyList_SetItem(listlist, i, get_array(ll[i])); + } + return listlist; +} + +} // namespace detail + +/// Plot a line through the given x and y data points.. +/// +/// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html +template +bool plot(const std::vector &x, const std::vector &y, const std::map& keywords) +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + // using numpy arrays + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + // construct positional args + PyObject* args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; +} + +// TODO - it should be possible to make this work by implementing +// a non-numpy alternative for `detail::get_2darray()`. +#ifndef WITHOUT_NUMPY +template +void plot_surface(const std::vector<::std::vector> &x, + const std::vector<::std::vector> &y, + const std::vector<::std::vector> &z, + const std::map &keywords = + std::map()) +{ + detail::_interpreter::get(); + + // We lazily load the modules here the first time this function is called + // because I'm not sure that we can assume "matplotlib installed" implies + // "mpl_toolkits installed" on all platforms, and we don't want to require + // it for people who don't need 3d plots. + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + detail::_interpreter::get(); + + PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } + + mpl_toolkitsmod = PyImport_Import(mpl_toolkits); + Py_DECREF(mpl_toolkits); + if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } + + axis3dmod = PyImport_Import(axis3d); + Py_DECREF(axis3d); + if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } + } + + assert(x.size() == y.size()); + assert(y.size() == z.size()); + + // using numpy arrays + PyObject *xarray = detail::get_2darray(x); + PyObject *yarray = detail::get_2darray(y); + PyObject *zarray = detail::get_2darray(z); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + PyTuple_SetItem(args, 2, zarray); + + // Build up the kw args. + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "rstride", PyInt_FromLong(1)); + PyDict_SetItemString(kwargs, "cstride", PyInt_FromLong(1)); + + PyObject *python_colormap_coolwarm = PyObject_GetAttrString( + detail::_interpreter::get().s_python_colormap, "coolwarm"); + + PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + + PyObject *fig = + PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + if (!fig) throw std::runtime_error("Call to figure() failed."); + + PyObject *gca_kwargs = PyDict_New(); + PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); + + PyObject *gca = PyObject_GetAttrString(fig, "gca"); + if (!gca) throw std::runtime_error("No gca"); + Py_INCREF(gca); + PyObject *axis = PyObject_Call( + gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); + + if (!axis) throw std::runtime_error("No axis"); + Py_INCREF(axis); + + Py_DECREF(gca); + Py_DECREF(gca_kwargs); + + PyObject *plot_surface = PyObject_GetAttrString(axis, "plot_surface"); + if (!plot_surface) throw std::runtime_error("No surface"); + Py_INCREF(plot_surface); + PyObject *res = PyObject_Call(plot_surface, args, kwargs); + if (!res) throw std::runtime_error("failed surface"); + Py_DECREF(plot_surface); + + Py_DECREF(axis); + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); +} +#endif // WITHOUT_NUMPY + +template +void plot3(const std::vector &x, + const std::vector &y, + const std::vector &z, + const std::map &keywords = + std::map()) +{ + detail::_interpreter::get(); + + // Same as with plot_surface: We lazily load the modules here the first time + // this function is called because I'm not sure that we can assume "matplotlib + // installed" implies "mpl_toolkits installed" on all platforms, and we don't + // want to require it for people who don't need 3d plots. + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + detail::_interpreter::get(); + + PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } + + mpl_toolkitsmod = PyImport_Import(mpl_toolkits); + Py_DECREF(mpl_toolkits); + if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } + + axis3dmod = PyImport_Import(axis3d); + Py_DECREF(axis3d); + if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } + } + + assert(x.size() == y.size()); + assert(y.size() == z.size()); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *zarray = detail::get_array(z); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + PyTuple_SetItem(args, 2, zarray); + + // Build up the kw args. + PyObject *kwargs = PyDict_New(); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject *fig = + PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + if (!fig) throw std::runtime_error("Call to figure() failed."); + + PyObject *gca_kwargs = PyDict_New(); + PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); + + PyObject *gca = PyObject_GetAttrString(fig, "gca"); + if (!gca) throw std::runtime_error("No gca"); + Py_INCREF(gca); + PyObject *axis = PyObject_Call( + gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); + + if (!axis) throw std::runtime_error("No axis"); + Py_INCREF(axis); + + Py_DECREF(gca); + Py_DECREF(gca_kwargs); + + PyObject *plot3 = PyObject_GetAttrString(axis, "plot"); + if (!plot3) throw std::runtime_error("No 3D line plot"); + Py_INCREF(plot3); + PyObject *res = PyObject_Call(plot3, args, kwargs); + if (!res) throw std::runtime_error("Failed 3D line plot"); + Py_DECREF(plot3); + + Py_DECREF(axis); + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); +} + +template +bool stem(const std::vector &x, const std::vector &y, const std::map& keywords) +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + // using numpy arrays + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + // construct positional args + PyObject* args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for (std::map::const_iterator it = + keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_stem, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); + + return res; +} + +template< typename Numeric > +bool fill(const std::vector& x, const std::vector& y, const std::map& keywords) +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + // using numpy arrays + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + // construct positional args + PyObject* args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if (res) Py_DECREF(res); + + return res; +} + +template< typename Numeric > +bool fill_between(const std::vector& x, const std::vector& y1, const std::vector& y2, const std::map& keywords) +{ + assert(x.size() == y1.size()); + assert(x.size() == y2.size()); + + detail::_interpreter::get(); + + // using numpy arrays + PyObject* xarray = detail::get_array(x); + PyObject* y1array = detail::get_array(y1); + PyObject* y2array = detail::get_array(y2); + + // construct positional args + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, y1array); + PyTuple_SetItem(args, 2, y2array); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill_between, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; +} + +template< typename Numeric> +bool hist(const std::vector& y, long bins=10,std::string color="b", + double alpha=1.0, bool cumulative=false) +{ + detail::_interpreter::get(); + + PyObject* yarray = detail::get_array(y); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); + PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); + PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); + PyDict_SetItemString(kwargs, "cumulative", cumulative ? Py_True : Py_False); + + PyObject* plot_args = PyTuple_New(1); + + PyTuple_SetItem(plot_args, 0, yarray); + + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); + + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; +} + +#ifndef WITHOUT_NUMPY +namespace detail { + +inline void imshow(void *ptr, const NPY_TYPES type, const int rows, const int columns, const int colors, const std::map &keywords, PyObject** out) +{ + assert(type == NPY_UINT8 || type == NPY_FLOAT); + assert(colors == 1 || colors == 3 || colors == 4); + + detail::_interpreter::get(); + + // construct args + npy_intp dims[3] = { rows, columns, colors }; + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyArray_SimpleNewFromData(colors == 1 ? 2 : 3, dims, type, ptr)); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_imshow, args, kwargs); + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) + throw std::runtime_error("Call to imshow() failed"); + if (out) + *out = res; + else + Py_DECREF(res); +} + +} // namespace detail + +inline void imshow(const unsigned char *ptr, const int rows, const int columns, const int colors, const std::map &keywords = {}, PyObject** out = nullptr) +{ + detail::imshow((void *) ptr, NPY_UINT8, rows, columns, colors, keywords, out); +} + +inline void imshow(const float *ptr, const int rows, const int columns, const int colors, const std::map &keywords = {}, PyObject** out = nullptr) +{ + detail::imshow((void *) ptr, NPY_FLOAT, rows, columns, colors, keywords, out); +} + +#ifdef WITH_OPENCV +void imshow(const cv::Mat &image, const std::map &keywords = {}) +{ + // Convert underlying type of matrix, if needed + cv::Mat image2; + NPY_TYPES npy_type = NPY_UINT8; + switch (image.type() & CV_MAT_DEPTH_MASK) { + case CV_8U: + image2 = image; + break; + case CV_32F: + image2 = image; + npy_type = NPY_FLOAT; + break; + default: + image.convertTo(image2, CV_MAKETYPE(CV_8U, image.channels())); + } + + // If color image, convert from BGR to RGB + switch (image2.channels()) { + case 3: + cv::cvtColor(image2, image2, CV_BGR2RGB); + break; + case 4: + cv::cvtColor(image2, image2, CV_BGRA2RGBA); + } + + detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords); +} +#endif // WITH_OPENCV +#endif // WITHOUT_NUMPY + +template +bool scatter(const std::vector& x, + const std::vector& y, + const double s=1.0, // The marker size in points**2 + const std::map & keywords = {}) +{ + detail::_interpreter::get(); + + assert(x.size() == y.size()); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); + for (const auto& it : keywords) + { + PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); + } + + PyObject* plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; +} + +template +bool boxplot(const std::vector>& data, + const std::vector& labels = {}, + const std::map & keywords = {}) +{ + detail::_interpreter::get(); + + PyObject* listlist = detail::get_listlist(data); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, listlist); + + PyObject* kwargs = PyDict_New(); + + // kwargs needs the labels, if there are (the correct number of) labels + if (!labels.empty() && labels.size() == data.size()) { + PyDict_SetItemString(kwargs, "labels", detail::get_array(labels)); + } + + // take care of the remaining keywords + for (const auto& it : keywords) + { + PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if(res) Py_DECREF(res); + + return res; +} + +template +bool boxplot(const std::vector& data, + const std::map & keywords = {}) +{ + detail::_interpreter::get(); + + PyObject* vector = detail::get_array(data); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, vector); + + PyObject* kwargs = PyDict_New(); + for (const auto& it : keywords) + { + PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if(res) Py_DECREF(res); + + return res; +} + +template +bool bar(const std::vector & x, + const std::vector & y, + std::string ec = "black", + std::string ls = "-", + double lw = 1.0, + const std::map & keywords = {}) +{ + detail::_interpreter::get(); + + PyObject * xarray = detail::get_array(x); + PyObject * yarray = detail::get_array(y); + + PyObject * kwargs = PyDict_New(); + + PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); + PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str())); + PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw)); + + for (std::map::const_iterator it = + keywords.begin(); + it != keywords.end(); + ++it) { + PyDict_SetItemString( + kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject * plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject * res = PyObject_Call( + detail::_interpreter::get().s_python_function_bar, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; +} + +template +bool bar(const std::vector & y, + std::string ec = "black", + std::string ls = "-", + double lw = 1.0, + const std::map & keywords = {}) +{ + using T = typename std::remove_reference::type::value_type; + + detail::_interpreter::get(); + + std::vector x; + for (std::size_t i = 0; i < y.size(); i++) { x.push_back(i); } + + return bar(x, y, ec, ls, lw, keywords); +} + +inline bool subplots_adjust(const std::map& keywords = {}) +{ + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + for (std::map::const_iterator it = + keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyFloat_FromDouble(it->second)); + } + + + PyObject* plot_args = PyTuple_New(0); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; +} + +template< typename Numeric> +bool named_hist(std::string label,const std::vector& y, long bins=10, std::string color="b", double alpha=1.0) +{ + detail::_interpreter::get(); + + PyObject* yarray = detail::get_array(y); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str())); + PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); + PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); + PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); + + + PyObject* plot_args = PyTuple_New(1); + PyTuple_SetItem(plot_args, 0, yarray); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; +} + +template +bool plot(const std::vector& x, const std::vector& y, const std::string& s = "") +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); + + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + + return res; +} + +template +bool quiver(const std::vector& x, const std::vector& y, const std::vector& u, const std::vector& w, const std::map& keywords = {}) +{ + assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size()); + + detail::_interpreter::get(); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + PyObject* uarray = detail::get_array(u); + PyObject* warray = detail::get_array(w); + + PyObject* plot_args = PyTuple_New(4); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, uarray); + PyTuple_SetItem(plot_args, 3, warray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_quiver, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) + Py_DECREF(res); + + return res; +} + +template +bool stem(const std::vector& x, const std::vector& y, const std::string& s = "") +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_stem, plot_args); + + Py_DECREF(plot_args); + if (res) + Py_DECREF(res); + + return res; +} + +template +bool semilogx(const std::vector& x, const std::vector& y, const std::string& s = "") +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogx, plot_args); + + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + + return res; +} + +template +bool semilogy(const std::vector& x, const std::vector& y, const std::string& s = "") +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogy, plot_args); + + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + + return res; +} + +template +bool loglog(const std::vector& x, const std::vector& y, const std::string& s = "") +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_loglog, plot_args); + + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + + return res; +} + +template +bool errorbar(const std::vector &x, const std::vector &y, const std::vector &yerr, const std::map &keywords = {}) +{ + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + PyObject* yerrarray = detail::get_array(yerr); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + } + + PyDict_SetItemString(kwargs, "yerr", yerrarray); + + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_errorbar, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + + if (res) + Py_DECREF(res); + else + throw std::runtime_error("Call to errorbar() failed."); + + return res; +} + +template +bool named_plot(const std::string& name, const std::vector& y, const std::string& format = "") +{ + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(2); + + PyTuple_SetItem(plot_args, 0, yarray); + PyTuple_SetItem(plot_args, 1, pystring); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; +} + +template +bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") +{ + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; +} + +template +bool named_semilogx(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") +{ + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; +} + +template +bool named_semilogy(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") +{ + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; +} + +template +bool named_loglog(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") +{ + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; +} + +template +bool plot(const std::vector& y, const std::string& format = "") +{ + std::vector x(y.size()); + for(size_t i=0; i +bool plot(const std::vector& y, const std::map& keywords) +{ + std::vector x(y.size()); + for(size_t i=0; i +bool stem(const std::vector& y, const std::string& format = "") +{ + std::vector x(y.size()); + for (size_t i = 0; i < x.size(); ++i) x.at(i) = i; + return stem(x, y, format); +} + +template +void text(Numeric x, Numeric y, const std::string& s = "") +{ + detail::_interpreter::get(); + + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(y)); + PyTuple_SetItem(args, 2, PyString_FromString(s.c_str())); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_text, args); + if(!res) throw std::runtime_error("Call to text() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void colorbar(PyObject* mappable = NULL, const std::map& keywords = {}) +{ + if (mappable == NULL) + throw std::runtime_error("Must call colorbar with PyObject* returned from an image, contour, surface, etc."); + + detail::_interpreter::get(); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, mappable); + + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(it->second)); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_colorbar, args, kwargs); + if(!res) throw std::runtime_error("Call to colorbar() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + + +inline long figure(long number = -1) +{ + detail::_interpreter::get(); + + PyObject *res; + if (number == -1) + res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple); + else { + assert(number > 0); + + // Make sure interpreter is initialised + detail::_interpreter::get(); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(number)); + res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, args); + Py_DECREF(args); + } + + if(!res) throw std::runtime_error("Call to figure() failed."); + + PyObject* num = PyObject_GetAttrString(res, "number"); + if (!num) throw std::runtime_error("Could not get number attribute of figure object"); + const long figureNumber = PyLong_AsLong(num); + + Py_DECREF(num); + Py_DECREF(res); + + return figureNumber; +} + +inline bool fignum_exists(long number) +{ + detail::_interpreter::get(); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(number)); + PyObject *res = PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, args); + if(!res) throw std::runtime_error("Call to fignum_exists() failed."); + + bool ret = PyObject_IsTrue(res); + Py_DECREF(res); + Py_DECREF(args); + + return ret; +} + +inline void figure_size(size_t w, size_t h) +{ + detail::_interpreter::get(); + + const size_t dpi = 100; + PyObject* size = PyTuple_New(2); + PyTuple_SetItem(size, 0, PyFloat_FromDouble((double)w / dpi)); + PyTuple_SetItem(size, 1, PyFloat_FromDouble((double)h / dpi)); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "figsize", size); + PyDict_SetItemString(kwargs, "dpi", PyLong_FromSize_t(dpi)); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple, kwargs); + + Py_DECREF(kwargs); + + if(!res) throw std::runtime_error("Call to figure_size() failed."); + Py_DECREF(res); +} + +inline void legend() +{ + detail::_interpreter::get(); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple); + if(!res) throw std::runtime_error("Call to legend() failed."); + + Py_DECREF(res); +} + +template +void ylim(Numeric left, Numeric right) +{ + detail::_interpreter::get(); + + PyObject* list = PyList_New(2); + PyList_SetItem(list, 0, PyFloat_FromDouble(left)); + PyList_SetItem(list, 1, PyFloat_FromDouble(right)); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, list); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); + if(!res) throw std::runtime_error("Call to ylim() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +template +void xlim(Numeric left, Numeric right) +{ + detail::_interpreter::get(); + + PyObject* list = PyList_New(2); + PyList_SetItem(list, 0, PyFloat_FromDouble(left)); + PyList_SetItem(list, 1, PyFloat_FromDouble(right)); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, list); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); + if(!res) throw std::runtime_error("Call to xlim() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + + +inline double* xlim() +{ + detail::_interpreter::get(); + + PyObject* args = PyTuple_New(0); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); + PyObject* left = PyTuple_GetItem(res,0); + PyObject* right = PyTuple_GetItem(res,1); + + double* arr = new double[2]; + arr[0] = PyFloat_AsDouble(left); + arr[1] = PyFloat_AsDouble(right); + + if(!res) throw std::runtime_error("Call to xlim() failed."); + + Py_DECREF(res); + return arr; +} + + +inline double* ylim() +{ + detail::_interpreter::get(); + + PyObject* args = PyTuple_New(0); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); + PyObject* left = PyTuple_GetItem(res,0); + PyObject* right = PyTuple_GetItem(res,1); + + double* arr = new double[2]; + arr[0] = PyFloat_AsDouble(left); + arr[1] = PyFloat_AsDouble(right); + + if(!res) throw std::runtime_error("Call to ylim() failed."); + + Py_DECREF(res); + return arr; +} + +template +inline void xticks(const std::vector &ticks, const std::vector &labels = {}, const std::map& keywords = {}) +{ + assert(labels.size() == 0 || ticks.size() == labels.size()); + + detail::_interpreter::get(); + + // using numpy array + PyObject* ticksarray = detail::get_array(ticks); + + PyObject* args; + if(labels.size() == 0) { + // construct positional args + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, ticksarray); + } else { + // make tuple of tick labels + PyObject* labelstuple = PyTuple_New(labels.size()); + for (size_t i = 0; i < labels.size(); i++) + PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); + + // construct positional args + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, ticksarray); + PyTuple_SetItem(args, 1, labelstuple); + } + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xticks, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if(!res) throw std::runtime_error("Call to xticks() failed"); + + Py_DECREF(res); +} + +template +inline void xticks(const std::vector &ticks, const std::map& keywords) +{ + xticks(ticks, {}, keywords); +} + +template +inline void yticks(const std::vector &ticks, const std::vector &labels = {}, const std::map& keywords = {}) +{ + assert(labels.size() == 0 || ticks.size() == labels.size()); + + detail::_interpreter::get(); + + // using numpy array + PyObject* ticksarray = detail::get_array(ticks); + + PyObject* args; + if(labels.size() == 0) { + // construct positional args + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, ticksarray); + } else { + // make tuple of tick labels + PyObject* labelstuple = PyTuple_New(labels.size()); + for (size_t i = 0; i < labels.size(); i++) + PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); + + // construct positional args + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, ticksarray); + PyTuple_SetItem(args, 1, labelstuple); + } + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_yticks, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if(!res) throw std::runtime_error("Call to yticks() failed"); + + Py_DECREF(res); +} + +template +inline void yticks(const std::vector &ticks, const std::map& keywords) +{ + yticks(ticks, {}, keywords); +} + +inline void tick_params(const std::map& keywords, const std::string axis = "both") +{ + detail::_interpreter::get(); + + // construct positional args + PyObject* args; + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyString_FromString(axis.c_str())); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + } + + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_tick_params, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to tick_params() failed"); + + Py_DECREF(res); +} + +inline void subplot(long nrows, long ncols, long plot_number) +{ + detail::_interpreter::get(); + + // construct positional args + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number)); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args); + if(!res) throw std::runtime_error("Call to subplot() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void subplot2grid(long nrows, long ncols, long rowid=0, long colid=0, long rowspan=1, long colspan=1) +{ + detail::_interpreter::get(); + + PyObject* shape = PyTuple_New(2); + PyTuple_SetItem(shape, 0, PyLong_FromLong(nrows)); + PyTuple_SetItem(shape, 1, PyLong_FromLong(ncols)); + + PyObject* loc = PyTuple_New(2); + PyTuple_SetItem(loc, 0, PyLong_FromLong(rowid)); + PyTuple_SetItem(loc, 1, PyLong_FromLong(colid)); + + PyObject* args = PyTuple_New(4); + PyTuple_SetItem(args, 0, shape); + PyTuple_SetItem(args, 1, loc); + PyTuple_SetItem(args, 2, PyLong_FromLong(rowspan)); + PyTuple_SetItem(args, 3, PyLong_FromLong(colspan)); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot2grid, args); + if(!res) throw std::runtime_error("Call to subplot2grid() failed."); + + Py_DECREF(shape); + Py_DECREF(loc); + Py_DECREF(args); + Py_DECREF(res); +} + +inline void title(const std::string &titlestr, const std::map &keywords = {}) +{ + detail::_interpreter::get(); + + PyObject* pytitlestr = PyString_FromString(titlestr.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pytitlestr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_title, args, kwargs); + if(!res) throw std::runtime_error("Call to title() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +inline void suptitle(const std::string &suptitlestr, const std::map &keywords = {}) +{ + detail::_interpreter::get(); + + PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pysuptitlestr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_suptitle, args, kwargs); + if(!res) throw std::runtime_error("Call to suptitle() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +inline void axis(const std::string &axisstr) +{ + detail::_interpreter::get(); + + PyObject* str = PyString_FromString(axisstr.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, str); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args); + if(!res) throw std::runtime_error("Call to title() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void axvline(double x, double ymin = 0., double ymax = 1., const std::map& keywords_string = std::map(), + const std::map& keywords_double = std::map()) +{ + detail::_interpreter::get(); + + // construct positional args + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(ymin)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymax)); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords_string.begin(); it != keywords_string.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + } + + for(std::map::const_iterator it = keywords_double.begin(); it != keywords_double.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(it->second)); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvline, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if(res) Py_DECREF(res); +} + +inline void xlabel(const std::string &str, const std::map &keywords = {}) +{ + detail::_interpreter::get(); + + PyObject* pystr = PyString_FromString(str.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xlabel, args, kwargs); + if(!res) throw std::runtime_error("Call to xlabel() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +inline void ylabel(const std::string &str, const std::map& keywords = {}) +{ + detail::_interpreter::get(); + + PyObject* pystr = PyString_FromString(str.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_ylabel, args, kwargs); + if(!res) throw std::runtime_error("Call to ylabel() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +inline void set_zlabel(const std::string &str, const std::map& keywords = {}) +{ + detail::_interpreter::get(); + + // Same as with plot_surface: We lazily load the modules here the first time + // this function is called because I'm not sure that we can assume "matplotlib + // installed" implies "mpl_toolkits installed" on all platforms, and we don't + // want to require it for people who don't need 3d plots. + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } + + mpl_toolkitsmod = PyImport_Import(mpl_toolkits); + Py_DECREF(mpl_toolkits); + if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } + + axis3dmod = PyImport_Import(axis3d); + Py_DECREF(axis3d); + if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } + } + + PyObject* pystr = PyString_FromString(str.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject *ax = + PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, + detail::_interpreter::get().s_python_empty_tuple); + if (!ax) throw std::runtime_error("Call to gca() failed."); + Py_INCREF(ax); + + PyObject *zlabel = PyObject_GetAttrString(ax, "set_zlabel"); + if (!zlabel) throw std::runtime_error("Attribute set_zlabel not found."); + Py_INCREF(zlabel); + + PyObject *res = PyObject_Call(zlabel, args, kwargs); + if (!res) throw std::runtime_error("Call to set_zlabel() failed."); + Py_DECREF(zlabel); + + Py_DECREF(ax); + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); +} + +inline void grid(bool flag) +{ + detail::_interpreter::get(); + + PyObject* pyflag = flag ? Py_True : Py_False; + Py_INCREF(pyflag); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyflag); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args); + if(!res) throw std::runtime_error("Call to grid() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void show(const bool block = true) +{ + detail::_interpreter::get(); + + PyObject* res; + if(block) + { + res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_show, + detail::_interpreter::get().s_python_empty_tuple); + } + else + { + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "block", Py_False); + res = PyObject_Call( detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple, kwargs); + Py_DECREF(kwargs); + } + + + if (!res) throw std::runtime_error("Call to show() failed."); + + Py_DECREF(res); +} + +inline void close() +{ + detail::_interpreter::get(); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_close, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to close() failed."); + + Py_DECREF(res); +} + +inline void xkcd() { + detail::_interpreter::get(); + + PyObject* res; + PyObject *kwargs = PyDict_New(); + + res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd, + detail::_interpreter::get().s_python_empty_tuple, kwargs); + + Py_DECREF(kwargs); + + if (!res) + throw std::runtime_error("Call to show() failed."); + + Py_DECREF(res); +} + +inline void draw() +{ + detail::_interpreter::get(); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_draw, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to draw() failed."); + + Py_DECREF(res); +} + +template +inline void pause(Numeric interval) +{ + detail::_interpreter::get(); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval)); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args); + if(!res) throw std::runtime_error("Call to pause() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void save(const std::string& filename) +{ + detail::_interpreter::get(); + + PyObject* pyfilename = PyString_FromString(filename.c_str()); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyfilename); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args); + if (!res) throw std::runtime_error("Call to save() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void clf() { + detail::_interpreter::get(); + + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_clf, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to clf() failed."); + + Py_DECREF(res); +} + +inline void ion() { + detail::_interpreter::get(); + + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_ion, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to ion() failed."); + + Py_DECREF(res); +} + +inline std::vector> ginput(const int numClicks = 1, const std::map& keywords = {}) +{ + detail::_interpreter::get(); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks)); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_ginput, args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to ginput() failed."); + + const size_t len = PyList_Size(res); + std::vector> out; + out.reserve(len); + for (size_t i = 0; i < len; i++) { + PyObject *current = PyList_GetItem(res, i); + std::array position; + position[0] = PyFloat_AsDouble(PyTuple_GetItem(current, 0)); + position[1] = PyFloat_AsDouble(PyTuple_GetItem(current, 1)); + out.push_back(position); + } + Py_DECREF(res); + + return out; +} + +// Actually, is there any reason not to call this automatically for every plot? +inline void tight_layout() { + detail::_interpreter::get(); + + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_tight_layout, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to tight_layout() failed."); + + Py_DECREF(res); +} + +// Support for variadic plot() and initializer lists: + +namespace detail { + +template +using is_function = typename std::is_function>>::type; + +template +struct is_callable_impl; + +template +struct is_callable_impl +{ + typedef is_function type; +}; // a non-object is callable iff it is a function + +template +struct is_callable_impl +{ + struct Fallback { void operator()(); }; + struct Derived : T, Fallback { }; + + template struct Check; + + template + static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match + + template + static std::false_type test( Check* ); + +public: + typedef decltype(test(nullptr)) type; + typedef decltype(&Fallback::operator()) dtype; + static constexpr bool value = type::value; +}; // an object is callable iff it defines operator() + +template +struct is_callable +{ + // dispatch to is_callable_impl or is_callable_impl depending on whether T is of class type or not + typedef typename is_callable_impl::value, T>::type type; +}; + +template +struct plot_impl { }; + +template<> +struct plot_impl +{ + template + bool operator()(const IterableX& x, const IterableY& y, const std::string& format) + { + // 2-phase lookup for distance, begin, end + using std::distance; + using std::begin; + using std::end; + + auto xs = distance(begin(x), end(x)); + auto ys = distance(begin(y), end(y)); + assert(xs == ys && "x and y data must have the same number of elements!"); + + PyObject* xlist = PyList_New(xs); + PyObject* ylist = PyList_New(ys); + PyObject* pystring = PyString_FromString(format.c_str()); + + auto itx = begin(x), ity = begin(y); + for(size_t i = 0; i < xs; ++i) { + PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++)); + PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++)); + } + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xlist); + PyTuple_SetItem(plot_args, 1, ylist); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); + + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + + return res; + } +}; + +template<> +struct plot_impl +{ + template + bool operator()(const Iterable& ticks, const Callable& f, const std::string& format) + { + if(begin(ticks) == end(ticks)) return true; + + // We could use additional meta-programming to deduce the correct element type of y, + // but all values have to be convertible to double anyways + std::vector y; + for(auto x : ticks) y.push_back(f(x)); + return plot_impl()(ticks,y,format); + } +}; + +} // end namespace detail + +// recursion stop for the above +template +bool plot() { return true; } + +template +bool plot(const A& a, const B& b, const std::string& format, Args... args) +{ + return detail::plot_impl::type>()(a,b,format) && plot(args...); +} + +/* + * This group of plot() functions is needed to support initializer lists, i.e. calling + * plot( {1,2,3,4} ) + */ +inline bool plot(const std::vector& x, const std::vector& y, const std::string& format = "") { + return plot(x,y,format); +} + +inline bool plot(const std::vector& y, const std::string& format = "") { + return plot(y,format); +} + +inline bool plot(const std::vector& x, const std::vector& y, const std::map& keywords) { + return plot(x,y,keywords); +} + +/* + * This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting + */ +class Plot +{ +public: + // default initialization with plot label, some data and format + template + Plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { + detail::_interpreter::get(); + + assert(x.size() == y.size()); + + PyObject* kwargs = PyDict_New(); + if(name != "") + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + + if(res) + { + line= PyList_GetItem(res, 0); + + if(line) + set_data_fct = PyObject_GetAttrString(line,"set_data"); + else + Py_DECREF(line); + Py_DECREF(res); + } + } + + // shorter initialization with name or format only + // basically calls line, = plot([], []) + Plot(const std::string& name = "", const std::string& format = "") + : Plot(name, std::vector(), std::vector(), format) {} + + template + bool update(const std::vector& x, const std::vector& y) { + assert(x.size() == y.size()); + if(set_data_fct) + { + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + + PyObject* plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject* res = PyObject_CallObject(set_data_fct, plot_args); + if (res) Py_DECREF(res); + return res; + } + return false; + } + + // clears the plot but keep it available + bool clear() { + return update(std::vector(), std::vector()); + } + + // definitely remove this line + void remove() { + if(line) + { + auto remove_fct = PyObject_GetAttrString(line,"remove"); + PyObject* args = PyTuple_New(0); + PyObject* res = PyObject_CallObject(remove_fct, args); + if (res) Py_DECREF(res); + } + decref(); + } + + ~Plot() { + decref(); + } +private: + + void decref() { + if(line) + Py_DECREF(line); + if(set_data_fct) + Py_DECREF(set_data_fct); + } + + + PyObject* line = nullptr; + PyObject* set_data_fct = nullptr; +}; + +} // end namespace matplotlibcpp diff --git a/src/volesti/include/sos/plot_saved.png b/src/volesti/include/sos/plot_saved.png new file mode 100644 index 0000000000000000000000000000000000000000..4bf495f659687be7af402820c5598b7192bace40 GIT binary patch literal 132753 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 KYuQ&UAN(I|K4R(s literal 0 HcmV?d00001 diff --git a/src/volesti/include/sos/utils.cpp b/src/volesti/include/sos/utils.cpp new file mode 100644 index 00000000..bb1265e7 --- /dev/null +++ b/src/volesti/include/sos/utils.cpp @@ -0,0 +1,6 @@ +#include "utils.h" + +std::string getEnvVar(std::string const &key) { + char *val = getenv(key.c_str()); + return val == NULL ? std::string("") : std::string(val); +} \ No newline at end of file diff --git a/src/volesti/include/sos/utils.h b/src/volesti/include/sos/utils.h new file mode 100644 index 00000000..09285ec9 --- /dev/null +++ b/src/volesti/include/sos/utils.h @@ -0,0 +1,331 @@ +// VolEsti (volume computation and sampling library) +// +// Copyright (c) 2020 Bento Natura +// +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SOS_UTILS_H +#define SOS_UTILS_H + +//Preprocessor directive allows us to forbid Eigen to allocate memory. Temporariliy helps to debug where allocation might slow down the program. +#define EIGEN_RUNTIME_NO_MALLOC + +//Note MKL Macro is set in CmakeLists file. + +//#define EIGEN_USE_MKL_ALL +//#define EIGEN_USE_BLAS +//#define EIGEN_USE_LAPACKE + +#include +#include + +#include + +#include +#include +#include +#include "spdlog/spdlog.h" +#include +#include +#include + +#include "ChebTools/ChebTools.h" + + +#ifndef DIGITS_PRECISION +#define DIGITS_PRECISION 50 +#endif + +typedef boost::multiprecision::cpp_dec_float mp_backend; +typedef boost::multiprecision::number BoostDouble; +//typedef boost::multiprecision::number > BoostDouble; +typedef BoostDouble InterpolantDouble; + +typedef long double long_double; + +#ifdef IPM_DOUBLE +typedef IPM_DOUBLE Double; +#else +typedef double Double; +#endif + +//Change typedef here to use different double type in interior point method. +#ifdef IPM_USE_DOUBLE +//typedef Double IPMDouble; +#else +typedef BoostDouble IPMDouble; +#endif + +typedef Eigen::Matrix BoostMatrix; +typedef Eigen::Matrix BoostVector; + +typedef Eigen::Matrix InterpolantMatrix; +typedef Eigen::Matrix InterpolantVector; + +typedef Eigen::Matrix DoubleMatrix; +typedef Eigen::Matrix DoubleVector; + +//Note: Boost Dependency +namespace pt = boost::property_tree; + +template +using Matrix = Eigen::Matrix; + +template +using Vector = Eigen::Matrix; + +//Stack the columns of a square m x m matrix to a vector of length m x m. +template +Vector StackMatrixToVector(Matrix M) { + assert(M.rows() == M.cols()); + Eigen::Map> x(M.data(), M.rows() * M.cols(), 1); + return x; +} + +//Unstack vector +template +Matrix UnstackVectorToMatrix(Vector v, unsigned matrix_dimension) { + assert(v.rows() == matrix_dimension * matrix_dimension); + Eigen::Map > M(v.data(), matrix_dimension, matrix_dimension); + return M; +} + +//template +//class Constraints; + +template +class Solution { +public: + template + Solution cast() { + Solution sol; + sol.x = x.template cast(); + sol.s = s.template cast(); + //Note: Boost Dependency. + sol.centrality = boost::numeric_cast(centrality); + sol.gap= boost::numeric_cast(gap); + return sol; + } + + Vector x; + Vector s; + T centrality; + T gap; +}; + +//TODO: Need full row rank matrices for IPM. Also, is preprocessing A, e.g. row-echelon form useful? +template +class Constraints { +public: + Matrix A; + Vector b; + Vector c; + + Constraints() {}; + + Constraints(Matrix A_, Vector b_, Vector c_) : A(A_), b(b_), c(c_) {}; + + void print() { + std::cout << "Constraints are as follows. Constraint matrix is A: " << std::endl; + std::cout << A << std::endl; + std::cout << "Objective c is " << std::endl; + std::cout << c.transpose() << std::endl; + std::cout << "RHS b is " << std::endl; + std::cout << b.transpose() << std::endl; + } + + //TODO: use optional argument to indicate sparseness. + Constraints dual_system() { + std::cout << "Create dual system... " << std::endl; + Constraints dual_constraints; +// dual_constraints.c = A.colPivHouseholderQr().solve(b); + + //TODO: use proper tolerance / reference. + + Eigen::SparseMatrix A_top_sparse = A.transpose().sparseView(0, 1e-10); + Eigen::SparseMatrix A_sparse = A.sparseView(0, 1e-10); + + A_top_sparse.makeCompressed(); + A_sparse.makeCompressed(); + + Eigen::SparseQR, Eigen::COLAMDOrdering > QR_top_sparse; + Eigen::SparseQR, Eigen::COLAMDOrdering > QR_sparse; + + QR_top_sparse.compute(A_top_sparse); + QR_sparse.compute(A_sparse); + + dual_constraints.c = QR_sparse.solve(b); + + Matrix QR_from_sparse(QR_top_sparse.matrixQ()); +// Matrix QR = A.transpose().householderQr().householderQ(); + + dual_constraints.A = QR_from_sparse.block(0, A.rows(), QR_from_sparse.rows(), + QR_from_sparse.cols() - A.rows()).transpose(); + dual_constraints.b = dual_constraints.A * c; + + std::cout << "Done." << std::endl; + + //TODO: use different measure to calculate centrality error + assert((dual_constraints.A * A.transpose()).norm() < 10e-5); + return dual_constraints; + } +}; + +//Naive implementation +class DegreeTuple { +public: + DegreeTuple(const int num_vars, const unsigned max_degree_) { + max_degree = max_degree_; + v.resize(num_vars, 0); + } + + bool next() { + for (int i = v.size() - 1; i >= 0; i--) { + if (v[i] < max_degree) { + v[i]++; + return true; + } + v[i] = 0; + } + return false; + } + + bool valid() { + int sum = 0; + for (auto el : v) { + sum += el; + } + return sum <= max_degree; + } + + bool next_valid() { + if (!next()) { + return false; + } + while (not valid()) { + if (not next()) { + return false; + } + } + return true; + } + + std::vector &get_tuple() { + return v; + } + + void print_tuple() { + for (auto el : v) { + std::cout << el << " "; + } + std::cout << std::endl; + } + +private: + unsigned max_degree; + std::vector v; +}; + +class AllCombinationTuple { +public: + AllCombinationTuple(std::vector const bounds_) { + + bounds = bounds_; + v.resize(bounds.size(), 0); + } + + bool next() { + for (int i = v.size() - 1; i >= 0; i--) { + if (bounds[i] > v[i]) { + v[i]++; + return true; + } + v[i] = 0; + } + return false; + } + + std::vector &get_combination() { + return v; + } + + std::vector bounds; + std::vector v; +}; + + +//Implementation copied from https://stackoverflow.com/questions/1577475/c-sorting-and-keeping-track-of-indexes + +template +std::vector sort_indexes(const std::vector &v) { + + // initialize original index locations + std::vector idx(v.size()); + std::iota(idx.begin(), idx.end(), 0); + + // sort indexes based on comparing values in v + // using std::stable_sort instead of std::sort + // to avoid unnecessary index re-orderings + // when v contains elements of equal values + stable_sort(idx.begin(), idx.end(), + [&v](size_t i1, size_t i2) { return v[i1] < v[i2]; }); + + return idx; +} + +//Access environment variables + +std::string getEnvVar(std::string const &key); + +template +class OrthogonaPMatrixLibrary { +private: + std::map, Matrix > matrices; + + void build(int L, int U) { + Eigen::MatrixXd cheb_P = ChebTools::u_matrix_library.get(U - 1).block(0, 0, U, L); + Matrix P_tmp = cheb_P.cast(); + Matrix P_ortho = P_tmp.householderQr().householderQ(); + P_ortho.colwise().hnormalized(); + matrices[std::pair(L, U)] = P_ortho.block(0, 0, U, L); + } + +public: + /// Get the \f$\mathbf{U}\f$ matrix of degree N + const Matrix &get(int L, int U) { + auto it = matrices.find(std::pair(L, U)); + if (it != matrices.end()) { + return it->second; + } else { + build(L, U); + return matrices.find(std::pair(L, U))->second; + } + } +}; + +template +static OrthogonaPMatrixLibrary orthogonal_P_Matrix_library; + + +template class CustomLLT : public Eigen::LLT<_MatrixType, _UpLo> { +public: + + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + + CustomLLT<_MatrixType, _UpLo>() : Eigen::LLT<_MatrixType, _UpLo>() {}; + + CustomLLT<_MatrixType, _UpLo>(unsigned int n) : Eigen::LLT<_MatrixType, _UpLo>(n) {}; + + void copy_and_scale(const CustomLLT<_MatrixType, _UpLo> & other, Scalar scalar){ + this->m_matrix = other.m_matrix * scalar; + this->m_l1_norm = other.m_l1_norm; + this->m_isInitialized = other.m_isInitialized; + this->m_info = other.m_info; + } +}; + +#endif //SOS_UTILS_H + + + diff --git a/src/volesti/include/volume/copulas.h b/src/volesti/include/volume/copulas.h new file mode 100644 index 00000000..11a89492 --- /dev/null +++ b/src/volesti/include/volume/copulas.h @@ -0,0 +1,244 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2018 Vissarion Fisikopoulos, Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// VolEsti is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// VolEsti is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// See the file COPYING.LESSER for the text of the GNU Lesser General +// Public License. If you did not receive this file along with HeaDDaCHe, +// see . + +#ifndef COPULAS_H +#define COPULAS_H + + +template +std::vector > twoParHypFam(const int dim, + const int num, + const int num_slices, + const std::vector &pl1, const std::vector &pl2, + double seed = std::numeric_limits::signaling_NaN()) +{ + + int i,j,col,row; + std::vector vec1,vec2,Zs1,Zs2; + NT sum1,sum2; + std::list points; + typename std::list::iterator rpit; + std::pair< std::vector,std::vector > result; + Point p; + + Sam_Canon_Unit (dim, num, points, seed); + + std::vector > Matrix(num_slices); + std::vector > pos_Matrix(num_slices); + for (i=0; i +std::vector > hypfam_ellfam(int dim, + int num, + int num_slices, + std::vector pl, + ellipsoid G, + double seed = std::numeric_limits::signaling_NaN()) +{ + + int i,j,col,row; + std::vector vec1,vec2,Zs1,Cs; + NT sum1,sum2; + std::list points; + typename std::list::iterator rpit; + std::pair< std::vector,std::vector > result; + Point p; + + Sam_Canon_Unit (dim, num, points, seed); + + std::vector > Matrix(num_slices); + std::vector > pos_Matrix(num_slices); + for (i=0; i +#include +#include + +// From rosetta code at http://rosettacode.org/wiki/Combinations#C.2B.2B +// We made some adjustments to vectorize the output +// Compute all the N combinations from N elements +inline std::vector< std::vector > comb(int N, int K) +{ + std::string bitmask(K, 1); // K leading 1's + bitmask.resize(N, 0); // N-K trailing 0's + std::vector iter_comb(K,0); + std::vector > combs; + int count; + + // print integers and permute bitmask + do { + count = 0; + for (int i = 0; i < N; ++i) // [0..N-1] integers + { + if (bitmask[i]){ + iter_comb[count] = i; + count++; + } + } + combs.push_back(iter_comb); + } while (std::prev_permutation(bitmask.begin(), bitmask.end())); + return combs; +} + + +template +NT exact_zonotope_vol(const Polytope &ZP){ + + typedef typename Polytope::MT MT; + typedef std::vector< std::vector >::iterator IntMatIt; + typedef std::vector::iterator IntIt; + + int n = ZP.dimension(), col, k = ZP.num_of_generators(); + MT V1 = ZP.get_mat().transpose(), SubV(n,n), V(n, 2*k); + V << V1, -V1; + NT vol = 0.0; + + std::vector< std::vector > combs = comb(2*k, n); + IntMatIt iter_combs; + IntIt it; + + iter_combs = combs.begin(); + for ( ; iter_combs!=combs.end(); ++iter_combs) { + it = (*iter_combs).begin(); + col = 0; + // construct all the nxn submatrices + for ( ; it!=(*iter_combs).end(); ++it, ++col) { + SubV.col(col) = V.col(*it); + } + vol += std::abs(SubV.determinant()); + } + return vol; +} + +template +NT vol_Ali(std::vector &plane, const NT &zit, const unsigned int dim) { + + unsigned int i, J = 0, K = 0, k; + std::vector Y(dim, 0.0), X(dim, 0.0), a(dim + 1, 0.0); + + for (i = 0; i < dim; i++) { + + if (plane[i] + zit < 0) { + X[J] = plane[i] + zit; + J++; + } else { + Y[K] = plane[i] + zit; + K++; + } + } + + a[0] = 1.0; + if (J>0) { + for (i = 0; i < J; i++) { + for (k = 0; k < K; k++) { + a[k+1] = (Y[k] * a[k+1] - X[i] * a[k]) / (Y[k] - X[i]); + } + } + } + return a[K]; +} + +#endif diff --git a/src/volesti/include/volume/math_helpers.hpp b/src/volesti/include/volume/math_helpers.hpp new file mode 100644 index 00000000..11e2742b --- /dev/null +++ b/src/volesti/include/volume/math_helpers.hpp @@ -0,0 +1,46 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis +// Copyright (c) 2021 Vaibhav Thakkar + +// Contributed and/or modified by Vaibhav Thakkar, as part of Google Summer of Code 2021 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef MATH_HELPERS_HPP +#define MATH_HELPERS_HPP + +#include +#include + + +//An implementation of Welford's algorithm for mean and variance. +template +std::pair get_mean_variance(std::vector& vec) +{ + NT mean = 0; + NT M2 = 0; + NT variance = 0; + NT delta; + + unsigned int i=0; + for (auto vecit = vec.begin(); vecit!=vec.end(); vecit++, i++) + { + delta = *vecit - mean; + mean += delta / (i + 1); + M2 += delta * (*vecit - mean); + variance = M2 / (i + 1); + } + return std::pair (mean, variance); +} + + +template +static NT log_gamma_function(NT x) +{ + if (x <= NT(100)) return std::log(tgamma(x)); + return (std::log(x - NT(1)) + log_gamma_function(x - NT(1))); +} + +#endif // MATH_HELPERS_HPP \ No newline at end of file diff --git a/src/volesti/include/volume/rotating.hpp b/src/volesti/include/volume/rotating.hpp new file mode 100644 index 00000000..64c2198b --- /dev/null +++ b/src/volesti/include/volume/rotating.hpp @@ -0,0 +1,64 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2019 Vissarion Fisikopoulos +// Copyright (c) 2019 Apostolos Chalkis + + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef ROTATING_H +#define ROTATING_H + +#include + +template +MT rotating(Polytope &P){ + + typedef boost::mt19937 RNGType; + unsigned rng_seed = std::chrono::system_clock::now().time_since_epoch().count(); + RNGType rng(rng_seed); + boost::random::uniform_real_distribution<> urdist(-1.0, 1.0); + unsigned int n = P.dimension(); + + // pick a random rotation + MT R(n,n); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + R(i,j) = urdist(rng); + } + } + + Eigen::JacobiSVD svd(R, Eigen::ComputeFullU | Eigen::ComputeFullV); + + // apply rotation to the polytope P + P.linear_transformIt(svd.matrixU()); + + return svd.matrixU().inverse(); +} + + +template +MT rotating(Polytope &P, unsigned seed){ + + typedef boost::mt19937 RNGType; + RNGType rng(seed); + boost::random::uniform_real_distribution<> urdist(-1.0, 1.0); + unsigned int n = P.dimension(); + + // pick a random rotation + MT R(n,n); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + R(i,j) = urdist(rng); + } + } + + Eigen::JacobiSVD svd(R, Eigen::ComputeFullU | Eigen::ComputeFullV); + + // apply rotation to the polytope P + P.linear_transformIt(svd.matrixU()); + + return svd.matrixU().inverse(); +} + +#endif diff --git a/src/volesti/include/volume/sampling_policies.hpp b/src/volesti/include/volume/sampling_policies.hpp new file mode 100644 index 00000000..6645fcb6 --- /dev/null +++ b/src/volesti/include/volume/sampling_policies.hpp @@ -0,0 +1,50 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef SAMPLING_POLICIES_HPP +#define SAMPLING_POLICIES_HPP + +struct PushBackWalkPolicy +{ + template + void apply(PointList &randPoints, + Point &p) const + { + randPoints.push_back(p); + } +}; + +template +struct CountingWalkPolicy +{ + CountingWalkPolicy(unsigned int const& nump_PBSmall, BallPoly const& PBSmall) + : _nump_PBSmall(nump_PBSmall) + , _PBSmall(PBSmall) + {} + + template + void apply(PointList &randPoints, + Point &p) + { + if (_PBSmall.second().is_in(p) == -1)//is in + { + randPoints.push_back(p); + ++_nump_PBSmall; + } + } + + unsigned int get_nump_PBSmall() const + { + return _nump_PBSmall; + } + +private : + unsigned int _nump_PBSmall; + BallPoly _PBSmall; +}; + +#endif // SAMPLING_POLICIES_HPP diff --git a/src/volesti/include/volume/volume_cooling_balls.hpp b/src/volesti/include/volume/volume_cooling_balls.hpp new file mode 100644 index 00000000..d90604fa --- /dev/null +++ b/src/volesti/include/volume/volume_cooling_balls.hpp @@ -0,0 +1,853 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Repouskos Panagiotis, as part of Google Summer of Code 2019 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLUME_COOLING_BALLS_HPP +#define VOLUME_COOLING_BALLS_HPP + +#include +#include + +#include "cartesian_geom/cartesian_kernel.h" +#include "convex_bodies/hpolytope.h" +#ifndef DISABLE_LPSOLVE + #include "convex_bodies/vpolytope.h" + #include "convex_bodies/zpolytope.h" + #include "convex_bodies/vpolyintersectvpoly.h" +#endif +#include "random_walks/uniform_cdhr_walk.hpp" +#include "convex_bodies/ball.h" +#include "convex_bodies/ballintersectconvex.h" +#include "sampling/random_point_generators.hpp" +#include "volume/math_helpers.hpp" + + +//////////////////////////////////// +// ball annealing + +template +struct cooling_ball_parameters +{ + cooling_ball_parameters(unsigned int win_len) + : lb(0.1) + , ub(0.15) + , p(0.75) + , rmax(0) + , alpha(0.2) + , win_len(win_len) + , N(125) + , nu(10) + , window2(false) + {} + + NT lb; + NT ub; + NT p; + NT rmax; + NT alpha; + int win_len; + int N; + int nu; + bool window2; +}; + +/// Helpers + +template +bool check_convergence(ConvexBody &P, + PointList const& randPoints, + bool& too_few, + NT& ratio, + int const& nu, + bool const& precheck, + bool const& lastball, + cooling_ball_parameters const& parameters) +{ + NT alpha = parameters.alpha; + std::vector ratios; + std::pair mv; + int m = randPoints.size()/nu; + int i = 1; + NT T; + NT rs; + NT alpha_check = 0.01; + size_t countsIn = 0; + + for (auto pit=randPoints.begin(); pit!=randPoints.end(); ++pit, i++) + { + if (P.is_in(*pit)==-1) countsIn++; + if (i % m == 0) + { + ratios.push_back(NT(countsIn)/m); + countsIn = 0; + if (ratios.size()>1 && precheck) + { + boost::math::students_t dist(ratios.size() - 1); + mv = get_mean_variance(ratios); + ratio = mv.first; + rs = std::sqrt(mv.second); + T = rs * (boost::math::quantile + (boost::math::complement(dist, alpha_check / 2.0)) + / std::sqrt(NT(ratios.size()))); + if (ratio + T < parameters.lb) + { + too_few = true; + return false; + } else if (ratio - T > parameters.ub) return false; + } + } + } + + if (precheck) alpha *= 0.5; + mv = get_mean_variance(ratios); + ratio = mv.first; + rs = std::sqrt(mv.second); + boost::math::students_t dist(nu - 1); + T = rs * (boost::math::quantile(boost::math::complement(dist, alpha)) + / std::sqrt(NT(nu))); + if (ratio > parameters.lb + T) + { + if (lastball) return true; + if ((precheck && ratio < parameters.ub - T) + || (!precheck && ratio < parameters.ub + T)) return true; + return false; + } + too_few = true; + return false; +} + + +template +bool get_first_ball(Polytope &P, + Ball& B0, + NT& ratio, + NT const& radius_input, + cooling_ball_parameters const& parameters, + RNG& rng) { + const unsigned max_iterarions = 20; + NT tolerance = 0.00000000001; + typedef typename Polytope::PointType Point; + int n = P.dimension(); + int iter = 1; + bool bisection_int = false; + bool pass = false; + bool too_few = false; + std::list randPoints; + NT rmax = parameters.rmax; + NT sqrt_n = std::sqrt(NT(n)); + NT radius1 = radius_input; + + if (rmax > 0.0) { + for (int i = 0; i < 1200; ++i) { + randPoints.push_back(GetPointInDsphere::apply(n, rmax, rng)); + } + pass = check_convergence(P, randPoints, too_few, ratio, + 10, true, false, parameters); + if (pass || !too_few) { + B0 = Ball(Point(n), rmax * rmax); + return true; + } + bisection_int = true; + } else { + rmax = 2 * sqrt_n * radius1; + } + NT radius = radius1; + + while (!bisection_int) { + randPoints.clear(); + too_few = false; + + for (int i = 0; i < 1200; ++i) { + randPoints.push_back(GetPointInDsphere::apply(n, rmax, rng)); + } + + if (check_convergence(P, randPoints, too_few, ratio, 10, + true, false, parameters)) { + B0 = Ball(Point(n), rmax * rmax); + return true; + } + + if (too_few) break; + radius1 = rmax; + rmax = rmax + 2 * sqrt_n * radius; + } + + NT rad_med; + NT rad0 = radius1; + NT rad_m = rmax; + + while (iter <= max_iterarions) { + rad_med = 0.5 * (radius1 + rmax); + randPoints.clear(); + too_few = false; + + for (int i = 0; i < 1200; ++i) { + randPoints.push_back(GetPointInDsphere::apply(n, rad_med, rng)); + } + + if (check_convergence(P, randPoints, too_few, ratio, 10, + true, false, parameters)) { + B0 = Ball(Point(n), rad_med * rad_med); + return true; + } + + if (too_few) { + rmax = rad_med; + } else { + radius1 = rad_med; + } + + if (rmax - radius1 < tolerance) { + radius1 = rad0; + rmax = rad_m; + iter++; + } + } + return false; +} + + +template +bool get_next_zonotopeball(std::vector& BallSet, + PointList const& randPoints, + NT const& rad_min, + std::vector& ratios, + cooling_ball_parameters const& parameters) +{ + const unsigned max_iterarions = 20; + NT tolerance = 0.00000000001; + int n = (*randPoints.begin()).dimension(); + int iter = 1; + bool too_few; + NT radmax = NT(0); + NT radmin = rad_min; + + for (auto rpit = randPoints.begin(); rpit!=randPoints.end(); ++rpit) + { + NT pnorm = (*rpit).squared_length(); + if (pnorm > radmax) radmax = pnorm; + } + ball Biter; + radmax = std::sqrt(radmax); + NT radmin_init = radmin; + NT radmax_init = radmax; + + while (iter <= max_iterarions) + { + NT rad = 0.5 * (radmin + radmax); + Biter = ball(Point(n), rad * rad); + too_few = false; + + NT ratio; + if (check_convergence(Biter, randPoints, too_few, ratio, + parameters.nu, false, false, parameters)) + { + BallSet.push_back(Biter); + ratios.push_back(ratio); + return true; + } + + if (too_few) + { + radmin = rad; + } else + { + radmax = rad; + } + + if (radmax-radmin < tolerance) + { + radmin = radmin_init; + radmax = radmax_init; + iter++; + } + } + return false; +} + + +template +< + typename RandomPointGenerator, + typename PolyBall, + typename ball, + typename Polytope, + typename NT, + typename RNG +> +bool get_sequence_of_polytopeballs(Polytope &P, + std::vector& BallSet, + std::vector& ratios, + int const& Ntot, + NT const& radius, + unsigned int const& walk_length, + cooling_ball_parameters const& parameters, + RNG& rng) +{ + typedef typename Polytope::PointType Point; + bool fail; + int n = P.dimension(); + NT ratio; + NT ratio0; + std::list randPoints; + ball B0; + Point q(n); + + if ( !get_first_ball(P, B0, ratio, radius, parameters, rng) ) + { + return false; + } + + ratio0 = ratio; + + PushBackWalkPolicy push_back_policy; + RandomPointGenerator::apply(P, q, Ntot, walk_length, + randPoints, push_back_policy, rng); + + if (check_convergence(B0, randPoints, + fail, ratio, parameters.nu, + false, true, parameters)) + { + ratios.push_back(ratio); + BallSet.push_back(B0); + ratios.push_back(ratio0); + return true; + } + if ( !get_next_zonotopeball(BallSet, randPoints, B0.radius(), ratios, + parameters) ) + { + return false; + } + + while (true) + { + PolyBall zb_it(P, BallSet[BallSet.size()-1]); + q.set_to_origin(); + randPoints.clear(); + + RandomPointGenerator::apply(zb_it, q, Ntot, walk_length, + randPoints, push_back_policy, rng); + if (check_convergence(B0, randPoints, fail, ratio, parameters.nu, + false, true, parameters)) + { + ratios.push_back(ratio); + BallSet.push_back(B0); + ratios.push_back(ratio0); + return true; + } + if ( !get_next_zonotopeball(BallSet, randPoints, B0.radius(), + ratios, parameters) ) + { + return false; + } + } +} + + +//////////////////////////////////// +/// +/// ratio estimation + +template +bool is_max_error(NT const& a, NT const& b, NT const& error) +{ + return ((b-a)/a +struct estimate_ratio_parameters +{ +public: + + estimate_ratio_parameters(unsigned int W_len, unsigned int N, NT ratio) + : min_val(std::numeric_limits::lowest()) + , max_val(std::numeric_limits::max()) + , max_iterations_estimation(10000000) + , min_index(W_len-1) + , max_index(W_len-1) + , W(W_len) + , index(0) + , tot_count(N) + , count_in(N * ratio) + , iter(0) + , last_W(std::vector(W_len)) + , minmaxIt(last_W.begin()) + {} + + NT min_val; + NT max_val; + const unsigned int max_iterations_estimation; + unsigned int min_index; + unsigned int max_index; + unsigned int W; + unsigned int index; + size_t tot_count; + size_t count_in; + unsigned int iter; + std::vector last_W; + typename std::vector::iterator minmaxIt; +}; + +template +bool estimate_ratio_generic(Pollyball &Pb2, Point const& p, NT const& error, + estimate_ratio_parameters &ratio_parameters) +{ + if (ratio_parameters.iter++ <= ratio_parameters.max_iterations_estimation) + { + if (Pb2.is_in(p) == -1) ratio_parameters.count_in = ratio_parameters.count_in + 1.0; + + ratio_parameters.tot_count = ratio_parameters.tot_count + 1.0; + NT val = NT(ratio_parameters.count_in) / NT(ratio_parameters.tot_count); + ratio_parameters.last_W[ratio_parameters.index] = val; + + if (val <= ratio_parameters.min_val) + { + ratio_parameters.min_val = val; + ratio_parameters.min_index = ratio_parameters.index; + } else if (ratio_parameters.min_index == ratio_parameters.index) + { + ratio_parameters.minmaxIt = std::min_element(ratio_parameters.last_W.begin(), ratio_parameters.last_W.end()); + ratio_parameters.min_val = (*ratio_parameters.minmaxIt); + ratio_parameters.min_index = std::distance(ratio_parameters.last_W.begin(), ratio_parameters.minmaxIt); + } + + if (val >= ratio_parameters.max_val) + { + ratio_parameters.max_val = val; + ratio_parameters.max_index = ratio_parameters.index; + } else if (ratio_parameters.max_index == ratio_parameters.index) + { + ratio_parameters.minmaxIt = std::max_element(ratio_parameters.last_W.begin(), ratio_parameters.last_W.end()); + ratio_parameters.max_val = (*ratio_parameters.minmaxIt); + ratio_parameters.max_index = std::distance(ratio_parameters.last_W.begin(), ratio_parameters.minmaxIt); + } + + if ( (ratio_parameters.max_val - ratio_parameters.min_val) / ratio_parameters.max_val <= error/2.0 ) + { + return true; + } + + ratio_parameters.index = ratio_parameters.index % ratio_parameters.W + 1; + if (ratio_parameters.index == ratio_parameters.W) ratio_parameters.index = 0; + + return false; + } + return true; +} + + +template +< + typename WalkType, + typename Point, + typename PolyBall1, + typename PolyBall2, + typename NT, + typename RNG +> +NT estimate_ratio(PolyBall1 &Pb1, + PolyBall2 &Pb2, + NT const& ratio, + NT const& error, + unsigned int const& W, + unsigned int const& Ntot, + unsigned int const& walk_length, + RNG& rng) +{ + estimate_ratio_parameters ratio_parameters(W, Ntot, ratio); + unsigned int n = Pb1.dimension(); + Point p(n); + WalkType walk(Pb1, p, rng); + + do + { + walk.template apply(Pb1, p, walk_length, rng); + } while(!estimate_ratio_generic(Pb2, p, error, ratio_parameters)); + + return NT(ratio_parameters.count_in) / NT(ratio_parameters.tot_count); +} + +template +< typename Point, + typename ball, + typename PolyBall, + typename NT, + typename RNG +> +NT estimate_ratio(ball const& B, + PolyBall &Pb2, + NT const& ratio, + NT const& error, + int const& W, + int const& Ntot, + RNG& rng) +{ + estimate_ratio_parameters ratio_parameters(W, Ntot, ratio); + unsigned int n = B.dimension(); + Point p(n); + NT radius = B.radius(); + + do + { + p = GetPointInDsphere::apply(n, radius, rng); + } while(!estimate_ratio_generic(Pb2, p, error, ratio_parameters)); + + return NT(ratio_parameters.count_in) / NT(ratio_parameters.tot_count); +} + +//-------------------------------------------------------------------------- + +template +struct estimate_ratio_interval_parameters +{ +public: + estimate_ratio_interval_parameters(unsigned int W_len, + unsigned int N, + NT ratio) + : mean(0) + , sum_sq(0) + , sum(0) + , s(0) + , max_iterations_estimation(10000000) + , W(W_len) + , index(0) + , tot_count(N) + , count_in(N * ratio) + , iter(0) + , last_W(std::vector(W_len)) + {} + + NT mean; + NT sum_sq; + NT sum; + NT s; + const unsigned int max_iterations_estimation; + unsigned int W; + unsigned int index; + size_t tot_count; + size_t count_in; + unsigned int iter; + std::vector last_W; +}; + +template +void full_sliding_window(Pollyball &Pb2, + Point const& p, + estimate_ratio_interval_parameters& ratio_parameters) +{ + if (Pb2.is_in(p) == -1) ratio_parameters.count_in = ratio_parameters.count_in + 1.0; + + ratio_parameters.tot_count = ratio_parameters.tot_count + 1.0; + NT val = NT(ratio_parameters.count_in) / NT(ratio_parameters.tot_count); + ratio_parameters.sum += val; + ratio_parameters.sum_sq += val * val; + ratio_parameters.last_W[ratio_parameters.index] = val; + ratio_parameters.index = ratio_parameters.index % ratio_parameters.W + 1; + if (ratio_parameters.index == ratio_parameters.W) ratio_parameters.index = 0; +} + +template +bool estimate_ratio_interval_generic(Pollyball &Pb2, + Point const& p, + NT const& error, + NT const& zp, + estimate_ratio_interval_parameters + & ratio_parameters) +{ + if (ratio_parameters.iter++ <= ratio_parameters.max_iterations_estimation) + { + if (Pb2.is_in(p) == -1) ratio_parameters.count_in = ratio_parameters.count_in + 1.0; + + ratio_parameters.tot_count = ratio_parameters.tot_count + 1.0; + NT val = NT(ratio_parameters.count_in) / NT(ratio_parameters.tot_count); + + ratio_parameters.mean = (ratio_parameters.mean + - ratio_parameters.last_W[ratio_parameters.index] / + NT(ratio_parameters.W)) + val / NT(ratio_parameters.W); + + ratio_parameters.sum_sq = (ratio_parameters.sum_sq - + ratio_parameters.last_W[ratio_parameters.index] + * ratio_parameters.last_W[ratio_parameters.index]) + + val * val; + + ratio_parameters.sum = (ratio_parameters.sum + - ratio_parameters.last_W[ratio_parameters.index]) + + val; + + ratio_parameters.s = std::sqrt((ratio_parameters.sum_sq + NT(ratio_parameters.W) * + ratio_parameters.mean * ratio_parameters.mean - NT(2) + * ratio_parameters.mean + * ratio_parameters.sum) / + NT(ratio_parameters.W)); + + ratio_parameters.last_W[ratio_parameters.index] = val; + + ratio_parameters.index = ratio_parameters.index % ratio_parameters.W + 1; + if (ratio_parameters.index == ratio_parameters.W) + { + ratio_parameters.index = 0; + } + + if (is_max_error(val - zp * ratio_parameters.s, + val + zp * ratio_parameters.s, + error)) + { + return true; + } + return false; + } + return true; +} + +template +< + typename Point, + typename ball, + typename PolyBall2, + typename NT, + typename RNG +> +NT estimate_ratio_interval(ball const& B, + PolyBall2 &Pb2, + NT const& ratio, + NT const& error, + int const& W, + int const& Ntot, + NT const& prob, + RNG& rng) +{ + estimate_ratio_interval_parameters ratio_parameters(W, Ntot, ratio); + boost::math::normal dist(0.0, 1.0); + NT zp = boost::math::quantile(boost::math::complement(dist, (1.0 - prob)/2.0)); + NT radius = B.radius(); + + unsigned int n = Pb2.dimension(); + Point p(n); + + for (int i = 0; i < ratio_parameters.W; ++i) + { + p = GetPointInDsphere::apply(n, radius, rng); + full_sliding_window(Pb2, p, ratio_parameters); + } + + ratio_parameters.mean = ratio_parameters.sum / NT(ratio_parameters.W); + + do { + p = GetPointInDsphere::apply(n, radius, rng); + } while (!estimate_ratio_interval_generic(Pb2, p, error, zp, ratio_parameters)); + + return NT(ratio_parameters.count_in) / NT(ratio_parameters.tot_count); +} + + +template +< + typename WalkType, + typename Point, + typename PolyBall1, + typename PolyBall2, + typename NT, + typename RNG +> +NT estimate_ratio_interval(PolyBall1 &Pb1, + PolyBall2 &Pb2, + NT const& ratio, + NT const& error, + int const& W, + int const& Ntot, + NT const& prob, + unsigned int const& walk_length, + RNG& rng) +{ + estimate_ratio_interval_parameters ratio_parameters(W, Ntot, ratio); + boost::math::normal dist(0.0, 1.0); + NT zp = boost::math::quantile(boost::math::complement(dist, (1.0 - prob)/2.0)); + + unsigned int n = Pb1.dimension(); + Point p(n); + WalkType walk(Pb1, p, rng); + + for (int i = 0; i < ratio_parameters.W; ++i) + { + walk.template apply(Pb1, p, walk_length, rng); + full_sliding_window(Pb2, p, ratio_parameters); + } + + ratio_parameters.mean = ratio_parameters.sum / NT(ratio_parameters.W); + + do { + walk.template apply(Pb1, p, walk_length, rng); + } while (!estimate_ratio_interval_generic(Pb2, p, error, zp, ratio_parameters)); + + return NT(ratio_parameters.count_in) / NT(ratio_parameters.tot_count); +} + +template +< + typename WalkTypePolicy, + typename Polytope, + typename RandomNumberGenerator = BoostRandomNumberGenerator +> +std::pair volume_cooling_balls(Polytope& Pin, + RandomNumberGenerator &rng, + double const& error = 0.1, + unsigned int const& walk_length = 1, + unsigned int const& win_len = 300) +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef Ball BallType; + typedef BallIntersectPolytope PolyBall; + typedef typename Polytope::VT VT; + typedef std::list PointList; + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > WalkType; + typedef RandomPointGenerator RandomPointGenerator; + + auto P(Pin); + cooling_ball_parameters parameters(win_len); + + int n = P.dimension(); + NT prob = parameters.p; + int N_times_nu = parameters.N * parameters.nu; + + auto InnerBall = P.ComputeInnerBall(); + if (InnerBall.second < 0.0) return std::pair (-1.0, 0.0); + + NT radius = InnerBall.second; + Point c = InnerBall.first; + + std::vector BallSet; + std::vector ratios; + + // move the chebychev center to the origin + // and apply the same shifting to the polytope + P.shift(c.getCoefficients()); + + if ( !get_sequence_of_polytopeballs + < + RandomPointGenerator, + PolyBall + >(P, BallSet, ratios, + N_times_nu, radius, walk_length, + parameters, rng) ) + { + return std::pair (-1.0, 0.0); + } + + NT vol = (NT(n)/NT(2) * std::log(M_PI)) + NT(n)*std::log((*(BallSet.end() - 1)).radius()) - log_gamma_function(NT(n) / NT(2) + 1); + + int mm = BallSet.size() + 1; + prob = std::pow(prob, 1.0 / NT(mm)); + NT er0 = error / (2.0 * std::sqrt(NT(mm))); + NT er1 = (error * std::sqrt(4.0 * NT(mm) - 1)) / (2.0 * std::sqrt(NT(mm))); + + vol += (parameters.window2) ? + std::log(estimate_ratio(*(BallSet.end() - 1), + P, *(ratios.end() - 1), + er0, parameters.win_len, 1200, rng)) + : std::log(estimate_ratio_interval(*(BallSet.end() - 1), + P, *(ratios.end() - 1), + er0, parameters.win_len, 1200, + prob, rng)); + auto balliter = BallSet.begin(); + auto ratioiter = ratios.begin(); + + er1 = er1 / std::sqrt(NT(mm) - 1.0); + + if (*ratioiter != 1) + { + vol += (!parameters.window2) ? + std::log(NT(1) / estimate_ratio_interval + (P, + *balliter, + *ratioiter, + er1, + parameters.win_len, + N_times_nu, + prob, + walk_length, + rng)) + : std::log(NT(1) / estimate_ratio + (P, + *balliter, + *ratioiter, + er1, + parameters.win_len, + N_times_nu, + walk_length, + rng)); + } + + for ( ; balliter < BallSet.end() - 1; ++balliter, ++ratioiter) + { + PolyBall Pb(P, *balliter); + vol += (!parameters.window2) ? + std::log(NT(1) / estimate_ratio_interval + (Pb, + *(balliter + 1), + *(ratioiter + 1), + er1, parameters.win_len, + N_times_nu, + prob, walk_length, + rng)) + : std::log(NT(1) / estimate_ratio + (Pb, + *balliter, + *ratioiter, + er1, + parameters.win_len, + N_times_nu, + walk_length, + rng)); + } + + return std::pair (vol, std::exp(vol)); +} + + + +template +< + typename WalkTypePolicy = CDHRWalk, + typename RandomNumberGenerator = BoostRandomNumberGenerator, + typename Polytope +> +std::pair volume_cooling_balls(Polytope &Pin, + double const& error = 0.1, + unsigned int const& walk_length = 1) +{ + RandomNumberGenerator rng(Pin.dimension()); + return volume_cooling_balls(Pin, rng, error, walk_length); +} + + +template +< + typename WalkTypePolicy = CDHRWalk, + typename RandomNumberGenerator = BoostRandomNumberGenerator, + typename Polytope +> +std::pair volume_cooling_balls(Polytope &Pin, + Cartesian::Point const& interior_point, + unsigned int const& walk_length = 1, + double const& error = 0.1) +{ + RandomNumberGenerator rng(Pin.dimension()); + Pin.set_interior_point(interior_point); + return volume_cooling_balls(Pin, rng, error, walk_length); +} + +#endif // VOLUME_COOLING_BALLS_HPP diff --git a/src/volesti/include/volume/volume_cooling_gaussians.hpp b/src/volesti/include/volume/volume_cooling_gaussians.hpp new file mode 100644 index 00000000..ca06ce31 --- /dev/null +++ b/src/volesti/include/volume/volume_cooling_gaussians.hpp @@ -0,0 +1,490 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLUME_COOLING_GAUSSIANS_HPP +#define VOLUME_COOLING_GAUSSIANS_HPP + +//#define VOLESTI_DEBUG + +#include +#include +#include +#include +#include + +#include "cartesian_geom/cartesian_kernel.h" +#include "random_walks/gaussian_helpers.hpp" +#include "random_walks/gaussian_ball_walk.hpp" +#include "random_walks/gaussian_cdhr_walk.hpp" +#include "sampling/random_point_generators.hpp" +#include "volume/math_helpers.hpp" + + +/////////////////// Helpers for random walks + +template +struct update_delta +{ + template + static void apply(WalkType, NT) {} +}; + +template +struct update_delta> +{ + template + static void apply(GaussianBallWalk::Walk walk, + NT delta) + { + walk.update_delta(delta); + } +}; + + +////////////////////////////// Algorithms + +// Gaussian Anealling + +// Implementation is based on algorithm from paper "A practical volume algorithm", +// Springer-Verlag Berlin Heidelberg and The Mathematical Programming Society 2015 +// Ben Cousins, Santosh Vempala + +// Compute the first variance a_0 for the starting gaussian +template +void get_first_gaussian(Polytope& P, + NT const& frac, + NT const& chebychev_radius, + NT const& error, + std::vector & a_vals) +{ + // if tol is smaller than 1e-6 no convergence can be obtained when float is used + NT tol = std::is_same::value ? 0.001 : 0.0000001; + + std::vector dists = P.get_dists(chebychev_radius); + NT lower = 0.0; + NT upper = 1.0; + + // Compute an upper bound for a_0 + unsigned int i; + const unsigned int maxiter = 10000; + for (i= 1; i <= maxiter; ++i) { + NT sum = 0.0; + for (auto it = dists.begin(); it != dists.end(); ++it) + { + sum += std::exp(-upper * std::pow(*it, 2.0)) + / (2.0 * (*it) * std::sqrt(M_PI * upper)); + } + + if (sum > frac * error) + { + upper = upper * 10; + } else { + break; + } + } + + if (i == maxiter) { +#ifdef VOLESTI_DEBUG + std::cout << "Cannot obtain sharp enough starting Gaussian" << std::endl; +#endif + return; + } + + //get a_0 with binary search + while (upper - lower > tol) + { + NT mid = (upper + lower) / 2.0; + NT sum = 0.0; + for (auto it = dists.begin(); it != dists.end(); ++it) { + sum += std::exp(-mid * std::pow(*it, 2.0)) + / (2.0 * (*it) * std::sqrt(M_PI * mid)); + } + + if (sum < frac * error) { + upper = mid; + } else { + lower = mid; + } + } + a_vals.push_back((upper + lower) / NT(2.0)); +} + + +// Compute a_{i+1} when a_i is given +template +< + typename RandomPointGenerator, + typename Polytope, + typename Point, + typename NT, + typename RandomNumberGenerator +> +NT get_next_gaussian(Polytope& P, + Point &p, + NT const& a, + const unsigned int &N, + const NT &ratio, + const NT &C, + const unsigned int& walk_length, + RandomNumberGenerator& rng) +{ + + NT last_a = a; + NT last_ratio = 0.1; + //k is needed for the computation of the next variance a_{i+1} = a_i * (1-1/d)^k + NT k = 1.0; + const NT tol = 0.00001; + bool done=false; + std::vector fn(N,NT(0.0)); + std::list randPoints; + typedef typename std::vector::iterator viterator; + + //sample N points + PushBackWalkPolicy push_back_policy; + RandomPointGenerator::apply(P, p, last_a, N, walk_length, randPoints, + push_back_policy, rng); + + while (!done) + { + NT new_a = last_a * std::pow(ratio,k); + + auto fnit = fn.begin(); + for (auto pit=randPoints.begin(); pit!=randPoints.end(); ++pit, fnit++) + { + *fnit = eval_exp(*pit, new_a)/eval_exp(*pit, last_a); + } + std::pair mv = get_mean_variance(fn); + + // Compute a_{i+1} + if (mv.second/(mv.first * mv.first)>=C || mv.first/last_ratio<1.0+tol) + { + if (k != 1.0) + { + k = k / 2; + } + done = true; + } else { + k = 2 * k; + } + last_ratio = mv.first; + } + return last_a * std::pow(ratio, k); +} + +// Compute the sequence of spherical gaussians +template +< + typename WalkType, + typename RandomPointGenerator, + typename Polytope, + typename NT, + typename RandomNumberGenerator +> +void compute_annealing_schedule(Polytope& P, + NT const& ratio, + NT const& C, + NT const& frac, + unsigned int const& N, + unsigned int const& walk_length, + NT const& chebychev_radius, + NT const& error, + std::vector& a_vals, + RandomNumberGenerator& rng) +{ + typedef typename Polytope::PointType Point; + typedef typename Polytope::VT VT; + + // Compute the first gaussian + get_first_gaussian(P, frac, chebychev_radius, error, a_vals); + +#ifdef VOLESTI_DEBUG + std::cout<<"first gaussian computed\n"< + (P, p, a_vals[it], N, ratio, C, walk_length, rng); + + NT curr_fn = 0; + NT curr_its = 0; + auto steps = totalSteps; + + WalkType walk(P, p, a_vals[it], rng); + //TODO: test update delta here? + + update_delta + ::apply(walk, 4.0 * chebychev_radius + / std::sqrt(std::max(NT(1.0), a_vals[it]) * NT(n))); + + // Compute some ratios to decide if this is the last gaussian + for (unsigned int j = 0; j < steps; j++) + { + walk.template apply(P, p, a_vals[it], walk_length, rng); + curr_its += 1.0; + curr_fn += eval_exp(p, next_a) / eval_exp(p, a_vals[it]); + } + + // Remove the last gaussian. + // Set the last a_i equal to zero + if (next_a>0 && curr_fn/curr_its>(1.0+tol)) + { + a_vals.push_back(next_a); + it++; + } else if (next_a <= 0) + { + a_vals.push_back(a_stop); + it++; + break; + } else { + a_vals[it] = a_stop; + break; + } + } +} + +template +struct gaussian_annealing_parameters +{ + gaussian_annealing_parameters(unsigned int d) + : frac(0.1) + , ratio(NT(1)-NT(1)/(NT(d))) + , C(NT(2)) + , N(500 * ((int) C) + ((int) (d * d / 2))) + , W(6*d*d+800) + {} + + NT frac; + NT ratio; + NT C; + unsigned int N; + unsigned int W; +}; + +template +< + typename WalkTypePolicy, + typename Polytope, + typename RandomNumberGenerator + +> +double volume_cooling_gaussians(Polytope& Pin, + RandomNumberGenerator& rng, + double const& error = 0.1, + unsigned int const& walk_length = 1) +{ + typedef typename Polytope::PointType Point; + typedef typename Point::FT NT; + typedef typename Polytope::VT VT; + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > WalkType; + typedef GaussianRandomPointGenerator RandomPointGenerator; + + //const NT maxNT = std::numeric_limits::max();//1.79769e+308; + //const NT minNT = std::numeric_limits::min();//-1.79769e+308; + + auto P(Pin); //copy and work with P because we are going to shift + unsigned int n = P.dimension(); + unsigned int m = P.num_of_hyperplanes(); + gaussian_annealing_parameters parameters(P.dimension()); + //RandomNumberGenerator rng(n); + + // Consider Chebychev center as an internal point + auto InnerBall = P.ComputeInnerBall(); + if (InnerBall.second < 0.0) return -1.0; + + Point c = InnerBall.first; + NT radius = InnerBall.second; + + // Move the chebychev center to the origin and apply the same shifting to the polytope + P.shift(c.getCoefficients()); + + // Computing the sequence of gaussians +#ifdef VOLESTI_DEBUG + std::cout<<"\n\nComputing annealing...\n"< a_vals; + NT ratio = parameters.ratio; + NT C = parameters.C; + unsigned int N = parameters.N; + + compute_annealing_schedule + < + WalkType, + RandomPointGenerator + >(P, ratio, C, parameters.frac, N, walk_length, radius, error, a_vals, rng); + +#ifdef VOLESTI_DEBUG + std::cout<<"All the variances of schedule_annealing computed in = " + << (double)clock()/(double)CLOCKS_PER_SEC-tstart2<<" sec"< last_W2(W,0); + std::vector fn(mm,0); + std::vector its(mm,0); + VT lamdas; + lamdas.setZero(m); + NT vol = std::pow(M_PI/a_vals[0], (NT(n))/2.0); + Point p(n); // The origin is the Chebychev center of the Polytope + unsigned int i=0; + + typedef typename std::vector::iterator viterator; + viterator itsIt = its.begin(); + viterator avalsIt = a_vals.begin(); + viterator minmaxIt; + +#ifdef VOLESTI_DEBUG + std::cout<<"volume of the first gaussian = "<::min(); + NT max_val = std::numeric_limits::max(); + unsigned int min_index = W-1; + unsigned int max_index = W-1; + unsigned int index = 0; + unsigned int min_steps = 0; + std::vector last_W = last_W2; + + // Set the radius for the ball walk + WalkType walk(P, p, *avalsIt, rng); + + update_delta + ::apply(walk, 4.0 * radius + / std::sqrt(std::max(NT(1.0), *avalsIt) * NT(n))); + + while (!done || (*itsIt)= max_val) + { + max_val = val; + max_index = index; + } else if (max_index == index) + { + minmaxIt = std::max_element(last_W.begin(), last_W.end()); + max_val = *minmaxIt; + max_index = std::distance(last_W.begin(), minmaxIt); + } + + if ( (max_val-min_val)/max_val <= curr_eps/2.0 ) + { + done=true; + } + + index = index%W + 1; + if (index == W) index = 0; + } +#ifdef VOLESTI_DEBUG + std::cout << "ratio " << i << " = " << (*fnIt) / (*itsIt) + << " N_" << i << " = " << *itsIt << std::endl; +#endif + vol *= ((*fnIt) / (*itsIt)); + } + +#ifdef VOLESTI_DEBUG + NT sum_of_steps = 0.0; + for(viterator it = its.begin(); it != its.end(); ++it) { + sum_of_steps += *it; + } + auto steps= int(sum_of_steps); + std::cout<<"\nTotal number of steps = "<, + typename Polytope +> +double volume_cooling_gaussians(Polytope &Pin, + double const& error = 0.1, + unsigned int const& walk_length = 1) +{ + RandomNumberGenerator rng(Pin.dimension()); + return volume_cooling_gaussians(Pin, rng, error, walk_length); +} + + +template +< + typename WalkTypePolicy = GaussianCDHRWalk, + typename RandomNumberGenerator = BoostRandomNumberGenerator, + typename Polytope +> +double volume_cooling_gaussians(Polytope &Pin, + Cartesian::Point const& interior_point, + unsigned int const& walk_length = 1, + double const& error = 0.1) +{ + RandomNumberGenerator rng(Pin.dimension()); + Pin.set_interior_point(interior_point); + + return volume_cooling_gaussians(Pin, rng, error, walk_length); +} + +#endif // VOLUME_COOLING_GAUSSIANS_HPP diff --git a/src/volesti/include/volume/volume_cooling_hpoly.hpp b/src/volesti/include/volume/volume_cooling_hpoly.hpp new file mode 100644 index 00000000..c8183384 --- /dev/null +++ b/src/volesti/include/volume/volume_cooling_hpoly.hpp @@ -0,0 +1,385 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLUME_COOLING_HPOLY_HPP +#define VOLUME_COOLING_HPOLY_HPP + +#include "volume/volume_cooling_gaussians.hpp" +#include "sampling/random_point_generators.hpp" +#include "preprocess/min_sampling_covering_ellipsoid_rounding.hpp" + + +template +< + typename RandomPointGenerator, + typename Zonotope, + typename HPolytope, + typename NT, + typename RNG, + typename VT +> +bool get_first_poly(Zonotope &P, + HPolytope &HP, + NT &ratio, + cooling_ball_parameters const& parameters, + RNG& rng, VT &b_max) +{ + + typedef typename Zonotope::PointType Point; + typedef typename Zonotope::MT MT; + + PushBackWalkPolicy push_back_policy; + + const unsigned max_iterarions = 20; + const NT tolerance = 0.00000000001; + + MT G = P.get_mat().transpose(), A = HP.get_mat(); + b_max = (A*G).cwiseAbs().rowwise().sum(); + VT b_min = HP.get_vec(); + HPolytope HPiter(HP); + + int n = P.dimension(), m = b_max.size(), N = 1200, iter = 1, count = 0; + Point q(n); + bool too_few, print = false; + std::list randPoints; + + NT l=0.0, u=1.0, med; + VT b_med(m); + + while(iter <= max_iterarions) { + + q=Point(n); + med = (u + l) * 0.5; + b_med = b_min + (b_max-b_min)*med; + HPiter.set_vec(b_med); + + randPoints.clear(); + RandomPointGenerator::apply(HPiter, q, 1200, 10+10*n, + randPoints, push_back_policy, rng); + too_few = false; + + if(check_convergence(P, randPoints, + too_few, ratio, parameters.nu, + true, false, parameters)){ + HP.set_vec(b_med); + return true; + } + + if (too_few) { + u = med; + } else { + l = med; + } + if(med>0.9) { + HP.set_vec(b_med); + return true; + } + if(u-l < tolerance) { + u=1.0; + l=0.0; + iter++; + } + } + return false; +} + + +template +bool get_next_zonoball(std::vector &HPolySet, + HPolytope &HP2, + const VT &b_max, + const VT &b_min, + PointList &randPoints, + std::vector &ratios, + cooling_ball_parameters const& parameters) +{ + + typedef typename Zonotope::PointType Point; + + const unsigned max_iterarions = 20; + const NT tolerance = 0.00000000001; + + int n = HP2.dimension(), iter = 1; + bool too_few; + VT b_med(b_max.size()); + NT ratio, med, u = 1.0, l = 0.0; + + while (iter <= max_iterarions) { + med = (u + l) * 0.5; + b_med = b_min + (b_max-b_min) * med; + HP2.set_vec(b_med); + too_few = false; + + if(check_convergence(HP2, randPoints, + too_few, ratio, parameters.nu, + false, false, parameters)){ + HPolySet.push_back(HP2); + ratios.push_back(ratio); + return true; + } + if(too_few) { + l = med; + } else { + u = med; + } + if(u-l < tolerance) { + u=1.0; + l=0.0; + iter++; + } + } + return false; +} + +template +< + typename RandomPointGenerator, + typename ZonoHP, + typename Zonotope, + typename HPolytope, + typename VT, + typename NT, + typename RNG +> +bool get_sequence_of_zonopolys(Zonotope &Z, + const HPolytope &HP, + std::vector &HPolySet, + std::vector &ratios, + const VT &b_max, + unsigned int const& N_times_nu, + unsigned int const& walk_length, + cooling_ball_parameters const& parameters, + RNG& rng) +{ + + bool too_few=false; + typedef typename Zonotope::PointType Point; + typedef typename Zonotope::MT MT; + + PushBackWalkPolicy push_back_policy; + + int n = Z.dimension(); + MT G = Z.get_mat().transpose(); + MT AG = HP.get_mat()*G; + NT ratio; + std::list randPoints; + Point q(n); + + RandomPointGenerator::apply(Z, q, N_times_nu, walk_length, + randPoints, push_back_policy, rng); + HPolytope HP2(HP); + if (check_convergence(HP, randPoints, + too_few, ratio, parameters.nu, + false, true, parameters)) { + ratios.push_back(ratio); + return true; + } + if ( !get_next_zonoball(HPolySet, HP2, b_max, HP.get_vec(), + randPoints, ratios, parameters)) + { + return false; + } + + VT Zs_min = HP.get_vec(); + + while (true) { + + ZonoHP ZHP2(Z,HP2); + q=Point(n); + randPoints.clear(); + RandomPointGenerator::apply(ZHP2, q, N_times_nu, walk_length, + randPoints, push_back_policy, rng); + + if (check_convergence(HP, randPoints, + too_few, ratio, parameters.nu, + false, true, parameters)) + { + ratios.push_back(ratio); + return true; + } + if ( !get_next_zonoball(HPolySet, HP2, HP2.get_vec(), + Zs_min, randPoints, ratios, parameters) ) + { + return false; + } + } +} + +// TODO: Rewrite to avoid some matrix operations and improve the signature +template + < + typename Zonotope, + typename HPolytope + > +HPolytope compute_hpoly_for_mmc(Zonotope const& P) { + typedef typename Zonotope::PointType Point; + typedef typename Zonotope::NT NT; + typedef typename Zonotope::VT VT; + typedef typename Zonotope::MT MT; + + MT V = P.get_mat(); + MT G = V.transpose(); + int m = G.cols(); + std::list randPoints; + + MT XX(m, 2*m); + XX << MT::Identity(m,m), -MT::Identity(m,m); + MT AA = XX.transpose(); VT b = VT::Ones(2*m); + MT T = P.get_T(); + MT Tt = T.transpose(); + MT A2 = AA * Tt, B = G * Tt; + MT A3 = A2 * B.inverse(); + + NT row_norm; + for (int i = 0; i < A3.rows(); ++i) { + row_norm = A3.row(i).norm(); + A3.row(i) = A3.row(i) / row_norm; + b(i) = b(i) / row_norm; + } + + return HPolytope(P.dimension(), A3, b); +} + + +template +< + typename WalkTypePolicy, + typename HPolytope, + typename Zonotope, + typename RandomNumberGenerator +> +double volume_cooling_hpoly (Zonotope const& Pin, + RandomNumberGenerator &rng, + double const& error = 1.0, + unsigned int const& walk_length = 1, + unsigned int const& win_len = 200) +{ + + typedef typename Zonotope::PointType Point; + typedef typename Point::FT NT; + typedef ZonoIntersectHPoly ZonoHP; + typedef typename Zonotope::VT VT; + typedef typename Zonotope::MT MT; + typedef std::list PointList; + + typedef typename WalkTypePolicy::template Walk WalkType; + typedef RandomPointGenerator ZonoRandomPointGenerator; + + typedef typename CDHRWalk::template Walk CdhrWalk; + typedef RandomPointGenerator CdhrRandomPointGenerator; + + auto P(Pin); + cooling_ball_parameters parameters(win_len); + + int n = P.dimension(); + NT prob = parameters.p, ratio; + int N_times_nu = parameters.N * parameters.nu; + + HPolytope HP(compute_hpoly_for_mmc(P)); + VT b_max(2 * P.num_of_generators()); + if ( !get_first_poly(P, HP, ratio, parameters, rng, b_max) ) + { + return -1.0; + } + + std::vector HPolySet; + std::vector ratios; + std::vector diams_inter; + + if ( !get_sequence_of_zonopolys + (P, HP, HPolySet, ratios, + b_max, N_times_nu, walk_length, parameters, rng) ) + { + return -1.0; + } + + int mm = HPolySet.size() + 2; + int mm2 = mm + 1; + prob = std::pow(prob, 1.0/NT(mm2)); + NT er0 = error/(2.0*std::sqrt(NT(mm2))); + NT er1 = (error*std::sqrt(2.0*NT(mm2)-1))/(std::sqrt(2.0*NT(mm2))); + NT Her = error/(2.0*std::sqrt(NT(mm2))); + + HPolytope HP2(HP); + std::pair InnerBall = HP2.ComputeInnerBall(); + std::tuple res = min_sampling_covering_ellipsoid_rounding(HP2, InnerBall, + 10 + 10 * n, rng); + NT vol = std::get<2>(res) * volume_cooling_gaussians(HP2, rng, Her/2.0, 1); + + if (!parameters.window2) { + vol *= estimate_ratio_interval(HP, P, ratio, er0, parameters.win_len, 1200, + prob, 10 + 10 * n, rng); + } else { + vol *= estimate_ratio(HP, P, ratio, er0, parameters.win_len, 1200, 10 + 10 * n, rng); + } + + HPolytope b1, b2; + if (HPolySet.size()==0) { + if (ratios[0]!=1) { + if(!parameters.window2) { + vol = vol / estimate_ratio_interval(P, HP, ratios[0], er1, parameters.win_len, N_times_nu, + prob, walk_length, rng); + } else { + vol = vol / estimate_ratio(P, HP, ratios[0], er1, parameters.win_len, N_times_nu, + walk_length, rng); + } + } + } else { + er1 = er1 / std::sqrt(NT(mm)-1.0); + b1 = HPolySet[0]; + if(!parameters.window2) { + vol = vol / estimate_ratio_interval(P, b1, ratios[0], er1, parameters.win_len, N_times_nu, + prob, walk_length, rng); + } else { + vol = vol / estimate_ratio(P, b1, ratios[0], er1, parameters.win_len, N_times_nu, + walk_length, rng); + } + + for (int i = 0; i < HPolySet.size()-1; ++i) { + ZonoHP zb1(P,HPolySet[i]); + b2 = HPolySet[i+1]; + if(!parameters.window2) { + vol = vol / estimate_ratio_interval(zb1, b2, ratios[i], er1, parameters.win_len, + N_times_nu, prob, walk_length, rng); + } else { + vol = vol / estimate_ratio(zb1, b2, ratios[i], er1, parameters.win_len, N_times_nu, + walk_length, rng); + } + } + + ZonoHP zb1(P, HPolySet[HPolySet.size() - 1]); + if (!parameters.window2) { + vol = vol / estimate_ratio_interval(zb1, HP, ratios[ratios.size() - 1], er1, + parameters.win_len, N_times_nu, prob, walk_length, + rng); + } else { + vol = vol / estimate_ratio(zb1, HP, ratios[ratios.size() - 1], er1, parameters.win_len, + N_times_nu, walk_length, rng); + } + } + + return vol; + +} + + +template +< + typename WalkTypePolicy, + typename RandomNumberGenerator, + typename HPolytope, + typename Polytope +> +double volume_cooling_hpoly(Polytope& Pin, + double const& error = 0.1, + unsigned int const& walk_length = 1) +{ + RandomNumberGenerator rng(Pin.dimension()); + return volume_cooling_hpoly(Pin, rng, error, walk_length); +} + +#endif // VOLUME_COOLING_HPOLY_HPP diff --git a/src/volesti/include/volume/volume_sequence_of_balls.hpp b/src/volesti/include/volume/volume_sequence_of_balls.hpp new file mode 100644 index 00000000..b79358a6 --- /dev/null +++ b/src/volesti/include/volume/volume_sequence_of_balls.hpp @@ -0,0 +1,266 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 2012-2020 Vissarion Fisikopoulos +// Copyright (c) 2018-2020 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +// Licensed under GNU LGPL.3, see LICENCE file + +#ifndef VOLUME_SEQUENCE_OF_BALLS_HPP +#define VOLUME_SEQUENCE_OF_BALLS_HPP + +#include +#include +#include +#include +#include + +#include "cartesian_geom/cartesian_kernel.h" +#include "generators/boost_random_number_generator.hpp" +#include "convex_bodies/hpolytope.h" +#ifndef DISABLE_LPSOLVE + #include "convex_bodies/vpolytope.h" + #include "convex_bodies/zpolytope.h" + #include "convex_bodies/zonoIntersecthpoly.h" + #include "convex_bodies/vpolyintersectvpoly.h" +#endif +#include "convex_bodies/ball.h" +#include "convex_bodies/ballintersectconvex.h" +#include "random_walks/uniform_cdhr_walk.hpp" +#include "sampling/random_point_generators.hpp" +#include "volume/sampling_policies.hpp" + + +////////////////////////////// Algorithms + + +// ----- VOLUME ------ // + +template +< + typename WalkTypePolicy, + typename Polytope, + typename RandomNumberGenerator + +> +double volume_sequence_of_balls(Polytope& Pin, + RandomNumberGenerator &rng, + double const& error = 1.0, + unsigned int const& walk_length = 1, + unsigned int const& n_threads = 1) +{ + typedef typename Polytope::PointType Point; + typedef typename Polytope::VT VT; + typedef typename Polytope::MT MT; + typedef typename Point::FT NT; + typedef Ball Ball; + typedef BallIntersectPolytope BallPoly; + + typedef typename WalkTypePolicy::template Walk + < + Polytope, + RandomNumberGenerator + > walk; + + typedef RandomPointGenerator RandomPointGenerator; + + auto P(Pin); //copy and work with P because we are going to shift + unsigned int n = P.dimension(); + unsigned int rnum = std::pow(error, -2) * 400 * n * std::log(n); + //RandomNumberGenerator rng(P.dimension()); + + //Compute the Chebychev ball (largest inscribed ball) with center and radius + auto InnerBall = P.ComputeInnerBall(); + if (InnerBall.second < 0.0) return -1.0; + + Point c = InnerBall.first; + NT radius = InnerBall.second; + + // Move the chebychev center to the origin and apply the same shifting to the polytope + P.shift(c.getCoefficients()); + c = Point(n); + + // Scale by number of threads and prevent edge case rnum=0 from producing overflow later + rnum = rnum >= 2*n_threads ? rnum/n_threads : 2u; + NT vol = NT(0); + + // Perform the procedure for a number of threads and then take the average + for (auto t=0u; t::apply(P.dimension(), radius, rng); + std::list randPoints; //ds for storing rand points + + PushBackWalkPolicy push_back_policy; + RandomPointGenerator::apply(P, p, 1, 50*n, randPoints, push_back_policy, rng); + +#ifdef VOLESTI_DEBUG + double tstart2 = (double)clock()/(double)CLOCKS_PER_SEC; + std::cout<<"\nCompute "< balls; + + for (auto i=nb1; i<=nb2; ++i) + { + if (i == nb1) + { + balls.push_back(Ball(c,radius*radius)); + vol = (std::pow(M_PI,n/2.0)*(std::pow(balls[0].radius(), n) ) ) + / (tgamma(n/2.0+1)); + } else { + balls.push_back(Ball(c,std::pow(std::pow(2.0,NT(i)/NT(n)),2))); + } + } + assert(!balls.empty()); + + // Estimate Vol(P) + typename std::vector::iterator bit2=balls.end(); + bit2--; + + while (bit2!=balls.begin()) + { + //each step starts with some random points in PBLarge stored + //in list "randPoints", these points have been generated in a + //previous step + + BallPoly PBLarge(P,*bit2); + --bit2; + BallPoly PBSmall(P,*bit2); + +#ifdef VOLESTI_DEBUG + std::cout<<"("< counting_policy(nump_PBSmall, PBSmall); + RandomPointGenerator::apply(PBLarge, p_gen, rnum-nump_PBLarge, + walk_length, randPoints, + counting_policy, rng); + + nump_PBSmall = counting_policy.get_nump_PBSmall(); + + vol *= NT(rnum)/NT(nump_PBSmall); + +#ifdef VOLESTI_DEBUG + std::cout<, + typename Polytope +> +double volume_sequence_of_balls(Polytope &Pin, + double const& error = 1.0, + unsigned int const& walk_length = 1, + unsigned int const& n_threads = 1) +{ + RandomNumberGenerator rng(Pin.dimension()); + return volume_sequence_of_balls(Pin, rng, error, + walk_length, n_threads); +} + + +template +< + typename WalkTypePolicy = CDHRWalk, + typename RandomNumberGenerator = BoostRandomNumberGenerator, + typename Polytope +> +double volume_sequence_of_balls(Polytope &Pin, + Cartesian::Point const& interior_point, + unsigned int const& walk_length = 1, + double const& error = 1.0, + unsigned int const& n_threads = 1) +{ + RandomNumberGenerator rng(Pin.dimension()); + Pin.set_interior_point(interior_point); + + return volume_sequence_of_balls(Pin, rng, error, + walk_length, n_threads); +} + +#endif // VOLUME_SEQUENCE_OF_BALLS_HPP From 9e9e82fdc6abe40c53947201d2567d86ac7f830a Mon Sep 17 00:00:00 2001 From: vfisikop Date: Thu, 29 Feb 2024 18:00:00 +0200 Subject: [PATCH 17/17] Update NEWS and DESCRIPTION --- DESCRIPTION | 6 +++--- NEWS.md | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index a91a2ee9..5af65f37 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,8 +12,8 @@ Description: Provides an R interface for 'volesti' C++ package. 'volesti' comput for sampling, rounding and rotating polytopes. Moreover, 'volesti' provides algorithms for estimating copulas useful in computational finance. Methods implemented in 'volesti' are described in A. Chalkis and V. Fisikopoulos (2022) and references therein. -Version: 1.1.2-6 -Date: 2023-04-11 +Version: 1.2.0 +Date: 2024-02-29 Maintainer: Vissarion Fisikopoulos Depends: Rcpp (>= 0.12.17) Imports: methods, stats @@ -21,4 +21,4 @@ LinkingTo: Rcpp, RcppEigen, BH Suggests: testthat Encoding: UTF-8 RoxygenNote: 7.3.1 -BugReports: https://github.com/GeomScale/volesti/issues +BugReports: https://github.com/GeomScale/Rvolesti/issues diff --git a/NEWS.md b/NEWS.md index e3329b9f..cbc58db0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -50,3 +50,15 @@ # volesti 1.1.2-6 - Fix UBSAN issues (lp_presolve) + +# volesti 1.2.0 + +- New functions: dinvweibull_with_loc, ess, estimtate_lipschitz_constant, gen_birkhoff, geweke +ode_solve, pinvweibull_with_loc, psrf_multivariate, psrf_univariate, raftery + +- New features in sample_points function: + a) new walks: i) Dikin walk, ii) Vaidya walk, iii) John walk, + iv) Hamiltonian Monte Carlo (HMC) for general logconcave densities + v) Underdamped Langevin Dynamics (ULD) for general logconcave densities (using the Randomized Midpoint Method) + vi) Exact Hamiltonian Monte Carlo with reflections (spherical Gaussian or exponential distribution) + b) new distributions: i) exponential, ii) general logconcave

TEz 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≺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ⅆ{J7$` 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 SmE72tYykD$gkxvSUpTr@w^W=dn^OhAde zm79;wig~eXui{2&FBM|$d8cg)bi?WrF3hc*4xU|{j4?lv$mq7kg2qEJOfb&sh)Z$r zBt(KiXUfKEt&WW^nkO3ze*ij2st_i&#{b89{b%=IyY^p`_TQD6g@qC7|7c8{|I5oU zGyLC}xf`u5$89#GnoTjE5uU^>B4FpE&vTBnnN%mIQY!cfzIDgzcA^ni>zoSRwxV<$c-&!8h~@uNVtT(vIP1<|O*5M){SZ&IOz2g?GpAIVU9(cr^qJR>SrMb&j}` zqRr+>e*fM-|AproV6c$lv-BMZgPyA1N^EHWi>t~s3ex<*Y2j{P-Sv;gjUIIKwR>w$ z9s;`LaI5GBbGH?9+ZrVnm^D>ezNg)$e0l0fTKjK#5;z49(j+V6+z0|uVw-ex!psqQ ztbPnZM6~(q}^1p((}L-qK|hDa^xlWWAC z7UAxmK|&Why#tIGhgt?|ZC~w5DvSe&c|bB=-&b>%zO{ZTNkFbF*QI&YK{Z7i?DxXx zIxkiMix~4u{DuZ5!&3|a1$e|)#1uI<57~w`xA5wbHwi5PTu!=!R%8ude${$@<@J&d z^V-p7WL#YzV#}0peW5+$@CY!f{xtw|20rPPF?mJ#COWcWR6U;A7O@qul34=S)z3>{ zkK}~*UP$OC9Tvhp8;e0N7K3SAcQ{2YhG}078yo+!x-KQ!8RzAN{-%v0Z)cgftp+d` zo*PCA>-?aGS3G{og3&Kk2klipFG`}K;JFc<$nd6!FSfcdCXB`a>hNTm*N^d()-8VU z9s`cte#IO^yD=e+umoB*+lGqq3kf5g7ZCHD#X%x^O4_7=S5W@LYzJgi`-ULVo@x7S zlFKvh2v4wl1+2yei=+J?L7hcI0wFHlsRCO7uFw^1{x*@WjG}p(0pf;>98DFj7ELxN z*rbOzg=kW6b@CJ5%RlbISJ#Nm-S(x;=4NI(gIK}&UAXI8id~Dqc%!N2DXIF6eb=Z% z>v7Qs_K38JM$bIxI+9-S$UF0lUIP_57l;2`w$ah0KZ0lg!y2J=E`T{q2b-j_5t1(( z=eM`qVgEHPEThx65^t3Ul8kRQ@X%Di36VHoa7B>EnSmv=4(oIVUg2_jX3m49wFGM! zF*Der9PJZHo8d#wqL80nWImi^a%yLUjk9DN;d8YI^V1Jx5Zj^6#if@TgHa`@F}-MZ ziTot8i8OQ4KU^`pqzpKoa9ZM@3O0L>E}g(fR1$Af(6*qDORt!*A~Lf z>2&VQr6;_9C-E=SLQ=Y2oW>C`;|7dS4R>w~-K%ts(6k@!#Ob&ILky;4owu&%T||Q4 zb(xj_*f}#B3@`)Zp1#nd#>nTw&iSkPYKqVTE2o_K1BR|-;l<18>?e@xKwj2?X7?%v zv&si=og=Jr{vA6{91v{ZPjir+8J=czTxa+Y;zcsEL;%a>)I0NePeI;9T?5c|I-Qsq zA-u~R+yuA-9S*3$14>ryTTApjMt?STwQP^@{i<^ClwP;KYawef+191Ke5=d3rIs1k zD2X2nZOE|6{HaAjPIl9_wVE{Ke}cTtebsC2u?fL(V>WIbI_=!O2B`9oBY}#yei(uV zWrP6K3OD}+1wp&@cM{g8wx}O?rVwt6DcPft!+B0Ky3Ky2?bC;dc2;Y&SXX0*N0PFW zG65J*p;XRU8eA;|d;1$Q6Oo31qWZEpCQc`RU6H09>(1j2@1`uZ#F#@IqqqqNiRqj& zFSO^at^4l8RM@$d1Jb*yb<1yTs6f(V7E)VFp9xEY}IVv;ti* zSC$~7nwAyHp7ORbOyj01gBffmWXxlb;7b;64OUDrNyo1qr*^XWHQ=V@HST^p9`^^S zzJ4T~JZLq8FDy&Eh(CEc>QhG-6O~PejfgXZh4w}*vMoko8B)fNbK)dEk2~vfP9F&d zK5w)&$quOTNOU5W69v<=9<*vB!vcLEm2j!AH!=F6v?pu7*jf9{((AHgK>zWP5o&R7<5_Lrb4Gz`*O;+_3jh;SMulg{kUuPXLI-O=NGT*5Bz;^$;jkIz(XE*3SXT8T~@MnY? z4bFHjO6ZDZ_vC=C=;q+zq-q}z@mD$RnN2R#6y)a-{S>yc8|f@bIdX9v2#@Ve|TdA- z`;#r=(YSp`7lR_w{Jm%e&63$3o zx(`9C$0PF#wjKqjxjZ*pnW>1q*Tvpzwy4Hx-^Gqk-S1wl2e}@kE<`+FfBqq!Lkeps80u!vV9y z<^;vB_MY$2;xVrEOpFpv*sWu^{|(@bP!`hlQO+oe^#x6mnn;i8Mu4ZQ4;1wYI8%%G z2Wy&MXhckTSdEr4Ev&MC)251=i~6BfOwl`^6*64QBf!DR+{7x-&Iw#6JO{Dj86W6N zREIBvx^K}Eu%yvrv41Fv8*u&t7kmmYMo9!r=;6)-mMsM|WNt9d|5B%Bkt1n_sTP9x zix=j+m|#VJ-q0({Gst!%%Ic^S=Ep8xbc%|&>V=ho)4Ckhz|mx?V8;bpI5tCx2jB0G z;t?iyxsn&yeyAHcY3&iAsQ{XyJDg4H~K<~dsNCX=E$CwQQ(wpq+H2x6+T z%Wp9xAK5hotd8C-Hw#rLpshSM;U0V?JJl_js5X^a%-@xj-vIfMO!|$m5@f#W!khKR zRe(G}DFMPbY+NK)O9CM~iX8q36Z8v!sdzV8)l;}kP1nIjYe7FuQOms0y1F$k?>Jf*{gd=I0vH$uLgbDf=8}E zNgtPJqsYMy1+}W*1a^Ok*D{b%gkW7;%BV}9zZ*U_+0w3P#A$#yS?BSfWm?iteG#px z`ys_D0jA?a+6-Up6&rlJyeuq8jOiB_RB111Y@`x{AcBmF2_T9lZs*ReuoB!>bo}nh zMG%Tiz1?mhTsco?)ongODN4%;_fIQn0?IVVFF+$vTGT?{kO!NC*C1#F!cl-Y+rQ=j zF5diQGy^?zJG={s(>3H!1KZfWdkky<6Az?4Qc!o^FKxjLjhr8Qre_XXCVh)x&+3-a zRWuf)o>=?12|oy;dG|?ofqWGg zaxk!zgMogXty`A1aJ+X?eX^H*tOGL7mHuC<0Wk^{Ef>Ttz8gKj6rbHU`oeTTn6|vm z!Wgm3zlo;sUxbV3eY=Th=rPb<$~qwF3ekn3&|h+AY4~q7lyp!57{AdL;FWL8J*0D> z^|?ERWesS%(>gJScVxU?@O}$8y;MXHyyQ3iVVqt%bcYD6(xheEtcn)AE&_bkDyeSd z4B~9Ps`g5?n>mV3YX;KZX>jt3!^w&mHi$zp6cfMxA4mAsl|21EzY#I#{X4uQvSNYe{%v**?n(dqJ#)dwwm<2T-+<@l5U!u3|L_ zEzsN)7?ehDK1jm88j6d#EOI9 zBIQgAhxO89U3Sau!4&2d4$HkY5s|r-b{7f?;Qm?mRSR8w*ewqB<1sa{X2R-7L6RRO z_pZ9r05X-bqa2jy?Svi8kwTWS9k)!A6MLaNaCQp1#zien!JDjE--)mXITi-nh;@=6 z(5p}s(x-rKv7Rt1yozqg%wyE?=1vy%bn$SqOzuEXBH}B&W($^I1=4+USi3$gMmuiu zZiH;7TYB05qz?Nlf&)b^55|BJ`CsivZ5aU{RE}uYb)M&3PshA)b_%*aFFLq{&=DDR z>HZkXK_RK(S*SW|50K%4i^fF+3>W3?L(?{phR z=xCw@yZwU2D$fxRxZS6E!rBBXt_UIw5B3qg5T+%yoIG4Qg+Pn9?x0w=@N*qf)mRMe zKcP&WZh0fju{;Bntr-orN4HUEJT$%KzQ}+YjzdoG=vDdvehi3U2lU=xDfj**ka(To zDVkaLGSAWJ*CG463Qs2^!S&P)ci8MHX7r4eNKN|#E!~3l;E={Hy8)QD=1ef#XJ?4j9p|Ai9r?O})rm3+#^|U3U zMLpb)Hr8LNSfCYSneuwi-fd*z=mPo-Ee6qf@{VUj1ZPY|$IrrzYR(Um=;JTdk7DQq z_=h>}a<4e#LjmGNDB^FNrHfdKoc0+M)Zz#wSOV!obHRLz4wN?#V=TL8JYT6`gE2sg z&*!e4Q1WEZL4TZ|_EP;J_N>!#=D($oo2OpagHiFIT+y5$jZvF|^bPEsc%Ew(fWwHj z>dL^ht{&_eCs=l;<@H((_h@y%{I4;BSuyuSBdG`9W!Zcy#~@Ps6@b7@cNfw*gzrdp zjpk4^!14-OT!92#>L?KMt6&md;2y|imEYfZb3~>xJkbvXD8nF6C4>Ub2*J2NI1kpv z9YQ-8kPFIvN2*U~2z-D0_LM@EaEE`J6p%zdi$}SDy^(*PPtBOLLAs%-j@J;*Kx>yPtno?yQgzk&}4JRN>Gy7``Scw}_K0=2) z{51)xNCeAJDMB-F!Qhfc#A<_x(4Xxx@RgWw&Wc%~Rco%l@ zBQPm};Ik%qn=|R<0sg5)Au-yo6}5XiqSOk=m3e{QgEZFAdqxZ*5LTGGX1!kl7fi&F zp`D7|Rjx=t>|UdnQ4$E@s0@eIQ#&0c{_E&Z0%()`t--=88HJlwkrMI)1@AX~`Y9DE zdA?|r`HWqsrLkV4QcfM|-l|otG=&1Z?u{Zq;0oGl=k;1n> zXUX_Mqvpfa3_j%e&@IHr#p=p&S^{zArf4KMOvXE%nZ>F_N+d4QdeP2co@}#DiSBbH z^knmH`sKZ8k9k>vex0^$0YxGF8TpM(#DF+w2hBm+Bt&1#oZp4s8|R6!gJ2-iZ=3lo zD%DIs@?o)AKtwEw&U}RfeX>j!-)WuvGJ%G=A8=BkNjjQHi^_RQ4Az%f=|T&V2tKBS zT=$PlD54KSg3Tx@aJfKvUR)X;$bpx0Hz1ZNXX`_@ZwGX~mkSYhaV{){$zXsT@F$}~ zO5XR+T536$_jiS#k^;GcAg8O=2Z8|VOYK2Qqu(QU@G7f7q)JOB{6{FqOTEvRBhl!< zy;1=Un_&nmVtZ(z3SoB?n=43YXY<>wLtONpp1c~?c=qV4kbI)x`6TrMkizQw+-}wb zrnBz5_b?Z4pcguJPw^W?1={{@8rfcynAkf zz5NE^Ghjw6xHI0}lVZ4&Zj5XAaLJ$pLX#UATXg8hA1<3Tqss(QSF&SubCt-A-R_VW?j&ko)*rNJ3Q}&gdo>Y+pA_)D=1M^4ug2|Mn8;jC$m-sl_mN3K|$;n`rgxfVmIr%Ck>*ma$ zNVhlFq>;TA4~i85r$HgTBt@r)O!P6dn4D@j%7{{L+WX{4SX$ZBk~cLbd(LDkKQ<_X zVYO!=6ghNzP>NKR`uTRh4xB2r()>INfm)bNG(g6Z&S|55AH-N|i-feTX#k#NJ+^YJu^wtx%vIO_GH;6cqPdkK% z2~~+cn}XBnL~2=y1ChzO$>?%jV*GuP!MpzezY>fMS1~1u7$KbHghB(+*miHZevGej`7c z?s4l794wcA^3#pJx$e$zPfj#F%2sm5Fqh9&X-DT!S7$kf`_qU&>+X}coHvTf>pIV6 z4-OJXt}2PTc*U(Ybxq~A@B`Zulm-1l)v=!Cr$G-9>4DyF+XM>mn=eB;9m`x_zr z@OaLkBFS2&vh(3FpMIBZ=fe#X;@3N!v};S^I6ik-V>eYpfARV{2s^$vLRtUvx?%c7 zu{^#OK`8i}&%*rLWAvw68QZ}dHrg$`rAf)Epepxi7ailN_?Myr-pYt*I4>2xGJXV- zGR@p<#iV(z=vc`um1DQ~WQF=7V&{UvAvAsoqUqw#$mnx#@Z(gog~&RUbn$N)Rd$}__FHWRh$!;H#j|K}&c)njhDqjISB1WnvI_Lyr|y|TFu0pA zssPTDN|sMC5Pv1 zbk9&0TtXP8{ki`?M`!TPDJ8!wGhgZ}B-<6|{n~j4V_6!$lJfSJwe08+UB8Ts#ATPB zN;k+UAR&SM-z?|C5~*^yFP#c>MoaOR8J1CTSbAISv3_O)C&Ntr7cpO_(VPp}fT3Y$ zu3m9x@>#fQ)m)8Y<_$)g%;jsFW_97T>TM!9@!D|`90+7z2bi6!eVo;h^_Z;^si}tb zf+0nruha6gPYMuP_evD*ss?<^@28`chK}3~YE`nq+nrz|bKI4Cqdn-ku*(EY7P-Q# zjqNefAQNF~Mae0Cz6$zS0iC^|{O_oHoon9IFn?M}V*0k2!MF`2lCk~U?=%+OYeOHH z2m>=^(k2!QDvG%W$`BS~ges=8i_#|xR)JTr(9z&Y&&>JG>0REOtzwu_6m(0@xnjT2 z?|6dlM77h}#PVdZv6`Hf5pZnZGA(?Q6?ZW2oWb7UiYu;(afn7u#=OBg?GVct4~gG> zpj@@XH~2E;f6oZ{*I8~@RBp4pQRRA68^5&w+`xRLT}%wjrsW+8YqwPr#%9B1b`3a2 zdk0L?V0;9`9#k-`V^waPKShOx?=z(3P_50{+Ii}>NnZ2Xd1EIkMRBD<4m@u@-CZ$> z8c@TGgAjbEI$)+yf|lVMk-`6ymtiZ3_<4&bnplQ#FIZL7+N0Zzus%j~QdWzwpbn_o zpdT@}_p}%b*Vzn&9Y6`-hjcaxYfW z{EaY{nmEWrAxR{R5ulm=b2XbR9++FF8J#=wz3za&huTy-;WIk(Q`&RRL*Y7&hSW_DIcIdnet)kB z!_9C2y5No;#C!%F$dpq_N8WLwqUTMF$F0d&9_JxaXV<~@h>PMv)Oob`uxq?iQEvZC zi3R?WVh{5C zvo}_+r*6;Df`1AQT{j4X^}Q&4S(M#&)obX%CJ~7{F&xL3%5jhB@E>qZ#Fq7!tA=0( z_oHc1Ya+%*p*UL3;%%n0{wP^4V4WcjN2@d7mkHKY>NtdFsY&>pT`zrH@rELnBDT-I@2y?#`ieFNv)PcV@uttNekn`y>}e?!_NtVef)tRnTOE0fp(YINnHDtDP2@y1MBaSG zrhTa6-P6Y|kgTH?>V7W$B`uHu@J@0bJ=TNgP0mYVTS-2$sfMh&8J58pkiHp4MZgkG z_^Tn~Tz2$wM1Zvbh7EcrH-ZKu=r6sRoDW-`d`xJw@OlO@+>G@pn*`{*RuBfY zPZd@*buJEFt`dvk&C zsSOz=qxJZ=WLn$x(?hkzV)G%$CHvIyFT1%-s?d%1rG*%x8M--9=egIW?S+e^ivm#? zO703bA{%R2@*Aoo&&3il12Y%s!-D%sD!t0QLhoh2Zp3tHIuP!Orimm{y?+h@CgXux zezkoWlH#kL|E|%~A7EBIXK0PL3l1nqWQn@D;)s-(XUEqzKV5YPy=`#~K~|(`){=sk zPVf_~=lDBahavq}7)!x3P`i+;q@tio(|UWWdHcBfmZ_jzFn0lu_jXyHa|x9J z&1T&GUB5@xvRxq)V@)!N#dNZ0e?6pl=88PCRLCc%sgH3!+wz;N_`I^GL4}XMG)bQ=<05L8;E)aHQ#M*4F z@0^GCroV!}Nt}vC4cLeE2g8*v%dB`&t0M-u?JkW0`f;W>YV~*Gwmy5vFoVPHp>~H7 z)#81SVpsD7dwzQz2~x+sF~TQ?w=is$Vk0bmi8UPAZ7~JqZcrS=ce~Ix`$$+fJ|jm~Y$xdk+I!CR z!G`?tj1M?SRI|0|JtK~y-X=e0SC7F&2NCcm!^+Ox+U^D`3u{^A=B~emy{NvSK{c^d z?Y0kaJse;79Jf0-ijBzwl|Y+qpJ2INFpS`pCL_>)uvsQ&L|ZM(PS7p_UVkj4w7n&E z{17o~RuY$sXck+t?twCMNoQ`Cj0q|8$cZm)#OFh|^qIPrgp3V1adrkRaPaSNpyw~} z?-!m5XaVnkCxHJm=fuvy&i+3MAj|)V|KCRz*8jWVCvB}bk``1^kMKmqxPzXQvPje=j{#S|u)Z+#IeK^VdhFjW-|nw_BrFH;z2{ty-mdRgqTU%d z)$3}O9;B|RF?2oGxmWNV>Jrww+E=W9l3{r!Z!@|B!03fu4gbn@C5M#V?C3qMYQ8@o z^B4k?ok1wQrh38@=?>r$iZDU48;vRC-QHuw9>Iy3RMlpg}=a-s-%vrD&oL1rry zu1UJoy*9edLhSAc-%{2E=z!}Ze%E%5F=XvxSSjycyy$A~vwIWGjQQpz>riZ28G)Tb zS1bY@+|fI9YHATbUd1gADPdedx&l~1SH00L<|hNj9@5j_qr*m(clznm&pMfQ#XTrc zR%pLakb2J-dc}i}>FS=vy*(X?11-`!*BvCt^NPnk22NjkdKbcsZg_~L0|&iEJD{(V zu+}RKBmv-2l2LNTy(PXVQbYt7fLJA@kGm5MI0|{V!)rP&LF1)eOyG$Ky6-27J2Fq} zsqZ4W?{S~&qyW`fD`Buk)-7@$`&j?t6pwR^86a#hkF{hWN?-_IkFJTTG-xS|#)3_Z`8SH^Ga4_iYCtjn@5mm{=En}$i`mqIBlFK9dphBR8HLU4TGfXu zPIlk@@`7CrqsBKL1w}ipaQRmtEq)dFEn1mbtM}4wUa@3G@j5HHV`^1b@F0E*xF!y9 z1p`G4u3^tV}ltAv7gvKYEbR?;gI0k*|816X1!>(3?B1fEJ`BH8WDa_M{zg=p- z<7IB@kETC)$WM?TNuh`$Uyt6)nhjJZ4$^$tj#Ae2^ zI$da%+W>7chq~3bN$$6*ODr%f;BO8+U-GY?SUk^P+7hi}$$yJBi2mBo-RJ;#(CSvf@C; zQ~YH1n2=TLj*xdeLJ68~QSvq^Dj0x9-RKY}NlchCI3K#baWFD%B77u;4%R6H04{DY z+Es)6pEGn9Oaf7}3i+l<_FW)1k71fX5M%OwI#i+pf!m|FLkbTqH@@L`9A|E!e$&7M zfJfUaH;J52Sz&%+P$JNLXn1LfznFyXH~!10i@09Az%t5_Y>X(t5#kMj&mx)S zLKA{aA=4;+^ut3~c}yU5rH#Tu(#81cF(2DNHL|0Vff1C!#2p7;JL&9`o6RbNqBE!8 zD?ntx>&5Fv5Uj6rj15di6#CW@_)P{UmXSLRsS4vLI0g;CNIO5FwLd%qWOb1EIo2Ri zX-NaW2NV@hPd_DulDH!w_D$y!Uq_dhPRTX2SViMt%6i%m2~bV7O}%pIAqe2LqQ<$Mz{>U2m;pN z86ta&^z_Y{tmRS-Yj6;@mxVNeeOy~Q*H3O|(A4k<(V#Q5D(cnxVm3r`{V^uIjl4yk z&tEDIe-%t^-Q(@#Ql%nC9cq96^UABp7iO}mWhsynk%|68Z{Val!&iWZ~;td zgFK`Q!cFmN#o_Z5VSv~7CQGUvgJO333@eu+VN)(y^hxh(7%NZeM%;e))FoqFB4x4Q zeN@c8gpVoG>}+L%(NcnXs6u)NMlH5%uc)`(4Ryw5;XWZ^xgYEZUr@dJ6={htZ%tES z^XTlps5Fm_97FoA#w)EFuW3ik5lD0EN8%g~ zGAVw=FaVBaI!z0;Q!r(X$F?F1%n~y#(!FnsOOfE;`d{O z)yH9srA5aR)1#h&fQ3p4Qx)zo7~{&{5dEVb)Q2}i*Wnv!WUhzSj~CKmisd(>sU!N5 zlFg^nYnOzmlL++C;!kkA$R{12tD@F&kCjcztAm!`C;#Q5MR_ee)t7a^iSYOz6hF=P zw{Xj0PA>T{w}d5x77v`aB6 zA>M;BR0uN4L^P)&d{88oD&(ONyiV*z-|a-zE7QQFct}oDvZOa+Z;yifu*<;GFQb4U zZWY8ZJ|x9$UBLPjYF^lgV=G}D2Z6B2Rq)VwaQXhx&4ixwl)AuJ3zf=fRPwZM{w3pkn<;;GuwXv_z?MA<|Vi7U3*Vuq9|JMv4%0V{i;?oKH9uwVFnfh9s7nQ$`i6?_`S2 z5}g-ojw~J~+>_5HtejkU*33`S)~cYML|ctaAwVA_BUv#+pv^bZ7)#iAR>+#LgE(?bHkqT+#7SSopaOEIu*K=cWR20*Ezw z*4qBp3Ul*v`g0U9*a#ZJyY%ZoC^RTF$MFRC?l|08-WlqGj<@4^ z#P0c=PM*6{q}Ju%*mJIeRU0XOk(T8bw1Btphl=46v@Sink7kuVy$#l|P!PV1lyySL zd%M~o6OjMzhn7JLj@mSy|Nk)dPQjuCTes%2ZQHhO+qP}nwrv}G*|u%l#_oMi_r2W_ z|HJL~io_gQkyT&LIg-1QW`@PlQ@THfn8$%yX+3#rE*YhbLZ_~JAxAb;s!Hj?*T-HJ zsT*fQt(-l*H8kf{H#D!acyTd=-n~nUJWa6QCe`mzPScf1v3i{ayTc~^d{XGj>LhMF zw^XPzun-zsRU1>Q_7QDkigUzmgIZWpWHaPy)51{dFA=ozhFvTnFh7(8Rh->w@0?Y4 zR1k_!@Aw&EH5PE*(LLf()5)X6b|$sq{&cu6#p5ih8;CSjC;l5#H{R`1f=rm~H+2bt z-FLF|Rau1dw#y`HJop1G=?4JV0jbQ-Ldz%9;%?C2Z}QzJa=}%|rt(gdJ3uAcLE3ck z;tt6{{eI9zkyFNFLkh`%1%Z2`%kN7L;gM}^Uk3SdH{n>|!XQ|?N1;UokGz~CLFWV) zg_dFGSta!8tEr+374fW;(u!gpbamyfX&&-B*%`u2X)0o*v9E%|Hf(ep_ zFRdjr`e$+A9AO0g(SMS||7G~=)6F5~=kU#?8$gkqBqGT~25^M^s39T;aIS*vrkK}EK@){bBTF>4bZR<91vPEYpqa$pZFr#da+N8@ z3WC7V+W`yRqx|%}dBh`BsTS1*LQx6wT1NkZTP>mtO|hHWvVp-qRz9xpUW?Nu@*%ZN z5LQjL7mA#ymd}Q8v4b8RXdM;udtnr^D6StZi-@9Pp)`d)c}sZ|(O5pR_(ftKtqXS5 z#2O=D3*CTsq^V*h*O|_EZa-(K%--y`Jl0l#N8t7MKa8gKn^kb2Vbrf_8co@y+;OT~ ziVExmT>}q*p+sDOG$=d(bVWDk^Vf!Ou@FxVHZ9F30@Vj1n*T9EC5kbq%$9S30NkI# z##uCm5PVaNILP516cLjamp!Xjp4h@%@U-mCb_l*+H$4Fs*7~=()(epl zJ!^`|`ltA+^cGWH;~j<(;M;4886;D&ziKHd9kPeJXK}la^jsT)Rb2p-qtP3;iFb3> z?fy&rCoOMAz#^j2?o3KMjC=R;XP`(?)}WuPh_@7O>SRgevWV<}Jw}O2ePQ{2Ihu-| zJJ|CFy!>|%^FNr+e_76dvc6eZ8JPbU^I`p8RdD|w=EMH~C+oXMN3x!{9TCoHxqe>J zsR-_kG!UNA037m=pt|4x6V z&3QuOd+rOpI=#An)(MY&b_wcCqr@Pz?#LI@dx{)!lt#g2cg%4!|BP^e96?al>*tB8 z!t&=u|B;S5MZTPb%J9kW1`q9OkOOxu_m6Zk`7xo@qcwgBfca|nKx$?lueiQ$l)Dj(1z={@~)E8 zMVeJ}S0w4+yI^7E$p!OqL#iED7Oq6JB|`LPK%(WI^T*j&!{06RONrZux#MMJO0`?m z@d%Thv6J|_Wcu^6VUuS^&$n-Lpz_75eC6}#wx67uy}r%s$+2rl$uwi5d+&CcBoPIKr7?L?M(YB0 ztcbr1_ix81_C`>rSL3_7Q4gSpO(iDDex)pC=z@S$!`0*sX5? z{8Y0|8@E7C>+4e}YEEP=Tfbc>KFgco(;1&a@Ivz8+NYqc*McFpF~VTz8ZhNvZAe&Y z_JO2PQjQYrxO4%vK3}jVDHfjLVp4IWt_1NhgVgD%%eC#sd-NQBO-VZ4t2!l!wXW)8 zxwhM^vD&UY;Vqxp@cQPs*~qW z1u1s(mT1cYK2L}v*KYbF7{lm3lJV@f{IFk3^}yfIsICB4t~j_c9DK=TX@-VHV#>S) zEwLBHl+h9C7CYb3h}HbT?F)(|h+?{^m~I^u)^D7!?pLMUh$3($E`Jo$@qAt);a{HF zc~)jhY~N58mU z$wK;=xXw3%6>!RbEd2(w9Me%V=VU_l`?YYz!vV*fBfTKOR=r3@p0uU2cC{RJI0ac; zR+H-5)$;f)Yg5dsx)=3kwGxYm0**RFdO)UDy(mVWG^L$)O$V9e6l*iji*$-_ZJGr# z`Ewy_FPREYuZ=Pr&dwXAA9MysWZpHR;BOo#iOB&}pjD32KLPh_@@(DnoPz-R2|@r5 zT`mB-X?X*_gos97SgeRtMUDZrup~@6rV_@TmV}5j2@u=15s?|;LGcWUPD(63MgMLN zf^AX<26N66w#?Uf<8>*@M6}@#=Zc|NTI-7RIL2T&Bc6D1VwJy){S?@7w_{ZuIl;t* z08fHb4w&+A#d6Zz016(c;}Hus1Emb=VCdF|n8GYS)-c}8vP8Kwvc)q}uX&c6(C%;$ zbv`V!WOK%FbNkf?lLu$rngmE*dEl!JBZtUA%_d=_s{z!yd5s@Y#J1Ee05f@&gGYMz zT>`dCbuq^>^_k+7OmG*MWZJC?^RdpD!5z)U?Nxv)EY+s0*PAPq*-u8b6xVUYa~C0@ zAK>Iqazq19dgzZbA?%`iOGqG(Ir``6TV;>vj*ib(De9jkhXqxioiprv&?(Xn>|YEM zQ|{)>X^p<`gYq2~^Pn-cVTHg+UuF2L|NH5Ba{OD=N^-?4n@%`uQyW)2L^S3M*$e_~ z&6{+HS&RB3pt&=WHYt0@<5m?-8@kx{Qq1Jf)n+8etwMGBkDYLE!EoeBFH1LIXv3Fz z@L5Yzcgj%I-ZB~DomJ4e3rqre{=JaNa6ge*-y=C|RhwHd?0?W9a+D_Xp&4P;nmVUL zK6Q+C6Ymlz6;tQe`r}OH2lNW)*Y4 zt4spB|GJP}FYFd7bqJ7MbN+W2Bz6-a@ulwxqpUpvMax>0DFN|5tMx*uZNfJ!kSIET z*NYyXVUkQz@pKl5&o$Y2aa;d_NV(nC6`Ep^5FVgzXtRDJ8Kw z%3W;a`h7|}z=p=Xy+}U9=pQ6W8s3mVkeN~_3D>-Xm0KSm#{u~>kUpz<9y6@;yo_(7 zb)7T$I4pK%zbb>|+$sbQ!kJv4@f}qI!V&G|`^1!XFu7&0Yc%e=(&s{P2c2n@A=XpR zaPu+zM=o#m&(k#J)N6$dHMH?Y5MGhi(jRa|3!jdM>e1I!otK77IYnh9x5P-4YZbNS zt^3jFRIRv{OL9w$>s&N{C^l9n!0V42@AvZ6$@`i1?CGI%4n%F7%wc5>MeY&JHX>_u zVbFPD!`t=}KSBju7|?@chnTaGIRQ-9wiX`}NISqWjf;JsE>O!c^4Svtv&FH6!ipBp zBXfF!PDHJb!q>RxvgA|*6QaHbsj=}`_kM5Lzy^$N$ytTw@$Ny8k$2OKp`Q$KrG=w4e4iK z>WJfV#%D|={~rHdk}FJ87AG_gTyW1QGIP~38V~FZS%%T|6CoDrt&qG4CAzHpL@Dc2 zGj>hv1;QSS;!({bOAXvq_^6o^25DT`-7aabk6Q}N)$K)y-@v%hl^7MhGx!^8*Aary zvCX2`PViI7&hjZc!M`ScIu08LRdiVxTunhj9I4UOj`gE3$L=H{qGZe;Vg;i%$z6_; zaX?@m@?TkCcF>{#dh~P%QEWP1p^taB!XH^Og^E6L5M2{(k$*Q32oiK@*Mt4AW7Y8G zEN#T;`81G;)9Zo+oZJA(U9rnX-O)~RxXt-?AN`^`9~vjl#M2?JR&-TS%{JeDooNX+Y1*J0dv;h;oOzCwlMQJEtm^C!kbPro>d;JyD~DX z)kkv|d(Mi>=rDB(_%6dNw2=W>In}P< zKi1u;T)|h^_q96J%uQcvXAXR+nx{~K=b~3|cRGo^8~l*QsKZXa@*U4pqeEwQYOg|d zH6+ReayIkbjMo%zw|Lc^)n3h?H+Vb;o(v1)E!x?J{E{LDTRs0?=rms96=U!Xf1D0< z(qsROMffDG6au;S-DS2i1~HqNWhF36r`*{WP+V%UCDYhdnqjLyYoT!3yZ>^?T+|vs z3lX`hlH;$uaDiB6!L2!Sx_Amiv>4gx<-amc#usXlot9Zt-*a6V%%5N3T6o&`@P4zXW>lv zVdZ#P+}$_(k>y<8T%V!H==^wdv4~`mSUwgP{^?KHU7ViTna}>+@&csiWrZ=Y6(dZN z*=P0R^VOvY18(sV=!V;m`8WiHPGVPt10-19!^2&5wfwo*u4A0Sfzy*F09ew|csWOo z_GjoG_$ASjv}g+d@o>Ys@@pzu04U;{{511e>=L1Z;zq&k@qv@cXo!FzU`Du3LgnwD z(ku~*9R%OtI}9j(xlhFXbnMjlGbwdV0Emk<91!$nlM5mGCIYJNc7^d4So?)wWIPZN zsC6INPa~y-Y{OM&Y4L%%%9r0TI3o&?pci1jGZkh-@MYn*`m6+q#DKTJ7E<4N@L=4( z&~uVCEM=N+#JFRu(7CxSrJSVsJVNjRf}M%bsL?{Er-Am1xT|l;Z{KJmer`Fh6}>^ zW?M@)e{$v9<6+=6)AIjPPzfpj{4&8^FlC%fZrBThc$W4pDKn1vj-RLy9_xDMLzlfr2j=Yq)u6X5~ppI1~Ur)r)^TMajkh>4eDDCg7U_Y6Wuu- z{u3gg(q8TSLU2qQ?uh0(k3UN9j-ku_V|cihBJ&zPH1EF&ZX&KK~))pBe&cDGfyZ3vLRcm}lWx>hA zEy$s=g?mD>5=6dle!;Baw1Qj_Iu*0mX8{hSz3&6T#Sl_#Xnb3q8sPXDPrF6{QR zM?BiNJf`w9!laKNb$$eQI+Az2(eMKG$L;v1j0(gQoDWYf;inTLv<7&}8kVe(^0v?s z@g^^W^_1nkez{HAk=SyBY{E9T8OfSe;K@^V0qzvHc7C0 zr;NA%lUe+SMf{gpFmn8FX2HhvKPuJPnE(GUi<^HIgg7IJub#oh{{7oLw#>C&{6Y7C zHa5oqKnJn+fE*H=qDVDr)Fgz&e?863R9#J7)#t9Q#4)x&2w5&G>%>>nGxg0(XP#m) zPQ>>8KkmZiQ7y$b+$Z-a36P%Tr71^x(W;1}Hmxb9NaQ?C#*vdIJu= zDZXeYO=4(|tnhU)+n*iP{!WOQ$JSkrMKj4?Yhhh}UeU(hwEmtx+Y{*5Vt;_~N`Vsb z@d7vQ;tyK{Ck~djjoN%;^du~=#_d8{vvYHH5{O9LK_KKoNpfyGXJl zPau1Uhf}27MJf&iHCW>9!~elSXSEoFp}2u63Aqcg8Tr7407wy(Z2zGhPpA=aCpN(V zrrVAO`(*;nK-=>3@p8I5{utd-z%W|G)fE_A$>0&UzIBN|pMmitV1uxyv1ID@hFyJAR)kmI!o z6MO&<)eCI>l2WnSy*r?2>a2#rFCa+KlLZS4SbD1zev3tll7$UiYTNa4b=~kZey@q{ z8&lg4h)#5?n1RJtJvV?DW&j5&Tr2pMD%ZD3vHs02;ifCXjz9z&sp1kSv(`ld5W8w~ zAHXc_F|^pN!5R)ExRZX;KR3ajL|b&{0|7(j4{#9T<59itgxK&_MQqLL4n+%CI>~D1 ziINfmrXlwu44U_f^yTuJ0(xPQPLkYGm0P<4LUMx6Dsc0Pq{QJkQN%||o|^lizIiL< zU4ZQ1WK&{e=vPlAqC$55I4m(YK{fGK`6dG`h{T`%xfRGry$pFxl4D@U49^l!0m}IC zk^WRgOiHZ28S1*xRw)kuf%;+5ZuPCV=6md(=QGe31RBRZo3#ml$cOJN;moeco_MZ??Fvu(MbdnOy)`q-x7=QPj4i}WrhZbgKK)~ zJQ|hucYaD9^&SmMwGhuASPTKSKuodNl2WKQ6dh*ovkL$@6>5iEc!bf4gl*0>TP@}d zLolWC*bBnC_3Len86p{SQM$Cu*w4zl_N{TCbnqyn+^cXB*y!=EYu(McOl)RA6%Ry3 zeTF%>NAQmlIr=3GFo6~F_NC@VQl&abTM`8ea}osUk?Y5D4(9W`YlIdxseTT5a36?e za^D0r+7kXiY{v{Se2%A6vEKs4g1`?dt-{*Uz zQKiKa3fKlfAO{G|O}x3-%0x}5FXJa;Ps(Pg9$Vt zyaqW>2k5TBEMTLBr9e|Z1`Pscq6B9^T#(%uIW!|YY*F@rw0)M-fLb+Iv=a)k!CGKr zC?SrNZHdIqmu75eq$lY~yYDp4Ht7>*Lm1!%$YV20Q4 zu&olErhcTN9T0u^K;cy^4RGMU$t53x_IofE+OcBRHpx${3=a{Ug4q2Q9!6YIhv&<( zpM%h;y&eNz05^z0<|(JAv8K=!$dz-KB9~YRib1K*fpgASq>VCwPR_>%(e6oHr`dz3y(}MQaZ2ZKS#%Iz`cKwi2sL*dL{Vxk{^(T_B4<>7!0ph_Y zjeK6kehJ#S)SNl{xU*m|%!*aad`g9UHf^jCiW2V6P&AxoEx21Jyg);=pb`fk+zj7)E=A!U%HWkE?+n#v(yw+OI91On`ry9E*H8o9e3*8QZKL#b{QYF|ehXIOkxg4;k{gyw} zCk6rFl9RNog_zOlj3X%rC%)cLg$D34T~}}5H;lH01Y|Xlsi`R7z40uN6OK{^B?61y z8>E4@c>7h4vTc3fVVC8QP_D)n7||R%uPIGZd}1Bwdllp_hG}_l(51DuyK|fxvMNz! z5F&Y^K9V?s;dVSjo)+S33O~Ra#}2l&0z4G>;;aWg@m}+%U1V0ucyn2Uu&UUMyo8>U z_&KWI=!}&7Ycb5Z4Y1KvbxF2tZH$pdQj#tBzkpF>%2eRHFwi0DUhxC5Pr;*%ae-Ay8sZjloKuT#DBDn@mh}n zu+ImpGme>Wyo?KK5ShvO;Z_0G)G77XiQy$H$#hCWjAk-NBQiGq0vP~7nF^(0I(3Sm zPTij*>#U5Jgtj+!g-GE^R2E+`diR!O(7;GF!)=BbxZzjtsw>4Id7*3|5{(6x^7_U% z<~X7lj!M;4yB0P?4ttm3e;x17vWZyUccEnW-WcbQ5P8AJbx4DiC-SQ2Pj?AwW-x$+ zWG+5==^#-r$mk@2u>|MGNUS!!4*lw@^KakXczy!kxBkR&HUHdoZ&Z}J1#--BnP=91 z5D{{5`E}2yFkTzdpAX}eq8in`3HoX^>I6=jgh0{(sNUtL3|04bw*QzZsc4XeX3~Kp;t?Jz zM$27#dh=@KTX-~CnLumv1)0a;%p3VRP32AW1BsuwT*o-Ck9e_Wu z?-7MZpvEw$WYjzT0_5$Whsn}_7NY_H>XR&B?m|KAvB5k2!Wo!E*v3CJ;zTIUk(}{n zOG)_Jmvzu-+c-b)nd{!g&g2Q`11lE)xIko}wKgB?nx1o58;7kR1O5fT;oi|BU@UgaEML}me} z@PiLR0fOF0ova$L%I*hxo(QNV>b(PW_BqN8a4r%tLkrM}AH^kqD(2(T=XHzE-~xHy zE%R=6mgKyx5B|iX`{p0}L45OB%YYA}=hshVMAIi_d(BXfm^q-s;%3e07aS?T`e+sz z?TIT=Rod5kiGyTB4MwvuM+oxo-Ot>0WY@lY(;&kQzAECUTjSs+UX?X;q08UR%mK9F zE!Lm5N%OWhf2B2>&S}BWAKYDj$5HFqJ1N z9ego!;xr?DcjQ_eyBt5JjeobgvJi4&k&1#Cy$k4AXlpF;C3A@z0XM)lRzGdCNAxRY zqBi^SvRR^CFGF&U_ddT3C!_D-Uqvp?SHAG*H?ZLBdI&7HBJ!BC74Iu+Qk>(gS*_*O zSX;V7gGRYxmvlqhHr7$ODsfKv3EO-ZBgX5$EEyUq-0ek)kMdTB;m&9+2-eX|o%ZZ^ zAf|8IW4`lI%W}h?vYdle^BxD5o&``lt9!F^=5|F#Ub3~tkkj6=x%}d_s)Ov?m_E}D z>j1BIKLPMonN8H4ogGtrKj-Eyg11b!hNAUMy(Z1I0Y6%#EL{8Wu+{x*Hc017A8HGi z2*SO!w4w=0#n*`!ogrHf(}c2_BypRohc!ll{3v4zQz}G|$_mZ3JZ~fBiF@B*EhBuC zR%d&Cb~;p(8E?Ei@$XCi6kA2S8}yqExYxDzdSHMej$f~qhJivpQh@+2v)FG}C-v;@ zh}+$3ePXwUZY4L=!38q1DVU_ zUM;n;^YxnCc5lPTxyriJ5Y);>R09rTMN~7Jx+al0Ld`ueNYd@#$sS9C7hnGy1+;uv z58C3i+6)vyIr2&6VGSPcnpBeI8|j#+a>XMb4>O>@Sz(IGej+n9$rZYx5x(p2FK&06 zg(+5|JpKCu_l>zJ8C90juZ}JAS8Ifl4-}kk(s5ZfKdezhgCNJ0{Swo+&^FTjAbSBz zI>-T_xBXNXmHOyiibx=L{{nB?;Dj9|1rWbR72~#hI~{$3el+WDQ`FV0D>r(Df{pyk zbPWddSffCD38xut^h0HLNr#!}TePS$OK_5a)Wx;VmUBl_j4bF1Ym_QN7OHWF*a*nE z_|eOzlQ*>M5IGrZb(O_3jdY8$xGDi(?J9zM!uy=Wq+c>qdf7A;?Y}&$N5_?rGKd7L zwJX{A&ByhBC&gZ{&2{|qO)O^0GcJse%7z{f0#n+@J=QVrl|i{XSmH~FpeOkKjKQ&6 zeoQG_(k{}CS(}?@fmq}$VQh=G@7CURt6KCsmr{{z${0=<4LO$#QY*c2Q-l|zXyA=9 zeVFqV@VZ^7i*D3FnP|l$d8>;UccsCZc{CB=JM+DJRm~@KHkP>XgHYwxFzriKH@S@vGVhIp;mJkjyurSRgq6L#5(+KM z#m{I@bk4;sXuQ&YQcO05YRX;0=Etg0%|}6`D{?f}@}E?qB3JXiu#^HQ&Y{d#1KORq zY9LYlSGPE?>sW%CDG$c(;8O3EM@m;}Pc60O8q}g*ACf9`9a2f(Ob%96(GjP%FwR~7RY)*_#e z5gSF;IvFohH`dbiN%|q{*I5+N6MdU!>yzv-sm-!%wBD{xm7 zy^J`io!CuJvnpU@lnzeJF_ex}70))L`Qbb!W)w;E@){G9-@jXgte;Z-O*zO=boGw- zO-KZCi5R}WE5S)h+#$Lt1GUV?%>4Q5b9kkmo?Xte35s)yxF2ZYx}&~pH@`GQ)?`Zn zzXBvTX5jq0jJaZ6uiaa3bHA}kINmS+2hRB~rui?o-A2fIjo!(BzFgcN&ZWD&9)?D?pzBhzmM5vI$r?9LylZ)P z_4O%i#osKyUq+^)>($xOeM0^1^j(%nlWj(FjjOlr=<@U{B1f~xn-+%N1+OH6KTv2$Kd*90`-Cy(u{XL|qLD!jVfMZUS`p}DH0Fodf zTibRvNUF|Ado%PyD2o@!;^I7DV`_4so{-(S^K=UinpUIds&Sjrc!9h+^jKh3tz#-PSNo%}y@0e~xCd`XBMoOtV(ddw; zmZpOsOY#n~^z6$wmP3p)y?Rx^DgJ*R9dA@7%u6&@Q;s|N>TY60L2h}VE&KSvyiKTd^IGU%IiS+JQ#kqriVeIn|hR zGlh4!R*5)^7l9f}5~uyAHD>a0Aat4MuV37VW~wpfq_AU*K`XYw&RrD#;Xhdtje|xw zdG_g3Y^dhUn=+WjBi)EJWl)bPcJ$M|=aNYi1aDiECzMJ)K7N#77umnoRVf|=X~+hw z*vGuus5_$rM|cnqF@1B+QjGPK<4V7RBGXa+8_DhxoGAsoC}WW79e^u8^>2hRxeyPr z=t`D@43#_wn!U5s(k7549)JF^#hSrJpp!rjvBv)@S^D1@m>*}ng&0Rk4mSGatE`NnJ0qVHr;pw?B~x5!UlU9{n^vuzK5mV_k6qbo?OoMZ>U}yr z-Y=hIarOrGg!JJL0685D2UP(q$-{_VOhEgDu=l*#feLm>YW0r+!T|4;0K=64$1ngM zjP|J-wjF7Pg>-23w^d?@SWA*y>Y+g!I9=&|K0AWb6 znqS6)rG43^?vc^#e!DvC#-3Kss-EvD7Fkh z0;cx@A{roF_NdzP*l--&a)77WbBr0ff9r98E&< zI2u4O0gaAn9N;h-Lw~gDX$Z9P>>er^~OZ4>CP7;aXL?AHYaU6XX zHx&qwjRl$^CLl8ZSz7CWL$uKl*gHf*)DZ_M%9s%85be|+$<*nqkpaU5Kw-S=?JkxY zmO;I73&1iSz93G{!2%2Z%J zPpJxCf*-?PPgn*~B&|#Y)VTucA`n4iO_)>zcg2E0hJnB^CNn5NFV%!g1kfi*k$7oD z&QFPD^Y)3tK}x`XJ7)2gEi~z&VYPk&M<5VocTt%FdP`~-N5K4s!;ZHL#EG#Jr0qOB>>h76c~i#6>kza zF6p)kk5j>_290}|UbAl^aUkFj&+?$}C-9AFIYY*X6LT+{S!E1^H^r!31N;4D4({hf zaEvyvoh%XiSAI+ly}H0^w5N%Ly0f)2f+lpA>A#2 zJQJIe29F6&#{^p<1?C6d<6w-aP>~PIa)_A(nTNGYiE=&1jXW*aiIaf~vOJqP=<}0J%SqHD+bdJ?HCFiQzVA@%=u>tbENe&^w3Yv5w7i@YjkNoNsxR=H&B+f zd5My6)|ug4u@|L1v&WE1f=r9CK?%$-MpsqcJ6U9EGZd)4N2H;kpOvu8B8U$=u>Qvu zm8tyHYaP~v$RPm{0KWW^f>Sl0~Tygvx5PReCvz(^d?_Q=Mp$VuhIblXBsC)! z$ixKH_O-95s$q$}Bp;$Qj6&ZG(9)4;qId9DM_U|Yh)3{-8uy&ie3T^oS?od2%Eq{e zdsQwKk^NdKVTE6oZBM=Ik)&K=D15sh(G(%_U(I;iKEdm_ujHQne*@rlNn9C_lSl4h z*%#LT0Wl*q8j{f19>Go(Wqs1m^h^b|!7U3h<0Pv)jE04x)aMUwVHAdn!SyOp48b-+Wy#hpWRh@1`1n+M+`)hvP% zM1uZNF!`k@0Jmkwr?2iI27@Ui<;*@MFg1wK=?y=!Fo!>jUU|#d*`&#N|PkWrEY&s zNh#XCB*HRXxSPv+s9HX039w1)je#^)8ydeNo#|ExUs>1VKX8$|A>lNOAh=3Q-bp3! zXwXO_lw(%Sm# ziL10dP}9>J=8U7X1yzVIdUxLiJ`EO7KY_I0BW1dU*r6ZlBnlH?Ggs6aEi|M1r(>%y zqdl1$_+%1^*M<_HkU6fg!H)o$rZVJA;mSnDj{)pQwa>B_qi0upv!XSLJO3FR8#HMU zT-Hzs9U(M9cPYGLS0L6ci95Vw0;$JJNRa+abjVi^2dP%A3VQBy#cmb6!mUYX@ z1Bb(fpYA7$upmtWopK@n)R%yzjeaYI3mHg(D*F9ZI(izV68C*X==-}3BFtJ^K*FB< zL|}O{A1EalmpW+QibqDI`xtjFd8EDPTC{7Ix<)g#ISAz@9SZC~d z@1OEIgTT;M97lz;!F4}JFt8eGrIVcI?2$G7@aW}n4@heq^vu24t!M=r(`*nR9Bl(6 z%IXv9#&u00bHE_V0l_0CaTqRS;vy9b*oVlro~Bn+SWK*J*?Jm5~ zT5dw-;=N$N)k&lEXq%<&%RP36E0~99V*H%iDwBF~2#HQ@!kbfb5Oy_S`xCB+X{eU{ zyEF~B*J?KrLz~s)XuE0!NgRsR1mctvU_x|#^gB+X(ANf?Oz9hMi%{phaMGfNiQ}2I z8LRk8)b8oMb{Y#~%(rSi%dAU=2k^);WB|A?rJ2QvkaF z78+V!yso(*H4X5hpxHUr&>z(#r34mgGJ;&+iUJ=~^R)Zz*+An;VSmmI3SNaW*Vus} zpHgJ8@e%+KVsyVHG$|Uo4ie#ZYw)Iv;(SrqfX6z0e|(wF(XP=oJ4b++N+aBO-PGl3 z?0wl&!Wa6VqjeOiF{Ml*m13*LhQ14F=xDKz47BsEtgm>*DvDm*v>H&Y>a%8Np7eR# z!ala~=-{SEBr21f5}ORw;PSvo&jG{HP}T7JBmqj2%5TJ{L^03_Ry{A8Orqf5%GA?Fmy=HV0omCf#@ej@?wc3f{?CDiLVbr;CbSY1$Pz-V3kXB%|57AUN-_(rYG5Lmp6Ckh+nl3q*TUb>IdW%A1|@mjItJIP;olg*{= z2)@2?JQa{%Ut%C>pdlzymA2T^B#f!H+;x~Lm2ZT26V0=4uogz6L~&|(6(X(+ypE8r z`o5@6+H6n@4De&84#oN4o@rSrhX#;75EDM#`_x2Ers z**i1fD@B|MM8ci)crb^WT1RRH3&3*5cy>A!^wd3=7M~(uo`{CwE~qjv_WgN^2L;n~ zD3*=7Pc?b?fJFomy_Ly!?D~@q38g)77w!=-(l+Xe?2V(*hOkIwzZ&Y|z%2xRYF)4F z?b#`A^9+GOA5mvR;z(eLuU4@H5@)e!Mxkxsj1n<)(PmF?n1(1l4k3t;uw|tE))S1w zqwH`*N)uNxw!r~AM@)ttHf-RaQ-I=&D`{*Cb9GB#@A04j^5!am?%QF}{<2_`vlpDj zrhhPNSwln5G~MQ3Cbp_<&2QK*a3v2Xskmb8E$IoVKWOlv2n$#}hqO*R;f(XU%EGLo zhunhDS|MsIALKcEn!+S0bqCapF^ffQr0ADXn9G&bXdg(P(B1Tk0F?ztYVrk_kQ5s} zL)afg=ziv5ZG*PhSIbwCu7wg2v6_6r@y$qEeoZC&#YbYV^-^p=&+?LsmX4x5)2clE zHV>685n^v~k^17VTDj6Di^jGH!O!rMJMN7!%B2s|djNUBaz>BjS+Ql+l1`IRXrT^> zGSykQ@2VYbYE+2X6bVj|F=g1#2rJ_Fy4cW>BXbaysZ$hQ${n(Djjijg4qhrSP*B6E z+DnRl4~W+7cdH-U@XT#SZtsczw68bZx!u>GFJfjnL!$(Epw#`+tM}dVa61*wgX(c7GZ@WGBPbKF>hr z5ic5{`k4w2*UP=G;<&y)3f7R^NNbh@_}JP09jocCjwNVVf1vLW)q z^6BTee^(t_7$sa~Kpm#T9nH;TSP4Z0XN*n}v>g z8OEUkt^!dMK5&?=*slX?SFs@AMZ6h4@&`T|49`{zvX^d1qfnI&%YX$v8Z1tmodV0l zQh5R3ilotEAGH0cNDz@-3cA05^b6&Q=KD4zq+#QqMU9EA%#8*c1WaFLLJcB#-!CGz zD6&O9wS1r+euD@nBel%eGg@sMo7h&OVk#r%Wfo}+R$Z^;X)q-LbDZ8hxpRBF^D_PU z=ZHkj#sv}B8*4?BCwxtw$UIeU6Z=#emn<#t>mAvWk$t_SE3YMTlROh~rF<0;)=oh# zYEY^VEsj+FO{r(Jq9SnNC1QvmP&P){utHvZ!Kg759Dg<5vph^ezPV)$tG(!|>5Yw) zW$c3|eOm_MGE6Y&J9nl8@J8y38)4G5PCQZ7dP{9shi(nR8=-IdwyR_Yu9%^d!GQQY z3@slHXj0ZUu;vb+hQ~Fiib70sr{U@cwUIclTzs~jaKaH%J6**_x;>TsAZBWYTF}zr zQMVF(L}V_SgZBEB;1p9sa;{+>a(2uvvesNv2b-UwxS6xG;U-P?Rx-V7;E5*%d&9)f zM#xY~mptBe9E>X?4=2|$?L~uil?Q9VW%mZ+N}O_)@v(KT&o!=5P#w9_;;fTm76l~UEbBwc;xGIwik~ZX&rP-=ehbZm^O6Ggi=6(j zGi=ON#>5DR*Xs9hJXSD8;@n>8RhjIbNSHSHH+OzkzfyOjH)pAE z=Z)^6aM`U5#R!MnSKM0YIi~Jah~+j1c|e}^G>U-e-Eg4wWKh?;L%nNhw1IC&2a@)@ zD@-jz;qNO&IXW}Hq^?5I+|a~6r5(3b$-?}PxY-9f4!?mrkvRN7oHXtWv`*5~HA3;b z-t>PJc&Z<7#=pIpTXiLJg#6y(#i~`HJDvx~*ZlkD$E}szc#bMD>ojjiJqlx^#P{=tE96vN&x?N9ZiZJNA9bH*+m6o`FQTh0dY|2$&%dFf|SQ zRO}Stg;z0OVutRs@Mdbl31K6<&iy?r1US5LS@>-0;|Vr4Qo3BkTU&9>$fk{xJ#%g? zj9JtRs~bc5=(BQ9Zu;nL5#x&tH;O<@LSI`zGH@ z$+bK%uQw&NdCdo%L&om`oJ`2gO_6ivG4X71Wdhxqe2u43b~ga&r~>bI zW3G>{MV;F44i?x7EphIdZS%3ggN$N>Un?95G*&tGrMLRFO3f)LeHJwUtYX{_{i1Ot zKC7{|1WB+n(;koWKQG-Io7r|0%-%NF8njn?+vHh>I1qP~hpPUW5r{=9`h41PCA1Op z-IZsoND|JEhuDO(VkcYCwS^9+fay`L)hpJ(=ySKhE5tNjrfaq1&St)!?hgD5aQO=!~U!EYM7ByfoKnSA3u_GaHGG;ErQV zU8JM+Rf9=!U+G2F?$BXAv3E2T<^EFs=r$s~O6C}yPdg}J9MyPXuGy;aKw&nvAwIm# z1P2*lf`{l=o3}bcZ8vEx^Npk~{$WNQ zU`D3RMpb*97MUVrMy$?^d`vn^B5cq>x`~R~;`4MkCGyB)jI6Q+b!2z;q&|GIV5gci zL#MGrcSr4V=`sQ)R=vRyxkxMZ@qIUIoLWU2;j!^>`fm&-M-DnrA3E}MVqSl}qa!gb zFB~n>@xn6I8X1|Xd6A~p#OVSsV~4W}@z|I!tNjvPu}>z)wCf( za};=R`hps{q??a%4`pwnrlOL1Rg7A#Krgb=9@tfWu8r`f%1V-eMPt^e*J-sMSp{Qe z<{<)!&&2*z{s<~^X|PaG6mkTo{1++ZRa+HV6y_*f5ge4INgVFN(cb|wl_pE0`NhUa zA|xYZ$ke?|1z!VXsxa5}kV(y#=$b8xbn@leh_C;Pv3Cj*EoicZ+qP}nwr$(CZQI?a zZQI6a+qO^J*6nZpe8h^z$1)FN<-gejT4>5|2S%nIO zDI4Rtfc9uU{!naCc&ac%*@1g#4-W*M#Rp ziTA=172-O5465HLFC@;N7AQkCkTL3Dh%?{-f0@q|?r-Ub6jOvzTPViZRAQ=c{Z08? zMJ1-kG6p>+_>aGYxniSkDcVpUIQy!WJ#T(i&iq@5sbvWC6w_QcI${9rOPpLhfzHaev(gf_9@2 zrxc?YResZLv1Jj)qW?JOsx_<(<6Ii0noTukvzbivPo>e3XcTHnFv?-|JLa#kIEXjY zL~v7~;V8>M51>%9F+%1t|7Z2)swl)L#Q$Sb$SaHGTfF}{PyCb&x(&!dEp{tqi*R2) z^%|0clzP3C3`lztgek+Mq0s-k zx4-E>ls={@w;rg6Bz;V-={lGSpQqw{DlrO&g#THA1|QQ`wIRUd;Y62x|8D7Jy~$aUlH(P{+!)kCAX)NABT_6KKP&S&!@@byS9H; zK8~@v;-I)($3ic!7iXV@{D=crsA1q!%T$ZJFoZmjWlM2+rNTh(@)JXoR1tW)NqkP_dP_s|H+_%R<>oy1|Q9^ilbEZ2TKKVLxVr`fKjv*@foK@W?=*+0&aLFetrjHl_+CII=tT>jlW0BhDVYBOwn zkLemng9;1TyTeIUN)sCm7vZmSb;~{xu7DH@r+s<{B&UZ)e%}++ADsR!3wicK;a05I zpJn^T)e;-Q)=PKPpzmd){@FpDSeQb;T5G+fGjE6M`cn!aMxXdNSkwjo`;zhBf`Uv8 ztpAhj=lI{X8yx>{wHv(SwK%`Yes-9=6JRXo^Obh#4tg}6fTKiP%=><0#9P7VKra+Y z&AJXr6j9$Vks~a{)MfiKKW09Nkg=KCsj;=TDXX5HC`mieZ^|D%Tz35LW&1qeHqRc& z8JnIvGo?*qEDdT0haN?}S#i^Yr<1o6`%QH0j$wZL+<8|fkA_slZ?A`^ld};Sw-38y zw%zY1cx=msShB$kakx=Q=Z}{TbEdQTAS;j=wr&b?)WE5pvo^Og+q!t zo^yeke1TXSfnW_lEErHMR&XpL&`dF0ixXgm%O|mu+2g#b!{=mi0u=1MEXrSz?rjsr z@tQ`ndqg%xLnf<_f9>l%>-_KJQVRCl#g4T(A&q=C@MG8F@@(9H%n=;I*~HJ8&deB~ z%zfT19{6uhzblDqT(r?cX=7R|<7zJ`wEFKmAqc5-#VmC_!KhvU|840MIE1S2AP|aa zmN>$#lqTPz4Cn-L|%Lm@bV`kS<*ysK%{%7h>MwiTbGdukAT@Mx@=m{ zE}pAWMi@jqk$GRPcq39W@Wfqs%$!kqz!y7`4K!A9NgdP%?T=H?vqmFkUqIXix% z)5Jl;a6Y5#qJHlJFRXvUBYh??4!_@1plCl(t!v?rnq;xgw4R<6t2<33NaO%{#V?^J z0u3Iv$DL-=HJzu|5ytN2K9{SD)>x~(QEnv6(0^w23Wlyfezq}*^O%(ZsV6q`_tv>bS`Itji zdJ@OMBXGYHn!F;6dc2aaL4!*c2|$<$2`BugK4CEgG+FOAo_B3n(lhQeeZxfX-K0mx*Q==o- zk$otnJM8Y|!~ZzyluGB=$Y+rE!6X*Vn`+>qYtT~|uF!Ztawg>K!-&OdA0s*8N!P$+ zm#G9*k(qqQCMgXzysi+axQ^7m29`)L^u$qMEK}Mg8DJ*VQwUnQI12WOyK$xYCBfJL zkrmrB=@g%wy`RW3S?wlz7=tIE4q9}Kpf9U7;!l|8CDc`5TequOoWf6YY;wLpeF$IC zJb~YQSp2R(SkKP@PeK?xZy)j!L@hH~#^1p{I3_VGHd#^mtwoWMvizVx%9)t8t1s2e zH7WK`urB1l4S#i-SgASdO`x-r%}3$ss^7}YK3qAXX)AsHY-Sa;lil0JXX|p%dEs7x zuH-q25JiB~Aol1s=tQ(ggKrJ1FcN;JL%9*^2{!8x3y zT&!w1`*j5REHr%77Z=Z6zQX6+yqsqjRW@3C1^A0I3b|3j-t0zX&p}>7TI^JBAw!IzDv5h?2D7$&%UZ&eKak=S{) z_4)QGeI3gE$r1pgV)R38QZg<`=gGT)hImjp7RB-LmELklKHkZqs#r)V*+$-{z}jkJ z?US?yWR1JkR8H#3|I1jWdw>E?IAD`v>K0?VgK1^6*z9Dppwz`?z%428o#M<@kyvLcfxdKAq-_#ceX_q zy8Jb#T2AA|03_6l_r(r3&E-$j)uKto#e4R!WygT0Y$~~7f_QG^gNI4UDe**_LXiPB zy2M$4baK(rGvTI&=TDP3gP4PzFwaQv1dQLUj(f_USk~Fsi9k=$m^$pW9E;ydJ7HI+3uQO_So;(gW3V7_% zjo}i~73W{esA6-okC?RC=13$siUwWD{!=p;mVArYrE!XLfpb}$>SPB!j?2l`<2*s% z>MxO`Q6x}GTNII`Z!X5#mT)9myf?Xh^Wt^C!`=5`R(G4c!@Ie;y~(?0854;X0bq!z#Exl34xPORetC=nuI%1fjMh~pX_NdX!ZSgtWP**n}FX<2yLNJJJEn+tC zN@#7nt0%}r6JzT-e*nzbp(zIw0ywW&Nfkvd197tCQ6c-Ano&pC7UV?8L;lRG1H5%W z5AoHp{G(R@71+yFW(t=YKXA4Ad9A@G_2ri7vi9PThaQOEZ#_?sbcV_{iXib#?*b0R z^qwes>|a8dyyBJ>lV-l@zyn+pB+pz96mGsNsAri7C;;k_LN;DTSvij=%wJVcs|1-e z^Fk|1`CCTOTr7?3W6bVoX-}-tdL{MNpy!f|AOT;W^QTP?{A_3oSQE^O!Y( z_n&YeT;U+Cj9pks4yiW~7{|KB5MuXurvcE5qHY$b9G;ZE;06)sMQpn0g<(O!0*Z;S zRm+9j*jc&V1B417DZ2(kxlPT5C1mtC{-KNfzACqfTL?&yLclkQM^x|QAm>Od29{GEqa$v@I8z*|xQz>r&@E+E+<4tve(0-&M}`+2tO5@d=k`u=6eATZ?W>apJ2bff$E+8C5*X{~r1_?lGEvo`q3<+DR zoTD$TmI{DA$||Y3tA}yHW|eh|k+>{01&R+HP8pu4x$NcH6t1`w z)+X;<4wDd`Zycl(s0JzQhfZ}2g9s8+A@m;wAq>Yu%*mjpxDlhs{25nsP-(xh4s;n> z!wwC`&x?`4AvH6sP#aR!S#%Ia>7y4Ghu;^l#wyV}NH;2baT}6`9Ivxi`NV8Z*f8Vo zmVkqOc<|p%q@~&J;`FF&Jg!2Y&ItQVPo8ru1sTp&YuOw9BHRuHDksiBh+@&}#~nN( zv66W(F&_1AM&Ja(889tK+Janv`A6HM#G2upfX2D?^5Z}9M2=9;AQ;EkGTv?%YN`Ck zciKcL<8K?Z>vMxHZPN8G{%zxFDBMhrS2XMR{0w%6K^j|11cH;#Ckursg9G|zKG}j< zV!B9Xey?>W4Me-UCM-XX2=BF~Zbd2RWQ{^te%{{2iE6vZlunwAoID6ClGy3x*`x0u zUM$0KFdXBbuY)o(xDOP2E_tGiafSBq*#Up_pq{;bxcpE$5klz$c7Q&Q?5i^eHiy{0^l` zik)Yk6ZM?Ts`KKkw%S%eRT;&-eAP);;h{nUn-UAwj2{6pX6<$4vWx7G&=XN8Zms#b zae~N=CY&+200ZWiG`(q)fah)Aei;Yk_=~u+jKP-`EVi z*LTv#li-kqKX-f`1sFVQ`Cz)>@K7rN-jGKY<)|t9gi%X}K+4_*d(PEUgtCu+Qt^@n zC{Rf)ic~SQ;44;C6}kbf`)|Bq-l6bu*u`GJ}NV$=-i z;9xX|jz|~5oBwpKy4&us|AFTsQEZI_2d>2d0fk$z5@A`e%{>vNL$@)V&*D8|pQ+Rd z2cwCm`p)3uwMS0=2t~nAJ7pc=g5oWzXw+JrmI;O4 z*8;@Cc@$>8fOG%3E(4`Wm?M2&o3q`abO0@_+B}!Y((Wf!0hpqinJFHK9+5_9i^q*= zxXeW`7$6hFbKQlaGeFw%XCUsX+&K&nF`iTn%npB`1`?Bjbb!OW08Ra#06 zVzeZr>Ci}|Zkdq!_^)w{?trE7wcbW`z^8?p+EmmN_X@MVVe5H6DC~Gm%MUMgW}@6) zpwb_X`LtKS*>*uf?X*DV?;ub3i1Fq<48|=31F+&qX^)NUeHDIKN0Ki3F1dep7PCa8 zk$Q=KmKD%@%xmnlzFJo3;J6<+#LA<1Oxy0hyD6k&FqO}tbG{JGi3N@Hi4ywhwYK?G~uq7M}9jwy0c5s!@~$CBdbVO67W}Y zCb>9qHP2x(~*{Dv$&Q3eaM!H_1SyJ#%VZv^~-fk zj0a1Xz$oA{=nxf7`VpOF_-{qY+kuRC64waYQ)K!N!O2Pd86Sn<69U4tYB)^_dL~BP z`>3B^uToEO4CbXMxu^&!@<5Ku$7(HVivps(TL^r;u#Lkq^1l5SL@PA}>Z7V^5a;E( z<#WG&GC2F){q;S)S@#<)u%-TO5P#l}==*~G`9A8d=-Kl8T16!)tdKn-@DbvMpU{I3 zS<+zLgP6-`O5b1cFt*Ps0hZ|2%yzAUQ>}YxZ|{D^=y19V@FTPM+?cf<8iWinIDJcN zaZXg&b1Uhw#4H}^Ev6bDd7NrS7a6|J;>`cDXLt3SpRV`dvRrujN*5|UktSdy@O|Dk z$B4c;RkPXTOhp9q3i30-W8G}{ ziFGps5OLwu_4S=eN58i_=78d}Ry3he@;O5Sde86%=jKV!5P;`q6dlL*&mcL^f!;nU zKnf*70 zwT`(0D?Vd>K2{iD+GHO(P(l1CD(RzXbpn)kg)7|^q6h+3DFEpHD>&$aN)mtDpufpf2o zaaVgXral`pcWhahCrN3harNe&5=AY1aIsmGZmB7weah%0lAuEiQ!6M$5^h{)=ea_? z$p}8xKaun!<2nmA?&c(t(9b;YzI-AsdUE_Ey<38t44qTl+{3b`gFk`zZ@6CaR;r5?4?{rz5 zbZP*<=vJt08Yr+&LSp%gB>d;s!wor5BBdoGr}(;6gYup5c|?f1Ahj3}JYbnKvnz^Uo0DmyMvLTez~ zK+~eXuu~JD4oYW?Co88_>53aymhOdxbiQTWDoY%vPN*%SD-)Y4OBIjFKKv)Fizt`* zbZcXDTZrV!nv+4IpdfG0ZNbslC>1*VU5`;?g+X|`j=roADo44u0K19yM($iuP6=6< zM`B|VH|Ffxg1aTLO;u`#FGMkQ_N-ekJrOQh`rAybQXiPkepVswxq{l3Bg%ta?jdKX zIciFFeU~YbV?s|+PRW=<2H4d@c5+pJ zVD|@FTrMArbmjwZ!S#BmY>=z)cjb(9*LMa0Xl^`^xhb=T@(zKmIR(m{YWxFLu*vQ1 z^vlD~bVZbEwt9RO=~VJ|dv7n6KWOM4dIjl6#%GFnh4_p6Di{sF?K8lw2T5kBJ_&yl z6X$t(Hqw5LzWw?;ibOP4JDT9z@^QsdRz8B8ZoPW{(gtCooupRMRFhd>!HB3i^sZ0q zI7ntslPs~ieH0d?eT=m;RE7-FqE(S3YrIR0<<+!5y>oUycuxKyO z+9Y17LEJZtXwYE(`rRvx&e@x%AId*bL^jp56gMvg!Zyi2yXnwHU-ArR^hBrq(k>AG zORx(iY+>!KI;f~Ej_OsLS6zkga7@A+F{oVwVVc=Yvj=(JO4eEHe*{3Lt2glhybkjQ zYDe@HULb7V@xY{` zJ95Gsr@BYg>tV!@dJGmz1{z;QE*O)zCd%D_M8eh2`f(<^Tf<^L5m*0yGNT_|#`d^c zT99jKdrI##Nqv0ltXj9#45S!w6`l7Cm&~t(vB%S|Fi;H(3wr-RC1~gJF}S4d^E{xH zuE)c36ot5e4w1Q_=N zI|=hHO&6hypU_t$O8!_vP{b?PE+gaEnw;r%A#~V`(zl?8cHo5myN=HVqxhf#99HcE z6jhwgHXup<`HLXp_6$<~hw6+d`%^R!Q2rj*hn7~u)BYj>j?hfQ&q0aByX^w4PgfC+ z{_~TUVZl)Z_1Q2;syff3=93}FtnZznYcSaap`z|GBgIQ~zlQ4?F^u1-zP(wQ#mi+H zJ8JJldA$quX`0|(4KyX_vL@>O?@E1C1ahS3Qej#T+x3hXTyS<&X&!X|3buXh`(;%| zPI_MYo_giOvg(QrW#6FT&C1T_@mT4Gy^V-pE*DSsUf;)@JGLjh*E@tZNJIe1e z`LA5be}Efo%$)xN+~D}%zzvT7C*a1Fj$|^9IBGBaH==V9cGqMFJs6yu0RaTZT5i@= zuvihSbyINz?5AgMy7aNq`f@aOC?rIvP)%+1RJ#4w4%5RIYt}1k5&zk4IDIYO_kte3 zr}uE}Pe`A9ZExR!;lOVd)_(NGZ`?BCm$=&V4Y{Hg3WUJPM<~z+1TyDVdoWfMJ1~&KCI&0;c}oMnAPY3 z%e!4tl5Q}Ie$vqW_)tde=@nAX#OPiRwpE#HS6@Qax05*PxVU+Wx`ko+A^l1>58W+b zb;jt5zm=N}@^&I?+CCB$EruC*WLc)2ezWvfyeu$zN?vRH9qd6)UdxSV)6e@MoqAt@ zikP&$BY2IUVitcU-2*`n#4xiRZh`!?I^~$HJvfC1qMOUZ4~Dlf*KB{r{wJE8z_wn3LfO{1SBSwUjc|hn? z!#}l5+4|~B6d@e@m(%OY7>XLEhqeUG%Wp*7u3l@KSu> zxMEwk?ERu{JWdVXz(x-*yo%C`c=K#ox2K&CAb$CfY|m4V^XvZMo=WUrPd=c#@IvZh zT2Y7Jq>$;~TudRuRX7}Ez0|wi&q!g+35U$~8gE?3kmk{jn+1I_e15sDIC#l&D$o-a z=BENJ$Pl48Q&=qXT30?xYFm`K7sEgW-D+n2v--LK@*xk+?IYjmpJYJ41f3;Etp=5o z9Kt5w=dQA1o`cYp{II>1cR(L-3nROW$Q53%j^f*3X4`aeVCJsSRX9TO^^3}+u=rW6 z8I=ECVPI}9r4v#`58z1|t~EHAM&sl1K;hC2Br`vErgEk?LlJH^?}QXvkD(|R$e8My zm_}wy6{s9rP3%~kS1ZlMtX5e-z9_iP!7mJzk4$9?nX&sJtEG?=!^;=!d_P2Jr%>C} zT(|xS42_E8MFEktj8^kgurZ8dQ{6*mV~#x>u+`MLaCN)-1ODJ^+@Gm>+8PF>U>XZ- zWn3*2@EI~W7n-|`eqhdJj$N_hyA+Z>RAWHTC60bjVNO_Hf({vy$Uw#J&7B~v_+^nI z6g58xrgOHkBS1UcLDjj4eZ1(4TM|H9tPuNRZzQ+Qxyeh;PMK9s)&r~M*;mi5lz_Gn zNIh};{#G?aZLWn{8=HeN1g(LpOTCqlra#gDcPG20K#PkRwLr9sA$f@pTa2*v9s?En zzBkexHIrs9Y-;WdfibETdF{S%`jgV z(ZtG1CJlq8qoyA*=c}7JVyu)ch{na;06}DW5du8*Ire%Qqao?Isu-i6X};joYBFeC zLK#;bh2I;qR&38!O26dRKJ)g5%-Q#0wJK}Zr{@hNoWhcIowO+D4qpMUo&zRC7VK_F z!4ECh%l_{`#CfLOXwXb~zbqy1>o_1G1Qx&$qnyi}_mn0#5E0x5a+oaF>zUe8K}}=j z$V7U7vGFf zZ%7C@ektoXvDu(TvgsB_)2o=wa)4IGW;`!<3Ea@kc+EK4FGiuh#!2d0Mp4dPf)kcy zcvhQWe9O=lLIgj_1R^Wb5YL7PM}i&ng<17#&C?0abbCaGF;DjG1_%_Ap^Pk z4V;r@2APUG5m^k6{SWTCaBV7QhgnFV=)z;a5nJC7A$@1Q+_J!;olY5@rzIhWC$@n| zlBBHYu{MM(yIYJ8?7Dw=q;%SH`YJ0+HY^=?ns?iMzKRy}ZB>wwUex#2?x6bvdD?Jvy zRBcd!2nm8=#hxxOO6IVhZbR}2yjb{7=$JLp`HfYO0NU*}qO+O&T!aZExuhOdkT$L@ zRS?RA!QHiWz)!&Ry}c4?Qm$j;nGE-?l6XA2js65g?by&kxJQM^_{qw!7s_RWY}j6! zC8UAuBe73L^nIH$5Z?!RD z2S6@>qE%F?3WUglOb@=qEr*Ey3es*Lc^RMKlXGjuQP3y!z#mggd1)_Q%Er9~L9_`EBzSe%1^!u%I{ZYl(++Ya zd7fL=Cc%n)_R>tw+j>n#O>Y2CRAck$EOjodNY6wrMntiik^bG9qr*s?BsCLydI?k@ zc4FP2Fh_cNb>fKL$3o`glEM>09$gIxwVc@SeSHk-yy3Pxtb78Dn|o9~&-hkny<1}I za;oE+4&0(evXTk`lMU&m!C|Ut-9|O12U$4SPY_#d%Zs$YQf3M$RtV4NyEz?M2ti?W ztB7;tM)!0&RKKpXLj0-!p?o@PMc$6}+lRCD?8GdO95#%9ibmQ>_}g=4Zq3pFD=LNT z#onOl)G-rV02B+8gY9Z^8a8N{A~&w&i=CQj-R%9=YL8$!PDf=nPq3gh?Yh>d^Q?Hn z$>A$?=AqI`U(%{%g3ID|QZal}1t;60Z=O>E%~FegZQm762Zf5I|BS4t;x8HN#L;Qi zZ@`(hC@<;Cn4nltnlcZNlbvV3hvR-=UO~9~i+S}d{(5~J?Bt|ymQk4ayl^BIKS5S??C`l?j1|_zmpy3idg2w?P zXnc?1n-3oOB78X293O)GU-&y4zDIWNa7TE~LBs0ndfFDoDw%xzY3~J&5mb~HLkNff zy3nxnlJN=@yyqf71zvyadS zX0P&~#Hz{RT?To<8s76%i7j(^bE+@^hLSRqf?(<$vmw+RyAwHHySx@s499=LX^j{%22@+2!-wXF@3b zr1|-<@K0F!T|_(u^VQyJ#Umld8K^J08@@jbfwHr)((fH>$o@KVh^y*IXSF89&{Rg@ zc_LckuR$7!*OxB4*(NL8hNnlWKSc}arhIo!;^sU9C-ic!%=7lvE+&*1+bK`cJ{ySX zmSYazbr9gm%a-Ef_OwWkRLim9x3DrO*H8X%^=E^A^iUGw90$UCiCyheE*4k%AMCTy z@xRSuKcnMm$`Y8o#Mm-9kc%K}nPf#XBG_wVF(v3W8q7ne!j4|7?enwz?%$^CarUqU z7q)r4G7jZkQ__~Wd^aaI(a?>llEAvNl2dN(<+k#s4d+(yl5dYVEz`YL71VH!K@E=l zy$KQ=OS+A-_RAIoM;T^)jj?Za;%h5!xRPFd$Gx84@DN9@ph!Y;xys5D>!!6>u?z6$y$mF(|94fJwLeCBnmWB89_o|1?s^ z*=&(fb%tOZK!Ulw8CNYoZem)j+^GDt#Tou3Xt@-|u1K=R_883 z?Hm7WF?`CduD>_i2bdWv`u@Lv{r?+I$;`>g@!#@Yoc|k6$@%{Tr`+b9j3XI!42!oO zAlLW7#$cGUZJ12Gk-^}TXth{NkeJNO`Zw6yUiEYtjJ8VjvxE2+GZj@?yOWElG-TA! zqpF#zn0OaAHfC>kBDpp7O8)cldG%X+NALb{9xs3LD<;@im+SK&G6cx}mUn7z@8<~} zoYIbH1T`|2wON-}Ts9n=%r3VsIXK)V@8_>D>ZwG?aRtAqC?n-bxOX54Q9KE~AA_3{d>43=Yh& zT(ye_XlI`zoX;X-Y7R01z=0J8K{<&)34p=Ld5QvH3=s8-#Jvd;u7m=2NFM4i#ok5_ zU@s5oby%~&1xd-)*5BMS_ zCwAd>MOzyl`m_)Z|5OF%e*8Qc1;tda8*=QB+^}FO!>NjwD21=L=OZ8>CDMHGs3kih z$@K23otf^SzX5><=Rbd)kcWEv3}t6&KoKrhFujD3tR@!S7{iN3-RKYJfSIpZ2Ct7h zQN5)F#R>%)Cd&w(!Xx|Rrk6uYl zag((uG~Nm!@DN=GN+fewO@EO?PtON;apVP*>Q4rg);arqgW3RAQj5_MCra9f^F&Gk zj9?U^Chmo2qxc@4%LR6V%lpe5Nn@KuDvwH*1c~mb4G|Xx^WvBQkYqlJiAL}K$cVlA zYP}6&lW2<=2Y5ObEZdN)dnO`yYKj#6TZTQ0d|b2m@y#ZiLDNq)f z08CKWK%h_n3vB-*7}&QLkw~$9PlpWm6U59xA3)-lOQzhf^{QM!ah&O-=l2v8i@^)F zNM-D3l#iJ`8A`y>qJ$u;_YzlNc1`9EyNY$D3jAlnRZzSA?hDbh1%T|euPDkMnFqcb z&r$myZbGe1N^lOS!qG}st~sXbJ5N(s7M1xUb2VcdEHc!9dF(YG+rHj#VSdOPAGAz@ z1yFUQ>;8A=w&lTxrZ;by)<#Q+^o3|LQYD2e&L590sn<6YPC5?-^RXec2zZxpK#1?& zB%KrFA^AF@#?D|+AWpR%tN#92_r_6_htZcquO zyeoSJX7^z~vJ>}~3Vs@~5^XdUYR1vzSULdwE$KOi)d!mWKg?kr%Mn?Rq!5)Y8)~9~ zLr&}#d;)T$L-5)*t4D&##o0S5K&f@tHHJ4t^gI`?IGS~-me*c@hk`Fasbc`e0OeRq zhGkKPa)_$Q- z|E_pcV0VBOy<9|^{sdMX(Q3)6oZ9hCW#l_{pxmLF7sl|nHmoPzvc|UO<0Aqr$;@J)m6otGWP(<>Gi%}qHE=C7~D~Q_XON&=elkR@Wqm&4)Sv6I^ z+h(QF5*h6wsU)M-khPVPVe)F^11y>lSQ;t2O3~}(h`M6TP5JDu;cLb>z2!1``9Y~)r<-3ry-4Sugw@QT8{{`9V;%D*Q0c+iytuXBd!v30@|9;T ziQ=me0G-;~B(xF)*qf!%cXe=_=mO=;M@^revRH_NiOK#|QA#1kn&`2@{QSkYhDAbX zn`qt9S-~@a62%0M&diZw3Zj5%@diprji|sp%eU?Ww;aix6W_e#sx(y7+px zm<>z-lok>7F_Sn3VcBE7=6uqeq`@akxsjWm9_>qND5U^&)rzG^^*i6nF-DYtQmRun7w?f|MWwzIX?IIKuFT>fUgBW4!;Q<$*8g&r z%0OD)FIf5l!zE^kmd0`}a5eg2eZyux;ptSN-x@#1ksmR1U_4jHKhYr|0Au<%VGY_Z zqz{~z?*TUVIsiHNPxudTxJvv8VIY(o+-eU+Cj8B(#ab$&|47M-mwcR8JIPuiHv~majj63>#&)tjjeZc`Sl3Rx3j!^5`b2Mnse+J92f3 zJ2QD7Ydk@17(}4o6C9%M2%lUraqw{9fE6Lw%S<#(39OEkk??Pt_>nj<1_{Jo(_nfK z#}sdz1vIGEYqZ7VqU%?-*64n;! zO8BK{Bx*wPy3(QqAmu1zw6Nb3D(SxBm(CPw=|;`b=WuwfJOC?d5?zS@nOloT|4X=f z|6#`(5#B&ixPL(l##6flh*pKGQjf@mHdOuR$G!+faxG3yWHZ6G)Dx}w2>=Dg!S zxx*x@UO4A#BcU1)=drl@y?1b6R{n!(ZXZ1QD9(^UdxR<>ENr;GepV!<>D<^c&)uNl z;#}vy!%}G*^p&wvNYZC^r^m2F3~IYN65BHYaSqbwW;-JuE3Clfm%OJ#7cjCRBx(=! z5=Hp(XhsBprEKwS?<*iX|nKq@I( zFN(LTR*kdNHEyiL_?oPexepEP2D}1!L=Pnbmeg$Y{!|TbFkdihZ)-$%kY4a0 zv?^Wsk8I&vJ%4V51MR!h-mT!5vSs* zH_gnAuh}nDR6&}Ca?0(8*?dhRDj=0@Law5PZv$QWA`r;u8nW|oxE8#I)b zw7jay${myNP)TW=WGe&qe7zf$!X4#No+y)Z3Mj~`UB*M`)*N_Mk9+8ZNepVco)z+- zwXkJcacWLooeKyQd0N&|^=wE(hgb=`A}eD^$~Zy&6*Chx?pO~g`X7=el_ty6J2@+% zTO=6i&Y`0Tz+4AYf$35ZjsGQBjN-QRc?paLXx0*oTIbnqnlvxhL%K20)DQ8hUwTSH zMRoD(wLB8`)G7PdJHvdWV0Q%8nROoe{Wx|ulPp+aP; zm27uBuXifu!sn!JOKEM(yMAd>I~GNtsc8}{!h2sCow3{FDJ3(D;(<0m?Y7w(!y~gs zTv7n#+i#7MIbm=~*N*tna6e)`{2S~#zf6(M;StP7Z}KpQRf9E627vMK4t?&8;p;-O zi9FP>>a;vPJ8R7BrJF9CMFo1&{6hho5}hir%~o!=dROmk+STa4MzZXS#dDTi3F5C*! zpozjoWCJxFG8`75v0-v(PmRVn(A)||B>mX$Gb)WYtbVO=M@mxcWoZ3xjKjG2t~cw{ z@`}I07f7C%ZMx)G1Cigg-a8}+{O(U)=!nxWj(|f z1$^Hd)4UhMG!Z)gt@z&B!V{||kS@G^be>Mzdq`H-uAa6JL`QhrIUcgM9|+N{LFbQn zg&|&^Hv0u6MhicZtsNSauU9bG*0vzjB(f{Gm>XO#g<`81q&joq?Go_e7{P42S+4b^ z85TWF7q`p?=4qqdPc4R8nA3@CHTS7<&LLb|62FG6kz^^U26OZ0utt za;ir2qhHP?A_hux+1&*ar$iVkzQv#lW9SB*Ms#kXiW)Yj602a4ZT9Gg1Fl)KBxm*V z%@$BfVabogPevn6riBvZ`UKYzEvQnZ2I+proC}E@pEK69EaoKc*bnsK$(~CO$7@Tj88`Si&_Nj&G_3LOhnbODj0}dv1*&X>|pF;ZS6q`0Xk&vPUMLp8ok*jQBYhEQt zeixL^N$pIml5}$k)HN$OzP7vWUDzSrg1Xz!Ui81Xo0=}I_KeCCYgV|{>0+$UUfUZq z!M1$rSP#vf7dEW4WUZQfuVhulC10Lc4f$fK8cKU^mt~5DZ083RFQ0fx$F@$1WETSa z;>LaHLwWeY00u4G4o@G^rO0;!8@!-;EKSGt7TO4cP5$xc*soCh*0a}J_rBoSj=(zb zfA@wfw{Df++Mj|LH6r!az|SlMgyHC>@$0=wcz~!MPdtKL z0pX0PNp&uD>C`V0dIz4EcEE?HbvwJVvvj205F3(Dr1T+?z3~)};JJBa+HRf|-AXYw zNqyX@+vdyhSK}YNkl_YtaKR%dK+U&_wYA@`nm43(u5T0C(iM#tHTQ-cDaY~BHITL0 z18cDGMdBMca=7mj-h?pI*ogHh8lqO2@j>~)4{#uzm`k59)D~WQWOy7%igxOgIx-3& z6<_!AfvC+PAg_qSTNH>_jgr>@q8+TSqq9!sfGEe9UL<-AZRdfp@G+gr)?985>)xcq zTZS`NeV4sgdB*Nfc%`q+)G%n$ah5P%Rt^^Eb~3rN-tV7QjLBHSAuQz&vV(w*Cp=c3_9P_xKD{Cp}Li!Yp+{J;Qf>j?n`c<24N z|JJBcE3&d;MrCH?%o%M%oxGx1Pjh1l8sdMWd{8Ao-myWY^q=VaR%+q~!|x8(+S4;B zP9DAUp}!Do&plnjm*5hIFvfRiX$x|mMzN>i_SZy@67Wz%ZOk%)UoPNzk@x)gHk{t! zaDUyV3_qHs`k6f^Yl5EEszqEr*NJX8LzP@&PNaOrm#yb)Feu5qzq~c8-EBPI!dt@9 zxR`};X10>MEwd4W5vs8A90S~R8M%}@@ZiA>OE zFf{KLY+SV@wK5k7^j1xm0|bnZ-5sl_f!y@b^d{z6EB>{&gND)fD9nU#OZuR7HZI4v z9kxF}VnrrX30?J|uERI% zERba0e$`=Jo(%#^;U5h9KA?8k1mhGtUPqt0lOCKEGm=Sl`@&kS@PzUW^blGsxGu@! zoYY(#3_4|sjJ78(RbsaDxl<1d;1u1O71pwk5Fl5V(Z$qW6CZN{9iM?^7k_q{f_C;2j{4WTA?|H#v-k3Ovw)j$M0xPUg_!5 zy#i}1xP3I*_!+CSWczNs+0rf}muy`mvnp`o0k z4ZljKGtXKl{a`eN)(@exEt8%ivVo7P>tDc*7>Puy2lSVg`NO>hS-e5MMicq;fVWan ze<9d#oxoj5+aeyx!_f?fr?5HC{M50E1(oB)E((=DH?iK!`W%!(3~a~%n=W0Yx7g8+ zO)e?LK4YV03M;?Wq3Y+FbgG`@gxz!CFF z6UVolnUF=E$ya<*4Q;_U=f1xR&u-Vn%ssn`HLe1U&YbM9#)}Q_WCVMNZ_-Y2&LC=I zmo{#PW$_q$s`1t)cXx7G-I9fD-?x0M!nQ6_YM#LG(0Xd*6oH$+8QaYCMN7{^DkVn| z`8azTwb`Bi25MtZr1-ZTfPZE7|3&_zWB%`AnC$;f{$u}Nk^dSsMx!@a;6dy!s%PYI zayU0qumgxe?d!S!zzW0eTV3QB_3r}%-aY1yl;w#_xNybO)p%$pD0vPJXJyhxv5gBq z@ZP)@aen+1P=V8d(}9&&vtx;3l>RF)PoTW@@M2HbJFPe3!|uuUvE|7zQuOWSiJoRr zEST^0c740K``Mdmbw5qOXr|4@NuK}KvE-I=H->-Lop^5~Zr>yWSyv;!WPG7S@krt& zz}}-DOVoCuJs##kBRFMgNxwvBT!DkR&&nKveq591Vw0EECS&YnjL^Gs>JNgk&zcHE zGZ5MdJ3j{an}dVZ5U!oWGKUq6 zNWQFCQ7Hy~;;7S)8oF6&cJ=rEFT- z4-PLBlDJl*l${8Zy$z~~?-@919S{eS?8Ti&L#9$zqJPM9k^d2>Dyks+M*|93~0<)nUPjzduE) z;8v1uRf`8rwBr$@2@UpL+uB&B&CJHg$^Xz-g1-2-xCe0q?@gi@RkR=ywa<7@B#zMLrRa+oWK1pr6kZ@NAA&hft_A)z~&_SM- zx&5Pj%1U<#u%&k}wmK#HLYsP+@XURW)(%itQczhwiqfGLq0>Q=D z4ddsI8w(Mw;mI*5?sW&WsKTWz+|q$fYP#HGt@M&}DB4NtHiJ>uG1`Cw2Y@I=bVMe8+CR|MD3vsp@Q3U^b*R zE$M&zEWGu{XN=UYEdEr=wnqAdDih>Y=tVk8&{IAZ9l}@Ts(SzFT~+0aQrwW@X(JL< zne6j1_|7tXHSW=t%hK}N=u6=QI!o4YbQdK@Oz3vHhVCw`K_u63v<}^l4`h!JNcLy#=cnMQij1#rr}Fjfg)|Y(fAvI_3(&o87{-QU3;gydqx%x&@3Mf=Uii7Y(NUrREBxk?c^e+wty?kmieiVsV``xjV+6!02 zV>8bL;Y}{GW_dS0r39ZypWk-N{@@10hp|_8Bwujd!fOv@Or0(xDXI&ar=#r;2|18I_lswy*5IVP9SJb`N)c%Z#G$$-VgeMAsu69(mh{Ql!HT z7njS`mt@ORZ0NQxp}pf(w|$O+X|PHoLLcq^in1milhIApz)|B8v_$^&Rk?Sk9jz^{ z>t6e<3^EGE1t`)n&)LV9c>e>Jp%T<>1fCx^xwI?apfM`nq~HK$UNfC@a~A1zj%mot z-k>oAy0o2F%Ka9ikA7Q%N+a{w`05OD_+VIq@-7WUrzIH2dQaEB=$7NV>-#3u?H*sn zJoH4~FQG3x9A7PVzd%VGs#o7_Tv+C(xVp`)j=baa>^M!&zIx-GnU%uryxzha%f1{D zmT*8FV)?A0Q4n`A)#-SDc&%;k_WXGMs*cI##ku~HEMkd09bK(QQ!X;oETp^M!d-ih zr+9TS**vZ_DYkTAAo*0HiY~_Q;KJG*+sAY1wu>>Vn%QDWeE)J~C`~Dvj(6H&Wf-nZ zui5Xirnh)x>L>`4J&pL;@wE54bjYd@QxcBlltSfEY}VgXF4`1$2oBwRqQ}y(&+Y0R zmULBraX7<@5{Jr(KYd(@iljGTU*xKrz8}{*Gn-c6^!TZ0BF~u6+CZL%-NaNVzsTxU z{Lq@!Y3{>)R{y;9`m`_i*nRr6Yr5VYQ@l_U71rb>3A0-5!;eH|%ti|WsQrab?s^ldDb02ac7PW^ z?=QbBK>L-+-rH!C4%5}u+5mvp$K8C<+6DP?5h;Dxd6K%WH*q{MIg-m=A*kdz=Vo{vqO+`dobK!y_pOKXe(TusJnB4}M( z0VvL=8NmKu!gZ5@d{NJF;R0|)P@E6Cnt?=^dA>AK;eD2&La@fraCU*AcJWrCC%f_cK}Ly=$@)ebH_&t5=M7n;i}rhwl(b#7*hSrj30_l8%=)^$(c=L+_IWs;3h#CNnW9X! z03>j;k>YFQ!D`V^Y7_MXYFd;owE99Ir4RCAR{P}QO}lmnwt234a=)`cOlxV}ttWop z+`bfU{wepLZy7-|NnW8fZ9ppZV*$3v2MMPVw_*}li`8fd<4IsiXI>0WG;th4GZAKK zaATJDvO*qQBUc-mRMt&xl9~~=nI*O9G&O6UGafVcqn(P>UOsvheQb<_wjff%VAb%; zMJDM#X)XLY)88P5Zq<-hr?wNpXt3Pq#{FXocz6w5>mO6(hNE=u)@Ddvq*jQ+SXM=r zs4sgHFHTn4F%Q_*3;M~Lg;FiZO02t@K{#2&m?wTx6tePxDapjSbegZ!3_i;0yi5A!4nN;jhnyOq9i=+jMF~D zSvX_QEDWgUgjJ*W?)b4vh6`96#-H5Fb_J~%{`DZgU-u>^-k#japDTR#jxhhJ+y3pgq!vzmYSD2F`7{`HUSvrAf8(f)3CW!BE7D20?b5O1yAWh zsH`|km#ZQWnvFO^K$8!ImRfl8t;bi-^2e8qlvh`Cj2eWH@0pj8qv&-O7Ap3*X!pHo z^-7mdgo_JPWjQDnnf@U#a4W}_P^q`4r)(i8gs&0jRAdufic3I=n)u@-QOeR2_8k-Y ze-Ek4K@P73p$kb``XV&d85+#ZD5M0-ZU+pJVJOw5CC$foMQkW&{H9aMwEIjMZ;LGd z@zII7nHjD8r%(cV1&k5-?;rfe6SNjK0P_T~M2Sm~6x+r9fU_bw`H4_zIanj2kpO$} zu#hDX6}s0!BI6?*!8Cy`-BiKQq4y^mR;x|xf&PhxpR>$=?1XZqyq%!dtbdx6St@e< zEDvKTV9R&tVyj=Ao6|0MTSE_Tq_2z2sO*`!iO>C4n+YsMa%QCmOuz33(<=XIb8%Ui zu_8*@Xu}dDSUvFv7lkDM;Nm}?>>pe-HZ-99g9`(z#NYeIRCJ1Uh9&>BI9(8BuBcAQ z9yR`h*Yd)`PEqZQ#6ru)PGiE0Q+CGNt*}wL{9YcD^{AqnV;5OJc(Pkug4Z9{)5?jf zJ^+ARKqN7*IT|GI8=>A)o2b!rXu6T&^qzoR-ySvB ztWyN5MnP2^tA)`FUI|uwsaq;TYadU9lW+49`wdxiq^Vn~YQ3uFv!xe3XHA2H4zDyn2Tb#5EsiXo7t`-+ud{b#}@PxsOq{dC>I zo+(NFLmz#}FRXB(YO&hDxeFM;py}((M!gN#}D(kAXHx| zcEcI&Q2*`ae(^Nze*69Kcjx=t?S1jr?sI=w+a?^@#-)qv^UeD1%lRzc*HtAG#_hon zB*7Y+>h``(tpJAn8|=*2`0a8RYbM;n4AG^`(q**202)k!3r zKRnlExek_tvIA}>du1jbIRBE0$6wUCpo56# z8<3emS$qcearg77iYa*3%{(4-S8)^ybn+fhd|^l0F!XonuF%q7prJp7=2RG1J1z9- zp!=|Yt=#ijkR09`OmEOm#>jMXeHFU^3AJqcadbUMi%kU?MfZj|FpCnzEIGZ^TOb(g zD&lUppt6Fd%8x3kx_KURe6pmGp+&SqOhs=$>(dECI(=|~+-rTI_?VkV9}gVQI-&a8 zAynrk<}8#5AfEpED3%0z+ne91D_$y^vSQC>O9)UT1n82XK<fW?dw;zZm21bAJFi|$*u4{8wzL-jt_2p=9tGoLw zn|=1E9qH`heR#e!dpQ6!yv%zyLZV$+<_*$dCqir)u zfl-rujFY{yS$Cn4xShu zm7wmigZFiBcqcL-LyWeL1BimEUa3|y94it?|Jyw0tU>H?#AtV~Jw^qa3vvHk!eSz5 zgy|?xpG+l{+)0j3-0YhhGha)|0A)OhX~~Yc*!`f+wzjku%%G!VNPmZi^gx%&enu6W z90sqTmd%t}Tg4!%oMMqEB`LxxiW}Jgk4~t=y2o+N{(0UHNol7q&{;Fh?b~Z?qCK4f zD%paf-ma1Faip+QCJk=T)$Lg!+Coyu+ujeVun@EK{7QgH*#dJ;wcM;KzjkO5^vCQK z&yUspLuS&?9D4f9i3mh8H-@6mXEVvWyaN5!rGk0FTQJ-92{v}ncFd-FG>{A1XGg7 z{YsJlyF^KY(X8GUX^8Jm#w{Vf3Zk5t*C>O!yc0{^;9O~>^_Rvfkzqq2O8>98ZT}cY z!}vO2@ms^_A~{H_+8Y<@{vnr|MHZ7OjGVl#44FZF8HZ}QpxNp7dD|;|)Y97!gz`50 zMom0HGev(8^H>Rwl>@VQuci4z96Bv}1`x4nWIDg08kpz~N|%K#^3AeV8WuYNP;W!_ z()92VzM7@8bb>I%Id}vWb7JP%d|-K;#nd6OGB4sjh!+!PTAt+y=Hl!-=(dvTyQh5? z|KK0nEuE$5cT)^N_~f-e_p3`Np7C;S>9+Ltqpj&L2SoJRvH`-x6H~FEQBN>k*d5KRckM)=p2CRMZA<8J_t?{53NyQLoL@Al&VW4k)JJg;Azzwj^{g}#5jWRB@^s9X;c{>wvJF(@ny zQ*`WY)mLK7dbzV;q8vm~c+o85EN=YL_f0fXEN#F^;V7v1L{TlRL={BBwtOJa5Tzn4 zU8TmzOCaq+xp{L!#A!2}S-~k?mfL=?b)`xbFZ^~-s{l6GDGqq~N&7`t3m*gr)lm<~9GfRaBU*NMDEm^4Oj*u})C>^6O`wuYr0k_gGO2!( zkSxU{ZqX-zDWj4yXGkc9uPg zIQ%OwvCYTwy*?Yw_REtp~vWJ`Up%iZLjD%Ii} zNxdl@esh~YfMsbayTw^nT3sv7=aQLG?(YaSQYSI()@zt2_AWE#7NAdfC{ew(90@9iLh4heE}9GCk}0^jVMn=Cw~RIapZ)=V54&v zkX!5o>L6tJ_Y8U#%wI%wJxw85|eEC>&;6(SFp# z#*M^MM6=C6x>{XN;n#sHz9m9&QeFN623>TS)N0R<=vCL;@6BO%;V;GJ09^xHIM_L^ zQ`LA>y_tV7K+AbhQ^3e`^#gMcB?*Jh>Z`KAYPS`f0;+X^R@7QDwcD54zgB_;H_#55 z$9KX=BML;KM9N~!V=_-}Oe!;*c^fwpEJlk0j@=lkAh4t+1<67*vp-lMr)7iauM0A9 z5$--w&FaiW+CxVtbaUp%%Vp3l(y*5x=o!6i5La3Vel!F3qHnpZi0BI=P|I4n2^&A6 zr(LuyydOtnOxKw(6*kN9T{FS@NIbmykcoxb5tTR^9ZB+up}jssf{?_5B4?5aNNT!nM_+#RB9kMAl|mPHK#+f0 z07QUUP3RhuH`g}Yfuv$clv*6sbfZ4|R%I0OS`2deXh5$P#@jciiVhp*yLjtx-><;= zqsfR>--x3+)c`bCSimY6<&zgm63B2YK3a)%J4jwso2RtB!!`;SzeP7CWH1Gq(Ak9=-^1cApMq;bQLh^uzk{H}s;!dm zc2lO2o=Aij&e|R|SN2@^PIutWa(}>E5MCALO#U}O#}nDKmf~A@eQ{OMSZ~Q|v!e?H zL6PjM9Mgi7HVg0dlwVz`;UGvkjDg)BfSwZC`>tvb;vmowM^lGA2GkeNU=&3OjX2nz zs?0FiWUR8(i-?pd()ZKgM7wJeTi?owlj^}u+WKAFIDlWE(0}kS*)Uaz{G_-6@>{2T zoSuDL+=+zbUN$O_kJODP*+9Ttu$FH*QvV!LJNl{>HIh7c>7lpRz5?uM@o}c)uahU! z@Uax|L?>c#&Hd&=f_4>Gv|BF+Qc9hquI{77D}i@B9x^l4>#aSig#L$1jJk}tzCJaa zt7tSAQg!&@6whpkdiko#cuQSba>m1~P+flE){&eT);B`_ta;?UB4s#-3*DIuR9}`G zy9c1lNH5cb2y^qJ8F!+fM=JZE&uAR7O$of%A;2-&O9pDq0xUiKzR7$fX8{xI-uV|h zSoO^EuBg^A%Ou{NDsa6F^UcpeF~|sjRw3~WN~7>%2bio|gFdbE`6&T+=$1=24K)LN z7kL5kGg;jtf`ljUxnh@dw>&`6DM_ZWgV|J={-KyA7%VM!qm}h4zmZhE;@o16c{Uj9 ziHbl;ARmAIoUD8ZB=0)4w_V~nbRMa5Pl9LN$NH%qvQLP;%m;s_;mge1OSxhMQQV0= zV!3BKnn#Ua68C!o&y%4hcW%No2eKhfxepmcf2(#fYy0|U8|>}@$IO|s7^Fim+q*ap zCJ|3(gxo!??{xOtKxN&-g$Evr1l70JQ){MnxeuC>jOB^Enqj~d*^N5NME-vXn z$_9;;&|~f?**G#yBmdAZ5lIQ{plO(Kr~6II1r{*-+9q;y75;(E{q-3DacHM zoR^5B`A_qzNP$=&`M?Y`Kw30gY`B(%y-pw1L!*ti5L0YoBF4G#2I8-V)>=3W#cy;h z4%`*|5^RW|s_0%a&=LvhQD+_W+7j2uMtYrA%aeu52_9~3X%qzljZ}-t# z-*#}af^kmCwuo3~cM|`U1J;XsLtbjiBz+6Ds6dA9iO7f*#PVPj~tVGXUg}#*|0_G%KCI zs{~*J10-td7#Sx(0baANu{}ih>ZxnG#+<5L#@%{5#P322R+h>=yD3ikb}wuT8!a~_ zcyz6_8_qx@i1g{fdGh}>CL%iGS`=o?b2wsh#ic+J--{C$zRROIE>kp6kvYWjlH4<0 zOZ+0=R4SwIN#GdhwAw={^;j{DEy96t>2%+=k5wa&z3kg}={&at-2dA?rRK@!gHro_ z*xSH)VHp0Z5J+4vU1VZRXi8RX4-+c33p1^9E^s3Z@&JW59^2koG7mFk^Bo3|oE5Ew zzd64iO#&mF*Hwt07k*mV*H4UoJ601lKZ<5t<+hG}IP?bylH%KwFf%&HUbh~8vWBg9 zyMp3?YFZ8C3qY(!EppcS@BZ@C)NitSW0G?Njo{*Jh8?NH*hGho_^rpy(@k0R_o_QD z)>(V4gWBB*O)wj575e?jv3LRAC@rlem{YWvOs876uFFotpcZ*}#2&OwMDvdR592%T z>+Qc+U4gvcK%H>?sQ;e@`#;O@e-`YF>8e zrQ@8+G4p31Y4nJ37$D3K``3y#K3G2)?-F1#>;?YKbqI1sV!C@|ZC(xfbutBNxe}q$ z!Bz8Lm=2R$sBWK|&2iQupO5DECA+H?d=Hvbbir#zP?o)7X%5wX*H49?v7r2W?Uu*e z%cr6(Ag%(eSM~enYHLEknU73UAw9KrXbU@ z@DL!AOBjJc;Sg0z9o-5>G_N3Za8u0Z@cIqmMo1gKs8NA-mtv-xx)KtN?x#WFm`!6OhhmKp3wWq%)bpCJaOT_b6U?wY?Iblvvf6-H zG=h@1kdqclrT$=TK;$JhQeWgmPQ1Y{Hp!P#U5fb;{uVh3^eDE+>C5wIz3Lwk$k}5y zVWro?Ga;ph@InI-3=NKPZ}; zvX7>-Hu1(rN?aq$lhlS;f>MAaLrYsjTe|}YiAv!LXsZydVOtn6o3pwiN$NTHjSarM zIU5x@6_^ZITtmz2oZSV!{F4!oe0dvg!Mr=LB(YiJP;|Vf3X8RSW7%F|pvX zCn}{yrFlvfZ0?bt|Dt~%MPo|g2x%swi~aCQIivjxuLlPhI+YmC)nE~mybHtI#O@kW;D7#*tZynNnT0@1h=jsM4^N9P2$(>|>O3GH=K)dbgLo{k9BV&yS?RStN!O z>lH;FW9#lCX+B8Ge!mvU?KrwIN14<^RkQF^XUZ%4tSb{*e3NG4_<6l@IqN78j_vVw za=(~@kH6ld#f9O#VhaP;``nWQ{;(pAQ=U;?TggN-2!q{=Hy~PL1>x?FT(d(C1^wDH zFg{+1z5kr+>IQHS7JxHx1RTooUb^Nk5%1O#(t14d_eiWYvIMaw)u8)J;~#fWfj~`W z5?AQ;EUC=KCo_xmSCx8j5{LLd`)bl4f{`CU@&?JLE$|C?3#2A^GKJYTefB+ZzpzOk zmo~{h2xzFCVKAlUX?|7So5+`g;jbfjZYV5Z-CW$XZGE(Tk%psfe^_*lL6y^q<5fA% z*+?o1j{8BZ`Vk{EOaCn-1@ta?i>)YNpn(6%&Q7>JotA^GFgsg0TMu0yV7dyno*Ef; z==!7|c6e(pftoGm5ou;0o(t1Q;a3JDw#@WgxiN=ShAMWiNzk7Bq41A?TFWkFKQxDq z*0F7)IYxY=xpoT@?edV{Z8$=UO$PY}kRpg)h{1{}m&;4ae=AvWyjTvY1^yzF4wq$j zgA#Dzf{+ozVcNU}7jfZg^&-MEsJ67$t3AFljsF2lXGyqnZD+pQZ-aLnDLd!{S377s zIseg4^1&|`Y@oIr)Z}{XpGXWpYom0V{t5T{)8aAMqFX*r9#UOjN#23OT1dbo$?n*? zk%u4D%)_Z5@Yaqf6yufMNi0T`nWALOU#~_^7H=P@x<`@@!=sU$)@CIB6C(e78I0M- z>GQ^_ORCD_pRqYdR(QHi-DhCaap9+LUjF_TYiYRtMiF~!&!&*spPS{7S#}eeH@GQ0 zYa8RVHEVJZQ4#umQkJ^@tRf^N0C7`~NGf-jQii59OWC9{c+9}! zI1z3q?KflsR%`fDfsUS?+w-DDa@rUDIjjfee7{x*mZrjoIT_Jf0?}`q=jCfTJ?CPv`&|{ zjsDD`|m{)k`cCv_)o$fPe{BH6t4h^hv?~Ax$UEYlBN|8aQ$vD8 zyy@c{2O&)jBP~g3lRGocS+75KRUyk|=vHVz<}<3-!uAQv9x@*P9n{90r z-Qw`;a+jR6Ffq~lTg%-tZRFs4%DTEIf1F$AeAZ;CjWa z(^6x4`n#OxT*ZIgzVBkY7b9q=v1bGKT?+a(DV1o7s8 zUDMS8SY4Ejh+XqK!&{4g5B1i$GQmv>m9$eDrW5)e!L_ji`GZGC9DH2DZ}wI?F9&3I zcCSl+;IAda7P`=!1Xz9_AX_n?b@w2To|m8NpL;Y{wi>)t!i#9vV~$`)QJXU50*FHS z3#0_VR9Oo)PPQ7Rs{F-%0nWjd@mS?#V$_sdOWZ1N%rROIHXWZ$u&Lz=Ne!7f4G|iB z7zqXr=oI9Za1{hSETd-C!3Hz2eGTT5#6|%L#@#q&J7cgx!J806wuYGdO?Q)*VGbLd z2&1B!Aj9A>f6C!9_7O7SV{0cUj^Qe|I%LJGg*wS7?^-B z4bQ9-vgnuX4cfBGJ2wY4eb{rn+r-2*EIsHa)kqRk@|6NK8}anXeZ;{$4Yc?WtG-heqEy3u>econUgv31nVz5JRE31wuLVWjXSvxbL5kAkBX zIUA2N_#+5QC_Tf0fN9nH0c7QELO=pL0-FJE5Ik{sW$ z(S2REfgg4j0X)VU1ZN_(D95oR!1ku>G1$TxJ2Ersi@!ff-TAzhYynFco^M3Lxl7gt z*-fm-5dYSjCOM-z;T<_v`VZ?W+X^DxqHaJKC;lOgnM~X`g5qEOBW78%1`;b(U~)E% zGzdDDv3qh?uPKppY9Qcc0s7e7v0heJ&YC~xA|fz&%=gY@gX47H?Ep@B2-|ZP0hjb7 zdkl+aaq!Mc5&AKX?rMlM(4peGI|Cjc!z2>odr`S>s>)cYw*(d2#pL^DP#@VQ5+(cZ z7|X&N9N0QtAw9)e0TA1CvfeDbfv*WA! zUgOE`kln3^2hgRB(m4ET{(QDlhA_(R%*j5F(Vw{|gH+}qzc=CCSb2*s6@G$=6dLA6 z^*UO-0@8JxfGsT4OcBk*^5EUgY_rPaVO(zy9fjw%hOxt-w5i_nF&v=prx@Hfrr>O9 zxuMVjYIQ(dnD*^>V&V#cj}s*v0rl(}6|61$v4R|&To|y9>Ejcc#dOLlaTt9DU%g{N zB}%_t=jxz&)z3>i0?dT8d%7aJdWifWaUD#Bxl_H)SN}TBvZfys2amREF2x>s(e3EZf zX?QZIqpC>-m7bKFS?oYF#p=exqv&5ld4yPJe^EMq6BJGHaTP&#`8 zuc$^r!IvP5XjD`#H_dFzI|Hzisk^m3V}oZ9a$Z z)WH&Iqq?n>N@+7P1|zpy{p+~d`sK|B6Nh2|vIdNj49AB_M zc#)ME#v8vFK@?g+j0b39@B1xN6L$oBU9t%V@p2OyW-ORgD0GgR$T6iU`}XnQ<^*p# zmQOXgfLF7z{{V2gp``ZZK!0E6z$1z_Al0;$coK>d{7USvxdFG$7(o&Nsh@u=a_S3( zm7I$Gsq%?xVsy5}WE{0jnZ(J%j_=7D*uzAEeaBBwats-MgkV%LY7)s!x)zwk^uX{* zzvc{WwoFCxi@SHAJ5Cl+uBvD(MPO=>k7V)?tDf$gCH6DPTbAMR$ju|K5w&QGuVny9Wd(+4jNDP|#(&>1;U)=q5N=2?}@nTH&zw*`yf=YYfZAI{KZ0Qd9 zq3E?~w4B6fPe0KPCBoXlBC#jFst;Ew;xj7~8*+&h7{#vcC9MNEDLPuN$MTC?lp>DE z+fm72#1sU1E1$D>2_U3~u=M*!sfxqcOaf(D3~h{q7%djqk8GRGCWCTkL=C43)1I2} zuVk9#8ANyvHE??t8=P1n($P1o61PPItdGrVhO4tRz1$z2!QyQ3{h#9?(c!W9MC%z1 zMSj^2@w$6Hp4{R&Uw6G;m6wb7Y>k$SbPs;NpCo(-`QZ9=yziVX$GBuy*^hF1WVRl} zTU&(;s#zf>bvy^}&&fAAnz%7Urb+E3w!aX$F(CW5JB+b%V+?WJaj6(<2;+ektmP8M z?9U4A4i7!0Nh~Bw-MNpIj>J>S%|Sb~FEdy56B{Ss3_)P=0Kx%hVMntE0XusW24ZfLcd9{g+URT;%4mdQo)3!!SRm6?+vc( zKlFR%8^j@k`=sD+0BHL?pwOkTl4|m=ionc}qp4LNbymw{h{w)lR?P*_4ZWCzu1F=w zPQj(ZHP#nqCaM!{DgvV8J+@*u=zss}ot7;=C^Q8E{e7p?YSp4%r3(TD4PZnnh^8_g zhhc7^ne>hlWfIH{7DNiVRWU;OM542>m8h_%fIT-Zr8&pq_DjeSOMP z!89Lkg8P_>1Gg4e|d#xs${?>iSHvDb5?nT_~*{S7kFYw)VxLNS`X`1dUnvF&q z{PM-HCS$Jl>BJO7Wwu5yYCl_siMv+Z;0q&6ewl+k<*8b0oAS&htWl{~M(i%ygUQh8 z^$jI{@SDg(gefnQS5;|=j=!tTsD6fX zi=4n#6Jxr?i$^|0NGxHKJLS>KFXnbwU0^*cjY8Z!fIOsBvOyofePxZk*(cI%cv5v-?oCF@jN z*ziQ&q;yM~^p{g;2#x@=MQmZ6WyA&zusxaHXffvXLEXzOy{w+Aa{QMk)5%y|P49v= zzc!%6`Bp(DRhPLt@9z>#=I!H!G~*Oi>5p@3NR~6Y$Jbrx|*D;m*haz zYv^urN4?`WO%3CXu$3E~q3VU?Bx|Fq__|QB6AaX7%8h;20^x$sEe?X}R2|%sIOxW3 z8)zR4sKWSLqSRD_UHcncWt9l_T}M@C3y6DqsjVQ~nl?SrgBvIECM2K7z%mDK}N&v$BQy$GH@FTvLGQ#AQA+hsb&gnTVd*dSB`ch3( z=kuogc0=8OmREXlJV6}xXL8`$qviM%Qb_&CiW$|2V!WGK)b0XV?$!kE5TpJ~u~p0@ zmlo5H+kmHG2x!F53;sfl41spDI@D#d1(y{wM|3INLVgKQcES~!(!MVhM$ZupJ)8@O zXI|n;x6p?Vcj+9cjWyF3!M0we(_o<@JBls@hwQjiaIS=+eet%2@q)^M@d(MajVnP? zE1LUXEb;PYPPq~Yb2MwJW1MR`L%}%680cuYbip7pe$?F;_od)4gw$qL>z8yvV&LVd zCi+5ZpbY{&0d39XHrmY`(QTL2~8ewHy01F)3?j&F2Ryob&NAaG7 z&9IPnZ^e4aTI#*qd5ILc*KzW}hawVC$R|47b^^CBEqJ+QaH2qt;~rS{tAQ)Aw(&t& z2Sxak@Up$4r2rwy50+YxuvDs?g*~7_U<)MJxIKx8_c5bc`T)S%-W`d6u{s={uAl%% zhq$5R&VWEU`x4M2mWqMZhE3G_aDR-)uT3`mCWH$>P^R?jS0CE#F9iVPm$G#8jn;ns z1-dG8FZ(DHN64dTOV3)^0jVroq)t$xkw}r>XD5aXOgU_5FRHqKuRomD{Nh$R!0B#h zmdxEOO(ZiY0~O8Kntkb>GM^dkc6~ztWi)1HDpHK_l>lDR#YIlWFYoo`19`Df7KPbY z*Ag%nUC<{w#w#;De|%k*#Z6p<|Jshjfpoq5e)<0cYvfcFy#HzS|BAWCVxMd(8lV;}^w`SR~z+Mnn{S%lOTUL(pr5h^QWL z$-u=E;U~L=ESGAM{>*MjfIf_a`IUnmX&C4>vI4*kUZXJ~gBFvln;+p1O_Z8mldo?_ zuawaNM5eId80c;H^xHPgWBB8_Smhg-&`qT1-}kfrm0SBK3&+Gx$MD|-REB>)yiL#W zPi9V@R@lta(a0X3R@hR{(MZt9z{b#shX>NZ(cVbU3eq*RQ&S>=SR`)A49l`T3C^Gbc4Lj??YRrh$ZRN&umywo<#9p z9TBS!eX9*0H`$11?dxy%83}M{F?+4nRl&w?PE~uc_H#fU@6GHdP|zpm+Myz>kLE&2pM~R)e1Dm&P&3y$f0dUghpojya;vB3&s)5mw!ORt$rzu}+qP}nwr$(CZQHi-uJx|1dif(NvLdn?+1ZWL+^w_68f(sEn7D7- z-!tepa-V8k8Cmh3(ZG???O)!>)p3cm?;c*<^%J59rYCzvC^h;uYCc#dUmp$~vO}?3 z6du@PFfT%!@2Blye0zV#;vn6kVBG%RaNQq4JmN3`vcXu-&6Deq?`6CF;(0ZMLN}kW!H5FvTL!~i=vzOgN3bZ4^JU;!|XEr*$ zv5L!8e!)(v(wuBqT2dL&39tKIK^is=TN%vF>o26DOj?ze#AtP0Vu9@M+tAlF3LnmA-9ta(9ZtXGFntDePwa_nPc&ez1J$JT-cZ>X6Iq zwkiIjbBfjXUil~2wdj-}pL(I5#7iR-@E?(O zbM;(eKJ|xzn)Hv{ zgc*=I06oxT@Pq(0X8-hzx(;wCdb&(Z`p7h)G)qw9aM}69jMFkmHz3V?ntn`dGAM(P zqM3(OA?JnrG}s&aLbdMpX(MzUC6<3-_7pDOSn!OoL_!4v4i^Lm6wsC|e%?AtFF1Pd zhx;BJ{y=O4S%F2pP`^!cB4~R(jDY!$)wmczk!(G{n*O60RZf`T?cb_-21^ejPX+9U}^Wxe~h8xYae zTMmK06DgRf>d~CG%|9MLNTIdd;>R%VU0-=2rywN3Ev_-MPu|!N032*cnQD9_It7=f zV6RZHz2Fmd4!A|r3k9Cbzwi$XMhFm0l;$^wv4}2U`1VjlJsjL>FV->#g@Ed!%`<5o zKUg$SO#~tcX|YaBwD;)Y>C|8_?e=aWZM*@LYla*uBdCZrU8>>Imn0xQ%FJB}|F!%L z8ldDEE>g^F7#@3=Ja{hdltac`puBnPrkYg)2*kD{n>WL9%j*~7@8AGRKFUU0NUzl8 zroj`>F;09k?H1clt)h8zWl)p4Zb)#6YzfXYS2ClNuW@AGoVp+lCphqkF?~? zW3ZTq@l6I7@_|6^hGJoswj}U?u+}*-uZTp(Os+0CPb)S7AgP6CLo_2t1MY@G;zQ!Q zl68YIu#6z!{H`j9K^4g`xbitX@}gTRgZ-c%I2`6|#o$k|;+y86Z)6fdk%_faW+4nw&?$abqvq$42y?w*g= zLUEcSt+#b)T{nvhY|GWI# z^ZmTr?XJ(${oiF-H~S1F!`B1OgQY{pB1Ru_;_z@uD;?MoF(Xh^tWtmyWCL5mkZFV@ zLI-Wz@E=!%ode9+&{#;wXN?}}UE)(M`)F+xa>z`eQz2pXK(u#@wace2Psk%c!CCb6lSin}f-?v4jS#AF@Bp-Tn) z`;hivwq_SUJ1TP9;iG`uLV}-nRJ0c@zA74ERf!edve<%tff$iNwSq?t5(?q&<|KDG z3h%fz-jH}M_OR>_q%9(U8qi^w%q|V~lDU@BY)ByY5xpsk}Jaf}oTqsK^{#-R&CF`#%!fBuA4E6s|#wu$a$onZ@;#{vgNQ@U2NM%2ad?W~GEEvPXW-*V#y;mFC;gw%A!xrTxP zXus2?Q}d0VSj|AQVdj*Osr9iU7Q0G_$Q;tP3ZAYzCeQK6pqVrgX?teOwJhY2t|~VH z1&tPr!}O{+mLX_~Kh@c)RX)P5&b1KMa7jTo5aSlJLy_;iniiUci^&}O`G_$VvNRp7 z$ogq*sFgI?r!(3Y|K7)wSeR}xb5q(PwDFJQQaCCssu!LEG${>z*LQ=nu{r@aD^R)9WRPrBzC0R_G5 zNJzL$VfaQmmwB?)IWw$2fk;wg{Vi-aSZGz(0C9?LelaR%tx$T^5{-5dS;K(7!EzD) z2!s(O15Wx@J8iE{`YJIT6r<`--!vCr9b6O+el1ZB00kR))J9Xks^h>nZtJyF0{rB# z&(db%38k?i{~X;%1g2kvMnJlGD0p(;t(4FP=UBRch)Az!2I;KFyD1Tm|I?W48j$DC zO6$jfyp?Efw3#?6=}Ydc20P*7RNy8E!FQRE4S=xoWg#@Jzb`RtAPP#qR!=Rm-(_OLTbF>U=*+Y8MH*xXg+VXS{*@F2Jhy(>l13;_ zbHyTc#ybH)Vr#pnUydB`86IA@a#2HV$||Ik+@Ym$4n;^ULg<|maadZW*N)YV9##dI z#&sOex$SDo25Z;~RZ?EQ-e{#XfzGu3>n*2f$t<&`TC7YZMHBHBLiihvNVPx>*YK>` zw|c8niCaPV)^-99Sa^L&1?GBggbm&-dyVoYFic#bo?#A0GM?cIk~pu$r??eTlFo8VQEcqD_YopRo#uGXxJnedpj;ojDPbkn zL4j>)P;saT#USOirJPns#TKXGS=6MuUCD^=xC_MT!W`ozlB{FR_Te!$i3$dv9^IPo zyHCU345~>2!Y0cVwA36kx+sTW%NaYB0R^+|8o<~B2>qxQlbxU(AYVi*_5%frV!KU9 z_;n9kugTq8b}=879r{@gIKn}c*-Sf?7?jJM_qx*zjvI@5g|x9EHDJJwB^axx9$eZ5 z;=88j-gqcz9pCS7UDgJ}rwwS*)*}50Q&g%-RXIC#UUZf8ok(4|_82UM7u&1(* z2b{FA#e#d(%Rn0|}~ z2cGY)G0YXn5(%v4GG%z0SR(uL@g0dyKpjMxqlZt`n)ZKP=&}pY^8rM)J8MmSrZ09Q zNoOrlQY;lXh5MjvGO`nLW{^?i{49tkr&WTS<*|b+4Dln!P0iw8+ABQ}EXZD02h_*B?k6 zW#Zb7(}geD6V_rE=dIb(;4}kUsy3}v{0IoEusiFS>m2vUN~qe7Z(4N5yfW1j`qYW+ zZ~zNya%@H}VoqpV3LR8Gooe=dhsuQ3SXa8WUVHS(=dl^3-F?`%lSMw@|h-H%Jdzcr^HMrC+`AF z;mJ}1`F&J-cfnEhYXiWb!dIa}Lo^J3G44h}~tvK6Ohim#+FlUW98`e_bnA zl}Sds)mBMQly^dm{dp=_!MkfLxS~jr?D>z*AIg2nc+=!9P}#IL7OlEmd>cBTaKLn( zMZ_VWHG+vv%U3=J5{rEu#D&0t*O(6IjZvTn0Z+va z_LI%ILlKjIMUq!zqO4xPMlh9t_q7_Bq&OcmU5@B)w4@i?#A=NR zh?D@+!I%yUA>y3sCY@2@hSM7?6VPgb2vE)v^y=Wam((O|^U+B{^QK|pkyKV*Trg3% z?`Ia!CiU{urV$e4jvK8{OUqK-hvZH>#~(aXT4UgFf_~?N>ns!X&ZKe&wt4%eg720h zg)%b#B2NGqNwiL~RQ}!zanRm57aerHJk^0%lN1y^v0QE?V&q%hEdwi!Nc(qAHG|nD zD?vBBv_M|TDI7VIpA%o`L++{Tn&f0gDxzbN`@1hBdk#Oql~yFNf9X~W<|t;Pzk@1FKh<LR}f#>I?JWS_9^&8DWzJ~F`R~Y0#oWzGMBpRGS3b^QJCvU$9d^xupi}zq6vL< zN70mo7oW>FsCfLo&2cXZ#h^Ix?@wj!YML|g#QV}gmHV9ZV!%PB3*~l}NU67o0iwpx zqhLhZ^k~A4@YCag;Q?{p*to!?{&CYsIx}w5HrL-CjXy(0?meZ$T%;tz9RYp`Kq-GZ zKiGc@W{kpzd}RbbxL#w%jmw#AbYK`PB-Zbt)2Sc6)8lf|ONh$ishOn-oi3t3g{sW2 z=xT?wDiy)5hS{GjxNNsHiTbwV)#fq6MU;7Ea|nk8~LtnPcRutK9AV z#Pc0w`={E5;iSV!ETwaMJ_C|CYti)bhAr#G4ZeZCziOVN`xiD25I$`8mX+J*c8na8 z*{JmGb#(%T4>YOS;Klzs7GXUxzTq10uuYyl|1uj>2&Xze9fF+KuX&iAZ7l4b&av8< zqA9a7=|mLQG6&x%zAd=K=+Zz+^5p!STFWmY578j3jl}EU%CY^@9c6Z?c;P?pdKU_w zX&@K?&{Jujok@?!{a`@X_x|dC3avldfD#lZqsFr$ow`48xLg{B>z4rq%An)*In#|M zH5lbw2~(Tkkg46Zn?zO@B?Tl(`j({@P95>~v+Ujul!+f)OLKS(g%>S#H`0o%UMLdU zd&wK)NUdQQe?d`JW`_Tk{F(nt=>M;; z|BvL)%=*89qOCfT={Tc^r#$h=H_`UK*K977uQLx$0!(1g z&p$-#6IIuFn8dvNnzHU{bcu-iK8-1Qwj(6$K|krgelCtqx7PihFCQW{e8c(XeR{G$Yzce5mSm>p{hU8An7^#Z$!}(^mx#3IfQZvUDkL8u+^C@SYvtvAyNp zb?*{5$}VH%6IP!J`eyWP@W}zg5S$|Hzdk|{5(5RIgJY{P@alGX25AHr*lQ}uf^-(G zu_N=%EKNfX%w@5D+o4%v-lt;8O32sn+%oy022Z^-e19U(Zb?f%!tB!Pl?)$AJ?pL# zrj|UBvG4q4f4MQLaOq)kLfPGbbQUawn9ZjbO3XY)xtV*fO-p(=EtxHj`F0g{pD#IVRcFC^XcjqXs@~HqBOMXg_k55GwZ4XN8pPVIYXG=-9Oy6yD!_enMNfZQ~)vG zI(A<`L-s^Hhp(j~u=szy*@8Vo5k= z0Bo8rjBBl%2?K=pJ_s4-=LAeHXe+oQzxihrV!S8Q(^pfmA+B20M1L~=xJ__OEd8-@ z1INuMh9P&-;*yB8d{qL19DX6RjL(fFO(DF=iY4Y9_On^h;$fn;m#9Z+;oc0(DnZyn zDMek#S=P-c2RNDm&ShBu(pnH?%`-$7g9wdRzd0f$bK0Z&*##}3E4fRn{SI@{8rrUU zzTP`5dMV)^3+-S9yh#-%jEK#zwt44A}2v3_U9+xPY(4gYM0}EUlJ1pWDivSng zj#-uc`^XNS`-P(uU%rxPopu>0f3*gX)JYKc<<2{qfeEmQ7*7fw<>=8&BsPq^&2e({ zCr3qC2%BvDwcUgwy7E48fCQcoIB4^rg|RqU7(O>awSaxl<4F+Ys434E;;3!t?W{%p z3{>P1*{^L&&^aPW_&x>B7>D1!pBcbUQ!pF21}*_qV^H6%nQ$Seun4mUIf}cK1A%!$ zZdv0hY{4Jds9Pcr*~q_7d8ApS2UXz7l}K{zl2OYX3Dh-aO|eoQyutt?;gvCi(8M=M z9Ba4}y^q=eY z`&cXi(#6~)%^p_NIUZ}8(bzDJrE!HJI3k`WKqse}3R~f921LAP z?F1?^YJ*T9K&6W{sGtL*@yM88!nv+Den=yPb(tvj_mKAj@$j>It$5)I@sD}qN@@Jx zKOS~U|IEwRayf%PaYH59RGwc=;nfMOF2v%3hR%~H83Q;V@kQuz!3(d( zP=SpwIyln}JZA&Cd0cXQJ<(Op(kCP1RpYq5FKlJ(8L|x%hcVr)=^{ME&NYNo68zqG zHsy}XgKG3pm2mg77a&ER{QJ*7rQD@_K75WLU%ujclp`uT_HkxGXooEuu%@xr6vTe} zwr%+`&6Y+vxyIHH87F(Cyg3DpfF8#7nFW-QmHJ5qj*kb93Eh&_+8zfeGMs(ui<1y%I3OBeZSn}uB)p{Ubds?Iff#|y$ZN`%z##j{Sbehm(&&#ATnl8+FV;~Y zIWd`qgA?rBNEJ+M_DYh2QxY((p|0F>4Lf!|Bx4-4|rg_s5Q_jN@Sf7PMb4BEhIcCOiv6gHXUjhpJz zrVdv|<$~Xxqnd<7wJ+(cNM0vCfNdma&T+Y+JV}Mxf_iF(8)aePEU3)s?Isl;lo~lE zsaD!Jw@vQKoD`n=af+&fr;oKy7JbkQAG{NwS6JP({($+*>7-OM} z@3OxUO%>BOo}6YA4n)n>NIrp+12DS@h%=pRv>qzhzF5+OyhM8hOn~^0e$`Kw4U@9SsP~clo6d+Fj{ zM+YAU6n5Ypy71;0s+Z&c!Wr|mO9YGq#nH9@4zumaGf=#UG-W+BLRU_d#4|TeqiL-n zeNe>lui!Qez;msJ#77WD-sP6bfF$mp5tEjI<_wyc;Y2UG@Sshv+n^~g;Qi|`_1D)i zx`2ZS3lFI_9uSEddFD9=U*u}q@H+C?(K)rXpu7+)EKQLUP}pqN4ZzqPc?Uqzr1MJ* zAU`NHX?MBJICcWzQUf7!jfpiDA3{|fP6Y?4Q}aeNp=mUrfFx2~Vw8vD(SgC41=&UL z(YyzY>8W-CT8SE#T#VK(;WzKNg2nV$+vGp)_PF3IH+S?QUrkahDQFp1p<+xXH` zz!#77;Q4kf)slBs+5Fm}Cv|TC+gl88&0gjMr3Y79QH2pxY%mV#8ruYDW&>W+NW)>e#ElV0qmBod>rXu^<)FWF$ z5#MTZZS#g$tzE5>h9QLEbQq@(bJfhm_C9Xsy`kb1C_b!yZd4PA=lmHOTc2mghFp4@ z>BqWa;7hP0Sg*_YRbLJP1Mazy$Eg#6OSKumhc?Ck&v25Iv+@N*D=QUc9$6e_zwT^zj`o`6|yDz)7BEhyO2h7D%+8Vtu0rG+&{90##%j^Q zj_1cZOaDBsY@M%iJT=rE1%FE*z3ID&HDGlK*diz}poS(_j|+(B3Z%G>4!qGfjMCf+ z>+$-v>#52!CMc{;bnS8!t7mILo5d}+4(etUmaMu0M|A2H1#`sCi>BqEd?pD^S&u}( z`Cmyx76peCg}tVc_uz}YU{mr7EEbk|&A&hrz6E*hCsXPW)x zHTVY-Nx;Zz%QODeU7}=0<+R4x9LU!3_*bLE!3kC6L{Tu}wJDvsH%T!Uq3{MBxA}S7 z_~x;pqt{BkU*8fZQ;DKr_waJ>D`6SfdV|3Hqwm4R`F7W>4b>n%JhO|{=`~JnDgvfHY$O`#gfVI`ozCwe>`YQWvifa+O zJ2(Ggd*1G#JO%%F4M01Zsq9FP_Hk)tj4b9Z9Zh5YsBhw zfm7GHf%crA1JTW;nxX9uDP^@+HyUc#CpXo}e*IUq(?5>Me-CJ`HL@q^rgE2afjWe~ev&GPpqypd$&Ro8k)!1qLM zv^3Gp94Rh@e#?SeavD81T^mD)9Cm0bK|0gN>5C8d(KF7-X z-}#Nq|2HJ!{~<*z4F4Nabc3^LhcnJNFXBgF4-E?8W^-F{E1U*AqFyw>Mw2auM>AX? z62(Rn*<~RK@o-yt+1yobE-6)*7euh;R2F~PRi9^$&Gra8{9Ede&%@X8p1YgV$6=7& zKm02XpND(LV37RddFSzXK?w5)E^j8!vyaoq1Ht?;78RR%wr1q??tUPxdFx%m}G z@;3Z^Il_|G7iCK(DErY(jiM+2u*`0mjgOKUZ@!yVnv!RuFXIgdfI;lW+?~n**ph93 zy@NY<4nYQtS;yTe;g=-~Jgm@YN?oL_*YGy*c{j`@3&U9v9{# zDZBWKB9lG_EVToav0}-sO>WvYdj~~W2$g_~Q;CjTL&I+M523wMp@q1`1)}HK*uv4a|c82E(jMp;(9vD<$<0 z$oeP-$=}EU1U>kY@kL7-!pV|2)gXXb7tZ&dXtZF7X*L_0_5|sdLjsYUxvL{m5Q-97{|%yvr<2T|K`4G> zJ;n(=Q0&a&b&%qUnup$<9&l)?n>^WhRCofEE1=w34RZ4QblK{bGmr_$230(n4FO$;ox5+eTt zL0XhP!PIXCIsT+cplu{ITz?9!RHpOJR743+LdBDHpM-fY80%pDG4fD(5(wVl#P4Zh zG_AHEI)(~Wx3fVw!jfxRzn#~%=K_@86cD1&!)mBUGm+zYrTOqNT)3F`(u z>Sa|#NKw6+klc4PTMwbf;U+1aAAHfyg3IKUT8*?0e0m+4!on2}Mol(y?`#yzq-QFt zD8v-BW1Q!+n~?u8hSfdOUY^sypJ-YD5+n-n>*0X=Tooi^#IJFMj0GBMJTN|XZ2~wv zKur#3IQPga?G*Y7+VbcZN|k};i85m%YDul*mOwbmJQbA2c9j##50Mh9OuVm*xkU%y%I;W12n5iJbfzb%i)F_?U^QAukwAFbxC)4S2Z z`)OK-ACE|XTLMND*jrL@K|{ok`+|)J7BP0)6(U{Yo$%>{>3Ez7SyW%7N6N9=qN%SD zo~gZ&tW8PbB&(s@(#EbVfCCAM5q=10BX5M&K-@7vM@y&eG$BCw0CB?bYasg?NJt7C z2E;nGdqNInTp@7XX>(}jdX7?7+o6ToUzGUbvkW!5@ba`}lKx!vi8Mgd!;Dl56JkPG z9)%h?f(lGz(v{$avH#a2W*iT4SVIp7{}+#EI{^Poh{`UUzr;+yr=1bY*EoE$SN;}f zr|0qc^yKj|IXL@w>+at%@ayw&V}3WEJJ0UV@%Cr#^sm)gJut25cS)LMHf9Eq$xj|S zhkR8DerkC9i!@ivkckA>qc}#Im|m2TS+Bjy2Sxe1cvJ3Y9Ghdmr8l^D?h=;UOi~6& z6<5&RanvR~PvwWIRxRM4wg|=e>bMFw*MoRBt>QRQHPr#0uKAxB4b)B=WLk@uqY|31 zok>*YKwFiGBpqybr&0rWK5Ruz<_PD5@q$OSWGYXx=VqD8c|X0NrK_Pr>XjfW8%Xbo=qBR)AJZI#JitLEgOl_MH2Q)a_QKGbJ= zM?-u{HM}@fh&iZ^aY)NduKFn@ ze0H|lqsnlO8%x2*dDFFFr<(OpX)2_6rey@TxbPq8LTyEZmC8Dq&ni9<5k!MQuAmMi zvtG=*2qGDghzNL4zj5JseFQdgVZa+0N!<4UH|3dFk|=M0*8YaznF&Xw#!Y zyIDzjK?^O#EO4f#7SgR!F)ftU^L8uw`01%h&KPlx4si7Y-LK<4)KIrkh~x-78dDb1 zaV^9{m59zwfVWeL2yckO>^jX;QWM>%MQDw+M*=X%t0{wzYagsuC+|R;gJe9@(@3h= zvxABXwu3ic1arOmXMPy@Dkn_w36X1t&nY7*w7g*jQ_biu+|_Qe7Q?>vU|vC-joabR zkoE+#r$@;gf5mX!yJNa_!%0KhZb{a@gApstD2)#~czjxd(k>fxCOh2u6b1xwcC()T zSn8OJmm>c8b^D@n>v>&{fKUhjHZ3y`x%DK{Hd7TVbywG&LxT7*$0DC{W4%OVEW`}+ zXO3J6P}XL36I%4KOP}DAy5Xwe+VXKy7aiMh&`E^^>m0)u^iv_L}f34Ic zly$>O1X{Y&e<@J!HP3+e36icMF2`RgqCEWLvWuIDMCM9%ec90Tf+^|1B8jnG!|*|& zu)I>54S@o&svKzrwha|=kpvWr*88-u>%O2;p!%tkY?n`k!X0ylbNt@TbDGInMuROnYj``_=9 z2w+O=8;HbM>ZyBqjx}v&HE%Pv)&((CyKWLR1i_O8R>1+Dj4F9DUad5AU8QpGyWx?g7Ih|`ljX>dYP&;90QQ(3*il{zFe*0M8I{YYY zBAu@e6cAzoq34BpaY37e!sQwbhyx`xAKwId3^gy+yw*;r=tRJ2zVs+=U*C!v&X0N& zF_baW3LNHi%4@EIHr&ul11;$r+@Rt|y`@x;W2z|}qzz?sH9c!tTp(y*Y=}oiT#};; zm;1H80l}26&7FP|oE#|u>QJA{Nu|ovoY{mYV}X&7d*aLW&Vtw>fa-F+;laSdH-(u! zNUb0pK)1nKL6v!0q}tqVXK9xbEoW3*!|L5_M_BI9Qf(?0XgT%P;;~}04Fu<SYY(T+oYw3)`$Y2yEpw~ZdqD-O^IA6JnyE2~0z^^@MhmX5V<2EO z)IYTyr6V(iu)2K?lL#^Kx#}5iQ~jvi4_(D$Qp6er6uDb{D*L(FQ#rCs+S0JfM|`i> zgTZ@J^W*5u5PXH_RH4CM?Ph}qkBq)Em{=;uVdj>}_KY)e5;^Odq#Y>STJ zKk^F5&b%hftVRf)t+@lm%KMgs$X;pn)V7p%t24G#;)75Km+PuN3J2m1qp{+V}1joHUr{-?coGdq%cUI1#^jL zEQ|>YRgQM#)9W@|I!y(IcBNaumht?62ABn<)RBM`-GlQ*CqBi34MqZ48;5Y01^)~1 zGei|{;n#80e$RWGHAE!^Z=cerF;QenaiKY(0bkCZ8?_h4va`Kko{Yzj&_E8qry>k= zkhC2HbL31XJLRCwVIBKY?xL}?q^tIVggXv^4{zieC`G%D_^}j;xVyWi;qp)b$9#B| z`dSd53MnU*{o4E5Q=6M0Q?09K8$DZGxQ^+(C~sZ%U}N>S=>&xQbgY-A^WIhM@}X-O zx+plhU^Qn2`eq+q!^)$ROg-ViH<{hm9;Ka9=}9gF4iA*por5C>GD33}WKeH*FL>Mf z*!&eVP-k$CHUpk}TB#~K_ zsGIKY2v@wMlA^5r6s*)Xg?xICTJaHYhD^e>LoOHvy>m6Kj|7RGKsTs^Zt=K|+dV9< zN#`A9;#x+Cxm0}@-un?vt+`w$(Y*DsN_fHKq{6GlH-S{(B>HOgX_l&3`s@oV^ilkv z3Ombt*}pv@cn~68_D;-hW6`R!=S``S%Ab+o-3Dbl^X4YMWX@*BNp&XrS zO*8ihJzvt7^KdXZz@M^NQs0g+5+)dd3>r!1q-FNr?ty;ppp$To`; zmOxMwt`m`fy;E4RP6-WWz1*2nIwPHXUwB(mh4|EM{L~B4&y$7j26^4rHQNvoI)Whv z*_vH$NuGor9aQhP2-ms*-TloU7|tdcfc1uaoe=FM&&69yjWevYIQmO^8$V`AdAqWn zC6gM`_$=rM-1-c_6uTH%e1oi>G@$@dQuOTI)8^78&Z@Ot)=a_pdA_}@G|9NsrId%YfB zqHJ{WdiEU)ch6?z-c5xm6Z#o3Xxcmdj?cy9VsQDsogQ!J@9XoyS8T7R1Lv>#;(GjH zetEvluaon9dA9uliMJm72nv3b+eqxFr_+SOiPPrdb@7#C)E~~?4IVDADM}a_FKL=x z9De=><)S;fW$4DmJ27guaddHhyWia#k9iDbD{s(n58bSrX1Fp^3gc~dT$v1K!_AkH z)Q7hB)R@ew*mOVh>!O-eequ&J}cQ33D4$8JT^%&3;9hk=Ur+3 z@p^7KZ^{^$MzEUctV z1bK5R5!|SyT@{_Jg#ZHt$7#CCcBt?&+xFSQ%p>d7!4w9s8t8Ex+PkqrxJCmWw*(@MQUt!KStL4B6LU~3z&9`n zW%RW&M9tE`6guj%s8XE9+1_zaI?Q_27Yj63?qQ*7$dBik_ev>D#r$IYh1yDR?)nA< zGZMf+gfs9}(ltuUZ#ZRN;Zt_YqzEM5o1jxp5K6saQalzw{+BNqG_zr1s!E@te`>Gb z41kt`tQ!_SpuOPCOPV%$gNdgIl+-Ic35HrcEb0EvqzflcjgYLrkWvtpn#2U#)MQ)> zdhaX`?ew-&zEMGgY;8o^wAircMOUQDF+IVb?WzdWI4G)_tsw?#F_fgCL`9&IIksJ4 zsZ~6wm#aqof+Tec5$k4SE7YkLhmYKnWneagQ>)lqFI$~ZV7gsW)F#p``FhV9^aR!- z7o>t6-8ZmBpvrc^C30aI#rB|R*c{BMY@l+9aEzHn%Mf>9xC?eoEzcFyo56znf#)rl zve3uY*rt~^#fStbggoX0@R~{)>##3=&D2vbV>95b41#dS#4+W9cmPlChI9u7sf~XZ z&{A+t$K}w^xmqRS0rQO(;A$E~tie2ps%DE&6?tb6u-a0xNQX_}V{2sFOClTYmigGr^zWUf zY%_|#^-DYOHZ=kk!;TqEz#h1vNt-X=$I+NfaE5@eBUxpLcRcg7^%jZF=PZ@ed+Rnr zO4q6vu%2oe=Rs>xr&Ph{%|WdJS#OPOZT+{ZP?!qs$kkMeSP$B0Hlg;Dcp+jvH4@gt zmI>`Xs=ww}J79e{GIxX;`BM0}h2r$h&NRgy{xP*2%TP~76=wF~fxy$&s5YaFg##X% z_x9i2GSEk;3TGsbGU#V+06dZzBc8HloR~83=WIx3C`<8+sKqSkRU*#lduKS=s%kn& zYc_yx=w|wfY06uDXhI|>6@<1d3blLL2GQPhRzbwwJC!<27-7`QFs%2S*^C%+xzL+Y zr2|vc%QPgEup5mWZrO}y81Vg#_u$L<6S-B;@=?bE3^V$dOd+!tR;t(=mnfBfG09jG zUiz;_6>EqcKd5OzDyz{a3Tuc`R^^TQg#*94Jwu{o9!4FX00MFAd1a?-slBaLu)eYq z_cef{LSCz-B7L1)4eD~rLx6sp2w|NprFfWww&JILL*^&z{#J@7d09!do6!nk<_|Uk zH>9+z+Kp;tgG!0|wVcwowK7hjrb10P9rU(&9umts*;81JnqN z6#EvBRMwF@>A}40Alw4G!yIXV&qSJ;O#{@U8i@*}KIX-@k$rXC9nh&QR}G(uj&0v1 z!*gVmU(-?Kev`Iql(1O(N8{4v`Yug5{>+JqR9@B@M_HynW7j z9Tyfr+61lj6gkGEk_4}N-ZeYDyX`22N0$-&@rD4(8Lj0^s&xDG#a{L>#V>O#kdUY# zJ!RS9Gl@Gp8+RwzX0ZimKHC>*uY6-24jEqbx4Aweex zuo#JsYowD3y*@0X%W9Qv9MGp`h^*uoW`LcJQpf3aaF-Ks>Uf!X0;$w{8o7!3Cbm241WU`41JX^CTbU z9BScvPY~StNdppAAB%L;zvS{c$(JAd0kSA1;9T*b%jriQPxsp>Po!|$Rihzm1qcH! zMKL(c1StFEj@-f@LZRCMs{>oHHa3D7>n-9P9)k=92SpsiB2T4C(LK1V^(u+`mt_|{a z#58R_a@2=eDr5j$xz*oS60{6Qa915Lyc|w8D?HGXQa?$x;3FqVP2XB%v_vetE1dIs z`Av^l00!3a7zo;OL=R{o6rwZm_d%zZ3smlZ`wi=wcIr)YKU{waoj1Uvij0d4%e`BN z_;K8=p)ApAr`zf}%;TI{ajSjsDR!_Ar7nDM$3aPiz<-Nz)3dz!dd{I>^Ol3_?;mT^Q+cGr98Fo=nq;#ehZLfCC#4`Hrr@yM> z_rw+oatwP1Mt|hEj&{bB*macK%s%4aA{}>ITGbSU)V|oSW#xc2@H$vFZ)_ks@^d(M zukT#_F|1!y*0xB}2bITdmRH%9n z3sehq;#-BX@9rRfIL=z#(mzdtUz+%va^Gl~ZR5puGX$sT5*f;vd>YEE z5bneEYJNgb|3IL?D(sQuu(}w>?J%G12>6{I^lSRz*bR;^3s&1gYS;F?R2x3nbYtzO z-p@WK>!(ftGEw;D3qVHp(0ZYH;(iVeoNVdKKmg0hO!Q{fKShP*b)`ypo@JOPojG*P zs&9n081AE^0v>Z_fpNTpM!^#gIfaFF`j~X83toURIV{>Gs1V1)Ad%9WvqvPTWrLPyITvD3D&+$(H$p5^fwDppoI{>A z@O?&wx!$(Wq~v~9Dl)jsTK#2OGQfn$tdfRZ#OOo!n(|p!Kj0<$*`lf#uJS-+JFQJ z!Egqw`^Y6i-#w3FpI9q-F9$7Mua=IUqY9P6-%zWh(4v$tXSgDDK>eizYp^t@ihVH& ze20R8zvD8BA?4{d%J8sd?K+)4Wj2wpp8Vz-O>R69`j928_86|3S0+ zPP|uY-0!chJ3{s%Yn^PL!Sefzdw^y(O+3yYDvbs-6wCoDrNvhI#NeH&!$0)7HkH;p z%+r6v0Q;Wv<+1(`e*mLzX57VkduhgB*p8%p7-DM^ICrUVJ|( z+@prN1*AC8UPkGV^x~3%@FS6?V?nK5R=b&#v0=7x%r( z0yp^>G0l8g^Va>&s@iQTX! z=ny>_lXRAOdZ&QAbBdyY5d5gEpL|t|z+4DH@tC?pYSofJ_TY2j@$siI$8?17o1^o~ zv6|&N_{^K7$4wUSX%Kz4l+>WC70Rvs%~UKQL}B5U3%h9pBamv}*eki?io0yyDQLZ_ z6oM|T9jJ3Dx}@bob*%o{!QJy}Z9E+Y`p}lQpvdDnlwJV13PgoA#RDTt1Pw(rhyt;g zK@9j{v%pyVJ-m@JF2GROpecmqE}WMdUP11x`R@668XD9>W4Mb2V0A#ov#PHWX|wKZ zDK?LxxrEENz~fLn0@921o4iR9E9u&_)LHgaMA1(C8mz)&xiNPMR}l9P~Q5Ki{i*&^0KiEXPaw8tNm2dF^8#pep9t%UF^zqy+z+$O&}N( zrR-U7N7x#Gilb!jRQcB8C?x2#vB`EIs_GRnG2cnyu#ysBjFE+O+fHVPU!vl@oJt** zN$ySRC$M6$EYQ3lqx-eetJIld^J%eW-+@H^dnX&ha<^%{4-zvRBEoNhwHzc^`ZTLj zgJ&vkg|4@fqj{6mti`IsML3!sDWL=@^-dh(dbFOOM>-zr79K(oud#GK9o*5;teT)S z(~(%}6<4oh#grTVV^}oeR#?YA#iIlrD4}_wMG982EXa#^IUbD4`o-lC%ShL>rW;{E zu+66H|FIRXsxCe(afJ^^?}Bp4?O3%*;ydT9v%PPOZla(Y4nL|VRH+3Wd3$h3u;+tu zrCLKaLxJnvDG0e@x~S}t`6)Ci%wYloeCctT%OCXRwxB`nwwH2r`jV`8M!Bdjf)dv> zh}Ep!&u~t-;52>0d*rx$U|d5UX9R(u;#=X zUKdTLhxoo+s$5aG=d0RkkWlfa9iGS$u3jp%vk|l0VYAWShrTj&@Yg=)tVeb5$5Z>e z-}iwiU_@-22mUhjthw0ygw#6|C})JMB69GGw)J2JNz|`VN^ZfZqNpJJR(iKEEALQz z{(~{;xAarZ@N46*M-2YlTez94vi<89)*swGo&7Oa+i&!@m!pL>a3NW&GLDbWfrUIj zgH3EsVfemp65k!Y6hoydHlJ)xw>~QRR{W!}B3sg{-5UjP_K^HG@|L5bog0NgX82kkeL3l`V%mEbjYFzi z#}TNt${Y>-%d_VLRc%4+WA^Q11Z_7a1+Csn_`T+^xyzw7OK$xo$;s)mWX{(<A z0vAaN_>$~s!39p>hClV(C`s^5lHICii()0L9Q3E-7$LeL0g@{q%MuMfP)TxHmj(t| z$H&>q2@a%WMF|M7_94jvIRP!geno~f8lOCplr_#Zd#2czIAP&|in#>Pywww$`-+$- z=8H_TE|OW9<7k%TjNDR(jwFRY5#9%i-XeI)m39O=4fO(Lk-QT&9H5rrOhm8@$~Jp9 zB@N+zHe>TF~m5OHb|6nzfCm#`xf9#r1mOL8P!XQuT;J8$cclwZv>)Y)cI zw}IujLICd)P;F3yTnqJ>Qu6Gk)&uy1!F0snd{qBoWCF!jB~z}SnfsOb?T;`F-`t?6 zrfs~AQp18Zi~LtY1pv85Bcql%mYzH3@0VQj2Uc{gjF~OMP_g(v+b~OzsJ0g_0TjqG zq9Z}HE5Yp=jAtC?hUHv13ZN+-+GR(ZE3XNYT}({RH;c%rpad-ilEbfsXqn?w$y!pq z=#Td`SRPgjHu)tAI0gvOHHq}#g+^+`4CN;89%(y4Wwq{KEd z3Xh4xn{GeU%R|_Mlz@Fiov^=U8-MCqIY<*2!O<&4KoEnao~w08e_^50vjR*jmk03# z7)l4D^=Z#IWXr^4n51Mv5AqGgy>Ph65KL@VZy7RNFQjR5j-$hjXtx156B7-QK(E-i z6bzv8e&ULh==GB%F3O%L17h=YUrk#lCU<`VtNUC8HSFvKuvPQIV45Nj>o4R+6H7 z(ZqhWaRh&K)v7>Iz~9uBDYUPYBY+-2S7(1GV=o|*f%TZoK*(8AHK=rPuL>IQpl6WW z9&5gnkdjp?8<4BiR;EBR#N*=rz<@S2qTpp>e=~pc?BLBG*E2x#00WX~_^L&~^3ipv zzc&Zc#}N&KZ^vrYxj^kHVcZDdZ@?IYWgDj3QM9B`PAt7Z99O_?Sdb=uM>eu^MpEs;A`ueZXf)=bA!a zX3;pnwgeDG?WCOvgaujJ7)~U}+h2|!kqNuVY}=%F2%&3?ee*%&jY?>%&g0$<27$i; z8gE!JbQrw`qKutuQ9JV6qRhFhwKN7oE&y_{hhwbaAUz_@xGVb!BLp%;VojNrqfUBE ztKIG#5IqOpC5#|8e&p*&ZBtNEcnMU))Y+7K@>-2s86&)zhOCuMDO&lu^rtFl^@?#Lx{3cV;Fjx z_zSveKw4aZmzjcpn`DDYp!g>&(pgps2RrTWDnk7ymt_4~VI)KUpfp;rRdp%>0c!#q z!`UUMU_n23emg9mjG8;&q41||6U6+vgrSxh9Qjc?jut~ZSBu##6VTjdse@RPHI*<> zdPsO+3yvG`e*(Vhuu&+JcGCho;UXF|Da%!6@XBGGhH)SvL~CQ67gHE-oQ`E_A-dQ* z!h<1P2HC4xLEsoG<6xkMuAZUqQkbE}w>0)r$L^b6Yqhc0sU=oM|Cqrw3I%0P@;JYrlnP0k<2?c4k}gQ9B7V?DEip{Gqc=gPeM>$b-}1uz&#@d zia!&GW%vRcKnG}J(!>?#&&{P_#aB*Uq)%dPV!MBmCcB3Mc>~t-_X!<#u2q_mvQS12E@8*aMjc)f z?1E-Op+bG}lY}MWMc@c23D-HvwDIT3m-5t zv+V_WRp465y?8s7OnQT39RTyb`M@y;xXU)Fio!kUAMfT8%!dS%c(H&|8^Pz7r1Z5g zh}FOvV`7bUq_{A}j@u$#e#JhcHI2tG!xmoA`pwC-@5dm~hew^WKJ_b4mgUXN0fju} z`xAf3nWgv4#Q_*HMTW#h!IK|eO57YI_nG~^f)_Lb`2K_e2_~{iP4Cnc1FNj~lAaZS zc@DGCiGT)@K!(85uaqqD$cY#2&_mBCTHs^=GCDI;e*4ItX}@@ZmNq%#pCHa#s(1Lj z*)Fx1e84HIj`oXSSIHo80rzVc7RArMpi=9GuGuyz7}$mEBv3xx$ocln7)8HV0#a>$ zmFFdM9tQH4seBKD4YTlcqaJvyB({WY(@T6b^inpcTUjg2MON zcD%D1c<+Z6BDI^%LBX&Y7?ITj2zQcswIE`%h$ReRf2IwE;LKwX_Vx|Qir@sGbuquk4f}LIz{)wjLVxhqrrZ<%V_LL1gcBeGhJmv zAx1uHNPWdqojH$NVM%$zo-_p?`!VT&kunVMm}a;W{i$8en!1&V3c2b3?5^uDt!3`l znLq{P&uS2yftl@M*rAb9ewg998)3{;k#;tW6B6x7wR=nETV<+4y4A+0SmH)YLvW}(HB($rBv?1@D8h@5EyemsDxGm17_aH3f!?>> z;SNmesLZIw)Ll6xy3Izd;}&Qa%`oTTYy4cAXKFt$LjUf>im!5$8NNhF1IO3-p?MwM zyhxX=J?3mbB9U_9TduvPhp#Hr04r8XL=(=fToYw;T-S~z!uXtfT8YNIFUJ#z{o=>HkY-;SjCrkX zrIB^4nH4K*11VS}W~gri>dhHAvl^BtJV_0cyfEvZ|7naj4P`Mfit@96*6w6E7NB^q zgQ1V2(;;O-*mY_JJ+ZF|MX6ysZGE|55_LqXYnhZCJm$)u;Gy-E*(X8+Z*F&Bt706YL&_!*g&pPIR| zHoFbhqs3TPi8zZMZ=`&DNZ!l{Wmzv}eVsjx$R~y=l%rh+=ns*yv$>FDafo{fin8&_Z9KU!ErEEMZf57X z7`HGc9CmPQd}1ALl?y`O0Og{BUF+I}={13&QivBP|JI6>+>9~`rwF~}Vu9ylVjB_; zP>$1aAYx|-0?;KL>sKCg+j_jr<6LHGNpETW9KF?mC}*!kn*tc_$8)SYz$sc(G4XEfZ5}9*mZ5aWv26jdK8=V)ch2rd5#I|{nK6$807150}HKTMJPaM%& zBvwtk9`vp4&lozt0Q31!ij^5cOLw4b9{~c)cpO$zxVlqN@YK^{nczKw`CmJUJciHo zb_#RLPp3yH>+CBj87G;lC83$n2$#ZsVv$5}5#;gW3n(tMtvWX#djwZNK21a96(z}% zwWb|X7)-g>UE04RMM}gD%d5}gD2xJ4J^J7C1uz#1H=W@(;eqSVfbL0Lf{^y7AAT(Q z9w(6)Hgb`Y01L76MmshZgH7Ze=m#;4?g=)LpPQDO@$hpp=Z0trFWDb-Kkf-3+Qs44 zFE4DaFv5yftin;ni;c0d>3wSF7No7vq!NG+os7xao?y&NxWdi{{t_z53AmdI4h_IgOV}Z}K1imK0EOs&eJ3Ci z5zkdo1sbv7V5(^190#3$*gP( zK!l?y{i4xSDo_y2xtGr|k@@atjaXZn*Gze4FHs2U>0h0mlLt=06Uzm51dwE0j<*QJ z1_AqTG#@D4YkYh=pdZ4uRB@v=!EZPd!Jcy>{2rE|rI!p|3HE+g@9x)32DG)pkjNX< zmHz@IVK)sQumAJrO6`ONNHY23HIzST)*D35@J4=;SHT9_TRTdcqfU!VH6}{(9w}vt z>i9bFiDz6I#cKBn$^$}OeE~?vNE1ck)CrSTn*@L=pV(_;w$xTnnbGJeMm*nzP#7L1 z;3VPTuIdo=#meg^3T@J3v1rhU-5O}akR*9C7q;FQ?qt4kp(l8#d#)D{PDB($0ygHdTqS}!(P2M7L7MxO%Krw%si=irR*Y}>*ln9}Jx95RGjfGn1&xgeP)~`guj2tt5*$$Z zZxjPXz7&?}hGM|C17lNDx);#h_x-|Wu%2wwuM||sa``b9?q)cDtfWJsEe~J>+qV9r z^3?|y@P>qhf2nbBB+8byKOZ*eafLqBI3OaYJ2<$ip7LkW>w}|Ge}LOpsboljSReY+TYAJ^F;Xmg=VML`jr_0D!VOfu(1 zpk`=JIkA?ui(q9xa!H9)xV9&0nUw`??~-+-IYDV3v65Y%crz%O9bPatBPIewTx9o} zD zAY&{S)~4eKqRftp&nDKjktJTqCxEy~BiqmN%qMRnU^(f&kK#ZAgp=6DN6?;^O~s%r zr_8iG@OR+WJ4)pm5ow?2p*M{@-7@-1fq!`bHTQ&|6}BD}>Ul$F=Nm{P##HqF4Lk++ zuTK>U#{jhP+<}uEiBjrC|Jgnd_yNwy59$8joZ|mt3jf0?vNAGo{QpS`tp9_Cj`jaZ zL)XJQQA^U{_zFHi?k6ySt{$UmnK-Zlj87twY8VYWGY#y5aWVgCePH_f#KxOh=MJ)F zpk}t}t-28;=1!hu!oW52CM>cPM848}hfe)6<>!$Y1y8 zcBB3Cn-&hJBgqTEcID>fL^hMwUG5rZUGQk;{xt0S7EYWp1#kLi&Vv=Aq-Cp*A7{j% zdePw39$q{i7zEa%0_>o<^)t<5DPv$VBx?_En4r6PxBRL@sDf84Afc(=;3H`?b}|~2>g@|><(B_B#~A_aN+A4m(5uiJ(-T?*%cf*s zA9uQ-6d>K?CU6lEif|HXFS;MSqi{A1oXuTE=h~@hoK0JK_bh^1A-Jcs496kiSo3z!$*}z5vuw za3q^K@hPnBH{3vJuZf~fC=1EM7iX>>t03sIf)~EpU5_@HGR>3`h+>tchD0V0mT!+4 zs91>P)0SL9M;NHr2@a&8$#8K#`l^Pvbp{$b!sh6qs`Uv(dy9m_ix9^b{KLljk-j z&$FIn}0=-NEfNkWIB?|>1Y1_ zvNd26P~&>d`dU}ffRQJX<_!V-5$WmAuHN&;jrN{TQ(=C{gFyQ3P%;5i%suB;lo;1- z{yaZVhRGj)2byIfG6}~OHGr<~6Gumb@+Ox^Rg@q1_Jr1&6J#F7dzXmUe=YI~m$*C1 z=M@Dh5k+Q^t`$o$==A_OlT#J@qPP%Kv8Yal@@(pd0Tv=Ie1 z#eQR)+ygAGCfu>HM$R?|cL*vq!1sig5~o8e6=O3!VQge}#6YLmA(1!~hYouIiNVJ* zMIH%y(PAPZF~$){97qp7`mVH*j9TajF6aD%ZQ{VRz^CWqJ_|F9U$d1tcOxu8!> zs3z*1y9UJGriSq~t;JMJ`c<9OVMZ(CY|xncp=3QX51Ll{obWXGi7>rw#Mf0BlkVZh zh@9<>gT6FQThW!C!k9e|SG2mZ!ax&QYAW~MH*J;dH76+-@U3d0-1pb2zIU^;P-j@y zhtemH;cHP0m`wbHZu2B>oE$E)5s&SKdDBSJTq1SvI|R-!R3HPo>ywF#^uiiuRa+ld zcFb6%5aWteXcb*VKsi-brO;iIoTg|QkeF2Kf(2$Gt%);1I@v7GC6qP@P2!}J?K#|Q zvr*aBOR9rCQnf@uK4V~bsS!oz!sB>+=T0|AOiGPA6%PydEd zu-kLCin0-z&LNm6$f)Qz^&z0gDZu!6fIvf(@#F1g;_!SN2pm`iP!!iKht_(X z5zA+m+iMxH9sSurQ+cux4FG5)pjA(XAC~fWf&iF|67A)0#>f%>3k4GyW2E(bu5-|I zdA85tVbdmz3xM+D26O4;Vnr>RVh&c7QZGaK5g!Gupi`A4H+l)&> zgrRoLbJGbQD>b^jqp3_O*HE&f$yRVi{ioFypIPTTj?26AM!-cdDy>=lMC8h2a3()H zaSHmQ+!{`uQh0=10YZ{=Z`Oj+j1-P)ChzGRYutWzCht85Z=f<@ip1NYf!r)VlyUk* z6}x#{BF^G^Ebdx^=XQWjW0~^5vm``kX0&I=0&-JCeHAjNNO2JpJiLyVV*C&>bzOqy zvv;%oQsN%XVW$#Q_cv}NHTa(idpHWN1ftfz?i6A2_C;NGwXAsoGoSx#r&S5|ZqR(V zL#Rv~c3*r`@slX+iHV%cEG|eY-|hkveK6!EuoxbZ)dQVNQx!_chl%+14G^(4@#W_~ zDC%e!td)-pj|qYoNa4t&Cupvl0%b-bbAyVmSHM16oEfle%V5|>FL?m6bD0VSv$<~* zE1}z$5ObC*v)vcK+fp`6Dbi_(Vkl1Pni6tk_`m-oxwKbZXOWdFok%G z@IrEAxA5HC8+-vAY{|UY-t}8c*t%Z+R;j^f%IE%6EXk}*hozO# zc>hWLJT@rhD%EWsoko$Bm4W$NshH{SF-tsbRtrV*K!v54Q2%=yWF@>?o!*54rA4AE zD>Ps=N0BLFP8xF3uwPbs_PgxzvLTZy-M}ny%I1`*9X%_&xQ!Av^KRsY7$~X^4+TND zMfZGsA8F9v%=>Bod_RJt()qP$QsLTYR^88K8G1X@bj(W^>P=(2bnPW)f?-a6`7(#x zO|G^kN`;@{IN_T%CGNZa!qPB=6RLNLTrnhGRX^ND>eujP6CJ+m{T z?`Cf|e{z)fTGtt25Yq1_aJAdlPUxGtbC&g_cH-{WnSc>`9WTp*M2hx%*nVqI61a=0 z(h(kkRM~oC{4WmSIE*;Ap``y*q_JmTw7$F)7lY(rpB;}t!TB!>d zmi8h_eniJ@+MW@sA~la+2&!j*M(Dt>yT-!X?oZBcL=PNuNG9GRq)2ua(G^OV*m)3Q(i#gVYx zLib{r)HkZtvxxChbnv6b8Ty1yUjzr-5u`8g4bE1YevXkBW^vc$yFKn%UsCpOgQ|LK zfHCS>&Osk+_OItp=$AQ1wDK2Odzb5vKE#FB4{&7LJ^l>Ik)Ksc3qXz85Y>$KnO~;j z+rJYFV}7wi9)bFCT{0Wfv z(+V!Vp7f4?zj(l$w5}(^9~sPW&WjAoVGa5rVe+e&=Fw@Fe;+IH*O;%K*401O%=2o$ z(>KbN`ORJ^fAN{Mm)}UiGUMHrEb}1|H1adFS?57uY34!ZN&k4uEQbs^G|T)>>>OA; zll1tmi`z74$eZ=SDh3a~#!iS5s~`1=}5+W@&ZK)%_gNtZ0_e zZCe#n^bH$nk3LDJY9L9mCbT0!efr_HbGb&0`rh-~+&J;vVXlFPOD14?TM*$;XxpS6E+2IMbDz%Gumhp@n>EZ-I z`X>FzzD)`f<`}6T=N)!EebbJx2-YP8FxW(9J`OY#zTG)TTjp$sAwawYe{JAeAp{+q0cE@A#qYYW&nx`#zl44A8={ia^OYH;w zC4aWixL(BKC;Y>VGfL@;{GWTi`VbbMzrk+iV|YmmtwajVq38xo_`$#wHhV9i7vct&;>EHDqqux1+rCB3+l3- z^5a_Jzbr$g+0hD<`M*~*t@MdjxciCiyiFzhwuq#_sDmTsUXkJ^sO^zg^U~D{av4uq z_>@h$@#}x}4b$1ME9G!ZF7(Q?^OXA>w+8pq|H74+DNanbR{rny(n_67LAagt-qfE& zMjdvVdPQq(Z4#xkSo=|R6qR4b^W>hx^BhwPLYLfDO6CncVggl;L>Kj>9gfjcve08{ zt(2F6-_z#it+wZ5dCiN!4K}Tdr6}2`B5qjOFF_w^ji%;;854ynMX4J`RTtWxB;nd8 zTiEceYI9?PoqZ}LoY7;AxYmYMxB?Abx4J9raM8v~cEyODLXn{ibv}Ji3da@+L>q() zoR(v0ae*U+(X&`>wU{o?F<Gc7^O71x-AZNgv0dKEHqpps zGG=O2Iy^;spbPCteXs&S(ekHH%hvoV^1b%^G+=N^>K=g+%0e1`m6(N&n1|G1A?QDa zfGg2NtyX#|>Q&d#x(%x(euo3IkpHq$6a}=os__aO5f3N>rr<8M!r?z=4Q^m{jqgJp zpK*7{%jh??T~AeC5yFxY&j_Y_Zhkl*obCPQ1SIN$@fk&nwdLmkczwvf(#4nCI_Cn? zhb|KcPScwp2I1d>v@hgHhg0u_8MrN1fO6EvZb@RLLO>3|KPGjt319d}9*)KZGx{dJ zWl(keE1^r{oE%dS8j|Tg8S;EDz zCG?NW@^SKK4yM0q;#5%trY7|j8sx1_hqR2^|g(yLj{+d^3R~LbVjmU0&U8X8X(q%BfE|_ zwRV!nmYLsZ=|x+VR+`Vk7I)}*|1cJ0S1>77vHN-s3`-&7rp>A8b&FA0s2b7Mbp+jYJ=jbk;IfL>|mk# zXqgacgX?A3H9CjG~1xz1HM}e!ZEpL2J zd!K9u)v2HgUi|6Zsc9NhyQoS4P2t6ZFg)WdgQqv!-?R8a!{ zr7{cz5Xp>Nzo%WEav>mYRZC?jEu?<0o7G+R0p-(#HsHdK$W~*;LF=~p%>YTw@z$l~ zaA(M0KzQ~-avi5G`FT#Bzg|a>ZxyHe-dgxBucT>8xOr=45@VP084vl$`gn|B=1X*_ z{;mqG_Zm035YNrL(PsjW5wxo1c(#@G3oholh4w!X!T&8T{bvct`hSSv|3O^J_WvX< zUD1+u-WEYPt!CyI7?Gr!_wd>sf)xf(4koHs#i4*Bl(SLd*e8+m*Oz}i=X|*OYe>DJ zGO20lZnpd_`??ml;5v(Woc+U^Ctur`lxO36Pj&Rp{^p*bYmNYRJTsP5_2m*4)PKM} z?F|2Zqp3C*x*fSKHIzK=uD($Rn%wzTU0v)V<4-B_hx$kxb+4B;#5|B%k*90py2wV<4;|$1o zZl)|qgWaBuD^%T!?wu{O8HooE)5Pw`PH@gQ>PpWbSV6}JlNZqreXqR_Os%Q7cB zVWqht+ay12N=n8$)MbRT%~aRVMru}IIZsflAm3WSBc4COi#l0_gCo_?c#(NiP=uo5 zO(U`-ung63i4s;MFwJi-id?m%p-x}}Nv8x#7KHg9Kc9&U@h?8 zu4HcwYo6&Lm z8HEnPW8=QwXM;u6TT@|TbW$p%LgqAdXw*SA*#*#)=*!y)nX8ZrBc|ZKM|U_tw#%e) z(o9-zeTzv{qC5v;f}R45=i|by5foxXe)Ojp=p10--|=`kn9J|p9;4W&U|B27Gp0S= zO{E@^X{(OLKw*VC5Z8B&WCTWAs30aL^-XF(%1I5cP9l6WX|I9;kx6^j(Rl5Qfm600 zn5)?Q{NmT*Nky87ZhnaH3#(bpO$Q}e*n_}vc3NmElWUe^kv7k?|74mIx=7M%$KJ-1 znFHs-lZ8K87rQzl6=bBz@Rnkk0ULuj;7`OtA62-A;-;=NfL9Vn>+VR&V zW0ePTm?z=$wvF61rh7SdMbrUL?aA%Wa?s84m<%?=wega|qJjwn1*c9%LowbRbX*j&G zQ&BCY+YDD62N`XJ?KjP0*r#E{D)|m0GSOt?pxQ;?&@fyX(}B)RbuioBQ!W>}3$30|ZkL2eoKb!{;+Ur7#mC^8B;gi*Q?=qT zsEa=1tzcKMt#usHJo1u%6x=DJ_?|vk>jsM{!?oo!NZ~izA=$YRc+O}0Q#^AgVK4}BpjJVQo%6g6(e!&|oK&M-cr_kI(4ZP7XZN?d zEn;)c}Q1vkc z!H@sJ#>#C!Tkf*q55*^bFe}>d|Gnq^ubu2a!%s#wj{oC>%l1DEKiU4DhM(8klFq2& zNWG8Hr{zl`?(20GJ_n~e2aM}xER0dtK?p*F{v^bJt1`iUpC+rRn%{qUr;YM(f<>x2 zUnV~urb_(y5TbYx-s-;mzOGiUKY#Y0{-$!g0r|?=fB%s#g#th6J2D5y#}^`{g)i1@ zcKl~5xKOjLh3DbA-=YnJ!q@fu?c%2v<*c-MVX_+KFe@8p)&NovsG{=nXE%I=%e1*@ z@X@m<-_P{Wg~$FFDc^W?4)w`6Id0+G;|+CM94!6dUnD&Teph->3{%u1BMuHy`O6{5ZuWToVHrmnHG_gn$6vD_EKh4CHSY zK;3~>gE_s<3#?RJ18^5w7R8bSgESi}qcYg#1uaWfQ4hj;?e+M5htn1~x6im`r4x4fx0>4k_^4Seu9ZEKO~rjWyF6NwQ~l||VHkOL$@coQFpqDP5RxARBWHR9Eu zky2xE(7d_IO~_O}a&hWU9^EUzY}_S_&|ZHyGcj)l#i+Y1TUweRoFn=kC@_tG?=0dk6K`y#KCD@Y;o_@ARe(nvPak zm&iX(nqK+m&fwVvqI$YrJVUS+Cq*$lSEmutSh5#~0RkC7LB#9~qCCT;AM%W$7z=Lh z;;8^l!)F1P`}f}hqIR3wgHnxth>qW=875H&TUhrf!w5KdqBRGMB&`@NEONu)*|M@E zwJuH($|>xPU01MDI{OEeHb!-4cTXkCKa$~xzbxDzUIx;l#dS#l#eBf9FLB<=kmCVh z20|n0;GzdtUjugV&;ZEoBhPECavU7Hu+K?h^Wx6#(xbV7r&qM*)*8G_t7$vZx+UnT_Xr#y6+#Zo#+oa;-bM*~1 zRRRLCuP;>MYmY`1qW6tK4OjNKp~xOchAV0;5jNJzp}hjH9vU1;9zBgz^0)ev*;2gV z_TjA+q}nKh^XVy4Q!%CPp|%M=_c76Br8-E;+W9^}i&ZYdTMvI0qpo?mzalnyH?(ZB zyr1X%`6!pd3VVX*dsq4e?|XNvFj+6&4n_4Cr{PQU)nAk1@Km2wKQ5=3mUSsI41Ji2 zV0flK1BD=@(X*OC-mcmH+nOH>GU3Ik4g6U*m~|;`K5b;{(c`Papz)CrHC4;%yc1>zztdg-p=d8CoaNFJoW*H zogzdov*NCmQB1HaD(?z{fqaymCK+axyj^@!FtD&LyK*cmzgY~6YQ0fIo!06pSY_dTWEn5qV2PtYNk;FnS;h-kjjqK^G(9c0 zyw(LP-+g8rjerMhF*uC}?+e>w4?9 zovyb=l*1;oW=G5^=v!wgZCGa^eL(#)ylg|ct#B|={yO&bR$nExSwg8<+Mo$9e{moF zHy6HRSas24SE6=w10}V1vhbuX&TYEnWq3EPV<1ZHS<*YtE`1TvM|LM=IEGe%z^e|W zAGd?%XR&tcynM*Wltk~jxDgVCcc%H9^2c+teDHddpYCogDmNJ6`Mm154ZahYGK74` zH+hhG+t}baHDaqt@RE=KivlzJ;$F`Q79PPl zm+pp4e_!)ZAwM{C<97>Hf8LAMf|Hw*KYUKlZHu~gxM8?uG z=25vH4oxKn?vH5Z9NW^o?kJ6T0@GORmo?|+4;2ff3X9R^)h7Bh`Mo{=vgL zQECF)Q_tgsk)N(!3Mx)ksp_qz7TU9(YRkjo*xIWV%IAn#Dvx9K%4E$3o3`rp^g6w$ z_h2x^N7LGm-NAD69VccY{b#9d!}WMyUN=nWI2VMm)?usmD1E>btEKrM@5=Ygt#6** z|5>_S{+^=-65U!n6kZO|#>Ue5wbww;X3^Tgb*+60Thxl-Xq zx11QMw|w-z71OoB>1p>ePZ!ywd|n$Q68dy!%$AcLOzW-?@N6v9Qrt~@ynFn;-IWY1 z96t8!5=lhgQIBn&)I54t^y_-hH`2*C?5U@*D0Y7eTI$SuhiQgglVpjaVM3$eLpfJ6 z-_)qDmDxntG1q4ion)2vzB8KsrT<*=9l2!Adlr$0h0<$Gb)J{K5Y=qOV&5S1&bN8J zddx_ZFx1PM=o@8u*wqXtBieuQq~9y5=^rNF?rsdszz0j$A9AhT71lhEdX|npLK(NQ z+zvO`PCJ=fJ|ySYD9C&~f0#4wqp8wu1m$JpotYFudgH=-jWPsOxV@*dKVB?i8t!!@ zGNL+eKc_;-ZoMyRrv1`t=iZt(+5M(cPQ<1=Z0G_vecR?PfxT}Em>F1<_S}wDKS(ID zzc^?rzfWX7$%^---PyF z@=Rfi$}Ni9Z#{RPGxL2}H8ZNgH~isc?)*y~aj9#fqvz*{)nBY|8L^4YA0lp>PhJSv zCW+?5eW3^|Yoyxt{l||PcKPycKV7sCo(#O9JHu*xlVUu*#6~w-hjau@IDQ?aDER4> zV9`07B^kOY@(lXD#Jl2_=GYq(au@^GANzl5^Pa$sao6nPx<_$DUe~bCF`}h{^_}Xy zNAEf1to+UK2jlv}A4{gw3eUbfN}D3y{9e9@DtJ@Po`w&5FCRvJSGNi%-TFvzgH_yC zx!W|K!zg*uuPNa2_!x_CHLar%e^+kT-f?dBz+)aESMagt8lHM?wZw#-o)UJ`$p(CK z6ZcI@PHYWBvAN!MqqdAxkGpe2t5Q~p*}>_q`fTBkd)_4_hbB$-)=$qfFuS+Wu}57# z^xR)7|30b!8)M>xQ>O?lXBz=3`;fIp@l$_tVtZ`{ajD$rN$K ztDHD`?&`)h&nt)W+XyGl^Pl-~reV@^kFj_YZ*WrK^WoE-0Y7ARi)^5dy_4^}mD1+~ zilR7h4)e9a)_n8Pu0-W&+OKs}{GMnB4LaG2&8^bb5yyk)-g+liD&-)L1i5Y7Ygsx}iV3DZ`bK!u>GHv6`IL7}_BA@5G@N+CktwXS)0`t;2bHBY z_3BORRNQAipOM@HlR|tupUZ#Vo?Rj?NmU*t9~yP-gLwU`$;k38ja{2ZHm47K;<&nz z3vtX&&ptjVigIVoLxt06((bzhO?L#_=Q4fqtBrq~5a`j-5Vdt+17lCxMmjma+lSlm zg@OF@JZtwia4To-UHeRZ-G$Ef+lrxuA0m_O-tW$!3OrNK$0nMxzetUxsIW33O-eq$ z!y`=LlKKZ*oQbkqR z!sUXKAFbE;mZ@BBJA`{6zxiOToPs`G-2*XEL`;$I`1-g2meX;FF2$Qy9 zjGV^{+gNrf*PUC~i1?l1%sc!p)(P_a^K`#8Id_Xuo5uTb(k;fVs_QO&W8a!7XEkVX zmMZ3AQS#HlQ$3Tm3Wb!fgC#pHY-iNs;-c`|v>)8t9a$mi$!z#uK)w1$|dt zdyY!*`Tw9bc_%WFxh{I*UMVK&d~#`(^RA`~ME$t_Ybg(3e;jy8`?1c#^MdIH4g;mk zRQ@QcGn8s<8z`AorTFxxge0%C@7T8U$i_qH4cq0hYmvS4l=m!yOAoy}eaZB5hNyL@ zgHzHw-@|mTJe1<|pZIv!bCs~_1(cjU^TWUWn*PTu-!IZCgYBtIoBFfp(scWX<7X#~_u5uUW>q96>CPPQ5bn8WdY$sTPP1^T3>$Y$ia2V%#EJAA`z0|Ox$ zNoajfnTSf279*VerUK%5vs)Td+fVFHV)We`a@q~AGve($eWvKCVT4uEmvWC(gIV{) z121;G9W6D=GwYn7$Va6rG9e|K*Chw-sZrWV5sMMx{Au8oU=v=)r7@ASUINK`VP@w^ zr?ic0*Sp*19TwxTm1_&ONwd*76qvs&;yOy2uu}sNDLP zCmi~?suVBCr4la2Z^W0tl+CtZ#M;jF=zd#qN#j!w%Abh@{gjrXtE8jsFmGY5c3JOl z-;jn*X;7MXph!VP4u4HZeH_7d0o&@ykipR2L2GT;prZeZp}KR!^WFi;OWe7Y302{V zdPC0Nb6j4(-obywXWECV@r?MBsR+^M70+$VF9mVY9X!-$Cs=h-%aO*-_t=h}@6zVm zUhF)pnF9Rw=X9uiZ+?w)E@wTQw%#_p5(WI<-7tU&U#% z7%0GEM8y{a1!OK0JwJCMQp?hgh*T%qI9STM`Er{;5d)k!j#~_iHbWqF+`KHkh}@!} zl?Tz)iyH&|#t4e zKnw}mUjRfy0TvxH2r$0N1$X&SqE1bg0`l2d|TGo)sxC^(WN-si-|L@Z4N}S?rI6K}O)9jir~Rvzy)G3qyziGptVZva|s;+^7X* zYFS)D(317A?1VrdHMP{aQPR>2Gf%!`=@1a`TP}A4FB%K|=l~E&t0GbdjYgnhW(Aj` z2sEsJAazh;2-2DeF7e2c#Ue;D6ujK>NLVz2v^XGj zu%d{CpQ6_J>+#7&*~&L1|E|H`BNvAPUqv8c^bY{80@X4qV41M^vMW%rlzRkC2~G?_ zBI7?z`QL)_*I0qg{y&2P39rT4)oBusN08*pF971P2$DzvrOV{`x3I1@nagN{4gW=G zR-g^`1T5D6E!xO9<==t|Fbf7jfQ7@V)DF7<7NJ=IE9?_ktX&<}rS6yCA2!w(pIX@` zq&F|tuHNT=Iu&FuO$HS?jjVtQw%8Y;BIE0SlR%b;d3k);{9YVk<@n@S`G0ZzRVN)@ z8vY0h$?#sR{=*C{%eYktfMg>t_ORkzNk;Qx^`8a+ON8j+8u8bo!HdRXcPmGOjo-y; zGSsflPpixv3PnCSs|*0GYJWArG7*q$$i=oRyMm3%#cC8-Qo%uz-zc!Cf@9%kc~vDR z(B)Nib!or!ZmUuS3SJ2p2VV&rY(g$pqtN0299U-rqVpF(|2wde<9HR=$dnrZ4moUp z2M*bC{Wti5t-eJBtb_qx=@+ZX=!{I7k?8|VW04WZU#lJ@%k8fRAbT0IvB;OJl?o3E zRuLBaUHM+H3AR{Grsb`^qOMMPWWX%F`07Lm8&->>tpW`BME^l_{D%uU+3K-6h~%M< zl^Pn!yjpw%GCEjY-jL1M5@yIq0@hg;J6nkk*hpHeCL4>aS2EQ94IRMjywI7fr6&;z zTp$g!Waahrb{$ZcH$;iT=B^yk)7rzq-OJ5`8-*bWBsdTWH3!iS2M%Z**1mVF*e|IdmTDpRJUVLt;6pX)E z2F_$t3SR8jx%ku6!prmT*flt)t9sB7U{_rk|2uY3Xp%C%I?IUS$nXpJZINfN@X|oy z3<80i`2H!RIaR%%j&G8q`!JqEZLi) zQA=+s1}y;pfDV3@5?Bx{f)3~p4dR0s;B1A?qQL&ea{qpSS^uq+tT!+=0YqjAz-L4N zOF9Acf(HsS9*)+Ea$^?Gcu*$})kAH?#bJmE0CpU7zfexr>msFtcJSDs6&836q1MnD zvj`8khvDYNfj*!s2p>=@@Du_*gK{hgFkwL)3VP$kGH6V22dAejN)RBJNE+H7=95(Y zp9&@{($?02OyH2T)D=9TqN|{w0P_S2y^<+NUW!$jfm=thxky~rYp~+W(+;i|=@E}Yk>TK**@#Dnfv*qH>WqdR0zA zp_U;-p$SW%;lwc9I7p6wLkx0#BG3d#WBH3en9y(w5*2<1WT%O7V}L+`4oF6dkxD>+ zPz`8{fZG3)18Pr#0O|!m0Y)Q$@3;~~fy@Pq3<3D$@W2I+2fk29{z7#apmjr%6M6y< zbO3SSZHJ#Bh#{c1P&uSkLhlIo0e=@3gyKOl)HQs&0O-Qt(6BJL;1MkF-a?N;XAB7H zFP1}3!1tgZ=o&%=j7sQrL3OyK!EC?1MVB322vYgoP*BENCmJ9 zNyhw1O0P&1S(NYpiu#BGQW|ONfV6dn#5M55BW>KgoQa;E4hReudDz>{iwIZ-)+Z4d zaZ&;4P8_{d4Jn_1)i6j9DX{dkM-pA26DT1dZ5)8A>gnJ~&UBFbW(94_DCih#!0Uk6 z@5{i#9@SN88~+P!FKb3P*lY2xYYe~9{wjhM1L9;LLljIYq{fH??>!#7sO4f|zv*Hh zIB~IG=ab~K`Qv;-)eCs}cf}t-ho>{ZJsG;;;L7jl*48_qC+Y%5W#LrdxhLJW@I{ep!RIQffOcvk-ZR*xkBUL`=O*^9Cf zNL7;c@kcI%s{cbS6h*o?xO#j3o%TWQU|Z5YbPVKl)gi~q0o?;Dmw8g&!|L;nB{veu zjBpHCpTNYeBFcW5PKfEiLJF0M0hbSKtbh=#7!2iNOL4$TP%AOu&x2ZkGt6f|%K=OB zADlO!XFSN)!hx_64lG)Dz{WV(?!*JvDO7`jGvn~kG7ats5JQ18xCd(~1UnuwY`_)# zG^ha(g0@f_Xr(5f-ha$+WU_V%^~wj8v^5~q16T3yYY;rv9~ch%i_N;kaX>MEMuGn$ zu&{DV`oD(e-n&a&smpUoyTVMRoqj zmt-RH-(4L+iUyMC?&;v{=1R6e!Z`dLQbjdYg@XqmT38`_q17H(F{`R0SaREs%(+P> zd=Zdp@>j>=BrKeNnV8?|03g3k++Wq>U=jqA3XKo0(QvZw@)?wav2gf*%U*%|T0o+L zgEKI}kP6OVGmIP&I75j@HKMDXmpwNcFD5PR>E%JRbV2y0nw`Gy8Y;RWY)n%AZE&rY z4u7I*zz54`#UoU*R>BPQ+A53zvmMP6>U)nyFg|)XSDfAW?aT|a5;r0pS6MDmy|5=R zATikS;in6%l)?p;KT&tWQd-ml{3pIm?aI7NS*8~>O>pjh;v27c6@PN~JE_^=y6j!} zgcsn8K`$+$eyTit5$E^KGjXkGkJ5d8uj0J4%{jsnWl`z$pH90<89btNa;Nz4xLj;4 zcHHJ!MLm0&#OJ*B#x-8Yy}c;*2k}$AjX-7Y9I@x4-tT#qT8;A5$LmiOLY;|r2S2TQ z`dEbyoi)d+s_XLO5OS?lLQkI9wB2K2NtOG>jWpi_~+|cXfbp4pzFqAXIWC0Z6+AYZb9*zCmZm z)`9+@Ytnz%m|D7n>Oh+%`dqGEZcXX|{tx1|3muXb!UHVb!(HKFBYUZAl@@=fgIX>D z^M^J|^^ozkXn-xgD9QF*coG3jIx@KjunDbhf8o43~BA)VeL$WN)|5RipA@N4uC?kPy&oRkYI+i z@^*G6dV#DgBxLdt4|`iWBkcfpLgPX=Ad3s>>EPnvZ0Uitba(e~^CcBy&`3)U54R(* z;fE7N5}k=I09phNi*zO0S$a9Rxx!`$H0MZbqJuLChd^x?uVD)kY2{&Q4W0zW@D<6d z1X%z`D@zX$xIixkF34r|YU{SF0&t9|1Gdo9Tgeu1dhH*iHL!4(Ls4k_A{Q(<{~=O< z0s*A|WFdj277JyFzo`4Y?TUMdXjhK1I5w$f1qASjzm_hO{NghUeJu3#3{Eio9kcREs;csEs84wnruhr+HsDJ#rde7#fZ_n)ha;Z< z1i#C8B9O^@=o$lA+(6lf&cF5q;b9RMaP0zC;F*B=oB*611UP{g_7TDvcchWZ{NRY464Asxayo;kryG(5g$CS1j=HkmDN%CtEL-0L();|ASEp zJqfp1YQNAIw2DK=q6!16FRQjBJt+=G6%&Q10`nKG;I^P12TWrubil}hpj))CFu*Rr z0t*cXhla!e1b9f_0p4#6&@@0hU`Ru641260zXasL2K0)9u7Ns^#gp?kpaDo8{)N6l zCNBS7eFIF94bj=l63Qa*vIi;R2s94q4tstV%p{P#yP%CAt=(K)EJ0fQ3I`*cM*2J1 zkV42hZZ4Ltax;p7Uc?|d0(ys53^9=FDhAvgSjYnk z{_sHJ1H}fa0FA+ChzXE;6A+9Ta3w-v3j(}R6~qEaX+mV9z*LTdTq0100~iZFgYhtc zk06Mld+03yZ9>5Ya3v0)fLnq0g3q8FT8Chrcd-tXK~O`_z{fAKhkv?clM;Q%B@vVv zvYZJ7FWDNFF2q&xg=E186u|crcB3%>G!T#h*b+5^j7B?8ZY%;R;|b}%pbYFbw{%w` zLK!Xa9n?Ei13!pBlCl(JApH`c3aRTwbUDb4!lA*Q2L}+vaJ7R?8z6Zxpd*lf1U|kX z_?Ia&i&`g0ml1;D2j(mh!wtTq2~e!FoIpb=Sa|o9CNBVmJniSeq6v({V#rP4Q=0Z? zEZHArD5`g%b5Dl1<=&;0G2>;N<@4&u8jlxc*4El^Xu2!6zxr*J%XW#^h|eSMGb~ds zbG%{ojymY<>>NIV&57ptSY)+pTiTIGsT%a0DsYX)~(|}YgZ_Am0?D`RmQuM?$+mRoAnyIJl)i%%` zi>w-|tnK88llI}Rjb4jnvF%Fje}5pg{RDRa<{kaG&MV{W*eRQfUvhJ( z@P-7ODx76TbLJjH=ZM>+e;CTQdiPrQWq48za_8elBXPp_xB8w;asf(*MAKQWhjb&)*=-uCmZJbMt0^yX+DaAo(9`g%_<_)nt%QC{ad_=XN&|x z@0tdSe+oK8wAaiRL4Q8Ie#Xq*dBvtFrY*^nakZmb^R+C8lwD;2Bb!|C+ z`-xp$$HUeSh3sd{33WYmrF8yST*5OC)~DaE83~@frFG5lv2@3LKKRIXE8D4eYH4Y9 z#rrPkaOs-k+GeL69Ipf-`!cE%9t9ojmZd%<;%2dd!*CUAL=F~2t^;h ze4S{Ycm4Iy8!?9u?X|pNaRVXuTbqPV%u@_y%$TqA3c$9=KVi2OiCjzE1_vKO92JFM zWHyL17Pt+RlCv9pyUcHBu|KdIh_xbRd=@wj7!H88>40=XIwM_?Zi~y12htPijr2kKBK?s5zcMIn5Rfw{RPZk| zC|Z<&BjeC3`c8Vf2F!QQ^AimAU+HYj$)B+bj2UZ_crII!7=HOOt$v}f&i$nQ{QSrH zB`JADg`MAW#|yeCi(W)qX&Yz@+)~iG)7PUXm+bST-O(h!q%`eAeO_N4j_`2P)#>iJ z`Ua!->25&*V{Sny{I#6xl+WlXeSMKX-yG!0e&cl9ODXne^1;tI)Z^?njWZv={k8T} z`fCB6u3ds&yvD-^B9zcxHO(7i@qF^zGcL}gu%TPru2ug;RA1!xi_&4v{z7-^nZV9r|}?{DwBFpLucdeD>FqPG1^y+t4vHntIlTq!yf{eX*Shr@P-(w%WXbg%gUQ*sOrq~g2^OuT5elmtZ&9E zr4yU)*D~;)NK$IzKDNU~F=9QNcq}%aE8wd?`}j5{s;G~3sCQ%h6K5LRorN`3RBN() z{OMgT%|6Qa#%j7_`><~hrwRmw$;Istr}5m7r*%k2-|G4a+wudH$s$*C)adyK^=wiY z4-cGV7DI?n7ONY*#<>j$GhS_z*OLk89OzH^K2LGW_1=a@mo8@&R2ES;d4)5hQ^#(# z$0r?1F$gsD?Piz`zS_ribqmY6k#JtYr`1k+Y&IX?BtB>e6FQl#6KAs~zh3EOgLC0| zx6T`ZFPhhj99ImK{N_$8kB|&)^Bb&ho|><)LeJD2Ikgo^4GRmK^pv$w*p9N7j`?J? zdNkOajMLb??M|0q#CO}TdTuwAS?aCKE@Mr4N?Jb7InF2D5af9)!`anSU(LYt+<-Tn z;qVP|{-eVAnFy`=58sfPwGh0g5hI3{<46SGxj`%6PZQ@vD!>&5J1h~g`SZ5{#m z;RiA)<4%p^C)@@vN7`Q`^phY~^U7SgJX!}*_a?Ra` z8`5vO z@cOr_tZlk|~VCEU)~W_8+A75lr&jgN-xevT*N)^@epek>bge0X+kVRf&MNW86s zrTJydlSi+l!##4d($FPz8(G&{G2l8?u;#Rv6xouq3DO7FnOwig+`hK!saI3y&7X6x zQwSd~UlvdK>_=6Ygf3U{O`K&bt?rY)Y6Yv1!GO1THGi*SXWgH#zKA3ll&T#I4T zfA>gI;z2aKOx*UE?IMxiLuct2ZcT=z#R~}v)RxKJ$~Q4cX-QKn;hdAs9cfi!7`~|g z@dQ(J%b5_#rFwXb8h+}goPr?+;5JxiPIu*&QejS#$2(4{H6u%F#(>5Vr1dp8ZN zm+j>+*p_=&KC|r;?gDew^UkMM#KY^p5Z@TbTkK62-I>>wA4_QE_$aPyaPm>oW$TZs zSM_A_4eyxW+0^MG;y6$ADT^`o;``QoZFF+8l(UgeX3vw)<1P6&*9W#FxAOgCcb&~7DTSYuw6YqRJWY=Ad@vrxxlcAB{-ud#p|64I zRf*#@33Y*iRR%P(FXq2Ix#@jBcjlN!hmhy~1m!KUJEL14dtX&0D(l(nMZ2QadB%aQ_qaY(*ycqIw4QtU=jsz&Et0oq_FFV8h$8h6d7H$P6OT5P@9M(7^VN zKiET}K(AYA4=rn9C^9X~0qOdSUPamhPo`JF1^=>Mg~O4nRn|5ZmR1%vHa4DLrl+x# zg?h)7F!S-2-+3C}bbZCV%VuyQFnSUg8ymP(Pu@*I93%AnEa1C$uj%p7Ajd$ext394 za=P~Ai@kZ*EbJW|3VM=Kj1!Ynld^kK49}dtkYY&D-!HiCy@1`-b9x_z#0J65ajz}A z)?H!FWnWnU*TEi5*f2 zwwm!NK>-sVpLel;sz_kXWlJ!b956w!CUmNpL>be8WAYPUg^9Szdu&tMUc2vk^&VB5 zv-46!gPu0j?-IyQEiMikt(Dh(QLy)>WzC%5>w}!bgJNAA+D!2mwYCd%8$Z|SGOJ@M z#QbOtuj<~x{KZH8M&5{HuKn3u2317N&|r>zdWpq+mbfnSrkwYKOt5yOFxz`_EO2-?aB5JepJX-Z7Am zOWQrEzy55b{`zUZt~Bpt?>jEuVyUdB`x(6)iO)XQO$jt#C{#(m;e20nma6)^MjAeL zmaQjsVz!3p* zAwcG``&YalzhnU98?Fb&)!QJRGc$UL!gr%IW<%IUql{VJFi|`Ndi#y^ic4MdHOy@DB#d23j7MZ$s!rW$@^anp{C>9cTXX8vga<|H z)Z}Q@&!29yiu>tNm)21nzgCqHoj<|oG_Of9zkBn%3y;NgclWS5nhL{{MDK9aY3_d8 zP=jDUG0wJ1W<3=}IDN(N0a@#;`MbxM4n7;sHhFTZ#LSt<(!YCpW|FqlV=`iVieWN> z`=?S~=?UL&0gAOvg+&QUd8RQLw6{Gb7-r6m&b+lV?U3dd#G)UVI1X$nYB6C|({6td z>w~&~NW)3?i%SvXw9g^7!CM+1KW!X+zp1x+hsURiS<2h;%90_H=bK8?tTAr$Ar50| z=Ec4*l%g)Et7rM>Hn$r)GTzUfv-8v{M(_uH0} zy@NG3QerW594JeWU*JTct; zdS>t(A>~m4y9w?KU87SqZ7GWuUl2;!sN zDX$TlbmLMA26yU?f9PXPSIR>-SyJ!eLQm^Gz1@!HI&4lgk{O@h&&z)_-@YFyn^4oA zY=7$bk#@w_j;gUjhm(_U3?r!I9UVm&h69|FH*V~`ty8Zw#JAnwK1?A;>sXG(hT`at z!D}11HPfy%f83w9<7q^5PjYgkuy|H1T_=TQ>Iv!7hgmy&*V){Y61NuHzxTKZvM}sE z{TvT7ea#WI!WicVC+M!;43DcOZnUr7IGNiTSbDL%c%bM)-X_8G`z22V%Wb{9Gr-;; z7?~9+B88IbU==WW$CD#gLR++7s-S#rgE(cC3DcF&YsAa$NqabcUzaF^y<~rk%SDJz zJytU=?hWT~+1f1ki&_QE`p^4iDRnu_Hu1zp(tEp1i~mr5t!S4kR+N1uX-im;uE72S zuDls3veIh?Zn`r%9$@8jtfV_~pWXdP1f6AKi@a}Clb)>k6V;C04~_kAY9-o2M%N3M+eDJQ3O z^o;SZH}hytxZRq4midl5Y5g+=eS`B{Xj9W8HfuRC&k&~`3`c*k-?r&xCX*lQ_Q%AF zV(|^~V$SPduWt${&2GcmcxLw%9A6{pc%{M7HcjSlnX`v-Ku z&%bDB&91>oZJca-oFQ*5P<3zc)q_sNsr#9S)D8?jeizOaFCLybuHnPtJH#6oRw7)n z{#8nEYQdG!_WE}_#b|aPH~y)@_*`Rr4VSO^N9Ljo|IE?3VD!R9 zGhF)E)|j~u#m+BBw`MU79(;z1Rt$8D4eJ(bJ9tR=*eL}Jof>_ywT{O5q#&8s(`ME@ z)crIws-`mv0mt|H>YpqLD-n_o^B;OVS|EK;yW46g?4Vew`B;7=)ebpKXcL##X3FYL zZn>hjRL(^T3~fI~ZZLU=?2)HZGk+GeZa>eH8$$ufDe>}3Vg>6w8f%Zzwgu z25i|;d-}d{pUQQ}+JaY`ch^m|9W4H=_!-Z7fzj{qMEseoEdqT;ubN!ZqS50OT|=VR z71_$i0%x8%R_3VW#Ti`LN})?(H1O_tRNn|SR`ro)0aImVq%bF~_S!>D45H7@6)-r_ ze%-PS%bqNGz`AYdLwBF$Yae-k|7c{J?}gL7FN<`$>H?2dH@-ewneOs+D6e#DQcFt< zpK2sKi-`Ahf$IjAM@};ILM5M{f5%=Qm>cR{bgXcSBc=u$MK&-FXCS z%eQY)pO5ZAN#WOKsTmr@XqX!x46L0Uf3dYRCEFwE(4IT7KPAdyUA-@T+`=9zJg}oE zR?t7T@?OrJooS;AMtj}%hqJ}(iF@OCqH5}UtZCHyu#P_c69ES)HR2l5Te}_k>FRJf zSh-?R8FX4o=oxuRWBj2L&KGS>)|Xm2%JB$3Egt*sbbn7k*D0hNpPy6T9b+owjjB1F z2zJ^|V??VS|GUIuS>?D*%_q)r<%rx6Dad8K?&^UZp^Tgiz7;K=a=@( z*z==9+EItJW_cuhb9)c)u%2W$GRy0=*elv?TJ2x4D~so@($TzYl-T&4ni`?o6wYln zGtxU_eu&v_9N}~J+xNqlw$4UH$Tk-pF}-`M`F8(B!%MUid^``UYYn(=VO})SNL#!( z_CDkx_f$r?TZtUk%(Zu=*t?9kxDgaQMjWXe`2ytzXY^AI9)HfdHP!nXQB~fnb+*hf z7g?}&kf|vmB6#~$?TGIVlT%G+Rm~2TH>bP4JY35E^BK*3Wdk} zKAAOt3H)5GDk5?E@ac$ol<$Vv+Lsg#DPHQgwh3{zwa`(p&=c3PyG*$FgzX~r zo?P3+eOAeysXs9?C?PWD>a=2^z0~lR1ZT^{p=A60xLKKYgUO%gqt%W@UE3H!6MebuY)oBv>3p`~AelT-?xk znKC)D;6T+8TmLjh_7f%jSl6_-9MbPEtx1}#W6Uj*IglzKWF(~VT5$dOjgRY#>%-3o zpDDPSu5>rai;9kIyNIAIDs)m_E5CVHW0UU71LC5L@l-t@=(_OlZpVZqbU3SRXQnQe z+x7L^PF8b1xlgenn7ZKXi?KYs16I}bnEsg0JOx)K;wzZC3|Nuoea$DGGe`LE-u=|u zsIy6j%I(o~_={t8bvDem#7aW z^8c)x$waHf+-}p3&#TwIh7~upbg-K1ea4dJCg8)Q7gss^qtSErRQSutu9k9UXTd{S zu6NYfe747744O^vo_h5RlOJ6cW3PYVDpykITMe<#gHC#vKUMztBvKNWUj0dGc87zD zotRg~Lrm<&N~+0kR+6D%^gV5(25isIcx=!(U_c!4Qz(%;akYxUkhUsnS5|YGQLyVi zbl6stFI>ff2*nF8BBJ8+OPQZ=Cur^N-=*?QKeCpS_QhOpkDP0UTzleiwTrQ){RW$_ zO-cLnZXs;TMC#>E7AJpmluD%V3&xkxzGhGg+49r2^#ILRu2(xt1{tz35t0LO{5^L< z`FziOP*CjF7Wm3~dL&oL@S*aK%jaTpe?*&|WG*_Fc<5>?Hj>6!|6ube0_|7a(5})s zz09lH^8TFsN2uy1aM@$f_2#f6e{7*1|~q8MSUjhh`|8Bx4p8}I$2 zAF(MSlv}OjNttV*PJ*mL^U$lWSvHbu8E^O340Ufk)#sRYeXH^QY{>~@$zvCh8REl- z#tx3*3;5B5weOPT{6ez0y7jMTX6>5Ra;iPD=0xt%z2-a2DaHza4D3kPZ0F#xm@s+9 zq8`YVg1Bo1Cd_Vsi00 zx%JOx8l;rd__OAYKb-v}lPeO@n6TCV^l^jJJ1MX1jw?<*iO`K!?r&nx-q+b(T$SZ; z$R^EVoNz2k@Vac@DZ{y2#DlU5R;c7gG#z>>pw+UGYofQQR?P?%vnwoSuIZ?)mbE`s zKIQkK!XuQk;pJ}`INX&wrps|Rt_pE<-pCE?%-Q_LH{@JbrN*$=PGP%x-}au)cb>kQ z6MHO}Z%p}W>KwahY<@N0)w5A2YWjJaU&*~xiT73e+4o;Bmv9NZSY_VNFmC0X5q{{- zk$z`&rt<^ZpI%ePjOt8qOoa28IBXxDoIcsSsktB_tx)3e2<7dtgpBmr4NsbPS_!?` zK9v&r_T|@2Um9XZr#CuMT$nNNbNCVhL>_HN`goziGb=;tiX+3_je^xbZcrbU3p_SC z*YPy))BLOY`Dl|MTvazi4DtpKNAe-|c|~M;d+QUH4Xy$Qn(vj}N7=R)bKOktqd9h| z_g##-$fext7qc!^<@VDuMOybh=X^f;@kwo+v{D!6;MVO!LLCeRJ!V_qvKug5pTAW1 z>i**khdk4Ij0LWL;C~u5U!R^=et6R;TGK8gjnL*ZZ92x=ecfVI%gg7xqBNZ(-i^Y< z_JQUnrjto-Kf`j~`=mYHq;ZbT^g?`1&o%Q~GDIeSdt2#m!$aA(_qdweZuYxjuRoI3RKF}=Jom>(4+l#2tO=ri%&^fGO*Zt z-MkUeUwSX|*iS^(`^UJ@Laxz^=`2#z_ph-Cjyv&;MZP$#tiY5}DK3r2)gM>d9?Bs% z(5Z=ZK(ubcPRJ5Ii$GS0(@_)DX0lsvAglGI?uR-KD69fIfIe}W}4cI+M1lxl3r!vw^9S1`p!{<-_0u-n#)q& zg};Hwi73e5jlAKq0r}v?u)RNGUn6RFxtCvY>yJSIFX$Eal>OPkEdwe zO@kfQ_wlYRP2=^!;h}+?%xQHs(hmnUU&PxdB%Dpg7!oO2-Fl>y8u!HClPW*YXDK2e zA#*K8_1(BYp!C&EdR6hos-+jA6C?Hzk5Y@rQ@xz6c8Jb0V9PG1kqQ$k=0IA_%5578 zdW})xPLV!|Hu`Epf$lhdBIWeAR5qKVN_zZ_&)O$bw@fJRjbDF^Zk*{}!h>;6Nvfa9 zJer!}ScL+O=9iM?aqH~Ln(g%WQJ@2MZ?7~wGWN+jYu_pD*3CSur$%qJ>bh$L8NYry z-KO3u8{mHh*|(XEgY_V*zOL>;>nEuB$*~B=2Y=Fg16a-hd$?(WbHpWiQXx<6hdVX9b(9h_}oqU0F;%?GCw^E|7PhIca z5QF0^_YT>XGXL6ObG2Rj&<(+!pOw};U8gz5gdT4F`9b>aiP2e+~F<$DSVEWSD8fHld_pAvMd2xaMIWZ-e38&;mSGa%6abWzHJ~DMSG0U|Fb}V6pPwC@*l^d_B*cH6kqX#v=)Q(PfB?M%D)|lV(!@8ul_Tgyr{M9k_pmY_roH3WQr&1*q**Ah1Xv{l8 zGQQ9qL#9c%u)ZEUac!9HPP2RC;liYr_r0cKk>TOT*RGklaH(!3_5NexUGdSLrKk_s zGmisV+XT_2*y^w33o6LT7rtF2-`!{w$ZA_P*OUMvLQ9FI|EE(SNA+lb&~(>RU15@*?wFnGdGfxF zO@OT@9>P}jvRF8^{6+aw;g?Fv6bGN@ENkg zTA#mEjUGREe((Mc>U`|sJ0Nb!&gm<6 z?OfBnjScVDXlvhDr}F_>*wG(wGo)%-ScawVNJqQCVM2O2-L?+(I>8<6Q*|T4^f&KD zq;VL$$vt`UgmF>MWTj5b`U0hRHXF`h(IJY5X?YW38Y+2P(%0(lr}PlUpASE+Bi7+T<%3r=h3bo(YFdbZPsUZ*vITKo%ryPBXzxW%?~|3 zIuXSSH$>4eoI>Y_P1u|oPLK^pDk)Oton?C$zQ(&J>HS;x9g>wiLstGly9Wh|E}w5< zZbh$0zQ`&YK7kP3opJ0MX5#%})$F%MzIwsa87)oxPZ5F^$R7@2o8|B)*t}!6`zpT> zzGZD}fqfjKFtC2(eGIi_`^X7BnRokl_c6wPeVXvBk_!KZ@Ntah+eVu7=W+c9F7T>4 zvW9!_edk*%9Ojro(2Hvp?RZms?(BoTR_rXgr*Yaf8ouv++_s+DhP#$&^5_^xx5`0% zp2z0j(qrDN6czA(qJI(}{{2HwORF3Xd_)Zowqq=0qyH)4q~>y$_98VJbAE{Z1ej+54NJk$YsOQGUxq;9!-fNPq_GzIg`C-A>pEY zj6=4YeqHsa$)J~;EG%qoyr!bOnvppo4Qefd+O6&{^hAq`u?Fr<>bo1%G@5kB)kW%+ zo~Vng_3+pdGL?YQRTZAz!tM!f_De)<4bh3#jc(A<-Ty`6{d?xbb6LznUk9_Zm#xT;-Y~xMZ|^4g7FmaI89LGP=NdtoiKJ=Yt~} zV}nYT=AD71vtK>p_EilVb$JVWtQT?)|L*3)Mg1V9E4))2zvE`ESBoKEi$Q5m_K%Qm zRlC5JjD|0J53|nhEC_hxVJb2>odOf;Tt(eWrmTQVC=ms329BG7hwm+Dlmn z21rAs5uAGf?Z$N?g0Da=?92t%qz${)U=tAdb^>WD5J*i}{J7K7H={t>HYr(w^aZG; z??J&Ei=hMtQVs)1V{q^R8Q$QVQ!COL9N~oTf1J<&Z3S6OY=Cz^AO7X5R7*Fc7l6NK zMY<&AK)8Ud^3WSCXG6gImRHG%fb-2)$%}yZ*pss=RPZk^<6 zt>u%}`&L$Lk0ajld}gLiI&=W{yo}(Ee9(0+I?sdu&J1rON5?Db*IS;MaBsx#r(G+% z{zU2e^~X=VLA|W_Huj3TsN43|?AVNyKd02Wt8ezcuN(__>uJOE{Lbbbno%`t9ICBG`qYKJY&hpjH?${gG|h=7#^0v?)Thu*0hcDKgbJ04ed}IO&9Cj1 z*(+HkOC4t_>ey48jLBv!p7v(!?EN@Qa6juAalYb)C+)W$jC)n}Cx)5$@d-+oz@yX! zn+s?;%bKNLN!g#TFnRryZZ43rpJ!Bt(%@Ug9hz_2TS_dixRhml8q}ZSo9ik)TFHGc zaQeaiQ@H35$(MT=z8K%;w|Vsa%48bbxsl0I6V8u)mAp+aiMppoFc0OM*}DT?&~RyN z!=oIVvzWdlC~9oSd5@PmSX)(Sa=p$MkdMuz)-}2Iyj|9DCoY{;)+O_WR&cSXn*u zp;pQ@{r01dA|)rkCmHX5a&a!_lpx!6fdS3wOn>cbdO_C-xZsZmlm%5)4jfKQw94Vo zGrvw(r(^d2uy&5km4EA^?YLvx$%;C*ZQHhO+qTV)Z95&?w%u`Zd+($FKKq_iyKdbN zu&UOZSx^1uGv*jSpUUJcSd4|8OOeQRe`TBzp8YrzS&i?MV>;yHMhJGUKRKYmBj#Cm zH$Oj0ZS${iGHa3U0>$4*bww(3xFZ8|MKe{El9-DLC-^SHQXFFoG&Kz;C7@YJ#G zmNm&$O7G8mcwhfS?66DH+sBR3YAXo%bYo z;;bH~x>b_>hHe+gzGJNCC2@-F5nNi?l=ORnoM$h<`&mGII>pUlo!Z1FU0!L^fbs~U zrP3(Tfyq1`0n)-)fyu@CV~WG8!7?d3Fv>3FJwQ%g4(d81vEJs+bNx+>jI&^ zo{xatf8>ce}L zVvQq;3k&Mj{-KHdEH^zbCb!WzEzbNQh&qYTekrWKUG4I?vk|7>xZ(4b$2Ubq=tu{vg4jwMON9ax5Wy%U;DW6~*Q^TElf@o&bI=CEf$ zId!dRE^@sJcR@|p?vYd;8*qV=UBQIZsbSfjD=H6ffBM?5B=+ zd~2}3^I<}j?wNNY+7DQ2P+0;GM2lBfTC10tfG1-Wxh}jcIpbc0cV;nJleVmZ1Xf)E zuLc!$3S0t^^+nE`A)%@T9IF|kt(wD#p8~+*tbeT35XT-D=fGd>fK)i)0U(7RD99zK z?nEi;RMz^FpvZ$@1RXTcu%fssX8~Dxs+dZ7 z&HifcL5bf6GzORh%1hvsg5%|`Bvy@0JU&UM=oByE1He`8id%-nRv99qS$~eXU~-L= zD$U6NG2?#wEx7LB5LBrHBFfYxAQK|s7#vkIKP&dqB11t>ahtA}2o~~iKviUYs$XT< z_2YCWrfBPi{erlrhmU*JmF^>+v_fV$b-~tT`8u`VY&qU4PA^1WV_eTW za6Yk?mk%y)#f=ZCtT=cC{4B9}pO1YL=W1Ehn!&bpt+E{mc@B*spLOQ%bHSM=aF>1v zNw?n81rptnY1bq0hK%)m@MBUkz(J1B8AAT08cE^`x+sf<7Nt_06Ls6Sla45TuQ94PuFzjaYh`<#X;)V)-3wx!E2P9gq zhF8_OcBQlB2(Tcan6yer86ZTI!X=jT(!BNtab z{9Y>;%pg*nw42_FK`R)F%qZTgXcaM@0FRV^2f(ohFdPi&r&d^QR^e}I+aOsy>DStx3 zyCPYE7}YqQ12C(a$~icWvo)MNNb=~lv>I!T<1~MpL-~O^NNT5P#eK$tj|;-$#+(yD zW=M7sQL-C;V#xFX0T7fTEgB-{!z@iE*&zfmH9(}p-=FlHp?AWuUUS3?$CK$Dh-UC+ zDs%K7gAlB%#D#M!#EfRXWs&ALPN|_OeD6@1TQ>vp7*xUKRB(n#vpgmLKFv#jCNBCF#yM^8k`(%@Xqv`3@7N+^PJZd^QKaMP zB<6fPA2_CIq*D9Pbl%o^wFP6+mYDCIq@N09ECWXFy>OEe*ykuP|DbD|wh%Ge15aqL zU;@-zp;IJ1FWwzC`@4NDn3ikk!W&+Qq}<3!#Hte0na(Pno{q`hr_F)EHWDVEmuE$oM=9kC- zZ_nE|SoJVQJ3=4dR|tV`w=K3s2_4gtxsjHdNAwL|dmKWCO)yE04yY=rY{U4SLB}gs)L;4lmEg|5Qnw*`{?E>z$(~Bqda)rH{UJBy2 z_g&KsZezK@X}rXxn`CNYxyr+>TFKeBSP4)}Su_5f!axNXAx7`|6z1vN^NtpN;nGj^I z3ar5%$z(N1DuaHf6hd3u3ybPT$dk8E6tE*L;L5k;e$m}&zIVg)yk@8L?7CO&N-8Y= z?sqQpR+V2U^Ajeyl=mU$Pt>1L@g#WkzUx?=N|&F^VNq;tlGA0vtVnLu{-_=D?Bt8?j37@yMyixi@2GNEq zBLAGQ<({pOthv$@hc9qvRcFdU$q3agKIN}CURaw^`)>3eStu23yt=r^qbzgJoysBX zIEP(^9Bq6(*!Dami_O0ST7=3Q{VsWIej600z<###zBIBF=WMewm zot%SOkXsp`eK!0&3aWYWJJhkgtsF^O>Kv%Oq^z=$0cV|wuIc$$3He36qXZP6(YW2u zE8&8k=kORtxnfs^Q*?_qyD9Rx@59)-Iy8b^QeLR~FOK~vPCIRB(~^er%gEb#-G0M# zUxRKCOyII2sfbR>wui}gcQ3-dl_p0Is=h@5kqH-8a{Xw zBlo*x}MJ#9&hOEjfUZ8Wfz`3y5TDAiVWwfsO;$ z42ZrS@_W4O`(0hFnypNmG}|3Hh`Z`|EX}7@eVPuVpX5uE!g)Mo&~y2F`w#de;xEWF z$SuUN!lHK<%3Ix%*ShAw3cindw9{%|CtvFlrYb|I{OAcY*>Zumwh<2EfSe3W+m2CK z+3xr_-D%~ROPnpp1@tgdSAl)0PJ*otvx1Pfg%_O#LE@54C7LCmsZV&nc()}p0SD)) zQvE8)4_a{%Ea`@d>g5v1R(4z2+fjEbx^Rbit9&?eZyocBC3(8}4)=8kA9RinD-mj! zucYCDGMnJFH*e{B@d&6kqo9IV7I_kxR{XWuyFt~em=2|0^e{q(R7CRt1s_lqXGisF zKC!&Ci{7$iMUI5yeH}QcIfyMEmyU1J3_!J86TG(Qd**KMJsQQQ#@Q)G5JRlbLT zz8=+L9DxxpKm}a9kMuGm$)($n=lRLVqL&N=%$d%L##QOaFstyGYJHl(Jt6`?81Y};hXeE<^N?Za50NmiOKBVS4}2bw2IKs-;C z*haS6sXR`aI2Tca-FU#MkE3FY)xlDZlJ3HiIU_RPM@al{>nN%D(}p>=8B7>vvI$Na z4<10&AE*PqZ2sQZwFG-zQ{mw!=A}8l8;&pek>)eHm8dVtt-^W&ULWq!hflM=;f+0t z;MXbpSM^>2mlMwM#|wLpr6^Sf)%vP63GDvcwgK6k*~z*|kdmm-KH~L{Ed!5V%_R4w z6&bOPCz&zCto+>KA)@n+dcatouY8B4%32+!eN!dxwl6y@Vy!&vpW_a|5zC{3--gt7 z`>` zE{;{Pw3L-KepC{%xdybS4~;hKD5-|b;TJgV5e~OZ7-_}>V8+Ec(;`cb7W3$fC}=~a zr0bwi5c=Z?uKZ5=vMOSWW^aJu;+G}O06-4K5Q0~D`G}+%37TnH!b(5Gl4wWQ(<4ip zDPLlMY_pF2LJa`u2^OR6L&?yPnbLEI;0^0YDl*#)P5`N@v`H|iu~uP)I(VBV65_a= zDjQjF`m_jx?p$@HLD9$;iW8npd4<}qgsaO3a*XD4;-KncolI0lGFF-K{Q%9w}0(GDvlyyUR1tZFc?}+w%L>O=PflMs{S_6@|Wj z7_{n~R_piL+Q5r>Qaa%2ijV!I6}#7Dm34|5z#-;RCEXNF_Kd;+(Vd4naF+DPfO}kJB}0*Jzi7 z2WjioKqJW6gE_4oE%F*Ht@$vUT`XH`c8D}UkNP`wsjJGgt`$+BB2lsej`;A>$}OgR zPDA- zwFFxAt%9IU-9A-3Ew)2A1D4zz*M>Ro z1><6>_`q>_X}xYl2KO%Rdz>8-Du)(l%)wtcSg5I8d5gj$l36KEl2HFfmDCdbn9}IKB7RMzWe*-f zxIW)FiZ!lbeXFFC&uli|dqX?b4L2sfiZ|NbG_;0b8#eB*HB(A*oR{tu!JM&}vG!Rk z{nm2*3IolkfQc%g7{e7V?KUhK4(82T^GwXY`z7P~gSx0iIdAdj!&VzjQ9WZ|o!JKh zX3G0O<9=+l2&>tFxyh}J5uy9*Vm>Y74}$K5NLArseuAJdE; zrZ$qCq3aHwx3`tq>Jx8$2dWyYQ)QD?C#HjMKX?%w79>1Cq>oz~)?F)p^8q=2Hk4E> z)7WU#PFb&8&`zNY`N;bouxT0ONO3Ug4^pk)?s|lhdeTrf^OQxY&4gNhlvLxd+Rj84 z_Yg#0UwvwnvYl)V2BV6aRGaCEur(wjPIiZS{}{;d-ZdO{*(ym_MECM|vU(h{%>Wx{ z`V}KkF&$Aamye;Gzj&dp)tW-eDRfGTc=Ln4M&Pvar!qXM7E=sny|;BxwvX&LSAwqcUYsnl*oSCWO^>-lY~babq70AmuxLj8*L!j7 z<`7Ocdm1>xDZ1D`?{3^+j$B(S4$c1EE&RIpT3j!hpydI1Hej;+w$|>wldUWbgC+|X z6gDGIvYMdFi74DcvL17Mo$#w3q(WG-6<#|J+qcM=U-w`0>YZ)ech_qE*DuqOJDI+E zbneV7C_Os1DA`9iU^!->~Eu!zs#>Drvh#q>_&$IcskGdV2%>+EKVqoU}4% zBy)3yzppYjsbvbiT@^fZ7;Hgm8}6PL)pT`le11%LnX19wgJ=(X(#m9j;P-bPghx9* z{UNP?80}Q-fov=5%CteYU|fXP)Y;UQP`!H&vAY51_6oNq`P6@R?#Ph`XSSp0G&4H# zNQ0&nr_d}=Lu7Wzt^slS{^Wt&PfAA^cysbM7^r@;6CE@lm_ zprv3?kg#5FWZvtg23qlp1qYsTCHWY3Vk3Co9(`j!Qhm85+dYiqSm3^TzkZr4hIFy- z&-O7ZqjFxOW$nY-zzYTdSvMIqJ+VM$TI^{2?#AMz4wn(k^G;RzJV?LZm$ufZmnh{S z+gD@r2bdlCZkJjcrgN{i`=zELM(?lO_2MTV1l)~Mc$Cs@MR6k7s!T~5*dliq1z;N) zPgj{!_-RPyuqA-#`wjNsF|R7ja~^q7{jol$rf@CoH;2MIhl1YPHZkPYd-0sY`vZDKwD#VQUk#yU$+^MhU=lYRn5=ZE_vk~H5&TjI?IxHzd#w5I{|Iy`9= zFH}gt+QsnQjYAD6tl6U0m?_!$H1NIKS7kcBO?sDpGK_66Cu)CpU{)Q><0n>{2d?SVhv zHwJZS&q?g+ts@jx_Ha+_JzU6X3r2z(*d`NYx#_Mf8U;t#&@~$3zU4xU>Pk$)&tNMl z>m*FwBjGhR0m#@l+(^GOi77;Wb6$ zjjp9fd;6uq&gy{FD@D3We9$1G6p9s)l&PRnKj`jYa$r_)Z?xb$5uS!q=r85uNh=f> z@x5b;a_vUsO057Pp4N9N;T#`)aiNu08h9V}nV7-cD8CYM*QN?=7;_CRG8Kzb$wFCX zMU171=s(MrlSkNpo+SxUw^=4oG5J`YDA)yGn*Sbx1cz0R2oDSr!{iYN6%??^*ow=T zFg{$)?Svi4xAOFG>ki$5ZKj)BE<)A9y}-FRLTt@W>vkq1;-N=qNCcCT=2tg27h0V% zs~Nmhq>UxzCcNMC1fcG{hft_oBH%yiCIs`n_w23kx+PT)78V(Ttkbzm~J zT_wFu0Dc8VQy<$Z{N_Qsi<_dT$_s%#OFjyYB~MTU(RP;LXe9qTnzE$Bwi!{i2y=oK zITUPgtr^_%4i?92G!oIBI1;SB2r0 z*{agC;gs8W4gN4ICQ#?uUa{-F>Osj*x1QbkO=o*TktyNAhfpySQ+FwOaq-V9Sr(?% zn2}8uL&T@tRYgL_p?6c(ddM*}9>jZ~cS;t)IDZ513P3BFnS+wBRGbl+OC5P4#vLQg zfl+Dk766wkP>UpDbX5(o&5roA9d1k)Mc2DKL&%mkhFiFsw5#+wc95{~2Ek+OW2-D= zhRJ`E;H6SJFH&>!wKA(yqLd)2)C5HHwnQzY3uEKt9XR`bvG5>8m?wF`LFE-8E);NT ze|ic+-@~6qIB$Z4B64?Qci+eR?p;4+IsM8Ub5#zQs`2rb_(o?)-vFujmF_Q!_jLpz z9Z;-FRiBr;Y5sa4W{hDMeeGc!+ zleoEcf-l%{NjQ9nT%4&W8Yw+@cyg9yKPuQLtz>|9y}9G^_-1Q$b}4p;E4{JPwa@BG z7liN!FDa8({Xat^e=k-2OVG%F&t3f+H1emc@^@&2{f|TRugnO;e=@fJ-A?g0O5{K2 zqrd(1e_}@dv{e3)K>9~!gy|mzFn@zE{)S-ufh7J`lcII|M?C58Cj7rL4}Wpr|Lb@X z{U3wrUzvv_Pbo!JoRMp5#OfHfY;&UYj!QgxNA(G<`tT`knDmHha$#@-ccoS$v2E(+ z#$uG$1J$)Thc^KUO{yk7_cAUNeql1nW=!-H>PYpJAxz}WPc54|o2vq=v59w6br~6l zDSWcy`;VOKmjW?x5Po?k#@O-6X>AQ_sjjdR7CHr5dS~+yklzO9P@^USE+PhkM=|JS z?@dZq_X|hN-;sq~FDb{7B^T=3*9@Vi&fPQFG@Gs0f2~pgjGPyN5ko2EaEOC|!Me#!M8*}X>J05egUrHpN9cy;0o;8%SN3OC2zq{{|M8^5rMfy;fyI+ z+H|6(qu@y%cK{uu7p1cUyO_<-k3=W6{lH%_fusnk!FinX)VW|m8!NSQ?2q%$aLjYJr#G@^y0YsgnG zn~m91@%H|eGtrY+i9v)CldD&A)Dv$~WvN}N>mb*FtVY*X*>&xhYQ=z8uW+a)CB-v&vx9@52gQ@XBMf>0DV3ck~l!Hiwi}5Gv zCQa$!adWN&ivF6SECDKq6ec1 z<5v@<)Ptj;dg|Z%UFy-t@ABI@4!1g=mMk=(j!+h-c2>u@ZKKHSryh2(w5Jr(<>%uO zX)G4!jWVXDXM^f+_Uh{QJ2FXUh~NJ_ zi~gQ)`d4PrKeqt?6Pf=bO8R4@{?nEESNKI1_CHTK{%aQf&!!~if0{*qdpPo6{NmsE z+W$af|L>-xf8!RFXjT8+mGn1X?0;I4{*D&@6Ib{*jqJZn75=HO{JpO7znYR<|2Sy> z*^~5lbN^pw<6m6;|N3nF6AAe16P4^go+Omz=Bkq7@+>)XNjouCEL1Wg8F^$;lq@>| z;RO{S!n{#(f;<7}fKBiAbkTG~qY`28y1{UaQotvh7qahNf+#4DXj+hkfMmx^z0XXUS$i-*#;_ zXJ^2LibwF<8@NaCysyi874%l!JS*2Lbw_ZA>v~&y?26&>QO5`?)!cHa>Ai5DKOy37 zx4sKyptsjR07gjqRXd&8h(xJgu5zS+mnyA~ZM0+Bwni%Vr_C-W@e(ccO2<0oj~UN> zs?b$icA0bz>y3w7#;qK-M17nz`3VaX`g^o{(uNRw6H>;)^rk52{n04>6gtJUx(N;w z3dZC*(L`eY1bYJ(`ieB+2`UqE!yy<$aJoPYOOZYp6Awn< z`B2RM6}k@$O%rfx+yQ#ZG`RFassV!p;pom(uDazg9pMyYss{CxM}rzW#a5InOy9Pa z>Zbu7Oz*aCX>ZQAsE?zhz)EvyPfMmsLIPqQ}x`I?i#gnFDjiRZ%-H9v<%`B0@m{rYN zt*nYTwdR!1+-JeO*^@RcdsK9h*mcE!X??o$!##NoK)!`v+2G|%44IDF8Sw`cXLMx1 z@%(T|-YU9yLXgggpQ`LyEaE zI(bUGq4w&)dU@ysv7^(diIi3dX}1Z#`<7Go?NYW2o((iO1h|St)%ek~M%Y)6{;Kl< zdsDzdSkoUT;Rn+pR+wKtU_$G8oJ#2Av2TrA1GkD6-=um$*n^hZ4lcvIxWCmbj9v&k zFyRrL{E%d}SKobkexi$Ae3h;dgCVBK$e>Ed3U(8tzT_ZuMoM=(FdR=B1B#6}L5>EP zDGzd=qF3=PopRHpA9yz7ZV;SyhrB<(D_Wo*>t~H0SZkK23Y+P9Cp9HmSzI(?9L4Ji zgV(Sx>xUzQ;OgO=G=|FU#snrl+V;!Y=llp`bz*mVIp%?&f2#p@d&3ZcK$mGYdL;-f zK7sT?&1^lTRK14AZzmk8R2jWEzX4KSV#YD55qcwXOt)>k?ICtu-T@$q1b!{B zf^SCMHh~RbkL3mX)FhXUOw?}@ACfNp9xMH_5d8?j^`ZRXC4BM;nReEpO_(c6zi7cR zv4x`^lu%Ug0+4^4vWsSY0R}{5W_=9iha0sA##ZF#N5u@@HUT)UhYz!NSc@ibszvGv zsLuifV_H^?PaYC(24E{+!{)7W_5#X_L?(;9!e&t9uzOWLP2P(h$HfE3hi>_fA$eo7 z*hHuNvtRf{k?a&d&&Mm-0(tO5@B$|olAS^W6$0}aUd-wdfB4g)tLs)_KmXZaf!eN( z9%>PyoIpD(YXssEJy+izh2)s(F%>*ikiQ||0<1ea8M_=KBxpFqMM><1Iuen?+FkCXv zr`SrnawFGcCtz0{g#*eFqL;n}=M`rR@3Pi;%M5ySmtIa)1FjxwX*Xgt?utk$CVh1o zgX@`7Wm}AG=*lZ4+SdM-* z`;dZ?;@ZNBiZZBrwsc6?fjw>W&D(}d)Y(>wu7~Iaft?MIJ;-*fn1Y-%Q9{V5n4(+6 zoHH?<;hyy1$!y?VN!nebsQR#M${xP$=NA3atq}O6Umpj)OuB74K9D>?vH_VX%++G) z5R$zwH%hdr)-qFw*u9;)Ck?!ZzClKTza1K{mA-TDb%zZ|v-tzJLko_AqKuj-rzU0y zTQ7t7M+~Z689pSOw-!xBZmiGeank=@H9>SG!mjaAws2nf#E)>T z-O*BB-duG09#S4X#mUkZLWyc^HWu&tX`q%gRI`{`PQGf=ZTrVd`Pn;? zVKaYfQDmqPPbEQnK|$V$nYz@#(uYOWmy5AejOv(xM)>UcF^4C*=W<9zzz4D`Kj1=k zbpxfBk|SNMO0npiE;a_NOysyD)kcX-OH-ppOSKJ)RDj$n7%=eLNtzf%K;>6_Bh2TI zRRTTRW^ii#;@oL)hMnyJy>TN2A{q9Fpi#kN&lync1Otq zenTmRe8&QW5=bd1AfJ!?wJbwrVdvyWtYrhnKkEm2VqH>$Q%N0Zv|CsS>h&phfo0hm zHG#HQ7#RW1E_60?(sMg1&H?VJ_ds*VB=gK#l*J@k1VFnrS`g^ zbJF2iT@!t_IZp(9%b_tTd^NNpX5a;KY5NPJbcw42u zcoj27G`7DN8^9xM-91x+=<3RRhTcz!rMs3!pkRm-f<%KnzlbB2$Q-2wr5m*T+GW< zG(2Nt+35!4gR%+ruZ?&n~=2a?y<&O?67pvJZj6%6-BYKDI>|q zCcF_f5nh3I0-q+J)3M}c9fl_SK~iCOFXP zNfB;4sM59J`}#c2FV7DxS)p%I|+gjc-G1@bF+6BwJLkJvR}eO3BsK zEZ-Wy97C=pt#~n@e^}X%HgsN0Je<=h9SAh)E5Qotwb5_%RlgGy~Tw8^($bbFflsw2rRNy6KW+mB2nn|bBLND7EoUUdO zNt@1DtzI^33iqh8Kp6v2Eva|Y)&sppxAS44bVa?LWJqd_kNmTm zw@hhX!*=L**qc5vIGvm&;w3uN;)J9KN~n1BK~L0q^E>FgMfnN~o$`mmxEmc{Y_(~* zU3QZLS)LY|v79KjzoxPp)34TZHte5SIMJyu8(h1yeJ{-vCQ>-NPp`#p{uFf2pq}zC zG_FIa&@lGXBHyR?^Q?U)X4r~W!_E0^p-~a@V+JcZ*}N$PXXl#9ZBq5}0AT^PhNUIxhoM~rU3exNJIjQj=zzG9oyAtV(V^~6YO+M?;WNnDPsQE1jn2jg z+DVMpbC$D20vHy|z)9t)KpgF<{DJU1PA|8Tz)QCSs!SmkrR#ENzi%rh8^Y+D2WFD( zNZ1DW$cGS`dgRUy`|J{F4$pk5QjxvFqQxbcy>U7D9svxE1cFrF3s%9q5p{4D`O>PQ z%Tz3dzAgjj`*o_*vC#z|+cBfMExJ{-o1E9Wv7=wd0XSEtu}8Bp*sRvD%2(cO?5pS{ zf$PoRRCR8LdN9g#;dDvnus1!vH2P!vwC6B@WV?p%$%s;QmE_WN2Prj>8P-_2Fk<$7 zuVU`uwiD@PG6Q~J$rmGHJu9>t!j@KWn3>f9&uy#2o(p;_@o#A|1MR@@pu^$iADk=u z#EO%j8VRq?B6js3_UOcg#i{7gEdkK6B?-1ShzLD=H(73ilXpag{U}4{<%$WR+m}|6 zOwBV74etSm-*TK%GC)3^hPbQU0hs(bUmdo$Gq&}O-o6if8ay20SIs92ho5VuN0lY! z$=z)F-mhCBoN9Ka1KbVzw(-BezcBK%EmZ=%fs!C=Cp*uA1Z%>t*V?;S~yr1uD!g`o*@xa7ic z;)#durNg~#40mLAc|Lhr3f8TX?y=CFJP4&k;*Z{vqBfEp1VU;(H6`!lxgdAvp@$J) znB7+mvzmn@h3pCw27r`4GrzeGKfBpD`kUP_f}>D}pW14s-!AX$&k#<%)X(JmT2q18>Y_i(SwvB-Y2DyO z`mL~Tap0}FZc@(u`RELVNvPzx)Tsv8E+|KIISvIVjDjtk?8tT4r>&V_yWe%3=`-PR zLnMX0RPp40wk3|ynk86T&mv=wkiobGJX4M}(d=@rI7n@GGA70DKtKeEsumJg9 zFC>7`bW3KTSB$Fcp<;`YcwNt!nd`$RImrl&Be&c{i!f%uB~kp~kniT-K7AK@yJhD= z=b>oxM!dSso$Qf#I^}d7>d0XxjJ7^(8sS}u^IplbD&tC*2YF8&qjVWM3!lsk|6!Xg zlUu}Fwv1Z@5>OzCoDCNIE0GGedY^lN?r3r*nqpB?$vOS-aAu+fY{Ba34*oXn&WO4A zBw?M`XC2WgHtMJR?*m)|s+YxRv{ZMZRPea(geNO^lsar|4?TO`fp319Q+p`C%wOV( z!9j}dRv{9R4LY|X!&Q((8Q8O06?#YWI1C7p;5NA&GJ7TFdbr+3;kr-$`%3s)q1&5Rbx9IdoV^& zang83P;~@%A8(HyVtdT$Yf%9y`(VKmPJ?5{Cs4E=54%v4KMSR|clgdD*{qh_Ir3u} zp7ii+P~$T1|!Nr26+~cO+Y*q%uGBR zb@4;L1I=70#C(UUYN{F=I4gQ2yXUgaCGrulJ>EZC_K5ZFcek@3xGpgi}=;OS0 zH=6#8zOC5meq(_fJeBR?uej>A9Lkk0wbu(ySCY^Q4!p1niY|cGzKxd~1ga;gIu(tH zK$lWINm8w$hJKQafb-|`F;%|_M;4{Ts`1cflsvt_1SCsN8 zg$&HtTv*TX8sJVmDR7f52Sp<|5E1(7Qg4I^;6Z33aN_j=ztz>+Q60u}u>=t|lRPh? zzI)b5W%vlw1RI3;sjhBVlvP6pfGot{U}b_V`gahLtB6<2hjJi*c7Rk6ss^i$F|)t{ zS6Jr#XnFfBQN)CkBv`kp=OeOC%8gQA-}fDs>(*$%f29;pEL>{V^sL^E`CN9+oMrNr ziIsIIE&DeZ%8X{(xQM8bu!yJ>pbIU2mv8uX>RM2vJMFdZ4h5n#QMNQPeOZ!*ndx37 z<3gs?Dj_9&T|%v%IUD(nU|n^!ba`UWrz>;r?NP9~Ue{NUmontV){Qzd{r_^c{g*e^ z|EtMJQbbKkUGYysQc<1l?=8RoyxIOEgJEU-6N~--EQ9&e>iEm$wUNH5sjaIj>j|` z=!Xca&O{#)w`m<;Le%dZZ+RH(`>)1FzNNtnT?xqlQb<&z9(rqI2% z9oRg+jc#~8V;4di{0J2Z9LK#`t76INwrUm7y}r6C^2Ci==J{hp^CsOIW|MmV?3)X$ zM&ulTsGjYjl7zufD-5L!|H}0;tB~-v38B0EA}1vdGHy$fwr(yhptt4E_#5HmU; z?B$95gmOT1^%8#lUaIL7d7Gq!$SXrF#-67$eV7WhE1YvL0v5YPB2uiUMKhZ2U*TOE&_2j?L)Dg1E)*ogO>GUCVe{KwpU z*h@O20WdmqC-~S)@4$j}R9RC#t{l`kjpP&Da9K;`6(?9CQSXuvT_r%QjR}O;z z-wPe0|K8{LFI&dH6*~U5b^2SQ@Sjo~e`1Ay-yAvp>1q6ZOZ1ON{=at$S^u9pg^X;h ze?9ZXYUWBxs?FWPV_nNYQKZIsi3>w@P0L^X9JR%uOo}ni}mj$m}^f5CDDMUBNqt$U9&#SMIOYec?sI z8s03i%SWFjKvTbKbUEiC-2 z%w&W*8hVG1df0Jgd2CgHsI5ji(tc{YyQ_zln#e{BCJdv$`ZSfH3B!6C?1X~}T0^{g zpBqDB`sxJAaIE@}8@;a)3u8q(`-IYPu6uC$I4IrLgoX+9{&?pED8o4h{%~@-kiQ<= zFu}g@sf;Z95PNp=m;4rw)kN*PeoK~jR>$ht1ujiw?$|Q?6ZYp~SRip~j1|VqW0^&2 z9gTanqIM5Xmz~o!ayxxL#&}wDu5mg#=5p>K;}ye45;vg^I8VyD=>`2`uce)guIUbED>cQWbg^E&5|r%@o2ixFbUbE?){J1C zXD2>@!mv&7=-du8rq6!B(xaO=Q4{3l@TH3D6zz#(CV9i2bg1%tS|vgw8junCwR$(*n^N(7l?`M>Rd1;n}(WD*Z&EZ|f3ZjrP1~iBPr1CHwQDo8(y98MauHt#J0m#$=Zb6J#b?>FS zBw#54PLnuLZ}ax@X@1~#957a+Enw7f3oNLtI$Uo5XvM+Vh=hEZ9&lqLh4re!;rj?X^Nq- zudgaNc{JQ}?$|R)(y#|Gy*vmyU9A?+l%Zs=NWi$L)M><4E*f!7nrA8Pag&mks!t^H z4f}2VJR-+|+ z%vYZSic~5+PM#tY9v&OU_>Ps+L41u`1%>K5RIo|Nre+f=(;@M*Uj}aED~yXL^p`kW zh7?lRh=g$L&_;3R&{m)!|6+PGs3wM#fAFfldB;;x2>2AR4?T7j7dN`Hn>rkw{T6znCK=tmTu;>tdJ?hQ-mjNri;T5>N$%pE_416=*0W0H8 z1Gf;r;Aqxrm3yFF`cxDf<1H?MKt~{BijnwDwer*r4tyr?q?oRAa99nhfxmivp2j$R zPcUw0dEE#hcS9`%t{|{x~7+u-AF5T(awr$(C*|D)=+qTV)jZQl0sAJn5 z+qU&(?|sg>z0bM#j_>|^-*?Wr=K8VL7&WTusk%J{D^E~unJy4we4?@=W{-PgMH}c& zxPVZX>4y@wXF9={l1g3(&2ed7hysE_%@oy6XpAobbdM6y9e8GYb})G->kn8{xv1(s zDeXFF55jq!+|yi5;#iY1t&N**5Sb11i+@Sv|CxaybDdBltFM&#jJ{yv4d43$e)T5x z()zh)Q2T}qJDfjVb}#Xi1Y|#svkCImp3TE3F2PIwO-m4i$SZtTJu1<5{T)AxAr>IX zn!dxTLA8)y77N98pTlv@{n-3Q8nHlTqo5`mTS(Ya*91eptujo*&xFW31SJ*q^~2Hj z)CNxKkIQn(n|>|=ipcC4-bS((W@;KdcBX0Z*zEn;DPiR7GTI_exoq$d-8 z2heQ^r9GBU4flT0)ws=1x(ym?CGku6a*^)(c@{gi6MXU(-yF>-`|~|~fBVFiA%PrW zo5X^T+hJ-CrM%bcvh^_Rf z2?pE`f_wf8Pop^}x*PQK+7|T!qSZ?Z3M7DEqL?gkaG{BJ`4*(oyora0%3T zvJ*xU=@JWLyDk?yqbEJGBa8_|k${tz9E+an1@4HE;R<7%9W80h`uR9yq!ts|q~{Wz zEe>pzX`Y%U*00E(DOkLTu@#Y{bFQ^^hBoZDWK`9eDy$LBAmN2{v5|o*zTC?u^?rT< zimm%5N7t@eF0jvjwhicXe=$;m=w9r_ex1On0V=5T5nCOl_j@!)lPuw7GSLjP19M^qp8m zHQnGB$I?3)rp4vuJ>oaS^ zRg>rP-%%P!x?T7fx_w0xd?mk>G8I-l6z26Qr~zoe_|RF>Kf8x40|%oPi4|0dnb)e! zCMEqK&6aaVf2g5feLBbv!()Krjy++Vu$1{Ol4>sNAMM$am(9QCcm>6$As=#1rca*Chz{y;Nfv9H&06PL8}<8K)shH?K01-Dg-r3 z7O*_KDL7{=5Wiw}WcS-!qUSSU^B=3Qh-yAc$pKCJq|mg*S7eUOwZK^Z?NPXrnU*{L6hMenc-_iVB1kX(IB--dke=8@1EdG zmHy>H=z69u5)AaX$EMc;&oT6tpz_(Ai}yiwES$p|M4_sVh+mIk07Z_gg)D!dGMoXn zUj;Z1-%TM=&ouRk{=yS9ZZb0Eh>W6* z6=t^oiZdVC9$|zw0xK1d(Npn6A`$RKmJvzRM%{^}lYvg9L`w{E(wxWIBIKi~Hya5k z<&b8eup)$C`ttbRD*0BYe}aCbR$V(ct(E>13umh)$8(>KYCkbAk3rVCxi{+dIWvSBniE9?0c$r{>n>T24SxS7)9Fv_|50YCl@n$7`c zE7ir?vEeUrsOv0FSa%k(E#e8#TXh0PtCmcusZ{WKR?)ufH$Y9>Y0LM%FK)~HVag-I zsEVf*V5GCMp>Z7Ac?3W2QR<64nld^|J@!vLeg!H-n=2rtXGvhz3-HNoj=^Qo zIU@24sVcI%ICdQ7L~o-0{9X44+9C9(%pX-<(8UMeVW}hvMaScjz8^_9}IIFVCRJ5Vzd4Db?b=t<~Vv$UI)hoT)Y)Jt&B(Md3$^N5DxG{=T3 zo+Bro*1eHTX-!?IIa;TNx5pLBGZjvSU|J0)eeS5Hj}4?zKre&s3nImDil2I@s)mwL zI9mG!3VTy-A0p5eaptrf9gcJ)-l?fwTOy;=hFiF)p_e3Yyp!*A)}B{bs?FsJa)ih4 z3PTlHY0ojRxv!SbhOQ=vnl>U$WtTYi}mqZm00f6kGCq3H{$SqA#LDE%Hsod(8{4NT{?$MQU~rKn^v*n)&F-ln(#xpKtIhjH$)qi6+To2dE zlSMI<+R^wJSx{*0)<%3YzkET)XrYgN=S31C){@{Xh>kOjkAoCA$cKVtzlm4fEsX=c zqTQ!e-|K1R)SI#ycK>ZJ=H7=KG!{$E)N zNi_*Yxj$G6B}S&dQ-};KA93hRzf*|Jzs+gS5|J()h(h`;j_OdoF9e|ckB zJ~sdI#tMD#6B2)Bc`E-SyYt^WL(M--uODQDiNha;)<60^UH<6y{7=^^2gARGQU7`H ze@?MvV&P!;jfV72@zho^!5Wyb(;t^qROSXV-`B6Aq%+ry$T0sLPoyujrqB!#7{IUs zW=s(Wly0z>zR9PZ)a=gyyeQy+h$J-M_cJcJ*sAR0$YtilaQV!$7@hwVv-2pH@0ET3 z+MT}R{?Xfaf|;m$E*K$P#PB`t8>w`5N6w~MFGezh|j#P!Q_){y*| z0OpM1221^cVgO3`XLhlNlEUE`@V@VGotEgFO60slh2{^>a zd!90oFyf74aRXXbqx|=Bt23{Y)&lRlXhlh(n(gSNl4`q=tv<9)xLRo zhUv*kt-whN2t7Pi7N(&_=4c%WBTOJ6_DUHdUb56Cx<$JMtBZmlU562Dfp%vuhy@|| z1d%gFr^$_y6eGk!*AP<}Y`W&dr6Qz$-$6F!d+b!*eM(+7Wv}26dSOnMT0AsyBKN+H zuZqpeWO+qb+`uzDqNl4B23f$dVMEMt4BVm5qastTO#2#YX6T!%hag@?9b^Bptdd#1 z$F^*UEIl{T!zU3^6qi>}M0O+<$LFs1YT=be5~WzSFIf7+AbZOY-%c>ivqmLED=|xGFnPm zTmVWU2-aX6Hc+9gQdVfKfDTX5QLu_?R8lTk)@yK?3^d)GWEAf>gxEK4yk;cP%9jX+TaL08LBaQ%Gp3O)Pvz+c;aNE@=%wX47YAE`@pjnf-bQ60b~5! zm&n^w9NVHL>6IsFY>Sv;fmT`DuaA@a(l{?n8N^G}l?pOASkD5f0j40u zAR$&L9LDgcZ!e3>eR`zX#z6Z!HBy1(eY+^LdR7>5x?30z(dkFhMOHAK8~Av`oqDT0 zPHm6H!VhYb{WB{_2-6ljI3a^A^h^bz&_TW^7<37zVGX9Eo?&4` z9JzuztEoE$L1f-_Z2l0lwOcW|9`dJXXKjMuy`xpD*@|FPZ=82MD!>gDu}CQgJG0R` z-XUD>3`3r{R?V!FdW?PXub5vx9PoD%uh^`dLSZ{M=i7eOzG7GFSB+^#B_5V7*^e+^ zDnQH1z5!2X>o1SGW4a$1_F?1rKx)}jaOo6&Nfl@=X)fzc2Tu1$jtfA6z?kSVDW&~! zBI5k@$iz0cS{)wlAlP6)CsaV4sB9G6sB7}fQgu6aB)KD{CH;XK;cFU}xuf4<< zf>!$>IiK9X3<nEWGgi`8 z7c2mE-_rW>0*25lB0v&jmEs=}NJ!?&xha!Mu)sF0&6EOFOa?gDlCcIzM)uUFX}Ea9 zs$mi+V)Xj0YNAgQue2-e8Lt~u)p z$e=lC+6_)?<4>h`>f?B3^i4F4=x?-*)dxBn$7m2GdTUtUh?ojnxC2&sa$E8_i_2(G z)bp+dDfjJ2NWqKRwJR#h^rflCPLo5I4e3c%khXR_w&@%=$&V#@KT`EY)CLZHgP~y; z6`mJV1cAHtn|t0v(*bx}Ft<{9&SX8q>k|l-oxl=;uZJ1yFG%b$h;vueibk+dn3JE_ zULG?pFAEv2zgrUl(-XVn-*76Ml%2PiW@E@n#(~;)+mMgx9Mu^Qy26p~cDXk#0k;yh z*2w{PV*otBVLrbd9$5>iAq8s&bFpUuwUv`W@<#jKQH;z{TIPNpc1FGJ*xIx^#xUD9 z5l0AY_G%1oip}sP&XbQ@`TVNyobc_E;KmM_cPPjqO5C>H7AdJA20OTq_@iO~*= zD?jGkle&%$>L9sGE10Dz7i7utPHFEx`8_VPN%i%uX z!6Apav9xQ$8LFWzUS;ce_7F@-Hxra2sofC+_UwS$GpU&Pi8*&T&tygh8!k5;am6BI zmE!DTqzAX;4W38m4ZH6&^n0Q0b9;wdm$c6W=>c*jq?L1y+O>Ue_aN?ZS8UF6PsnrF z%hR6x{i~-g*U%QH9)r7Tt?Z;Yy!V3p;c%bzTb?3f=KXhnt#s4Jq61TRWjR}(-l03UT6`&VSX;Wq*e+*CG>+-SKpjRv-3I?#{{;+egd}&w4Qfb??E_$QfgE3B{{;d9 zWQY0c1rCW@at%Io3OyUC4v6sRhY8@cCg46O(Tr1Nc232 zEn`7yaPhV+(G(l1Bj2%G77I5s?OfWTLsLtBWfm>vQ~(+PcZZ6-*g9fqtSo}nLSbr{ z$wXuBLRF+z`TJ;0gXxMBM$thZM{!1-ELO&-oWm_^GrwWEgDXDQ7GE&I8$4^DD%*cz zWS0M5nO$5)T1-v&4~+b8D*H!|0y`TU0UPJPDp!7k$RDSFD^mDl`(NW@4F6N#3M1pk zeSe3@tRE-;g2=2NApBnt+4vtx^Zy(N`wI|%^ey}~-evz=@VgVh!NlZGko+G*T^xTj zC;i^l^5?<-86GooGXD)8t6D0cn4z8KWgo&39>qD$MlbVHh0`Y>ZC8i4?^4*^7v*o$=VE0fB>nxId|}#49y=^@_9KzT54rTp*&?a}@igFi$Z$%JB003mXtkpsNFa=A z*x4u*5i_G#nXkK9{4e%<+5zgqG^D=}N5WA>5D;Y~AZ@pVw}=A>`N;Yab$i`}sAPl` zf>PXQl-x!y0YsDzl>#oMu4pE{O-XSf4aj-uJj`k3tX{>6=@as%4~La*PEN& zfg?qU)wV7OnD!!`nIUk1=M}~M{*D8Cb%hG*@eIAS%!IR#7w0|d3ctPA59o?%x*p2? ziYzWLpRAd(mb4MPWkXxCqOG>Ltj8UvC6BwA&MV047w6~D<|!F+{#u`)R|Wnd^6RcB zH6&(n@2lkyRR+P0Ta3+QzONDe+NV59UwSreOT%Q;hsw(e_FPIq(d)aWyq09EMuXsN zAgN6Bxle{AA~bMQ5W#O=R0G2V5{_NL&YUKoH`f4>1=;}jbz1achwvj0#c=^)`fxNo z0~S*qTAYxKHqt&)>z0%itlY2Ktqv8OuL^a66%>wIo>13Uj*bT0))edZx%n-!h56q=Mzt<1t)fL-Qwq&_UgwUcz)AFt~kz(yQbza{BcoK+Ww}DHx zmS^WqGM=Ita1XY=jG|x2YbnPUOn0j;XPYz#yu&an8#*L1fe=`~$N^94xFIS>_IJHZ z249&gzjDiKRCt^_*vf#g^WS|zp7MO95S(Ph%{K5+$jNvq^78)aPb&~h;^vX^P*u~j z+d)dGDcIJu>bNPiyQfOS_VO#UKrgcRDasD{bPzh8Xoqr#JPY4c`$_Zm7m4@J;p8Jg zeG3jvj{g_vvKI9^X$jTak7D<0%+eqS4)|!|G(mS!S{!+W_uyC8XY9dSVZqUNYhRFD zEE+o~&9{;=#8BmI;T$=|V}d&9Wh0RIQF> zo<93q$IEh1i!ZfT3yH-Qdx8fAwRrc8_6X|9PejKwJVR^_g^p4&~I|2ny^F>Gp!F%w{%d|vEoC5jM!b-)zRMG}*6oW{gKUWo71jkIw zP4_&PrM<}LsTHNvXZQxe<>3ibFoZ{3jnP|ra6_$#%Im#sp=Epd-@Fqj5-*LasQzfK zplat{_QZOV`{;#PL%Hj!M zvz^tcG0iI(8I5uLZeaws*W?*rVdDU~#SCiuL_j=cj3@OIe}g^n_}YkFX8WnD zON7;$=6hP;GVU!}W(8$CQBj;VxA%< z^~13K=0^y@!3uVX9Ned%+RpY_Tu{({D>OY^wXnI;OKQR1%HT>}+Gg7|ShL_I*n3dK z+u81yX|l65>3b3jM0(SZzNRz}_SHyoiplV_XA`t3YIY`GlK7}|li}+a<3^z9!dHIP zlrxt744JrRbUH5K989XTpS4QLDLTdhpL1OWQv7?elYp2i?$Ym1s0~M`GGo_6nH8w% z7u9fEwcw@@lo86KS5k5(g9H&Ot>*62q3?tUC*_v?pNgw?%|jke#(Wx0385tQ_{5A5 zdG9&zR=$~gW?kuo9X~*hbHm4Sh@8ew<%p9QPQk;hP`A502(8v`SFJ-2osTwF5XrdP z$BB|J%vi!TNo7um-#i$=$s<)m!Bz{djo`a}rQl!c!ZSAq1cI|*v7Zu>0ke&;A8GV% zV9Q{XW-?_x!OfTeioa&H_*|>&BplULvVsw9ObCe<_mK^$+S0)|=)y4eMYNxbpwkk) zxn`4e;1n^IN-G%vxAKBU;hYJrCO~~Vyp1z7wr4*pj-t$+U(O8>?v^wRLTSadLbb(} zYq)B21x24+5+||U(_RrJOy5TUS;{&=a*JvFA|JreD<2@F+6WGoOOp zoDd{V?gjs@9^jpLX{F9Uh`pnuGpX3`qU`5o5oack&&xH(wPV{CBUQLc-=$cTwEX?u z`*(Leo#|K?!gWMd%UWcg^D z`>)CQ50LVYqQw6-#O343|A`#@TS|+8ot=ZNo5`QmItC67wyuBn_57`X&d|Vt{*N*r zlYdqDd@z9@8D8{;&Q?|?PV`15=2qr5W`AD&$NI19M&=GiRwl-_AGiFu`qQ`g&$Y9a zlewLh`#-jSmhODK!1|9O9y13M11A%QzrK`}iKF9RtG^z%{^x@>CT0ds=C(F}Wy}1< zPX5v3^YP$cWA}gE?_gkTZe(CZ?`UpqZe`%`HxCuZzZXINdGLQmWK4`KoWBv4$!gFF zD*LGIsllt%N-T0V#>$I2=9-dKED@^P#8MZCq6uOws)EO8bl+NNzCnNPuCu+fzw4@_ zM}wvb2on=s?Nwig90N4`s+BTT(y~xCo8`haTUvPUj$Oc4KweId$!_($f7$PN=UwMq zcc+o<>sMfrjb!@n$TsFTKZ!!B9~lGnX3cnwgQv1^e`1QM*~f3y`OT$ zj)k*%asqYbB!v`;RI_F^p(oTB+*P+BX~~q`ZnA?(d`2SjZ5jwTgUSz;uL{T%Jm4gxDz`;=oU3d(qxlrDQS6 zc@$+7R#gdfo3AP&eS--g^xwDYI4?(of!8%2jfTEho@_SKD<$2S z-SQ%lqR(k4ro`KZeDYp2X?Fer6ie8m9Tp{zO{IQcgR8$C2pcrPE-hCGQupEKzjmXA zsi+D@s|CPs1`=7bs;D*ysz<`?^yI9LHy#PC1pU zU`0#Kl^q@)o}9#!T(O=WGf5q2l+^V z@y)M`QfEcot$V=|Ish7u`5=^N0p9G?|A|_|6FPw64dDgvj&VB|vNR7T0AQ%%Hx0gP zg|Q^@sk|W}KA4DBrLj?1%MHa#4}=#`^88Tt`HgpLy<@sVs_W(_CuLp7_$$^_qmA$D zY|j=%O&HJZ1sU|$p1iNPi@y?g*MXwCK0VpPzfIa1S9A=;o!HzRiiG+ZVM-7!Y3eRDVCQLZpiT6zxHzBi zx7nM=xLW%xQfE=m2eaZ!8W(W%!5jTpJqwV2sm?T ztVd*IkIe~{zED(rKN>maE8~M4<@de5uvO|l&x3fUf>eCO1cEBS`qB8a@kGLXa6xk& zM8=V$e%TBbkXiWf{6%qCwS#;ocy()aNr5+SyfAfx(%gV$e(_|X0L-3tcE0r(vyCEZ z<}!)<*2gYGP`hSs?pFwnPX&ssFSxv(N&OLx0(CX)afYP(g9Jm#%G?&HJuH!yjyigX zjUR8!1#%7}q={w_{|evu-RCzr?28AU${o zN`SpTcv|i{K1}*5SB+B>Fz2@0y4~E2S6L#Qc<@sB{?#u3$=F9or+a_bbvS>DUM{#`VpAYK9#|TT?h5lm z=Wm(VN`7Ifm^dRXT#|^!mMt4exDg8lmcYJNZ?jQS^%-h50$g8E)%@Cp2ykO>;*q8G zTAREo?9ZKnj4niea^}j^FpDSQi!tXE*cF$gj8(lB|NOS|k<^#M5LBG^;0eHD0Gu^? zTze7bMC$c~yD{p9DP0(|=tb;#Tk`9KnfQs`J0HxEo7d+?;-BR|x45maYH}6A8*ism zpw{#+a-dH1TH3K?gey;3Bt1rt8#3s_s^`8YjI) zFI-&XvD=ox-wH$1KpYU;#WxecqETvZqXc&h12|sDxftn+U49<)i#l`V7X=l0MuF#A z4VH@*B67WQ7gUMWwK4UkfbA`0Gzkj~mvd0wN5Fd$ykri_M^a>IO(y=pO%Xp+tJl;$B!2JN* zdFVF;ES7Xh^EMbnGRU&`4U!c35cG6Y>wzdTb<3DURC-d$kR6kjsAEVyA{C+S!!jp2 zU2#CL^ixdzi!f9Zs%9aDqjAi|VzWqP6pSp9j>@nWXdp5; zdfi%F;thWB&~zoMIt|HcC_hFX4Ob{`rQ!ERKAZ~=VO>c>lYxzCIKrG2iirO842tmB z?DZT5!li^b0mXMPw^k0{{{)*KAn{)VAOEWoEhMI*EFt>=HdR!92b;8iWCnhCA#3JrVmH_|DZ+x@ewX&U(Ai200gW|A3IqCx8HZ>j*cJj z@wbzIk#H=(lW>Cc!hbsH|7f3-`InboiC&prg@p8912O{E3eLT>XWO|DmS;TiN74lA`|q`Xrrgj6dFFWb0t^A;|wRAn+l| zn>g719pV1*v-lh5_}lN~{}3d_$@)9OjaG+LN1k8GK}d^g4JgQ6!>sXKJ<`38fJUWK z1DdCT7;X=xC4`hF5OC;@3F0+?jU+mYXj0J*_<^3 zAq^f(i#^H+^heel1O_|IIHuf%J5LGh*J{XE1Zdz_;QRr3t?RXiX6Y0!hBe07FBnbp z+*Us;T%G)EOI=y;f}&jE*Rc<)kj7)gwOW*&x@UfQ%Pws)(qd$)PkK*C3dh2NpZ zxWcaQQ^OEL7_Nee+b`(ytTd5zyY7=)62G!lAwP+OHi?3_)Wlxe#lXfuU!ny+(d4ii z44s7QO0Vm&iPw3ZC81s$Q}4@5t6!{jA4ZybF(eY0A|i)n_mcN&8nV@ftBe1Xmmy+9 zmg{9Qgkwy%BjzL8LsEutHRRnCd3|kfI?eMqUns?RDu+R+l{?U_-Km z05_!QBJW4&fv_}0WlX`4iy{_7XbtN?_z~o%55e7?s2?8R`>8uJH{8FM!VrP+!-Kql zEDS+@V)e3s*4^p2uhWQS>l|;}6T8d*{q9@4Df1NBVsWIlj2a|JfOo*a>=RHTSUp@8 z^TmiDG}tbjF4seMoQ80cunlZB_stH&yuo4*ZpfDyb^fieQjGIX zfR6EL%Mg>i5aEXA0UnrzZ)470&-V+8l3@bg1yioi4)%6V}83`OFsiID(=B)Xa>4QgE0L{93jOcqJ6Ylvc_ula8 zrqh-a-1W;6^eiXxOZDrH+I(#GS65wI$9w%J<}-Khs6IO9ZJU{J@Innlzo$W%xeS~1 zNuH-U7zIM~YO>9cv#5MOB~@BGz@#y*Y4Zw};Coa@2BP*1ZZ%Vpc!K{3`$Y`a=2(T7 zT<>a$w4iJ$2Q4RSv|=%^NdSXHhgK`D8EP~yJ6LmWEa6*l0l{tQ@u5FO97!ygffUc73i+?w5H#;m`P#%EWo&`hO#99e&?AuOPL^KzI=Yt2AJNv zSHYI9^fkH+M%g1Rs6g`L41kDR#&hH=G6BK0Nc>z}9Jce44xb}RI7s7D-C62L@Ar)( zo7WC0%sO=N?dtA$u(iJBZ(C?zwcu9GbwC0BI|KZAih#+eG5`kFlLb^^p7-$#ma;>b zGLuM|)#x4VmEAX}N-k=5k+PcUKG$mP6Oetu&~N%kbNH1NVr~$?qp{qA=k!3$`uxre zU||?HX)scfkcMBNMF{qChk!>x)hPtvTlhgZlE#ACT=XDURu0=cm;i``GtF{w1!Q~kzz)zB&02k8PTI_Y)U~Gcm z>KyBy5&eS23g5*$Nke}*dD87-1yZeVJ)J^wKe*uDY1JuAOv8Rv;}CatZ2+mYNE=D` zEIt_;RPeR)(vDF38-koT91W6F<8;=EI8eR0I9!+zFq;wn6|JFHBe;s{!vWrkTg&_p zH$AfawUS@31w$SV@r%;l~LatPBl+^ID7PKRDkl`{{w zZOYWy&m)vL{*V@dmwiTjqkX%$a19g-!dgSm>;k}vy5DLvv{cJGT3{qns#e}2jRS#9G2Ad}q+|l5^N{8dk(69>_Eqrx+Tl^d*{pXy#w)?(K=nkAWfZ8%F~+dwFpddE8O$o0*t7bT&!+q`M~$-Ebw_I@gMh-c=q3IFndK@fGE}!#$@=#r6 zqpQ2T&hkPE4|`u;yhaokA9H(c z4GI=$F%@<)owO>GE*CvPH)bu3NBH}*?bg>J_11lBNe?PwH#dx zNDAef#lz2#2y7@xya#U_kS5@QVHiIx@UaP*6*MYFPSN-~ezW)~%g5b(IFp@|x&O^l;s8;` zcS_0N(?F>>KA#9MX8m|V3Pba4>;?g}OQ2xk>f*C8DBzRqW^ye7m>V!Uf+nIiT6roC z$ZW$AJsi7%kW>s<9u=GDAv!*soL&>y)vl_Hp?AHU+5$P04VKT;l}zr}&}$6<6;>o| zME_Q(v2QW0hJH1&yaE{KcJX_5E4-XjGO&(792HxRVsp#pX=(C?7wj`z0p8F0;CA}# zZllxrAsYTTz^5WN|Jv97PR$#Y{XrWMn))nZ#;pDrcE(A3N_} z_P^aYu20BDHg<7o);y5G(d7O(U99KvGILqg?Qf-fZ7SDREvAA$1BR(P7nCuvu~!Ci zWSJ#sjzJV&^_g~FeRnfNJa%LuvK8etxwkNHZtQQwtmn$ITfh8142PNvg@Tv+bm)e_SC0CtTHKyAs3*6xcySBkJ1v(O60H!k^+u-d zKvNq0YG^Guo$6Fokl=bt|cMk!^$)8>lvGH67fFLj6nk4Bh$5vUF439tx zg8uwq6!A@y3IWh%u`_{vD-~l8>+Zu|qb*C!U?2j_`OU(#sM)qK$=Dv*Q8u^n;;P2S zD6ssu0q0!*wuqLxg71am{ymCustPux?)h}%<%P4x<3G??2)IR&_25`?L5-75zbL)q z^k93F8svzM;RXkaYenH}HufjLwkFAuMV;Vj^Sq3|}b1>VWd zN=)wVM(C=dUC)>FBnzC&8Y7T3pwNIx&c~6a4c^@W2`J^MGdnia_%d4$t&mDX84|8NEYOAUq4GhJ-MZ+;>ik zOU+z(>EzrP=d7zu>dd9~g4>nwlaCPu5}dH&yiGvfnC`VI$AJ& zg^gt=ZGNQRGIdWq%3r;n(`(N3o}4l>E9NckDiEHITa>LgkZ$d!yjdV%-}E1q8fUoM zBaBacG*F)}i&c$^`M<)83oM(->#jI9Zvlx->id?YZF~b#p*-O-lL7q_n*hb zHkMolku?$-`GavqpCL(U!|5wtE|~$ZqJYF2Ut>3YHw^);j^eFs)^1|;lAv^tljy`v zfN^kT$=%PBg$FEGWwpsCQXF9WH}DNU&tPc2ZWfNdhwD}D?DM=bKn65cQ}<46ADG*( z8+Wb!5dh45fyU!*=vQMlV5%>fTG7xgZn#^dqp@bHP~U)qlpR4k@g-hK-==Y?DoIf7 zBT6SN=6rsrfx2U+Yf;pzd=m2CfBd#Rg|0mD32H{ss7-;^Y^sUI4uVC=F7MNAu})1CPsGR=eW_DVD_NLSYM8u$ zV&JYR*;l*LL!N$C?x3t)YGpRZG76_9iPrUj(X zFIEYwiBZccxbH}N_Wg@(C#r%dreTM6t5M|sEU)`n3#^RP@Lp^(m0}Xfr;}nnE1m1z zM~y86@Y7VFThZ4sW^f{ZCMC@D{$kH?B$7_}hny5kr?I7(XG@BDAi0vFeN}XZ zPxSM@)bK{N>EV%|&+W{%;#jlE%OI#?s#j8p=_9X}kG_%tCz)L}v@i!}{JQ21w$|G3 zky|qu51eT3POabjG>*bgLp?Q7I0|nz43tHA0h=L--nhXnSd8#$p#+t9JxP3ENl!hf zFaku1NGcY2F{grtCpr${o2cYDiKP>-t{GQEN8uyw`o%uez_ac+kq*OP#WJ5e1|EOr zH1sC5(`)ZtnPi8vO=_srGs3pp|2R)%!1oUp`T68?LAB~m`Lxpu6UuMj%ivQ zC;<$U`$$Pwrjntr6IJU!r@VWz+Nhzmh!?Z6ero3ym5XW`I7P9ztwoNdiHg0yE?#T3 zR&%hYjei%p*Fup)5?3Dh9t=iWwrB{8^>%7=@F|zo%^jo8Zq&6X*nKfKXjpzmEF_P2 z5Dzt1uw6uoFMryJj55!qOPgAv+ zwyXwZ*V`kT!4CK${`|)^d=HFf&Zi`T7$_8|G7C!gJljD-=SiwChLbG;7%oozvw7!Ym8)8VIFM9nm~=c^@t*NF zJI1Z#+~B67uScQkEG{uDoTRW4Wl1Yd86HjG-(3XtX0c)I2yKE|e_-X6i*K>m!8v`p zkh%?Llnhn5ezsAO1S=X2)KDQr2s|jhI>~x{l+l@~bxepSTb2$lIMeX=Xf{d zfV1<#gR}E?GOuJ&X}7&>JkDjsxx7{$^li8zXPKQZ+<9+~drO zJ$r64dBK|gfvyGGgn>8^P(T893@r9XCBP8pjX|a2pm4U3dX*IO@*x5<8X*xgk(7~9 z2*wFF+x22Dv|luyRf$UQdW1`pH;yNI=0+L&JbK z5m;-3rfJDeR4XSPGJ;b1TP=V9wa8Gp0|~-Vq|#L{QYWub0gh+ED;qxZJD}{CPSViB zA%p{#c#Q-;L)RCq3g$V+?KBz=_O1#YZ*WZ6Y>!bm5sFhiYz10^WE>d-3=5nQxT3siF~aYpgCLJia3 z^^(8JGi*O(jz2<;-2X0Bu>8AJ@yEgc-HgM)$nl#Qr$NnK0mT*V%v8Sxl_Y!#O5+rB z8!_6yG*c&lQ6U1I3QUlN$^o{3U;w7ak9lf04wB&-LymJ`69Yq3U{|4w1SbMO)yq^N zT9>SvSZQxmeOv0Fmx$kL6Iy1 zv&ml1FeaHs2ZY4g;_CDrx=FHg?D3%9-jyu>iBtO$#)Ds465z4BR0TxUpo`qWEJytn zn?b&Pa(-UCokg-N{EGfG9s%lNEorO^mZ!1h9Ig~;ayM|)KoZ&jgOX*i?Tm5|7R(BY1M4ztOHe= zreIZ}sd7cBf(0U~M6jY|sjLb@LzJdGsKVJS3L;jYf>vpT6Own&(E1SFWzvh_Rgq)v zH6nWO$o@3vH19NZ6uD!R>NoTS?0fT?^GAT6{4Eh9RZM86!fY7Vj?5A(3-@>}45Df{VVi1>=(9q|PJ#vT4a}RS!qh6= zK904`#-H}x^`mciE~&>shN|06^R~d@ze39)kcg$>_D#{EM&NaJ#-7F0+aE2xdAwXYL3_IU)DI^Sw7#WLP5+FA*+F93W=L<_ zLQA^+U0W&5@p*lbjF>ixiQcse;>Xkqtx$vTgzz`Z>j*BBruavXi*5dxBv6}e$#LH& zrAjSC$zAo@&FQdY8|?mr5u~4iuAJFWTPi=Y%BT0b3sTjx?=oxcayV=!Y|&P_%(UXl zD40;OV91oka%fEK2hS+<=by5BnjfwP(;c&gTcTlOM)5WKM!PG}o;DcR;H85;t@Ldg zpKB3?*5Cg>%HAMREL=>1XfmLZKnNM8DK5&Pwj-twnx&|4` z7J)g`C3qOW+)DjOo6@F|Mv;?*Zti`mA^h)RzS0fA?pczaOgW#+Uvg-Y+IHjD`nun# z1uy6Dd|K7}wa`=Gto{l;C`m%ga>}+B(bdRroD?a8WLDAHvWB+3_+1C5ZsMu(9$8Pw z74f%{$Ak=KH6`}_fK0D(HFaoeAAm{Rs57?l{jjiZsDty2>Z%aOpef8-sFd4_caP(B zud2>_ELwEQ!EsJ*tA)Ym2{;hB$kWSyZdS0XS`a2pvdLEk3lz=Y3Kozdb*g+e*b-L3!x8eAr0=uJOGg+I?$`z$ zflQYZTGLNYTj@Q9q|@;CBd^go?Yy`SruJWD&^d_6%h6u9vjq$QQ>OQ1`6J%9RzEdx z==CF$X~W+xpCe_&C8S;oIP=luOpsY|c;{fB#M% z$+Ybd=#atKQu)gZ0q|aH|YH=>nx=O=1rK~o4n>lq%Edp)S!PP(U75kg4_A&NwjE>OJLUb zS9=qx+Bm;-?adRdf=1udBZY$bUE3$YOka(jdtO=^QJ64x4N|W~U1kE=UU2-neD+Lm zS9~V7^aw`f5m~w8ow!FF+j5!3aI|cRylzm^_MQ@W`3hhFS{bq*sYe8cvTf>k6xCRK z>#Cdl8yMu0)snKLHWABwR;fKB*@FIH?39PoiVMQ>Dn%4WEkDG~P1C?wA~qOtqk9on(T5mgKA84wfenprdTYYQd|w+n?0cD_@ne3c)GtT*vm?MH zOno9Zjc77HtDO(CKC+Y8yu@YesNYHH`H_~(CeBzJx-qC1K18icrRUS!b6`F+R-B?<7zjB!{<%J5STvkW9z=qickuYNaAYtg4 zzEPO(oijRG+HWtz|8+;QeOnX$1HGpE%b=iVEBbGBVSjz-?<~xJT{@%sYu8M{(a1^} zm+r4_&3_)InEqytWc(Mzi!hDI-*^|ff8$-0{=vKG)BJag4od z{QDBy_u=e6k&=HN(*9cJ`v*_*x9z_F4Nt=SkJ3&xa90JRR&G2-OyE*~K>SHIU*G0z z%F$eVuyS8tRp)%mjV4F2IV!7EVzb9PBD2T*lO#u*)@gYNsyBT#L69L35JX4BffSDL zsJkKu_u#jpiZB?L%E|HgqN%yUqxAQ$@AKJP#}y6?ojq{Rj;*jz`C7aC=K6G1oxcKW zPp~ks&j3;#oe#xjG)zE^CMpO0H5?Uo^yGT8~AR?8T}qYKN>LKxQCk?pYVEX@A$I{z94ukjWJOr3W=3!N=7?5 ziIKwPA?Hrgdp)X{`*|~)3^#|x&Yl4Z7ayEM$cZ50FR^w}PO)>`MFfhFq7bxxP<!hz zl>eN68th~yz18Su@F359k&c`9LywhoFB<;)W5~G>Fz_oI;N;6TnzgO|HAZLN#%Vif ze!R+#KY7y+$2ON!JrpcRxby4cW!K z*9GR2DW7(l^=N@Nc|KSb63nEig>b0yHk5V?Oer~;5WL}?Xrkd@rJCfjXl9XgBj205 zs_Ri}yxJLDQ;U85MHuJ-c+N zqvB*9**j(2xLVX+%8l}b;YD@IhE%~;`*U7sBpyHw*sy_RY(BqouECC;*?Pc>D5mV> z@WQq=-j%ZkuGl~a!wPFxGoNUH+@|X}Y&Ni+BPgS&rDu|Pv0IAhPOg)!j^)A?Ku+~( z5l=`HMemP6aqm!V)H3QewIx)tmZOFFab9MOedgn^V&nA1y$B1~(Bp7e79mx0wK!b9hO{&|4$;#uVP(8KH!pr(Ae`I`Qy91C;8U)`3h#wdwa~TC|YgWLP zaS?R8H%X2rYU3NN<%#ri)fK=jvVR5oEsO7l9b6MYrohIxv{UwM zbaEwcBa2wbs^;QqLA=D;@D-tW7OTw~%!K?|K;`5aLyCe?PiU@;ZLqdndCknL#BJ!W zPr_8yGt6E0nkee!P68tITR!tKmwb=?f|qO%BvZ7s4V9(k5RBfIk7-(I0V$WR321dW z->-#N2^pWI1GK8DH`I;bXH$0f-})7nOg!9l*ON2u{Z?#ZF6kWW?XbfHksga8N0V^E zvuJ5L8(1JN+^9s(4sdp{7@R9S(Kd`}=eqb<=yl>%hNxUj8bl_DP@=#PBneo|C5i4{ zbhPuJjm@OE<{>>+N3~DYGSzj+kCm=ISA1Ks)T>Afu}d&m+hup4d;%%Z?ThN}UQ{}XkE0H0e(NRdbDoU8!Ya;lPDk$1mtY+8w23LwB@94> z*(Q#l&@UNKFyi(;zH3Hku&pEn zJKavKFgv}GtM3?0%*m^CaUC(U`xP-^IDo!h}}KSc1=&E)w+a>An_WhBJ$ zjlDUy4e$tTGU6(k%$nFgKQ`W}NCU3yt&>LlsxhEnoVB;R4^#15&u-Ua3Dy;b1q6V4$Ej?c{bA!9c`miy+&M-D&%eRb6O31)t5O3@%=nF>w{seXocl6w@`(c zebs7Exg5XULdCf*e$k{l77`~XkGT`|4l{<~vL3SM7-yAh_%d#rVXIEVXqF-_j-eo- zovh3YEXKv&)knljfFPr5{^!dSMK0QgdJyFxBzR9(!3I=?BZV^<70+PsgkevX8E-*X z#Ax~ppe6ddl1e}vKk)tXHIR{(NF9PjA2iIN7gTxM#%KJ{ME8C3aaGV1M z*ZdQ8HRk>Jmc-H9*4EQUpcczw#AkrnO=~?Y>yxK!7Z*sfZ8pD{3Q0f(v?)U7MEB#ycGaAS6 zI9vk)dou|*luJ16y5LvtmFk_ASLna*6EL)sYI zo!-43Z>o!{@3|rfkJBSJYecclEPDcvrJX6V&(j4I+LD_hc7d9vJeS6>yFNdVO@EuK z)$-E-Q>mTD%&UNoK4R(YiEE|QbE$}vN%yN1e1;F^tt=X{1V(^e#=Qk-eCjrDZR7at zwjS^1gZuEY$Xwqck2G1_(klSlaqMOP48wyKtTb<114GI$$|osOL##Vp$tO0u4uzrPB9Ne0y zlmpjMYefUrfJ)X1PMa~^!fU;jvURF!zfNiT4O-K1F7u@ zN+ZPB+3s3Sz5CS;gmOB)ca zx0+utLy5wpL9M9EkRuvg*+CTyllfaqjXZ`S;Ed%lu-lo`*M03;Qlncac{=z#+RcVF zOfZn0%sPy3;+R)PpE6OOtEyJEDCQ?DETk$h^*Lb}nwMV^xvL!cAb}p))erkM4FPgI z-aI#-hoXw{MTvVhMgnL^uIzDDb+cUaX$Xf7&}f04ZW!$xc!)47*$uE#c+mM){8TU7 z><1Fu%Ts0nlnuz>doEt~^*?V!xCM#rDBYLfJI@};2R;|%?zg*gB5l*rxi-a7QxE5Y0Rm_g0ds_1{8{?6DB0jGu&uGq% z-9-_8WPky@JEWzzZseNArSoh4#H;AR@_q(YGm0zn8}{1|Oq#cLp`g z1_~f&aOvcW%|yfMQp*I`N08VQ^zT%!-$vj4Wbm+sISFbH+0Jr{HR((VD)hOrwh{TZ z7d(=*bb}I?tsu?bB!kr@;QHd1)-bS+31L%ivjoB0|0JHeVV^#qr62bk!@V0A<>^<*WPhqxF)CZztDGNCmRM{)7XppI$5@5I_O3$F80T=8)N=K=HF_Mk+6n-lJi10 z@7U9DbL=-4EIr`KXvPnxB?5bQWhxMKY_|@YIKC`kMa^+JbKZzqk>=$qa#4}NSYYlR@9xWh5Nz&^)ai#BRXr;%3(pK` zO+H(FA)<{QvkPR6S@l3l>U6HW>{?Ch8ezyB#dXWP-gE=CWa$u(Y2H^rJ(Wc zWc_9y6@Y+P4R2gZY$w>T4@+&9sfBfA0eKVVwBkp(A{}s4%uWz*%*A$g$S2+Ub}a7h zTIvOH+l;ljF9dE^No+;mph`<7GikT)kiMNM6R|@3U(DU7(f0GQbRVD^faBe)d)6IL%y5Unj?( z$JW)D9C|?p_jar$Jj})U3)Do2GeVqX`6>4?W{XMuXfO!N=rI8>uGO0kQ;8YEc|3cd zi9I#%&ThB_BI)r8mI@jRidIQ#At0m!%+n4bmEaaKJf6 zT2SaGvyVJxzUE=Gm^9lD(PrlaP$t)(o9^pZ7LRG=hNd%NE&@51aRHc)@9EDc7F@{% zG2vY>@0*lcNPSFJ*dhvWUe(`A4UvRnhGiAB3@jMcO~YX$w*A5>(6ycC6J9)_{q{^A zAFu<-tH~lHBh=HNO8F4>ZOYHfX-wBTnAnM_16P~X$)%_u2E9i5GS@-b9;;N{0e zmsw{sEEhP*aN7>l?(aEB1BWJX0~f#+t=6tJy>ch4{I;1p<*qtN24X^BH1l7`{?}{$ zRtTFSJY8uHXXiBiQy)OAM5cE+qSa0|yE~JoLF}+fGuqC2sMzYzrVk0~n$7(1@!N;43{It^I;IRhy^= zyG?5#wu!W$(DH6!Jdvp<-U}pKjEb06j1+P9HOy znv$?=Se<@`%VH&r@fZ2uy#`6Fe!&J8h;;8iGGTE|yMPaCyEcgCPtqoA@1MN{1C3MP z-%oS-`!t{EJjpMyC|+sScui$GiaVKUELey9E}l=&`pTJGyn5d+UWz%c&;i|E0bS`|Dq22o;G!pH$&tq zC}*8xT|FcRtFzTN`Spvek2u@H^XY6ey5o%^8jI#^(5@5glu+$-S2G4`@Z}UDdX68> zZRR{&Zzo`@FMD_{T_z?mTw5Uaot!gy!a9E8bcos9ma}f{+LhV2ayffOoF8o&CUHK7 zekBW&c9e?^RESy$y1Uq7>X1gT`63x~i2DQj~ zb_OD;KS>IiTg%9%;Ie%rBHsBQaFI7zZO#Z5lh0$Mr7xBM71leCG~mQHJ}zXw!rR|X ze~#jJwGeYe)`3|$aar0eCL->NuM%|CKfWK;n~F*b8VaT~qZA29#bg~=<<0b7SWG(Y ziyZ?kVEV&H%(d`~Z|~#IjF*mkCQNT+b=)y_KZp0gJlr{iKJ)Jl-K@#2K3}2K--#Ie zdVcId?h7yX{H{MJe$hN=>5r!$HJObQ>1v2@^D+2NZu3U*3FQ-r%qXHopT{9yX;=G zT?>>=rU+h650)A|`oA`g4o>EUO2tYXTBW1$(HIUL);7jXWB`N8ekV0PVnI#xGS!(`s; z26pj|LS~27!Tg+%Zfc-0GtAMErcU*w z&#@Iu>aX#G+#&z8XLPpCWV}S@>Xry$4YCLB)R(|-udxQQlaR5Sa7*KlQ9zYp=5Vhh zy8Oz_vC3FU5Uj(RT>Y%Pf(dDm>1Aui9M}yP%*gYOdDre8z&TkqMs=)XCq+5$-K73z zRk(fs%`%%gr_)q}8op3@t7T0GL>&u40kAEIpxBaNv@z?O9HW5#LwmV|WSx7RwWZ65 z_%Q)0f(PS<3iyri!5q4ZT6ev4RyG0EynChOHgPIN*UGN1CGWi;gyEY)`PXEL6pb{E%>Us^ z{*R#A$kI{oyFAOt*1^ou#+t_XKg2OKW;EtB7Bv64m}~nV?rK{z8oPgAv9;85F#Tpw z98HbBZ}C44JDC6Lu;Z@_|7#+Jk%58!Z$wIgiie|uQzJK?EDV%P25ZGGn-Fk#o_0!$ zif|8K#b}+ZdiO1JkN7&;U9v1`3OdT9bI4 zWI}8`LS#uX&QZ#u@~y!^r8!+>1Y@ZS?d!XFFxf57{HYr9`OTxFu;dMb#aCpaE+4iw=nmNoY&_sLI}Qd44| z0tuOY$Tel=%S0|$Q|0Awis36tCu)gA=j5Hf8oiqo(%5*^`jCR_9f_V$eaPZ318TLA zKOTwo%o0V%D-IVDmXO8BAKR~YN4F|P)9$(rP#b~Pes_d%1zhxhf~-Y!L~(^5=$6%J zT0%A=X#`vKEgIP9VpMaUBUy4Z{u=8))W541p@UDGpr!NGGghr0I&RgSt6_)`9z;=? zq*GGJN_#CbYb(r<9FfQ=Ji9@2{fJm$keg$3Z00wy*qyl2@J=!)m`VIUm0CU^BmiM)A@ z_z7V6kfm88LWT=y+Z%ujNKU&r*}UlRZs08Da^Tk~|3C|nS5GB%?hW)y)UFjjby^ne z_b{EDV;ai)=QF2uG>-ZWwx1N);ljnTck_KkRo5q`)EWMvCkv4cNMSR^W0QdtN(#$l z4b+1=-Lb71@4~)?U10j}m0gb05cW2$jY(^fylV;g|Yl6#?o8yAjrxrg2As%G%QNaB=nOwMuHd>Az493Qj=CN0{1uL>CCt3ozVW3%O`wf zpCBy~28fi`h8}f)J^&#~(Ws{U9R#S-f>BHfCZMco3*v2S>(pBbMaLTkjFmzT1ZR12Sn? z${ri-3xSNStgI}|&Tk<&fCA!J9vPb46`#m6k9b*;R3TnBMB=bf>ejpxa}-dq+4<9| z|1!pOLjifBn;}*l3hQwi3vHy>cyxg4hOL9`l%^}u;TAKZ=5D$j%lcD~qJ;DGI?g3^ zBqs9534rUtNdatIgyUshXuhi#`?Zp6E3>bZW6%e|D7`tOdc!bE0^UOPcncXnLS6i# z09cV~fw#lAkeV-=00ursJfE0YJJU5W@n+R0d%h2_e&35R!1IVkC-WWPn#VIqf@%U$ zcFI`RVI%`1Yf2kN40m(?6ALatB_*_7ka*14))aAIz!Oc*t2Dm_)7G4rDS0#-nT9Lb z>0;XN(KTJs&UKyuG_h5_Vv1=eQoNYSZ}zKiMTvhy8lApqK+i57F%-F)`D65TR`U67@EObqQe1 z^PgTsO~5||e1CZBpBL8_cQf-(;3o5Ugch9>KoV7tQr)mK?In=U5*)5FzT&22cXhvB z|3uDx;s>5!=7`739O*{1I)0bpslfbldlPZ$(?1h{w4|!^qEf-kVD?#>d8{5oJ7X0T zSteu=2&_~G=5L>*$l;)czoNOC)KIC;ljkoE9&>m3z)^qpz-wmaZ#o!;PO!Tm#gbYj zRc9ZSQp&iXEWji^X2ZIU%}J~2YKxSp-rBr9aP~k)Whd_@52#_A}$qHm-F3TaRqWU73%L;uUei zJs^0u!s5hC*~xy0Tdi4Mr&D=i4~hZWM)ahPf;+iZ_|)4>;$NN4;d986#2H1c6zj)1 zX%VnPFp+dK-Or55#W`w}a6dufe9O6Ju}+AUrN;GN^c^4AweUQA8v(^EZW!N+%7r2e zg{ue`dN&ZYRf-XrZ?i$S7KYe_$6oZGw)Qn!{hH{mGSfMJ#NLhgX_0ka5fVegy}m+? zwH4{O{(QxQH@k!${5b0Im&w$dWkC%I(i}O>Z4(Rh64Cm|=eP`Q+gl`_^45sw+Fg-J zrZ`D=iqB>AbwEdnM^Z{BZnk3GCjOD726D zuc{!CU@))M1^b-mP%5Gx2=414w4F$fk3W22)XShW+@tF6>XxL;r&w8qLky+pM{4kE z6*VytA>ljta$=LHH8+u_HqF7#zqBTiQpamon;|dWT}nWLRmN$v_t=k?+f~+wfOZBb zH_H?HSVK8lha9tFZfXrVejzxL0m3$&LD@LaWe-d1dy*JxK1LJhYVMP?g=alWs8&-a zf>&l)ZO{gpQ9eWAN7Js4jgx1%?f)Q|a@0P139$wBfmmG5f)Ubx!ph?%Ac7=qr^A7; zgJ_d;0j|Il!pV|U=jDtse5jf7)Kg^d=MARL>X;A=*eg>NgGTDgWrhH65Yx-mh(8Q3 zG(e7wqn(!zC#gKaFeO?n3>9E+>KeIlfOCGL9kmyHgYqd3XvhZa;4@3Wh)<&RsD3td zF}dUY6N3Gi`5DYz>?M>gLXnj}g8{pW-)KCGoUb+xWMdP;b#!5NauHdNgTZEh%*xsg zg&aR+3YxsA|08~&c`lnA@P#mNn$aa&qus{EsMB4DxW_0NvF^+)Gu8ek{7T}j(c^py zMUGi(o$DBR4Eqsk{fxFnQL79ODpgC7+G>I#LOy1ec*1lsv`Oil3eXco6ZUZx7E{PU z%%+JT=KiSN59{lE$_D57q-)JuDNH;vqViGFjl25BgvMbL5<8G0Kyc{QrGDcY8?$qo zZt>|&`<@oFPX;p8P)Uh#5T-&y$`wft?;M!gr-W9fIkGQ@>W-T4$uzlc2w8M;ne(c{ zGR~ZdfG(H_Yun|rkjWgBDcap%{r3K(p$j&nS__LnOX#EBfQQMw)@k;f%NG&~E2nzk zfJ@Rnm=gQxb6ZHmVy?I@TwC2TUXrM^s*)M*j1)(L1jL}DTz4$9RlK{2C7s(g7iA(6 z6VGE38WxIJ>D;Yiv6$&3j<0fhhVRuTH#eWP-#TZk>Cdr(W9q?i$a?D!XVc)uL2)fB zq!8j*$friA%yAf77Kg^r1HpxY3T*D}aC=hag?zDk^>yUht27C@)ExCUrL?Ef#RT~i zLoMW@Vqa`Qy7}k4M_UZ3wTy+z*D!KDx6jbkzT{mMOyyn`bQ(0E_cOWvFycBDvyK9)|UTgsoONj|C>(lB_$9qb)i zocFU5ZXj#o;PG42Dl6Ix%GOo>M%4x#lI0jwr<-0n0&I~hJcT(EQa(jFUNW2U2&q4! zqVNXVB=bMFNO|*iPt8M)N?`Zb9-O+xJj@Bt*i%Cfa|@1drkXnH@O@x?Y(V7RG?EA; z8M2<9*ZRbQQLf5WeWrA77x$BA>IM#+v~2={qqSy?eD9j}815t5^;2b>N_=j%U(w(oRKNoa?r3mP5h>>KE=hQH!YRbjA88yqNWsmTniHFxm{3mJa-p0Qk)~$mYFaS+Z=R-R-(a{?<)tS!#V4rUm9=Dob9;y;%zc{5EV@O2 zqkIQ^@K2DUA?e&)J=JIojw`CzyWE3A{X-il&AkWW=LL8t27TPV`?-@0a=rojEwSKBG9s9Dd9%&$sC znz-78NO@LgZrDws@(B9;=mi)Rj_!|d=%AJ)UvJQMq#8$U{ryT!hrodVx=2KZkD@^M z0JR_^mK7y8J~hLCkBBa)?~k5`Z3IFS#Esd$>gKsUPP;vXb>`q)d?$#OUz%PpT;&#( zF`_oHDfg4LirCp>*sgJJ%n(v%Vz4widt!$dm5R0Z_&h{7YTU&~cs#w8lhHzWp3VX}=mT~^?Y-(>gi-&` zt?p9dLJAV!VHCgWe=SU7qQj-5WB6`$|63IGKTDBmzr&}$MN!|2OaD5GV*I|vKcgs? z?-1)hqo}`tBfBSpI9a_1A^}b+*OC!ou>mY%4<*Ocl|jDbq>M zM6f?eF4PXWeA$9Bba1&kmz1152}FoD^`5xnGE*yNQ;!UPn>@FglvtFphIr954823x z06vuX^$hLyhU5uDHa_kBtK;b-BhsBqOQq&^Q=fNE*Bbd6 zz|%tb4=zk(unIN!bF|$9%+rfqR)h8N8w6tMRL%k8)8eM0$dpOiy+3&4!p1~(zGzcW zM(L;?#&`^VXoKbYmMNj*w#H0#Au0V4x|n|k#tn@L>w?sJCG{--G>pp|lQV>=4Yf~1 z7-Oa!jJpp;r3_i>D%0MjphXZF^t6xM>$@{}{ppnCIfO3c=wmRJ^f3l7n1nJko{OO; zb~FvQ%q%ZV6KaBJY0)J5nl@}7L7OWm96};MWD6TAC$QLechFu>xjZq3F$QxlKebz5 zAJII2(UyrE5DXU&*YTjbEVz6yxa#L7c(XjcbJE%J=6VI`)O!SN&p86Sxw_eT|M)a` zO@?+g;ob3V^m7LCslKnbbazNt?%RE`M&hB`VS-Pdnl4wspd8EXe{ zREBk;Z|K4Ayh z4B_pX@hfi(EPu_D94>am+z=nFDm-lJOE|>OS=0BpzInV<+U*}oVfGs;8E+r04qblb zh!4JsS&Wg$qpUaiNdu!%C?5K=2I1YcKWFQn_I0H^n*7wk1;fZU7+86$Ok{;{8j;sB zF+#yGpzPpK#^pT8c#^IkasbCpI)e;o-f27~yqmqSzusGe&J{-GM<+Qci53qn2Z z#|BlaU{#=|MiIGFWBc}jR}v!ggWRU!2esR*7K?t>l!G61bIHc6!Z1?@VKes76GDoE z)CA}LWJzVG*sTb_6xG=#7XO)n**87jAy!n7GA88Eq8ixA36pyLM-qRo2!`lm#5Z`) z7GApR3H1#^P(=AApGWs;ZQ@!_ETi^(ct)SV6UwPA%u`~c`&W818n42T#IywCbc&so z1PSFV+4FTMVp6i`S}$m#Wkq8_H7dP6L=C zUXxcmzCnISdmNopp^N6X-L*ev#}ap`kVorRJIn@J<*n#!4}^uX-I=N6b^d_()r*d; zkf$bxC7c>ofciH_nZbiaM(X889;=2)JRIvMWC&Qut&!S9TV;(V3z)}Te|iS*g;N~L z0}RFA7_zSLY1$&91)A3jAyZA4lghPmFeB?Tjb-l>4|{us95Ek&CyjeaL1U%(RCsFC`0+TlH(b-&W@R>#0F7L~oF$hi z8ck@6Lr|?iKu|dr4?eX{BR$DdSBmg-a$=ENz*}2dE3>n(?^ooeq_A4Co=B8uO83*T z9vRQqN1{WgiyuCZnu!`dB`> z%f~s+nJnBiYo2#JP(Lth1~E}w(158JZ3u|fMPM)psOTC(U$Bf>T&vxN6*?v96gunp zjCRi&*+s9<;jBgo7*RbJI#2BX%mQ*PRmfZMupQWoF=&t&5ys_=xopGQU}>?@Zg+@d zv?Au*@s<&$_m>W1*5~OlN1Baaw%DG>_@p2r-@ow;$)i?FKa*bn{Mg)tj z6r0gb*aXW4c+!g7thAI=xSUJH*FCCpQ9jcLIO=7qQYY)Rkg<}BxBs}dkIC(I` z<~9c1clT@d=g|@`d)`IazRFp)w__Ak+)gtqqo=)L_4r!i@Jxhqnzr7@n;kDiuVK|6 zljOF9*`ZiTIgH}J2`rn+n}YlSg#?zf^EF26DvNgsL6XAF5Im* z7p3N3nmr}@Xp%XFCKQBbN@z0=vOiK*{nI&NJo4`pc5Z77Y+p9HsyEuvEQ`oxevy3M zQOHGIUY41v!8&p%`Rs5alD3IQmJ6H(wK4}Zi;y=r#jtGassGqr_<_B}y%f<0IMM8* z;;9B5vq5=IR7h0U33O=&{m7mS0@9YpNOyy7lslH{s$<^E*>eCd5?AVWdq~zzWQ%@j z;x+TEwlN}eR@p;h%T?{Byw^J~Rvm{$H=MlSwW)zaNCG9kemn zrjW$|3S?oS04(av#hN_9^<-WZoPXi|4xV3u^!NCx$sN{H+ca-a2m~u|g)I(uHX31) zs#ly>LW`3&O6K8!4HiLpjxBa+H<>IOP1b$BYc2r$=BVsQU1{usPH%{WeN7Q-FpXct zMyPqn8WmwtKOtekGxDWq5&5B%FwhAPum{)zF%$TiQv ze)&jd%w}Cta7Fke=)m9pP*{jb5FQdytvriEnucqdYHZ>JC$-0fjsP7xm6Q8%7r@3M=F`jsy8-c{nR8gp?q0VQ-n z5*_QYC*@sE+9r4iM=2K3lmnzXH;&e#LDNAPgNuYzq8dLvPK0nCeUhKUbx`V=Nnu^R zF=HS!sSPD?6PcV}=@&yB#ErV91s^OE^3nvS`yvcc*;y$VnWdST$<`+|@{<}mv0N|Q z=St4_%jyW)`qzdmh%_mSyw#Wqg#BBWZy8|XO|_!{FYG(@OfH;JXkb;A)T4v?f>uHO zC~+l&$?V3zw zed#{=4wENVVd7#l>D#Hag|HNqgVw`tXJH_5GnQH3*PfdH5~FJg9hizd*SVD7HT=RC zbw#v^%k$})a$eEAlzI3)HGotlb?NRrnS@lgB~!^swoQ<<{F#GTb3Po-O+#MUN0pcD zCF1AsMcYm+|DxI~W`2JGj^<5-_1(j!`Nbyxi(kKFY>Hp)Pzw`uy&}qH1T}HD@RH0* zcCr`6R$;e{Znud=?bYG%o^rM#Jau%0Vu(IrGU&~ece=mWRv{YL#61$21^kE@%c@^+ zVDTiAm!Kn)H-j;w9&03(@I+{nG*7D>Bug$P$xZuIe`EfX$E8NeNDLf(ns7i|%aVEL z#&>=Iv=n6)kGs0GE}Uy>*FUOLEvGf#zMAs4q?YoD#MPXUSh{*Kz}O8ny6gFygtfEu zqER(tt@kP1>gJoDK+%}71BLuRM2a%e%@Sia9$E$RroMs5>uKjYBmELv#_FDaO zF=CkUkRpvm+ZsGLvf^ck4HHLQ$bEsl`D+p0pZ%r;3h@dCw=L99n@*397y2^Xd8ABx z2q)*@kgRZKOA>elAC*m`gJx1e8=cP?Qd&IfhBxL%V)cVtUFk z_Y1X9@^cJfEh>T3jSY@UEiP~JpI0-@t7wsBRp6~_F>Q*K(*+Pk=!RgVo?3)-vOD0P zJM9krtA2loq+B*}l$8`Js%XSWVWxgTsUgiX+a{xHnvgfu7}BTICJvL}qu9TU!J?;$ zCvxyU)J^A3oA9Gq4G|=cHOT%vz>Lk<9g>2*TR+xkeZ-<~;JNtqnP7v)&AS~dFqYn4 zK5mn*$RR81Cfi=ztpZ^g%1y>6#bbUjFUPz*SAV1Qy_;io6jDYhO;~1D#!k;pkEaK! zTDr`@jx{e2yZG}hKOzY9YGm4@BPGU zWU-RR?MNWxn|B{+q71W@6hI%>l&8L$$c7xsSQ&w&I+=ndr>j>a+ zYU94nmls7jD1>OQ3rz=@aV>SacSt zb$-_a=&a2MvMN@!z{x>9*&+gRPt;zl5uzk9Is>uv)@C2+TZ8g@#uVvd!B*^cr=PJA z)5Iv}&tuAN8$5ZS(|uN=l!cm9O85>*A4YP{g}ol6c3sD#Yz|8xz2R+`pyk;tpBPkE zL@G2HRLIW^9)oqBmiuc(@NL9#w39$q8At4pJC5`@0`{wmH|Yo&#JN(}ITm_C4t{7G=uXThwrK&^e9U!@nLwff51IJXpxu4Q*Je zwn$3ZyyC#-`yh=*J8GfS!6(e`NRepJ51TR+c z@VKA5ChFonc7wngwJehl$=CsY#nemd>Rgomn2<)lW@%;m{@;HC-NFh0dkg7DNrKR^ zP7fInc=?`eA_$;1x`owVI&T+<^~Pi z0Q)AW^~e3s%baumm(xFtMvYCAFwXXnUY3Pj@GhliF1CL#1*)7zUmr8Hsi{G$`Z8)i zYcE6>zD{MyIY_8u!|g6Cp1E3LK-nu<={d+5+1Tmjj%mNF%;gyg16JuNd6=sRsfutv z4(et=Z)*4!1>dGcPbOr!wfArUiQ`b=Aqn;FQUAZzt^~TNDh;#f009IQi=a5KP)re% z-20aM-ivK&+fYOr3SCYqndCy76F<0TZ{h5Phi5l8Cns+x?R>=P^6ah$ z)Hox_1**mGKL4N;(}fq@_|~~mq_Ak2(sk9>!_Tg-<(Cz{QSBkLr_RTbDk& zujt^3g|(B&)WwsdmA=K7#=f+md8kbZy(Zau9co(f^T|&p$t}a`Q<_s!NbQ@I3!Uqg zV^W73Ui$09y_z@6{l9fQV3!-_9z6We#%w96+U)CCvE@3`i1Z`NMb|BRvwD7Zq~Nc1^ou+JnVY8t$~teQi|k^y}_BvE|dHg*c7q!>oH+>T6w3<(~U;M^UF!mk&+;Fk5ZT8qjO~p`^aE zleeFr|8jDY^7P(v-M%?!@S@%~4=T}A%HeAk_Wty%@|x;u&fNDW$Jh5Z7B5Mv+ce{Y zj(rgR=A%T81C`#;0$rn-N$=vE#7U}*vV2ov*((@`r7<24|aZQcYi76 zJ>7GI$otCZ3*nsy3g;~uxx2nY-=1$Ay?>9j#*^z=JLK|Gor&Q0{ z<$ckblJ>SeL;bj^C8j)A48tT8;1QGa&)g8ZYW7u|le`vzs*EARgLm!89H?zM{t zWL>B`uzG~WR^0LZFW!Ceovfh^{U2U@u4~DkK3&@_b&&fP=aI`}LayZ<{aHy*Y#NvM z{InjYYa6CVpU@?J*Esi`s*RO51?KPEdPe`{vY{7#h~>;4Zfmk++;!r3j~9xLXYMmT z@UNMDwwHvSsX5oVWnxj?O6vvNjn{8@<)_Wf7mJTCT{**k=k*6NtHuO+|M^VGfh8|( z9eJovw+3I!&8vRMD>(W0)vEiwQJwbwbI+}lhV0yN|7GaTZdj1o)z`di;Kozmb?Ym4 zBcYk&xz&7zX;Aj|q029=>=vA0S@eA4<`V14wV!-2xL?WoHHDeP{d$ft@5kOB=B`+< zD4J2{^w+)_`r(Cf;qKMv1E-UY^}lp^nn^&%;)uo&9QWS ze2N*aHL^+1#fL;H()D9)qq#^R;H~gPINqolu>lKrbFPrx=ZpqI6&yAaJ+KTpT;_ah z&J~96SGvAD5(%13rf4*3j0(m;$ZY~&s3ydlc-{ab4B<+D#2z#F!-GIxToKRMtJC~p zV_YXAyn1W$j5T2+GW{kmV@jDbl&&8>I+q(>9tcNTMRSUg@Osk~ z%dde#5UuuYhc8-<=ith03phNkN-LaJnXcy%0t+PrQVJ2YKeAaw1|*vi0wZB)cKlGw zKN@ODyE--yDuh5=98*2P@PyQg2}3x^s1hza5Uh+JA;f_|iOX+2-jW+u>EsFmu1FLP z8%i_LOwvqRVA}ZQntIA+`|rN;`LqX9HBdH1JL7YN^?yu|YZy~U*;O@zQ$pT2 z1{`IkE2oZzA*>kmbd;H0fsoG*84PEmdg1I)EwN3IetJt9d>QdXyiQ9TVVT}Y>xI{& zA;#>r``zjKn8D$6+2NE@y=Ah)2;pmWzSm=Ey@<~;{m!YK(8NiAUx6iB3P%!Kha7;3^z}WcO)T+5^Ql&wPIaaB6$_9RiP7HIRV2HJGGY%^IYT1pjzm zj-yErXo6t?yFVBaXA}sf{vYT8{)JkMQ(L1q#A3N(M=a!Y>5v9HomR+NKOB)HLE>Co zoQ9Et;r9~puQ>k|nu@RpyCi9!t287l+Dep26BHSKV9=JPU1Mr4LW0Co<21Rdk~kM* zMUttxm?)~ijFSaK(T%>qT_+F%=iF~`fbPeJiLyO$PXzzVQ8QV zrHd9zk+K)6y2ALBM;KA#FpQ$}1!Gl|X`jL)dY<+RQXttFokh&hM4pzb=||@?!n~?d zwn6@>Jnd&0Ix1Q}tRMypgcq0`!i0#lev&LOK0pd&8)KWqAvI__ zu%zv?n;3Hm@r1SwDTIgp=(Nx8*jEU=!$Q$SNuXpPEEDYr@wRgjj}=1a0fuuMS-CQk z<4BPiJCI1|xta_*H^4Fxw#7@(1!GZ^Xqt!>OxXc1>qs(96Cigf+n|QEQxhSFSR8>< z6Lrz_V{imAM0ze1Zx*X6Vs%k5V)T;`UE6^m(zYQ%l$ctKR9-uwvu!Lf(3tvyBtUTD zIN(PxWd{<$Ta*ugpTOc=1?MO7!%#U1Eknc@_Kr@_4~Y_H^@IAt@KYtk&?IJ!()cm9 zNl*_c+rSP2jctN18Vy!iiY5_-#xL*_sa(^{g^e7kxhjiaq{=eo7x0xrV^!lv*AgTV zUfXw4`&`92hH^z?6{!lXAK0d_`9}c$iTpGfgpLtdCQ&{Beh`qvxf)HO^+NON) z`UpgrwE`LosCJ3^A(@ASPNZQdc8q?omndy7=)yC;0t6zOA0kAh^8n=1xrSuef|Q;M zKiJiN{F|7QQ1BTX5hnjMekzqOT8qi#GXR8LkB~&`j3I1H&j9@BItj}q+xtN0L+3sC zkI=OQWbkx6G=8-2q3j}_&QqAo)YEt~Px%kY0^M6^{Aj#EDS^OIa{+{O-2gjOiH;GH zCAt^Y_%ZQ-j*G??kR4A(l}Frr^>0_ zv~#BXKp?_FRpatG(+IySz`?4)<-;4{R&IP+wxndLWEiNQNz^Pg6Xj%q(Y%$HCE03K hWy>Frn5eT@bm53S6p8;=0amqEP&at+u)G}Ie*j?K +#include + +/* include/qd/qd_config.h. Generated from qd_config.h.in by configure. */ +#ifndef _QD_QD_CONFIG_H +#define _QD_QD_CONFIG_H 1 + +#ifndef QD_API +#define QD_API /**/ +#endif + +/* Set to 1 if using VisualAge C++ compiler for __fmadd builtin. */ +#ifndef QD_VACPP_BUILTINS_H +/* #undef QD_VACPP_BUILTINS_H */ +#endif + +/* If fused multiply-add is available, define to correct macro for + using it. It is invoked as QD_FMA(a, b, c) to compute fl(a * b + c). + If correctly rounded multiply-add is not available (or if unsure), + keep it undefined.*/ +#ifndef QD_FMA +/* #undef QD_FMA */ +#endif + +/* If fused multiply-subtract is available, define to correct macro for + using it. It is invoked as QD_FMS(a, b, c) to compute fl(a * b - c). + If correctly rounded multiply-add is not available (or if unsure), + keep it undefined.*/ +#ifndef QD_FMS +#define QD_FMS(a, b, c) std::fma(a,b,-c) +/* #undef QD_FMS */ +#endif + +/* Set the following to 1 to define commonly used function + to be inlined. This should be set to 1 unless the compiler + does not support the "inline" keyword, or if building for + debugging purposes. */ +#ifndef QD_INLINE +#define QD_INLINE 1 +#endif + +/* Set the following to 1 to use ANSI C++ standard header files + such as cmath, iostream, etc. If set to zero, it will try to + include math.h, iostream.h, etc, instead. */ +#ifndef QD_HAVE_STD +#define QD_HAVE_STD 1 +#endif + +/* Set the following to 1 to make the addition and subtraction + to satisfy the IEEE-style error bound + + fl(a + b) = (1 + d) * (a + b) + + where |d| <= eps. If set to 0, the addition and subtraction + will satisfy the weaker Cray-style error bound + + fl(a + b) = (1 + d1) * a + (1 + d2) * b + + where |d1| <= eps and |d2| eps. */ +#ifndef QD_IEEE_ADD +/* #undef QD_IEEE_ADD */ +#endif + +/* Set the following to 1 to use slightly inaccurate but faster + version of multiplication. */ +#ifndef QD_SLOPPY_MUL +#define QD_SLOPPY_MUL 1 +#endif + +/* Set the following to 1 to use slightly inaccurate but faster + version of division. */ +#ifndef QD_SLOPPY_DIV +#define QD_SLOPPY_DIV 1 +#endif + +/* Define this macro to be the isfinite(x) function. */ +#ifndef QD_ISFINITE +#define QD_ISFINITE(x) std::isfinite(x) +#endif + +/* Define this macro to be the isinf(x) function. */ +#ifndef QD_ISINF +#define QD_ISINF(x) std::isinf(x) +#endif + +/* Define this macro to be the isnan(x) function. */ +#ifndef QD_ISNAN +#define QD_ISNAN(x) std::isnan(x) +#endif + + +#endif /* _QD_QD_CONFIG_H */ diff --git a/src/external/PackedCSparse/qd/qd_const.cc b/src/external/PackedCSparse/qd/qd_const.cc new file mode 100644 index 00000000..6f4e01d2 --- /dev/null +++ b/src/external/PackedCSparse/qd/qd_const.cc @@ -0,0 +1,62 @@ +/* + * src/qd_const.cc + * + * This work was supported by the Director, Office of Science, Division + * of Mathematical, Information, and Computational Sciences of the + * U.S. Department of Energy under contract number DE-AC03-76SF00098. + * + * Copyright (c) 2000-2001 + * + * Defines constants used in quad-double package. + */ +#include "qd_config.h" +#include "qd_real.h" + +/* Some useful constants. */ +const qd_real qd_real::_2pi = qd_real(6.283185307179586232e+00, + 2.449293598294706414e-16, + -5.989539619436679332e-33, + 2.224908441726730563e-49); +const qd_real qd_real::_pi = qd_real(3.141592653589793116e+00, + 1.224646799147353207e-16, + -2.994769809718339666e-33, + 1.112454220863365282e-49); +const qd_real qd_real::_pi2 = qd_real(1.570796326794896558e+00, + 6.123233995736766036e-17, + -1.497384904859169833e-33, + 5.562271104316826408e-50); +const qd_real qd_real::_pi4 = qd_real(7.853981633974482790e-01, + 3.061616997868383018e-17, + -7.486924524295849165e-34, + 2.781135552158413204e-50); +const qd_real qd_real::_3pi4 = qd_real(2.356194490192344837e+00, + 9.1848509936051484375e-17, + 3.9168984647504003225e-33, + -2.5867981632704860386e-49); +const qd_real qd_real::_e = qd_real(2.718281828459045091e+00, + 1.445646891729250158e-16, + -2.127717108038176765e-33, + 1.515630159841218954e-49); +const qd_real qd_real::_log2 = qd_real(6.931471805599452862e-01, + 2.319046813846299558e-17, + 5.707708438416212066e-34, + -3.582432210601811423e-50); +const qd_real qd_real::_log10 = qd_real(2.302585092994045901e+00, + -2.170756223382249351e-16, + -9.984262454465776570e-33, + -4.023357454450206379e-49); +const qd_real qd_real::_nan = qd_real(qd::_d_nan, qd::_d_nan, + qd::_d_nan, qd::_d_nan); +const qd_real qd_real::_inf = qd_real(qd::_d_inf, qd::_d_inf, + qd::_d_inf, qd::_d_inf); + +const double qd_real::_eps = 1.21543267145725e-63; // = 2^-209 +const double qd_real::_min_normalized = 1.6259745436952323e-260; // = 2^(-1022 + 3*53) +const qd_real qd_real::_max = qd_real( + 1.79769313486231570815e+308, 9.97920154767359795037e+291, + 5.53956966280111259858e+275, 3.07507889307840487279e+259); +const qd_real qd_real::_safe_max = qd_real( + 1.7976931080746007281e+308, 9.97920154767359795037e+291, + 5.53956966280111259858e+275, 3.07507889307840487279e+259); +const int qd_real::_ndigits = 62; + diff --git a/src/external/PackedCSparse/qd/qd_const.o b/src/external/PackedCSparse/qd/qd_const.o new file mode 100644 index 0000000000000000000000000000000000000000..44cbfa0c9affa9c3158975261842693ffa0c6ade GIT binary patch literal 47400 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