diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..05748818 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "volesti_cran_include"] + path = src/include + url = https://github.com/GeomScale/volesti.git + branch = cran_include diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 00000000..55424ed1 --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,24 @@ +Package: volesti +Type: Package +License: LGPL-3 +Title: Volume Approximation and Sampling of Convex Polytopes +Author: Vissarion Fisikopoulos [aut, cph, cre], + Apostolos Chalkis [cph, aut], + contributors in file inst/AUTHORS +Copyright: file inst/COPYRIGHTS +Description: Provides an R interface for 'volesti' C++ package. 'volesti' computes estimations of volume + of polytopes given by (i) a set of points, (ii) linear inequalities or (iii) Minkowski sum of segments + (a.k.a. zonotopes). There are three algorithms for volume estimation as well as algorithms + 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 +Maintainer: Vissarion Fisikopoulos +Depends: Rcpp (>= 0.12.17) +Imports: methods, stats +LinkingTo: Rcpp, RcppEigen, BH +Suggests: testthat +Encoding: UTF-8 +RoxygenNote: 7.1.1 +BugReports: https://github.com/GeomScale/volesti/issues diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 00000000..e12919ab --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,35 @@ +# Generated by roxygen2: do not edit by hand + +export(compute_indicators) +export(copula) +export(direct_sampling) +export(exact_vol) +export(frustum_of_simplex) +export(gen_cross) +export(gen_cube) +export(gen_prod_simplex) +export(gen_rand_hpoly) +export(gen_rand_vpoly) +export(gen_rand_zonotope) +export(gen_simplex) +export(gen_skinny_cube) +export(inner_ball) +export(read_sdpa_format_file) +export(rotate_polytope) +export(round_polytope) +export(sample_points) +export(volume) +export(write_sdpa_format_file) +export(zonotope_approximation) +exportClasses(Hpolytope) +exportClasses(Spectrahedron) +exportClasses(Vpolytope) +exportClasses(VpolytopeIntersection) +exportClasses(Zonotope) +exportPattern("^[[:alpha:]]+") +importFrom("methods","new") +importFrom("stats","cov") +importFrom("utils","read.csv") +importFrom(Rcpp,evalCpp) +importFrom(Rcpp,loadModule) +useDynLib(volesti, .registration=TRUE) diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 00000000..e3329b9f --- /dev/null +++ b/NEWS.md @@ -0,0 +1,52 @@ +# volesti 1.0.0 + +* This is the first release of volesti R-Package. + +# volesti 1.0.1 + +* Fix some bugs for solaris os. + +# volesti 1.0.2 + +* Remove r-striper to avoid CRAN policy violation. + +# volesti 1.0.3 + +* Fix CRAN warnings. + +# volesti 1.1.0 + +* New volume computation algorithm. +* Billiard walk for uniform sampling. +* Modified exact volume computation function. +* Implementation and evaluation of PCA method for zonotope approximation. +* Boundary sampling. +* Improved functionality for finance applications. +* Improved names for functions and input variables. +* Use exclusively Eigen/BH library for linear algebra. + +# volesti 1.1.1 + +* Fix CRAN warnings about deprecated use of ftime + +# volesti 1.1.2 + +- Improve functionality of R function. +- Use lower case and "_" separated names for the volesti's functions. +- Use R classes for the convex polytopes + +# volesti 1.1.2-2 + +- Remove `loadSdpaFormatFile()` and `readSdpaFormatFile()` functions. + +# volesti 1.1.2-3 + +- Remove unneeded-internal-declaration warning in clang-15 + +# volesti 1.1.2-4 + +- Remove uninitialized warning in clang-16 (lp_presolve) + +# volesti 1.1.2-6 + +- Fix UBSAN issues (lp_presolve) diff --git a/R/HpolytopeClass.R b/R/HpolytopeClass.R new file mode 100644 index 00000000..8cf7ecc7 --- /dev/null +++ b/R/HpolytopeClass.R @@ -0,0 +1,42 @@ +#' An R class to represent an H-polytope +#' +#' 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}. +#' +#' \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) +#' +#' @name Hpolytope-class +#' @rdname Hpolytope-class +#' @exportClass Hpolytope +Hpolytope <- setClass ( + # Class name + "Hpolytope", + + # Defining slot type + representation ( + A = "matrix", + b = "numeric", + volume = "numeric", + type = "character" + ), + + # Initializing slots + prototype = list( + A = as.matrix(0), + b = as.numeric(NULL), + volume = as.numeric(NaN), + type = "Hpolytope" + ) +) diff --git a/R/RcppExports.R b/R/RcppExports.R new file mode 100644 index 00000000..ba2c1cd6 --- /dev/null +++ b/R/RcppExports.R @@ -0,0 +1,323 @@ +# Generated by using Rcpp::compileAttributes() -> do not edit by hand +# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +#' Construct a copula using uniform sampling from the unit simplex +#' +#' Given two families of parallel hyperplanes or a family of parallel hyperplanes and a family of concentric ellispoids centered at the origin intersecting the canonical simplex, this function uniformly samples from the canonical simplex and construct an approximation of the bivariate probability distribution, called copula (see \url{https://en.wikipedia.org/wiki/Copula_(probability_theory)}). +#' At least two families of hyperplanes or one family of hyperplanes and one family of ellipsoids have to be given as input. +#' +#' @param r1 The \eqn{d}-dimensional normal vector of the first family of parallel hyperplanes. +#' @param r2 Optional. The \eqn{d}-dimensional normal vector of the second family of parallel hyperplanes. +#' @param sigma Optional. The \eqn{d\times d} symmetric positive semidefine matrix that describes the family of concentric ellipsoids centered at the origin. +#' @param m The number of the slices for the copula. The default value is 100. +#' @param n The number of points to sample. The default value is \eqn{5\cdot 10^5}. +#' @param seed Optional. 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 \eqn{m\times m} numerical matrix that corresponds to a copula. +#' @examples +#' # compute a copula for two random families of parallel hyperplanes +#' h1 = runif(n = 10, min = 1, max = 1000) +#' h1 = h1 / 1000 +#' h2=runif(n = 10, min = 1, max = 1000) +#' h2 = h2 / 1000 +#' cop = copula(r1 = h1, r2 = h2, m = 10, n = 100000) +#' +#' # compute a copula for a family of parallel hyperplanes and a family of conentric ellipsoids +#' h = runif(n = 10, min = 1, max = 1000) +#' h = h / 1000 +#' E = replicate(10, rnorm(20)) +#' E = cov(E) +#' cop = copula(r1 = h, sigma = E, m = 10, n = 100000) +#' +#' @export +copula <- function(r1, r2 = NULL, sigma = NULL, m = NULL, n = NULL, seed = NULL) { + .Call(`_volesti_copula`, r1, r2, sigma, m, n, seed) +} + +#' Sample perfect uniformly distributed points from well known convex bodies: (a) the unit simplex, (b) the canonical simplex, (c) the boundary of a hypersphere or (d) the interior of a hypersphere. +#' +#' The \eqn{d}-dimensional unit simplex is the set of points \eqn{\vec{x}\in \R^d}, s.t.: \eqn{\sum_i x_i\leq 1}, \eqn{x_i\geq 0}. The \eqn{d}-dimensional canonical simplex is the set of points \eqn{\vec{x}\in \R^d}, s.t.: \eqn{\sum_i x_i = 1}, \eqn{x_i\geq 0}. +#' +#' @param body A list to request exact uniform sampling from special well known convex bodies through the following input parameters: +#' \itemize{ +#' \item{\code{type} }{ A string that declares the type of the body for the exact sampling: a) \code{'unit_simplex'} for the unit simplex, b) \code{'canonical_simplex'} for the canonical simplex, c) \code{'hypersphere'} for the boundary of a hypersphere centered at the origin, d) \code{'ball'} for the interior of a hypersphere centered at the origin.} +#' \item{\code{dimension} }{ An integer that declares the dimension when exact sampling is enabled for a simplex or a hypersphere.} +#' \item{\code{radius} }{ The radius of the \eqn{d}-dimensional hypersphere. The default value is \eqn{1}.} +#' \item{\code{seed} }{ A fixed seed for the number generator.} +#' } +#' @param n The number of points that the function is going to sample. +#' +#' @references \cite{R.Y. Rubinstein and B. Melamed, +#' \dQuote{Modern simulation and modeling} \emph{ Wiley Series in Probability and Statistics,} 1998.} +#' @references \cite{A Smith, Noah and W Tromble, Roy, +#' \dQuote{Sampling Uniformly from the Unit Simplex,} \emph{ Center for Language and Speech Processing Johns Hopkins University,} 2004.} +#' +#' @return A \eqn{d\times n} matrix that contains, column-wise, the sampled points from the convex polytope P. +#' @examples +#' # 100 uniform points from the 2-d unit ball +#' points = direct_sampling(n = 100, body = list("type" = "ball", "dimension" = 2)) +#' @export +direct_sampling <- function(body, n) { + .Call(`_volesti_direct_sampling`, body, n) +} + +#' Compute the exact volume of (a) a zonotope (b) an arbitrary simplex in V-representation or (c) if the volume is known and declared by the input object. +#' +#' Given a zonotope (as an object of class Zonotope), this function computes the sum of the absolute values of the determinants of all the \eqn{d \times d} submatrices of the \eqn{m\times d} matrix \eqn{G} that contains row-wise the \eqn{m} \eqn{d}-dimensional segments that define the zonotope. +#' For an arbitrary simplex that is given in V-representation this function computes the absolute value of the determinant formed by the simplex's points assuming it is shifted to the origin. +#' +#' @param P A polytope +#' +#' @references \cite{E. Gover and N. Krikorian, +#' \dQuote{Determinants and the Volumes of Parallelotopes and Zonotopes,} \emph{Linear Algebra and its Applications, 433(1), 28 - 40,} 2010.} +#' +#' @return The exact volume of the input polytope, for zonotopes, simplices in V-representation and polytopes with known exact volume +#' @examples +#' +#' # compute the exact volume of a 5-dimensional zonotope defined by the Minkowski sum of 10 segments +#' 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(V = V) +#' vol = exact_vol(P) +#' } +#' +#' # compute the exact volume the 10-dimensional cross polytope +#' P = gen_cross(10,'V') +#' vol = exact_vol(P) +#' @export +exact_vol <- function(P) { + .Call(`_volesti_exact_vol`, P) +} + +#' Compute the percentage of the volume of the simplex that is contained in the intersection of a half-space and the simplex. +#' +#' A half-space \eqn{H} is given as a pair of a vector \eqn{a\in R^d} and a scalar \eqn{z0\in R} s.t.: \eqn{a^Tx\leq z0}. This function calls the Ali's version of the Varsi formula to compute a frustum of the simplex. +#' +#' @param a A \eqn{d}-dimensional vector that defines the direction of the hyperplane. +#' @param z0 The scalar that defines the half-space. +#' +#' @references \cite{Varsi, Giulio, +#' \dQuote{The multidimensional content of the frustum of the simplex,} \emph{Pacific J. Math. 46, no. 1, 303--314,} 1973.} +#' +#' @references \cite{Ali, Mir M., +#' \dQuote{Content of the frustum of a simplex,} \emph{ Pacific J. Math. 48, no. 2, 313--322,} 1973.} +#' +#' @return The percentage of the volume of the simplex that is contained in the intersection of a given half-space and the simplex. +#' +#' @examples +#' # compute the frustum of H: -x1+x2<=0 +#' a=c(-1,1) +#' z0=0 +#' frustum = frustum_of_simplex(a, z0) +#' @export +frustum_of_simplex <- function(a, z0) { + .Call(`_volesti_frustum_of_simplex`, a, z0) +} + +#' Compute an inscribed ball of a convex polytope +#' +#' For a H-polytope described by a \eqn{m\times d} matrix \eqn{A} and a \eqn{m}-dimensional vector \eqn{b}, s.t.: \eqn{P=\{x\ |\ Ax\leq b\} }, this function computes the largest inscribed ball (Chebychev ball) by solving the corresponding linear program. +#' For both zonotopes and V-polytopes the function computes the minimum \eqn{r} s.t.: \eqn{ r e_i \in P} for all \eqn{i=1, \dots ,d}. Then the ball centered at the origin with radius \eqn{r/ \sqrt{d}} is an inscribed ball. +#' +#' @param P A convex polytope. It is an object from class (a) Hpolytope or (b) Vpolytope or (c) Zonotope or (d) VpolytopeIntersection. +#' +#' @return A \eqn{(d+1)}-dimensional vector that describes the inscribed ball. The first \eqn{d} coordinates corresponds to the center of the ball and the last one to the radius. +#' +#' @examples +#' # compute the Chebychev ball of the 2d unit simplex +#' P = gen_simplex(2,'H') +#' ball_vec = inner_ball(P) +#' +#' # compute an inscribed ball of the 3-dimensional unit cube in V-representation +#' P = gen_cube(3, 'V') +#' ball_vec = inner_ball(P) +#' @export +inner_ball <- function(P) { + .Call(`_volesti_inner_ball`, P) +} + +#' 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) +} + +#' An internal Rccp function as a polytope generator +#' +#' @param kind_gen An integer to declare the type of the polytope. +#' @param Vpoly_gen A boolean parameter to declare if the requested polytope has to be in V-representation. +#' @param Zono_gen A boolean parameter to declare if the requested polytope has to be a zonotope. +#' @param dim_gen An integer to declare the dimension of the requested polytope. +#' @param m_gen An integer to declare the number of generators for the requested random zonotope or the number of vertices for a V-polytope. +#' @param seed Optional. A fixed seed for the random polytope generator. +#' +#' @keywords internal +#' +#' @return A numerical matrix describing the requested polytope +poly_gen <- function(kind_gen, Vpoly_gen, Zono_gen, dim_gen, m_gen, seed = NULL) { + .Call(`_volesti_poly_gen`, kind_gen, Vpoly_gen, Zono_gen, dim_gen, m_gen, seed) +} + +#' An internal Rccp function for the random rotation of a convex polytope +#' +#' @param P A convex polytope (H-, V-polytope or a zonotope). +#' @param T Optional. A rotation matrix. +#' @param seed Optional. A fixed seed for the random linear map generator. +#' +#' @keywords internal +#' +#' @return A matrix that describes the rotated polytope +rotating <- function(P, T = NULL, seed = NULL) { + .Call(`_volesti_rotating`, P, T, seed) +} + +#' Internal rcpp function for the rounding of a convex polytope +#' +#' @param P A convex polytope (H- or V-representation or zonotope). +#' @param settings A list to set the random walk and its walk length +#' @param seed Optional. A fixed seed for the number generator. +#' +#' @keywords internal +#' +#' @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. +rounding <- function(P, settings = NULL, seed = NULL) { + .Call(`_volesti_rounding`, P, settings, seed) +} + +#' Sample uniformly or normally distributed points from a convex Polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes). +#' +#' Sample n points with uniform or multidimensional spherical gaussian -with a mode at any point- as the target distribution. +#' +#' @param P A convex polytope. It is an object from class (a) Hpolytope or (b) Vpolytope or (c) Zonotope or (d) VpolytopeIntersection. +#' @param n The number of points that the function is going to sample from the convex polytope. +#' @param 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{'BCDHR'} boundary sampling by keeping the extreme points of CDHR or vi) \code{'BRDHR'} boundary sampling by keeping the extreme points of RDHR. The default walk is \code{'BiW'} for the uniform distribution or \code{'CDHR'} for the Gaussian distribution.} +#' \item{\code{walk_length} }{ The number of the steps per generated point for the random walk. The default value is 1.} +#' \item{\code{nburns} }{ The number of points to burn before start sampling.} +#' \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.} +#' \item{\code{seed} }{ A fixed seed for the number generator.} +#' } +#' @param 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.} +#' \item{\code{variance} }{ The variance of the multidimensional spherical gaussian. 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()}.} +#' } +#' +#' @return A \eqn{d\times n} matrix that contains, column-wise, the sampled points from the convex polytope P. +#' @examples +#' # uniform distribution from the 3d unit cube in H-representation using ball walk +#' P = gen_cube(3, 'H') +#' points = sample_points(P, n = 100, random_walk = list("walk" = "BaW", "walk_length" = 5)) +#' +#' # 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(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 +#' P = gen_rand_hpoly(2,20) +#' points = sample_points(P, n = 100, random_walk = list("walk" = "BRDHR")) +#' +#' @export +sample_points <- function(P, n, random_walk = NULL, distribution = NULL) { + .Call(`_volesti_sample_points`, P, n, random_walk, distribution) +} + +#' The main function for volume approximation of a convex Polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes) +#' +#' For the volume approximation can be used three algorithms. Either CoolingBodies (CB) or SequenceOfBalls (SOB) or CoolingGaussian (CG). An H-polytope with \eqn{m} facets is described by a \eqn{m\times d} matrix \eqn{A} and a \eqn{m}-dimensional vector \eqn{b}, s.t.: \eqn{P=\{x\ |\ Ax\leq b\} }. A V-polytope is defined as the convex hull of \eqn{m} \eqn{d}-dimensional points which correspond to the vertices of P. A zonotope is desrcibed by the Minkowski sum of \eqn{m} \eqn{d}-dimensional segments. +#' +#' @param P A convex polytope. It is an object from class a) Hpolytope or b) Vpolytope or c) Zonotope or d) VpolytopeIntersection. +#' @param settings Optional. A list that declares which algorithm, random walk and values of parameters to use, as follows: +#' \itemize{ +#' \item{\code{algorithm} }{ A string to set the algorithm to use: a) \code{'CB'} for CB algorithm, b) \code{'SoB'} for SOB algorithm or b) \code{'CG'} for CG algorithm. The defalut algorithm is \code{'CB'}.} +#' \item{\code{error} }{ A numeric value to set the upper bound for the approximation error. The default value is \eqn{1} for SOB algorithm and \eqn{0.1} otherwise.} +#' \item{\code{random_walk} }{ A string that declares the random walk method: a) \code{'CDHR'} for Coordinate Directions Hit-and-Run, b) \code{'RDHR'} for Random Directions Hit-and-Run, c) \code{'BaW'} for Ball Walk, or \code{'BiW'} for Billiard walk. For CB and SOB algorithms the default walk is \code{'CDHR'} for H-polytopes and \code{'BiW'} for the other representations. For CG algorithm the default walk is \code{'CDHR'} for H-polytopes and \code{'RDHR'} for the other representations.} +#' \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{400+3d^2} for CB or \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 A boolean parameter for rounding. The default value is \code{FALSE}. +#' +#' @references \cite{I.Z.Emiris and V. Fisikopoulos, +#' \dQuote{Practical polytope volume approximation,} \emph{ACM Trans. Math. Soft.,} 2018.}, +#' @references \cite{A. Chalkis and I.Z.Emiris and V. Fisikopoulos, +#' \dQuote{Practical Volume Estimation by a New Annealing Schedule for Cooling Convex Bodies,} \emph{CoRR, abs/1905.05494,} 2019.}, +#' @references \cite{B. Cousins and S. Vempala, \dQuote{A practical volume algorithm,} \emph{Springer-Verlag Berlin Heidelberg and The Mathematical Programming Society,} 2015.} +#' +#' +#' @return The approximation of the volume of a convex polytope. +#' @examples +#' +#' # calling SOB algorithm for a H-polytope (3d unit simplex) +#' HP = gen_cube(3,'H') +#' vol = volume(HP) +#' +#' # calling CG algorithm for a V-polytope (2d simplex) +#' VP = gen_simplex(2,'V') +#' vol = volume(VP, settings = list("algorithm" = "CG")) +#' +#' # calling CG algorithm for a 2-dimensional zonotope defined as the Minkowski sum of 4 segments +#' Z = gen_rand_zonotope(2, 4) +#' vol = volume(Z, settings = list("random_walk" = "RDHR", "walk_length" = 2)) +#' +#' @export +volume <- function(P, settings = NULL, rounding = FALSE) { + .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 +#' +#' @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. +#' @param seed Optional. A fixed seed for the number generator. +#' +#' @keywords internal +#' +#' @return A List that contains a numerical matrix that describes the PCA approximation as a H-polytope and the ratio of fitness. +zono_approx <- function(Z, fit_ratio = NULL, settings = NULL, seed = NULL) { + .Call(`_volesti_zono_approx`, Z, fit_ratio, settings, seed) +} + diff --git a/R/SpectrahedronClass.R b/R/SpectrahedronClass.R new file mode 100644 index 00000000..822ce013 --- /dev/null +++ b/R/SpectrahedronClass.R @@ -0,0 +1,33 @@ +#' An R class to represent a Spectrahedron +#' +#' 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. +#' +#' \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); +#' +#' @name Spectrahedron-class +#' @rdname Spectrahedron-class +#' @exportClass Spectrahedron +Spectrahedron <- setClass ( + # Class name + "Spectrahedron", + + # Defining slot type + representation ( + matrices = "list" + ), + + # Initializing slots + prototype = list( + matrices = list() + ) +) diff --git a/R/VpolytopeClass.R b/R/VpolytopeClass.R new file mode 100644 index 00000000..0bb34758 --- /dev/null +++ b/R/VpolytopeClass.R @@ -0,0 +1,37 @@ +#' An R class to represent a V-polytope +#' +#' A V-polytope is a convex polytope defined by the set of its vertices. +#' +#' \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) +#' +#' @name Vpolytope-class +#' @rdname Vpolytope-class +#' @exportClass Vpolytope +Vpolytope <- setClass ( + # Class name + "Vpolytope", + + # Defining slot type + representation ( + V = "matrix", + volume = "numeric", + type = "character" + ), + + # Initializing slots + prototype = list( + V = as.matrix(0), + volume = as.numeric(NaN), + type = "Vpolytope" + ) +) diff --git a/R/VpolytopeIntersectionClass.R b/R/VpolytopeIntersectionClass.R new file mode 100644 index 00000000..d0764708 --- /dev/null +++ b/R/VpolytopeIntersectionClass.R @@ -0,0 +1,42 @@ +#' An R class to represent the intersection of two V-polytopes +#' +#' An intersection of two V-polytopes is defined by the intersection of the two coresponding convex hulls. +#' +#' \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) +#' +#' @name VpolytopeIntersection-class +#' @rdname VpolytopeIntersection-class +#' @exportClass VpolytopeIntersection +VpolytopeIntersection <- setClass ( + # Class name + "VpolytopeIntersection", + + # Defining slot type + representation ( + V1 = "matrix", + V2 = "matrix", + volume = "numeric", + type = "character" + ), + + # Initializing slots + prototype = list( + V1 = as.matrix(0), + V2 = as.matrix(0), + volume = as.numeric(NaN), + type = "VpolytopeIntersection" + ) +) diff --git a/R/ZonotopeClass.R b/R/ZonotopeClass.R new file mode 100644 index 00000000..d226ae35 --- /dev/null +++ b/R/ZonotopeClass.R @@ -0,0 +1,37 @@ +#' An R class to represent a Zonotope +#' +#' A zonotope is a convex polytope defined by the Minkowski sum of \eqn{m} \eqn{d}-dimensional segments. +#' +#' \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) +#' +#' @name Zonotope-class +#' @rdname Zonotope-class +#' @exportClass Zonotope +Zonotope <- setClass ( + # Class name + "Zonotope", + + # Defining slot type + representation ( + G = "matrix", + volume = "numeric", + type = "character" + ), + + # Initializing slots + prototype = list( + G = as.matrix(0), + volume = as.numeric(NaN), + type = "Zonotope" + ) +) diff --git a/R/compute_indicators.R b/R/compute_indicators.R new file mode 100644 index 00000000..b16012e1 --- /dev/null +++ b/R/compute_indicators.R @@ -0,0 +1,133 @@ +#' 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. +#' 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 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 +#' # simple example on random asset returns +#' asset_returns = replicate(10, rnorm(14)) +#' market_states_and_indicators = compute_indicators(asset_returns, +#' parameters = list("win_length" = 10, "m" = 10, "n" = 10000, "nwarning" = 2, "ncrisis" = 3)) +#' +#' @export +#' @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) { + compRet[j] = compRet[j] * (1 + returns[k, j]) + } + 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) { + if (row+col<0.8*m || row+col>1.2*m) { + red_mass = red_mass + cop[row,col] + } + } else { + if (row+col>=0.8*m+1 && row+col<=1.2*m+1) { + blue_mass = blue_mass + cop[row,col] + } + } + } + } + indicators = c(indicators, blue_mass / red_mass) + } + + N = length(indicators) + + index = 0 + set_index = FALSE + col = rep("normal", N) + + for (i in 1:N) { + + if(indicators[i]>1 && !set_index){ + index = i + set_index = TRUE + } else if (indicators[i]<1) { + if(set_index){ + if(i-index > nwarning-1 && i-index <= ncrisis-1){ + col[index:(i-1)] = "warning" + } else if(i-index > ncrisis-1) { + col[index:(i-1)] = "crisis" + } + } + set_index = FALSE + } + } + if(set_index){ + if(N-index+1 > nwarning-1 && N-index+1 <= ncrisis-1){ + col[index:i] = "warning" + } else if(N-index+1 > ncrisis-1) { + col[index:i] = "crisis" + } + } + + return(list("indicators" = indicators, market_states = col)) + +} diff --git a/R/gen_cross.R b/R/gen_cross.R new file mode 100644 index 00000000..a1f20f1a --- /dev/null +++ b/R/gen_cross.R @@ -0,0 +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. Default valus is 'H'. +#' +#' @return A polytope class representing a cross polytope in H- or V-representation. +#' @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 = 'H') { + + kind_gen = 2 + m_gen = 0 + if (representation == 'V') { + Vpoly_gen = TRUE + } 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), drop = FALSE] + + if (Vpoly_gen) { + P = Vpolytope(V = Mat, volume = 2^dimension / prod(1:dimension)) + } else { + 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 new file mode 100644 index 00000000..b57de75a --- /dev/null +++ b/R/gen_cube.R @@ -0,0 +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. Default valus is 'H'. +#' +#' @return A polytope class representing the unit \eqn{d}-dimensional hypercube in H- or V-representation. +#' @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 = 'H') { + + kind_gen = 1 + m_gen = 0 + if (representation == 'V') { + Vpoly_gen = TRUE + } 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), drop = FALSE] + if (Vpoly_gen) { + P = Vpolytope(V = Mat, volume = 2^dimension) + } else { + 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 new file mode 100644 index 00000000..3bd304c4 --- /dev/null +++ b/R/gen_prod_simplex.R @@ -0,0 +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), drop = FALSE] + + P = Hpolytope(A = -Mat, b = b, volume = (1/prod(1:dimension))^2) + + return(P) + +} diff --git a/R/gen_rand_hpoly.R b/R/gen_rand_hpoly.R new file mode 100644 index 00000000..9e1a4fb4 --- /dev/null +++ b/R/gen_rand_hpoly.R @@ -0,0 +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 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 +#' # generate a 10-dimensional polytope with 50 facets +#' P = gen_rand_hpoly(10, 50) +#' @export +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), drop = FALSE] + + P = Hpolytope(A = Mat, b = b) + + return(P) +} diff --git a/R/gen_rand_vpoly.R b/R/gen_rand_vpoly.R new file mode 100644 index 00000000..3486f2e1 --- /dev/null +++ b/R/gen_rand_vpoly.R @@ -0,0 +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 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 +#' # 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 = 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), drop = FALSE] + + P = Vpolytope(V = Mat) + + return(P) +} diff --git a/R/gen_rand_zonotope.R b/R/gen_rand_zonotope.R new file mode 100644 index 00000000..656f844b --- /dev/null +++ b/R/gen_rand_zonotope.R @@ -0,0 +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 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 +#' # 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 = 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), drop = FALSE] + + P = Zonotope(G = Mat) + + return(P) +} diff --git a/R/gen_simplex.R b/R/gen_simplex.R new file mode 100644 index 00000000..48fbfcbc --- /dev/null +++ b/R/gen_simplex.R @@ -0,0 +1,41 @@ +#' 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. 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 = 'H') { + + kind_gen = 3 + m_gen = 0 + if (representation == "V") { + Vpoly_gen = TRUE + } 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), drop = FALSE] + + if (Vpoly_gen) { + P = Vpolytope(V = Mat, volume = 1/prod(1:dimension)) + } else { + P = Hpolytope(A = -Mat, b = b, volume = 1/prod(1:dimension)) + } + + return(P) +} diff --git a/R/gen_skinny_cube.R b/R/gen_skinny_cube.R new file mode 100644 index 00000000..d31c19de --- /dev/null +++ b/R/gen_skinny_cube.R @@ -0,0 +1,28 @@ +#' 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 +#' # generate a 10-dimensional skinny hypercube. +#' 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), drop = FALSE] + + P = Hpolytope(A = -Mat, b = b, volume = 2^(dimension -1)*200) + + return(P) +} diff --git a/R/read_sdpa_file.R b/R/read_sdpa_file.R new file mode 100644 index 00000000..ebabb8bd --- /dev/null +++ b/R/read_sdpa_file.R @@ -0,0 +1,26 @@ +#' Read a SDPA format file +#' +#' Read a SDPA format file and return a spectrahedron (an object of class Spectrahedron) which is defined by +#' the linear matrix inequality in the input file, and the objective function. +#' +#' @param path Name of the input file +#' +#' @return A list with two named items: an item "matrices" which is an object of class Spectrahedron and an vector "objFunction" +#' +#' @examples +#' path = system.file('extdata', package = 'volesti') +#' l = read_sdpa_format_file(paste0(path,'/sdpa_n2m3.txt')) +#' Spectrahedron = l$spectrahedron +#' objFunction = l$objFunction +#' @export +#' @useDynLib volesti, .registration=TRUE +#' @importFrom Rcpp evalCpp +#' @importFrom Rcpp loadModule +#' @importFrom "methods" "new" +#' @exportPattern "^[[:alpha:]]+" +read_sdpa_format_file <- function(path){ + l = load_sdpa_format_file(path) + S = Spectrahedron(matrices = l$matrices) + + return(list("spectrahedron"=S, "objFunction"= l$objFunction)) +} diff --git a/R/rotate_polytope.R b/R/rotate_polytope.R new file mode 100644 index 00000000..26837e01 --- /dev/null +++ b/R/rotate_polytope.R @@ -0,0 +1,75 @@ +#' 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 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. +#' \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}.} +#' \item{If \eqn{P} is a zonotope and \eqn{G} is the matrix that contains column-wise the generators of \eqn{Q} then \eqn{T^TG} contains the generators of \eqn{P}.} +#' \item{If \eqn{M} is a matrix that contains column-wise points in \eqn{Q} then \eqn{T^TM} contains points in \eqn{P}.} +#' } +#' @examples +#' # rotate a H-polytope (2d unit simplex) +#' 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) +#' poly_matrix_list = rotate_polytope(Z) +#' @export +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) + + 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]) + + # first column is the vector b + b = Mat[, 1] + + # remove first column + A = Mat[, -c(1), drop = FALSE] + + if (type == 'Vpolytope') { + PP = Vpolytope(V = A) + }else if (type == 'Zonotope') { + PP = Zonotope(G = A) + } else { + PP = Hpolytope(A = A, b = b) + } + return(list("P" = PP, "T" = Tr)) +} diff --git a/R/round_polytope.R b/R/round_polytope.R new file mode 100644 index 00000000..32b92bd4 --- /dev/null +++ b/R/round_polytope.R @@ -0,0 +1,62 @@ +#' 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 settings Optional. A list to parameterize the method by the random walk. +#' \itemize{ +#' \item{\code{random_walk} }{ The random walk to sample uniformly distributed points: (a) \code{'CDHR'} for Coordinate Directions Hit-and-Run, (b) \code{'RDHR'} for Random Directions Hit-and-Run or (c) \code{'BiW'} for Billiard walk. The default random walk is \code{'CDHR'} for H-polytopes and \code{'BiW'} for the rest of the representations.} +#' \item{\code{walk_length} }{ The walk length of the random walk. The default value is \eqn{10 + 10d} for \code{'CDHR'} or \code{'RDHR'} and 2 for \code{'BiW'}.} +#' \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, +#' \dQuote{Practical polytope volume approximation,} \emph{ACM Trans. Math. Soft.,} 2018.}, +#' @references \cite{Michael J. Todd and E. Alper Yildirim, +#' \dQuote{On Khachiyan’s Algorithm for the Computation of Minimum Volume Enclosing Ellipsoids,} \emph{Discrete Applied Mathematics,} 2007.} +#' +#' @examples +#' # rotate a H-polytope (2d unit simplex) +#' 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) +#' listHpoly = round_polytope(P) +#' +#' # rotate a V-polytope (3d unit cube) using Random Directions HnR with step equal to 50 +#' P = gen_cube(3, 'V') +#' ListVpoly = round_polytope(P) +#' +#' # round a 2-dimensional zonotope defined by 6 generators using ball walk +#' Z = gen_rand_zonotope(2,6) +#' ListZono = round_polytope(Z) +#' @export +round_polytope <- function(P, settings = list()){ + + seed = NULL + if (!is.null(settings$seed)) { + seed = settings$seed + } + + ret_list = rounding(P, settings, seed) + + #get the matrix that describes the polytope + Mat = ret_list$Mat + + # first column is the vector b + b = Mat[, 1] + + # remove first column + 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 { + 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 new file mode 100644 index 00000000..52387076 --- /dev/null +++ b/R/zonotope_approximation.R @@ -0,0 +1,48 @@ +#' 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{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.} +#' } +#' +#' @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, 8) +#' retList = zonotope_approximation(Z = Z) +#' +#' @export +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] + + # remove first column + 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/README.md b/README.md new file mode 100644 index 00000000..01928c1c --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# Volume computation and sampling + +## About +The `volesti` package provides [R](https://www.r-project.org/) with functions for volume estimation and sampling. In particular, it provides an R interface for the C++ library [**volesti**](https://github.com/GeomScale/volesti). + +`volesti` computes approximations of volume of polytopes given as a set of points or linear inequalities or as a Minkowski sum of segments (zonotopes). There are algorithms for volume approximation as well as algorithms for sampling, rounding and rotating polytopes. Last but not least, `volesti` provides implementations of geometric algorithms to compute the score of a portfolio given asset returns and to detect financial crises in stock markets. + +## Download and install + +* The latest stable version is available from CRAN. +* The latest development version is available on Github `https://github.com/GeomScale/Rvolesti` + +To use the development version of `volesti` that includes the C++ code, you need to clone the repository and fetch the submodule. Follow these steps: + +* Clone the main `Rvolesti` repository. +* Change into `include` directory inside `src`: +``` +cd src +cd include +``` +* Fetch the submodule from the repository https://github.com/Soumya624/volesti: +``` +git submodule update --init --recursive +``` +* Install volesti by running: +``` +install.packages("volesti") +``` + +The package-dependencies are: `Rcpp`, `RcppEigen`, `BH`. + +## Documentation + +* [Using the R Interface](https://github.com/GeomScale/volesti/blob/v1.1.1/doc/r_interface.md) +* [Wikipage with Tutorials and Demos](https://github.com/GeomScale/volesti/wiki) +* [Tutorial given to PyData meetup](https://vissarion.github.io/tutorials/volesti_tutorial_pydata.html) + + +## Credits + +* [Contributors and Package History](https://github.com/GeomScale/volesti/blob/v1.1.1/doc/credits.md) +* [List of Publications](https://github.com/GeomScale/volesti/blob/v1.1.1/doc/publications.md) + +Copyright (c) 2012-2020 Vissarion Fisikopoulos +Copyright (c) 2018-2020 Apostolos Chalkis + +You may redistribute or modify the software under the GNU Lesser General Public License as published by Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. + diff --git a/inst/AUTHORS b/inst/AUTHORS new file mode 100644 index 00000000..956eb325 --- /dev/null +++ b/inst/AUTHORS @@ -0,0 +1,4 @@ +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. + diff --git a/inst/COPYRIGHTS b/inst/COPYRIGHTS new file mode 100644 index 00000000..39cabd5a --- /dev/null +++ b/inst/COPYRIGHTS @@ -0,0 +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 + +Copyrights and modification details are explicitly described at the beginning of each of those files. + diff --git a/inst/checks/README.md b/inst/checks/README.md new file mode 100644 index 00000000..a21b374b --- /dev/null +++ b/inst/checks/README.md @@ -0,0 +1,31 @@ +| Check | cran-name | ERRORS | WARNINGS | NOTES | URL | +| ----------------------- |-------------|:-------------:| :-------: | :---: | -- | +| `macOS 10.9 Mavericks, R-oldrel (experimental)` | `r-oldrel-osx-x86_64` | 0 | 0 | 0 | [PREPERROR](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-4561558b7b96f9d9ab4662b565f1b871)* | +| `macOS 10.11 El Capitan, R-release (experimental)` | `r-release-osx-x86_64` | 0 | 0 | 0 | [OK](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-b02c328852acddb3fcd3048f9f327055) | +| `Windows Server 2008 R2 SP1, R-oldrel, 32/64 bit` | `r-oldrel-windows-ix86+x86_64` | 0 | 0 | 0 | [OK](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-f43945e177bd0a39f2a4effa8f6e9c88) | +| `Windows Server 2008 R2 SP1, R-release, 32/64 bit` | `r-release-windows-ix86+x86_64` | 0 | 0 | 0 | [OK](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-707ffbeaa5fb7f3b00a1cff357e63c7f) | +| `Windows Server 2008 R2 SP1, R-patched, 32/64 bit` | `N/A` | 0 | 0 | 0 | [OK](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-3376f9b5f6a17ba0a2c30b0cbf0bf917) | +| `Windows Server 2008 R2 SP1, R-devel, 32/64 bit` | `r-devel-windows-ix86+x86_64` | 0 | 0 | 0 | [OK](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-901e108dfc1802c6f8726f162c95e597) | +| `Windows Server 2012, R-devel, Rtools4.0, 32/64 bit (experimental)` | `N/A` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-c48dc0fa6839ef8b509c73c48f0ca887) | +| `Oracle Solaris 10, x86, 32 bit, R-patched (experimental)` | `r-patched-solaris-x86` | 0 | 0 | 0 | [OK](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-39bda3d243591a660eb3a4d2cdb4ac3f) | +| `Debian Linux, R-devel, GCC` | `r-devel-linux-x86_64-debian-gcc` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-907c4d873965c64d435bc672335063eb) | +| `Debian Linux, R-devel, GCC, no long double` | `N/A` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-cb5a82d9e3112bb706e6c767e778e3a5) | +| `Debian Linux, R-release, GCC` | `r-release-linux-x86_64` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-4c142b0323d2f174d53a761564e0b498) | +| `Debian Linux, R-patched, GCC` | `r-patched-linux-x86_64` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-031047558bf41664b387180f558adfd1) | +| `Debian Linux, R-devel, clang, ISO-8859-15 locale` | `r-devel-linux-x86_64-debian-clang` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-eecaf29af2fd88fdf148667c5463e141) | +| `Fedora Linux, R-devel, GCC` | `r-devel-linux-x86_64-fedora-gcc` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-f919c24153e9afd09ef0654d4799fb12) | +| `Fedora Linux, R-devel, clang, gfortran` | `r-devel-linux-x86_64-fedora-clang` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-0efbc2539568b86e2fa716db1a1231d6) | +| `Ubuntu Linux 16.04 LTS, R-devel, GCC` | `N/A` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-7d47fbd13a90cb4c3712232cfa320fc3) | +| `Ubuntu Linux 16.04 LTS, R-release, GCC` |`N/A` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-6911a48496c075be151e7ba5d6326fbb) | +| `CentOS 6, stock R from EPEL` |`N/A` | 1 | 0 | 0 | [ERROR](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-6f5d27f5b548d46da8fcafab677b86e4)** | +| `CentOS 6 with Redhat Developer Toolset, R from EPEL` | `N/A` | 0 | 0 | 1 | [NOTE](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-efce07b30b0a73c6c4f39db5fed70efa) | +| `Debian Linux, R-devel, GCC ASAN/UBSAN` *** | `N/A` | | | | [SUCCESS](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-1d695055bc3e2facdc252827c5d27f1b) | +| `Ubuntu Linux 16.04 LTS, R-devel with rchk` *** | `N/A` | | | | [SUCCESS](https://builder.r-hub.io/status/volesti_1.0.2.tar.gz-a35e57a5f29f45f2e74e07fd3b72c9e5) | + +All the checks above have performed via [rhub](https://cran.r-project.org/web/packages/rhub/index.html) package. For more details on system characteristics see [rhub's vignettes](https://cran.r-project.org/web/packages/rhub/vignettes/rhub.html). **cran-name** column shows the correspondance to [CRAN package check flavors](https://cran.r-project.org/web/checks/check_flavors.html) + +*The check on `macOS 10.9 Mavericks, R-oldrel (experimental)` fails because the system fails to install `RcppEigen`. You can see our [issue](https://github.com/r-hub/rhub/issues/280) at [rhub's github page](https://github.com/r-hub/rhub). + +** This is expected since our C++ code needs C++11 standard at least while this systems uses gcc4.4.X which does not support C++11 + +*** Checks for compiled code diff --git a/inst/extdata/birk10.ine b/inst/extdata/birk10.ine new file mode 100644 index 00000000..eb07974e --- /dev/null +++ b/inst/extdata/birk10.ine @@ -0,0 +1,106 @@ +birk10.ine +H-representation +begin + 100 82 integer +-8 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 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 -1 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 -1 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 +1 0 -1 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 -1 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 -1 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 +1 0 0 -1 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 -1 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 -1 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 +1 0 0 0 -1 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 -1 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 -1 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 +1 0 0 0 0 -1 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 -1 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 -1 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 -1 0 0 0 0 +1 0 0 0 0 0 -1 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 -1 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 -1 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 -1 0 0 0 +1 0 0 0 0 0 0 -1 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 -1 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 -1 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 -1 0 0 +1 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 -1 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 -1 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 -1 0 +1 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 -1 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 -1 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 -1 +1 -1 -1 -1 -1 -1 -1 -1 -1 -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 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 0 0 0 0 0 0 +1 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -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 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 +1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -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 0 0 0 0 0 0 0 0 0 0 0 0 0 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 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -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 0 0 0 0 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -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 0 0 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 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -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 +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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 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 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 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 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 +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 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 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 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 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 0 0 0 0 0 0 +0 0 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 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 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 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 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 0 0 0 0 +0 0 0 0 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 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 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 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 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 0 0 +0 0 0 0 0 0 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 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 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 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 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 +0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 +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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 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 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 0 0 0 0 0 0 0 0 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 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 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 1 +end +input_incidence diff --git a/inst/extdata/birk11.ine b/inst/extdata/birk11.ine new file mode 100644 index 00000000..78999dee --- /dev/null +++ b/inst/extdata/birk11.ine @@ -0,0 +1,127 @@ +birk11.ine +H-representation +begin + 121 101 integer +-9 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 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 +1 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 +1 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 +1 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 +1 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 +1 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 +1 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 +1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 +1 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 +1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 +1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -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 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +1 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -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 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 0 0 0 0 0 0 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 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -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 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 0 0 0 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 -1 -1 -1 -1 -1 -1 -1 -1 -1 -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 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 +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 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -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 0 0 0 0 0 0 0 0 0 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 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 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -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 0 0 0 0 0 0 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 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 -1 -1 -1 -1 -1 -1 -1 -1 -1 -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 +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 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 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 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 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 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 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 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 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 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 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -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 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 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 +0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 +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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 +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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 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 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 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 0 0 1 +end +input_incidence diff --git a/inst/extdata/birk3.ine b/inst/extdata/birk3.ine new file mode 100644 index 00000000..3a09c642 --- /dev/null +++ b/inst/extdata/birk3.ine @@ -0,0 +1,15 @@ +birk3.ine +H-representation +begin + 9 5 integer + -1 1 1 1 1 + 1 -1 0 -1 0 + 1 0 -1 0 -1 + 1 -1 -1 0 0 + 1 0 0 -1 -1 + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 1 0 + 0 0 0 0 1 +end +input_incidence diff --git a/inst/extdata/birk4.ine b/inst/extdata/birk4.ine new file mode 100644 index 00000000..f7515b94 --- /dev/null +++ b/inst/extdata/birk4.ine @@ -0,0 +1,22 @@ +birk4.ine +H-representation +begin + 16 10 integer +-2 1 1 1 1 1 1 1 1 1 + 1 -1 0 0 -1 0 0 -1 0 0 + 1 0 -1 0 0 -1 0 0 -1 0 + 1 0 0 -1 0 0 -1 0 0 -1 + 1 -1 -1 -1 0 0 0 0 0 0 + 1 0 0 0 -1 -1 -1 0 0 0 + 1 0 0 0 0 0 0 -1 -1 -1 + 0 1 0 0 0 0 0 0 0 0 + 0 0 1 0 0 0 0 0 0 0 + 0 0 0 1 0 0 0 0 0 0 + 0 0 0 0 1 0 0 0 0 0 + 0 0 0 0 0 1 0 0 0 0 + 0 0 0 0 0 0 1 0 0 0 + 0 0 0 0 0 0 0 1 0 0 + 0 0 0 0 0 0 0 0 1 0 + 0 0 0 0 0 0 0 0 0 1 +end +input_incidence diff --git a/inst/extdata/birk5.ine b/inst/extdata/birk5.ine new file mode 100644 index 00000000..7a59b3f1 --- /dev/null +++ b/inst/extdata/birk5.ine @@ -0,0 +1,31 @@ +birk5.ine +H-representation +begin + 25 17 integer +-3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 -1 0 0 0 -1 0 0 0 -1 0 0 0 -1 0 0 0 +1 0 -1 0 0 0 -1 0 0 0 -1 0 0 0 -1 0 0 +1 0 0 -1 0 0 0 -1 0 0 0 -1 0 0 0 -1 0 +1 0 0 0 -1 0 0 0 -1 0 0 0 -1 0 0 0 -1 +1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 +1 0 0 0 0 -1 -1 -1 -1 0 0 0 0 0 0 0 0 +1 0 0 0 0 0 0 0 0 -1 -1 -1 -1 0 0 0 0 +1 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 +0 1 0 0 0 0 0 0 0 0 0 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 0 1 0 0 0 0 0 0 0 0 0 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 0 1 0 0 0 0 0 0 0 0 0 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 0 1 0 0 0 0 0 0 0 0 0 +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 0 1 0 0 0 0 0 0 0 +0 0 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 0 1 0 0 0 0 0 +0 0 0 0 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 0 1 0 0 0 +0 0 0 0 0 0 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 0 1 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 +end +input_incidence diff --git a/inst/extdata/birk6.ine b/inst/extdata/birk6.ine new file mode 100644 index 00000000..01377514 --- /dev/null +++ b/inst/extdata/birk6.ine @@ -0,0 +1,42 @@ +birk6.ine +H-representation +begin + 36 26 integer +-4 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 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 +1 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 +1 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 +1 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 +1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 +1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +1 0 0 0 0 0 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +1 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 0 0 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 -1 -1 0 0 0 0 0 +1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1 +end +input_incidence diff --git a/inst/extdata/birk7.ine b/inst/extdata/birk7.ine new file mode 100644 index 00000000..4d172fe2 --- /dev/null +++ b/inst/extdata/birk7.ine @@ -0,0 +1,55 @@ +birk7.ine +H-representation + 49 37 begin +integer +-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 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 +1 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 +1 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 +1 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 +1 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 +1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 0 0 0 0 0 -1 +1 -1 -1 -1 -1 -1 -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 +1 0 0 0 0 0 0 -1 -1 -1 -1 -1 -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 +1 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 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 0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 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 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 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 -1 -1 -1 -1 -1 -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 0 0 +0 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 0 +0 0 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 +0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 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 0 0 0 0 1 +end +input_incidence diff --git a/inst/extdata/birk8.ine b/inst/extdata/birk8.ine new file mode 100644 index 00000000..7d24e4d5 --- /dev/null +++ b/inst/extdata/birk8.ine @@ -0,0 +1,70 @@ +birk8.ine +H-representation +begin + 64 50 integer +-6 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 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 +1 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 +1 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 +1 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 +1 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 +1 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 +1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 +1 -1 -1 -1 -1 -1 -1 -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 0 0 0 0 0 0 0 0 0 +1 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -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 0 0 +1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -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 +1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 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 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 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 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 +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 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 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 0 0 0 0 0 0 0 +0 0 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 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 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 0 0 0 0 0 +0 0 0 0 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 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 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 0 0 0 +0 0 0 0 0 0 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 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 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 0 +0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 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 0 0 0 0 0 0 0 0 0 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 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 0 1 +end +input_incidence diff --git a/inst/extdata/birk9.ine b/inst/extdata/birk9.ine new file mode 100644 index 00000000..72201c3c --- /dev/null +++ b/inst/extdata/birk9.ine @@ -0,0 +1,87 @@ +birk9.ine +H-representation +begin + 81 65 integer +-7 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 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 +1 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 +1 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 +1 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 +1 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 +1 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 +1 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 +1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 -1 +1 -1 -1 -1 -1 -1 -1 -1 -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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +1 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -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 0 0 0 0 0 0 0 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 -1 -1 -1 -1 -1 -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 0 0 0 0 0 0 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 -1 -1 -1 -1 -1 -1 -1 -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 +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 -1 -1 -1 -1 -1 -1 -1 -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 +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 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 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 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 -1 -1 -1 -1 -1 -1 -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 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 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 +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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1 +end +input_incidence diff --git a/inst/extdata/cross_10.ext b/inst/extdata/cross_10.ext new file mode 100644 index 00000000..c14ee63c --- /dev/null +++ b/inst/extdata/cross_10.ext @@ -0,0 +1,27 @@ +cross_10.ext +V-representation +begin + 20 11 integer + 1 0 0 0 0 0 0 0 0 0 1 + 1 1 0 0 0 0 0 0 0 0 0 + 1 0 1 0 0 0 0 0 0 0 0 + 1 0 0 1 0 0 0 0 0 0 0 + 1 0 0 0 1 0 0 0 0 0 0 + 1 0 0 0 0 1 0 0 0 0 0 + 1 0 0 0 0 0 1 0 0 0 0 + 1 0 0 0 0 0 0 1 0 0 0 + 1 0 0 0 0 0 0 0 1 0 0 + 1 0 0 0 0 0 0 0 0 1 0 + 1 0 0 0 0 0 0 0 0 0 -1 + 1 0 0 0 0 0 0 0 0 -1 0 + 1 0 0 0 0 0 0 0 -1 0 0 + 1 0 0 0 0 0 0 -1 0 0 0 + 1 0 0 0 0 0 -1 0 0 0 0 + 1 0 0 0 0 -1 0 0 0 0 0 + 1 0 0 0 -1 0 0 0 0 0 0 + 1 0 0 -1 0 0 0 0 0 0 0 + 1 0 -1 0 0 0 0 0 0 0 0 + 1 -1 0 0 0 0 0 0 0 0 0 +end +hull +incidence diff --git a/inst/extdata/cross_10.ine b/inst/extdata/cross_10.ine new file mode 100644 index 00000000..6f1b3048 --- /dev/null +++ b/inst/extdata/cross_10.ine @@ -0,0 +1,1030 @@ +cross_10.ine +H-representation +begin + 1024 11 integer + 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 -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 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 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 -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 -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 -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 -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 -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 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 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 + 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 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 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 -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 -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 -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 -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 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 -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 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 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 + 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 -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 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 -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 -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 -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 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 -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 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 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 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 + 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 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 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 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 -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 -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 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 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 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 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 -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 + 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 -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 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 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 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 -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 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 -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 -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 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 -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 + 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 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 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 -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 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 -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 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 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 -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 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 -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 + 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 -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 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 -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 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 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 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 -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 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 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 -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 + 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 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 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 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 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 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 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 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 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 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 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 +end +input_incidence diff --git a/inst/extdata/cube10.ine b/inst/extdata/cube10.ine new file mode 100644 index 00000000..1f035f33 --- /dev/null +++ b/inst/extdata/cube10.ine @@ -0,0 +1,26 @@ +cube10.ine +H-representation +begin + 20 11 real + 1 1 0 0 0 0 0 0 0 0 0 + 1 0 1 0 0 0 0 0 0 0 0 + 1 0 0 1 0 0 0 0 0 0 0 + 1 0 0 0 1 0 0 0 0 0 0 + 1 0 0 0 0 1 0 0 0 0 0 + 1 0 0 0 0 0 1 0 0 0 0 + 1 0 0 0 0 0 0 1 0 0 0 + 1 0 0 0 0 0 0 0 1 0 0 + 1 0 0 0 0 0 0 0 0 1 0 + 1 0 0 0 0 0 0 0 0 0 1 + 1 -1 0 0 0 0 0 0 0 0 0 + 1 0 -1 0 0 0 0 0 0 0 0 + 1 0 0 -1 0 0 0 0 0 0 0 + 1 0 0 0 -1 0 0 0 0 0 0 + 1 0 0 0 0 -1 0 0 0 0 0 + 1 0 0 0 0 0 -1 0 0 0 0 + 1 0 0 0 0 0 0 -1 0 0 0 + 1 0 0 0 0 0 0 0 -1 0 0 + 1 0 0 0 0 0 0 0 0 -1 0 + 1 0 0 0 0 0 0 0 0 0 -1 +end +input_incidence diff --git a/inst/extdata/cube_10.ext b/inst/extdata/cube_10.ext new file mode 100644 index 00000000..ed4dd559 --- /dev/null +++ b/inst/extdata/cube_10.ext @@ -0,0 +1,1031 @@ +cube_10.ext +V-representation +begin +1024 11 integer +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 -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 -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 -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 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 -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 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 -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 -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 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 -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 +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 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 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 -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 -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 -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 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 -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 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 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 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 +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 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 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 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 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 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 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 -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 -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 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 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 +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 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 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 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 -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 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 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 -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 -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 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 -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 +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 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 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 -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 -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 -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 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 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 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 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 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 +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 -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 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 -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 -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 -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 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 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 -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 -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 -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 +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 -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 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 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 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 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 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 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 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 -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 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 +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 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 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 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 -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 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 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 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 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 -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 -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 +end +hull +incidence diff --git a/inst/extdata/linear_extensions/simple.txt b/inst/extdata/linear_extensions/simple.txt new file mode 100644 index 00000000..b50a96df --- /dev/null +++ b/inst/extdata/linear_extensions/simple.txt @@ -0,0 +1,2 @@ +50 20 +[[1,2],[1,3],[1,5],[1,7],[2,3],[2,5],[2,8],[3,4],[3,9],[4,5],[4,7],[4,10],[5,6],[5,8],[6,7],[6,8],[6,9],[7,8],[7,9],[9,10]] diff --git a/inst/extdata/linear_extensions/simple_poset.txt b/inst/extdata/linear_extensions/simple_poset.txt new file mode 100644 index 00000000..8998e738 --- /dev/null +++ b/inst/extdata/linear_extensions/simple_poset.txt @@ -0,0 +1,2 @@ +4 3 +[[1,2],[1,3],[3,4]] diff --git a/inst/extdata/prod_simplex_10_10.ine b/inst/extdata/prod_simplex_10_10.ine new file mode 100644 index 00000000..0eca1b7d --- /dev/null +++ b/inst/extdata/prod_simplex_10_10.ine @@ -0,0 +1,28 @@ +prod_simplex_10.ine +H-representation +begin + 21 21 integer +0 0 0 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 0 0 0 0 0 1 0 0 0 0 0 0 0 0 +0 0 0 0 0 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 0 0 0 0 0 1 0 0 0 0 0 0 +0 0 0 0 0 0 0 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 0 0 0 0 0 1 0 0 0 0 +0 0 0 0 0 0 0 0 0 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 0 0 0 0 0 1 0 0 +0 0 0 0 0 0 0 0 0 0 0 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 0 0 0 0 0 1 +1 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 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 0 0 +0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 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 +0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 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 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 +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 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 +1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 +end +input_incidence diff --git a/inst/extdata/sdpa_n2m3.txt b/inst/extdata/sdpa_n2m3.txt new file mode 100644 index 00000000..0459374c --- /dev/null +++ b/inst/extdata/sdpa_n2m3.txt @@ -0,0 +1,13 @@ +2 +1 +3 +1 1 +-1 0 0 + 0 -2 1 + 0 1 -2 + 1 -0 -0 +-0 -0 -1 +-0 -1 -0 +-0 -0 1 +-0 -0 -0 + 1 -0 -0 diff --git a/inst/extdata/simplex10.ext b/inst/extdata/simplex10.ext new file mode 100644 index 00000000..c4097655 --- /dev/null +++ b/inst/extdata/simplex10.ext @@ -0,0 +1,18 @@ +simplex_10.ext +V-representation +begin + 11 11 integer +1 1 0 0 0 0 0 0 0 0 0 +1 0 1 0 0 0 0 0 0 0 0 +1 0 0 1 0 0 0 0 0 0 0 +1 0 0 0 1 0 0 0 0 0 0 +1 0 0 0 0 1 0 0 0 0 0 +1 0 0 0 0 0 1 0 0 0 0 +1 0 0 0 0 0 0 1 0 0 0 +1 0 0 0 0 0 0 0 1 0 0 +1 0 0 0 0 0 0 0 0 1 0 +1 0 0 0 0 0 0 0 0 0 1 +1 0 0 0 0 0 0 0 0 0 0 +end +hull +incidence diff --git a/inst/extdata/simplex10.ine b/inst/extdata/simplex10.ine new file mode 100644 index 00000000..0e3cb59a --- /dev/null +++ b/inst/extdata/simplex10.ine @@ -0,0 +1,17 @@ +simplex_10.ine +H-representation +begin + 11 11 integer +0 1 0 0 0 0 0 0 0 0 0 +0 0 1 0 0 0 0 0 0 0 0 +0 0 0 1 0 0 0 0 0 0 0 +0 0 0 0 1 0 0 0 0 0 0 +0 0 0 0 0 1 0 0 0 0 0 +0 0 0 0 0 0 1 0 0 0 0 +0 0 0 0 0 0 0 1 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 +0 0 0 0 0 0 0 0 0 1 0 +0 0 0 0 0 0 0 0 0 0 1 +1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +end +input_incidence diff --git a/inst/extdata/skinny_cube10.ine b/inst/extdata/skinny_cube10.ine new file mode 100644 index 00000000..aba4f052 --- /dev/null +++ b/inst/extdata/skinny_cube10.ine @@ -0,0 +1,26 @@ +cube_10.ine +H-representation +begin + 20 11 integer + 100 1 0 0 0 0 0 0 0 0 0 + 1 0 1 0 0 0 0 0 0 0 0 + 1 0 0 1 0 0 0 0 0 0 0 + 1 0 0 0 1 0 0 0 0 0 0 + 1 0 0 0 0 1 0 0 0 0 0 + 1 0 0 0 0 0 1 0 0 0 0 + 1 0 0 0 0 0 0 1 0 0 0 + 1 0 0 0 0 0 0 0 1 0 0 + 1 0 0 0 0 0 0 0 0 1 0 + 1 0 0 0 0 0 0 0 0 0 1 + 100 -1 0 0 0 0 0 0 0 0 0 + 1 0 -1 0 0 0 0 0 0 0 0 + 1 0 0 -1 0 0 0 0 0 0 0 + 1 0 0 0 -1 0 0 0 0 0 0 + 1 0 0 0 0 -1 0 0 0 0 0 + 1 0 0 0 0 0 -1 0 0 0 0 + 1 0 0 0 0 0 0 -1 0 0 0 + 1 0 0 0 0 0 0 0 -1 0 0 + 1 0 0 0 0 0 0 0 0 -1 0 + 1 0 0 0 0 0 0 0 0 0 -1 +end +input_incidence 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/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/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/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/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/compute_indicators.Rd b/man/compute_indicators.Rd new file mode 100644 index 00000000..9bee0ab7 --- /dev/null +++ b/man/compute_indicators.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/compute_indicators.R +\name{compute_indicators} +\alias{compute_indicators} +\title{Compute an indicator for each time period that describes the state of a market.} +\usage{ +compute_indicators( + returns, + 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{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. +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, + parameters = list("win_length" = 10, "m" = 10, "n" = 10000, "nwarning" = 2, "ncrisis" = 3)) + +} +\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.} +} diff --git a/man/copula.Rd b/man/copula.Rd new file mode 100644 index 00000000..be99e489 --- /dev/null +++ b/man/copula.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{copula} +\alias{copula} +\title{Construct a copula using uniform sampling from the unit simplex} +\usage{ +copula(r1, r2 = NULL, sigma = NULL, m = NULL, n = NULL, seed = NULL) +} +\arguments{ +\item{r1}{The \eqn{d}-dimensional normal vector of the first family of parallel hyperplanes.} + +\item{r2}{Optional. The \eqn{d}-dimensional normal vector of the second family of parallel hyperplanes.} + +\item{sigma}{Optional. The \eqn{d\times d} symmetric positive semidefine matrix that describes the family of concentric ellipsoids centered at the origin.} + +\item{m}{The number of the 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{seed}{Optional. A fixed seed for the number generator.} +} +\value{ +A \eqn{m\times m} numerical matrix that corresponds to a copula. +} +\description{ +Given two families of parallel hyperplanes or a family of parallel hyperplanes and a family of concentric ellispoids centered at the origin intersecting the canonical simplex, this function uniformly samples from the canonical simplex and construct an approximation of the bivariate probability distribution, called copula (see \url{https://en.wikipedia.org/wiki/Copula_(probability_theory)}). +At least two families of hyperplanes or one family of hyperplanes and one family of ellipsoids have to be given as input. +} +\examples{ +# compute a copula for two random families of parallel hyperplanes +h1 = runif(n = 10, min = 1, max = 1000) +h1 = h1 / 1000 +h2=runif(n = 10, min = 1, max = 1000) +h2 = h2 / 1000 +cop = copula(r1 = h1, r2 = h2, m = 10, n = 100000) + +# compute a copula for a family of parallel hyperplanes and a family of conentric ellipsoids +h = runif(n = 10, min = 1, max = 1000) +h = h / 1000 +E = replicate(10, rnorm(20)) +E = cov(E) +cop = copula(r1 = h, sigma = E, m = 10, n = 100000) + +} +\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.} +} diff --git a/man/direct_sampling.Rd b/man/direct_sampling.Rd new file mode 100644 index 00000000..a4259531 --- /dev/null +++ b/man/direct_sampling.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{direct_sampling} +\alias{direct_sampling} +\title{Sample perfect uniformly distributed points from well known convex bodies: (a) the unit simplex, (b) the canonical simplex, (c) the boundary of a hypersphere or (d) the interior of a hypersphere.} +\usage{ +direct_sampling(body, n) +} +\arguments{ +\item{body}{A list to request exact uniform sampling from special well known convex bodies through the following input parameters: +\itemize{ +\item{\code{type} }{ A string that declares the type of the body for the exact sampling: a) \code{'unit_simplex'} for the unit simplex, b) \code{'canonical_simplex'} for the canonical simplex, c) \code{'hypersphere'} for the boundary of a hypersphere centered at the origin, d) \code{'ball'} for the interior of a hypersphere centered at the origin.} +\item{\code{dimension} }{ An integer that declares the dimension when exact sampling is enabled for a simplex or a hypersphere.} +\item{\code{radius} }{ The radius of the \eqn{d}-dimensional hypersphere. The default value is \eqn{1}.} +\item{\code{seed} }{ A fixed seed for the number generator.} +}} + +\item{n}{The number of points that the function is going to sample.} +} +\value{ +A \eqn{d\times n} matrix that contains, column-wise, the sampled points from the convex polytope P. +} +\description{ +The \eqn{d}-dimensional unit simplex is the set of points \eqn{\vec{x}\in \R^d}, s.t.: \eqn{\sum_i x_i\leq 1}, \eqn{x_i\geq 0}. The \eqn{d}-dimensional canonical simplex is the set of points \eqn{\vec{x}\in \R^d}, s.t.: \eqn{\sum_i x_i = 1}, \eqn{x_i\geq 0}. +} +\examples{ +# 100 uniform points from the 2-d unit ball +points = direct_sampling(n = 100, body = list("type" = "ball", "dimension" = 2)) +} +\references{ +\cite{R.Y. Rubinstein and B. Melamed, +\dQuote{Modern simulation and modeling} \emph{ Wiley Series in Probability and Statistics,} 1998.} + +\cite{A Smith, Noah and W Tromble, Roy, +\dQuote{Sampling Uniformly from the Unit Simplex,} \emph{ Center for Language and Speech Processing Johns Hopkins University,} 2004.} +} diff --git a/man/exact_vol.Rd b/man/exact_vol.Rd new file mode 100644 index 00000000..c7328e39 --- /dev/null +++ b/man/exact_vol.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{exact_vol} +\alias{exact_vol} +\title{Compute the exact volume of (a) a zonotope (b) an arbitrary simplex in V-representation or (c) if the volume is known and declared by the input object.} +\usage{ +exact_vol(P) +} +\arguments{ +\item{P}{A polytope} +} +\value{ +The exact volume of the input polytope, for zonotopes, simplices in V-representation and polytopes with known exact volume +} +\description{ +Given a zonotope (as an object of class Zonotope), this function computes the sum of the absolute values of the determinants of all the \eqn{d \times d} submatrices of the \eqn{m\times d} matrix \eqn{G} that contains row-wise the \eqn{m} \eqn{d}-dimensional segments that define the zonotope. +For an arbitrary simplex that is given in V-representation this function computes the absolute value of the determinant formed by the simplex's points assuming it is shifted to the origin. +} +\examples{ + +# compute the exact volume of a 5-dimensional zonotope defined by the Minkowski sum of 10 segments +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(V = V) +vol = exact_vol(P) +} + +# compute the exact volume the 10-dimensional cross polytope +P = gen_cross(10,'V') +vol = exact_vol(P) +} +\references{ +\cite{E. Gover and N. Krikorian, +\dQuote{Determinants and the Volumes of Parallelotopes and Zonotopes,} \emph{Linear Algebra and its Applications, 433(1), 28 - 40,} 2010.} +} diff --git a/man/frustum_of_simplex.Rd b/man/frustum_of_simplex.Rd new file mode 100644 index 00000000..bcb075eb --- /dev/null +++ b/man/frustum_of_simplex.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{frustum_of_simplex} +\alias{frustum_of_simplex} +\title{Compute the percentage of the volume of the simplex that is contained in the intersection of a half-space and the simplex.} +\usage{ +frustum_of_simplex(a, z0) +} +\arguments{ +\item{a}{A \eqn{d}-dimensional vector that defines the direction of the hyperplane.} + +\item{z0}{The scalar that defines the half-space.} +} +\value{ +The percentage of the volume of the simplex that is contained in the intersection of a given half-space and the simplex. +} +\description{ +A half-space \eqn{H} is given as a pair of a vector \eqn{a\in R^d} and a scalar \eqn{z0\in R} s.t.: \eqn{a^Tx\leq z0}. This function calls the Ali's version of the Varsi formula to compute a frustum of the simplex. +} +\examples{ +# compute the frustum of H: -x1+x2<=0 +a=c(-1,1) +z0=0 +frustum = frustum_of_simplex(a, z0) +} +\references{ +\cite{Varsi, Giulio, +\dQuote{The multidimensional content of the frustum of the simplex,} \emph{Pacific J. Math. 46, no. 1, 303--314,} 1973.} + +\cite{Ali, Mir M., +\dQuote{Content of the frustum of a simplex,} \emph{ Pacific J. Math. 48, no. 2, 313--322,} 1973.} +} diff --git a/man/gen_cross.Rd b/man/gen_cross.Rd new file mode 100644 index 00000000..ea041442 --- /dev/null +++ b/man/gen_cross.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gen_cross.R +\name{gen_cross} +\alias{gen_cross} +\title{Generator function for cross polytopes} +\usage{ +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. Default valus is 'H'.} +} +\value{ +A polytope class representing a cross polytope in H- or V-representation. +} +\description{ +This function generates the \eqn{d}-dimensional cross polytope in H- or V-representation. +} +\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') +} diff --git a/man/gen_cube.Rd b/man/gen_cube.Rd new file mode 100644 index 00000000..dc97ae5a --- /dev/null +++ b/man/gen_cube.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gen_cube.R +\name{gen_cube} +\alias{gen_cube} +\title{Generator function for hypercubes} +\usage{ +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. Default valus is 'H'.} +} +\value{ +A polytope class representing the unit \eqn{d}-dimensional hypercube in H- or V-representation. +} +\description{ +This function generates the \eqn{d}-dimensional unit hypercube \eqn{[-1,1]^d} in H- or V-representation. +} +\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') +} diff --git a/man/gen_prod_simplex.Rd b/man/gen_prod_simplex.Rd new file mode 100644 index 00000000..108a2dc1 --- /dev/null +++ b/man/gen_prod_simplex.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gen_prod_simplex.R +\name{gen_prod_simplex} +\alias{gen_prod_simplex} +\title{Generator function for product of simplices} +\usage{ +gen_prod_simplex(dimension) +} +\arguments{ +\item{dimension}{The dimension of the simplices.} +} +\value{ +A polytope class representing the product of the two \eqn{d}-dimensional unit simplices in H-representation. +} +\description{ +This function generates a \eqn{2d}-dimensional polytope that is defined as the product of two \eqn{d}-dimensional unit simplices in H-representation. +} +\examples{ +# generate a product of two 5-dimensional simplices. +P = gen_prod_simplex(5) +} diff --git a/man/gen_rand_hpoly.Rd b/man/gen_rand_hpoly.Rd new file mode 100644 index 00000000..85dd1757 --- /dev/null +++ b/man/gen_rand_hpoly.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gen_rand_hpoly.R +\name{gen_rand_hpoly} +\alias{gen_rand_hpoly} +\title{Generator function for random H-polytopes} +\usage{ +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{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. +} +\description{ +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. +} +\examples{ +# generate a 10-dimensional polytope with 50 facets +P = gen_rand_hpoly(10, 50) +} diff --git a/man/gen_rand_vpoly.Rd b/man/gen_rand_vpoly.Rd new file mode 100644 index 00000000..e34125a5 --- /dev/null +++ b/man/gen_rand_vpoly.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gen_rand_vpoly.R +\name{gen_rand_vpoly} +\alias{gen_rand_vpoly} +\title{Generator function for random V-polytopes} +\usage{ +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}{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. +} +\description{ +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. +} +\examples{ +# generate a 10-dimensional polytope defined as the convex hull of 25 random vertices +P = gen_rand_vpoly(10, 25) +} diff --git a/man/gen_rand_zonotope.Rd b/man/gen_rand_zonotope.Rd new file mode 100644 index 00000000..41497ef2 --- /dev/null +++ b/man/gen_rand_zonotope.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gen_rand_zonotope.R +\name{gen_rand_zonotope} +\alias{gen_rand_zonotope} +\title{Generator function for zonotopes} +\usage{ +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}{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. +} +\description{ +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]}. +} +\examples{ +# generate a 10-dimensional zonotope defined by the Minkowski sum of 20 segments +P = gen_rand_zonotope(10, 20) +} diff --git a/man/gen_simplex.Rd b/man/gen_simplex.Rd new file mode 100644 index 00000000..0bbcb155 --- /dev/null +++ b/man/gen_simplex.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gen_simplex.R +\name{gen_simplex} +\alias{gen_simplex} +\title{Generator function for simplices} +\usage{ +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. Default valus is 'H'.} +} +\value{ +A polytope class representing the \eqn{d}-dimensional unit simplex in H- or V-representation. +} +\description{ +This function generates 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') +} diff --git a/man/gen_skinny_cube.Rd b/man/gen_skinny_cube.Rd new file mode 100644 index 00000000..916f5e20 --- /dev/null +++ b/man/gen_skinny_cube.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gen_skinny_cube.R +\name{gen_skinny_cube} +\alias{gen_skinny_cube} +\title{Generator function for skinny hypercubes} +\usage{ +gen_skinny_cube(dimension) +} +\arguments{ +\item{dimension}{The dimension of the skinny hypercube.} +} +\value{ +A polytope class representing the \eqn{d}-dimensional skinny hypercube in H-representation. +} +\description{ +This function generates a \eqn{d}-dimensional skinny hypercube \eqn{[-1,1]^{d-1}\times [-100,100]}. +} +\examples{ +# generate a 10-dimensional skinny hypercube. +P = gen_skinny_cube(10) +} diff --git a/man/inner_ball.Rd b/man/inner_ball.Rd new file mode 100644 index 00000000..9b0c21e1 --- /dev/null +++ b/man/inner_ball.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{inner_ball} +\alias{inner_ball} +\title{Compute an inscribed ball of a convex polytope} +\usage{ +inner_ball(P) +} +\arguments{ +\item{P}{A convex polytope. It is an object from class (a) Hpolytope or (b) Vpolytope or (c) Zonotope or (d) VpolytopeIntersection.} +} +\value{ +A \eqn{(d+1)}-dimensional vector that describes the inscribed ball. The first \eqn{d} coordinates corresponds to the center of the ball and the last one to the radius. +} +\description{ +For a H-polytope described by a \eqn{m\times d} matrix \eqn{A} and a \eqn{m}-dimensional vector \eqn{b}, s.t.: \eqn{P=\{x\ |\ Ax\leq b\} }, this function computes the largest inscribed ball (Chebychev ball) by solving the corresponding linear program. +For both zonotopes and V-polytopes the function computes the minimum \eqn{r} s.t.: \eqn{ r e_i \in P} for all \eqn{i=1, \dots ,d}. Then the ball centered at the origin with radius \eqn{r/ \sqrt{d}} is an inscribed ball. +} +\examples{ +# compute the Chebychev ball of the 2d unit simplex +P = gen_simplex(2,'H') +ball_vec = inner_ball(P) + +# compute an inscribed ball of the 3-dimensional unit cube in V-representation +P = gen_cube(3, 'V') +ball_vec = inner_ball(P) +} 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/poly_gen.Rd b/man/poly_gen.Rd new file mode 100644 index 00000000..be2ed4dd --- /dev/null +++ b/man/poly_gen.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{poly_gen} +\alias{poly_gen} +\title{An internal Rccp function as a polytope generator} +\usage{ +poly_gen(kind_gen, Vpoly_gen, Zono_gen, dim_gen, m_gen, seed = NULL) +} +\arguments{ +\item{kind_gen}{An integer to declare the type of the polytope.} + +\item{Vpoly_gen}{A boolean parameter to declare if the requested polytope has to be in V-representation.} + +\item{Zono_gen}{A boolean parameter to declare if the requested polytope has to be a zonotope.} + +\item{dim_gen}{An integer to declare the dimension of the requested polytope.} + +\item{m_gen}{An integer to declare the number of generators for the requested random zonotope or the number of vertices for a V-polytope.} + +\item{seed}{Optional. A fixed seed for the random polytope generator.} +} +\value{ +A numerical matrix describing the requested polytope +} +\description{ +An internal Rccp function as a polytope generator +} +\keyword{internal} diff --git a/man/read_sdpa_format_file.Rd b/man/read_sdpa_format_file.Rd new file mode 100644 index 00000000..50ca8ae2 --- /dev/null +++ b/man/read_sdpa_format_file.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/read_sdpa_file.R +\name{read_sdpa_format_file} +\alias{read_sdpa_format_file} +\title{Read a SDPA format file} +\usage{ +read_sdpa_format_file(path) +} +\arguments{ +\item{path}{Name of the input file} +} +\value{ +A list with two named items: an item "matrices" which is an object of class Spectrahedron and an vector "objFunction" +} +\description{ +Read a SDPA format file and return a spectrahedron (an object of class Spectrahedron) which is defined by +the linear matrix inequality in the input file, and the objective function. +} +\examples{ +path = system.file('extdata', package = 'volesti') +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 new file mode 100644 index 00000000..983a2911 --- /dev/null +++ b/man/rotate_polytope.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rotate_polytope.R +\name{rotate_polytope} +\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, 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{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. +} +\description{ +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. +\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}.} +\item{If \eqn{P} is a zonotope and \eqn{G} is the matrix that contains column-wise the generators of \eqn{Q} then \eqn{T^TG} contains the generators of \eqn{P}.} +\item{If \eqn{M} is a matrix that contains column-wise points in \eqn{Q} then \eqn{T^TM} contains points in \eqn{P}.} +} +} +\examples{ +# rotate a H-polytope (2d unit simplex) +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) +poly_matrix_list = rotate_polytope(Z) +} diff --git a/man/rotating.Rd b/man/rotating.Rd new file mode 100644 index 00000000..4b091f81 --- /dev/null +++ b/man/rotating.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{rotating} +\alias{rotating} +\title{An internal Rccp function for the random rotation of a convex polytope} +\usage{ +rotating(P, T = NULL, seed = NULL) +} +\arguments{ +\item{P}{A convex polytope (H-, V-polytope or a zonotope).} + +\item{T}{Optional. A rotation matrix.} + +\item{seed}{Optional. A fixed seed for the random linear map generator.} +} +\value{ +A matrix that describes the rotated polytope +} +\description{ +An internal Rccp function for the random rotation of a convex polytope +} +\keyword{internal} diff --git a/man/round_polytope.Rd b/man/round_polytope.Rd new file mode 100644 index 00000000..1e274882 --- /dev/null +++ b/man/round_polytope.Rd @@ -0,0 +1,46 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/round_polytope.R +\name{round_polytope} +\alias{round_polytope} +\title{Apply rounding to a convex polytope (H-polytope, V-polytope or a zonotope)} +\usage{ +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{settings}{Optional. A list to parameterize the method by the random walk. +\itemize{ +\item{\code{random_walk} }{ The random walk to sample uniformly distributed points: (a) \code{'CDHR'} for Coordinate Directions Hit-and-Run, (b) \code{'RDHR'} for Random Directions Hit-and-Run or (c) \code{'BiW'} for Billiard walk. The default random walk is \code{'CDHR'} for H-polytopes and \code{'BiW'} for the rest of the representations.} +\item{\code{walk_length} }{ The walk length of the random walk. The default value is \eqn{10 + 10d} for \code{'CDHR'} or \code{'RDHR'} and 2 for \code{'BiW'}.} +\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. +} +\description{ +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. +} +\examples{ +# rotate a H-polytope (2d unit simplex) +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) +listHpoly = round_polytope(P) + +# rotate a V-polytope (3d unit cube) using Random Directions HnR with step equal to 50 +P = gen_cube(3, 'V') +ListVpoly = round_polytope(P) + +# round a 2-dimensional zonotope defined by 6 generators using ball walk +Z = gen_rand_zonotope(2,6) +ListZono = round_polytope(Z) +} +\references{ +\cite{I.Z.Emiris and V. Fisikopoulos, +\dQuote{Practical polytope volume approximation,} \emph{ACM Trans. Math. Soft.,} 2018.}, + +\cite{Michael J. Todd and E. Alper Yildirim, +\dQuote{On Khachiyan’s Algorithm for the Computation of Minimum Volume Enclosing Ellipsoids,} \emph{Discrete Applied Mathematics,} 2007.} +} diff --git a/man/rounding.Rd b/man/rounding.Rd new file mode 100644 index 00000000..e8b92f91 --- /dev/null +++ b/man/rounding.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{rounding} +\alias{rounding} +\title{Internal rcpp function for the rounding of a convex polytope} +\usage{ +rounding(P, settings = NULL, seed = NULL) +} +\arguments{ +\item{P}{A convex polytope (H- or V-representation or zonotope).} + +\item{settings}{A list to set the random walk and its walk length} + +\item{seed}{Optional. A fixed seed for the number generator.} +} +\value{ +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. +} +\description{ +Internal rcpp function for the rounding of a convex polytope +} +\keyword{internal} diff --git a/man/sample_points.Rd b/man/sample_points.Rd new file mode 100644 index 00000000..890d2730 --- /dev/null +++ b/man/sample_points.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{sample_points} +\alias{sample_points} +\title{Sample uniformly or normally distributed points from a convex Polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes).} +\usage{ +sample_points(P, n, random_walk = NULL, distribution = NULL) +} +\arguments{ +\item{P}{A convex polytope. It is an object from class (a) Hpolytope or (b) Vpolytope or (c) Zonotope or (d) VpolytopeIntersection.} + +\item{n}{The number of points that the function is going to sample from the convex polytope.} + +\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{'BCDHR'} boundary sampling by keeping the extreme points of CDHR or vi) \code{'BRDHR'} boundary sampling by keeping the extreme points of RDHR. The default walk is \code{'BiW'} for the uniform distribution or \code{'CDHR'} for the Gaussian distribution.} +\item{\code{walk_length} }{ The number of the steps per generated point for the random walk. The default value is 1.} +\item{\code{nburns} }{ The number of points to burn before start sampling.} +\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.} +\item{\code{seed} }{ A fixed seed for the number generator.} +}} + +\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.} +\item{\code{variance} }{ The variance of the multidimensional spherical gaussian. 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()}.} +}} +} +\value{ +A \eqn{d\times n} matrix that contains, column-wise, the sampled points from the convex polytope P. +} +\description{ +Sample n points with uniform or multidimensional spherical gaussian -with a mode at any point- as the target distribution. +} +\examples{ +# uniform distribution from the 3d unit cube in H-representation using ball walk +P = gen_cube(3, 'H') +points = sample_points(P, n = 100, random_walk = list("walk" = "BaW", "walk_length" = 5)) + +# 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(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 +P = gen_rand_hpoly(2,20) +points = sample_points(P, n = 100, random_walk = list("walk" = "BRDHR")) + +} diff --git a/man/volume.Rd b/man/volume.Rd new file mode 100644 index 00000000..d1db8951 --- /dev/null +++ b/man/volume.Rd @@ -0,0 +1,54 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{volume} +\alias{volume} +\title{The main function for volume approximation of a convex Polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes)} +\usage{ +volume(P, settings = NULL, rounding = FALSE) +} +\arguments{ +\item{P}{A convex polytope. It is an object from class a) Hpolytope or b) Vpolytope or c) Zonotope or d) VpolytopeIntersection.} + +\item{settings}{Optional. A list that declares which algorithm, random walk and values of parameters to use, as follows: +\itemize{ +\item{\code{algorithm} }{ A string to set the algorithm to use: a) \code{'CB'} for CB algorithm, b) \code{'SoB'} for SOB algorithm or b) \code{'CG'} for CG algorithm. The defalut algorithm is \code{'CB'}.} +\item{\code{error} }{ A numeric value to set the upper bound for the approximation error. The default value is \eqn{1} for SOB algorithm and \eqn{0.1} otherwise.} +\item{\code{random_walk} }{ A string that declares the random walk method: a) \code{'CDHR'} for Coordinate Directions Hit-and-Run, b) \code{'RDHR'} for Random Directions Hit-and-Run, c) \code{'BaW'} for Ball Walk, or \code{'BiW'} for Billiard walk. For CB and SOB algorithms the default walk is \code{'CDHR'} for H-polytopes and \code{'BiW'} for the other representations. For CG algorithm the default walk is \code{'CDHR'} for H-polytopes and \code{'RDHR'} for the other representations.} +\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{400+3d^2} for CB or \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}{A boolean parameter for rounding. The default value is \code{FALSE}.} +} +\value{ +The approximation of the volume of a convex polytope. +} +\description{ +For the volume approximation can be used three algorithms. Either CoolingBodies (CB) or SequenceOfBalls (SOB) or CoolingGaussian (CG). An H-polytope with \eqn{m} facets is described by a \eqn{m\times d} matrix \eqn{A} and a \eqn{m}-dimensional vector \eqn{b}, s.t.: \eqn{P=\{x\ |\ Ax\leq b\} }. A V-polytope is defined as the convex hull of \eqn{m} \eqn{d}-dimensional points which correspond to the vertices of P. A zonotope is desrcibed by the Minkowski sum of \eqn{m} \eqn{d}-dimensional segments. +} +\examples{ + +# calling SOB algorithm for a H-polytope (3d unit simplex) +HP = gen_cube(3,'H') +vol = volume(HP) + +# calling CG algorithm for a V-polytope (2d simplex) +VP = gen_simplex(2,'V') +vol = volume(VP, settings = list("algorithm" = "CG")) + +# calling CG algorithm for a 2-dimensional zonotope defined as the Minkowski sum of 4 segments +Z = gen_rand_zonotope(2, 4) +vol = volume(Z, settings = list("random_walk" = "RDHR", "walk_length" = 2)) + +} +\references{ +\cite{I.Z.Emiris and V. Fisikopoulos, +\dQuote{Practical polytope volume approximation,} \emph{ACM Trans. Math. Soft.,} 2018.}, + +\cite{A. Chalkis and I.Z.Emiris and V. Fisikopoulos, +\dQuote{Practical Volume Estimation by a New Annealing Schedule for Cooling Convex Bodies,} \emph{CoRR, abs/1905.05494,} 2019.}, + +\cite{B. Cousins and S. Vempala, \dQuote{A practical volume algorithm,} \emph{Springer-Verlag Berlin Heidelberg and The Mathematical Programming Society,} 2015.} +} 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/zono_approx.Rd b/man/zono_approx.Rd new file mode 100644 index 00000000..9c27c369 --- /dev/null +++ b/man/zono_approx.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{zono_approx} +\alias{zono_approx} +\title{An internal Rccp function for the over-approximation of a zonotope} +\usage{ +zono_approx(Z, fit_ratio = NULL, settings = NULL, seed = NULL) +} +\arguments{ +\item{Z}{A zonotope.} + +\item{fit_ratio}{Optional. A boolean parameter to request the computation of the ratio of fitness.} + +\item{settings}{Optional. A list that declares the values of the parameters of CB algorithm.} + +\item{seed}{Optional. A fixed seed for the number generator.} +} +\value{ +A List that contains a numerical matrix that describes the PCA approximation as a H-polytope and the ratio of fitness. +} +\description{ +An internal Rccp function for the over-approximation of a zonotope +} +\keyword{internal} diff --git a/man/zonotope_approximation.Rd b/man/zonotope_approximation.Rd new file mode 100644 index 00000000..25510a0c --- /dev/null +++ b/man/zonotope_approximation.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/zonotope_approximation.R +\name{zonotope_approximation} +\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 = FALSE, + settings = list(error = 0.1, walk_length = 1, win_len = 250, hpoly = FALSE) +) +} +\arguments{ +\item{Z}{A zonotope.} + +\item{fit_ratio}{Optional. A boolean parameter to request the computation of the ratio of fitness.} + +\item{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{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.} +}} +} +\value{ +A list that contains the approximation body in H-representation and the ratio of fitness +} +\description{ +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. +} +\examples{ +# over-approximate a 2-dimensional zonotope with 10 generators and compute the ratio of fitness +Z = gen_rand_zonotope(2, 8) +retList = zonotope_approximation(Z = Z) + +} +\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.} +} diff --git a/src/Makevars b/src/Makevars new file mode 100644 index 00000000..07c17c90 --- /dev/null +++ b/src/Makevars @@ -0,0 +1,11 @@ +PKG_CPPFLAGS=-Iexternal -Iexternal/lpsolve/headers/run_headers -Iexternal/minimum_ellipsoid -Iinclude -Iinclude/convex_bodies/spectrahedra +PKG_CXXFLAGS= -DBOOST_NO_AUTO_PTR + +PKG_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) + +$(SHLIB): external/lpsolve/build/lp_solve/liblp_solve.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)") diff --git a/src/Makevars.win b/src/Makevars.win new file mode 100644 index 00000000..29935f2a --- /dev/null +++ b/src/Makevars.win @@ -0,0 +1,12 @@ +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_LIBS=-Lexternal/lpsolve/build/lp_solve -llp_solve $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) + +$(SHLIB): external/lpsolve/build/lp_solve/liblp_solve.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)") diff --git a/src/copula.cpp b/src/copula.cpp new file mode 100644 index 00000000..065815c3 --- /dev/null +++ b/src/copula.cpp @@ -0,0 +1,113 @@ +// [[Rcpp::depends(BH)]] + +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +#include +#include +#include +#include "convex_bodies/ellipsoids.h" +#include "cartesian_geom/cartesian_kernel.h" +#include +#include +#include "sampling/simplex.hpp" +#include "volume/copulas.h" + +//' Construct a copula using uniform sampling from the unit simplex +//' +//' Given two families of parallel hyperplanes or a family of parallel hyperplanes and a family of concentric ellispoids centered at the origin intersecting the canonical simplex, this function uniformly samples from the canonical simplex and construct an approximation of the bivariate probability distribution, called copula (see \url{https://en.wikipedia.org/wiki/Copula_(probability_theory)}). +//' At least two families of hyperplanes or one family of hyperplanes and one family of ellipsoids have to be given as input. +//' +//' @param r1 The \eqn{d}-dimensional normal vector of the first family of parallel hyperplanes. +//' @param r2 Optional. The \eqn{d}-dimensional normal vector of the second family of parallel hyperplanes. +//' @param sigma Optional. The \eqn{d\times d} symmetric positive semidefine matrix that describes the family of concentric ellipsoids centered at the origin. +//' @param m The number of the slices for the copula. The default value is 100. +//' @param n The number of points to sample. The default value is \eqn{5\cdot 10^5}. +//' @param seed Optional. 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 \eqn{m\times m} numerical matrix that corresponds to a copula. +//' @examples +//' # compute a copula for two random families of parallel hyperplanes +//' h1 = runif(n = 10, min = 1, max = 1000) +//' h1 = h1 / 1000 +//' h2=runif(n = 10, min = 1, max = 1000) +//' h2 = h2 / 1000 +//' cop = copula(r1 = h1, r2 = h2, m = 10, n = 100000) +//' +//' # compute a copula for a family of parallel hyperplanes and a family of conentric ellipsoids +//' h = runif(n = 10, min = 1, max = 1000) +//' h = h / 1000 +//' E = replicate(10, rnorm(20)) +//' E = cov(E) +//' cop = copula(r1 = h, sigma = E, m = 10, n = 100000) +//' +//' @export +// [[Rcpp::export]] +Rcpp::NumericMatrix copula (Rcpp::Nullable r1, + Rcpp::Nullable r2 = R_NilValue, + Rcpp::Nullable sigma = R_NilValue, + Rcpp::Nullable m = R_NilValue, + Rcpp::Nullable n = R_NilValue, + Rcpp::Nullable seed = R_NilValue){ + + typedef double NT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef boost::mt19937 RNGType; + typedef Eigen::Matrix MT; + typedef Eigen::Matrix VT; + typedef copula_ellipsoid CopEll; + unsigned int num_slices = 100, numpoints = 500000; + + if (m.isNotNull()) { + num_slices = Rcpp::as(m); + } + + if (n.isNotNull()) { + numpoints = Rcpp::as(n); + } + + double seed3 = (!seed.isNotNull()) ? std::numeric_limits::signaling_NaN() : Rcpp::as(seed); + + Rcpp::NumericMatrix copula(num_slices, num_slices); + std::vector > StdCopula; + unsigned int dim = Rcpp::as >(r1).size(), i, j; + + std::vector hyp1 = Rcpp::as >(r1); + if (r2.isNotNull()) { + + std::vector hyp2 = Rcpp::as < std::vector < NT > > (r2); + StdCopula = twoParHypFam(dim, numpoints, num_slices, hyp1, hyp2, seed3); + + } else if (sigma.isNotNull()) { + + std::vector > Gin(dim, std::vector(dim)); + MT EE = Rcpp::as(sigma); + for (int i=0; i(dim, numpoints, num_slices, hyp1, Ell, seed3); + } else { + + throw Rcpp::exception("You have to give as input either two families of hyperplanes or one family of hyperplanes and one family of ellipsoids!"); + + } + + for(int i=0; i +#include +#include +#include "cartesian_geom/cartesian_kernel.h" +#include +#include +#include +#include +#include "volume/volume_sequence_of_balls.hpp" +#include "sampling/simplex.hpp" + + +//' Sample perfect uniformly distributed points from well known convex bodies: (a) the unit simplex, (b) the canonical simplex, (c) the boundary of a hypersphere or (d) the interior of a hypersphere. +//' +//' The \eqn{d}-dimensional unit simplex is the set of points \eqn{\vec{x}\in \R^d}, s.t.: \eqn{\sum_i x_i\leq 1}, \eqn{x_i\geq 0}. The \eqn{d}-dimensional canonical simplex is the set of points \eqn{\vec{x}\in \R^d}, s.t.: \eqn{\sum_i x_i = 1}, \eqn{x_i\geq 0}. +//' +//' @param body A list to request exact uniform sampling from special well known convex bodies through the following input parameters: +//' \itemize{ +//' \item{\code{type} }{ A string that declares the type of the body for the exact sampling: a) \code{'unit_simplex'} for the unit simplex, b) \code{'canonical_simplex'} for the canonical simplex, c) \code{'hypersphere'} for the boundary of a hypersphere centered at the origin, d) \code{'ball'} for the interior of a hypersphere centered at the origin.} +//' \item{\code{dimension} }{ An integer that declares the dimension when exact sampling is enabled for a simplex or a hypersphere.} +//' \item{\code{radius} }{ The radius of the \eqn{d}-dimensional hypersphere. The default value is \eqn{1}.} +//' \item{\code{seed} }{ A fixed seed for the number generator.} +//' } +//' @param n The number of points that the function is going to sample. +//' +//' @references \cite{R.Y. Rubinstein and B. Melamed, +//' \dQuote{Modern simulation and modeling} \emph{ Wiley Series in Probability and Statistics,} 1998.} +//' @references \cite{A Smith, Noah and W Tromble, Roy, +//' \dQuote{Sampling Uniformly from the Unit Simplex,} \emph{ Center for Language and Speech Processing Johns Hopkins University,} 2004.} +//' +//' @return A \eqn{d\times n} matrix that contains, column-wise, the sampled points from the convex polytope P. +//' @examples +//' # 100 uniform points from the 2-d unit ball +//' points = direct_sampling(n = 100, body = list("type" = "ball", "dimension" = 2)) +//' @export +// [[Rcpp::export]] +Rcpp::NumericMatrix direct_sampling(Rcpp::List body, int n) { + + typedef double NT; + typedef Cartesian Kernel; + typedef boost::mt19937 RNGType2; + typedef typename Kernel::Point Point; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + typedef BoostRandomNumberGenerator RNGType; + + int dim, numpoints; + NT radius = 1.0; + std::list randPoints; + + if (!body.containsElementNamed("dimension")) { + throw Rcpp::exception("Dimension has to be given as input!"); + } + dim = Rcpp::as(body["dimension"]); + if (dim <=1) throw Rcpp::exception("Dimension has to be larger than 1!"); + + RNGType rng(dim); + if (body.containsElementNamed("seed")) { + unsigned seed2 = Rcpp::as(body["seed"]); + rng.set_seed(seed2); + } + double seed3 = (!body.containsElementNamed("seed")) ? std::numeric_limits::signaling_NaN() : Rcpp::as(body["seed"]); + + numpoints = n; + if (numpoints <= 0) throw Rcpp::exception("The number of samples has to be a positice integer!"); + + if (body.containsElementNamed("radius")) { + + radius = Rcpp::as(body["radius"]); + if (radius <= NT(0)) throw Rcpp::exception("Radius has to be a positive number!"); + + } + if (!body.containsElementNamed("type")) { + + throw Rcpp::exception("The kind of body has to be given as input!"); + + } + if (Rcpp::as(body["type"]).compare(std::string("hypersphere"))==0) { + + for (unsigned int k = 0; k < numpoints; ++k) { + randPoints.push_back(GetPointOnDsphere::apply(dim, radius, rng)); + } + + } else if (Rcpp::as(body["type"]).compare(std::string("ball"))==0) { + + for (unsigned int k = 0; k < numpoints; ++k) { + randPoints.push_back(GetPointInDsphere::apply(dim, radius, rng)); + } + + } else if (Rcpp::as(body["type"]).compare(std::string("unit_simplex"))==0) { + + Sam_Unit(dim, numpoints, randPoints, seed3); + + } else if (Rcpp::as(body["type"]).compare(std::string("canonical_simplex"))==0) { + + Sam_Canon_Unit(dim, numpoints, randPoints, seed3); + + } else { + + throw Rcpp::exception("Wrong input!"); + + } + + MT RetMat(dim, numpoints); + unsigned int jj = 0; + + for (typename std::list::iterator rpit = randPoints.begin(); rpit!=randPoints.end(); rpit++, jj++) { + RetMat.col(jj) = rpit->getCoefficients(); + } + return Rcpp::wrap(RetMat); +} diff --git a/src/exact_vol.cpp b/src/exact_vol.cpp new file mode 100644 index 00000000..fdce37ac --- /dev/null +++ b/src/exact_vol.cpp @@ -0,0 +1,119 @@ +// [[Rcpp::depends(BH)]] + +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2019 Vissarion Fisikopoulos +// Copyright (c) 2018-2019 Apostolos Chalkis + +#include +#include +#include +#include +#include +#include +#include +#include "cartesian_geom/cartesian_kernel.h" +#include "convex_bodies/hpolytope.h" +#include "convex_bodies/vpolytope.h" +#include "convex_bodies/zpolytope.h" +#include "volume/exact_vols.h" + +template +FT factorial(FT n) +{ + return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n; +} + +//' Compute the exact volume of (a) a zonotope (b) an arbitrary simplex in V-representation or (c) if the volume is known and declared by the input object. +//' +//' Given a zonotope (as an object of class Zonotope), this function computes the sum of the absolute values of the determinants of all the \eqn{d \times d} submatrices of the \eqn{m\times d} matrix \eqn{G} that contains row-wise the \eqn{m} \eqn{d}-dimensional segments that define the zonotope. +//' For an arbitrary simplex that is given in V-representation this function computes the absolute value of the determinant formed by the simplex's points assuming it is shifted to the origin. +//' +//' @param P A polytope +//' +//' @references \cite{E. Gover and N. Krikorian, +//' \dQuote{Determinants and the Volumes of Parallelotopes and Zonotopes,} \emph{Linear Algebra and its Applications, 433(1), 28 - 40,} 2010.} +//' +//' @return The exact volume of the input polytope, for zonotopes, simplices in V-representation and polytopes with known exact volume +//' @examples +//' +//' # compute the exact volume of a 5-dimensional zonotope defined by the Minkowski sum of 10 segments +//' 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(V = V) +//' vol = exact_vol(P) +//' } +//' +//' # compute the exact volume the 10-dimensional cross polytope +//' P = gen_cross(10,'V') +//' vol = exact_vol(P) +//' @export +// [[Rcpp::export]] +double exact_vol(Rcpp::Reference P) { + + typedef double NT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + + if (NT(P.slot("volume")) > 0.0) { + return NT(P.slot("volume")); + } + + int type_num, dim; + std::string type = Rcpp::as(P.slot("type")); + + if (type.compare(std::string("Hpolytope")) == 0) { + dim = Rcpp::as(P.slot("A")).cols(); + type_num = 1; + } else if (type.compare(std::string("Vpolytope")) == 0) { + dim = Rcpp::as(P.slot("V")).cols(); + type_num = 2; + } else if (type.compare(std::string("Zonotope")) == 0) { + dim = Rcpp::as(P.slot("G")).cols(); + type_num = 3; + } else if (type.compare(std::string("VpolytopeIntersection")) == 0) { + dim = Rcpp::as(P.slot("V1")).cols(); + type_num = 4; + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } + + NT vol; + + if (type_num == 2) { + + if (Rcpp::as(P.slot("V")).rows() == + Rcpp::as(P.slot("V")).cols() + 1) { + + MT V = Rcpp::as(P.slot("V")).transpose(), V2(dim,dim); + VT v0 = V.col(dim); + + for (int i = 0; i < dim; ++i) { + V2.col(i) = V.col(i) - v0; + } + vol = std::abs(V2.determinant()) / factorial(NT(dim)); + + } else { + throw Rcpp::exception("Volume unknown!"); + } + + } else if (type_num == 3) { + + typedef Zonotope zonotope; + zonotope ZP; + + ZP.init(dim, Rcpp::as(P.slot("G")), + VT::Ones(Rcpp::as(P.slot("G")).rows())); + vol = exact_zonotope_vol(ZP); + + } else { + throw Rcpp::exception("Volume unknown!"); + } + + return vol; +} diff --git a/src/external/lpsolve/build/lp_solve/Makefile b/src/external/lpsolve/build/lp_solve/Makefile new file mode 100644 index 00000000..2b3931c2 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/Makefile @@ -0,0 +1,25 @@ +LP_SOLVE_CPPFLAGS=$(CPPFLAGS) -I../../headers/include \ + -I$(R_INCLUDE_DIR) \ + -DYY_NEVER_INTERACTIVE \ + -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_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 \ + lp_wlp.c lp_LUSOL.c lp_matrix.c lp_report.c lusol.c \ + myblas.c + +LP_SOLVE_OBJECTS=$(LP_SOLVE_SOURCES:.c=.o) + +liblp_solve.a: $(LP_SOLVE_OBJECTS) + $(AR) rc liblp_solve.a $(LP_SOLVE_OBJECTS) && $(RANLIB) liblp_solve.a + +.c.o: + $(CC) $(CFLAGS) $(CPICFLAGS) $(LP_SOLVE_CPPFLAGS) -c $< -o $@ + +clean: + rm -rf $(LP_SOLVE_OBJECTS) liblp_solve.a + + diff --git a/src/external/lpsolve/build/lp_solve/colamd.c b/src/external/lpsolve/build/lp_solve/colamd.c new file mode 100644 index 00000000..f48c6f56 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/colamd.c @@ -0,0 +1,3469 @@ +/* ========================================================================== */ +/* === colamd/symamd - a sparse matrix column ordering algorithm ============ */ +/* ========================================================================== */ + +/* + colamd: an approximate minimum degree column ordering algorithm, + for LU factorization of symmetric or unsymmetric matrices, + QR factorization, least squares, interior point methods for + linear programming problems, and other related problems. + + symamd: an approximate minimum degree ordering algorithm for Cholesky + factorization of symmetric matrices. + + Purpose: + + Colamd computes a permutation Q such that the Cholesky factorization of + (AQ)'(AQ) has less fill-in and requires fewer floating point operations + than A'A. This also provides a good ordering for sparse partial + pivoting methods, P(AQ) = LU, where Q is computed prior to numerical + factorization, and P is computed during numerical factorization via + conventional partial pivoting with row interchanges. Colamd is the + column ordering method used in SuperLU, part of the ScaLAPACK library. + It is also available as built-in function in Matlab Version 6, + available from MathWorks, Inc. (http://www.mathworks.com). This + routine can be used in place of colmmd in Matlab. By default, the \ + and / operators in Matlab perform a column ordering (using colmmd + or colamd) prior to LU factorization using sparse partial pivoting, + in the built-in Matlab lu(A) routine. + + Symamd computes a permutation P of a symmetric matrix A such that the + Cholesky factorization of PAP' has less fill-in and requires fewer + floating point operations than A. Symamd constructs a matrix M such + that M'M has the same nonzero pattern of A, and then orders the columns + of M using colmmd. The column ordering of M is then returned as the + row and column ordering P of A. + + 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.c + file. It requires the colamd.h file. It is required by the colamdmex.c + and symamdmex.c files, for the Matlab interface to colamd and symamd. + + Changes to the colamd library since Version 1.0 and 1.1: + + No bugs were found in version 1.1. These changes merely add new + functionality. + + * added the COLAMD_RECOMMENDED (nnz, n_row, n_col) macro. + + * moved the output statistics, from A, to a separate output argument. + The arguments changed for the C-callable routines. + + * added colamd_report and symamd_report. + + * added a C-callable symamd routine. Formerly, symamd was only + available as a mexFunction from Matlab. + + * added error-checking to symamd. Formerly, it assumed its input + was error-free. + + * added the optional stats and knobs arguments to the symamd mexFunction + + * deleted colamd_help. A help message is still available from + "help colamd" and "help symamd" in Matlab. + + * deleted colamdtree.m and symamdtree.m. Now, colamd.m and symamd.m + also do the elimination tree post-ordering. The Version 1.1 + colamd and symamd mexFunctions, which do not do the post- + ordering, are now visible as colamdmex and symamdmex from + Matlab. Essentialy, the post-ordering is now the default + behavior of colamd.m and symamd.m, to match the behavior of + colmmd and symmmd. The post-ordering is only available in the + Matlab interface, not the C-callable interface. + + * made a slight change to the dense row/column detection in symamd, + to match the stated specifications. + + Changes from Version 2.0 to 2.1: + + * TRUE and FALSE are predefined on some systems, so they are defined + here only if not already defined. + + * web site changed + + * UNIX Makefile modified, to handle the case if "." is not in your path. + +*/ + +/* ========================================================================== */ +/* === Description of user-callable routines ================================ */ +/* ========================================================================== */ + +/* + ---------------------------------------------------------------------------- + colamd_recommended: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + int colamd_recommended (int nnz, int n_row, int n_col) ; + + or as a C macro + + #include "colamd.h" + Alen = COLAMD_RECOMMENDED (int nnz, int n_row, int n_col) ; + + Purpose: + + Returns recommended value of Alen for use by colamd. Returns -1 + if any input argument is negative. The use of this routine + or macro is optional. Note that the macro uses its arguments + more than once, so be careful for side effects, if you pass + expressions as arguments to COLAMD_RECOMMENDED. Not needed for + symamd, which dynamically allocates its own memory. + + Arguments (all input arguments): + + int nnz ; Number of nonzeros in the matrix A. This must + be the same value as p [n_col] in the call to + colamd - otherwise you will get a wrong value + of the recommended memory to use. + + int n_row ; Number of rows in the matrix A. + + int n_col ; Number of columns in the matrix A. + + ---------------------------------------------------------------------------- + colamd_set_defaults: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + colamd_set_defaults (int knobs [COLAMD_KNOBS]) ; + + Purpose: + + Sets the default parameters. The use of this routine is optional. + + Arguments: + + double knobs [COLAMD_KNOBS] ; Output only. + + Colamd: rows with more than (knobs [COLAMD_DENSE_ROW] * n_col) + entries are removed prior to ordering. Columns with more than + (knobs [COLAMD_DENSE_COL] * n_row) entries are removed prior to + ordering, and placed last in the output column ordering. + + Symamd: uses only knobs [COLAMD_DENSE_ROW], which is knobs [0]. + Rows and columns with more than (knobs [COLAMD_DENSE_ROW] * n) + entries are removed prior to ordering, and placed last in the + output ordering. + + COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1, + respectively, in colamd.h. Default values of these two knobs + are both 0.5. Currently, only knobs [0] and knobs [1] are + used, but future versions may use more knobs. If so, they will + be properly set to their defaults by the future version of + colamd_set_defaults, so that the code that calls colamd will + not need to change, assuming that you either use + colamd_set_defaults, or pass a (double *) NULL pointer as the + knobs array to colamd or symamd. + + ---------------------------------------------------------------------------- + colamd: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + int colamd (int n_row, int n_col, int Alen, int *A, int *p, + double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS]) ; + + Purpose: + + Computes a column ordering (Q) of A such that P(AQ)=LU or + (AQ)'AQ=LL' have less fill-in and require fewer floating point + operations than factorizing the unpermuted matrix A or A'A, + respectively. + + Returns: + + TRUE (1) if successful, FALSE (0) otherwise. + + Arguments: + + int n_row ; Input argument. + + Number of rows in the matrix A. + Restriction: n_row >= 0. + Colamd returns FALSE if n_row is negative. + + int n_col ; Input argument. + + Number of columns in the matrix A. + Restriction: n_col >= 0. + Colamd returns FALSE if n_col is negative. + + int Alen ; Input argument. + + Restriction (see note): + Alen >= 2*nnz + 6*(n_col+1) + 4*(n_row+1) + n_col + Colamd returns FALSE if these conditions are not met. + + Note: this restriction makes an modest assumption regarding + the size of the two typedef's structures in colamd.h. + We do, however, guarantee that + + Alen >= colamd_recommended (nnz, n_row, n_col) + + or equivalently as a C preprocessor macro: + + Alen >= COLAMD_RECOMMENDED (nnz, n_row, n_col) + + will be sufficient. + + int A [Alen] ; Input argument, undefined on output. + + A is an integer array of size Alen. Alen must be at least as + large as the bare minimum value given above, but this is very + low, and can result in excessive run time. For best + performance, we recommend that Alen be greater than or equal to + colamd_recommended (nnz, n_row, n_col), which adds + nnz/5 to the bare minimum value given above. + + On input, the row indices of the entries in column c of the + matrix are held in A [(p [c]) ... (p [c+1]-1)]. The row indices + in a given column c need not be in ascending order, and + duplicate row indices may be be present. However, colamd will + work a little faster if both of these conditions are met + (Colamd puts the matrix into this format, if it finds that the + the conditions are not met). + + The matrix is 0-based. That is, rows are in the range 0 to + n_row-1, and columns are in the range 0 to n_col-1. Colamd + returns FALSE if any row index is out of range. + + The contents of A are modified during ordering, and are + undefined on output. + + int p [n_col+1] ; Both input and output argument. + + p is an integer array of size n_col+1. On input, it holds the + "pointers" for the column form of the matrix A. Column c of + the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first + entry, p [0], must be zero, and p [c] <= p [c+1] must hold + for all c in the range 0 to n_col-1. The value p [n_col] is + thus the total number of entries in the pattern of the matrix A. + Colamd returns FALSE if these conditions are not met. + + On output, if colamd returns TRUE, the array p holds the column + permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is + the first column index in the new ordering, and p [n_col-1] is + the last. That is, p [k] = j means that column j of A is the + kth pivot column, in AQ, where k is in the range 0 to n_col-1 + (p [0] = j means that column j of A is the first column in AQ). + + If colamd returns FALSE, then no permutation is returned, and + p is undefined on output. + + double knobs [COLAMD_KNOBS] ; Input argument. + + See colamd_set_defaults for a description. + + int stats [COLAMD_STATS] ; Output argument. + + Statistics on the ordering, and error status. + See colamd.h for related definitions. + Colamd returns FALSE if stats is not present. + + stats [0]: number of dense or empty rows ignored. + + stats [1]: number of dense or empty columns ignored (and + ordered last in the output permutation p) + Note that a row can become "empty" if it + contains only "dense" and/or "empty" columns, + and similarly a column can become "empty" if it + only contains "dense" and/or "empty" rows. + + stats [2]: number of garbage collections performed. + This can be excessively high if Alen is close + to the minimum required value. + + stats [3]: status code. < 0 is an error code. + > 1 is a warning or notice. + + 0 OK. Each column of the input matrix contained + row indices in increasing order, with no + duplicates. + + 1 OK, but columns of input matrix were jumbled + (unsorted columns or duplicate entries). Colamd + had to do some extra work to sort the matrix + first and remove duplicate entries, but it + still was able to return a valid permutation + (return value of colamd was TRUE). + + stats [4]: highest numbered column that + is unsorted or has duplicate + entries. + stats [5]: last seen duplicate or + unsorted row index. + stats [6]: number of duplicate or + unsorted row indices. + + -1 A is a null pointer + + -2 p is a null pointer + + -3 n_row is negative + + stats [4]: n_row + + -4 n_col is negative + + stats [4]: n_col + + -5 number of nonzeros in matrix is negative + + stats [4]: number of nonzeros, p [n_col] + + -6 p [0] is nonzero + + stats [4]: p [0] + + -7 A is too small + + stats [4]: required size + stats [5]: actual size (Alen) + + -8 a column has a negative number of entries + + stats [4]: column with < 0 entries + stats [5]: number of entries in col + + -9 a row index is out of bounds + + stats [4]: column with bad row index + stats [5]: bad row index + stats [6]: n_row, # of rows of matrx + + -10 (unused; see symamd.c) + + -999 (unused; see symamd.c) + + Future versions may return more statistics in the stats array. + + Example: + + See http://www.cise.ufl.edu/research/sparse/colamd/example.c + for a complete example. + + To order the columns of a 5-by-4 matrix with 11 nonzero entries in + the following nonzero pattern + + x 0 x 0 + x 0 x x + 0 x x 0 + 0 0 x x + x x 0 0 + + with default knobs and no output statistics, do the following: + + #include "colamd.h" + #define ALEN COLAMD_RECOMMENDED (11, 5, 4) + int A [ALEN] = {1, 2, 5, 3, 5, 1, 2, 3, 4, 2, 4} ; + int p [ ] = {0, 3, 5, 9, 11} ; + int stats [COLAMD_STATS] ; + colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ; + + The permutation is returned in the array p, and A is destroyed. + + ---------------------------------------------------------------------------- + symamd: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + int symamd (int n, int *A, int *p, int *perm, + int knobs [COLAMD_KNOBS], int stats [COLAMD_STATS], + void (*allocate) (size_t, size_t), void (*release) (void *)) ; + + Purpose: + + The symamd routine computes an ordering P of a symmetric sparse + matrix A such that the Cholesky factorization PAP' = LL' remains + sparse. It is based on a column ordering of a matrix M constructed + so that the nonzero pattern of M'M is the same as A. The matrix A + is assumed to be symmetric; only the strictly lower triangular part + is accessed. You must pass your selected memory allocator (usually + calloc/free or mxCalloc/mxFree) to symamd, for it to allocate + memory for the temporary matrix M. + + Returns: + + TRUE (1) if successful, FALSE (0) otherwise. + + Arguments: + + int n ; Input argument. + + Number of rows and columns in the symmetrix matrix A. + Restriction: n >= 0. + Symamd returns FALSE if n is negative. + + int A [nnz] ; Input argument. + + A is an integer array of size nnz, where nnz = p [n]. + + The row indices of the entries in column c of the matrix are + held in A [(p [c]) ... (p [c+1]-1)]. The row indices in a + given column c need not be in ascending order, and duplicate + row indices may be present. However, symamd will run faster + if the columns are in sorted order with no duplicate entries. + + The matrix is 0-based. That is, rows are in the range 0 to + n-1, and columns are in the range 0 to n-1. Symamd + returns FALSE if any row index is out of range. + + The contents of A are not modified. + + int p [n+1] ; Input argument. + + p is an integer array of size n+1. On input, it holds the + "pointers" for the column form of the matrix A. Column c of + the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first + entry, p [0], must be zero, and p [c] <= p [c+1] must hold + for all c in the range 0 to n-1. The value p [n] is + thus the total number of entries in the pattern of the matrix A. + Symamd returns FALSE if these conditions are not met. + + The contents of p are not modified. + + int perm [n+1] ; Output argument. + + On output, if symamd returns TRUE, the array perm holds the + permutation P, where perm [0] is the first index in the new + ordering, and perm [n-1] is the last. That is, perm [k] = j + means that row and column j of A is the kth column in PAP', + where k is in the range 0 to n-1 (perm [0] = j means + that row and column j of A are the first row and column in + PAP'). The array is used as a workspace during the ordering, + which is why it must be of length n+1, not just n. + + double knobs [COLAMD_KNOBS] ; Input argument. + + See colamd_set_defaults for a description. + + int stats [COLAMD_STATS] ; Output argument. + + Statistics on the ordering, and error status. + See colamd.h for related definitions. + Symamd returns FALSE if stats is not present. + + stats [0]: number of dense or empty row and columns ignored + (and ordered last in the output permutation + perm). Note that a row/column can become + "empty" if it contains only "dense" and/or + "empty" columns/rows. + + stats [1]: (same as stats [0]) + + stats [2]: number of garbage collections performed. + + stats [3]: status code. < 0 is an error code. + > 1 is a warning or notice. + + 0 OK. Each column of the input matrix contained + row indices in increasing order, with no + duplicates. + + 1 OK, but columns of input matrix were jumbled + (unsorted columns or duplicate entries). Symamd + had to do some extra work to sort the matrix + first and remove duplicate entries, but it + still was able to return a valid permutation + (return value of symamd was TRUE). + + stats [4]: highest numbered column that + is unsorted or has duplicate + entries. + stats [5]: last seen duplicate or + unsorted row index. + stats [6]: number of duplicate or + unsorted row indices. + + -1 A is a null pointer + + -2 p is a null pointer + + -3 (unused, see colamd.c) + + -4 n is negative + + stats [4]: n + + -5 number of nonzeros in matrix is negative + + stats [4]: # of nonzeros (p [n]). + + -6 p [0] is nonzero + + stats [4]: p [0] + + -7 (unused) + + -8 a column has a negative number of entries + + stats [4]: column with < 0 entries + stats [5]: number of entries in col + + -9 a row index is out of bounds + + stats [4]: column with bad row index + stats [5]: bad row index + stats [6]: n_row, # of rows of matrx + + -10 out of memory (unable to allocate temporary + workspace for M or count arrays using the + "allocate" routine passed into symamd). + + -999 internal error. colamd failed to order the + matrix M, when it should have succeeded. This + indicates a bug. If this (and *only* this) + error code occurs, please contact the authors. + Don't contact the authors if you get any other + error code. + + Future versions may return more statistics in the stats array. + + void * (*allocate) (size_t, size_t) + + A pointer to a function providing memory allocation. The + allocated memory must be returned initialized to zero. For a + C application, this argument should normally be a pointer to + calloc. For a Matlab mexFunction, the routine mxCalloc is + passed instead. + + void (*release) (size_t, size_t) + + A pointer to a function that frees memory allocated by the + memory allocation routine above. For a C application, this + argument should normally be a pointer to free. For a Matlab + mexFunction, the routine mxFree is passed instead. + + + ---------------------------------------------------------------------------- + colamd_report: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + colamd_report (int stats [COLAMD_STATS]) ; + + Purpose: + + Prints the error status and statistics recorded in the stats + array on the standard error output (for a standard C routine) + or on the Matlab output (for a mexFunction). + + Arguments: + + int stats [COLAMD_STATS] ; Input only. Statistics from colamd. + + + ---------------------------------------------------------------------------- + symamd_report: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + symamd_report (int stats [COLAMD_STATS]) ; + + Purpose: + + Prints the error status and statistics recorded in the stats + array on the standard error output (for a standard C routine) + or on the Matlab output (for a mexFunction). + + Arguments: + + int stats [COLAMD_STATS] ; Input only. Statistics from symamd. + + +*/ + +/* ========================================================================== */ +/* === Scaffolding code definitions ======================================== */ +/* ========================================================================== */ + +/* Ensure that debugging is turned off: */ +#ifndef NDEBUG +#define NDEBUG +#endif /* NDEBUG */ + +/* + Our "scaffolding code" philosophy: In our opinion, well-written library + code should keep its "debugging" code, and just normally have it turned off + by the compiler so as not to interfere with performance. This serves + several purposes: + + (1) assertions act as comments to the reader, telling you what the code + expects at that point. All assertions will always be true (unless + there really is a bug, of course). + + (2) leaving in the scaffolding code assists anyone who would like to modify + the code, or understand the algorithm (by reading the debugging output, + one can get a glimpse into what the code is doing). + + (3) (gasp!) for actually finding bugs. This code has been heavily tested + and "should" be fully functional and bug-free ... but you never know... + + To enable debugging, comment out the "#define NDEBUG" above. For a Matlab + mexFunction, you will also need to modify mexopts.sh to remove the -DNDEBUG + definition. The code will become outrageously slow when debugging is + enabled. To control the level of debugging output, set an environment + variable D to 0 (little), 1 (some), 2, 3, or 4 (lots). When debugging, + you should see the following message on the standard output: + + colamd: debug version, D = 1 (THIS WILL BE SLOW!) + + or a similar message for symamd. If you don't, then debugging has not + been enabled. + +*/ + +/* ========================================================================== */ +/* === Include files ======================================================== */ +/* ========================================================================== */ + +#include "colamd.h" +#include + +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#include "matrix.h" +#else +#include +#include +#endif /* MATLAB_MEX_FILE */ + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* ========================================================================== */ +/* === Definitions ========================================================== */ +/* ========================================================================== */ + +/* Routines are either PUBLIC (user-callable) or PRIVATE (not user-callable) */ +#define PUBLIC +#define PRIVATE static + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define ONES_COMPLEMENT(r) (-(r)-1) + +/* -------------------------------------------------------------------------- */ +/* Change for version 2.1: define TRUE and FALSE only if not yet defined */ +/* -------------------------------------------------------------------------- */ + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +/* -------------------------------------------------------------------------- */ + +#define EMPTY (-1) + +/* Row and column status */ +#define ALIVE (0) +#define DEAD (-1) + +/* Column status */ +#define DEAD_PRINCIPAL (-1) +#define DEAD_NON_PRINCIPAL (-2) + +/* Macros for row and column status update and checking. */ +#define ROW_IS_DEAD(r) ROW_IS_MARKED_DEAD (Row[r].shared2.mark) +#define ROW_IS_MARKED_DEAD(row_mark) (row_mark < ALIVE) +#define ROW_IS_ALIVE(r) (Row [r].shared2.mark >= ALIVE) +#define COL_IS_DEAD(c) (Col [c].start < ALIVE) +#define COL_IS_ALIVE(c) (Col [c].start >= ALIVE) +#define COL_IS_DEAD_PRINCIPAL(c) (Col [c].start == DEAD_PRINCIPAL) +#define KILL_ROW(r) { Row [r].shared2.mark = DEAD ; } +#define KILL_PRINCIPAL_COL(c) { Col [c].start = DEAD_PRINCIPAL ; } +#define KILL_NON_PRINCIPAL_COL(c) { Col [c].start = DEAD_NON_PRINCIPAL ; } + +/* ========================================================================== */ +/* === Colamd reporting mechanism =========================================== */ +/* ========================================================================== */ + +#ifdef MATLAB_MEX_FILE + +/* use mexPrintf in a Matlab mexFunction, for debugging and statistics output */ +#define PRINTF mexPrintf + +/* In Matlab, matrices are 1-based to the user, but 0-based internally */ +#define INDEX(i) ((i)+1) + +#else + +/* 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 + +/* In C, matrices are 0-based and indices are reported as such in *_report */ +#define INDEX(i) (i) + +#endif /* MATLAB_MEX_FILE */ + +/* ========================================================================== */ +/* === Prototypes of PRIVATE routines ======================================= */ +/* ========================================================================== */ + +PRIVATE int init_rows_cols +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int p [], + int stats [COLAMD_STATS] +) ; + +PRIVATE void init_scoring +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int head [], + double knobs [COLAMD_KNOBS], + int *p_n_row2, + int *p_n_col2, + int *p_max_deg +) ; + +PRIVATE int find_ordering +( + int n_row, + int n_col, + int Alen, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int head [], + int n_col2, + int max_deg, + int pfree +) ; + +PRIVATE void order_children +( + int n_col, + Colamd_Col Col [], + int p [] +) ; + +PRIVATE void detect_super_cols +( + +#ifndef NDEBUG + int n_col, + Colamd_Row Row [], +#endif /* NDEBUG */ + + Colamd_Col Col [], + int A [], + int head [], + int row_start, + int row_length +) ; + +PRIVATE int garbage_collection +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int *pfree +) ; + +PRIVATE int clear_mark +( + int n_row, + Colamd_Row Row [] +) ; + +PRIVATE void print_report +( + char *method, + int stats [COLAMD_STATS] +) ; + +/* ========================================================================== */ +/* === Debugging prototypes and definitions ================================= */ +/* ========================================================================== */ + +#ifndef NDEBUG + +/* colamd_debug is the *ONLY* global variable, and is only */ +/* present when debugging */ + +PRIVATE int colamd_debug ; /* debug print level */ + +#define DEBUG0(params) { (void) PRINTF params ; } +#define DEBUG1(params) { if (colamd_debug >= 1) (void) PRINTF params ; } +#define DEBUG2(params) { if (colamd_debug >= 2) (void) PRINTF params ; } +#define DEBUG3(params) { if (colamd_debug >= 3) (void) PRINTF params ; } +#define DEBUG4(params) { if (colamd_debug >= 4) (void) PRINTF params ; } + +#ifdef MATLAB_MEX_FILE +#define ASSERT(expression) (mxAssert ((expression), "")) +#else +#define ASSERT(expression) (assert (expression)) +#endif /* MATLAB_MEX_FILE */ + +PRIVATE void colamd_get_debug /* gets the debug print level from getenv */ +( + char *method +) ; + +PRIVATE void debug_deg_lists +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int head [], + int min_score, + int should, + int max_deg +) ; + +PRIVATE void debug_mark +( + int n_row, + Colamd_Row Row [], + int tag_mark, + int max_mark +) ; + +PRIVATE void debug_matrix +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [] +) ; + +PRIVATE void debug_structures +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int n_col2 +) ; + +#else /* NDEBUG */ + +/* === No debugging ========================================================= */ + +#define DEBUG0(params) ; +#define DEBUG1(params) ; +#define DEBUG2(params) ; +#define DEBUG3(params) ; +#define DEBUG4(params) ; + +#define ASSERT(expression) ((void) 0) + +#endif /* NDEBUG */ + +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === USER-CALLABLE ROUTINES: ============================================== */ +/* ========================================================================== */ + + +/* ========================================================================== */ +/* === colamd_recommended =================================================== */ +/* ========================================================================== */ + +/* + The colamd_recommended routine returns the suggested size for Alen. This + value has been determined to provide good balance between the number of + garbage collections and the memory requirements for colamd. If any + argument is negative, a -1 is returned as an error condition. This + function is also available as a macro defined in colamd.h, so that you + can use it for a statically-allocated array size. +*/ + +PUBLIC int colamd_recommended /* returns recommended value of Alen. */ +( + /* === Parameters ======================================================= */ + + int nnz, /* number of nonzeros in A */ + int n_row, /* number of rows in A */ + int n_col /* number of columns in A */ +) +{ + return (COLAMD_RECOMMENDED (nnz, n_row, n_col)) ; +} + + +/* ========================================================================== */ +/* === colamd_set_defaults ================================================== */ +/* ========================================================================== */ + +/* + The colamd_set_defaults routine sets the default values of the user- + controllable parameters for colamd: + + knobs [0] rows with knobs[0]*n_col entries or more are removed + prior to ordering in colamd. Rows and columns with + knobs[0]*n_col entries or more are removed prior to + ordering in symamd and placed last in the output + ordering. + + knobs [1] columns with knobs[1]*n_row entries or more are removed + prior to ordering in colamd, and placed last in the + column permutation. Symamd ignores this knob. + + knobs [2..19] unused, but future versions might use this +*/ + +PUBLIC void colamd_set_defaults +( + /* === Parameters ======================================================= */ + + double knobs [COLAMD_KNOBS] /* knob array */ +) +{ + /* === Local variables ================================================== */ + + int i ; + + if (!knobs) + { + return ; /* no knobs to initialize */ + } + for (i = 0 ; i < COLAMD_KNOBS ; i++) + { + knobs [i] = 0 ; + } + knobs [COLAMD_DENSE_ROW] = 0.5 ; /* ignore rows over 50% dense */ + knobs [COLAMD_DENSE_COL] = 0.5 ; /* ignore columns over 50% dense */ +} + + +/* ========================================================================== */ +/* === symamd =============================================================== */ +/* ========================================================================== */ + +PUBLIC int symamd /* return TRUE if OK, FALSE otherwise */ +( + /* === Parameters ======================================================= */ + + 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+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) */ +) +{ + /* === Local variables ================================================== */ + + int *count ; /* length of each column of M, and col pointer*/ + int *mark ; /* mark array for finding duplicate entries */ + int *M ; /* row indices of matrix M */ + int Mlen ; /* length of M */ + int n_row ; /* number of rows in M */ + int nnz ; /* number of entries in A */ + int i ; /* row index of A */ + int j ; /* column index of A */ + int k ; /* row index of M */ + int mnz ; /* number of nonzeros in M */ + int pp ; /* index into a column of A */ + int last_row ; /* last row seen in the current column */ + int length ; /* number of nonzeros in a column */ + + double cknobs [COLAMD_KNOBS] ; /* knobs for colamd */ + double default_knobs [COLAMD_KNOBS] ; /* default knobs for colamd */ + int cstats [COLAMD_STATS] ; /* colamd stats */ + +#ifndef NDEBUG + colamd_get_debug ("symamd") ; +#endif /* NDEBUG */ + + /* === Check the input arguments ======================================== */ + + if (!stats) + { + DEBUG0 (("symamd: stats not present\n")) ; + return (FALSE) ; + } + for (i = 0 ; i < COLAMD_STATS ; i++) + { + stats [i] = 0 ; + } + stats [COLAMD_STATUS] = COLAMD_OK ; + stats [COLAMD_INFO1] = -1 ; + stats [COLAMD_INFO2] = -1 ; + + if (!A) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; + DEBUG0 (("symamd: A not present\n")) ; + return (FALSE) ; + } + + if (!p) /* p is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; + DEBUG0 (("symamd: p not present\n")) ; + return (FALSE) ; + } + + if (n < 0) /* n must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; + stats [COLAMD_INFO1] = n ; + DEBUG0 (("symamd: n negative %d\n", n)) ; + return (FALSE) ; + } + + nnz = p [n] ; + if (nnz < 0) /* nnz must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; + stats [COLAMD_INFO1] = nnz ; + DEBUG0 (("symamd: number of entries negative %d\n", nnz)) ; + return (FALSE) ; + } + + if (p [0] != 0) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; + stats [COLAMD_INFO1] = p [0] ; + DEBUG0 (("symamd: p[0] not zero %d\n", p [0])) ; + return (FALSE) ; + } + + /* === If no knobs, set default knobs =================================== */ + + if (!knobs) + { + colamd_set_defaults (default_knobs) ; + knobs = default_knobs ; + } + + /* === Allocate count and mark ========================================== */ + + count = (int *) ((*allocate) (n+1, sizeof (int))) ; + if (!count) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + DEBUG0 (("symamd: allocate count (size %d) failed\n", n+1)) ; + return (FALSE) ; + } + + mark = (int *) ((*allocate) (n+1, sizeof (int))) ; + if (!mark) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + (*release) ((void *) count) ; + DEBUG0 (("symamd: allocate mark (size %d) failed\n", n+1)) ; + return (FALSE) ; + } + + /* === Compute column counts of M, check if A is valid ================== */ + + stats [COLAMD_INFO3] = 0 ; /* number of duplicate or unsorted row indices*/ + + for (i = 0 ; i < n ; i++) + { + mark [i] = -1 ; + } + + for (j = 0 ; j < n ; j++) + { + last_row = -1 ; + + length = p [j+1] - p [j] ; + if (length < 0) + { + /* column pointers must be non-decreasing */ + stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = length ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: col %d negative length %d\n", j, length)) ; + return (FALSE) ; + } + + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + if (i < 0 || i >= n) + { + /* row index i, in column j, is out of bounds */ + stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = i ; + stats [COLAMD_INFO3] = n ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: row %d col %d out of bounds\n", i, j)) ; + return (FALSE) ; + } + + if (i <= last_row || mark [i] == j) + { + /* row index is unsorted or repeated (or both), thus col */ + /* is jumbled. This is a notice, not an error condition. */ + stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = i ; + (stats [COLAMD_INFO3]) ++ ; + DEBUG1 (("symamd: row %d col %d unsorted/duplicate\n", i, j)) ; + } + + if (i > j && mark [i] != j) + { + /* row k of M will contain column indices i and j */ + count [i]++ ; + count [j]++ ; + } + + /* mark the row as having been seen in this column */ + mark [i] = j ; + + last_row = i ; + } + } + + if (stats [COLAMD_STATUS] == COLAMD_OK) + { + /* if there are no duplicate entries, then mark is no longer needed */ + (*release) ((void *) mark) ; + } + + /* === Compute column pointers of M ===================================== */ + + /* use output permutation, perm, for column pointers of M */ + perm [0] = 0 ; + for (j = 1 ; j <= n ; j++) + { + perm [j] = perm [j-1] + count [j-1] ; + } + for (j = 0 ; j < n ; j++) + { + count [j] = perm [j] ; + } + + /* === Construct M ====================================================== */ + + mnz = perm [n] ; + n_row = mnz / 2 ; + Mlen = colamd_recommended (mnz, n_row, n) ; + M = (int *) ((*allocate) (Mlen, sizeof (int))) ; + DEBUG0 (("symamd: M is %d-by-%d with %d entries, Mlen = %d\n", + n_row, n, mnz, Mlen)) ; + + if (!M) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: allocate M (size %d) failed\n", Mlen)) ; + return (FALSE) ; + } + + k = 0 ; + + if (stats [COLAMD_STATUS] == COLAMD_OK) + { + /* Matrix is OK */ + for (j = 0 ; j < n ; j++) + { + ASSERT (p [j+1] - p [j] >= 0) ; + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + ASSERT (i >= 0 && i < n) ; + if (i > j) + { + /* row k of M contains column indices i and j */ + M [count [i]++] = k ; + M [count [j]++] = k ; + k++ ; + } + } + } + } + else + { + /* Matrix is jumbled. Do not add duplicates to M. Unsorted cols OK. */ + DEBUG0 (("symamd: Duplicates in A.\n")) ; + for (i = 0 ; i < n ; i++) + { + mark [i] = -1 ; + } + for (j = 0 ; j < n ; j++) + { + ASSERT (p [j+1] - p [j] >= 0) ; + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + ASSERT (i >= 0 && i < n) ; + if (i > j && mark [i] != j) + { + /* row k of M contains column indices i and j */ + M [count [i]++] = k ; + M [count [j]++] = k ; + k++ ; + mark [i] = j ; + } + } + } + (*release) ((void *) mark) ; + } + + /* count and mark no longer needed */ + (*release) ((void *) count) ; + ASSERT (k == n_row) ; + + /* === Adjust the knobs for M =========================================== */ + + for (i = 0 ; i < COLAMD_KNOBS ; i++) + { + cknobs [i] = knobs [i] ; + } + + /* there are no dense rows in M */ + cknobs [COLAMD_DENSE_ROW] = 1.0 ; + + if (n_row != 0 && n < n_row) + { + /* On input, the knob is a fraction of 1..n, the number of rows of A. */ + /* Convert it to a fraction of 1..n_row, of the number of rows of M. */ + cknobs [COLAMD_DENSE_COL] = (knobs [COLAMD_DENSE_ROW] * n) / n_row ; + } + else + { + /* no dense columns in M */ + cknobs [COLAMD_DENSE_COL] = 1.0 ; + } + + DEBUG0 (("symamd: dense col knob for M: %g\n", cknobs [COLAMD_DENSE_COL])) ; + + /* === Order the columns of M =========================================== */ + + if (!colamd (n_row, n, Mlen, M, perm, cknobs, cstats)) + { + /* This "cannot" happen, unless there is a bug in the code. */ + stats [COLAMD_STATUS] = COLAMD_ERROR_internal_error ; + (*release) ((void *) M) ; + DEBUG0 (("symamd: internal error!\n")) ; + return (FALSE) ; + } + + /* Note that the output permutation is now in perm */ + + /* === get the statistics for symamd from colamd ======================== */ + + /* note that a dense column in colamd means a dense row and col in symamd */ + stats [COLAMD_DENSE_ROW] = cstats [COLAMD_DENSE_COL] ; + stats [COLAMD_DENSE_COL] = cstats [COLAMD_DENSE_COL] ; + stats [COLAMD_DEFRAG_COUNT] = cstats [COLAMD_DEFRAG_COUNT] ; + + /* === Free M =========================================================== */ + + (*release) ((void *) M) ; + DEBUG0 (("symamd: done.\n")) ; + return (TRUE) ; + +} + +/* ========================================================================== */ +/* === colamd =============================================================== */ +/* ========================================================================== */ + +/* + The colamd routine computes a column ordering Q of a sparse matrix + A such that the LU factorization P(AQ) = LU remains sparse, where P is + selected via partial pivoting. The routine can also be viewed as + providing a permutation Q such that the Cholesky factorization + (AQ)'(AQ) = LL' remains sparse. +*/ + +PUBLIC int colamd /* returns TRUE if successful, FALSE otherwise*/ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows in A */ + int n_col, /* number of columns in A */ + int Alen, /* length of A */ + int A [], /* row indices of A */ + int p [], /* pointers to columns in A */ + double knobs [COLAMD_KNOBS],/* parameters (uses defaults if NULL) */ + int stats [COLAMD_STATS] /* output statistics and error codes */ +) +{ + /* === Local variables ================================================== */ + + int i ; /* loop index */ + int nnz ; /* nonzeros in A */ + int Row_size ; /* size of Row [], in integers */ + int Col_size ; /* size of Col [], in integers */ + int need ; /* minimum required length of A */ + Colamd_Row *Row ; /* pointer into A of Row [0..n_row] array */ + Colamd_Col *Col ; /* pointer into A of Col [0..n_col] array */ + int n_col2 ; /* number of non-dense, non-empty columns */ + int n_row2 ; /* number of non-dense, non-empty rows */ + int ngarbage ; /* number of garbage collections performed */ + int max_deg ; /* maximum row degree */ + double default_knobs [COLAMD_KNOBS] ; /* default knobs array */ + +#ifndef NDEBUG + colamd_get_debug ("colamd") ; +#endif /* NDEBUG */ + + /* === Check the input arguments ======================================== */ + + if (!stats) + { + DEBUG0 (("colamd: stats not present\n")) ; + return (FALSE) ; + } + for (i = 0 ; i < COLAMD_STATS ; i++) + { + stats [i] = 0 ; + } + stats [COLAMD_STATUS] = COLAMD_OK ; + stats [COLAMD_INFO1] = -1 ; + stats [COLAMD_INFO2] = -1 ; + + if (!A) /* A is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; + DEBUG0 (("colamd: A not present\n")) ; + return (FALSE) ; + } + + if (!p) /* p is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; + DEBUG0 (("colamd: p not present\n")) ; + return (FALSE) ; + } + + if (n_row < 0) /* n_row must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ; + stats [COLAMD_INFO1] = n_row ; + DEBUG0 (("colamd: nrow negative %d\n", n_row)) ; + return (FALSE) ; + } + + if (n_col < 0) /* n_col must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; + stats [COLAMD_INFO1] = n_col ; + DEBUG0 (("colamd: ncol negative %d\n", n_col)) ; + return (FALSE) ; + } + + nnz = p [n_col] ; + if (nnz < 0) /* nnz must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; + stats [COLAMD_INFO1] = nnz ; + DEBUG0 (("colamd: number of entries negative %d\n", nnz)) ; + return (FALSE) ; + } + + if (p [0] != 0) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; + stats [COLAMD_INFO1] = p [0] ; + DEBUG0 (("colamd: p[0] not zero %d\n", p [0])) ; + return (FALSE) ; + } + + /* === If no knobs, set default knobs =================================== */ + + if (!knobs) + { + colamd_set_defaults (default_knobs) ; + knobs = default_knobs ; + } + + /* === Allocate the Row and Col arrays from array A ===================== */ + + Col_size = COLAMD_C (n_col) ; + Row_size = COLAMD_R (n_row) ; + need = 2*nnz + n_col + Col_size + Row_size ; + + if (need > Alen) + { + /* not enough space in array A to perform the ordering */ + stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ; + stats [COLAMD_INFO1] = need ; + stats [COLAMD_INFO2] = Alen ; + DEBUG0 (("colamd: Need Alen >= %d, given only Alen = %d\n", need,Alen)); + return (FALSE) ; + } + + Alen -= Col_size + Row_size ; + Col = (Colamd_Col *) &A [Alen] ; + Row = (Colamd_Row *) &A [Alen + Col_size] ; + + /* === Construct the row and column data structures ===================== */ + + if (!init_rows_cols (n_row, n_col, Row, Col, A, p, stats)) + { + /* input matrix is invalid */ + DEBUG0 (("colamd: Matrix invalid\n")) ; + return (FALSE) ; + } + + /* === Initialize scores, kill dense rows/columns ======================= */ + + init_scoring (n_row, n_col, Row, Col, A, p, knobs, + &n_row2, &n_col2, &max_deg) ; + + /* === Order the supercolumns =========================================== */ + + ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p, + n_col2, max_deg, 2*nnz) ; + + /* === Order the non-principal columns ================================== */ + + order_children (n_col, Col, p) ; + + /* === Return statistics in stats ======================================= */ + + stats [COLAMD_DENSE_ROW] = n_row - n_row2 ; + stats [COLAMD_DENSE_COL] = n_col - n_col2 ; + stats [COLAMD_DEFRAG_COUNT] = ngarbage ; + DEBUG0 (("colamd: done.\n")) ; + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === colamd_report ======================================================== */ +/* ========================================================================== */ + +PUBLIC void colamd_report +( + int stats [COLAMD_STATS] +) +{ + print_report ("colamd", stats) ; +} + + +/* ========================================================================== */ +/* === symamd_report ======================================================== */ +/* ========================================================================== */ + +PUBLIC void symamd_report +( + int stats [COLAMD_STATS] +) +{ + print_report ("symamd", stats) ; +} + + + +/* ========================================================================== */ +/* === NON-USER-CALLABLE ROUTINES: ========================================== */ +/* ========================================================================== */ + +/* There are no user-callable routines beyond this point in the file */ + + +/* ========================================================================== */ +/* === init_rows_cols ======================================================= */ +/* ========================================================================== */ + +/* + Takes the column form of the matrix in A and creates the row form of the + matrix. Also, row and column attributes are stored in the Col and Row + structs. If the columns are un-sorted or contain duplicate row indices, + this routine will also sort and remove duplicate row indices from the + column form of the matrix. Returns FALSE if the matrix is invalid, + TRUE otherwise. Not user-callable. +*/ + +PRIVATE int init_rows_cols /* returns TRUE if OK, or FALSE otherwise */ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows of A */ + int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + int A [], /* row indices of A, of size Alen */ + int p [], /* pointers to columns in A, of size n_col+1 */ + int stats [COLAMD_STATS] /* colamd statistics */ +) +{ + /* === Local variables ================================================== */ + + int col ; /* a column index */ + int row ; /* a row index */ + int *cp ; /* a column pointer */ + int *cp_end ; /* a pointer to the end of a column */ + int *rp ; /* a row pointer */ + int *rp_end ; /* a pointer to the end of a row */ + int last_row ; /* previous row */ + + /* === Initialize columns, and check column pointers ==================== */ + + for (col = 0 ; col < n_col ; col++) + { + Col [col].start = p [col] ; + Col [col].length = p [col+1] - p [col] ; + + if (Col [col].length < 0) + { + /* column pointers must be non-decreasing */ + stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = Col [col].length ; + DEBUG0 (("colamd: col %d length %d < 0\n", col, Col [col].length)) ; + return (FALSE) ; + } + + Col [col].shared1.thickness = 1 ; + Col [col].shared2.score = 0 ; + Col [col].shared3.prev = EMPTY ; + Col [col].shared4.degree_next = EMPTY ; + } + + /* p [0..n_col] no longer needed, used as "head" in subsequent routines */ + + /* === Scan columns, compute row degrees, and check row indices ========= */ + + stats [COLAMD_INFO3] = 0 ; /* number of duplicate or unsorted row indices*/ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].length = 0 ; + Row [row].shared2.mark = -1 ; + } + + for (col = 0 ; col < n_col ; col++) + { + last_row = -1 ; + + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + + while (cp < cp_end) + { + row = *cp++ ; + + /* make sure row indices within range */ + if (row < 0 || row >= n_row) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + stats [COLAMD_INFO3] = n_row ; + DEBUG0 (("colamd: row %d col %d out of bounds\n", row, col)) ; + return (FALSE) ; + } + + if (row <= last_row || Row [row].shared2.mark == col) + { + /* row index are unsorted or repeated (or both), thus col */ + /* is jumbled. This is a notice, not an error condition. */ + stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + (stats [COLAMD_INFO3]) ++ ; + DEBUG1 (("colamd: row %d col %d unsorted/duplicate\n",row,col)); + } + + if (Row [row].shared2.mark != col) + { + Row [row].length++ ; + } + else + { + /* this is a repeated entry in the column, */ + /* it will be removed */ + Col [col].length-- ; + } + + /* mark the row as having been seen in this column */ + Row [row].shared2.mark = col ; + + last_row = row ; + } + } + + /* === Compute row pointers ============================================= */ + + /* row form of the matrix starts directly after the column */ + /* form of matrix in A */ + Row [0].start = p [n_col] ; + Row [0].shared1.p = Row [0].start ; + Row [0].shared2.mark = -1 ; + for (row = 1 ; row < n_row ; row++) + { + Row [row].start = Row [row-1].start + Row [row-1].length ; + Row [row].shared1.p = Row [row].start ; + Row [row].shared2.mark = -1 ; + } + + /* === Create row form ================================================== */ + + if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) + { + /* if cols jumbled, watch for repeated row indices */ + for (col = 0 ; col < n_col ; col++) + { + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + while (cp < cp_end) + { + row = *cp++ ; + if (Row [row].shared2.mark != col) + { + A [(Row [row].shared1.p)++] = col ; + Row [row].shared2.mark = col ; + } + } + } + } + else + { + /* if cols not jumbled, we don't need the mark (this is faster) */ + for (col = 0 ; col < n_col ; col++) + { + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + while (cp < cp_end) + { + A [(Row [*cp++].shared1.p)++] = col ; + } + } + } + + /* === Clear the row marks and set row degrees ========================== */ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].shared2.mark = 0 ; + Row [row].shared1.degree = Row [row].length ; + } + + /* === See if we need to re-create columns ============================== */ + + if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) + { + DEBUG0 (("colamd: reconstructing column form, matrix jumbled\n")) ; + +#ifndef NDEBUG + /* make sure column lengths are correct */ + for (col = 0 ; col < n_col ; col++) + { + p [col] = Col [col].length ; + } + for (row = 0 ; row < n_row ; row++) + { + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + p [*rp++]-- ; + } + } + for (col = 0 ; col < n_col ; col++) + { + ASSERT (p [col] == 0) ; + } + /* now p is all zero (different than when debugging is turned off) */ +#endif /* NDEBUG */ + + /* === Compute col pointers ========================================= */ + + /* col form of the matrix starts at A [0]. */ + /* Note, we may have a gap between the col form and the row */ + /* form if there were duplicate entries, if so, it will be */ + /* removed upon the first garbage collection */ + Col [0].start = 0 ; + p [0] = Col [0].start ; + for (col = 1 ; col < n_col ; col++) + { + /* note that the lengths here are for pruned columns, i.e. */ + /* no duplicate row indices will exist for these columns */ + Col [col].start = Col [col-1].start + Col [col-1].length ; + p [col] = Col [col].start ; + } + + /* === Re-create col form =========================================== */ + + for (row = 0 ; row < n_row ; row++) + { + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + A [(p [*rp++])++] = row ; + } + } + } + + /* === Done. Matrix is not (or no longer) jumbled ====================== */ + + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === init_scoring ========================================================= */ +/* ========================================================================== */ + +/* + Kills dense or empty columns and rows, calculates an initial score for + each column, and places all columns in the degree lists. Not user-callable. +*/ + +PRIVATE void init_scoring +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows of A */ + int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + int A [], /* column form and row form of A */ + int head [], /* of size n_col+1 */ + double knobs [COLAMD_KNOBS],/* parameters */ + int *p_n_row2, /* number of non-dense, non-empty rows */ + int *p_n_col2, /* number of non-dense, non-empty columns */ + int *p_max_deg /* maximum row degree */ +) +{ + /* === Local variables ================================================== */ + + int c ; /* a column index */ + int r, row ; /* a row index */ + int *cp ; /* a column pointer */ + int deg ; /* degree of a row or column */ + int *cp_end ; /* a pointer to the end of a column */ + int *new_cp ; /* new column pointer */ + int col_length ; /* length of pruned column */ + int score ; /* current column score */ + int n_col2 ; /* number of non-dense, non-empty columns */ + int n_row2 ; /* number of non-dense, non-empty rows */ + int dense_row_count ; /* remove rows with more entries than this */ + int dense_col_count ; /* remove cols with more entries than this */ + int min_score ; /* smallest column score */ + int max_deg ; /* maximum row degree */ + int next_col ; /* Used to add to degree list.*/ + +#ifndef NDEBUG + int debug_count ; /* debug only. */ +#endif /* NDEBUG */ + + /* === Extract knobs ==================================================== */ + + dense_row_count = (int) MAX (0, MIN (knobs [COLAMD_DENSE_ROW] * n_col, n_col)) ; + dense_col_count = (int) MAX (0, MIN (knobs [COLAMD_DENSE_COL] * n_row, n_row)) ; + DEBUG1 (("colamd: densecount: %d %d\n", dense_row_count, dense_col_count)) ; + max_deg = 0 ; + n_col2 = n_col ; + n_row2 = n_row ; + + /* === Kill empty columns =============================================== */ + + /* Put the empty columns at the end in their natural order, so that LU */ + /* factorization can proceed as far as possible. */ + for (c = n_col-1 ; c >= 0 ; c--) + { + deg = Col [c].length ; + if (deg == 0) + { + /* this is a empty column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + } + } + DEBUG1 (("colamd: null columns killed: %d\n", n_col - n_col2)) ; + + /* === Kill dense columns =============================================== */ + + /* Put the dense columns at the end, in their natural order */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip any dead columns */ + if (COL_IS_DEAD (c)) + { + continue ; + } + deg = Col [c].length ; + if (deg > dense_col_count) + { + /* this is a dense column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + /* decrement the row degrees */ + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + Row [*cp++].shared1.degree-- ; + } + KILL_PRINCIPAL_COL (c) ; + } + } + DEBUG1 (("colamd: Dense and null columns killed: %d\n", n_col - n_col2)) ; + + /* === Kill dense and empty rows ======================================== */ + + for (r = 0 ; r < n_row ; r++) + { + deg = Row [r].shared1.degree ; + ASSERT (deg >= 0 && deg <= n_col) ; + if (deg > dense_row_count || deg == 0) + { + /* kill a dense or empty row */ + KILL_ROW (r) ; + --n_row2 ; + } + else + { + /* keep track of max degree of remaining rows */ + max_deg = MAX (max_deg, deg) ; + } + } + DEBUG1 (("colamd: Dense and null rows killed: %d\n", n_row - n_row2)) ; + + /* === Compute initial column scores ==================================== */ + + /* At this point the row degrees are accurate. They reflect the number */ + /* of "live" (non-dense) columns in each row. No empty rows exist. */ + /* Some "live" columns may contain only dead rows, however. These are */ + /* pruned in the code below. */ + + /* now find the initial matlab score for each column */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip dead column */ + if (COL_IS_DEAD (c)) + { + continue ; + } + score = 0 ; + cp = &A [Col [c].start] ; + new_cp = cp ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + /* skip if dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + /* compact the column */ + *new_cp++ = row ; + /* add row's external degree */ + score += Row [row].shared1.degree - 1 ; + /* guard against integer overflow */ + score = MIN (score, n_col) ; + } + /* determine pruned column length */ + col_length = (int) (new_cp - &A [Col [c].start]) ; + if (col_length == 0) + { + /* a newly-made null column (all rows in this col are "dense" */ + /* and have already been killed) */ + DEBUG2 (("Newly null killed: %d\n", c)) ; + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + } + else + { + /* set column length and set score */ + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + Col [c].length = col_length ; + Col [c].shared2.score = score ; + } + } + DEBUG1 (("colamd: Dense, null, and newly-null columns killed: %d\n", + n_col-n_col2)) ; + + /* At this point, all empty rows and columns are dead. All live columns */ + /* are "clean" (containing no dead rows) and simplicial (no supercolumns */ + /* yet). Rows may contain dead columns, but all live rows contain at */ + /* least one live column. */ + +#ifndef NDEBUG + debug_structures (n_row, n_col, Row, Col, A, n_col2) ; +#endif /* NDEBUG */ + + /* === Initialize degree lists ========================================== */ + +#ifndef NDEBUG + debug_count = 0 ; +#endif /* NDEBUG */ + + /* clear the hash buckets */ + for (c = 0 ; c <= n_col ; c++) + { + head [c] = EMPTY ; + } + min_score = n_col ; + /* place in reverse order, so low column indices are at the front */ + /* of the lists. This is to encourage natural tie-breaking */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* only add principal columns to degree lists */ + if (COL_IS_ALIVE (c)) + { + DEBUG4 (("place %d score %d minscore %d ncol %d\n", + c, Col [c].shared2.score, min_score, n_col)) ; + + /* === Add columns score to DList =============================== */ + + score = Col [c].shared2.score ; + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + ASSERT (head [score] >= EMPTY) ; + + /* now add this column to dList at proper score location */ + next_col = head [score] ; + Col [c].shared3.prev = EMPTY ; + Col [c].shared4.degree_next = next_col ; + + /* if there already was a column with the same score, set its */ + /* previous pointer to this new column */ + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = c ; + } + head [score] = c ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, score) ; + +#ifndef NDEBUG + debug_count++ ; +#endif /* NDEBUG */ + + } + } + +#ifndef NDEBUG + DEBUG1 (("colamd: Live cols %d out of %d, non-princ: %d\n", + debug_count, n_col, n_col-debug_count)) ; + ASSERT (debug_count == n_col2) ; + debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ; +#endif /* NDEBUG */ + + /* === Return number of remaining columns, and max row degree =========== */ + + *p_n_col2 = n_col2 ; + *p_n_row2 = n_row2 ; + *p_max_deg = max_deg ; +} + + +/* ========================================================================== */ +/* === find_ordering ======================================================== */ +/* ========================================================================== */ + +/* + Order the principal columns of the supercolumn form of the matrix + (no supercolumns on input). Uses a minimum approximate column minimum + degree ordering method. Not user-callable. +*/ + +PRIVATE int find_ordering /* return the number of garbage collections */ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows of A */ + int n_col, /* number of columns of A */ + int Alen, /* size of A, 2*nnz + n_col or larger */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + int A [], /* column form and row form of A */ + int head [], /* of size n_col+1 */ + int n_col2, /* Remaining columns to order */ + int max_deg, /* Maximum row degree */ + int pfree /* index of first free slot (2*nnz on entry) */ +) +{ + /* === Local variables ================================================== */ + + int k ; /* current pivot ordering step */ + int pivot_col ; /* current pivot column */ + int *cp ; /* a column pointer */ + int *rp ; /* a row pointer */ + int pivot_row ; /* current pivot row */ + int *new_cp ; /* modified column pointer */ + int *new_rp ; /* modified row pointer */ + int pivot_row_start ; /* pointer to start of pivot row */ + int pivot_row_degree ; /* number of columns in pivot row */ + int pivot_row_length ; /* number of supercolumns in pivot row */ + int pivot_col_score ; /* score of pivot column */ + int needed_memory ; /* free space needed for pivot row */ + int *cp_end ; /* pointer to the end of a column */ + int *rp_end ; /* pointer to the end of a row */ + int row ; /* a row index */ + int col ; /* a column index */ + int max_score ; /* maximum possible score */ + int cur_score ; /* score of current column */ + unsigned int hash ; /* hash value for supernode detection */ + int head_column ; /* head of hash bucket */ + int first_col ; /* first column in hash bucket */ + int tag_mark ; /* marker value for mark array */ + int row_mark ; /* Row [row].shared2.mark */ + int set_difference ; /* set difference size of row with pivot row */ + int min_score ; /* smallest column score */ + int col_thickness ; /* "thickness" (no. of columns in a supercol) */ + int max_mark ; /* maximum value of tag_mark */ + int pivot_col_thickness ; /* number of columns represented by pivot col */ + int prev_col ; /* Used by Dlist operations. */ + int next_col ; /* Used by Dlist operations. */ + int ngarbage ; /* number of garbage collections performed */ + +#ifndef NDEBUG + int debug_d ; /* debug loop counter */ + int debug_step = 0 ; /* debug loop counter */ +#endif /* NDEBUG */ + + /* === Initialization and clear mark ==================================== */ + + max_mark = INT_MAX - n_col ; /* INT_MAX defined in */ + tag_mark = clear_mark (n_row, Row) ; + min_score = 0 ; + ngarbage = 0 ; + DEBUG1 (("colamd: Ordering, n_col2=%d\n", n_col2)) ; + + /* === Order the columns ================================================ */ + + for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */) + { + +#ifndef NDEBUG + if (debug_step % 100 == 0) + { + DEBUG2 (("\n... Step k: %d out of n_col2: %d\n", k, n_col2)) ; + } + else + { + DEBUG3 (("\n----------Step k: %d out of n_col2: %d\n", k, n_col2)) ; + } + debug_step++ ; + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + + /* === Select pivot column, and order it ============================ */ + + /* make sure degree list isn't empty */ + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (head [min_score] >= EMPTY) ; + +#ifndef NDEBUG + for (debug_d = 0 ; debug_d < min_score ; debug_d++) + { + ASSERT (head [debug_d] == EMPTY) ; + } +#endif /* NDEBUG */ + + /* get pivot column from head of minimum degree list */ + while (head [min_score] == EMPTY && min_score < n_col) + { + min_score++ ; + } + pivot_col = head [min_score] ; + ASSERT (pivot_col >= 0 && pivot_col <= n_col) ; + next_col = Col [pivot_col].shared4.degree_next ; + head [min_score] = next_col ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = EMPTY ; + } + + ASSERT (COL_IS_ALIVE (pivot_col)) ; + DEBUG3 (("Pivot col: %d\n", pivot_col)) ; + + /* remember score for defrag check */ + pivot_col_score = Col [pivot_col].shared2.score ; + + /* the pivot column is the kth column in the pivot order */ + Col [pivot_col].shared2.order = k ; + + /* increment order count by column thickness */ + pivot_col_thickness = Col [pivot_col].shared1.thickness ; + k += pivot_col_thickness ; + ASSERT (pivot_col_thickness > 0) ; + + /* === Garbage_collection, if necessary ============================= */ + + needed_memory = MIN (pivot_col_score, n_col - k) ; + if (pfree + needed_memory >= Alen) + { + pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ; + ngarbage++ ; + /* after garbage collection we will have enough */ + ASSERT (pfree + needed_memory < Alen) ; + /* garbage collection has wiped out the Row[].shared2.mark array */ + tag_mark = clear_mark (n_row, Row) ; + +#ifndef NDEBUG + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + } + + /* === Compute pivot row pattern ==================================== */ + + /* get starting location for this new merged row */ + pivot_row_start = pfree ; + + /* initialize new row counts to zero */ + pivot_row_degree = 0 ; + + /* tag pivot column as having been visited so it isn't included */ + /* in merged pivot row */ + Col [pivot_col].shared1.thickness = -pivot_col_thickness ; + + /* pivot row is the union of all rows in the pivot column pattern */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + DEBUG4 (("Pivot col pattern %d %d\n", ROW_IS_ALIVE (row), row)) ; + /* skip if row is dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + /* add the column, if alive and untagged */ + col_thickness = Col [col].shared1.thickness ; + if (col_thickness > 0 && COL_IS_ALIVE (col)) + { + /* tag column in pivot row */ + Col [col].shared1.thickness = -col_thickness ; + ASSERT (pfree < Alen) ; + /* place column in pivot row */ + A [pfree++] = col ; + pivot_row_degree += col_thickness ; + } + } + } + + /* clear tag on pivot column */ + Col [pivot_col].shared1.thickness = pivot_col_thickness ; + max_deg = MAX (max_deg, pivot_row_degree) ; + +#ifndef NDEBUG + DEBUG3 (("check2\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Kill all rows used to construct pivot row ==================== */ + + /* also kill pivot row, temporarily */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* may be killing an already dead row */ + row = *cp++ ; + DEBUG3 (("Kill row in pivot col: %d\n", row)) ; + KILL_ROW (row) ; + } + + /* === Select a row index to use as the new pivot row =============== */ + + pivot_row_length = pfree - pivot_row_start ; + if (pivot_row_length > 0) + { + /* pick the "pivot" row arbitrarily (first row in col) */ + pivot_row = A [Col [pivot_col].start] ; + DEBUG3 (("Pivotal row is %d\n", pivot_row)) ; + } + else + { + /* there is no pivot row, since it is of zero length */ + pivot_row = EMPTY ; + ASSERT (pivot_row_length == 0) ; + } + ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ; + + /* === Approximate degree computation =============================== */ + + /* Here begins the computation of the approximate degree. The column */ + /* score is the sum of the pivot row "length", plus the size of the */ + /* set differences of each row in the column minus the pattern of the */ + /* pivot row itself. The column ("thickness") itself is also */ + /* excluded from the column score (we thus use an approximate */ + /* external degree). */ + + /* The time taken by the following code (compute set differences, and */ + /* add them up) is proportional to the size of the data structure */ + /* being scanned - that is, the sum of the sizes of each column in */ + /* the pivot row. Thus, the amortized time to compute a column score */ + /* is proportional to the size of that column (where size, in this */ + /* context, is the column "length", or the number of row indices */ + /* in that column). The number of row indices in a column is */ + /* monotonically non-decreasing, from the length of the original */ + /* column on input to colamd. */ + + /* === Compute set differences ====================================== */ + + DEBUG3 (("** Computing set differences phase. **\n")) ; + + /* pivot row is currently dead - it will be revived later. */ + + DEBUG3 (("Pivot row: ")) ; + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + DEBUG3 (("Col: %d\n", col)) ; + + /* clear tags used to construct pivot row pattern */ + col_thickness = -Col [col].shared1.thickness ; + ASSERT (col_thickness > 0) ; + Col [col].shared1.thickness = col_thickness ; + + /* === Remove column from degree list =========================== */ + + cur_score = Col [col].shared2.score ; + prev_col = Col [col].shared3.prev ; + next_col = Col [col].shared4.degree_next ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (cur_score >= EMPTY) ; + if (prev_col == EMPTY) + { + head [cur_score] = next_col ; + } + else + { + Col [prev_col].shared4.degree_next = next_col ; + } + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = prev_col ; + } + + /* === Scan the column ========================================== */ + + cp = &A [Col [col].start] ; + cp_end = cp + Col [col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + continue ; + } + ASSERT (row != pivot_row) ; + set_difference = row_mark - tag_mark ; + /* check if the row has been seen yet */ + if (set_difference < 0) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + set_difference = Row [row].shared1.degree ; + } + /* subtract column thickness from this row's set difference */ + set_difference -= col_thickness ; + ASSERT (set_difference >= 0) ; + /* absorb this row if the set difference becomes zero */ + if (set_difference == 0) + { + DEBUG3 (("aggressive absorption. Row: %d\n", row)) ; + KILL_ROW (row) ; + } + else + { + /* save the new mark */ + Row [row].shared2.mark = set_difference + tag_mark ; + } + } + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k-pivot_row_degree, max_deg) ; +#endif /* NDEBUG */ + + /* === Add up set differences for each column ======================= */ + + DEBUG3 (("** Adding set differences phase. **\n")) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + hash = 0 ; + cur_score = 0 ; + cp = &A [Col [col].start] ; + /* compact the column */ + new_cp = cp ; + cp_end = cp + Col [col].length ; + + DEBUG4 (("Adding set diffs for Col: %d.\n", col)) ; + + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + ASSERT(row >= 0 && row < n_row) ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + continue ; + } + ASSERT (row_mark > tag_mark) ; + /* compact the column */ + *new_cp++ = row ; + /* compute hash function */ + hash += row ; + /* add set difference */ + cur_score += row_mark - tag_mark ; + /* integer overflow... */ + cur_score = MIN (cur_score, n_col) ; + } + + /* recompute the column's length */ + Col [col].length = (int) (new_cp - &A [Col [col].start]) ; + + /* === Further mass elimination ================================= */ + + if (Col [col].length == 0) + { + DEBUG4 (("further mass elimination. Col: %d\n", col)) ; + /* nothing left but the pivot row in this column */ + KILL_PRINCIPAL_COL (col) ; + pivot_row_degree -= Col [col].shared1.thickness ; + ASSERT (pivot_row_degree >= 0) ; + /* order it */ + Col [col].shared2.order = k ; + /* increment order count by column thickness */ + k += Col [col].shared1.thickness ; + } + else + { + /* === Prepare for supercolumn detection ==================== */ + + DEBUG4 (("Preparing supercol detection for Col: %d.\n", col)) ; + + /* save score so far */ + Col [col].shared2.score = cur_score ; + + /* add column to hash table, for supercolumn detection */ + hash %= n_col + 1 ; + + DEBUG4 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ; + ASSERT (hash <= n_col) ; + + head_column = head [hash] ; + if (head_column > EMPTY) + { + /* degree list "hash" is non-empty, use prev (shared3) of */ + /* first column in degree list as head of hash bucket */ + first_col = Col [head_column].shared3.headhash ; + Col [head_column].shared3.headhash = col ; + } + else + { + /* degree list "hash" is empty, use head as hash bucket */ + first_col = - (head_column + 2) ; + head [hash] = - (col + 2) ; + } + Col [col].shared4.hash_next = first_col ; + + /* save hash function in Col [col].shared3.hash */ + Col [col].shared3.hash = (int) hash ; + ASSERT (COL_IS_ALIVE (col)) ; + } + } + + /* The approximate external column degree is now computed. */ + + /* === Supercolumn detection ======================================== */ + + DEBUG3 (("** Supercolumn detection phase. **\n")) ; + + detect_super_cols ( + +#ifndef NDEBUG + n_col, Row, +#endif /* NDEBUG */ + + Col, A, head, pivot_row_start, pivot_row_length) ; + + /* === Kill the pivotal column ====================================== */ + + KILL_PRINCIPAL_COL (pivot_col) ; + + /* === Clear mark =================================================== */ + + tag_mark += (max_deg + 1) ; + if (tag_mark >= max_mark) + { + DEBUG2 (("clearing tag_mark\n")) ; + tag_mark = clear_mark (n_row, Row) ; + } + +#ifndef NDEBUG + DEBUG3 (("check3\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Finalize the new pivot row, and column scores ================ */ + + DEBUG3 (("** Finalize scores phase. **\n")) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + /* compact the pivot row */ + new_rp = rp ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + /* skip dead columns */ + if (COL_IS_DEAD (col)) + { + continue ; + } + *new_rp++ = col ; + /* add new pivot row to column */ + A [Col [col].start + (Col [col].length++)] = pivot_row ; + + /* retrieve score so far and add on pivot row's degree. */ + /* (we wait until here for this in case the pivot */ + /* row's degree was reduced due to mass elimination). */ + cur_score = Col [col].shared2.score + pivot_row_degree ; + + /* calculate the max possible score as the number of */ + /* external columns minus the 'k' value minus the */ + /* columns thickness */ + max_score = n_col - k - Col [col].shared1.thickness ; + + /* make the score the external degree of the union-of-rows */ + cur_score -= Col [col].shared1.thickness ; + + /* make sure score is less or equal than the max score */ + cur_score = MIN (cur_score, max_score) ; + ASSERT (cur_score >= 0) ; + + /* store updated score */ + Col [col].shared2.score = cur_score ; + + /* === Place column back in degree list ========================= */ + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (head [cur_score] >= EMPTY) ; + next_col = head [cur_score] ; + Col [col].shared4.degree_next = next_col ; + Col [col].shared3.prev = EMPTY ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = col ; + } + head [cur_score] = col ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, cur_score) ; + + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; +#endif /* NDEBUG */ + + /* === Resurrect the new pivot row ================================== */ + + if (pivot_row_degree > 0) + { + /* update pivot row length to reflect any cols that were killed */ + /* during super-col detection and mass elimination */ + Row [pivot_row].start = pivot_row_start ; + Row [pivot_row].length = (int) (new_rp - &A[pivot_row_start]) ; + Row [pivot_row].shared1.degree = pivot_row_degree ; + Row [pivot_row].shared2.mark = 0 ; + /* pivot row is no longer dead */ + } + } + + /* === All principal columns have now been ordered ====================== */ + + return (ngarbage) ; +} + + +/* ========================================================================== */ +/* === order_children ======================================================= */ +/* ========================================================================== */ + +/* + The find_ordering routine has ordered all of the principal columns (the + representatives of the supercolumns). The non-principal columns have not + yet been ordered. This routine orders those columns by walking up the + parent tree (a column is a child of the column which absorbed it). The + final permutation vector is then placed in p [0 ... n_col-1], with p [0] + being the first column, and p [n_col-1] being the last. It doesn't look + like it at first glance, but be assured that this routine takes time linear + in the number of columns. Although not immediately obvious, the time + taken by this routine is O (n_col), that is, linear in the number of + columns. Not user-callable. +*/ + +PRIVATE void order_children +( + /* === Parameters ======================================================= */ + + int n_col, /* number of columns of A */ + Colamd_Col Col [], /* of size n_col+1 */ + int p [] /* p [0 ... n_col-1] is the column permutation*/ +) +{ + /* === Local variables ================================================== */ + + int i ; /* loop counter for all columns */ + int c ; /* column index */ + int parent ; /* index of column's parent */ + int order ; /* column's order */ + + /* === Order each non-principal column ================================== */ + + for (i = 0 ; i < n_col ; i++) + { + /* find an un-ordered non-principal column */ + ASSERT (COL_IS_DEAD (i)) ; + if (!COL_IS_DEAD_PRINCIPAL (i) && Col [i].shared2.order == EMPTY) + { + parent = i ; + /* once found, find its principal parent */ + do + { + parent = Col [parent].shared1.parent ; + } while (!COL_IS_DEAD_PRINCIPAL (parent)) ; + + /* now, order all un-ordered non-principal columns along path */ + /* to this parent. collapse tree at the same time */ + c = i ; + /* get order of parent */ + order = Col [parent].shared2.order ; + + do + { + ASSERT (Col [c].shared2.order == EMPTY) ; + + /* order this column */ + Col [c].shared2.order = order++ ; + /* collaps tree */ + Col [c].shared1.parent = parent ; + + /* get immediate parent of this column */ + c = Col [c].shared1.parent ; + + /* continue until we hit an ordered column. There are */ + /* guarranteed not to be anymore unordered columns */ + /* above an ordered column */ + } while (Col [c].shared2.order == EMPTY) ; + + /* re-order the super_col parent to largest order for this group */ + Col [parent].shared2.order = order ; + } + } + + /* === Generate the permutation ========================================= */ + + for (c = 0 ; c < n_col ; c++) + { + p [Col [c].shared2.order] = c ; + } +} + + +/* ========================================================================== */ +/* === detect_super_cols ==================================================== */ +/* ========================================================================== */ + +/* + Detects supercolumns by finding matches between columns in the hash buckets. + Check amongst columns in the set A [row_start ... row_start + row_length-1]. + The columns under consideration are currently *not* in the degree lists, + and have already been placed in the hash buckets. + + The hash bucket for columns whose hash function is equal to h is stored + as follows: + + if head [h] is >= 0, then head [h] contains a degree list, so: + + head [h] is the first column in degree bucket h. + Col [head [h]].headhash gives the first column in hash bucket h. + + otherwise, the degree list is empty, and: + + -(head [h] + 2) is the first column in hash bucket h. + + For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous + column" pointer. Col [c].shared3.hash is used instead as the hash number + for that column. The value of Col [c].shared4.hash_next is the next column + in the same hash bucket. + + Assuming no, or "few" hash collisions, the time taken by this routine is + linear in the sum of the sizes (lengths) of each column whose score has + just been computed in the approximate degree computation. + Not user-callable. +*/ + +PRIVATE void detect_super_cols +( + /* === Parameters ======================================================= */ + +#ifndef NDEBUG + /* these two parameters are only needed when debugging is enabled: */ + int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ +#endif /* NDEBUG */ + + Colamd_Col Col [], /* of size n_col+1 */ + int A [], /* row indices of A */ + int head [], /* head of degree lists and hash buckets */ + int row_start, /* pointer to set of columns to check */ + int row_length /* number of columns to check */ +) +{ + /* === Local variables ================================================== */ + + int hash ; /* hash value for a column */ + int *rp ; /* pointer to a row */ + int c ; /* a column index */ + int super_c ; /* column index of the column to absorb into */ + int *cp1 ; /* column pointer for column super_c */ + int *cp2 ; /* column pointer for column c */ + int length ; /* length of column super_c */ + int prev_c ; /* column preceding c in hash bucket */ + int i ; /* loop counter */ + int *rp_end ; /* pointer to the end of the row */ + int col ; /* a column index in the row to check */ + int head_column ; /* first column in hash bucket or degree list */ + int first_col ; /* first column in hash bucket */ + + /* === Consider each column in the row ================================== */ + + rp = &A [row_start] ; + rp_end = rp + row_length ; + while (rp < rp_end) + { + col = *rp++ ; + if (COL_IS_DEAD (col)) + { + continue ; + } + + /* get hash number for this column */ + hash = Col [col].shared3.hash ; + ASSERT (hash <= n_col) ; + + /* === Get the first column in this hash bucket ===================== */ + + head_column = head [hash] ; + if (head_column > EMPTY) + { + first_col = Col [head_column].shared3.headhash ; + } + else + { + first_col = - (head_column + 2) ; + } + + /* === Consider each column in the hash bucket ====================== */ + + for (super_c = first_col ; super_c != EMPTY ; + super_c = Col [super_c].shared4.hash_next) + { + ASSERT (COL_IS_ALIVE (super_c)) ; + ASSERT (Col [super_c].shared3.hash == hash) ; + length = Col [super_c].length ; + + /* prev_c is the column preceding column c in the hash bucket */ + prev_c = super_c ; + + /* === Compare super_c with all columns after it ================ */ + + for (c = Col [super_c].shared4.hash_next ; + c != EMPTY ; c = Col [c].shared4.hash_next) + { + ASSERT (c != super_c) ; + ASSERT (COL_IS_ALIVE (c)) ; + ASSERT (Col [c].shared3.hash == hash) ; + + /* not identical if lengths or scores are different */ + if (Col [c].length != length || + Col [c].shared2.score != Col [super_c].shared2.score) + { + prev_c = c ; + continue ; + } + + /* compare the two columns */ + cp1 = &A [Col [super_c].start] ; + cp2 = &A [Col [c].start] ; + + for (i = 0 ; i < length ; i++) + { + /* the columns are "clean" (no dead rows) */ + ASSERT (ROW_IS_ALIVE (*cp1)) ; + ASSERT (ROW_IS_ALIVE (*cp2)) ; + /* row indices will same order for both supercols, */ + /* no gather scatter nessasary */ + if (*cp1++ != *cp2++) + { + break ; + } + } + + /* the two columns are different if the for-loop "broke" */ + if (i != length) + { + prev_c = c ; + continue ; + } + + /* === Got it! two columns are identical =================== */ + + ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ; + + Col [super_c].shared1.thickness += Col [c].shared1.thickness ; + Col [c].shared1.parent = super_c ; + KILL_NON_PRINCIPAL_COL (c) ; + /* order c later, in order_children() */ + Col [c].shared2.order = EMPTY ; + /* remove c from hash bucket */ + Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ; + } + } + + /* === Empty this hash bucket ======================================= */ + + if (head_column > EMPTY) + { + /* corresponding degree list "hash" is not empty */ + Col [head_column].shared3.headhash = EMPTY ; + } + else + { + /* corresponding degree list "hash" is empty */ + head [hash] = EMPTY ; + } + } +} + + +/* ========================================================================== */ +/* === garbage_collection =================================================== */ +/* ========================================================================== */ + +/* + Defragments and compacts columns and rows in the workspace A. Used when + all avaliable memory has been used while performing row merging. Returns + the index of the first free position in A, after garbage collection. The + time taken by this routine is linear is the size of the array A, which is + itself linear in the number of nonzeros in the input matrix. + Not user-callable. +*/ + +PRIVATE int garbage_collection /* returns the new value of pfree */ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows */ + int n_col, /* number of columns */ + Colamd_Row Row [], /* row info */ + Colamd_Col Col [], /* column info */ + int A [], /* A [0 ... Alen-1] holds the matrix */ + int *pfree /* &A [0] ... pfree is in use */ +) +{ + /* === Local variables ================================================== */ + + int *psrc ; /* source pointer */ + int *pdest ; /* destination pointer */ + int j ; /* counter */ + int r ; /* a row index */ + int c ; /* a column index */ + int length ; /* length of a row or column */ + +#ifndef NDEBUG + int debug_rows ; + DEBUG2 (("Defrag..\n")) ; + for (psrc = &A[0] ; psrc < pfree ; psrc++) ASSERT (*psrc >= 0) ; + debug_rows = 0 ; +#endif /* NDEBUG */ + + /* === Defragment the columns =========================================== */ + + pdest = &A[0] ; + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + psrc = &A [Col [c].start] ; + + /* move and compact the column */ + ASSERT (pdest <= psrc) ; + Col [c].start = (int) (pdest - &A [0]) ; + length = Col [c].length ; + for (j = 0 ; j < length ; j++) + { + r = *psrc++ ; + if (ROW_IS_ALIVE (r)) + { + *pdest++ = r ; + } + } + Col [c].length = (int) (pdest - &A [Col [c].start]) ; + } + } + + /* === Prepare to defragment the rows =================================== */ + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + if (Row [r].length == 0) + { + /* this row is of zero length. cannot compact it, so kill it */ + DEBUG3 (("Defrag row kill\n")) ; + KILL_ROW (r) ; + } + else + { + /* save first column index in Row [r].shared2.first_column */ + psrc = &A [Row [r].start] ; + Row [r].shared2.first_column = *psrc ; + ASSERT (ROW_IS_ALIVE (r)) ; + /* flag the start of the row with the one's complement of row */ + *psrc = ONES_COMPLEMENT (r) ; + +#ifndef NDEBUG + debug_rows++ ; +#endif /* NDEBUG */ + + } + } + } + + /* === Defragment the rows ============================================== */ + + psrc = pdest ; + while (psrc < pfree) + { + /* find a negative number ... the start of a row */ + if (*psrc++ < 0) + { + psrc-- ; + /* get the row index */ + r = ONES_COMPLEMENT (*psrc) ; + ASSERT (r >= 0 && r < n_row) ; + /* restore first column index */ + *psrc = Row [r].shared2.first_column ; + ASSERT (ROW_IS_ALIVE (r)) ; + + /* move and compact the row */ + ASSERT (pdest <= psrc) ; + Row [r].start = (int) (pdest - &A [0]) ; + length = Row [r].length ; + for (j = 0 ; j < length ; j++) + { + c = *psrc++ ; + if (COL_IS_ALIVE (c)) + { + *pdest++ = c ; + } + } + Row [r].length = (int) (pdest - &A [Row [r].start]) ; + +#ifndef NDEBUG + debug_rows-- ; +#endif /* NDEBUG */ + + } + } + /* ensure we found all the rows */ + ASSERT (debug_rows == 0) ; + + /* === Return the new value of pfree ==================================== */ + + return ((int) (pdest - &A [0])) ; +} + + +/* ========================================================================== */ +/* === clear_mark =========================================================== */ +/* ========================================================================== */ + +/* + Clears the Row [].shared2.mark array, and returns the new tag_mark. + Return value is the new tag_mark. Not user-callable. +*/ + +PRIVATE int clear_mark /* return the new value for tag_mark */ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows in A */ + Colamd_Row Row [] /* Row [0 ... n_row-1].shared2.mark is set to zero */ +) +{ + /* === Local variables ================================================== */ + + int r ; + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + Row [r].shared2.mark = 0 ; + } + } + return (1) ; +} + + +/* ========================================================================== */ +/* === print_report ========================================================= */ +/* ========================================================================== */ + +PRIVATE void print_report +( + char *method, + int stats [COLAMD_STATS] +) +{ + + int i1, i2, i3 ; + + if (!stats) + { + PRINTF ("%s: No statistics available.\n", method) ; + return ; + } + + i1 = stats [COLAMD_INFO1] ; + i2 = stats [COLAMD_INFO2] ; + i3 = stats [COLAMD_INFO3] ; + + if (stats [COLAMD_STATUS] >= 0) + { + PRINTF ("%s: OK. ", method) ; + } + else + { + PRINTF ("%s: ERROR. ", method) ; + } + + switch (stats [COLAMD_STATUS]) + { + + case COLAMD_OK_BUT_JUMBLED: + + PRINTF ("Matrix has unsorted or duplicate row indices.\n") ; + + PRINTF ("%s: number of duplicate or out-of-order row indices: %d\n", + method, i3) ; + + PRINTF ("%s: last seen duplicate or out-of-order row index: %d\n", + method, INDEX (i2)) ; + + PRINTF ("%s: last seen in column: %d", + method, INDEX (i1)) ; + + /* no break - fall through to next case instead */ + + case COLAMD_OK: + + PRINTF ("\n") ; + + PRINTF ("%s: number of dense or empty rows ignored: %d\n", + method, stats [COLAMD_DENSE_ROW]) ; + + PRINTF ("%s: number of dense or empty columns ignored: %d\n", + method, stats [COLAMD_DENSE_COL]) ; + + PRINTF ("%s: number of garbage collections performed: %d\n", + method, stats [COLAMD_DEFRAG_COUNT]) ; + break ; + + case COLAMD_ERROR_A_not_present: + + PRINTF ("Array A (row indices of matrix) not present.\n") ; + break ; + + case COLAMD_ERROR_p_not_present: + + PRINTF ("Array p (column pointers for matrix) not present.\n") ; + break ; + + case COLAMD_ERROR_nrow_negative: + + PRINTF ("Invalid number of rows (%d).\n", i1) ; + break ; + + case COLAMD_ERROR_ncol_negative: + + PRINTF ("Invalid number of columns (%d).\n", i1) ; + break ; + + case COLAMD_ERROR_nnz_negative: + + PRINTF ("Invalid number of nonzero entries (%d).\n", i1) ; + break ; + + case COLAMD_ERROR_p0_nonzero: + + PRINTF ("Invalid column pointer, p [0] = %d, must be zero.\n", i1) ; + break ; + + case COLAMD_ERROR_A_too_small: + + PRINTF ("Array A too small.\n") ; + PRINTF (" Need Alen >= %d, but given only Alen = %d.\n", + i1, i2) ; + break ; + + case COLAMD_ERROR_col_length_negative: + + PRINTF + ("Column %d has a negative number of nonzero entries (%d).\n", + INDEX (i1), i2) ; + break ; + + case COLAMD_ERROR_row_index_out_of_bounds: + + PRINTF + ("Row index (row %d) out of bounds (%d to %d) in column %d.\n", + INDEX (i2), INDEX (0), INDEX (i3-1), INDEX (i1)) ; + break ; + + case COLAMD_ERROR_out_of_memory: + + PRINTF ("Out of memory.\n") ; + break ; + + case COLAMD_ERROR_internal_error: + + /* if this happens, there is a bug in the code */ + PRINTF + ("Internal error! Please contact authors (davis@cise.ufl.edu).\n") ; + break ; + } +} + + + + +/* ========================================================================== */ +/* === colamd debugging routines ============================================ */ +/* ========================================================================== */ + +/* When debugging is disabled, the remainder of this file is ignored. */ + +#ifndef NDEBUG + + +/* ========================================================================== */ +/* === debug_structures ===================================================== */ +/* ========================================================================== */ + +/* + At this point, all empty rows and columns are dead. All live columns + are "clean" (containing no dead rows) and simplicial (no supercolumns + yet). Rows may contain dead columns, but all live rows contain at + least one live column. +*/ + +PRIVATE void debug_structures +( + /* === Parameters ======================================================= */ + + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int n_col2 +) +{ + /* === Local variables ================================================== */ + + int i ; + int c ; + int *cp ; + int *cp_end ; + int len ; + int score ; + int r ; + int *rp ; + int *rp_end ; + int deg ; + + /* === Check A, Row, and Col ============================================ */ + + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + len = Col [c].length ; + score = Col [c].shared2.score ; + DEBUG4 (("initial live col %5d %5d %5d\n", c, len, score)) ; + ASSERT (len > 0) ; + ASSERT (score >= 0) ; + ASSERT (Col [c].shared1.thickness == 1) ; + cp = &A [Col [c].start] ; + cp_end = cp + len ; + while (cp < cp_end) + { + r = *cp++ ; + ASSERT (ROW_IS_ALIVE (r)) ; + } + } + else + { + i = Col [c].shared2.order ; + ASSERT (i >= n_col2 && i < n_col) ; + } + } + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + i = 0 ; + len = Row [r].length ; + deg = Row [r].shared1.degree ; + ASSERT (len > 0) ; + ASSERT (deg > 0) ; + rp = &A [Row [r].start] ; + rp_end = rp + len ; + while (rp < rp_end) + { + c = *rp++ ; + if (COL_IS_ALIVE (c)) + { + i++ ; + } + } + ASSERT (i > 0) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_deg_lists ====================================================== */ +/* ========================================================================== */ + +/* + Prints the contents of the degree lists. Counts the number of columns + in the degree list and compares it to the total it should have. Also + checks the row degrees. +*/ + +PRIVATE void debug_deg_lists +( + /* === Parameters ======================================================= */ + + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int head [], + int min_score, + int should, + int max_deg +) +{ + /* === Local variables ================================================== */ + + int deg ; + int col ; + int have ; + int row ; + + /* === Check the degree lists =========================================== */ + + if (n_col > 10000 && colamd_debug <= 0) + { + return ; + } + have = 0 ; + DEBUG4 (("Degree lists: %d\n", min_score)) ; + for (deg = 0 ; deg <= n_col ; deg++) + { + col = head [deg] ; + if (col == EMPTY) + { + continue ; + } + DEBUG4 (("%d:", deg)) ; + while (col != EMPTY) + { + DEBUG4 ((" %d", col)) ; + have += Col [col].shared1.thickness ; + ASSERT (COL_IS_ALIVE (col)) ; + col = Col [col].shared4.degree_next ; + } + DEBUG4 (("\n")) ; + } + DEBUG4 (("should %d have %d\n", should, have)) ; + ASSERT (should == have) ; + + /* === Check the row degrees ============================================ */ + + if (n_row > 10000 && colamd_debug <= 0) + { + return ; + } + for (row = 0 ; row < n_row ; row++) + { + if (ROW_IS_ALIVE (row)) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_mark =========================================================== */ +/* ========================================================================== */ + +/* + Ensures that the tag_mark is less that the maximum and also ensures that + each entry in the mark array is less than the tag mark. +*/ + +PRIVATE void debug_mark +( + /* === Parameters ======================================================= */ + + int n_row, + Colamd_Row Row [], + int tag_mark, + int max_mark +) +{ + /* === Local variables ================================================== */ + + int r ; + + /* === Check the Row marks ============================================== */ + + ASSERT (tag_mark > 0 && tag_mark <= max_mark) ; + if (n_row > 10000 && colamd_debug <= 0) + { + return ; + } + for (r = 0 ; r < n_row ; r++) + { + ASSERT (Row [r].shared2.mark < tag_mark) ; + } +} + + +/* ========================================================================== */ +/* === debug_matrix ========================================================= */ +/* ========================================================================== */ + +/* + Prints out the contents of the columns and the rows. +*/ + +PRIVATE void debug_matrix +( + /* === Parameters ======================================================= */ + + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [] +) +{ + /* === Local variables ================================================== */ + + int r ; + int c ; + int *rp ; + int *rp_end ; + int *cp ; + int *cp_end ; + + /* === Dump the rows and columns of the matrix ========================== */ + + if (colamd_debug < 3) + { + return ; + } + DEBUG3 (("DUMP MATRIX:\n")) ; + for (r = 0 ; r < n_row ; r++) + { + DEBUG3 (("Row %d alive? %d\n", r, ROW_IS_ALIVE (r))) ; + if (ROW_IS_DEAD (r)) + { + continue ; + } + DEBUG3 (("start %d length %d degree %d\n", + Row [r].start, Row [r].length, Row [r].shared1.degree)) ; + rp = &A [Row [r].start] ; + rp_end = rp + Row [r].length ; + while (rp < rp_end) + { + c = *rp++ ; + DEBUG4 ((" %d col %d\n", COL_IS_ALIVE (c), c)) ; + } + } + + for (c = 0 ; c < n_col ; c++) + { + DEBUG3 (("Col %d alive? %d\n", c, COL_IS_ALIVE (c))) ; + if (COL_IS_DEAD (c)) + { + continue ; + } + DEBUG3 (("start %d length %d shared1 %d shared2 %d\n", + Col [c].start, Col [c].length, + Col [c].shared1.thickness, Col [c].shared2.score)) ; + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + r = *cp++ ; + DEBUG4 ((" %d row %d\n", ROW_IS_ALIVE (r), r)) ; + } + } +} + +PRIVATE void colamd_get_debug +( + char *method +) +{ + colamd_debug = 0 ; /* no debug printing */ + + /* get "D" environment variable, which gives the debug printing level */ + if (getenv ("D")) + { + colamd_debug = atoi (getenv ("D")) ; + } + + DEBUG0 (("%s: debug version, D = %d (THIS WILL BE SLOW!)\n", + method, colamd_debug)) ; +} + +#endif /* NDEBUG */ + diff --git a/src/external/lpsolve/build/lp_solve/commonlib.c b/src/external/lpsolve/build/lp_solve/commonlib.c new file mode 100644 index 00000000..c53a7520 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/commonlib.c @@ -0,0 +1,997 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#include + +#if defined INTEGERTIME || defined CLOCKTIME || defined PosixTime +# include +#elif defined EnhTime +# include +#else +# include +#endif + +#include +#include +#ifdef WIN32 +# include /* Used in file search functions */ +#endif +#include +#include +#include +#include +#include "commonlib.h" + +#ifdef FORTIFY +# 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); + + /* Return the previous mask */ + return( u ); +} +#endif + +/* 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( *(LPSREAL *) current, *(LPSREAL *) 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. + 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. + 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, LPSREAL *weight, int size, int offset, MYBOOL unique) +{ + int i, ii, saveI; + LPSREAL 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); +} +LPSREAL sortREALByINT(LPSREAL *item, int *weight, int size, int offset, MYBOOL unique) +{ + int i, ii, saveW; + LPSREAL 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 */); +#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 + return((double)time(NULL)); +#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 LPSREAL values for the given index range */ +void blockWriteREAL(FILE *output, char *label, LPSREAL *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, LPSREAL *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]); + else + printf(" %2d:%12g", i, x[i]); + } + if(i % modulo != 0) printf("\n"); +} + + +void printmatUT( int size, int n, LPSREAL *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, LPSREAL *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 diff --git a/src/external/lpsolve/build/lp_solve/ini.c b/src/external/lpsolve/build/lp_solve/ini.c new file mode 100644 index 00000000..c157b9c8 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/ini.c @@ -0,0 +1,85 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#include +#include +#include + +#include "lp_lib.h" + +#include "ini.h" + +FILE *ini_create(char *filename) +{ + FILE *fp; + + fp = fopen(filename, "w"); + + return(fp); +} + +FILE *ini_open(char *filename) +{ + FILE *fp; + + fp = fopen(filename, "r"); + + return(fp); +} + +void ini_writecomment(FILE *fp, char *comment) +{ + fprintf(fp, "; %s\n", comment); +} + +void ini_writeheader(FILE *fp, char *header, int addnewline) +{ + if((addnewline) && (ftell(fp) > 0)) + fputs("\n", fp); + fprintf(fp, "[%s]\n", header); +} + +void ini_writedata(FILE *fp, char *name, char *data) +{ + if(name != NULL) + fprintf(fp, "%s=%s\n", name, data); + else + fprintf(fp, "%s\n", data); +} + +int ini_readdata(FILE *fp, char *data, int szdata, int withcomment) +{ + int l; + char *ptr; + + if(fgets(data, szdata, fp) == NULL) + return(0); + + if(!withcomment) { + ptr = strchr(data, ';'); + if(ptr != NULL) + *ptr = 0; + } + + l = (int) strlen(data); + while((l > 0) && (isspace(data[l - 1]))) + l--; + data[l] = 0; + if((l >= 2) && (data[0] == '[') && (data[l - 1] == ']')) { + memcpy(data, data + 1, l - 2); + data[l - 2] = 0; + return(1); + } + return(2); +} + +void ini_close(FILE *fp) +{ + fclose(fp); +} diff --git a/src/external/lpsolve/build/lp_solve/liblp_solve.a b/src/external/lpsolve/build/lp_solve/liblp_solve.a new file mode 100644 index 00000000..99d8be7f Binary files /dev/null and b/src/external/lpsolve/build/lp_solve/liblp_solve.a differ diff --git a/src/external/lpsolve/build/lp_solve/lp_BFP1.c b/src/external/lpsolve/build/lp_solve/lp_BFP1.c new file mode 100644 index 00000000..46fc57dc --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_BFP1.c @@ -0,0 +1,206 @@ + +/* 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/external/lpsolve/build/lp_solve/lp_BFP2.c b/src/external/lpsolve/build/lp_solve/lp_BFP2.c new file mode 100644 index 00000000..d5c73776 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_BFP2.c @@ -0,0 +1,184 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +/* 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/external/lpsolve/build/lp_solve/lp_Hash.c b/src/external/lpsolve/build/lp_solve/lp_Hash.c new file mode 100644 index 00000000..f380acc9 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_Hash.c @@ -0,0 +1,246 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/build/lp_solve/lp_LUSOL.c b/src/external/lpsolve/build/lp_solve/lp_LUSOL.c new file mode 100644 index 00000000..4db35454 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_LUSOL.c @@ -0,0 +1,735 @@ + +/* Modularized simplex basis factorization module - w/interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lusol.h, lp_lib.h, myblas.h + + Release notes: + v2.0.0 1 March 2004 First implementation of the LUSOL v2.0 C translation. + v2.0.1 1 April 2004 Added singularity recovery and fast/reuse update logic. + v2.0.2 23 May 2004 Moved mustrefact() function into the BFP structure. + v2.0.3 5 September 2004 Reworked pivot threshold tightening logic and default + values. + v2.1.0 18 June 2005 Made changes to allow for "pure" factorization; + i.e. without the objective function included. + + ---------------------------------------------------------------------------------- */ + +/* Generic include libraries */ +#include +#include +#include "lp_lib.h" + +/* Include libraries for this factorization system */ +#include "myblas.h" +#include "commonlib.h" +#include "lp_LUSOL.h" +#include "lusol.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +/* Include routines common to factorization engine implementations */ +#include "lp_BFP1.c" +#include "lp_BFP2.c" + + +/* MUST MODIFY */ +char * BFP_CALLMODEL bfp_name(void) +{ + return( "LUSOL v2.2.1.0" ); +} + + +/* MUST MODIFY */ +MYBOOL BFP_CALLMODEL bfp_resize(lprec *lp, int newsize) +{ + INVrec *lu; + + lu = lp->invB; + + /* Increment dimensionality since we put the objective row at the top */ + newsize = newsize + bfp_rowoffset(lp); + lu->dimalloc = newsize; + + /* Allocate index tracker arrays, LU matrices and various work vectors */ + if(!allocREAL(lp, &(lu->value), newsize+MATINDEXBASE, AUTOMATIC)) + return( FALSE ); + + /* Data specific to the factorization engine */ + if(lu->LUSOL != NULL) { + if(newsize > 0 || 1) + LUSOL_sizeto(lu->LUSOL, newsize, newsize, 0); + else { + LUSOL_free(lu->LUSOL); + lu->LUSOL = NULL; + } + } + else if(newsize > 0 || 1) { + int asize; + LPSREAL bsize; + + lu->LUSOL = LUSOL_create(NULL, 0, LUSOL_PIVMOD_TPP, bfp_pivotmax(lp)*0); + +#if 1 + lu->LUSOL->luparm[LUSOL_IP_ACCELERATION] = LUSOL_AUTOORDER; + lu->LUSOL->parmlu[LUSOL_RP_SMARTRATIO] = 0.50; +#endif +#if 0 + lu->timed_refact = DEF_TIMEDREFACT; +#else + lu->timed_refact = FALSE; +#endif + + /* The following adjustments seem necessary to make the really tough NETLIB + models perform reliably and still performant (e.g. cycle.mps) */ +#if 0 + lu->LUSOL->parmlu[LUSOL_RP_SMALLDIAG_U] = + lu->LUSOL->parmlu[LUSOL_RP_EPSDIAG_U] = lp->epsprimal; +#endif +#if 0 + lu->LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE] = lp->epsvalue; +#endif + +#if 1 + LUSOL_setpivotmodel(lu->LUSOL, LUSOL_PIVMOD_NOCHANGE, LUSOL_PIVTOL_SLIM); +#else + LUSOL_setpivotmodel(lu->LUSOL, LUSOL_PIVMOD_NOCHANGE, LUSOL_PIVTOL_TIGHT); +#endif + +#ifdef LUSOL_UseBLAS +/* if(fileSearchPath("PATH", "myBLAS.DLL", NULL) && load_BLAS("myBLAS")) */ + if(is_nativeBLAS() && load_BLAS(libnameBLAS)) + lp->report(lp, NORMAL, "Optimized BLAS was successfully loaded for bfp_LUSOL.\n"); +#endif + + /* Try to minimize memory allocation if we have a large number of unit columns */ + bsize = (LPSREAL) lp->get_nonzeros(lp); + if(newsize > lp->columns) + bsize += newsize; + else + bsize = bsize/lp->columns*newsize; + /* Add a "reasonable" delta to allow for B and associated factorizations + that are denser than average; this makes reallocations less frequent. + Values between 1.2 and 1.5 appear to be reasonable. */ + asize = (int) (bsize*MAX_DELTAFILLIN*1.3333); + if(!LUSOL_sizeto(lu->LUSOL, newsize, newsize, asize)) + return( FALSE ); + } + lu->dimcount = newsize; + return( TRUE ); +} + + +/* MUST MODIFY */ +void BFP_CALLMODEL bfp_free(lprec *lp) +{ + INVrec *lu; + + lu = lp->invB; + if(lu == NULL) + return; + + /* General arrays */ + FREE(lu->opts); + FREE(lu->value); + + /* Data specific to the factorization engine */ + LUSOL_free(lu->LUSOL); + + FREE(lu); + lp->invB = NULL; +} + + +/* MUST MODIFY */ +int BFP_CALLMODEL bfp_nonzeros(lprec *lp, MYBOOL maximum) +{ + INVrec *lu; + + lu = lp->invB; + if(maximum == TRUE) + return(lu->max_LUsize); + else if(maximum == AUTOMATIC) + return(lu->max_Bsize); + else + return(lu->LUSOL->luparm[LUSOL_IP_NONZEROS_L0]+lu->LUSOL->luparm[LUSOL_IP_NONZEROS_U0]); +/* return(lu->LUSOL->luparm[LUSOL_IP_NONZEROS_ROW]); */ +} + + +/* MUST MODIFY (or ignore) */ +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(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; + else if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TRP) + mem += sizeof(LPSREAL) * LUSOL->maxn; + if(!LUSOL->luparm[LUSOL_IP_KEEPLU]) + mem += sizeof(LPSREAL) * LUSOL->maxn; + return( mem ); +} + + +/* MUST MODIFY */ +int BFP_CALLMODEL bfp_preparefactorization(lprec *lp) +{ + INVrec *lu = lp->invB; + + /* Finish any outstanding business */ + if(lu->is_dirty == AUTOMATIC) + lp->bfp_finishfactorization(lp); + + /* Clear or resize the existing LU matrices - specific for the factorization engine */ + LUSOL_clear(lu->LUSOL, TRUE); + if(lu->dimcount != lp->rows + bfp_rowoffset(lp)) + lp->bfp_resize(lp, lp->rows); + + /* Reset additional indicators */ + lp->bfp_updaterefactstats(lp); + lu->col_pos = 0; + + return(0); + +} + + +/* LOCAL HELPER ROUTINE - Replace a basis column with corresponding slack */ +int bfp_LUSOLsetcolumn(lprec *lp, int posnr, int colnr) +{ + int nz, inform; + + 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 ); +} + + +/* LOCAL HELPER ROUTINE - force the basis to be the identity matrix */ +int bfp_LUSOLidentity(lprec *lp, int *rownum) +{ + int i, nz; + INVrec *invB = lp->invB; + + /* Reset the factorization engine */ + LUSOL_clear(invB->LUSOL, TRUE); + + /* Add the basis columns */ + lp->invB->set_Bidentity = TRUE; + for(i = 1; i <= invB->dimcount; i++) { + nz = lp->get_basiscolumn(lp, i, rownum, invB->value); + LUSOL_loadColumn(invB->LUSOL, rownum, i, invB->value, nz, 0); + } + lp->invB->set_Bidentity = FALSE; + + /* Factorize */ + i = LUSOL_factorize(invB->LUSOL); + + return( i ); +} + + +/* LOCAL HELPER ROUTINE */ +int bfp_LUSOLfactorize(lprec *lp, MYBOOL *usedpos, int *rownum, int *singular) +{ + int i, j, nz, deltarows = bfp_rowoffset(lp); + INVrec *invB = lp->invB; + + /* Handle normal, presumed nonsingular case */ + if(singular == NULL) { + + /* Optionally do a symbolic minimum degree ordering; + not that slack variables should not be processed */ +/*#define UsePreprocessMDO*/ +#ifdef UsePreprocessMDO + int *mdo; + mdo = lp->bfp_createMDO(lp, usedpos, lp->rows, TRUE); + if(mdo != NULL) { + for(i = 1; i <= lp->rows; i++) + lp->set_basisvar(lp, i, mdo[i]); + FREE(mdo); + } +#endif + + /* Reset the factorization engine */ + LUSOL_clear(invB->LUSOL, TRUE); + + /* Add the basis columns in the original order */ + for(i = 1; i <= invB->dimcount; i++) { + nz = lp->get_basiscolumn(lp, i, rownum, invB->value); + LUSOL_loadColumn(invB->LUSOL, rownum, i, invB->value, nz, 0); + if((i > deltarows) && (lp->var_basic[i-deltarows] > lp->rows)) + lp->invB->user_colcount++; + } + + /* Factorize */ + i = LUSOL_factorize(invB->LUSOL); + } + + /* Handle case where a column may be singular */ + else { + LLrec *map; + + /* Reset the factorization engine */ + i = bfp_LUSOLidentity(lp, rownum); + + /* Build map of available columns */ + nz = createLink(lp->rows, &map, NULL); + for(i = 1; i <= lp->rows; i++) { + if(lp->var_basic[i] <= lp->rows) + removeLink(map, i); + } + + /* Rebuild the basis, column by column, while skipping slack columns */ + j = firstActiveLink(map); + for(i = 1; i <= lp->rows; i++) { + if(lp->var_basic[i] <= lp->rows) + continue; + nz = bfp_LUSOLsetcolumn(lp, j+deltarows, lp->var_basic[i]); + if(nz == LUSOL_INFORM_LUSUCCESS) + lp->invB->user_colcount++; + else { + nz = bfp_LUSOLsetcolumn(lp, j+deltarows, i); + lp->set_basisvar(lp, i, i); + } + j = nextActiveLink(map, j); + } + + /* Sort the basis list */ + MEMCOPY(rownum, lp->var_basic, lp->rows+1); + sortByINT(lp->var_basic, rownum, lp->rows, 1, TRUE); + + } + + return( i ); +} +/* LOCAL HELPER ROUTINE */ +void bfp_LUSOLtighten(lprec *lp) +{ + int infolevel = DETAILED; + + switch(LUSOL_tightenpivot(lp->invB->LUSOL)) { + 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)); + 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 */ + +static 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 */ + +/* MUST MODIFY */ +int BFP_CALLMODEL bfp_factorize(lprec *lp, int uservars, int Bsize, MYBOOL *usedpos, MYBOOL final) +{ + int kcol, inform, + *rownum = NULL, + singularities = 0, + dimsize = lp->invB->dimcount; + LUSOLrec *LUSOL = lp->invB->LUSOL; + + /* Set dimensions and create work array */ + SETMAX(lp->invB->max_Bsize, Bsize+(1+lp->rows-uservars)); + kcol = lp->invB->dimcount; + LUSOL->m = kcol; + LUSOL->n = kcol; + allocINT(lp, &rownum, kcol+1, FALSE); + + /* Check if the refactorization frequency is low; + tighten pivot thresholds if appropriate */ + inform = lp->bfp_pivotcount(lp); + if(!final && /* No solution update-based refactorization */ + !lp->invB->force_refact && /* No sparsity-based refactorization */ + !lp->is_action(lp->spx_action, + ACTION_TIMEDREINVERT) && /* No optimal time-based refactorization */ + (inform > 5) && (inform < 0.25*lp->bfp_pivotmax(lp))) + bfp_LUSOLtighten(lp); + + + /* Reload B and factorize */ + inform = bfp_LUSOLfactorize(lp, usedpos, rownum, NULL); + + /* Do some checks */ +#ifdef Paranoia + if(uservars != lp->invB->user_colcount) { + lp->report(lp, SEVERE, "bfp_factorize: User variable count reconciliation failed\n"); + return( singularities ); + } +#endif + + /* Check result and do further remedial action if necessary */ + if(inform != LUSOL_INFORM_LUSUCCESS) { + int singularcols, + replacedcols = 0; + LPSREAL hold; + + /* Make sure we do not tighten factorization pivot criteria too often, and simply + accept the substitution of slack columns into the basis */ + if((lp->invB->num_singular+1) % TIGHTENAFTER == 0) + bfp_LUSOLtighten(lp); + + /* Try to restore a non-singular basis by substituting singular columns with slacks */ + while((inform == LUSOL_INFORM_LUSINGULAR) && (replacedcols < dimsize)) { + int iLeave, jLeave, iEnter; + MYBOOL isfixed; + + singularities++; + singularcols = LUSOL->luparm[LUSOL_IP_SINGULARITIES]; + hold = (LPSREAL) 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); + + /* Find the failing / singular column(s) and make slack substitutions */ + for(kcol = 1; kcol <= singularcols; kcol++) { + + /* Determine leaving and entering columns. */ + iLeave = LUSOL_getSingularity(LUSOL, kcol); /* This is the singular column as natural index */ + iEnter = iLeave; /* This is the target replacement slack */ +#if 1 + iEnter = LUSOL->iqinv[iEnter]; + iEnter = LUSOL->ip[iEnter]; +#endif + iLeave-= bfp_rowextra(lp); /* This is the original B column/basis index */ + jLeave = lp->var_basic[iLeave]; /* This is the IA column index in lp_solve */ + + /* Express the slack index in original lp_solve [1..rows] reference and check validity */ + /* if(B4 != NULL) iEnter = B4->B4_row[iEnter]; v6 FUNCTIONALITY */ + iEnter -= bfp_rowextra(lp); + if(lp->is_basic[iEnter]) { + lp->report(lp, DETAILED, "bfp_factorize: Replacement slack %d is already basic!\n", iEnter); + + /* See if we can find a good alternative slack variable to enter */ + iEnter = 0; + for(inform = 1; inform <= lp->rows; inform++) + if(!lp->is_basic[inform]) { + if((iEnter == 0) || (lp->upbo[inform] > lp->upbo[iEnter])) { + iEnter = inform; + if(my_infinite(lp, lp->upbo[iEnter])) + break; + } + } + if(iEnter == 0) { + lp->report(lp, SEVERE, "bfp_factorize: Could not find replacement slack variable!\n"); + break; + } + } + + /* We should update bound states for both the entering and leaving variables. + Note that this may cause (primal or dual) infeasibility, but I assume that + lp_solve traps this and takes necessary corrective action. */ + isfixed = is_fixedvar(lp, iEnter); + if(isfixed) + lp->fixedvars++; + hold = lp->upbo[jLeave]; + lp->is_lower[jLeave] = isfixed || (fabs(hold)>=lp->infinite) || (lp->rhs[iLeave] < hold); + lp->is_lower[iEnter] = TRUE; + + /* Do the basis replacement */ + lp->set_basisvar(lp, iLeave, iEnter); + + } + + /* Refactorize with slack substitutions */ + inform = bfp_LUSOLfactorize(lp, NULL, rownum, NULL); + replacedcols += singularcols; + } + + /* Check if we had a fundamental problem */ + if(singularities >= dimsize) { + lp->report(lp, IMPORTANT, "bfp_factorize: LUSOL was unable to recover from a singular basis\n"); + lp->spx_status = NUMFAILURE; + } + } + + /* Clean up before returning */ + FREE(rownum); + + /* Update statistics */ + /* SETMAX(lp->invB->max_Bsize, (*Bsize)); */ + lp->invB->num_singular += singularities; /* The total number of singular updates */ + + return( singularities ); +} + +/* MUST MODIFY */ +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, kcol, deltarows = bfp_rowoffset(lp); + LPSREAL DIAG, VNORM; + INVrec *lu = lp->invB; + LUSOLrec *LUSOL = lu->LUSOL; + + if(!lu->is_dirty) + return( FALSE ); + if(lu->is_dirty != AUTOMATIC) + lu->is_dirty = FALSE; + + /* Perform the update */ + k = lu->col_pos+deltarows; + lu->num_pivots++; + if(lu->col_leave > lu->dimcount-deltarows) + lu->user_colcount--; + if(lu->col_enter > lu->dimcount-deltarows) + lu->user_colcount++; + 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; + for(i = 1, temp++; i <= lp->rows+deltarows; i++, temp++) + if(*temp != 0) + *temp = -(*temp); + } + /* Execute the update using data prepared earlier */ + LU8RPC(LUSOL, LUSOL_UPDATE_OLDNONEMPTY, LUSOL_UPDATE_USEPREPARED, + k, NULL, NULL, &i, &DIAG, &VNORM); + } + else +#endif + { + /* Retrieve the data for the entering column (base 0) */ + i = lp->get_lpcolumn(lp, lu->col_enter, lu->value+deltarows, NULL, NULL); + lu->value[0] = 0; + /* Execute the update */ + LU8RPC(LUSOL, LUSOL_UPDATE_OLDNONEMPTY, LUSOL_UPDATE_NEWNONEMPTY, + k, lu->value, NULL, &i, &DIAG, &VNORM); + } + + if(i == LUSOL_INFORM_LUSUCCESS) { + + /* Check if we should refactorize based on accumulation of fill-in */ + DIAG = LUSOL->luparm[LUSOL_IP_NONZEROS_L]+LUSOL->luparm[LUSOL_IP_NONZEROS_U]; + VNORM = LUSOL->luparm[LUSOL_IP_NONZEROS_L0]+LUSOL->luparm[LUSOL_IP_NONZEROS_U0]; +#if 0 + /* This is Michael Saunder's fixed parameter */ + VNORM *= MAX_DELTAFILLIN; +#else + /* This is Kjell Eikland's dynamic error accumulation measure */ + VNORM *= pow(MAX_DELTAFILLIN, pow((0.5*LUSOL->nelem/VNORM), 0.25)); +#endif + lu->force_refact = (MYBOOL) ((DIAG > VNORM) && (lu->num_pivots > 20)); + +#if 0 + /* 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); + lu->force_refact = (MYBOOL) (lu->num_pivots > VNORM*lp->bfp_pivotmax(lp)); + } +#endif + } + + /* Handle errors */ + else { +/* 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)); + 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)); + } + else if(i == LUSOL_INFORM_RANKLOSS) { /* To fix rank loss and clear cumulative errors */ +#if 0 + /* This is test code to do pivot in slack BEFORE refactorization (pessimistic approach); + assumes that LUSOL returns correct information about the source of the singularity */ + kcol = LUSOL->luparm[LUSOL_IP_SINGULARINDEX]; +#ifdef MAPSINGULARCOLUMN + kcol = LUSOL_findColumnPosition(LUSOL, kcol); +#endif + lp->set_basisvar(lp, kcol-deltarows, kcol-deltarows); +#endif + lp->invert(lp, INITSOL_USEZERO, FALSE); + 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)); + else + lp->report(lp, infolevel, "bfp_finishupdate: Correction or recovery was successful.\n"); + } + } + return( (MYBOOL) (i == LUSOL_INFORM_LUSUCCESS) ); + +} /* bfp_finishupdate */ + + +/* MUST MODIFY */ +void BFP_CALLMODEL bfp_ftran_normal(lprec *lp, LPSREAL *pcol, int *nzidx) +{ + int i; + INVrec *lu; + + lu = lp->invB; + + /* Do the LUSOL ftran */ + i = LUSOL_ftran(lu->LUSOL, pcol-bfp_rowoffset(lp), nzidx, FALSE); + 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)); + } +} + + +/* MAY MODIFY */ +void BFP_CALLMODEL bfp_ftran_prepare(lprec *lp, LPSREAL *pcol, int *nzidx) +{ + int i; + INVrec *lu; + + lu = lp->invB; + + /* Do the LUSOL ftran */ + i = LUSOL_ftran(lu->LUSOL, pcol-bfp_rowoffset(lp), nzidx, TRUE); + 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)); + } +} + + +/* MUST MODIFY */ +void BFP_CALLMODEL bfp_btran_normal(lprec *lp, LPSREAL *prow, int *nzidx) +{ + int i; + INVrec *lu; + + lu = lp->invB; + + /* Do the LUSOL btran */ + i = LUSOL_btran(lu->LUSOL, prow-bfp_rowoffset(lp), 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)); + } + + /* Check performance data */ +#if 0 + 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)); + 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)); + } +#endif + +} + +/* MUST MODIFY - Routine to find maximum rank of equality constraints */ +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; + LUSOLrec *LUSOL; + + /* Are we capable of finding redundancy with this BFP? */ + if((maprow == NULL) && (mapcol == NULL)) + return( n ); + + /* If so, initialize memory structures */ + if(!allocINT(lp, &nzrows, items, FALSE) || + !allocREAL(lp, &nzvalues, items, FALSE)) + return( n ); + + /* Compute the number of non-empty columns */ + m = 0; + for(j = 1; j <= mapcol[0]; j++) { + n = cb(lp, mapcol[j], NULL, NULL, maprow); + if(n > 0) { + m++; + mapcol[m] = mapcol[j]; + nz += n; + } + } + mapcol[0] = m; + + /* Instantiate a LUSOL object */ + LUSOL = LUSOL_create(NULL, 0, LUSOL_PIVMOD_TRP, 0); + if((LUSOL == NULL) || !LUSOL_sizeto(LUSOL, items, m, nz*LUSOL_MULT_nz_a)) + goto Finish; + + /* Modify relevant LUSOL parameters */ + LUSOL->m = items; + LUSOL->n = m; +#if 0 + LUSOL->luparm[LUSOL_IP_KEEPLU] = FALSE; + LUSOL->luparm[LUSOL_IP_PIVOTTYPE] = LUSOL_PIVMOD_TRP; + LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij] = 2.0; +#endif + + /* Load the columns into LUSOL */ + for(j = 1; j <= m; j++) { + n = cb(lp, mapcol[j], nzvalues, nzrows, maprow); + i = LUSOL_loadColumn(LUSOL, nzrows, j, nzvalues, n, -1); + if(n != i) { + lp->report(lp, IMPORTANT, "bfp_findredundant: Error %d while loading column %d with %d nz\n", + i, j, n); + n = 0; + goto Finish; + } + } + + /* Scale rows to prevent numerical problems */ + if((lp->scalemode != SCALE_NONE) && allocREAL(lp, &arraymax, items+1, TRUE)) { + for(i = 1; i <= nz; i++) { + SETMAX(arraymax[LUSOL->indc[i]], fabs(LUSOL->a[i])); + } + for(i = 1; i <= nz; i++) + LUSOL->a[i] /= arraymax[LUSOL->indc[i]]; + FREE(arraymax); + } + + /* Factorize for maximum rank */ + n = 0; + i = LUSOL_factorize(LUSOL); + /* lp->report(lp, NORMAL, "bfp_findredundant: r=%d c=%d - %s\n", items, m, LUSOL_informstr(LUSOL, i));*/ + if((i == LUSOL_INFORM_LUSUCCESS) || (i != LUSOL_INFORM_LUSINGULAR)) + goto Finish; + + /* We have a singular matrix, obtain the indeces of the singular rows */ + for(i = LUSOL->luparm[LUSOL_IP_RANK_U] + 1; i <= items; i++) { + n++; + maprow[n] = LUSOL->ip[i]; + } + maprow[0] = n; + + /* Clean up */ +Finish: + LUSOL_free(LUSOL); + FREE(nzrows); + FREE(nzvalues); + + return( n ); +} diff --git a/src/external/lpsolve/build/lp_solve/lp_MDO.c b/src/external/lpsolve/build/lp_solve/lp_MDO.c new file mode 100644 index 00000000..6f7807c5 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_MDO.c @@ -0,0 +1,241 @@ +/* + Minimum matrix inverse fill-in modules - interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: string.h, colamd.h, lp_lib.h + + Release notes: + v1.0 1 September 2003 Preprocessing routines for minimum fill-in column + ordering for inverse factorization using the open + source COLAMD library. Suitable for the dense parts + of both the product form and LU factorization inverse + methods. + v1.1 1 July 2004 Renamed from lp_colamdMDO to lp_MDO. + + ---------------------------------------------------------------------------------- +*/ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "colamd.h" +#include "lp_MDO.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +STATIC MYBOOL includeMDO(MYBOOL *usedpos, int item) +{ +/* Legend: TRUE => A basic slack variable already in the basis + FALSE => A column free for being pivoted in + AUTOMATIC+TRUE => A row-singleton user column pivoted into the basis + AUTOMATIC+FALSE => A column-singleton user column pivoted into the basis */ + + /* Handle case where we are processing all columns */ + if(usedpos == NULL) + return( TRUE ); + + else { + /* Otherwise do the selective case */ + MYBOOL test = usedpos[item]; +#if 1 + return( test != TRUE ); +#else + test = test & TRUE; + return( test == FALSE ); +#endif + } +} + +STATIC int prepareMDO(lprec *lp, MYBOOL *usedpos, int *colorder, int *data, int *rowmap) +/* This routine prepares data structures for colamd(). It is called twice, the first + time to count applicable non-zero elements by column, and the second time to fill in + the row indexes of the non-zero values from the first call. Note that the colamd() + row index base is 0 (which suits lp_solve fine). */ +{ + int i, ii, j, k, kk; + int nrows = lp->rows+1, ncols = colorder[0]; + int offset = 0, Bnz = 0, Tnz; + MYBOOL dotally = (MYBOOL) (rowmap == NULL); + MATrec *mat = lp->matA; + LPSREAL hold; + LPSREAL *value; + int *rownr; + + if(dotally) + data[0] = 0; + + Tnz = nrows - ncols; + for(j = 1; j <= ncols; j++) { + kk = colorder[j]; + + /* Process slacks */ + if(kk <= lp->rows) { + if(includeMDO(usedpos, kk)) { + if(!dotally) + data[Bnz] = rowmap[kk]+offset; + Bnz++; + } + Tnz++; + } + /* Process user columns */ + else { + k = kk - lp->rows; + i = mat->col_end[k-1]; + ii= mat->col_end[k]; + Tnz += ii-i; +#ifdef Paranoia + if(i >= ii) + lp->report(lp, SEVERE, "prepareMDO: Encountered empty basic column %d\n", k); +#endif + + /* Detect if we need to do phase 1 adjustments of zero-valued OF variable */ + rownr = &COL_MAT_ROWNR(i); + value = &COL_MAT_VALUE(i); + hold = 0; + if((*rownr > 0) && includeMDO(usedpos, 0) && modifyOF1(lp, kk, &hold, 1.0)) { + if(!dotally) + data[Bnz] = offset; + Bnz++; + } + /* Loop over all NZ-variables */ + for(; i < ii; + i++, value += matValueStep, rownr += matRowColStep) { + if(!includeMDO(usedpos, *rownr)) + continue; + /* See if we need to change phase 1 OF value */ + if(*rownr == 0) { + hold = *value; + if(!modifyOF1(lp, kk, &hold, 1.0)) + continue; + } + /* Tally uneliminated constraint row values */ + if(!dotally) + data[Bnz] = rowmap[*rownr]+offset; + Bnz++; + } + } + if(dotally) + data[j] = Bnz; + } + return( Tnz ); +} + +STATIC MYBOOL verifyMDO(lprec *lp, int *col_end, int *row_nr, int rowmax, int colmax) +{ + int i, j, n, err = 0; + + for(i = 1; i <= colmax; i++) { + n = 0; + for(j = col_end[i-1]; (j < col_end[i]) && (err == 0); j++, n++) { + if(row_nr[j] < 0 || row_nr[j] > rowmax) + err = 1; + if(n > 0 && row_nr[j] <= row_nr[j-1]) + err = 2; + n++; + } + } + if(err != 0) + lp->report(lp, SEVERE, "verifyMDO: Invalid MDO input structure generated (error %d)\n", err); + return( (MYBOOL) (err == 0) ); +} + +void *mdo_calloc(size_t size, size_t count) +{ + return ( calloc(size, count) ); +} +void mdo_free(void *mem) +{ + free( mem ); +} + + +int __WINAPI getMDO(lprec *lp, MYBOOL *usedpos, int *colorder, int *size, MYBOOL symmetric) +{ + int error = FALSE; + int nrows = lp->rows+1, ncols = colorder[0]; + int i, j, kk, n; + int *col_end, *row_map = NULL; + int Bnz, Blen, *Brows = NULL; + int stats[COLAMD_STATS]; + double knobs[COLAMD_KNOBS]; + + /* Tally the non-zero counts of the unused columns/rows of the + basis matrix and store corresponding "net" starting positions */ + allocINT(lp, &col_end, ncols+1, FALSE); + n = prepareMDO(lp, usedpos, colorder, col_end, NULL); + Bnz = col_end[ncols]; + + /* Check that we have unused basic columns, otherwise skip analysis */ + if(ncols == 0 || Bnz == 0) + goto Transfer; + + /* Get net number of rows and fill mapper */ + allocINT(lp, &row_map, nrows, FALSE); + nrows = 0; + for(i = 0; i <= lp->rows; i++) { + row_map[i] = i-nrows; + /* Increment eliminated row counter if necessary */ + if(!includeMDO(usedpos, i)) + nrows++; + } + nrows = lp->rows+1 - nrows; + + /* Store row indeces of non-zero values in the basic columns */ + Blen = colamd_recommended(Bnz, nrows, ncols); + allocINT(lp, &Brows, Blen, FALSE); + prepareMDO(lp, usedpos, colorder, Brows, row_map); +#ifdef Paranoia + verifyMDO(lp, col_end, Brows, nrows, ncols); +#endif + + /* Compute the MDO */ +#if 1 + colamd_set_defaults(knobs); + knobs [COLAMD_DENSE_ROW] = 0.2+0.2 ; /* default changed for UMFPACK */ + knobs [COLAMD_DENSE_COL] = knobs [COLAMD_DENSE_ROW]; + if(symmetric && (nrows == ncols)) { + MEMCOPY(colorder, Brows, ncols + 1); + error = !symamd(nrows, colorder, col_end, Brows, knobs, stats, mdo_calloc, mdo_free); + } + else + error = !colamd(nrows, ncols, Blen, Brows, col_end, knobs, stats); +#else + if(symmetric && (nrows == ncols)) { + MEMCOPY(colorder, Brows, ncols + 1); + error = !symamd(nrows, colorder, col_end, Brows, knobs, stats, mdo_calloc, mdo_free); + } + else + error = !colamd(nrows, ncols, Blen, Brows, col_end, (double *) NULL, stats); +#endif + + /* Transfer the estimated optimal ordering, adjusting for index offsets */ +Transfer: + if(error) + error = stats[COLAMD_STATUS]; + else { + MEMCOPY(Brows, colorder, ncols + 1); + for(j = 0; j < ncols; j++) { + kk = col_end[j]; + n = Brows[kk+1]; + colorder[j+1] = n; + } + } + + /* Free temporary vectors */ + FREE(col_end); + if(row_map != NULL) + FREE(row_map); + if(Brows != NULL) + FREE(Brows); + + if(size != NULL) + *size = ncols; + return( error ); +} + + diff --git a/src/external/lpsolve/build/lp_solve/lp_MPS.c b/src/external/lpsolve/build/lp_solve/lp_MPS.c new file mode 100644 index 00000000..3c737de9 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_MPS.c @@ -0,0 +1,1855 @@ + +#include +#include +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_scale.h" +#include "lp_report.h" +#include "lp_MPS.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +/* Define buffer-size controled function mapping */ +# if defined _MSC_VER +# define vsnprintf _vsnprintf +# endif + +/* MPS file input and output routines for lp_solve */ +/* ------------------------------------------------------------------------- */ + +/* +A: MPS format was named after an early IBM LP product and has emerged +as a de facto standard ASCII medium among most of the commercial LP +codes. Essentially all commercial LP codes accept this format, but if +you are using public domain software and have MPS files, you may need +to write your own reader routine for this. It's not too hard. The +main things to know about MPS format are that it is column oriented (as +opposed to entering the model as equations), and everything (variables, +rows, etc.) gets a name. MPS format is described in more detail in +Murtagh's book, referenced in another section. Also, + +ftp://softlib.cs.rice.edu/pub/miplib/mps_format + +is a nice short introduction. exports + +MPS is an old format, so it is set up as though you were using punch +cards, and is not free format. Fields start in column 1, 5, 15, 25, 40 +and 50. Sections of an MPS file are marked by so-called header cards, +which are distinguished by their starting in column 1. Although it is +typical to use upper-case throughout the file (like I said, MPS has +long historical roots), many MPS-readers will accept mixed-case for +anything except the header cards, and some allow mixed-case anywhere. +The names that you choose for the individual entities (constraints or +variables) are not important to the solver; you should pick names that +are meaningful to you, or will be easy for a post-processing code to +read. + +Here is a little sample model written in MPS format (explained in more +detail below): + +NAME TESTPROB +ROWS + N COST + L LIM1 + G LIM2 + E MYEQN +COLUMNS + XONE COST 1 LIM1 1 + XONE LIM2 1 + YTWO COST 4 LIM1 1 + YTWO MYEQN -1 + ZTHREE COST 9 LIM2 1 + ZTHREE MYEQN 1 +RHS + RHS1 LIM1 5 LIM2 10 + RHS1 MYEQN 7 +BOUNDS + UP BND1 XONE 4 + LO BND1 YTWO -1 + UP BND1 YTWO 1 +ENDATA + +means: + +Optimize + COST: XONE + 4 YTWO + 9 ZTHREE +Subject To + LIM1: XONE + YTWO <= 5 + LIM2: XONE + ZTHREE >= 10 + MYEQN: - YTWO + ZTHREE = 7 +Bounds + 0 <= XONE <= 4 +-1 <= YTWO <= 1 +End + +*/ + +/* copy a MPS name, only trailing spaces are removed. In MPS, names can have + embedded spaces! */ +STATIC void namecpy(char *into, char *from) +{ + int i; + + /* copy at most 8 characters of from, stop at end of string or newline */ + for(i = 0; (from[i] != '\0') && (from[i] != '\n') && (from[i] != '\r') && (i < 8); i++) + into[i] = from[i]; + + /* end with end of string */ + into[i] = '\0'; + + /* remove trailing spaces, if any */ + for(i--; (i >= 0) && (into[i] == ' '); i--) + into[i] = '\0'; +} + +/* scan an MPS line, and pick up the information in the fields that are + present */ + +/* scan_line for fixed MPS format */ +STATIC int scan_lineFIXED(lprec *lp, int section, char* line, char *field1, char *field2, char *field3, + double *field4, char *field5, double *field6) +{ + int items = 0, line_len; + char buf[16], *ptr1, *ptr2; + + line_len = (int) strlen(line); + while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' '))) + line_len--; + + if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */ + strncpy(buf, line, 4); + buf[4] = '\0'; + sscanf(buf, "%s", field1); + items++; + } + else + field1[0] = '\0'; + + 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++; + } + else + field2[0] = '\0'; + + 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++; + } + else + field3[0] = '\0'; + + 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++) + if(!isspace((unsigned char) *ptr1)) + if((*(ptr2++) = *ptr1) == 0) + break; + /* *field4 = atof(buf); */ + *field4 = strtod(buf, &ptr1); + if(*ptr1) { + report(lp, IMPORTANT, "MPS_readfile: invalid number in columns 25-36 \n"); + return(-1); + } + items++; + } + else + *field4 = 0; + + 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++; + } + else + field5[0] = '\0'; + 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++) + if(!isspace((unsigned char) *ptr1)) + if((*(ptr2++) = *ptr1) == 0) + break; + /* *field6 = atof(buf); */ + *field6 = strtod(buf, &ptr1); + if(*ptr1) { + report(lp, IMPORTANT, "MPS_readfile: invalid number in columns 50-61 \n"); + return(-1); + } + items++; + } + else + *field6 = 0; + + return(items); +} + +STATIC int spaces(char *line, int line_len) +{ + int l; + char *line1 = line; + + while (*line1 == ' ') + line1++; + l = (int) (line1 - line); + if (line_len < l) + l = line_len; + return(l); +} + +STATIC int lenfield(char *line, int line_len) +{ + int l; + char *line1 = line; + + while ((*line1) && (*line1 != ' ')) + line1++; + l = (int) (line1 - line); + if (line_len < l) + l = line_len; + return(l); +} + +/* scan_line for fixed MPS format */ +STATIC int scan_lineFREE(lprec *lp, 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; + + line_len = (int) strlen(line); + while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' '))) + line_len--; + + len = spaces(line, line_len); + line += len; + line_len -= len; + + if ((section == MPSCOLUMNS) || (section == MPSRHS) || (section == MPSRANGES)) { + field1[0] = '\0'; + items++; + } + else { + len = lenfield(line, line_len); + if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */ + 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 + field1[0] = '\0'; + + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + } + + len = lenfield(line, line_len); + if(line_len >= 1) { /* name */ + strncpy(field2, line, len); + field2[len] = '\0'; + items++; + } + else + field2[0] = '\0'; + + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + + len = lenfield(line, line_len); + if(line_len >= 1) { /* name */ + strncpy(field3, line, len); + field3[len] = '\0'; + items++; + } + else + field3[0] = '\0'; + + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + + if (*field3) { + if((section == MPSCOLUMNS) && (strcmp(field3, "'MARKER'") == 0)) { + *field4 = 0; + 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; + } + 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++; + } + } + + if(ptr1 == NULL) { + len = lenfield(line, line_len); + if(line_len >= 1) { /* number */ + strncpy(buf, line, len); + buf[len] = '\0'; + for(ptr1 = ptr2 = buf; ; ptr1++) + if(!isspace((unsigned char) *ptr1)) + if((*(ptr2++) = *ptr1) == 0) + break; + /* *field4 = atof(buf); */ + *field4 = strtod(buf, &ptr1); + if(*ptr1) + return(-1); + items++; + } + else + *field4 = 0; + + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + } + + len = lenfield(line, line_len); + if(line_len >= 1) { /* name */ + strncpy(field5, line, len); + field5[len] = '\0'; + items++; + } + else + field5[0] = '\0'; + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + + len = lenfield(line, line_len); + if(line_len >= 1) { /* number */ + strncpy(buf, line, len); + buf[len] = '\0'; + for(ptr1 = ptr2 = buf; ; ptr1++) + if(!isspace((unsigned char) *ptr1)) + if((*(ptr2++) = *ptr1) == 0) + break; + /* *field6 = atof(buf); */ + *field6 = strtod(buf, &ptr1); + if(*ptr1) + return(-1); + items++; + } + else + *field6 = 0; + + if((section == MPSSOS) && (items == 2)) { + strcpy(field3, field2); + strcpy(field2, field1); + *field1 = 0; + } + + if((section != MPSOBJNAME) && (section != MPSBOUNDS)) { + for(ptr1 = field1; *ptr1; ptr1++) + *ptr1=(char)toupper(*ptr1); + } + + 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) +{ + int ok = TRUE; + + if (*Column_ready) { + ok = add_columnex(lp, *count, Last_column, Last_columnno); + if (ok) { + ok = set_col_name(lp, lp->columns, Last_col_name); + } + 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[]) +{ + int i = *count; + + if(rowValue[i] == 0) + return( FALSE ); + + while((i > 0) && (rowIndex[i] < rowIndex[i-1])) { + swapINT (rowIndex+i, rowIndex+i-1); + swapREAL(rowValue+i, rowValue+i-1); + i--; + } + (*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) +{ + MYBOOL status = FALSE; + FILE *fpin; + + fpin = fopen(filename, "r"); + if(fpin != NULL) { + status = MPS_readhandle(newlp, fpin, typeMPS, verbose); + fclose(fpin); + } + return( status ); +} + +static int __WINAPI MPS_input(void *fpin, char *buf, int max_size) +{ + return(fgets(buf, max_size, (FILE *) fpin) != NULL); +} + +MYBOOL __WINAPI MPS_readhandle(lprec **newlp, FILE *filehandle, int typeMPS, int verbose) +{ + return(MPS_readex(newlp, (void *) filehandle, MPS_input, typeMPS, verbose)); +} + +MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func read_modeldata, int typeMPS, int verbose) +{ + char field1[BUFSIZ], field2[BUFSIZ], field3[BUFSIZ], field5[BUFSIZ], line[BUFSIZ], tmp[BUFSIZ], + Last_col_name[BUFSIZ], probname[BUFSIZ], OBJNAME[BUFSIZ], *ptr; + int items, row, Lineno, var, + section = MPSUNDEF, variant = 0, NZ = 0, SOS = 0; + MYBOOL Int_section, Column_ready, Column_ready1, + Unconstrained_rows_found = FALSE, OF_found = FALSE, CompleteStatus = FALSE; + double field4, field6; + LPSREAL *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, + double *field4, char *field5, double *field6); + + if(newlp == NULL) + return( CompleteStatus ); + else if(*newlp == NULL) + lp = make_lp(0, 0); + 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) + delete_lp(lp); + return( CompleteStatus ); + } + + if (lp != NULL) { + lp->source_is_file = TRUE; + lp->verbose = verbose; + strcpy(Last_col_name, ""); + strcpy(OBJNAME, ""); + Int_section = FALSE; + Column_ready = FALSE; + Lineno = 0; + + /* let's initialize line to all zero's */ + MEMCLEAR(line, BUFSIZ); + + while(read_modeldata(userhandle, line, BUFSIZ - 1)) { + Lineno++; + + for(ptr = line; (*ptr) && (isspace((unsigned char) *ptr)); ptr++); + + /* skip lines which start with "*", they are comment */ + if((line[0] == '*') || (*ptr == 0) || (*ptr == '\n') || (*ptr == '\r')) { + report(lp, FULL, "Comment on line %d: %s", Lineno, line); + continue; + } + + report(lp, FULL, "Line %6d: %s", Lineno, line); + + /* first check for "special" lines: NAME, ROWS, BOUNDS .... */ + /* this must start in the first position of line */ + if(line[0] != ' ') { + sscanf(line, "%s", tmp); + if(strcmp(tmp, "NAME") == 0) { + section = MPSNAME; + *probname = 0; + sscanf(line, "NAME %s", probname); + if (!set_lp_name(lp, probname)) + break; + } + else if(((typeMPS & MPSFREE) == MPSFREE) && (strcmp(tmp, "OBJSENSE") == 0)) { + section = MPSOBJSENSE; + report(lp, FULL, "Switching to OBJSENSE section\n"); + } + else if(((typeMPS & MPSFREE) == MPSFREE) && (strcmp(tmp, "OBJNAME") == 0)) { + section = MPSOBJNAME; + report(lp, FULL, "Switching to OBJNAME section\n"); + } + else if(strcmp(tmp, "ROWS") == 0) { + section = MPSROWS; + report(lp, FULL, "Switching to ROWS section\n"); + } + else if(strcmp(tmp, "COLUMNS") == 0) { + allocREAL(lp, &Last_column, lp->rows + 1, TRUE); + allocINT(lp, &Last_columnno, lp->rows + 1, TRUE); + count = 0; + if ((Last_column == NULL) || (Last_columnno == NULL)) + break; + section = MPSCOLUMNS; + 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)) + break; + section = MPSRHS; + report(lp, FULL, "Switching to RHS section\n"); + } + else if(strcmp(tmp, "BOUNDS") == 0) { + section = MPSBOUNDS; + report(lp, FULL, "Switching to BOUNDS section\n"); + } + else if(strcmp(tmp, "RANGES") == 0) { + section = MPSRANGES; + report(lp, FULL, "Switching to RANGES section\n"); + } + else if((strcmp(tmp, "SOS") == 0) || (strcmp(tmp, "SETS") == 0)) { + section = MPSSOS; + if(strcmp(tmp, "SOS") == 0) + variant = 0; + else + variant = 1; + report(lp, FULL, "Switching to %s section\n", tmp); + } + else if(strcmp(tmp, "ENDATA") == 0) { + report(lp, FULL, "Finished reading MPS file\n"); + CompleteStatus = TRUE; + break; + } + else { /* line does not start with space and does not match above */ + report(lp, IMPORTANT, "Unrecognized MPS line %d: %s\n", Lineno, line); + break; + } + } + else { /* normal line, process */ + items = scan_line(lp, section, line, field1, field2, field3, &field4, field5, &field6); + if(items < 0){ + report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line); + break; + } + + switch(section) { + + case MPSNAME: + report(lp, IMPORTANT, "Error, extra line under NAME line\n"); + break; + + case MPSOBJSENSE: + if(OBJSENSE != ROWTYPE_EMPTY) { + report(lp, IMPORTANT, "Error, extra line under OBJSENSE line\n"); + break; + } + if((strcmp(field1, "MAXIMIZE") == 0) || (strcmp(field1, "MAX") == 0)) { + OBJSENSE = ROWTYPE_OFMAX; + set_maxim(lp); + } + else if((strcmp(field1, "MINIMIZE") == 0) || (strcmp(field1, "MIN") == 0)) { + OBJSENSE = ROWTYPE_OFMIN; + set_minim(lp); + } + else { + report(lp, SEVERE, "Unknown OBJSENSE direction '%s' on line %d\n", field1, Lineno); + break; + } + continue; + + case MPSOBJNAME: + if(*OBJNAME) { + report(lp, IMPORTANT, "Error, extra line under OBJNAME line\n"); + break; + } + strcpy(OBJNAME, field1); + continue; + + /* Process entries in the ROWS section */ + case MPSROWS: + /* field1: rel. operator; field2: name of constraint */ + + report(lp, FULL, "Row %5d: %s %s\n", lp->rows + 1, field1, field2); + + if(strcmp(field1, "N") == 0) { + if((*OBJNAME) && (strcmp(field2, OBJNAME))) + /* Ignore this objective name since it is not equal to the OBJNAME name */; + else if(!OF_found) { /* take the first N row as OF, ignore others */ + if (!set_row_name(lp, 0, field2)) + break; + OF_found = TRUE; + } + else if(!Unconstrained_rows_found) { + report(lp, IMPORTANT, "Unconstrained row %s ignored\n", field2); + report(lp, IMPORTANT, "Further messages of this kind will be suppressed\n"); + Unconstrained_rows_found = TRUE; + } + } + else if(strcmp(field1, "L") == 0) { + if ((!str_add_constraint(lp, "" ,LE ,0)) || (!set_row_name(lp, lp->rows, field2))) + break; + } + else if(strcmp(field1, "G") == 0) { + if ((!str_add_constraint(lp, "" ,GE ,0)) || (!set_row_name(lp, lp->rows, field2))) + break; + } + else if(strcmp(field1, "E") == 0) { + if ((!str_add_constraint(lp, "",EQ ,0)) || (!set_row_name(lp, lp->rows, field2))) + break; + } + else { + report(lp, SEVERE, "Unknown relation code '%s' on line %d\n", field1, Lineno); + break; + } + + continue; + + /* Process entries in the COLUMNS section */ + case MPSCOLUMNS: + /* field2: variable; field3: constraint; field4: coef */ + /* optional: field5: constraint; field6: coef */ + + report(lp, FULL, "Column %4d: %s %s %g %s %g\n", + lp->columns + 1, field2, field3, field4, field5, field6); + + if((items == 4) || (items == 5) || (items == 6)) { + if (NZ == 0) + strcpy(Last_col_name, field2); + else if(*field2) { + Column_ready1 = (MYBOOL) (strcmp(field2, Last_col_name) != 0); + if(Column_ready1) { + if (find_var(lp, field2, FALSE) >= 0) { + report(lp, SEVERE, "Variable name (%s) is already used!\n", field2); + break; + } + + 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)) { + strcpy(Last_col_name, field2); + NZ = 0; + } + else + break; + } + } + } + if(items == 5) { /* there might be an INTEND or INTORG marker */ + /* look for " 'MARKER' 'INTORG'" + or " 'MARKER' 'INTEND'" */ + if(strcmp(field3, "'MARKER'") != 0) + break; + if(strcmp(field5, "'INTORG'") == 0) { + Int_section = TRUE; + report(lp, FULL, "Switching to integer section\n"); + } + else if(strcmp(field5, "'INTEND'") == 0) { + Int_section = FALSE; + report(lp, FULL, "Switching to non-integer section\n"); + } + else + report(lp, IMPORTANT, "Unknown marker (ignored) at line %d: %s\n", + Lineno, field5); + } + else if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { + 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; + if(appendmpsitem(&count, Last_columnno, Last_column)) { + NZ++; + Column_ready = TRUE; + } + } + } + if(items == 6) { + if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { + 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; + if(appendmpsitem(&count, Last_columnno, Last_column)) { + NZ++; + Column_ready = TRUE; + } + } + } + + if((items < 4) || (items > 6)) { /* Wrong! */ + report(lp, CRITICAL, "Wrong number of items (%d) in COLUMNS section (line %d)\n", + items, Lineno); + break; + } + + continue; + + /* Process entries in the RHS section */ + /* field2: uninteresting name; field3: constraint name */ + /* field4: value */ + /* optional: field5: constraint name; field6: value */ + case MPSRHS: + + report(lp, FULL, "RHS line: %s %s %g %s %g\n", + field2, field3, field4, field5, field6); + + if((items != 4) && (items != 6)) { + report(lp, CRITICAL, "Wrong number of items (%d) in RHS section line %d\n", + items, Lineno); + break; + } + + if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { + if ((row == 0) && ((typeMPS & MPSNEGOBJCONST) == MPSNEGOBJCONST)) + field4 = -field4; + set_rh(lp, row, (LPSREAL)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); + } + } + + continue; + + /* Process entries in the BOUNDS section */ + /* field1: bound type; field2: uninteresting name; */ + /* field3: variable name; field4: value */ + case MPSBOUNDS: + + report(lp, FULL, "BOUNDS line: %s %s %s %g\n", + field1, field2, field3, field4); + + 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)) + break; + Column_ready = TRUE; + var = find_var(lp, field3, TRUE); + } + 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)) + 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)) + break; + set_semicont(lp, var, TRUE); + } + else if(strcmp(field1, "SI") == 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)) + 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)) + 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)) + 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)) + break; + } + else if(strcmp(field1, "FR") == 0) { /* free variable */ + set_unbounded(lp, var); + } + else if(strcmp(field1, "FX") == 0) { + /* fixed, upper _and_ lower */ + if(!set_bounds(lp, var, field4, field4)) + break; + } + else if(strcmp(field1, "BV") == 0) { /* binary variable */ + set_binary(lp, var, TRUE); + } + /* 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)) + 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)) + break; + set_int(lp, var, TRUE); + } + else { + report(lp, CRITICAL, "BOUND type %s on line %d is not supported", + field1, Lineno); + break; + } + + continue; + + /* Process entries in the BOUNDS section */ + + /* We have to implement the following semantics: + + D. The RANGES section is for constraints of the form: h <= + constraint <= u . The range of the constraint is r = u - h . The + value of r is specified in the RANGES section, and the value of u or + h is specified in the RHS section. If b is the value entered in the + RHS section, and r is the value entered in the RANGES section, then + u and h are thus defined: + + row type sign of r h u + ---------------------------------------------- + G + or - b b + |r| + L + or - b - |r| b + E + b b + |r| + E - b - |r| b */ + + /* field2: uninteresting name; field3: constraint name */ + /* field4: value */ + /* optional: field5: constraint name; field6: value */ + + case MPSRANGES: + + report(lp, FULL, "RANGES line: %s %s %g %s %g", + field2, field3, field4, field5, field6); + + if((items != 4) && (items != 6)) { + report(lp, CRITICAL, "Wrong number of items (%d) in RANGES section line %d", + items, Lineno); + break; + } + + if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { + /* Determine constraint type */ + + if(fabs(field4) >= lp->infinite) { + report(lp, IMPORTANT, + "Warning, Range for row %s >= infinity (value %g) on line %d, ignored", + field3, field4, Lineno); + } + else if(field4 == 0) { + /* Change of a GE or LE to EQ */ + if(lp->orig_upbo[row] != 0) + set_constr_type(lp, row, EQ); + } + else if(is_chsign(lp, row)) { + /* GE */ + lp->orig_upbo[row] = fabs(field4); + } + else if((lp->orig_upbo[row] == 0) && (field4 >= 0)) { + /* EQ with positive sign of r value */ + set_constr_type(lp, row, GE); + lp->orig_upbo[row] = field4; + } + else if(lp->orig_upbo[row] == lp->infinite) { + /* LE */ + lp->orig_upbo[row] = fabs(field4); + } + else if((lp->orig_upbo[row] == 0) && (field4 < 0)) { + /* EQ with negative sign of r value */ + set_constr_type(lp, row, LE); + lp->orig_upbo[row] = my_flipsign(field4); + } + else { /* let's be paranoid */ + report(lp, IMPORTANT, + "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line %d", + row, is_chsign(lp, row), (double)lp->orig_upbo[row], Lineno); + } + } + + if(items == 6) { + if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { + /* Determine constraint type */ + + if(fabs(field6) >= lp->infinite) { + report(lp, IMPORTANT, + "Warning, Range for row %s >= infinity (value %g) on line %d, ignored", + field5, field6, Lineno); + } + else if(field6 == 0) { + /* Change of a GE or LE to EQ */ + if(lp->orig_upbo[row] != 0) + set_constr_type(lp, row, EQ); + } + else if(is_chsign(lp, row)) { + /* GE */ + lp->orig_upbo[row] = fabs(field6); + } + else if(lp->orig_upbo[row] == 0 && field6 >= 0) { + /* EQ with positive sign of r value */ + set_constr_type(lp, row, GE); + lp->orig_upbo[row] = field6; + } + else if(lp->orig_upbo[row] == lp->infinite) { + /* LE */ + lp->orig_upbo[row] = fabs(field6); + } + else if((lp->orig_upbo[row] == 0) && (field6 < 0)) { + /* EQ with negative sign of r value */ + set_constr_type(lp, row, LE); + lp->orig_upbo[row] = my_flipsign(field6); + } + else { /* let's be paranoid */ + report(lp, IMPORTANT, + "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line %d", + row, is_chsign(lp,row), (double) lp->orig_upbo[row], Lineno); + } + } + } + + continue; + + /* Process entries in the SOS section */ + + /* We have to implement the following semantics: + + E. The SOS section is for ordered variable sets of the form: + x1, x2, x3 ... xn where only a given number of consequtive variables + may be non-zero. Each set definition is prefaced by type, name + and priority data. Each set member has an optional weight that + determines its order. There are two forms supported; a full format + and a reduced CPLEX-like format. */ + + case MPSSOS: + report(lp, FULL, "SOS line: %s %s %g %s %g", + field2, field3, field4, field5, field6); + + if((items == 0) || (items > 4)) { + report(lp, IMPORTANT, + "Invalid number of items (%d) in SOS section line %d\n", + items, Lineno); + break; + } + + if(strlen(field1) == 0) items--; /* fix scanline anomoly! */ + + /* Check if this is the start of a new SOS */ + if(items == 1 || items == 4) { + row = (int) (field1[1] - '0'); + if((row <= 0) || (row > 9)) { + report(lp, IMPORTANT, + "Error: Invalid SOS type %s line %d\n", field1, Lineno); + break; + } + field1[0] = '\0'; /* fix scanline anomoly! */ + + /* 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); + } + else { /* Remap XPRESS format name */ + strcpy(field3, field1); + } + /* Obtain the SOS priority */ + if(items == 4) + SOS = (int) field4; + else + SOS = 1; + + /* Define a new SOS instance */ + + SOS = add_SOS(lp, field3, (int) row, SOS, 0, NULL, NULL); + } + /* Otherwise, add set members to the active SOS */ + else { + char *field = (items == 3) ? field3 /* Native lp_solve and XPRESS formats */ : field2 /* CPLEX format */; + + 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)) + break; + Column_ready = TRUE; + var = find_var(lp, field, TRUE); + } + if((var < 0) || (SOS < 1)) /* undefined var and could add ... */; + else append_SOSrec(lp->SOS->sos_list[SOS-1], 1, &var, &field4); + } + + continue; + } + + /* If we got here there was an error "upstream" */ + report(lp, IMPORTANT, + "Error: Cannot handle line %d\n", Lineno); + break; + } + } + + if((*OBJNAME) && (!OF_found)) { + report(lp, IMPORTANT, + "Error: Objective function specified by OBJNAME card not found\n"); + 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); + } + } + *newlp = lp; + } + if(Last_column != NULL) + FREE(Last_column); + if(Last_columnno != NULL) + FREE(Last_columnno); + } + + return( CompleteStatus ); +} + +static void number(char *str,LPSREAL value) + { + char __str[80], *_str; + int i; + + /* sprintf(_str,"%12.6G",value); */ + _str=__str+2; + if (value>=0.0) + if ((value!=0.0) && ((value>0.99999999e12) || (value<0.0001))) { + int n=15; + + do { + n--; + i=sprintf(_str,"%*.*E",n,n-6,(double) value); + if (i>12) { + char *ptr=strchr(_str,'E'); + + if (ptr!=NULL) { + if (*(++ptr)=='-') ptr++; + while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) { + strcpy(ptr,ptr+1); + i--; + } + } + } + } while (i>12); + } + else if (value>=1.0e10) { + int n=13; + + do { + i=sprintf(_str,"%*.0f",--n,(double) value); + } while (i>12); + } + else { + if (((i=sprintf(_str,"%12.10f",(double) value))>12) && (_str[12]>='5')) { + for (i=11;i>=0;i--) + if (_str[i]!='.') { + if (++_str[i]>'9') _str[i]='0'; + else break; + } + if (i<0) { + *(--_str)='1'; + *(--_str)=' '; + } + } + } + else + if ((value<-0.99999999e11) || (value>-0.0001)) { + int n=15; + + do { + n--; + i=sprintf(_str,"%*.*E",n,n-7,(double) value); + if (i>12) { + char *ptr=strchr(_str,'E'); + + if (ptr!=NULL) { + if (*(++ptr)=='-') ptr++; + while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) { + strcpy(ptr,ptr+1); + i--; + } + } + } + } while (i>12); + } + else if (value<=-1.0e9) { + int n=13; + + do { + i=sprintf(_str,"%*.0f",--n,(double) value); + } while (i>12); + } + else + if (((i=sprintf(_str,"%12.9f",(double) value))>12) && (_str[12]>='5')) { + for (i=11;i>=1;i--) + if (_str[i]!='.') { + if (++_str[i]>'9') _str[i]='0'; + else break; + } + if (i<1) { + *_str='1'; + *(--_str)='-'; + *(--_str)=' '; + } + } + //strncpy(str,_str,12); + _str[12] ='\0'; strcpy(str, _str); + } + +static char *formatnumber12(char *numberbuffer, double a) +{ +#if 0 + return(sprintf(numberbuffer, "%12g", a)); +#else + number(numberbuffer, a); + return(numberbuffer); +#endif +} + +STATIC char *MPSnameFIXED(char *name0, char *name) +{ + sprintf(name0, "%-8.8s", name); + return(name0); +} + +STATIC char *MPSnameFREE(char *name0, char *name) +{ + if(strlen(name) < 8) + return(MPSnameFIXED(name0, name)); + else + return(name); +} + +static void write_data(void *userhandle, write_modeldata_func write_modeldata, char *format, ...) +{ + char buff[DEF_STRBUFSIZE+1]; + va_list ap; + + va_start(ap, format); + vsnprintf(buff, DEF_STRBUFSIZE, format, ap); + va_end(ap); + write_modeldata(userhandle, buff); +} + +MYBOOL __WINAPI 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"); + return(FALSE); + } + + names_used = lp->names_used; + + if((typeMPS & MPSFIXED) == 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++) + if((lp->col_name[i] != NULL) && (lp->col_name[i]->name != NULL) && (!is_splitvar(lp, i)) && (strlen(lp->col_name[i]->name) > 8)) + for(j = 1; (j < i) && (ok); j++) + if((lp->col_name[j] != NULL) && (lp->col_name[j]->name != NULL) && (!is_splitvar(lp, j))) + if(strncmp(lp->col_name[i]->name, lp->col_name[j]->name, 8) == 0) + ok = FALSE; + } + + if(!ok) { + lp->names_used = FALSE; + ok = TRUE; + } + + memset(numberbuffer, 0, sizeof(numberbuffer)); + + marker = 0; + + /* First write metadata in structured comment form (lp_solve style) */ + write_data(userhandle, write_modeldata, "*\n", + (int) MAJORVERSION, (int) MINORVERSION); + write_data(userhandle, write_modeldata, "*\n", lp->rows); + write_data(userhandle, write_modeldata, "*\n", lp->columns); + write_data(userhandle, write_modeldata, "*\n", lp->equalities); + if(SOS_count(lp) > 0) + write_data(userhandle, write_modeldata, "*\n", SOS_count(lp)); + write_data(userhandle, write_modeldata, "*\n", lp->int_vars); + if(lp->sc_vars > 0) + write_data(userhandle, write_modeldata, "*\n", lp->sc_vars); + write_data(userhandle, write_modeldata, "*\n", (is_maxim(lp) ? "MAX" : "MIN")); + 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, "OBJSENSE\n MAX\n"); + write_data(userhandle, write_modeldata, "ROWS\n"); + for(i = 0; i <= lp->rows; i++) { + if(i == 0) + write_data(userhandle, write_modeldata, " N "); + else if(lp->orig_upbo[i] != 0) { + if(is_chsign(lp,i)) + write_data(userhandle, write_modeldata, " G "); + else + write_data(userhandle, write_modeldata, " L "); + } + else + write_data(userhandle, write_modeldata, " E "); + write_data(userhandle, write_modeldata, "%s\n", MPSname(name0, get_row_name(lp, i))); + } + + allocREAL(lp, &val, 1 + lp->rows, TRUE); + allocINT(lp, &idx, 1 + lp->rows, TRUE); + write_data(userhandle, write_modeldata, "COLUMNS\n"); + for(i = 1; i <= lp->columns; i++) { + if(!is_splitvar(lp, i)) { + if(is_int(lp,i) && (marker % 2) == 0) { + write_data(userhandle, write_modeldata, " MARK%04d 'MARKER' 'INTORG'\n", + marker); + marker++; + } + if(!is_int(lp,i) && (marker % 2) == 1) { + write_data(userhandle, write_modeldata, " MARK%04d 'MARKER' 'INTEND'\n", + marker); + marker++; + } + + /* Loop over non-zero column entries */ + je = get_columnex(lp, i, val, idx); + for(k = 1, val1 = val, idx1 = idx, jj = 0; jj < je; jj++) { + k = 1 - k; + j = *(idx1++); + a = *(val1++); + if (k == 0) { + write_data(userhandle, write_modeldata, " %s", + MPSname(name0, 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)))); + } + 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)); */ + } + if(k == 0) + write_data(userhandle, write_modeldata, "\n"); + } + } + if((marker % 2) == 1) { + write_data(userhandle, write_modeldata, " MARK%04d 'MARKER' 'INTEND'\n", + marker); + /* marker++; */ /* marker not used after this */ + } + FREE(idx); + FREE(val); + + write_data(userhandle, write_modeldata, "RHS\n"); + for(k = 1, i = 0; i <= lp->rows; i++) { + 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)); + else + write_data(userhandle, write_modeldata, " %s %s\n", + MPSname(name0, get_row_name(lp, i)), + formatnumber12(numberbuffer, (double)a)); + } + } + if(k == 0) + write_data(userhandle, write_modeldata, "\n"); + + putheader = TRUE; + for(k = 1, i = 1; i <= lp->rows; i++){ + a = 0; + if((lp->orig_upbo[i] < lp->infinite) && (lp->orig_upbo[i] != 0.0)) + a = lp->orig_upbo[i]; + if(a) { + if(putheader) { + write_data(userhandle, write_modeldata, "RANGES\n"); + putheader = FALSE; + } + a = unscaled_value(lp, a, i); + 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)); + else + write_data(userhandle, write_modeldata, " %s %s\n", + MPSname(name0, get_row_name(lp, i)), + formatnumber12(numberbuffer, (double)a)); + } + } + if(k == 0) + write_data(userhandle, write_modeldata, "\n"); + + putheader = TRUE; + for(i = lp->rows + 1; i <= lp->sum; i++) + if(!is_splitvar(lp, i - lp->rows)) { + j = i - lp->rows; + if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite) && + (lp->orig_lowbo[i] == lp->orig_upbo[i])) { + a = lp->orig_upbo[i]; + a = unscaled_value(lp, a, i); + if(putheader) { + write_data(userhandle, write_modeldata, "BOUNDS\n"); + putheader = FALSE; + } + write_data(userhandle, write_modeldata, " FX BND %s %s\n", + MPSname(name0, get_col_name(lp, j)), + formatnumber12(numberbuffer, (double)a)); + } + else if(is_binary(lp, j)) { + if(putheader) { + write_data(userhandle, write_modeldata, "BOUNDS\n"); + putheader = FALSE; + } + write_data(userhandle, write_modeldata, " BV BND %s\n", + MPSname(name0, get_col_name(lp, j))); + } + else if(is_unbounded(lp, j)) { + if(putheader) { + write_data(userhandle, write_modeldata, "BOUNDS\n"); + putheader = FALSE; + } + write_data(userhandle, write_modeldata, " FR BND %s\n", + MPSname(name0, 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))) { + a = lp->orig_upbo[i]; + if(a < lp->infinite) + a = unscaled_value(lp, a, i); + if(putheader) { + write_data(userhandle, write_modeldata, "BOUNDS\n"); + putheader = FALSE; + } + 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) : " "); + 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) : " "); + } + else + write_data(userhandle, write_modeldata, " UP BND %s %s\n", + MPSname(name0, get_col_name(lp, j)), + formatnumber12(numberbuffer, (double)a)); + } + } + } + + /* Write optional SOS section */ + putheader = TRUE; + for(i = 0; i < SOS_count(lp); i++) { + SOSgroup *SOS = lp->SOS; + + if(putheader) { + write_data(userhandle, write_modeldata, "SOS\n"); + putheader = FALSE; + } + 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)); + 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])); + } + } + + write_data(userhandle, write_modeldata, "ENDATA\n"); + + lp->names_used = names_used; + + return(ok); +} + +static int __WINAPI write_lpdata(void *userhandle, char *buf) +{ + fputs(buf, (FILE *) userhandle); + return(TRUE); +} + +MYBOOL MPS_writefile(lprec *lp, int typeMPS, char *filename) +{ + FILE *output = stdout; + MYBOOL ok; + + if (filename != NULL) { + ok = ((output = fopen(filename, "w")) != NULL); + if(!ok) + return(ok); + } + else + output = lp->outstream; + + ok = MPS_writefileex(lp, typeMPS, (void *) output, write_lpdata); + + if (filename != NULL) + fclose(output); + + return(ok); +} + +MYBOOL MPS_writehandle(lprec *lp, int typeMPS, FILE *output) +{ + MYBOOL ok; + + if (output != NULL) + set_outputstream(lp, output); + + output = lp->outstream; + + ok = MPS_writefileex(lp, typeMPS, (void *) output, write_lpdata); + + return(ok); +} + + +/* Read and write BAS files */ +/* #define OldNameMatch */ +#ifdef OldNameMatch +static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL isrow) +{ + int in = -1; + + in = get_nameindex(lp, varname, isrow); + if((in < 0) && (strncmp(varname, (isrow ? ROWNAMEMASK : COLNAMEMASK), 1) == 0)) { + if(sscanf(varname + 1, "%d", &in) != 1) + in = -1; + } + return( in ); +} +#else +static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL tryrowfirst) +{ + int in = -1; + + /* Have we defined our own variable names? */ + if(lp->names_used) { + /* First check the primary name list */ + in = get_nameindex(lp, varname, tryrowfirst); + if((in > 0) && !tryrowfirst) + in += lp->rows; + /* If we were unsuccessful, try the secondary name list */ + else if(in < 0) { + in = get_nameindex(lp, varname, (MYBOOL) !tryrowfirst); + if((in > 0) && tryrowfirst) + in += lp->rows; + } + } + /* If not, see if we can match the standard name mask */ + + if(in == -1) { + 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) || + (in < (tryrowfirst ? 0 : 1)) || (in > (tryrowfirst ? lp->rows : lp->columns))) + 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) || + (in < (tryrowfirst ? 0 : 1)) || (in > (tryrowfirst ? lp->rows : lp->columns))) + in = -1; + } + } + return( in ); +} +#endif + +MYBOOL MPS_readBAS(lprec *lp, int typeMPS, char *filename, char *info) +{ + char field1[BUFSIZ], field2[BUFSIZ], field3[BUFSIZ], field5[BUFSIZ], + line[BUFSIZ], tmp[BUFSIZ], *ptr; + double field4, field6; + 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, + 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); + } + + ok = (MYBOOL) ((filename != NULL) && ((input = fopen(filename,"r")) != NULL)); + if(!ok) + return(ok); + default_basis(lp); + + /* Let's initialize line to all zero's */ + MEMCLEAR(line, BUFSIZ); + ok = FALSE; + while(fgets(line, BUFSIZ - 1, input)) { + Lineno++; + + for(ptr = line; (*ptr) && (isspace((unsigned char) *ptr)); ptr++); + + /* skip lines which start with "*", they are comment */ + if((line[0] == '*') || (*ptr == 0) || (*ptr == '\n') || (*ptr == '\r')) { + report(lp, FULL, "Comment on line %d: %s", Lineno, line); + continue; + } + + report(lp, FULL, "Line %6d: %s", Lineno, line); + + /* first check for "special" lines: in our case only NAME and ENDATA, + ...this must start in the first position of line */ + if(line[0] != ' ') { + sscanf(line, "%s", tmp); + if(strcmp(tmp, "NAME") == 0) { + if(info != NULL) { + *info = 0; + for(ptr = line + 4; (*ptr) && (isspace((unsigned char) *ptr)); ptr++); + in = (int) strlen(ptr); + while ((in > 0) && ((ptr[in - 1] == '\r') || (ptr[in - 1] == '\n') || isspace(ptr[in - 1]))) + in--; + ptr[in] = 0; + strcpy(info, ptr); + } + } + else if(strcmp(tmp, "ENDATA") == 0) { + report(lp, FULL, "Finished reading BAS file\n"); + ok = TRUE; + break; + } + else { /* line does not start with space and does not match above */ + report(lp, IMPORTANT, "Unrecognized BAS line %d: %s\n", Lineno, line); + break; + } + } + else { /* normal line, process */ + items = scan_line(lp, /* MPSRHS */ MPSBOUNDS, line, field1, field2, field3, &field4, field5, &field6); + if(items < 0){ + report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line); + break; + } + /* find first variable index value */ + in = MPS_getnameidx(lp, field2, FALSE); +#ifdef OldNameMatch + if(in < 0) + in = MPS_getnameidx(lp, field2, TRUE); + else + in += lp->rows; +#endif + if(in < 0) + break; + + /* check if we have the basic/non-basic variable format */ + if(field1[0] == 'X') { + /* find second variable index value */ + ib = in; + in = MPS_getnameidx(lp, field3, FALSE); +#ifdef OldNameMatch + if(in < 0) + in = MPS_getnameidx(lp, field3, TRUE); + else + in += lp->rows; +#endif + if(in < 0) + break; + + lp->is_lower[in] = (MYBOOL) (field1[1] == 'L'); + lp->is_basic[ib] = TRUE; + } + else + lp->is_lower[in] = (MYBOOL) (field1[0] == 'L'); + + lp->is_basic[in] = FALSE; + + } + } + /* Update the basis index-to-variable array */ + ib = 0; + items = lp->sum; + for(in = 1; in <= items; in++) + if(lp->is_basic[in]) { + ib++; + lp->var_basic[ib] = in; + } + + fclose(input); + return( ok ); +} + +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]; + + /* 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); + } + + /* Open the file for writing */ + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); + if(!ok) + return(ok); + if(filename == NULL && lp->outstream != NULL) + output = lp->outstream; + + fprintf(output, "NAME %s Rows %d Cols %d Iters %.0f\n", + get_lp_name(lp), lp->rows, lp->columns, (double) get_total_iter(lp)); + + ib = lp->rows; + in = 0; + while ((ib < lp->sum) || (in < lp->sum)) { + + /* Find next basic variable (skip slacks) */ + ib++; + while((ib <= lp->sum) && !lp->is_basic[ib]) + ib++; + + /* Find next non-basic variable (skip lower-bounded structural variables) */ + in++; + while((in <= lp->sum) && (lp->is_basic[in] || + ((in > lp->rows) && lp->is_lower[in]))) + in++; + + /* 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) : + get_col_name(lp, ib-lp->rows)))); + strcpy(name2, MPSname(name0, (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) : + get_col_name(lp, in-lp->rows)))); + fprintf(output, " %2s %s\n", (lp->is_lower[in] ? "LL" : "UL"), name1); + } + + } + fprintf(output, "ENDATA\n"); + + if(filename != NULL) + fclose(output); + return( ok ); +} diff --git a/src/external/lpsolve/build/lp_solve/lp_SOS.c b/src/external/lpsolve/build/lp_solve/lp_SOS.c new file mode 100644 index 00000000..512ab548 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_SOS.c @@ -0,0 +1,1575 @@ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_report.h" +#include "lp_SOS.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* + Specially Ordered Set (SOS) routines - w/interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h + + Release notes: + v1.0 1 September 2003 Complete package for SOS creation and use in a LP + setting. Notable feature of this implementation + compared to those in other commercial systems is + the generalization to SOS'es of "unlimited" order. + v1.1 8 December 2003 Added variable (index) deletion method. + v1.2 17 December 2004 Added bound change tracking functionality. + v1.3 18 September 2005 Added sparse SOS handling to speed up processing + of large number of SOS'es. + + ---------------------------------------------------------------------------------- +*/ + +/* SOS group functions */ +STATIC SOSgroup *create_SOSgroup(lprec *lp) +{ + SOSgroup *group; + + group = (SOSgroup *) calloc(1, sizeof(*group)); + group->lp = lp; + group->sos_alloc = SOS_START_SIZE; + group->sos_list = (SOSrec **) malloc((group->sos_alloc) * sizeof(*group->sos_list)); + return(group); +} + +STATIC void resize_SOSgroup(SOSgroup *group) +{ + if(group->sos_count == group->sos_alloc) { + group->sos_alloc = (int)((double) group->sos_alloc*RESIZEFACTOR); + group->sos_list = (SOSrec **) realloc(group->sos_list, + (group->sos_alloc) * sizeof(*group->sos_list)); + } +} + +STATIC int append_SOSgroup(SOSgroup *group, SOSrec *SOS) +{ + int i, k; + SOSrec *SOSHold; + + /* Check if we should resize */ + resize_SOSgroup(group); + + /* First append to the end of the list */ + group->sos_list[group->sos_count] = SOS; + group->sos_count++; + i = abs(SOS->type); + SETMAX(group->maxorder, i); + if(i == 1) + group->sos1_count++; + k = group->sos_count; + SOS->tagorder = k; + + /* Sort the SOS list by given priority */ + for(i = group->sos_count-1; i > 0; i--) { + if(group->sos_list[i]->priority < group->sos_list[i-1]->priority) { + SOSHold = group->sos_list[i]; + group->sos_list[i] = group->sos_list[i-1]; + group->sos_list[i-1] = SOSHold; + if(SOSHold == SOS) + k = i; /* This is the index in the [1..> range */ + } + else + break; + } + /* Return the list index of the new SOS */ + return( k ); +} + + +STATIC int clean_SOSgroup(SOSgroup *group, MYBOOL forceupdatemap) +{ + int i, n, k; + SOSrec *SOS; + + if(group == NULL) + return( 0 ); + + /* Delete any SOS without members or trivial member count */ + n = 0; + if(group->sos_alloc > 0) { + group->maxorder = 0; + for(i = group->sos_count; i > 0; i--) { + SOS = group->sos_list[i-1]; + k = SOS->members[0]; + if((k == 0) || /* Empty */ + ((k == abs(SOS->type)) && (k <= 2))) { /* Trivial */ + delete_SOSrec(group, i); + n++; + } + else { + SETMAX(group->maxorder, abs(SOS->type)); + } + } + if((n > 0) || forceupdatemap) + SOS_member_updatemap(group); + } + return( n ); +} + + +STATIC void free_SOSgroup(SOSgroup **group) +{ + int i; + + if((group == NULL) || (*group == NULL)) + return; + if((*group)->sos_alloc > 0) { + for(i = 0; i < (*group)->sos_count; i++) + free_SOSrec((*group)->sos_list[i]); + FREE((*group)->sos_list); + FREE((*group)->membership); + FREE((*group)->memberpos); + } + FREE(*group); +} + +/* SOS record functions */ +STATIC SOSrec *create_SOSrec(SOSgroup *group, char *name, int type, int priority, int size, int *variables, LPSREAL *weights) +{ + SOSrec *SOS; + + SOS = (SOSrec *) calloc(1 , sizeof(*SOS)); + SOS->parent = group; + SOS->type = type; + if(name == NULL) + SOS->name = NULL; + else + { + allocCHAR(group->lp, &SOS->name, (int) (strlen(name)+1), FALSE); + strcpy(SOS->name, name); + } + if(type < 0) + type = abs(type); + SOS->tagorder = 0; + SOS->size = 0; + SOS->priority = priority; + SOS->members = NULL; + SOS->weights = NULL; + SOS->membersSorted = NULL; + SOS->membersMapped = NULL; + + if(size > 0) + size = append_SOSrec(SOS, size, variables, weights); + + return(SOS); +} + + +STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, LPSREAL *weights) +{ + int i, oldsize, newsize, nn; + lprec *lp = SOS->parent->lp; + + oldsize = SOS->size; + newsize = oldsize + size; + nn = abs(SOS->type); + + /* Shift existing active data right (normally zero) */ + if(SOS->members == NULL) + allocINT(lp, &SOS->members, 1+newsize+1+nn, TRUE); + else { + allocINT(lp, &SOS->members, 1+newsize+1+nn, AUTOMATIC); + for(i = newsize+1+nn; i > newsize+1; i--) + SOS->members[i] = SOS->members[i-size]; + } + SOS->members[0] = newsize; + SOS->members[newsize+1] = nn; + + /* Copy the new data into the arrays */ + if(SOS->weights == NULL) + allocREAL(lp, &SOS->weights, 1+newsize, TRUE); + else + allocREAL(lp, &SOS->weights, 1+newsize, AUTOMATIC); + for(i = oldsize+1; i <= newsize; i++) { + SOS->members[i] = variables[i-oldsize-1]; + if((SOS->members[i] < 1) || (SOS->members[i] > lp->columns)) + report(lp, IMPORTANT, "append_SOS_rec: Invalid SOS variable definition for index %d\n", SOS->members[i]); + else { + if(SOS->isGUB) + lp->var_type[SOS->members[i]] |= ISGUB; + else + lp->var_type[SOS->members[i]] |= ISSOS; + } + if(weights == NULL) + SOS->weights[i] = i; /* Follow standard, which is sorted ascending */ + else + SOS->weights[i] = weights[i-oldsize-1]; + SOS->weights[0] += SOS->weights[i]; + } + + /* Sort the new paired lists ascending by weight (simple bubble sort) */ + i = sortByREAL(SOS->members, SOS->weights, newsize, 1, TRUE); + if(i > 0) + report(lp, DETAILED, "append_SOS_rec: Non-unique SOS variable weight for index %d\n", i); + + /* Define mapping arrays to search large SOS's faster */ + allocINT(lp, &SOS->membersSorted, newsize, AUTOMATIC); + allocINT(lp, &SOS->membersMapped, newsize, AUTOMATIC); + for(i = oldsize+1; i <= newsize; i++) { + SOS->membersSorted[i - 1] = SOS->members[i]; + SOS->membersMapped[i - 1] = i; + } + sortByINT(SOS->membersMapped, SOS->membersSorted, newsize, 0, TRUE); + + /* Confirm the new size */ + SOS->size = newsize; + + return(newsize); + +} + +STATIC int make_SOSchain(lprec *lp, MYBOOL forceresort) +{ + int i, j, k, n; + MYBOOL *hold = NULL; + LPSREAL *order, sum, weight; + SOSgroup *group = lp->SOS; + + /* PART A: Resort individual SOS member lists, if specified */ + if(forceresort) + SOS_member_sortlist(group, 0); + + /* PART B: Tally SOS variables and create master SOS variable list */ + n = 0; + for(i = 0; i < group->sos_count; i++) + n += group->sos_list[i]->size; + lp->sos_vars = n; + if(lp->sos_vars > 0) /* Prevent memory loss in case of multiple solves */ + FREE(lp->sos_priority); + allocINT(lp, &lp->sos_priority, n, FALSE); + allocREAL(lp, &order, n, FALSE); + + /* Move variable data to the master SOS list and sort by ascending weight */ + n = 0; + sum = 0; + for(i = 0; i < group->sos_count; i++) { + for(j = 1; j <= group->sos_list[i]->size; j++) { + lp->sos_priority[n] = group->sos_list[i]->members[j]; + weight = group->sos_list[i]->weights[j]; + sum += weight; + order[n] = sum; + n++; + } + } + hpsortex(order, n, 0, sizeof(*order), FALSE, compareREAL, lp->sos_priority); + FREE(order); + + /* Remove duplicate SOS variables */ + allocMYBOOL(lp, &hold, lp->columns+1, TRUE); + k = 0; + for(i = 0; i < n; i++) { + j = lp->sos_priority[i]; + if(!hold[j]) { + hold[j] = TRUE; + if(k < i) + lp->sos_priority[k] = j; + k++; + } + } + FREE(hold); + + /* Adjust the size of the master variable list, if necessary */ + if(k < lp->sos_vars) { + allocINT(lp, &lp->sos_priority, k, AUTOMATIC); + lp->sos_vars = k; + } + + return( k ); + +} + + +STATIC MYBOOL delete_SOSrec(SOSgroup *group, int sosindex) +{ +#ifdef Paranoia + if((sosindex <= 0) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "delete_SOSrec: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + /* Delete and free the SOS record */ + if(abs(SOS_get_type(group, sosindex)) == 1) + group->sos1_count--; + free_SOSrec(group->sos_list[sosindex-1]); + while(sosindex < group->sos_count) { + group->sos_list[sosindex-1] = group->sos_list[sosindex]; + sosindex++; + } + group->sos_count--; + + /* Update maxorder */ + group->maxorder = 0; + for(sosindex = 0; sosindex < group->sos_count; sosindex++) { + SETMAX(group->maxorder, abs(group->sos_list[sosindex]->type)); + } + + return(TRUE); +} + + +STATIC void free_SOSrec(SOSrec *SOS) +{ + if(SOS->name != NULL) + FREE(SOS->name); + if(SOS->size > 0) { + FREE(SOS->members); + FREE(SOS->weights); + FREE(SOS->membersSorted); + FREE(SOS->membersMapped); + } + FREE(SOS); +} + + +STATIC MYBOOL SOS_member_sortlist(SOSgroup *group, int sosindex) +/* Routine to (re-)sort SOS member arrays for faster access to large SOSes */ +{ + int i, n; + int *list; + lprec *lp = group->lp; + SOSrec *SOS; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_member_sortlist: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(!SOS_member_sortlist(group, i)) + return(FALSE); + } + } + else { + SOS = group->sos_list[sosindex-1]; + list = SOS->members; + n = list[0]; + /* Make sure that the arrays are properly allocated and sized */ + if(n != group->sos_list[sosindex-1]->size) { + allocINT(lp, &SOS->membersSorted, n, AUTOMATIC); + allocINT(lp, &SOS->membersMapped, n, AUTOMATIC); + group->sos_list[sosindex-1]->size = n; + } + /* Reload the arrays and do the sorting */ + for(i = 1; i <= n; i++) { + SOS->membersSorted[i - 1] = list[i]; + SOS->membersMapped[i - 1] = i; + } + sortByINT(SOS->membersMapped, SOS->membersSorted, n, 0, TRUE); + } + return( TRUE ); +} + +STATIC int SOS_member_updatemap(SOSgroup *group) +{ + int i, j, k, n, nvars = 0, + *list, *tally = NULL; + SOSrec *rec; + lprec *lp = group->lp; + + /* (Re)-initialize usage arrays */ + allocINT(lp, &group->memberpos, lp->columns+1, AUTOMATIC); + allocINT(lp, &tally, lp->columns+1, TRUE); + + /* Get each variable's SOS membership count */ + for(i = 0; i < group->sos_count; i++) { + rec = group->sos_list[i]; + n = rec->size; + list = rec->members; + for(j = 1; j <= n; j++) { + k = list[j]; +#ifdef Paranoia + if((k < 1) || (k > lp->columns)) + report(lp, SEVERE, "SOS_member_updatemap: Member %j of SOS number %d is out of column range (%d)\n", + j, i+1, k); +#endif + tally[k]++; + } + + } + + /* Compute pointer into column-sorted array */ + group->memberpos[0] = 0; + for(i = 1; i <= lp->columns; i++) { + n = tally[i]; + if(n > 0) + nvars++; + group->memberpos[i] = group->memberpos[i-1] + n; + } + n = group->memberpos[lp->columns]; + MEMCOPY(tally+1, group->memberpos, lp->columns); + + /* Load the column-sorted SOS indeces / pointers */ + allocINT(lp, &group->membership, n+1, AUTOMATIC); + for(i = 0; i < group->sos_count; i++) { + rec = group->sos_list[i]; + n = rec->size; + list = rec->members; + for(j = 1; j <= n; j++) { + k = tally[list[j]]++; +#ifdef Paranoia + if(k > group->memberpos[lp->columns]) + report(lp, SEVERE, "SOS_member_updatemap: Member mapping for variable %j of SOS number %d is invalid\n", + list[j], i+1); +#endif + group->membership[k] = i+1; + } + } + FREE(tally); + + return( nvars ); +} + + +STATIC MYBOOL SOS_shift_col(SOSgroup *group, int sosindex, int column, int delta, LLrec *usedmap, MYBOOL forceresort) +/* Routine to adjust SOS indeces for variable insertions or deletions; + Note: SOS_shift_col must be called before make_SOSchain! */ +{ + int i, ii, n, nn, nr; + int changed; + int *list; + LPSREAL *weights; + +#ifdef Paranoia + lprec *lp = group->lp; + + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_shift_col: Invalid SOS index %d\n", sosindex); + return(FALSE); + } + else if((column < 1) || (delta == 0)) { + report(lp, IMPORTANT, "SOS_shift_col: Invalid column %d specified with delta %d\n", + column, delta); + return(FALSE); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(!SOS_shift_col(group, i, column, delta, usedmap, forceresort)) + return(FALSE); + } + } + else { + list = group->sos_list[sosindex-1]->members; + weights = group->sos_list[sosindex-1]->weights; + n = list[0]; + nn = list[n+1]; + + /* Case where variable indeces are to be incremented */ + if(delta > 0) { + for(i = 1; i <= n; i++) { + if(list[i] >= column) + list[i] += delta; + } + } + /* Case where variables are to be deleted/indeces decremented */ + else { + changed = 0; + if(usedmap != NULL) { + int *newidx = NULL; + /* Defer creation of index mapper until we are sure that a + member of this SOS is actually targeted for deletion */ + if(newidx == NULL) { + allocINT(group->lp, &newidx, group->lp->columns+1, TRUE); + for(i = firstActiveLink(usedmap), ii = 1; i != 0; + i = nextActiveLink(usedmap, i), ii++) + newidx[i] = ii; + } + for(i = 1, ii = 0; i <= n; i++) { + nr = list[i]; + /* Check if this SOS variable should be deleted */ + if(!isActiveLink(usedmap, nr)) + continue; + + /* If the index is "high" then make adjustment and shift */ + changed++; + ii++; + list[ii] = newidx[nr]; + weights[ii] = weights[i]; + } + FREE(newidx); + } + else + for(i = 1, ii = 0; i <= n; i++) { + nr = list[i]; + /* Check if this SOS variable should be deleted */ + if((nr >= column) && (nr < column-delta)) + continue; + /* If the index is "high" then decrement */ + if(nr > column) { + changed++; + nr += delta; + } + ii++; + list[ii] = nr; + weights[ii] = weights[i]; + } + /* Update the SOS length / type indicators */ + if(ii < n) { + list[0] = ii; + list[ii+1] = nn; + } + + /* Update mapping arrays to search large SOS's faster */ + if(forceresort && ((ii < n) || (changed > 0))) + SOS_member_sortlist(group, sosindex); + } + + } + return(TRUE); + +} + +int SOS_member_count(SOSgroup *group, int sosindex) +{ + SOSrec *SOS; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "SOS_member_count: Invalid SOS index %d\n", sosindex); + return( -1 ); + } +#endif + SOS = group->sos_list[sosindex-1]; + return( SOS->members[0] ); +} + +int SOS_member_delete(SOSgroup *group, int sosindex, int member) +{ + int *list, i, i2, k, n, nn = 0; + SOSrec *SOS; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "SOS_member_delete: Invalid SOS index %d\n", sosindex); + return( -1 ); + } +#endif + + if(sosindex == 0) { + for(i = group->memberpos[member-1]; i < group->memberpos[member]; i++) { + k = group->membership[i]; + n = SOS_member_delete(group, k, member); + if(n >= 0) + nn += n; + else + return( n ); + } + /* We must update the mapper */ + k = group->memberpos[member]; + i = group->memberpos[member-1]; + n = group->memberpos[lp->columns] - k; + if(n > 0) + MEMCOPY(group->membership + i, group->membership + k, n); + for(i = member; i <= lp->columns; i++) + group->memberpos[i] = group->memberpos[i-1]; + } + else { + SOS = group->sos_list[sosindex-1]; + list = SOS->members; + n = list[0]; + + /* Find the offset of the member */ + i = 1; + while((i <= n) && (abs(list[i]) != member)) + i++; + if(i > n) + return( -1 ); + nn++; + + /* Shift remaining members *and* the active count one position left */ + while(i <= n) { + list[i] = list[i+1]; + i++; + } + list[0]--; + SOS->size--; + + /* Do the same with the active list one position left */ + i = n + 1; + i2 = i + list[n]; + k = i + 1; + while(i < i2) { + if(abs(list[k]) == member) + k++; + list[i] = list[k]; + i++; + k++; + } + } + + return( nn ); +} + +int SOS_get_type(SOSgroup *group, int sosindex) +{ +#ifdef Paranoia + if((sosindex < 1) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "SOS_get_type: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + return(group->sos_list[sosindex-1]->type); +} + + +int SOS_infeasible(SOSgroup *group, int sosindex) +{ + int i, n, nn, varnr, failindex, *list; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_infeasible: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(sosindex == 0 && group->sos_count == 1) + sosindex = 1; + + failindex = 0; + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + failindex = SOS_infeasible(group, i); + if(failindex > 0) break; + } + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]; + nn = list[n+1]; + /* Find index of next lower-bounded variable */ + for(i = 1; i <= n; i++) { + varnr = abs(list[i]); + if((lp->orig_lowbo[lp->rows + varnr] > 0) && + !((lp->sc_vars > 0) && is_semicont(lp, varnr))) + break; + } + + /* Find if there is another lower-bounded variable beyond the type window */ + i = i+nn; + while(i <= n) { + varnr = abs(list[i]); + if((lp->orig_lowbo[lp->rows + varnr] > 0) && + !((lp->sc_vars > 0) && is_semicont(lp, varnr))) + break; + i++; + } + if(i <= n) + failindex = abs(list[i]); + } + return(failindex); +} + + +int SOS_member_index(SOSgroup *group, int sosindex, int member) +{ + int n; + SOSrec *SOS; + + SOS = group->sos_list[sosindex-1]; + n = SOS->members[0]; + + n = searchFor(member, SOS->membersSorted, n, 0, FALSE); + if(n >= 0) + n = SOS->membersMapped[n]; + + return(n); +} + + +int SOS_memberships(SOSgroup *group, int varnr) +{ + int i, n = 0; + lprec *lp; + + /* Check if there is anything to do */ + if((group == NULL) || (SOS_count(lp = group->lp) == 0)) + return( n ); + +#ifdef Paranoia + if((varnr < 0) || (varnr > lp->columns)) { + report(lp, IMPORTANT, "SOS_memberships: Invalid variable index %d given\n", varnr); + return( n ); + } +#endif + + if(varnr == 0) { + for(i = 1; i <= lp->columns; i++) + if(group->memberpos[i] > group->memberpos[i-1]) + n++; + } + else + n = group->memberpos[varnr] - group->memberpos[varnr-1]; + + return( n ); +} + + +int SOS_is_member(SOSgroup *group, int sosindex, int column) +{ + int i, n = FALSE, *list; + lprec *lp; + + if(group == NULL) + return( FALSE ); + lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_member: Invalid SOS index %d\n", sosindex); + return(n); + } +#endif + + if(sosindex == 0) { + if(lp->var_type[column] & (ISSOS | ISGUB)) + n = (MYBOOL) (SOS_memberships(group, column) > 0); + } + else if(lp->var_type[column] & (ISSOS | ISGUB)) { + + /* Search for the variable */ + i = SOS_member_index(group, sosindex, column); + + /* Signal active status if found, otherwise return FALSE */ + if(i > 0) { + list = group->sos_list[sosindex-1]->members; + if(list[i] < 0) + n = -TRUE; + else + n = TRUE; + } + } + return(n); +} + + +MYBOOL SOS_is_member_of_type(SOSgroup *group, int column, int sostype) +{ + int i, k, n; + + if(group != NULL) + for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) { + k = group->membership[i]; + n = SOS_get_type(group, k); + if(((n == sostype) || + ((sostype == SOSn) && (n > 2))) && SOS_is_member(group, k, column)) + return(TRUE); + } + return(FALSE); +} + + +MYBOOL SOS_set_GUB(SOSgroup *group, int sosindex, MYBOOL state) +{ + int i; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "SOS_set_GUB: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) + SOS_set_GUB(group, i, state); + } + else + group->sos_list[sosindex-1]->isGUB = state; + return(TRUE); +} + + +MYBOOL SOS_is_GUB(SOSgroup *group, int sosindex) +{ + int i; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "SOS_is_GUB: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(SOS_is_GUB(group, i)) + return(TRUE); + } + return(FALSE); + } + else + return( group->sos_list[sosindex-1]->isGUB ); +} + + +MYBOOL SOS_is_marked(SOSgroup *group, int sosindex, int column) +{ + int i, k, n, *list; + lprec *lp; + + if(group == NULL) + return( FALSE ); + lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_marked: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->var_type[column] & (ISSOS | ISGUB))) + return(FALSE); + + if(sosindex == 0) { + for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) { + k = group->membership[i]; + n = SOS_is_marked(group, k, column); + if(n) + return(TRUE); + } + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]; + + /* Search for the variable (normally always faster to do linear search here) */ + column = -column; + for(i = 1; i <= n; i++) + if(list[i] == column) + return(TRUE); + } + return(FALSE); +} + + +MYBOOL SOS_is_active(SOSgroup *group, int sosindex, int column) +{ + int i, n, nn, *list; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_active: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->var_type[column] & (ISSOS | ISGUB))) + return(FALSE); + + if(sosindex == 0) { + for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) { + nn = group->membership[i]; + n = SOS_is_active(group, nn, column); + if(n) + return(TRUE); + } + } + else { + + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Scan the active (non-zero) SOS index list */ + for(i = 1; (i <= nn) && (list[n+i] != 0); i++) + if(list[n+i] == column) + return(TRUE); + } + return(FALSE); +} + + +MYBOOL SOS_is_full(SOSgroup *group, int sosindex, int column, MYBOOL activeonly) +{ + int i, nn, n, *list; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_full: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->var_type[column] & (ISSOS | ISGUB))) + return(FALSE); + + if(sosindex == 0) { + for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) { + nn = group->membership[i]; + if(SOS_is_full(group, nn, column, activeonly)) + return(TRUE); + } + } + else if(SOS_is_member(group, sosindex, column)) { + + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Info: Last item in the active list is non-zero if the current SOS is full */ + if(list[n+nn] != 0) + return(TRUE); + + if(!activeonly) { + /* Spool to last active variable */ + for(i = nn-1; (i > 0) && (list[n+i] == 0); i--); + /* Having found it, check if subsequent variables are set (via bounds) as inactive */ + if(i > 0) { + nn -= i; /* Compute unused active slots */ + i = SOS_member_index(group, sosindex, list[n+i]); + for(; (nn > 0) && (list[i] < 0); i++, nn--); + if(nn == 0) + return(TRUE); + } + } + } + + return(FALSE); +} + + +MYBOOL SOS_can_activate(SOSgroup *group, int sosindex, int column) +{ + int i, n, nn, nz, *list; + lprec *lp; + + if(group == NULL) + return( FALSE ); + lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_can_activate: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->var_type[column] & (ISSOS | ISGUB))) + return(FALSE); + + if(sosindex == 0) { + for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) { + nn = group->membership[i]; + n = SOS_can_activate(group, nn, column); + if(n == FALSE) + return(FALSE); + } + } + else if(SOS_is_member(group, sosindex, column)) { + + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + +#if 0 + /* Accept if the SOS is empty */ + if(list[n+1] == 0) + return(TRUE); +#endif + + /* 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) */ + if(nn > 1) { + + /* Find the variable that was last activated; + Also check that the candidate variable is not already active */ + for(i = 1; i <= nn; i++) { + if(list[n+i] == 0) + break; + if(list[n+i] == column) + return(FALSE); + } + 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 */ + n = list[0]; + for(i = 1; i <= n; i++) + 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 */ + + /* Check left neighbour */ + if((i > 1) && (list[i-1] == column)) + return(TRUE); + /* Check right neighbour */ + if((i < n) && (list[i+1] == column)) + return(TRUE); + + /* It is not the right neighbour; return false */ + return(FALSE); + } + } + return(TRUE); +} + + +MYBOOL SOS_set_marked(SOSgroup *group, int sosindex, int column, MYBOOL asactive) +{ + int i, n, nn, *list; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_set_marked: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->var_type[column] & (ISSOS | ISGUB))) + return(FALSE); + + if(sosindex == 0) { + + /* Define an IBM-"SOS3" member variable temporarily as integer, if it is + not already a permanent integer; is reset in SOS_unmark */ + if(asactive && !is_int(lp, column) && SOS_is_member_of_type(group, column, SOS3)) { + lp->var_type[column] |= ISSOSTEMPINT; + set_int(lp, column, TRUE); + } + + nn = 0; + for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) { + n = group->membership[i]; + if(SOS_set_marked(group, n, column, asactive)) + nn++; + } + return((MYBOOL) (nn == group->sos_count)); + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Search for the variable */ + i = SOS_member_index(group, sosindex, column); + + /* First mark active in the set member list as used */ + if((i > 0) && (list[i] > 0)) + list[i] *= -1; + else + return(TRUE); + + /* Then move the variable to the live list */ + if(asactive) { + for(i = 1; i <= nn; i++) { + if(list[n+i] == column) + return(FALSE); + else if(list[n+i] == 0) { + list[n+i] = column; + return(FALSE); + } + } + } + return(TRUE); + } +} + + +MYBOOL SOS_unmark(SOSgroup *group, int sosindex, int column) +{ + int i, n, nn, *list; + MYBOOL isactive; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_unmark: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->var_type[column] & (ISSOS | ISGUB))) + return(FALSE); + + + if(sosindex == 0) { + + /* Undefine a SOS3 member variable that has temporarily been set as integer */ + if(lp->var_type[column] & ISSOSTEMPINT) { + lp->var_type[column] &= !ISSOSTEMPINT; + set_int(lp, column, FALSE); + } + + nn = 0; + for(i = group->memberpos[column-1]; i < group->memberpos[column]; i++) { + n = group->membership[i]; + if(SOS_unmark(group, n, column)) + nn++; + } + return((MYBOOL) (nn == group->sos_count)); + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Search for the variable */ + i = SOS_member_index(group, sosindex, column); + + /* Restore sign in main list */ + if((i > 0) && (list[i] < 0)) + list[i] *= -1; + else + return(TRUE); + + /* Find the variable in the active list... */ + isactive = SOS_is_active(group, sosindex, column); + if(isactive) { + for(i = 1; i <= nn; i++) + if(list[n+i] == column) + break; + /* ...shrink the list if found, otherwise return error */ + if(i <= nn) { + for(; ilp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_fix_unmarked: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + count = 0; + if(sosindex == 0) { + for(i = group->memberpos[variable-1]; i < group->memberpos[variable]; i++) { + n = group->membership[i]; + count += SOS_fix_unmarked(group, n, variable, bound, value, isupper, diffcount, changelog); + } + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + + /* Count the number of active and free SOS variables */ + nn = list[n]; + for(i = 1; i <= nn; i++) { + if(list[n+i] == 0) + break; + } + i--; + i = nn - i; /* Establish the number of unused slots */ + + /* Determine the free SOS variable window */ + if(i == nn) { + nLeft = 0; + nRight = SOS_member_index(group, sosindex, variable); + } + else { + nLeft = SOS_member_index(group, sosindex, list[n+1]); + if(variable == list[n+1]) + nRight = nLeft; + else + nRight = SOS_member_index(group, sosindex, variable); + } + + nRight += i; /* Loop (nRight+1)..n */ + + /* Fix variables outside of the free SOS variable window */ + for(i = 1; i < n; i++) { + /* Skip the SOS variable window */ + if((i >= nLeft) && (i <= nRight)) + continue; + /* Otherwise proceed to set bound */ + ii = list[i]; + if(ii > 0) { + ii += lp->rows; + if(bound[ii] != value) { + /* Verify that we don't violate original bounds */ + if(isupper && (value < lp->orig_lowbo[ii])) + return(-ii); + else if(!isupper && (value > lp->orig_upbo[ii])) + return(-ii); + /* OK, set the new bound */ + count++; + if(changelog == NULL) + bound[ii] = value; + else + modifyUndoLadder(changelog, ii, bound, value); + + } + if((diffcount != NULL) && (lp->solution[ii] != value)) + (*diffcount)++; + } + } + } + return(count); +} + +int *SOS_get_candidates(SOSgroup *group, int sosindex, int column, MYBOOL excludetarget, + LPSREAL *upbound, LPSREAL *lobound) +{ + int i, ii, j, n, nn = 0, *list, *candidates = NULL; + lprec *lp = group->lp; + + if(group == NULL) + return( candidates ); + +#ifdef Paranoia + if(sosindex > group->sos_count) { + report(lp, IMPORTANT, "SOS_get_candidates: Invalid index %d\n", sosindex); + return( candidates ); + } +#endif + + /* Determine SOS target(s); note that if "sosindex" is negative, only + the first non-empty SOS where "column" is a member is processed */ + if(sosindex <= 0) { + i = 0; + ii = group->sos_count; + } + else { + i = sosindex - 1; + ii = sosindex; + } + + /* Tally candidate usage */ + allocINT(lp, &candidates, lp->columns+1, TRUE); + for(; i < ii; i++) { + if(!SOS_is_member(group, i+1, column)) + continue; + list = group->sos_list[i]->members; + n = list[0]; + while(n > 0) { + j = list[n]; + if((j > 0) && (upbound[lp->rows+j] > 0)) { + if(lobound[lp->rows+j] > 0) { + report(lp, IMPORTANT, "SOS_get_candidates: Invalid non-zero lower bound setting\n"); + n = 0; + goto Finish; + } + if(candidates[j] == 0) + nn++; + candidates[j]++; + } + n--; + } + if((sosindex < 0) && (nn > 1)) + break; + } + + /* Condense the list into indeces */ + n = 0; + for(i = 1; i <= lp->columns; i++) { + if((candidates[i] > 0) && (!excludetarget || (i != column))) { + n++; + candidates[n] = i; + } + } + + /* Finalize */ +Finish: + candidates[0] = n; + if(n == 0) + FREE(candidates); + + return( candidates); + +} + +int SOS_fix_list(SOSgroup *group, int sosindex, int variable, LPSREAL *bound, + int *varlist, MYBOOL isleft, DeltaVrec *changelog) +{ + int i, ii, jj, count = 0; + LPSREAL value = 0; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_fix_list: Invalid index %d\n", sosindex); + return(FALSE); + } +#endif + + if(sosindex == 0) { + for(i = group->memberpos[variable-1]; i < group->memberpos[variable]; i++) { + ii = group->membership[i]; + count += SOS_fix_list(group, ii, variable, bound, varlist, isleft, changelog); + } + } + else { + + /* Establish the number of unmarked variables in the left window + (note that "variable" should have been marked previously) */ + ii = varlist[0] / 2; + if(isleft) { + i = 1; + if(isleft == AUTOMATIC) + ii = varlist[0]; + } + else { + i = ii + 1; + ii = varlist[0]; + } + + /* Loop over members to fix values at the new bound (zero) */ + while(i <= ii) { + if(SOS_is_member(group, sosindex, varlist[i])) { + jj = lp->rows + varlist[i]; + + /* Verify that we don't violate original bounds */ + if(value < lp->orig_lowbo[jj]) + return( -jj ); + /* OK, set the new bound */ + count++; + if(changelog == NULL) + bound[jj] = value; + else + modifyUndoLadder(changelog, jj, bound, value); + } + i++; + } + + } + return( count ); +} + +int SOS_is_satisfied(SOSgroup *group, int sosindex, LPSREAL *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, + negative value means set incomplete: + + -2: Set member count not full (SOS3) + -1: Set member count not full + 0: Set is full (also returned if the SOS index is invalid) + 1: Too many non-zero sequential variables + 2: Set consistency error + +*/ +{ + int i, n, nn, count, *list; + int type, status = 0; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_satisfied: Invalid index %d\n", sosindex); + return( SOS_COMPLETE ); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + status = SOS_is_satisfied(group, i, solution); + if((status != SOS_COMPLETE) && (status != SOS_INCOMPLETE)) + break; + } + } + else { + type = SOS_get_type(group, sosindex); + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Count the number of active SOS variables */ + for(i = 1; i <= nn; i++) { + if(list[n+i] == 0) + break; + } + count = i-1; + if(count == nn) + status = SOS_COMPLETE; /* Set is full */ + else + status = SOS_INCOMPLETE; /* Set is partial */ + + /* Find index of the first active variable; fail if some are non-zero */ + if(count > 0) { + nn = list[n+1]; + for(i = 1; i < n; i++) { + if((abs(list[i]) == nn) || (solution[lp->rows + abs(list[i])] != 0)) + break; + } + if(abs(list[i]) != nn) + status = SOS_INTERNALERROR; /* Set consistency error (leading set variables are non-zero) */ + else { + /* Scan active SOS variables until we find a non-zero value */ + while(count > 0) { + if(solution[lp->rows + abs(list[i])] != 0) + break; + i++; + count--; + } + /* Scan active non-zero SOS variables; break at first non-zero (rest required to be zero) */ + while(count > 0) { + if(solution[lp->rows + abs(list[i])] == 0) + break; + i++; + count--; + } + if(count > 0) + status = SOS_INTERNALERROR; /* Set consistency error (active set variables are zero) */ + } + } + else { + i = 1; + /* There are no active variables; see if we have happened to find a valid header */ + while((i < n) && (solution[lp->rows + abs(list[i])] == 0)) + i++; + count = 0; + while((i < n) && (count <= nn) && (solution[lp->rows + abs(list[i])] != 0)) { + count++; + i++; + } + if(count > nn) + status = SOS_INFEASIBLE; /* Too-many sequential non-zero variables */ + } + + /* Scan the trailing set of SOS variables; fail if some are non-zero */ + if(status <= 0) { + n--; + while(i <= n) { + if(solution[lp->rows + abs(list[i])] != 0) + break; + i++; + } + if(i <= n) + status = SOS_INFEASIBLE; /* Too-many sequential non-zero variables */ + + /* Code member deficiency for SOS3 separately */ + else if((status == -1) && (type <= SOS3)) + status = SOS3_INCOMPLETE; + } + + } + return( status ); +} + +MYBOOL SOS_is_feasible(SOSgroup *group, int sosindex, LPSREAL *solution) +/* Determine if the SOS is feasible up to the current SOS variable */ +{ + int i, n, nn, *list; + MYBOOL status = TRUE; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_feasible: Invalid SOS index %d\n", sosindex); + return( 0 ); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; status && (i <= group->sos_count); i++) { + status = SOS_is_feasible(group, i, solution); + } + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + if(nn <= 2) + return(status); + + /* Find if we have a gap in the non-zero solution values */ + i = 1; + sosindex = 0; + while((i <= nn) && (list[n+i] != 0)) { + while((i <= nn) && (list[n+i] != 0) && (solution[lp->rows+list[n+i]] == 0)) + i++; + if((i <= nn) && (list[n+i] != 0)) { + i++; /* Step to next */ + while((i <= nn) && (list[n+i] != 0) && (solution[lp->rows+list[n+i]] != 0)) + i++; + sosindex++; + } + i++; /* Step to next */ + } + status = (MYBOOL) (sosindex <= 1); + } + return(status); +} diff --git a/src/external/lpsolve/build/lp_solve/lp_crash.c b/src/external/lpsolve/build/lp_solve/lp_crash.c new file mode 100644 index 00000000..8678983c --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_crash.c @@ -0,0 +1,863 @@ + +/* + ---------------------------------------------------------------------------------- + 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/external/lpsolve/build/lp_solve/lp_lib.c b/src/external/lpsolve/build/lp_solve/lp_lib.c new file mode 100644 index 00000000..268898dc --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_lib.c @@ -0,0 +1,10039 @@ + +/* ---------------------------------------------------------------------------------- + Main library of routines for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to v3.2) + Kjell Eikland (v4.0 and forward) + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: (see below) + + Release notes: + v5.0.0 1 January 2004 First integrated and repackaged version. + v5.0.1 8 May 2004 Cumulative update since initial release; + overall functionality scope maintained. + v5.1.0 20 July 2004 Reworked lp_solve throughout to fit new + flexible matrix storage model. + + ---------------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------------- */ +/* Main library of routines for lp_solve */ +/*----------------------------------------------------------------------------------- */ +#include +#include +#include +#include + +#if LoadInverseLib == TRUE + #ifdef WIN32 + #include + #else + #include + #endif +#endif + + +/* ---------------------------------------------------------------------------------- */ +/* Include core and support modules via headers */ +/* ---------------------------------------------------------------------------------- */ +#include "lp_lib.h" +#include "commonlib.h" +#include "lp_utils.h" +#include "lp_matrix.h" +#include "lp_SOS.h" +#include "lp_Hash.h" +#include "lp_MPS.h" +#include "lp_wlp.h" +#include "lp_presolve.h" +#include "lp_scale.h" +#include "lp_simplex.h" +#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" +#elif INVERSE_ACTIVE==INVERSE_LUSOL + #include "lp_LUSOL.h" +#elif INVERSE_ACTIVE==INVERSE_GLPKLU + #include "lp_glpkLU.h" +#elif INVERSE_ACTIVE==INVERSE_ETAPFI + #include "lp_etaPFI.h" +#elif INVERSE_ACTIVE==INVERSE_LEGACY + #include "lp_etaPFI.h" +#endif + +#if libBLAS > 0 + #include "myblas.h" +#endif + +#ifdef __BORLANDC__ + #pragma hdrstop + #pragma package(smart_init) +#endif + +/* ---------------------------------------------------------------------------------- */ +/* Include selected basis inverse routines and price norm scalars */ +/* ---------------------------------------------------------------------------------- */ + +#include "lp_price.h" +#include "lp_pricePSE.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +#define sensrejvar TRUE + +/* Return lp_solve version information */ +void __WINAPI lp_solve_version(int *majorversion, int *minorversion, int *release, int *build) +{ + if(majorversion != NULL) + (*majorversion) = MAJORVERSION; + if(minorversion != NULL) + (*minorversion) = MINORVERSION; + if(release != NULL) + (*release) = RELEASE; + if(build != NULL) + (*build) = BUILD; +} + + +/* ---------------------------------------------------------------------------------- */ +/* Various interaction elements */ +/* ---------------------------------------------------------------------------------- */ + +MYBOOL __WINAPI userabort(lprec *lp, int message) +{ + MYBOOL abort; + int spx_save; + + spx_save = lp->spx_status; + lp->spx_status = RUNNING; + if(yieldformessages(lp) != 0) { + lp->spx_status = USERABORT; + if(lp->bb_level > 0) + lp->bb_break = TRUE; + } + if((message > 0) && (lp->usermessage != NULL) && (lp->msgmask & message)) + lp->usermessage(lp, lp->msghandle, message); + abort = (MYBOOL) (lp->spx_status != RUNNING); + if(!abort) + lp->spx_status = spx_save; + return( abort ); +} + +STATIC int yieldformessages(lprec *lp) +{ + if((lp->sectimeout > 0) && + ((timeNow()-lp->timestart)-(LPSREAL)lp->sectimeout>0)) + lp->spx_status = TIMEOUT; + + if(lp->ctrlc != NULL) { + int retcode = lp->ctrlc(lp, lp->ctrlchandle); + /* Check for command to restart the B&B */ + if((retcode == ACTION_RESTART) && (lp->bb_level > 1)) { + lp->bb_break = AUTOMATIC; + retcode = 0; + } + return(retcode); + } + else + return(0); +} + +void __WINAPI set_outputstream(lprec *lp, FILE *stream) +{ + if((lp->outstream != NULL) && (lp->outstream != stdout)) { + if(lp->streamowned) + fclose(lp->outstream); + else + fflush(lp->outstream); + } + if(stream == NULL) + lp->outstream = stdout; + else + lp->outstream = stream; + lp->streamowned = FALSE; +} + +MYBOOL __WINAPI set_outputfile(lprec *lp, char *filename) +{ + MYBOOL ok; + FILE *output = stdout; + + ok = (MYBOOL) ((filename == NULL) || (*filename == 0) || ((output = fopen(filename,"w")) != NULL)); + if(ok) { + set_outputstream(lp, output); + lp->streamowned = (MYBOOL) ((filename != NULL) && (*filename != 0)); +#if 1 + if((filename != NULL) && (*filename == 0)) + lp->outstream = NULL; +#endif + } + return(ok); +} + +LPSREAL __WINAPI time_elapsed(lprec *lp) +{ + if(lp->timeend > 0) + return(lp->timeend - lp->timestart); + else + return(timeNow() - lp->timestart); +} + +void __WINAPI put_bb_nodefunc(lprec *lp, lphandleint_intfunc newnode, void *bbnodehandle) +{ + lp->bb_usenode = newnode; + lp->bb_nodehandle = bbnodehandle; /* User-specified "owner process ID" */ +} +void __WINAPI put_bb_branchfunc(lprec *lp, lphandleint_intfunc newbranch, void *bbbranchhandle) +{ + lp->bb_usebranch = newbranch; + lp->bb_branchhandle = bbbranchhandle; /* User-specified "owner process ID" */ +} +void __WINAPI put_abortfunc(lprec *lp, lphandle_intfunc newctrlc, void *ctrlchandle) +{ + lp->ctrlc = newctrlc; + lp->ctrlchandle = ctrlchandle; /* User-specified "owner process ID" */ +} +void __WINAPI put_logfunc(lprec *lp, lphandlestr_func newlog, void *loghandle) +{ + lp->writelog = newlog; + lp->loghandle = loghandle; /* User-specified "owner process ID" */ +} +void __WINAPI put_msgfunc(lprec *lp, lphandleint_func newmsg, void *msghandle, int mask) +{ + lp->usermessage = newmsg; + lp->msghandle = msghandle; /* User-specified "owner process ID" */ + lp->msgmask = mask; +} + + +/* ---------------------------------------------------------------------------------- */ +/* DLL exported function */ +/* ---------------------------------------------------------------------------------- */ +lprec * __WINAPI read_MPS(char *filename, int options) +{ + lprec *lp = NULL; + int typeMPS; + + typeMPS = (options & ~0x07) >> 2; + if ((typeMPS & (MPSFIXED|MPSFREE)) == 0) + typeMPS |= MPSFIXED; + if(MPS_readfile(&lp, filename, typeMPS, options & 0x07)) + return( lp ); + else + return( NULL ); +} +lprec * __WINAPI read_mps(FILE *filename, int options) +{ + lprec *lp = NULL; + int typeMPS; + + typeMPS = (options & ~0x07) >> 2; + if ((typeMPS & (MPSFIXED|MPSFREE)) == 0) + typeMPS |= MPSFIXED; + if(MPS_readhandle(&lp, filename, typeMPS, options & 0x07)) + return( lp ); + else + return( NULL ); +} +/* #if defined develop */ +lprec * __WINAPI read_mpsex(void *userhandle, read_modeldata_func read_modeldata, int options) +{ + 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)) + return( lp ); + else + return( NULL ); +} +/* #endif */ +lprec * __WINAPI read_freeMPS(char *filename, int options) +{ + lprec *lp = NULL; + int typeMPS; + + typeMPS = (options & ~0x07) >> 2; + typeMPS &= ~MPSFIXED; + typeMPS |= MPSFREE; + if(MPS_readfile(&lp, filename, typeMPS, options & 0x07)) + return( lp ); + else + return( NULL ); +} +lprec * __WINAPI read_freemps(FILE *filename, int options) +{ + lprec *lp = NULL; + int typeMPS; + + typeMPS = (options & ~0x07) >> 2; + typeMPS &= ~MPSFIXED; + typeMPS |= MPSFREE; + if(MPS_readhandle(&lp, filename, typeMPS, options & 0x07)) + return( lp ); + else + return( NULL ); +} +/* #if defined develop */ +lprec * __WINAPI read_freempsex(void *userhandle, read_modeldata_func read_modeldata, int options) +{ + lprec *lp = NULL; + int typeMPS; + + typeMPS = (options & ~0x07) >> 2; + typeMPS &= ~MPSFIXED; + typeMPS |= MPSFREE; + if(MPS_readex(&lp, userhandle, read_modeldata, typeMPS, options & 0x07)) + return( lp ); + else + return( NULL ); +} +/* #endif */ +MYBOOL __WINAPI write_mps(lprec *lp, char *filename) +{ + return(MPS_writefile(lp, MPSFIXED, filename)); +} +MYBOOL __WINAPI write_MPS(lprec *lp, FILE *output) +{ + return(MPS_writehandle(lp, MPSFIXED, output)); +} + +MYBOOL __WINAPI write_freemps(lprec *lp, char *filename) +{ + return(MPS_writefile(lp, MPSFREE, filename)); +} +MYBOOL __WINAPI write_freeMPS(lprec *lp, FILE *output) +{ + return(MPS_writehandle(lp, MPSFREE, output)); +} + +MYBOOL __WINAPI write_lp(lprec *lp, char *filename) +{ + return(LP_writefile(lp, filename)); +} +MYBOOL __WINAPI write_LP(lprec *lp, FILE *output) +{ + return(LP_writehandle(lp, output)); +} +#ifndef PARSER_LP +MYBOOL __WINAPI LP_readhandle(lprec **lp, FILE *filename, int verbose, char *lp_name) +{ + return(FALSE); +} +lprec * __WINAPI read_lp(FILE *filename, int verbose, char *lp_name) +{ + return(NULL); +} +lprec * __WINAPI read_LP(char *filename, int verbose, char *lp_name) +{ + return(NULL); +} +#endif + +MYBOOL __WINAPI write_basis(lprec *lp, char *filename) +{ + int typeMPS = MPSFIXED; + return( MPS_writeBAS(lp, typeMPS, filename) ); +} +MYBOOL __WINAPI read_basis(lprec *lp, char *filename, char *info) +{ + int typeMPS = MPSFIXED; + + typeMPS = MPS_readBAS(lp, typeMPS, filename, info); + + /* Code basis */ + if(typeMPS) { + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + lp->basis_valid = TRUE; /* Do not re-initialize basis on entering Solve */ + lp->var_basic[0] = FALSE; /* Set to signal that this is a non-default basis */ + } + return( (MYBOOL) typeMPS ); +} + +/* Write and read lp_solve parameters (placeholders) - see lp_params.c */ +void __WINAPI reset_params(lprec *lp) +{ + int mode; + + lp->epsmachine = DEF_EPSMACHINE; + lp->epsperturb = DEF_PERTURB; + lp->lag_accept = DEF_LAGACCEPT; + set_epslevel(lp, EPS_DEFAULT); + + lp->tighten_on_set = FALSE; + lp->negrange = DEF_NEGRANGE; + +#if 0 + lp->do_presolve = PRESOLVE_ROWS | PRESOLVE_COLS | PRESOLVE_MERGEROWS | + PRESOLVE_REDUCEGCD | + PRESOLVE_ROWDOMINATE; +#else + lp->do_presolve = PRESOLVE_NONE; +#endif + lp->presolveloops = DEF_MAXPRESOLVELOOPS; + + lp->scalelimit = DEF_SCALINGLIMIT; + lp->scalemode = SCALE_INTEGERS | +#if 0 + SCALE_POWER2 | + SCALE_LOGARITHMIC | SCALE_MEAN; +#else + SCALE_LINEAR | SCALE_GEOMETRIC | + SCALE_EQUILIBRATE; +#endif + + lp->crashmode = CRASH_NONE; + + lp->max_pivots = 0; + lp->simplex_strategy = SIMPLEX_DUAL_PRIMAL; +#define PricerDefaultOpt 1 +#if PricerDefaultOpt == 1 + mode = PRICER_DEVEX; +#elif PricerDefaultOpt == 2 + mode = PRICER_STEEPESTEDGE; + mode |= PRICE_TRUENORMINIT; +#else + mode = PRICER_STEEPESTEDGE | PRICE_PRIMALFALLBACK; +#endif + mode |= PRICE_ADAPTIVE; +#ifdef EnableRandomizedPricing + mode |= PRICE_RANDOMIZE; +#endif + set_pivoting(lp, mode); + + lp->improve = IMPROVE_DEFAULT; + lp->anti_degen = ANTIDEGEN_DEFAULT; + + lp->bb_floorfirst = BRANCH_AUTOMATIC; + lp->bb_rule = NODE_DYNAMICMODE | NODE_GREEDYMODE | NODE_GAPSELECT | +#if 1 + NODE_PSEUDOCOSTSELECT | +#else + NODE_PSEUDOFEASSELECT | +#endif + NODE_RCOSTFIXING; + lp->bb_limitlevel = DEF_BB_LIMITLEVEL; + lp->bb_PseudoUpdates = DEF_PSEUDOCOSTUPDATES; + + lp->bb_heuristicOF = my_chsign(is_maxim(lp), MAX(DEF_INFINITE, lp->infinite)); + lp->bb_breakOF = -lp->bb_heuristicOF; + + lp->sectimeout = 0; + lp->solutionlimit = 1; + + set_outputstream(lp, NULL); /* Set to default output stream */ + lp->verbose = NORMAL; + lp->print_sol = FALSE; /* Can be FALSE, TRUE, AUTOMATIC (only non-zeros printed) */ + lp->spx_trace = FALSE; + lp->lag_trace = FALSE; + lp->bb_trace = FALSE; +} + +void __WINAPI unscale(lprec *lp) +{ + undoscale(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)) + set_add_rowmode(lp, FALSE); + return(lin_solve(lp)); + } + else + return( NOBFP ); +} +void __WINAPI print_lp(lprec *lp) +{ + REPORT_lp(lp); +} +void __WINAPI print_tableau(lprec *lp) +{ + REPORT_tableau(lp); +} +void __WINAPI print_objective(lprec *lp) +{ + REPORT_objective(lp); +} +void __WINAPI print_solution(lprec *lp, int columns) +{ + REPORT_solution(lp, columns); +} +void __WINAPI print_constraints(lprec *lp, int columns) +{ + REPORT_constraints(lp, columns); +} +void __WINAPI print_duals(lprec *lp) +{ + REPORT_duals(lp); +} +void __WINAPI print_scales(lprec *lp) +{ + REPORT_scales(lp); +} +MYBOOL __WINAPI print_debugdump(lprec *lp, char *filename) +{ + return(REPORT_debugdump(lp, filename, (MYBOOL) (get_total_iter(lp) > 0))); +} +void __WINAPI print_str(lprec *lp, char *str) +{ + report(lp, lp->verbose, "%s", str); +} + + + +/* ---------------------------------------------------------------------------------- */ +/* Parameter setting and retrieval functions */ +/* ---------------------------------------------------------------------------------- */ + +void __WINAPI set_timeout(lprec *lp, long sectimeout) +{ + lp->sectimeout = sectimeout; +} + +long __WINAPI get_timeout(lprec *lp) +{ + return(lp->sectimeout); +} + +void __WINAPI set_verbose(lprec *lp, int verbose) +{ + lp->verbose = verbose; +} + +int __WINAPI get_verbose(lprec *lp) +{ + return(lp->verbose); +} + +void __WINAPI set_print_sol(lprec *lp, int print_sol) +{ + lp->print_sol = print_sol; +} + +int __WINAPI get_print_sol(lprec *lp) +{ + return(lp->print_sol); +} + +void __WINAPI set_debug(lprec *lp, MYBOOL debug) +{ + lp->bb_trace = debug; +} + +MYBOOL __WINAPI is_debug(lprec *lp) +{ + return(lp->bb_trace); +} + +void __WINAPI set_trace(lprec *lp, MYBOOL trace) +{ + lp->spx_trace = trace; +} + +MYBOOL __WINAPI is_trace(lprec *lp) +{ + return(lp->spx_trace); +} + +void __WINAPI set_anti_degen(lprec *lp, int anti_degen) +{ + lp->anti_degen = anti_degen; +} + +int __WINAPI get_anti_degen(lprec *lp) +{ + return(lp->anti_degen); +} + +MYBOOL __WINAPI is_anti_degen(lprec *lp, int testmask) +{ + return((MYBOOL) ((lp->anti_degen == testmask) || ((lp->anti_degen & testmask) != 0))); +} + +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; +} + +int __WINAPI get_presolve(lprec *lp) +{ + return(lp->do_presolve); +} + +int __WINAPI get_presolveloops(lprec *lp) +{ + if(lp->presolveloops < 0) + return(DEF_MAXPRESOLVELOOPS); + else if(lp->presolveloops == 0) + return(MAXINT32); + else + return(lp->presolveloops); +} + +MYBOOL __WINAPI is_presolve(lprec *lp, int testmask) +{ + return((MYBOOL) ((lp->do_presolve == testmask) || ((lp->do_presolve & testmask) != 0))); +} + +void __WINAPI set_maxpivot(lprec *lp, int maxpivot) +{ + lp->max_pivots = maxpivot; +} + +int __WINAPI get_maxpivot(lprec *lp) +{ + return( lp->bfp_pivotmax(lp) ); +} + +void __WINAPI set_bb_rule(lprec *lp, int bb_rule) +{ + lp->bb_rule = bb_rule; +} + +int __WINAPI get_bb_rule(lprec *lp) +{ + return(lp->bb_rule); +} + +/* INLINE */ MYBOOL is_bb_rule(lprec *lp, int bb_rule) +{ + return( (MYBOOL) ((lp->bb_rule & NODE_STRATEGYMASK) == bb_rule) ); +} + +/* INLINE */ MYBOOL is_bb_mode(lprec *lp, int bb_mask) +{ + return( (MYBOOL) ((lp->bb_rule & bb_mask) > 0) ); +} + +void __WINAPI set_action(int *actionvar, int actionmask) +{ + *actionvar |= actionmask; +} + +void __WINAPI clear_action(int *actionvar, int actionmask) +{ + *actionvar &= ~actionmask; +} + +MYBOOL __WINAPI is_action(int actionvar, int testmask) +{ + return( (MYBOOL) ((actionvar & testmask) != 0) ); +} + +void __WINAPI set_bb_depthlimit(lprec *lp, int bb_maxlevel) +{ + lp->bb_limitlevel = bb_maxlevel; +} + +int __WINAPI get_bb_depthlimit(lprec *lp) +{ + return(lp->bb_limitlevel); +} + +void __WINAPI set_obj_bound(lprec *lp, LPSREAL bb_heuristicOF) +{ + lp->bb_heuristicOF = bb_heuristicOF; +} + +LPSREAL __WINAPI get_obj_bound(lprec *lp) +{ + return(lp->bb_heuristicOF); +} + +void __WINAPI set_mip_gap(lprec *lp, MYBOOL absolute, LPSREAL mip_gap) +{ + if(absolute) + lp->mip_absgap = mip_gap; + else + lp->mip_relgap = mip_gap; +} + +LPSREAL __WINAPI get_mip_gap(lprec *lp, MYBOOL absolute) +{ + if(absolute) + return(lp->mip_absgap); + else + return(lp->mip_relgap); +} + +MYBOOL __WINAPI set_var_branch(lprec *lp, int colnr, int branch_mode) +{ + if(colnr > lp->columns || colnr < 1) { + report(lp, IMPORTANT, "set_var_branch: Column %d out of range\n", colnr); + return( FALSE ); + } + + if(lp->bb_varbranch == NULL) { + int i; + if(branch_mode == BRANCH_DEFAULT) + return( TRUE ); + allocMYBOOL(lp, &lp->bb_varbranch, lp->columns_alloc, FALSE); + for(i = 0; i < lp->columns; i++) + lp->bb_varbranch[i] = BRANCH_DEFAULT; + } + lp->bb_varbranch[colnr - 1] = (MYBOOL) branch_mode; + return( TRUE ); +} + +int __WINAPI get_var_branch(lprec *lp, int colnr) +{ + if(colnr > lp->columns || colnr < 1) { + report(lp, IMPORTANT, "get_var_branch: Column %d out of range\n", colnr); + return(lp->bb_floorfirst); + } + + if(lp->bb_varbranch == NULL) + return(lp->bb_floorfirst); + if(lp->bb_varbranch[colnr - 1] == BRANCH_DEFAULT) + return(lp->bb_floorfirst); + else + return(lp->bb_varbranch[colnr - 1]); +} + +static void set_infiniteex(lprec *lp, LPSREAL infinite, MYBOOL init) +{ + int i; + + infinite = fabs(infinite); + if((init) || is_infinite(lp, lp->bb_heuristicOF)) + lp->bb_heuristicOF = my_chsign(is_maxim(lp), infinite); + if((init) || is_infinite(lp, lp->bb_breakOF)) + lp->bb_breakOF = my_chsign(is_maxim(lp), -infinite); + for(i = 0; i <= lp->sum; i++) { + if((!init) && is_infinite(lp, lp->orig_lowbo[i])) + lp->orig_lowbo[i] = -infinite; + if((init) || is_infinite(lp, lp->orig_upbo[i])) + lp->orig_upbo[i] = infinite; + } + lp->infinite = infinite; +} + + +MYBOOL __WINAPI is_infinite(lprec *lp, LPSREAL value) +{ +#if 1 + return( (MYBOOL) (fabs(value) >= lp->infinite) ); +#else + if(fabs(value) >= lp->infinite) + return( TRUE ); + else + return( FALSE ); +#endif +} + +void __WINAPI set_infinite(lprec *lp, LPSREAL infinite) +{ + set_infiniteex(lp, infinite, FALSE); +} + +LPSREAL __WINAPI get_infinite(lprec *lp) +{ + return(lp->infinite); +} + +void __WINAPI set_epsperturb(lprec *lp, LPSREAL epsperturb) +{ + lp->epsperturb = epsperturb; +} + +LPSREAL __WINAPI get_epsperturb(lprec *lp) +{ + return(lp->epsperturb); +} + +void __WINAPI set_epspivot(lprec *lp, LPSREAL epspivot) +{ + lp->epspivot = epspivot; +} + +LPSREAL __WINAPI get_epspivot(lprec *lp) +{ + return(lp->epspivot); +} + +void __WINAPI set_epsint(lprec *lp, LPSREAL epsint) +{ + lp->epsint = epsint; +} + +LPSREAL __WINAPI get_epsint(lprec *lp) +{ + return(lp->epsint); +} + +void __WINAPI set_epsb(lprec *lp, LPSREAL epsb) +{ + lp->epsprimal = MAX(epsb, lp->epsmachine); +} + +LPSREAL __WINAPI get_epsb(lprec *lp) +{ + return(lp->epsprimal); +} + +void __WINAPI set_epsd(lprec *lp, LPSREAL epsd) +{ + lp->epsdual = MAX(epsd, lp->epsmachine); /* Mainly used as tolerance for reduced cost */ +} + +LPSREAL __WINAPI get_epsd(lprec *lp) +{ + return(lp->epsdual); +} + +void __WINAPI set_epsel(lprec *lp, LPSREAL epsel) +{ + lp->epsvalue = MAX(epsel, lp->epsmachine); +} + +LPSREAL __WINAPI get_epsel(lprec *lp) +{ + return(lp->epsvalue); +} + +MYBOOL __WINAPI set_epslevel(lprec *lp, int epslevel) +{ + LPSREAL SPX_RELAX, MIP_RELAX; + + switch(epslevel) { + case EPS_TIGHT: SPX_RELAX = 1; + MIP_RELAX = 1; + break; + case EPS_MEDIUM: SPX_RELAX = 10; + MIP_RELAX = 1; + break; + case EPS_LOOSE: SPX_RELAX = 100; + MIP_RELAX = 10; + break; + case EPS_BAGGY: SPX_RELAX = 1000; + MIP_RELAX = 100; + break; + default: return( FALSE ); + } + lp->epsvalue = SPX_RELAX*DEF_EPSVALUE; + lp->epsprimal = SPX_RELAX*DEF_EPSPRIMAL; + lp->epsdual = SPX_RELAX*DEF_EPSDUAL; + lp->epspivot = SPX_RELAX*DEF_EPSPIVOT; + lp->epssolution= MIP_RELAX*DEF_EPSSOLUTION; + lp->epsint = MIP_RELAX*DEF_EPSINT; + lp->mip_absgap = MIP_RELAX*DEF_MIP_GAP; + lp->mip_relgap = MIP_RELAX*DEF_MIP_GAP; + + return( TRUE ); +} + +void __WINAPI set_scaling(lprec *lp, int scalemode) +{ + lp->scalemode = scalemode; +} + +int __WINAPI get_scaling(lprec *lp) +{ + return(lp->scalemode); +} + +MYBOOL __WINAPI is_scalemode(lprec *lp, int testmask) +{ + return((MYBOOL) ((lp->scalemode & testmask) != 0)); +} + +MYBOOL __WINAPI is_scaletype(lprec *lp, int scaletype) +{ + int testtype; + + testtype = lp->scalemode & SCALE_MAXTYPE; + return((MYBOOL) (scaletype == testtype)); +} + +void __WINAPI set_scalelimit(lprec *lp, LPSREAL 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) +{ + return(lp->scalelimit); +} + +MYBOOL __WINAPI is_integerscaling(lprec *lp) +{ + return(is_scalemode(lp, SCALE_INTEGERS)); +} + +void __WINAPI set_improve(lprec *lp, int improve) +{ + lp->improve = improve; +} + +int __WINAPI get_improve(lprec *lp) +{ + return(lp->improve); +} + +void __WINAPI set_lag_trace(lprec *lp, MYBOOL lag_trace) +{ + lp->lag_trace = lag_trace; +} + +MYBOOL __WINAPI is_lag_trace(lprec *lp) +{ + return(lp->lag_trace); +} + +void __WINAPI set_pivoting(lprec *lp, int pivoting) +{ + /* Set new pivoting strategy */ + lp->piv_strategy = pivoting; + report(lp, DETAILED, "set_pivoting: Pricing strategy set to '%s'\n", + get_str_piv_rule(get_piv_rule(lp))); +} + +int __WINAPI get_pivoting(lprec *lp) +{ + return( lp->piv_strategy ); +} + +/* INLINE */ int get_piv_rule(lprec *lp) +{ + return( (lp->piv_strategy | PRICE_STRATEGYMASK) ^ PRICE_STRATEGYMASK ); +} + +STATIC char *get_str_piv_rule(int rule) +{ + static char *pivotText[PRICER_LASTOPTION+1] = + {"Bland first index", "Dantzig", "Devex", "Steepest Edge"}; + + return( pivotText[rule] ); +} + +MYBOOL __WINAPI is_piv_rule(lprec *lp, int rule) +{ + return( (MYBOOL) (get_piv_rule(lp) == rule) ); +} + +MYBOOL __WINAPI is_piv_mode(lprec *lp, int testmask) +{ + return((MYBOOL) (((testmask & PRICE_STRATEGYMASK) != 0) && + ((lp->piv_strategy & testmask) != 0))); +} + +void __WINAPI set_break_at_first(lprec *lp, MYBOOL break_at_first) +{ + lp->bb_breakfirst = break_at_first; +} + +MYBOOL __WINAPI is_break_at_first(lprec *lp) +{ + return(lp->bb_breakfirst); +} + +void __WINAPI set_bb_floorfirst(lprec *lp, int bb_floorfirst) +{ + lp->bb_floorfirst = (MYBOOL) bb_floorfirst; +} + +int __WINAPI get_bb_floorfirst(lprec *lp) +{ + return(lp->bb_floorfirst); +} + +void __WINAPI set_break_at_value(lprec *lp, LPSREAL break_at_value) +{ + lp->bb_breakOF = break_at_value; +} + +LPSREAL __WINAPI get_break_at_value(lprec *lp) +{ + return(lp->bb_breakOF); +} + +void __WINAPI set_negrange(lprec *lp, LPSREAL negrange) +{ + if(negrange <= 0) + lp->negrange = negrange; + else + lp->negrange = 0.0; +} + +LPSREAL __WINAPI get_negrange(lprec *lp) +{ + return(lp->negrange); +} + +int __WINAPI get_max_level(lprec *lp) +{ + return(lp->bb_maxlevel); +} + +COUNTER __WINAPI get_total_nodes(lprec *lp) +{ + return(lp->bb_totalnodes); +} + +COUNTER __WINAPI get_total_iter(lprec *lp) +{ + return(lp->total_iter + lp->current_iter); +} + +LPSREAL __WINAPI get_objective(lprec *lp) +{ + if(lp->spx_status == OPTIMAL) + ; + else if(!lp->basis_valid) { + report(lp, CRITICAL, "get_objective: Not a valid basis\n"); + return(0.0); + } + + return( lp->best_solution[0] ); +} + +int __WINAPI get_nonzeros(lprec *lp) +{ + return( mat_nonzeros(lp->matA) ); +} + +MYBOOL __WINAPI set_mat(lprec *lp, int rownr, int colnr, LPSREAL value) +{ + if((rownr < 0) || (rownr > lp->rows)) { + report(lp, IMPORTANT, "set_mat: Row %d out of range\n", rownr); + return( FALSE ); + } + if((colnr < 1) || (colnr > lp->columns)) { + report(lp, IMPORTANT, "set_mat: Column %d out of range\n", colnr); + return( FALSE ); + } + +#ifdef DoMatrixRounding + if(rownr == 0) + value = roundToPrecision(value, lp->matA->epsvalue); +#endif + value = scaled_mat(lp, value, rownr, colnr); + if(rownr == 0) { + lp->orig_obj[colnr] = my_chsign(is_chsign(lp, rownr), value); + return( TRUE ); + } + else + return( mat_setvalue(lp->matA, rownr, colnr, value, FALSE) ); +} + +LPSREAL __WINAPI get_working_objective(lprec *lp) +{ + LPSREAL value = 0.0; + + if(!lp->basis_valid) + report(lp, CRITICAL, "get_working_objective: Not a valid basis\n"); + else if((lp->spx_status == RUNNING) && (lp->solutioncount == 0)) + value = my_chsign(!is_maxim(lp), lp->rhs[0]); + else + value = lp->solution[0]; + + return(value); +} + +LPSREAL __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); + return( 0.0 ); + } + if((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE) + return( lp->full_solution[index] ); + else + return( lp->best_solution[index] ); +} + +LPSREAL __WINAPI get_var_dualresult(lprec *lp, int index) +{ + LPSREAL *duals; + + if((index < 0) || (index > lp->presolve_undo->orig_sum)) { + report(lp, IMPORTANT, "get_var_dualresult: Index %d out of range\n", index); + return( 0.0 ); + } + + if(index == 0) + return( lp->best_solution[0] ); + + /* Make sure we actually have dual information available */ + if(!get_ptr_sensitivity_rhs(lp, &duals, NULL, NULL)) + return( 0.0 ); + else + duals = ((lp->full_duals == NULL) ? lp->duals : lp->full_duals); + return( duals[index] ); +} + +MYBOOL __WINAPI get_variables(lprec *lp, LPSREAL *var) +{ + if(lp->spx_status == OPTIMAL) + ; + else if(!lp->basis_valid) { + report(lp, CRITICAL, "get_variables: Not a valid basis\n"); + return(FALSE); + } + + MEMCOPY(var, lp->best_solution + (1 + lp->rows), lp->columns); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_variables(lprec *lp, LPSREAL **var) +{ + if(lp->spx_status == OPTIMAL) + ; + else if(!lp->basis_valid) { + report(lp, CRITICAL, "get_ptr_variables: Not a valid basis\n"); + return(FALSE); + } + + if(var != NULL) + *var = lp->best_solution + (1 + lp->rows); + return(TRUE); +} + +MYBOOL __WINAPI get_constraints(lprec *lp, LPSREAL *constr) +{ + if(lp->spx_status == OPTIMAL) + ; + else if(!lp->basis_valid) { + report(lp, CRITICAL, "get_constraints: Not a valid basis\n"); + return(FALSE); + } + + MEMCOPY(constr, lp->best_solution + 1, lp->rows); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_constraints(lprec *lp, LPSREAL **constr) +{ + if(lp->spx_status == OPTIMAL) + ; + else if(!lp->basis_valid) { + report(lp, CRITICAL, "get_ptr_constraints: Not a valid basis\n"); + return(FALSE); + } + + if(constr != NULL) + *constr = lp->best_solution + 1; + return(TRUE); +} + +MYBOOL __WINAPI get_sensitivity_rhs(lprec *lp, LPSREAL *duals, LPSREAL *dualsfrom, LPSREAL *dualstill) +{ + LPSREAL *duals0, *dualsfrom0, *dualstill0; + + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_sensitivity_rhs: Not a valid basis\n"); + return(FALSE); + } + + if(!get_ptr_sensitivity_rhs(lp, + (duals != NULL) ? &duals0 : NULL, + (dualsfrom != NULL) ? &dualsfrom0 : NULL, + (dualstill != NULL) ? &dualstill0 : NULL)) + return(FALSE); + + if(duals != NULL) + MEMCOPY(duals, duals0, lp->sum); + if(dualsfrom != NULL) + MEMCOPY(dualsfrom, dualsfrom0, lp->sum); + if(dualstill != NULL) + MEMCOPY(dualstill, dualstill0, lp->sum); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_sensitivity_rhs(lprec *lp, LPSREAL **duals, LPSREAL **dualsfrom, LPSREAL **dualstill) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_ptr_sensitivity_rhs: Not a valid basis\n"); + return(FALSE); + } + + if(duals != NULL) { + if(lp->duals == NULL) { + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + report(lp, CRITICAL, "get_ptr_sensitivity_rhs: Sensitivity unknown\n"); + return(FALSE); + } + if(!construct_duals(lp)) + return(FALSE); + } + *duals = lp->duals + 1; + } + + if((dualsfrom != NULL) || (dualstill != NULL)) { + if((lp->dualsfrom == NULL) || (lp->dualstill == NULL)) { + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + report(lp, CRITICAL, "get_ptr_sensitivity_rhs: Sensitivity unknown\n"); + return(FALSE); + } + construct_sensitivity_duals(lp); + if((lp->dualsfrom == NULL) || (lp->dualstill == NULL)) + return(FALSE); + } + if(dualsfrom != NULL) + *dualsfrom = lp->dualsfrom + 1; + if(dualstill != NULL) + *dualstill = lp->dualstill + 1; + } + return(TRUE); +} + +MYBOOL __WINAPI get_sensitivity_objex(lprec *lp, LPSREAL *objfrom, LPSREAL *objtill, LPSREAL *objfromvalue, LPSREAL *objtillvalue) +{ + LPSREAL *objfrom0, *objtill0, *objfromvalue0, *objtillvalue0; + + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_sensitivity_objex: Not a valid basis\n"); + return(FALSE); + } + + if(!get_ptr_sensitivity_objex(lp, (objfrom != NULL) ? &objfrom0 : NULL, + (objtill != NULL) ? &objtill0 : NULL, + (objfromvalue != NULL) ? &objfromvalue0 : NULL, + (objtillvalue != NULL) ? &objtillvalue0 : NULL)) + return(FALSE); + + if((objfrom != NULL) && (objfrom0 != NULL)) + MEMCOPY(objfrom, objfrom0, lp->columns); + if((objtill != NULL) && (objtill0 != NULL)) + MEMCOPY(objtill, objtill0, lp->columns); + if((objfromvalue != NULL) && (objfromvalue0 != NULL)) + MEMCOPY(objfromvalue, objfromvalue0, lp->columns); + if((objtillvalue != NULL) && (objtillvalue0 != NULL)) + MEMCOPY(objtillvalue, objtillvalue0, lp->columns); + return(TRUE); +} + +MYBOOL __WINAPI get_sensitivity_obj(lprec *lp, LPSREAL *objfrom, LPSREAL *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) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_ptr_sensitivity_objex: Not a valid basis\n"); + return(FALSE); + } + + if((objfrom != NULL) || (objtill != NULL)) { + if((lp->objfrom == NULL) || (lp->objtill == NULL)) { + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + report(lp, CRITICAL, "get_ptr_sensitivity_objex: Sensitivity unknown\n"); + return(FALSE); + } + construct_sensitivity_obj(lp); + if((lp->objfrom == NULL) || (lp->objtill == NULL)) + return(FALSE); + } + if(objfrom != NULL) + *objfrom = lp->objfrom + 1; + if(objtill != NULL) + *objtill = lp->objtill + 1; + } + + if((objfromvalue != NULL) /* || (objtillvalue != NULL) */) { + if((lp->objfromvalue == NULL) /* || (lp->objtillvalue == NULL) */) { + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + report(lp, CRITICAL, "get_ptr_sensitivity_objex: Sensitivity unknown\n"); + return(FALSE); + } + construct_sensitivity_duals(lp); + if((lp->objfromvalue == NULL) /* || (lp->objtillvalue == NULL) */) + return(FALSE); + } + } + + if(objfromvalue != NULL) + *objfromvalue = lp->objfromvalue + 1; + + if(objtillvalue != NULL) + *objtillvalue = NULL /* lp->objtillvalue + 1 */; + + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_sensitivity_obj(lprec *lp, LPSREAL **objfrom, LPSREAL **objtill) +{ + return(get_ptr_sensitivity_objex(lp, objfrom, objtill, NULL, NULL)); +} + +void __WINAPI set_solutionlimit(lprec *lp, int limit) +{ + lp->solutionlimit = limit; +} +int __WINAPI get_solutionlimit(lprec *lp) +{ + return(lp->solutionlimit); +} +int __WINAPI get_solutioncount(lprec *lp) +{ + return(lp->solutioncount); +} + +int __WINAPI get_Nrows(lprec *lp) +{ + return(lp->rows); +} + +int __WINAPI get_Norig_rows(lprec *lp) +{ + if(lp->varmap_locked) + return(lp->presolve_undo->orig_rows); + else + return(lp->rows); +} + +int __WINAPI get_Lrows(lprec *lp) +{ + if(lp->matL == NULL) + return( 0 ); + else + return( lp->matL->rows ); +} + +int __WINAPI get_Ncolumns(lprec *lp) +{ + return(lp->columns); +} + +int __WINAPI get_Norig_columns(lprec *lp) +{ + if(lp->varmap_locked) + return(lp->presolve_undo->orig_columns); + else + return(lp->columns); +} + + +/* ---------------------------------------------------------------------------------- */ +/* Core routines for lp_solve */ +/* ---------------------------------------------------------------------------------- */ +int __WINAPI get_status(lprec *lp) +{ + return(lp->spx_status); +} + +char * __WINAPI get_statustext(lprec *lp, int statuscode) +{ + if (statuscode == NOBFP) return("No basis factorization package"); + else if (statuscode == DATAIGNORED) return("Invalid input data provided"); + else if (statuscode == NOMEMORY) return("Not enough memory available"); + else if (statuscode == NOTRUN) return("Model has not been optimized"); + else if (statuscode == OPTIMAL) return("OPTIMAL solution"); + else if (statuscode == SUBOPTIMAL) return("SUB-OPTIMAL solution"); + else if (statuscode == INFEASIBLE) return("Model is primal INFEASIBLE"); + else if (statuscode == UNBOUNDED) return("Model is primal UNBOUNDED"); + else if (statuscode == RUNNING) return("lp_solve is currently running"); + else if (statuscode == NUMFAILURE) return("NUMERIC FAILURE encountered"); + else if (statuscode == DEGENERATE) return("DEGENERATE situation"); + else if (statuscode == USERABORT) return("User-requested termination"); + else if (statuscode == TIMEOUT) return("Termination due to timeout"); + else if (statuscode == PRESOLVED) return("Model solved by presolve"); + else if (statuscode == PROCFAIL) return("B&B routine failed"); + else if (statuscode == PROCBREAK) return("B&B routine terminated"); + else if (statuscode == FEASFOUND) return("Feasible B&B solution found"); + else if (statuscode == NOFEASFOUND) return("No feasible B&B solution found"); + else if (statuscode == FATHOMED) return("Fathomed/pruned branch"); + else return("Undefined internal error"); +} + +MYBOOL __WINAPI is_obj_in_basis(lprec *lp) +{ + return( lp->obj_in_basis ); +} + +void __WINAPI set_obj_in_basis(lprec *lp, MYBOOL obj_in_basis) +{ + lp->obj_in_basis = (MYBOOL) (obj_in_basis == TRUE); +} + +lprec * __WINAPI make_lp(int rows, int columns) +{ + lprec *lp; + +# if defined FORTIFY + /* Fortify_EnterScope(); */ +# endif + + if(rows < 0 || columns < 0) + return(NULL); + + lp = (lprec*) calloc(1, sizeof(*lp)); + if(!lp) + return(NULL); + + set_lp_name(lp, NULL); + lp->names_used = FALSE; + lp->use_row_names = TRUE; + lp->use_col_names = TRUE; + lp->rowcol_name = NULL; + + /* Do standard initializations ------------------------------------------------------------ */ +#if 1 + lp->obj_in_basis = DEF_OBJINBASIS; +#else + lp->obj_in_basis = FALSE; +#endif + lp->verbose = NORMAL; + set_callbacks(lp); + set_BFP(lp, NULL); + set_XLI(lp, NULL); +#if libBLAS > 0 + init_BLAS(); +#if libBLAS > 1 + if(is_nativeBLAS() && !load_BLAS(libnameBLAS)) + /*report(lp, "make_lp: Could not load external BLAS library '%s'.\n", libnameBLAS)*/; +#endif +#endif + + /* Define the defaults for key user-settable values --------------------------------------- */ + reset_params(lp); + + /* Do other initializations --------------------------------------------------------------- */ + lp->source_is_file = FALSE; + lp->model_is_pure = TRUE; + lp->model_is_valid = FALSE; + lp->spx_status = NOTRUN; + lp->lag_status = NOTRUN; + + lp->workarrays = mempool_create(lp); + lp->wasPreprocessed = FALSE; + lp->wasPresolved = FALSE; + presolve_createUndo(lp); + + lp->bb_varactive = NULL; + lp->bb_varbranch = NULL; + lp->var_priority = NULL; + + lp->rhsmax = 0.0; + lp->bigM = 0.0; + lp->bb_deltaOF = 0.0; + + lp->equalities = 0; + lp->fixedvars = 0; + lp->int_vars = 0; + lp->sc_vars = 0; + + lp->sos_ints = 0; + lp->sos_vars = 0; + lp->sos_priority = NULL; + + lp->rows_alloc = 0; + lp->columns_alloc = 0; + lp->sum_alloc = 0; + + lp->rows = rows; + lp->columns = columns; + lp->sum = rows + columns; + varmap_clear(lp); + + lp->matA = mat_create(lp, rows, columns, lp->epsvalue); + lp->matL = NULL; + lp->invB = NULL; + lp->duals = NULL; + lp->dualsfrom = NULL; + lp->dualstill = NULL; + lp->objfromvalue = NULL; + lp->objfrom = NULL; + lp->objtill = NULL; + + inc_col_space(lp, columns + 1); + inc_row_space(lp, rows + 1); + + /* Avoid bound-checker uninitialized variable error */ + lp->orig_lowbo[0] = 0; + + lp->rootbounds = NULL; + lp->bb_bounds = NULL; + lp->bb_basis = NULL; + + lp->basis_valid = FALSE; + lp->simplex_mode = SIMPLEX_DYNAMIC; + lp->scaling_used = FALSE; + lp->columns_scaled = FALSE; + lp->P1extraDim = 0; + lp->P1extraVal = 0.0; + lp->bb_strongbranches = 0; + lp->current_iter = 0; + lp->total_iter = 0; + lp->current_bswap = 0; + lp->total_bswap = 0; + lp->solutioncount = 0; + lp->solvecount = 0; + + allocINT(lp, &lp->rejectpivot, DEF_MAXPIVOTRETRY + 1, TRUE); + + set_minim(lp); + set_infiniteex(lp, DEF_INFINITE, TRUE); + + initPricer(lp); + + /* Call-back routines by KE */ + lp->ctrlc = NULL; + lp->ctrlchandle = NULL; + lp->writelog = NULL; + lp->loghandle = NULL; + lp->debuginfo = NULL; + lp->usermessage = NULL; + lp->msgmask = MSG_NONE; + lp->msghandle = NULL; + + lp->timecreate = timeNow(); + + return(lp); +} + +MYBOOL __WINAPI resize_lp(lprec *lp, int rows, int columns) +{ + MYBOOL status = TRUE; + + if(columns > lp->columns) + status = inc_col_space(lp, columns - lp->columns); + else + while(status && (lp->columns > columns)) { + status = del_column(lp, lp->columns); + } + if(status && (rows > lp->rows)) + status = inc_row_space(lp, rows - lp->rows); + else + while(status && (lp->rows > rows)) { + status = del_constraint(lp, lp->rows); + } + return( status ); +} + +void __WINAPI free_lp(lprec **plp) +{ + if(plp != NULL) { + lprec *lp = *plp; + if(lp != NULL) + delete_lp(lp); + *plp = NULL; + } +} + +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) { + FREE(lp->row_name); + FREE(lp->col_name); + free_hash_table(lp->rowname_hashtab); + free_hash_table(lp->colname_hashtab); + } + + mat_free(&lp->matA); + lp->bfp_free(lp); +#if LoadInverseLib == TRUE + if(lp->hBFP != NULL) + set_BFP(lp, NULL); +#endif +#if LoadLanguageLib == TRUE + if(lp->hXLI != NULL) + set_XLI(lp, NULL); +#endif + + unset_OF_p1extra(lp); + FREE(lp->orig_obj); + FREE(lp->orig_rhs); + FREE(lp->rhs); + FREE(lp->var_type); + set_var_weights(lp, NULL); + FREE(lp->bb_varbranch); + FREE(lp->sc_lobound); + FREE(lp->var_is_free); + FREE(lp->orig_upbo); + FREE(lp->orig_lowbo); + FREE(lp->upbo); + FREE(lp->lowbo); + FREE(lp->var_basic); + FREE(lp->is_basic); + FREE(lp->is_lower); + if(lp->bb_PseudoCost != NULL) { +/* report(lp, SEVERE, "delete_lp: The B&B pseudo-cost array was not cleared on delete\n"); */ + free_pseudocost(lp); + } + if(lp->bb_bounds != NULL) { + report(lp, SEVERE, "delete_lp: The stack of B&B levels was not empty (failed at %.0f nodes)\n", + (double) lp->bb_totalnodes); + unload_BB(lp); + } + if(lp->bb_basis != NULL) { +/* report(lp, SEVERE, "delete_lp: The stack of saved bases was not empty on delete\n"); */ + unload_basis(lp, FALSE); + } + + FREE(lp->rejectpivot); + partial_freeBlocks(&(lp->rowblocks)); + partial_freeBlocks(&(lp->colblocks)); + multi_free(&(lp->multivars)); + multi_free(&(lp->longsteps)); + + FREE(lp->solution); + FREE(lp->best_solution); + FREE(lp->full_solution); + + presolve_freeUndo(lp); + mempool_free(&(lp->workarrays)); + + freePricer(lp); + + FREE(lp->drow); + FREE(lp->nzdrow); + + FREE(lp->duals); + FREE(lp->full_duals); + FREE(lp->dualsfrom); + FREE(lp->dualstill); + FREE(lp->objfromvalue); + FREE(lp->objfrom); + FREE(lp->objtill); + FREE(lp->row_type); + + if(lp->sos_vars > 0) + FREE(lp->sos_priority); + free_SOSgroup(&(lp->SOS)); + free_SOSgroup(&(lp->GUB)); + freecuts_BB(lp); + + if(lp->scaling_used) + FREE(lp->scalars); + if(lp->matL != NULL) { + FREE(lp->lag_rhs); + FREE(lp->lambda); + FREE(lp->lag_con_type); + mat_free(&lp->matL); + } + if(lp->streamowned) + set_outputstream(lp, NULL); + +#if libBLAS > 0 + if(!is_nativeBLAS()) + unload_BLAS(); +#endif + + 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 + construction routines to simplify future maintainance. */ +lprec* __WINAPI copy_lp(lprec *lp) +{ + int i, n, *idx = NULL; + LPSREAL hold, *val = NULL, infinite; + 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)) + goto Finish; + + /* Create the new object */ + newlp = make_lp(rows, 0); + if(newlp == NULL) + goto Finish; + if(!resize_lp(newlp, rows, columns)) + goto Finish; + 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 */ + 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_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++) { + 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; + } + + /* Load the constraint matrix and variable definitions */ + for(i = 1; i <= 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; + } + else { + if(is_int(lp, i)) + if(!set_int(newlp, i, TRUE)) + goto Finish; + 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; + } + 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; + } + +#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->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); + if(lp->duals != NULL) { + allocREAL(newlp, &newlp->duals, newlp->sum_alloc+1, FALSE); + MEMCOPY(newlp->duals, lp->duals, lp->sum+1); + } + 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); + + return( newlp ); +} +MYBOOL __WINAPI dualize_lp(lprec *lp) +{ + int i, n; + MATrec *mat = lp->matA; + LPSREAL *item; + + /* Are we allowed to perform the operation? */ + if((MIP_count(lp) > 0) || (lp->solvecount > 0)) + return( FALSE ); + + /* Modify sense */ + set_sense(lp, (MYBOOL) !is_maxim(lp)); + + /* Transpose matrix and reverse signs */ + n = mat_nonzeros(mat); + mat_transpose(mat); + item = &COL_MAT_VALUE(0); + for(i = 0; i < n; i++, item += matValueStep) + *item *= -1; + + /* Row-column swap other vectors */ + 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); + + /* Reallocate storage */ +/* +var_type +sc_bound +solution +best_solution +full_solution +duals +*/ + + /* Shift variable bounds */ +/* +is_basic +orig_upbo +orig_lowbo +scalars +*/ + + return( TRUE ); +} + +/* Optimize memory usage */ +STATIC MYBOOL memopt_lp(lprec *lp, int rowextra, int colextra, int nzextra) +{ + MYBOOL status = FALSE; + + if(lp == NULL) + return( status ); + + status = mat_memopt(lp->matA, rowextra, colextra, nzextra) && + (++rowextra > 0) && (++colextra > 0) && (++nzextra > 0); + +#if 0 /* inc_ routines not well-tested for reduction in size allocation */ + if(status) { + int colalloc = lp->columns_alloc - MIN(lp->columns_alloc, lp->columns + colextra), + rowalloc = lp->rows_alloc - MIN(lp->rows_alloc, lp->rows + rowextra); + + status = inc_lag_space(lp, rowalloc, FALSE) && + inc_row_space(lp, rowalloc) && + inc_col_space(lp, colalloc); + } +#endif + + return( status ); +} + + +/* Utility routine group for constraint and column deletion/insertion + mapping in relation to the original set of constraints and columns */ +STATIC void varmap_lock(lprec *lp) +{ + presolve_fillUndo(lp, lp->rows, lp->columns, TRUE); + lp->varmap_locked = TRUE; +} +STATIC void varmap_clear(lprec *lp) +{ + presolve_setOrig(lp, 0, 0); + lp->varmap_locked = FALSE; +} +STATIC MYBOOL varmap_canunlock(lprec *lp) +{ + /* Don't do anything if variables aren't locked yet */ + if(lp->varmap_locked) { + int i; + presolveundorec *psundo = lp->presolve_undo; + + /* Check for the obvious */ + if(/*lp->names_used || + (psundo->orig_columns != lp->columns) || (psundo->orig_rows != lp->rows)) */ + (psundo->orig_columns > lp->columns) || (psundo->orig_rows > lp->rows)) + return( FALSE ); + + /* Check for deletions */ + for(i = psundo->orig_rows + psundo->orig_columns; i > 0; i--) + if(psundo->orig_to_var[i] == 0) + return( FALSE ); + + /* Check for insertions */ + for(i = lp->sum; i > 0; i--) + if(psundo->var_to_orig[i] == 0) + return( FALSE ); + } + return( TRUE ); +} +STATIC void varmap_add(lprec *lp, int base, int delta) +{ + int i, ii; + presolveundorec *psundo = lp->presolve_undo; + + /* Don't do anything if variables aren't locked yet */ + if(!lp->varmap_locked) + return; + + /* Set new constraints/columns to have an "undefined" mapping to original + constraints/columns (assumes that counters have NOT yet been updated) */ + for(i = lp->sum; i >= base; i--) { + ii = i + delta; + psundo->var_to_orig[ii] = psundo->var_to_orig[i]; + } + + /* Initialize map of added rows/columns */ + for(i = 0; i < delta; i++) { + ii = base + i; + psundo->var_to_orig[ii] = 0; + } +} + +STATIC void varmap_delete(lprec *lp, int base, int delta, LLrec *varmap) +{ + int i, ii, j; + MYBOOL preparecompact = (MYBOOL) (varmap != NULL); + 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); + + /* 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(lp->names_used) + varmap_lock(lp); + else + return; +#else + if(!lp->model_is_pure && lp->names_used) + varmap_lock(lp); +#endif + } + + /* Do mass deletion via a linked list */ + preparecompact = (MYBOOL) (varmap != NULL); + if(preparecompact) { + preparecompact = (MYBOOL) (base > lp->rows); /* Set TRUE for columns */ + for(j = firstInactiveLink(varmap); j != 0; j = nextInactiveLink(varmap, j)) { + i = j; + if(preparecompact) { +#ifdef Paranoia + if(SOS_is_member(lp->SOS, 0, j)) + report(lp, SEVERE, "varmap_delete: Deleting variable %d, which is in a SOS!\n", j); +#endif + i += lp->rows; + } + ii = psundo->var_to_orig[i]; + if(ii > 0) /* It was an original variable; reverse sign of index to flag deletion */ + psundo->var_to_orig[i] = -ii; + else /* It was a non-original variable; add special code for deletion */ + psundo->var_to_orig[i] = -(psundo->orig_rows+psundo->orig_columns+i); + } + return; + } + + /* Do legacy simplified version if we are doing batch delete operations */ + preparecompact = (MYBOOL) (base < 0); + if(preparecompact) { + base = -base; + if(base > lp->rows) + base += (psundo->orig_rows - lp->rows); + for(i = base; i < base-delta; i++) { + ii = psundo->var_to_orig[i]; + if(ii > 0) /* It was an original variable; reverse sign of index to flag deletion */ + psundo->var_to_orig[i] = -ii; + else /* It was a non-original variable; add special code for deletion */ + psundo->var_to_orig[i] = -(psundo->orig_rows+psundo->orig_columns+i); + } + return; + } + + /* We are deleting an original constraint/column; + 1) clear mapping of original to deleted + 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) + psundo->orig_to_var[ii] = 0; + } + for(i = base; i <= lp->sum+delta; i++) { + ii = i - delta; + psundo->var_to_orig[i] = psundo->var_to_orig[ii]; + } + + i = 1; + j = psundo->orig_rows; + if(base > lp->rows) { + i += j; + j += psundo->orig_columns; + } + ii = base-delta; + for(; i <= j; i++) { + if(psundo->orig_to_var[i] >= ii) + psundo->orig_to_var[i] += delta; + } + +} + +STATIC MYBOOL varmap_validate(lprec *lp, int varno) +{ + MYBOOL success = TRUE; + int i, ii, ix, ie, + n_rows = lp->rows, + orig_sum = lp->presolve_undo->orig_sum, + orig_rows = lp->presolve_undo->orig_rows; + + if(varno <= 0) { + varno = 1; + ie = orig_sum; + } + else + ie = varno; + for(i = varno; success && (i <= ie); i++) { + ix = lp->presolve_undo->orig_to_var[i]; + if((ix > 0) && (i > orig_rows)) + ix += n_rows; + + /* Check for index out of range due to presolve */ + success = (MYBOOL) (ix <= orig_sum); + if(!success) + report(lp, SEVERE, "varmap_validate: Invalid new mapping found for variable %d\n", + i); + else if(ix != 0) { + ii = lp->presolve_undo->var_to_orig[ix]; + if(ix > n_rows) + ii += orig_rows; + success = (MYBOOL) (ii == i); + if(!success) + report(lp, SEVERE, "varmap_validate: Invalid old mapping found for variable %d (%d)\n", + i, ii); + } + } + return( success ); +} + +STATIC void varmap_compact(lprec *lp, int prev_rows, int prev_cols) +{ + presolveundorec *psundo = lp->presolve_undo; + int i, ii, n_sum, n_rows, + orig_rows = psundo->orig_rows, + prev_sum = prev_rows + prev_cols; + + /* Nothing to do if the model is not "dirty" or the variable map is not locked */ + if(lp->model_is_pure || !lp->varmap_locked) + return; + + /* We are deleting an original constraint/column; + 1) clear mapping of original to deleted + 2) shift the deleted variable to original mappings left + 3) decrement all subsequent original-to-current pointers + */ + n_sum = 0; + n_rows = 0; + for(i = 1; i <= prev_sum; i++) { + ii = psundo->var_to_orig[i]; + + /* Process variable if it was deleted in the previous round */ + if(ii < 0) { + ii = -ii; + /* Update map back if we have an original variable, otherwise just skip */ + if(i <= prev_rows) + psundo->orig_to_var[ii] = 0; + else + psundo->orig_to_var[orig_rows+ii] = 0; + } + /* Otherwise shift and update map back */ + else { + n_sum++; + /* Shift only if necessary */ + if(n_sum < i) + psundo->var_to_orig[n_sum] = ii; + /* Update map back if we have an original variable */ + if(ii > 0) { + if(i <= prev_rows) { + psundo->orig_to_var[ii] = n_sum; + n_rows = n_sum; + } + else + psundo->orig_to_var[orig_rows+ii] = n_sum-n_rows; + } + } + } +#ifdef xxParanoia + if(!varmap_validate(lp, 0)) + report(lp, SEVERE, "varmap_compact: Internal presolve mapping error at exit\n"); +#endif + +} + +/* Utility group for shifting row and column data */ +STATIC MYBOOL shift_rowcoldata(lprec *lp, int base, int delta, LLrec *usedmap, MYBOOL isrow) +/* Note: Assumes that "lp->sum" and "lp->rows" HAVE NOT been updated to the new counts */ +{ + int i, ii; + LPSREAL lodefault; + + /* Shift data right/down (insert), and set default values in positive delta-gap */ + if(delta > 0) { + + /* Determine if we can take the easy way out */ + MYBOOL easyout = (MYBOOL) ((lp->solvecount == 0) && (base > lp->rows)); + + /* Shift the row/column data */ + + MEMMOVE(lp->orig_upbo + base + delta, lp->orig_upbo + base, lp->sum - base + 1); + MEMMOVE(lp->orig_lowbo + base + delta, lp->orig_lowbo + base, lp->sum - base + 1); + + if(!easyout) { + MEMMOVE(lp->upbo + base + delta, lp->upbo + base, lp->sum - base + 1); + MEMMOVE(lp->lowbo + base + delta, lp->lowbo + base, lp->sum - base + 1); + if(lp->model_is_valid) { + MEMMOVE(lp->solution + base + delta, lp->solution + base, lp->sum - base + 1); + MEMMOVE(lp->best_solution + base + delta, lp->best_solution + base, lp->sum - base + 1); + } + MEMMOVE(lp->is_lower + base + delta, lp->is_lower + base, lp->sum - base + 1); + } + + /* Deal with scalars; the vector can be NULL */ + if(lp->scalars != NULL) { + if(!easyout) + for(ii = lp->sum; ii >= base; ii--) { + i = ii + delta; + lp->scalars[i] = lp->scalars[ii]; + } + for(ii = base; ii < base + delta; ii++) + lp->scalars[ii] = 1; + } + + /* Set defaults */ +#ifdef SlackInitMinusInf + if(isrow) + lodefault = -lp->infinite; + else +#endif + lodefault = 0; + + for(i = 0; i < delta; i++) { + ii = base + i; + lp->orig_upbo[ii] = lp->infinite; + lp->orig_lowbo[ii] = lodefault; + if(!easyout) { + lp->upbo[ii] = lp->orig_upbo[ii]; + lp->lowbo[ii] = lp->orig_lowbo[ii]; + lp->is_lower[ii] = TRUE; + } + } + } + + /* Shift data left/up (delete) */ + else if(usedmap != NULL) { + int k, offset = 0; + if(!isrow) + offset += lp->rows; + i = offset + 1; + for(k = firstActiveLink(usedmap); k != 0; + i++, k = nextActiveLink(usedmap, k)) { + ii = k + offset; + if(ii == i) + continue; + lp->upbo[i] = lp->upbo[ii]; + lp->orig_upbo[i] = lp->orig_upbo[ii]; + lp->lowbo[i] = lp->lowbo[ii]; + lp->orig_lowbo[i] = lp->orig_lowbo[ii]; + lp->solution[i] = lp->solution[ii]; + lp->best_solution[i] = lp->best_solution[ii]; + lp->is_lower[i] = lp->is_lower[ii]; + if(lp->scalars != NULL) + lp->scalars[i] = lp->scalars[ii]; + } + if(isrow) { + base = lp->rows + 1; + MEMMOVE(lp->upbo + i, lp->upbo + base, lp->columns); + MEMMOVE(lp->orig_upbo + i, lp->orig_upbo + base, lp->columns); + MEMMOVE(lp->lowbo + i, lp->lowbo + base, lp->columns); + MEMMOVE(lp->orig_lowbo + i, lp->orig_lowbo + base, lp->columns); + if(lp->model_is_valid) { + MEMMOVE(lp->solution + i, lp->solution + base, lp->columns); + MEMMOVE(lp->best_solution + i, lp->best_solution + base, lp->columns); + } + MEMMOVE(lp->is_lower + i, lp->is_lower + base, lp->columns); + if(lp->scalars != NULL) + MEMMOVE(lp->scalars + i, lp->scalars + base, lp->columns); + } + } + + else if(delta < 0) { + + /* First make sure we don't cross the sum count border */ + if(base-delta-1 > lp->sum) + delta = base - lp->sum - 1; + + /* Shift the data*/ + for(i = base; i <= lp->sum + delta; i++) { + ii = i - delta; + lp->upbo[i] = lp->upbo[ii]; + lp->orig_upbo[i] = lp->orig_upbo[ii]; + lp->lowbo[i] = lp->lowbo[ii]; + lp->orig_lowbo[i] = lp->orig_lowbo[ii]; + lp->solution[i] = lp->solution[ii]; + lp->best_solution[i] = lp->best_solution[ii]; + lp->is_lower[i] = lp->is_lower[ii]; + if(lp->scalars != NULL) + lp->scalars[i] = lp->scalars[ii]; + } + + } + + lp->sum += delta; + + lp->matA->row_end_valid = FALSE; + + return(TRUE); +} + +STATIC MYBOOL shift_basis(lprec *lp, int base, int delta, LLrec *usedmap, MYBOOL isrow) +/* Note: Assumes that "lp->sum" and "lp->rows" HAVE NOT been updated to the new counts */ +{ + int i, ii; + MYBOOL Ok = TRUE; + + /* Don't bother to shift the basis if it is not yet ready */ + if(!is_BasisReady(lp)) + return( Ok ); + + /* Basis adjustments due to insertions (after actual row/column insertions) */ + if(delta > 0) { + + /* Determine if the basis becomes invalidated */ + if(isrow) + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT); + + /* Shift and fix invalid basis references (increment higher order basic variable index) */ + if(base <= lp->sum) + MEMMOVE(lp->is_basic + base + delta, lp->is_basic + base, lp->sum - base + 1); + + /* Prevent CPU-expensive basis updating if this is the initial model creation */ + if(!lp->model_is_pure || (lp->solvecount > 0)) + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i]; + if(ii >= base) + lp->var_basic[i] += delta; + } + + /* Update the basis (shift and extend) */ + for(i = 0; i < delta; i++) { + ii = base + i; + lp->is_basic[ii] = isrow; + if(isrow) + lp->var_basic[lp->rows+1+i] = ii; + } + + } + /* Basis adjustments due to deletions (after actual row/column deletions) */ + else { + int j,k; + + /* Fix invalid basis references (decrement high basic slack variable indexes), + but reset the entire basis if a deleted variable is found in the basis */ + k = 0; + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i]; + lp->is_basic[ii] = FALSE; + if(ii >= base) { + /* Skip to next basis variable if this one is to be deleted */ + if(ii < base-delta) { + set_action(&lp->spx_action, ACTION_REBASE); + continue; + } + /* Otherwise, update the index of the basic variable for deleted variables */ + ii += delta; + } + k++; + lp->var_basic[k] = ii; + } + + /* Set the new basis indicators */ + i = k; + if(isrow) + i = MIN(k, lp->rows+delta); + for(; i > 0; i--) { + j = lp->var_basic[i]; + lp->is_basic[j] = TRUE; + } + + /* If a column was deleted from the basis then simply add back a non-basic + slack variable; do two scans, if necessary to avoid adding equality slacks */ + if(!isrow && (k < lp->rows)) { + for(j = 0; j <= 1; j++) + for(i = 1; (i <= lp->rows) && (k < lp->rows); i++) + if(!lp->is_basic[i]) { + if(!is_constr_type(lp, i, EQ) || (j == 1)) { + k++; + lp->var_basic[k] = i; + lp->is_basic[i] = TRUE; + } + } + k = 0; + } + + /* We are left with "k" indexes; if no basis variable was deleted, k=rows and the + inverse is still valid, if k+delta < 0 we do not have a valid + basis and must create one (in most usage modes this should not happen, + unless there is a bug) */ + if(k+delta < 0) + Ok = FALSE; + if(isrow || (k != lp->rows)) + set_action(&lp->spx_action, ACTION_REINVERT); + + } + return(Ok); + +} + +STATIC MYBOOL shift_rowdata(lprec *lp, int base, int delta, LLrec *usedmap) +/* Note: Assumes that "lp->rows" HAS NOT been updated to the new count */ +{ + int i, ii; + + /* Shift sparse matrix row data */ + if(lp->matA->is_roworder) + mat_shiftcols(lp->matA, &base, delta, usedmap); + else + mat_shiftrows(lp->matA, &base, delta, usedmap); + + /* Shift data down (insert row), and set default values in positive delta-gap */ + if(delta > 0) { + + /* Shift row data */ + for(ii = lp->rows; ii >= base; ii--) { + i = ii + delta; + lp->orig_rhs[i] = lp->orig_rhs[ii]; + lp->rhs[i] = lp->rhs[ii]; + lp->row_type[i] = lp->row_type[ii]; + } + + /* Set defaults (actual basis set in separate procedure) */ + for(i = 0; i < delta; i++) { + ii = base + i; + lp->orig_rhs[ii] = 0; + lp->rhs[ii] = 0; + lp->row_type[ii] = ROWTYPE_EMPTY; + } + } + + /* Shift data up (delete row) */ + else if(usedmap != NULL) { + for(i = 1, ii = firstActiveLink(usedmap); ii != 0; + i++, ii = nextActiveLink(usedmap, ii)) { + if(i == ii) + continue; + lp->orig_rhs[i] = lp->orig_rhs[ii]; + lp->rhs[i] = lp->rhs[ii]; + lp->row_type[i] = lp->row_type[ii]; + } + delta = i - lp->rows - 1; + } + else if(delta < 0) { + + /* First make sure we don't cross the row count border */ + if(base-delta-1 > lp->rows) + delta = base - lp->rows - 1; + + /* Shift row data (don't shift basis indexes here; done in next step) */ + for(i = base; i <= lp->rows + delta; i++) { + ii = i - delta; + lp->orig_rhs[i] = lp->orig_rhs[ii]; + lp->rhs[i] = lp->rhs[ii]; + lp->row_type[i] = lp->row_type[ii]; + } + } + + shift_basis(lp, base, delta, usedmap, TRUE); + shift_rowcoldata(lp, base, delta, usedmap, TRUE); + inc_rows(lp, delta); + + return(TRUE); +} + +STATIC MYBOOL shift_coldata(lprec *lp, int base, int delta, LLrec *usedmap) +/* Note: Assumes that "lp->columns" has NOT been updated to the new count */ +{ + int i, ii; + + if(lp->bb_totalnodes == 0) + free_duals(lp); + + /* Shift A matrix data */ + if(lp->matA->is_roworder) + mat_shiftrows(lp->matA, &base, delta, usedmap); + else + mat_shiftcols(lp->matA, &base, delta, usedmap); + + /* Shift data right (insert), and set default values in positive delta-gap */ + if(delta > 0) { + + /* Fix variable priority data */ + if((lp->var_priority != NULL) && (base <= lp->columns)) { + for(i = 0; i < lp->columns; i++) + if(lp->var_priority[i] >= base) + lp->var_priority[i] += delta; + } + if((lp->sos_priority != NULL) && (base <= lp->columns)) { + for(i = 0; i < lp->sos_vars; i++) + if(lp->sos_priority[i] >= base) + lp->sos_priority[i] += delta; + } + + /* Fix invalid split variable data */ + if((lp->var_is_free != NULL) && (base <= lp->columns)) { + for(i = 1; i <= lp->columns; i++) + if(abs(lp->var_is_free[i]) >= base) + lp->var_is_free[i] += my_chsign(lp->var_is_free[i] < 0, delta); + } + + /* Shift column data right */ + for(ii = lp->columns; ii >= base; ii--) { + i = ii + delta; + lp->var_type[i] = lp->var_type[ii]; + lp->sc_lobound[i] = lp->sc_lobound[ii]; + lp->orig_obj[i] = lp->orig_obj[ii]; + if(lp->obj != NULL) + lp->obj[i] = lp->obj[ii]; +/* + if(lp->objfromvalue != NULL) + lp->objfromvalue[i] = lp->objfromvalue[ii]; + if(lp->objfrom != NULL) + lp->objfrom[i] = lp->objfrom[ii]; + if(lp->objtill != NULL) + lp->objtill[i] = lp->objtill[ii]; +*/ + if(lp->var_priority != NULL) + lp->var_priority[i-1] = lp->var_priority[ii-1]; + if(lp->bb_varbranch != NULL) + lp->bb_varbranch[i-1] = lp->bb_varbranch[ii-1]; + if(lp->var_is_free != NULL) + lp->var_is_free[i] = lp->var_is_free[ii]; + if(lp->best_solution != NULL) + lp->best_solution[lp->rows + i] = lp->best_solution[lp->rows + ii]; + } + + /* Set defaults */ + for(i = 0; i < delta; i++) { + ii = base + i; + lp->var_type[ii] = ISREAL; + lp->sc_lobound[ii] = 0; + lp->orig_obj[ii] = 0; + if(lp->obj != NULL) + lp->obj[ii] = 0; +/* + if(lp->objfromvalue != NULL) + lp->objfromvalue[ii] = 0; + if(lp->objfrom != NULL) + lp->objfrom[ii] = 0; + if(lp->objtill != NULL) + lp->objtill[ii] = 0; +*/ + if(lp->var_priority != NULL) + lp->var_priority[ii-1] = ii; + if(lp->bb_varbranch != NULL) + lp->bb_varbranch[ii-1] = BRANCH_DEFAULT; + if(lp->var_is_free != NULL) + lp->var_is_free[ii] = 0; + if(lp->best_solution != NULL) + lp->best_solution[lp->rows + ii] = 0; + } + } + + /* Shift data left (delete) */ + else if(usedmap != NULL) { + /* Assume there is no need to handle split columns, since we are doing + this only from presolve, which comes before splitting of columns. */ + + /* First update counts */ + if(lp->int_vars + lp->sc_vars > 0) + for(ii = firstInactiveLink(usedmap); ii != 0; ii = nextInactiveLink(usedmap, ii)) { + if(is_int(lp, ii)) { + lp->int_vars--; + if(SOS_is_member(lp->SOS, 0, ii)) + lp->sos_ints--; + } + if(is_semicont(lp, ii)) + lp->sc_vars--; + } + /* Shift array members */ + for(i = 1, ii = firstActiveLink(usedmap); ii != 0; + i++, ii = nextActiveLink(usedmap, ii)) { + if(i == ii) + continue; + lp->var_type[i] = lp->var_type[ii]; + lp->sc_lobound[i] = lp->sc_lobound[ii]; + lp->orig_obj[i] = lp->orig_obj[ii]; + if(lp->obj != NULL) + lp->obj[i] = lp->obj[ii]; +/* + if(lp->objfromvalue != NULL) + lp->objfromvalue[i] = lp->objfromvalue[ii]; + if(lp->objfrom != NULL) + lp->objfrom[i] = lp->objfrom[ii]; + if(lp->objtill != NULL) + lp->objtill[i] = lp->objtill[ii]; +*/ + if(lp->bb_varbranch != NULL) + lp->bb_varbranch[i-1] = lp->bb_varbranch[ii-1]; + if(lp->var_is_free != NULL) + lp->var_is_free[i] = lp->var_is_free[ii]; + if(lp->best_solution != NULL) + lp->best_solution[lp->rows + i] = lp->best_solution[lp->rows + ii]; + } + /* Shift variable priority data */ + if((lp->var_priority != NULL) || (lp->sos_priority != NULL)) { + int *colmap = NULL, k; + allocINT(lp, &colmap, lp->columns + 1, TRUE); + for(i = 1, ii = 0; i <= lp->columns; i++) { + if(isActiveLink(usedmap, i)) { + ii++; + colmap[i] = ii; + } + } + if(lp->var_priority != NULL) { + for(i = 0, ii = 0; i < lp->columns; i++) { + k = colmap[lp->var_priority[i]]; + if(k > 0) { + lp->var_priority[ii] = k; + ii++; + } + } + } + if(lp->sos_priority != NULL) { + for(i = 0, ii = 0; i < lp->sos_vars; i++) { + k = colmap[lp->sos_priority[i]]; + if(k > 0) { + lp->sos_priority[ii] = k; + ii++; + } + } + lp->sos_vars = ii; + } + FREE(colmap); + } + + delta = i - lp->columns - 1; + } + else if(delta < 0) { + + /* Fix invalid split variable data */ + if(lp->var_is_free != NULL) { + for(i = 1; i <= lp->columns; i++) + if(abs(lp->var_is_free[i]) >= base) + lp->var_is_free[i] -= my_chsign(lp->var_is_free[i] < 0, delta); + } + + /* Shift column data (excluding the basis) */ + for(i = base; i < base-delta; i++) { + if(is_int(lp, i)) { + lp->int_vars--; + if(SOS_is_member(lp->SOS, 0, i)) + lp->sos_ints--; + } + if(is_semicont(lp, i)) + lp->sc_vars--; + } + for(i = base; i <= lp->columns + delta; i++) { + ii = i - delta; + lp->var_type[i] = lp->var_type[ii]; + lp->sc_lobound[i] = lp->sc_lobound[ii]; + lp->orig_obj[i] = lp->orig_obj[ii]; + if(lp->obj != NULL) + lp->obj[i] = lp->obj[ii]; +/* + if(lp->objfromvalue != NULL) + lp->objfromvalue[i] = lp->objfromvalue[ii]; + if(lp->objfrom != NULL) + lp->objfrom[i] = lp->objfrom[ii]; + if(lp->objtill != NULL) + lp->objtill[i] = lp->objtill[ii]; +*/ + if(lp->var_priority != NULL) + lp->var_priority[i-1] = lp->var_priority[ii-1]; + if(lp->bb_varbranch != NULL) + lp->bb_varbranch[i-1] = lp->bb_varbranch[ii-1]; + if(lp->var_is_free != NULL) + lp->var_is_free[i] = lp->var_is_free[ii]; + if(lp->best_solution != NULL) + lp->best_solution[lp->rows + i] = lp->best_solution[lp->rows + ii]; + } + + /* Fix invalid variable priority data */ + if(lp->var_priority != NULL) { + for(i = 0, ii = 0; i < lp->columns; i++) + if(lp->var_priority[i] > base - delta) + lp->var_priority[ii++] = lp->var_priority[i] + delta; + else if(lp->var_priority[i] < base) + lp->var_priority[ii++] = lp->var_priority[i]; + } + if(lp->sos_priority != NULL) { + for(i = 0, ii = 0; i < lp->sos_vars; i++) { + if(lp->sos_priority[i] > base - delta) + lp->sos_priority[ii++] = lp->sos_priority[i] + delta; + else if(lp->sos_priority[i] < base) + lp->sos_priority[ii++] = lp->sos_priority[i]; + } + lp->sos_vars = ii; + } + + } + + shift_basis(lp, lp->rows+base, delta, usedmap, FALSE); + if(SOS_count(lp) > 0) + SOS_shift_col(lp->SOS, 0, base, delta, usedmap, FALSE); + shift_rowcoldata(lp, lp->rows+base, delta, usedmap, FALSE); + inc_columns(lp, delta); + + return( TRUE ); +} + +/* 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; + else + lp->matA->rows += 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; + else + lp->matA->columns += delta; + if(get_Lrows(lp) > 0) + lp->matL->columns += delta; +} + +STATIC MYBOOL inc_rowcol_space(lprec *lp, int delta, MYBOOL isrows) +{ + int i, oldrowcolalloc, rowcolsum; + + /* Get rid of dual arrays */ + if(lp->solvecount > 0) + free_duals(lp); + + /* Set constants */ + oldrowcolalloc = lp->sum_alloc; + lp->sum_alloc += delta; + rowcolsum = lp->sum_alloc + 1; + + /* Reallocate lp memory */ + if(!allocREAL(lp, &lp->upbo, rowcolsum, AUTOMATIC) || + !allocREAL(lp, &lp->orig_upbo, rowcolsum, AUTOMATIC) || + !allocREAL(lp, &lp->lowbo, rowcolsum, AUTOMATIC) || + !allocREAL(lp, &lp->orig_lowbo, rowcolsum, AUTOMATIC) || + !allocREAL(lp, &lp->solution, rowcolsum, AUTOMATIC) || + !allocREAL(lp, &lp->best_solution, rowcolsum, AUTOMATIC) || + !allocMYBOOL(lp, &lp->is_basic, rowcolsum, AUTOMATIC) || + !allocMYBOOL(lp, &lp->is_lower, rowcolsum, AUTOMATIC) || + ((lp->scalars != NULL) && !allocREAL(lp, &lp->scalars, rowcolsum, AUTOMATIC))) + return( FALSE ); + + /* Fill in default values, where appropriate */ + for(i = oldrowcolalloc+1; i < rowcolsum; i++) { + lp->upbo[i] = lp->infinite; + lp->orig_upbo[i] = lp->upbo[i]; + lp->lowbo[i] = 0; + lp->orig_lowbo[i] = lp->lowbo[i]; + lp->is_basic[i] = FALSE; + lp->is_lower[i] = TRUE; + } + + /* Deal with scalars; the vector can be NULL and also contains Lagrangean information */ + if(lp->scalars != NULL) { + for(i = oldrowcolalloc+1; i < rowcolsum; i++) + lp->scalars[i] = 1; + if(oldrowcolalloc == 0) + lp->scalars[0] = 1; + } + + return( inc_presolve_space(lp, delta, isrows) && + resizePricer(lp) ); +} + +STATIC MYBOOL inc_lag_space(lprec *lp, int deltarows, MYBOOL ignoreMAT) +{ + int newsize; + + if(deltarows > 0) { + + newsize = get_Lrows(lp) + deltarows; + + /* Reallocate arrays */ + if(!allocREAL(lp, &lp->lag_rhs, newsize+1, AUTOMATIC) || + !allocREAL(lp, &lp->lambda, newsize+1, AUTOMATIC) || + !allocINT(lp, &lp->lag_con_type, newsize+1, AUTOMATIC)) + return( FALSE ); + + /* Reallocate the matrix (note that the row scalars are stored at index 0) */ + if(!ignoreMAT) { + if(lp->matL == NULL) + lp->matL = mat_create(lp, newsize, lp->columns, lp->epsvalue); + else + inc_matrow_space(lp->matL, deltarows); + } + lp->matL->rows += deltarows; + + } + /* Handle column count expansion as special case */ + else if(!ignoreMAT) { + inc_matcol_space(lp->matL, lp->columns_alloc-lp->matL->columns_alloc+1); + } + + + return( TRUE ); +} + +STATIC MYBOOL inc_row_space(lprec *lp, int deltarows) +{ + int i, rowsum, oldrowsalloc; + MYBOOL ok = TRUE; + + /* Adjust lp row structures */ + i = lp->rows_alloc+deltarows; + if(lp->matA->is_roworder) { + i -= lp->matA->columns_alloc; + SETMIN(i, deltarows); + if(i > 0) + inc_matcol_space(lp->matA, i); + 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; + SETMIN(i, deltarows); + if(i > 0) + inc_matrow_space(lp->matA, i); + rowsum = lp->matA->rows_alloc; + } + if(lp->rows+deltarows > lp->rows_alloc) { + + rowsum++; + oldrowsalloc = lp->rows_alloc; + lp->rows_alloc = rowsum; + deltarows = rowsum - oldrowsalloc; + rowsum++; + + if(!allocREAL(lp, &lp->orig_rhs, rowsum, AUTOMATIC) || + !allocLREAL(lp, &lp->rhs, rowsum, AUTOMATIC) || + !allocINT(lp, &lp->row_type, rowsum, AUTOMATIC) || + !allocINT(lp, &lp->var_basic, rowsum, AUTOMATIC)) + return( FALSE ); + + if(oldrowsalloc == 0) { + lp->var_basic[0] = AUTOMATIC; /*TRUE;*/ /* Indicates default basis */ + lp->orig_rhs[0] = 0; + lp->row_type[0] = ROWTYPE_OFMIN; + } + for(i = oldrowsalloc+1; i < rowsum; i++) { + lp->orig_rhs[i] = 0; + lp->rhs[i] = 0; + lp->row_type[i] = ROWTYPE_EMPTY; + lp->var_basic[i] = i; + } + + /* Adjust hash name structures */ + if(lp->names_used && (lp->row_name != NULL)) { + + /* First check the hash table */ + if(lp->rowname_hashtab->size < lp->rows_alloc) { + hashtable *ht; + + ht = copy_hash_table(lp->rowname_hashtab, lp->row_name, lp->rows_alloc + 1); + if(ht == NULL) { + lp->spx_status = NOMEMORY; + return( FALSE ); + } + free_hash_table(lp->rowname_hashtab); + lp->rowname_hashtab = ht; + } + + /* Then the string storage (i.e. pointer to the item's hash structure) */ + lp->row_name = (hashelem **) realloc(lp->row_name, (rowsum) * sizeof(*lp->row_name)); + if(lp->row_name == NULL) { + lp->spx_status = NOMEMORY; + return( FALSE ); + } + for(i = oldrowsalloc + 1; i < rowsum; i++) + lp->row_name[i] = NULL; + } + + ok = inc_rowcol_space(lp, deltarows, TRUE); + + } + return(ok); +} + +STATIC MYBOOL inc_col_space(lprec *lp, int deltacols) +{ + int i,colsum, oldcolsalloc; + + i = lp->columns_alloc+deltacols; + if(lp->matA->is_roworder) { + i -= lp->matA->rows_alloc; + SETMIN(i, deltacols); + if(i > 0) + inc_matrow_space(lp->matA, i); + colsum = lp->matA->rows_alloc; + } + else { + i -= lp->matA->columns_alloc; + SETMIN(i, deltacols); + if(i > 0) + inc_matcol_space(lp->matA, i); + colsum = lp->matA->columns_alloc; + } + + if(lp->columns+deltacols >= lp->columns_alloc) { + + colsum++; + oldcolsalloc = lp->columns_alloc; + lp->columns_alloc = colsum; + deltacols = colsum - oldcolsalloc; + colsum++; + + /* Adjust hash name structures */ + if(lp->names_used && (lp->col_name != NULL)) { + + /* First check the hash table */ + if(lp->colname_hashtab->size < lp->columns_alloc) { + hashtable *ht; + + ht = copy_hash_table(lp->colname_hashtab, lp->col_name, lp->columns_alloc + 1); + if(ht != NULL) { + free_hash_table(lp->colname_hashtab); + lp->colname_hashtab = ht; + } + } + + /* Then the string storage (i.e. pointer to the item's hash structure) */ + lp->col_name = (hashelem **) realloc(lp->col_name, (colsum) * sizeof(*lp->col_name)); + for(i = oldcolsalloc+1; i < colsum; i++) + lp->col_name[i] = NULL; + } + + if(!allocREAL(lp, &lp->orig_obj, colsum, AUTOMATIC) || + !allocMYBOOL(lp, &lp->var_type, colsum, AUTOMATIC) || + !allocREAL(lp, &lp->sc_lobound, colsum, AUTOMATIC) || + ((lp->obj != NULL) && !allocREAL(lp, &lp->obj, colsum, AUTOMATIC)) || + ((lp->var_priority != NULL) && !allocINT(lp, &lp->var_priority, colsum-1, AUTOMATIC)) || + ((lp->var_is_free != NULL) && !allocINT(lp, &lp->var_is_free, colsum, AUTOMATIC)) || + ((lp->bb_varbranch != NULL) && !allocMYBOOL(lp, &lp->bb_varbranch, colsum-1, AUTOMATIC))) + return( FALSE ); + + /* Make sure that Lagrangean constraints have the same number of columns */ + if(get_Lrows(lp) > 0) + inc_lag_space(lp, 0, FALSE); + + /* Update column pointers */ + for(i = MIN(oldcolsalloc, lp->columns) + 1; i < colsum; i++) { + lp->orig_obj[i] = 0; + if(lp->obj != NULL) + lp->obj[i] = 0; + lp->var_type[i] = ISREAL; + lp->sc_lobound[i] = 0; + if(lp->var_priority != NULL) + lp->var_priority[i-1] = i; + } + + if(lp->var_is_free != NULL) { + for(i = oldcolsalloc+1; i < colsum; i++) + lp->var_is_free[i] = 0; + } + + if(lp->bb_varbranch != NULL) { + for(i = oldcolsalloc; i < colsum-1; i++) + lp->bb_varbranch[i] = BRANCH_DEFAULT; + } + + inc_rowcol_space(lp, deltacols, FALSE); + + } + return(TRUE); +} + +/* Problem manipulation routines */ + +MYBOOL __WINAPI set_obj(lprec *lp, int colnr, LPSREAL value) +{ + if(colnr <= 0) + colnr = set_rh(lp, 0, value); + else + colnr = set_mat(lp, 0, colnr, value); + return((MYBOOL) colnr); +} + +MYBOOL __WINAPI set_obj_fnex(lprec *lp, int count, LPSREAL *row, int *colno) +{ + MYBOOL chsgn = is_maxim(lp); + int i, ix; + LPSREAL value; + + if(row == NULL) + return( FALSE ); + + else if(colno == NULL) { + if(count <= 0) + count = lp->columns; + for(i = 1; i <= count; i++) { + value = row[i]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, lp->matA->epsvalue); +#endif + lp->orig_obj[i] = my_chsign(chsgn, scaled_mat(lp, value, 0, i)); + } + } + else { + MEMCLEAR(lp->orig_obj, lp->columns+1); + for(i = 0; i < count; i++) { + ix = colno[i]; + value = row[i]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, lp->matA->epsvalue); +#endif + lp->orig_obj[ix] = my_chsign(chsgn, scaled_mat(lp, value, 0, ix)); + } + } + + return(TRUE); +} + +MYBOOL __WINAPI set_obj_fn(lprec *lp, LPSREAL *row) +{ + return( set_obj_fnex(lp, 0, row, NULL) ); +} + +MYBOOL __WINAPI str_set_obj_fn(lprec *lp, char *row_string) +{ + int i; + MYBOOL ret = TRUE; + LPSREAL *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); + if(p == newp) { + report(lp, IMPORTANT, "str_set_obj_fn: Bad string %s\n", p); + lp->spx_status = DATAIGNORED; + ret = FALSE; + break; + } + else + p = newp; + } + if(lp->spx_status != DATAIGNORED) + ret = set_obj_fn(lp, arow); + FREE(arow); + return( ret ); +} + +STATIC MYBOOL append_columns(lprec *lp, int deltacolumns) +{ + if(!inc_col_space(lp, deltacolumns)) + return( FALSE ); + varmap_add(lp, lp->sum+1, deltacolumns); + shift_coldata(lp, lp->columns+1, deltacolumns, NULL); + return( TRUE ); +} + +STATIC MYBOOL append_rows(lprec *lp, int deltarows) +{ + if(!inc_row_space(lp, deltarows)) + return( FALSE ); + varmap_add(lp, lp->rows+1, deltarows); + shift_rowdata(lp, lp->rows+1, deltarows, NULL); + + return( TRUE ); +} + +MYBOOL __WINAPI set_add_rowmode(lprec *lp, MYBOOL turnon) +{ + if((lp->solvecount == 0) && (turnon ^ lp->matA->is_roworder)) + return( mat_transpose(lp->matA) ); + else + return( FALSE ); +} + +MYBOOL __WINAPI is_add_rowmode(lprec *lp) +{ + return(lp->matA->is_roworder); +} + +MYBOOL __WINAPI set_row(lprec *lp, int rownr, LPSREAL *row) +{ + if((rownr < 0) || (rownr > lp->rows)) { + report(lp, IMPORTANT, "set_row: Row %d out of range\n", rownr); + return( FALSE ); + } + if(rownr == 0) + return( set_obj_fn(lp, row) ); + else + 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) +{ + if((rownr < 0) || (rownr > lp->rows)) { + report(lp, IMPORTANT, "set_rowex: Row %d out of range\n", rownr); + return( FALSE ); + } + if(rownr == 0) + return( set_obj_fnex(lp, count, row, colno) ); + else + 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) +{ + int n; + MYBOOL status = FALSE; + + if(!(constr_type == LE || constr_type == GE || constr_type == EQ)) { + report(lp, IMPORTANT, "add_constraintex: Invalid %d constraint type\n", constr_type); + return( status ); + } + + /* Prepare for a new row */ + if(!append_rows(lp, 1)) + return( status ); + + /* Set constraint parameters, fix the slack */ + if((constr_type & ROWTYPE_CONSTRAINT) == EQ) { + lp->equalities++; + lp->orig_upbo[lp->rows] = 0; + lp->upbo[lp->rows] = 0; + } + lp->row_type[lp->rows] = constr_type; + + if(is_chsign(lp, lp->rows) && (rh != 0)) + lp->orig_rhs[lp->rows] = -rh; + else + lp->orig_rhs[lp->rows] = rh; + + /* Insert the non-zero constraint values */ + if(colno == NULL && row != NULL) + n = lp->columns; + else + n = count; + mat_appendrow(lp->matA, n, row, colno, my_chsign(is_chsign(lp, lp->rows), 1.0), TRUE); + if(!lp->varmap_locked) + presolve_setOrig(lp, lp->rows, lp->columns); + +#ifdef Paranoia + if(lp->matA->is_roworder) + n = lp->matA->columns; + else + n = lp->matA->rows; + if(lp->rows != n) { + report(lp, SEVERE, "add_constraintex: Row count mismatch %d vs %d\n", + lp->rows, n); + } + else if(is_BasisReady(lp) && !verify_basis(lp)) + report(lp, SEVERE, "add_constraintex: Invalid basis detected for row %d\n", lp->rows); + else +#endif + status = TRUE; + + return( status ); +} + +MYBOOL __WINAPI add_constraint(lprec *lp, LPSREAL *row, int constr_type, LPSREAL 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) +{ + int i; + char *p, *newp; + LPSREAL *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); + if(p == newp) { + report(lp, IMPORTANT, "str_add_constraint: Bad string '%s'\n", p); + lp->spx_status = DATAIGNORED; + break; + } + else + p = newp; + } + if(lp->spx_status != DATAIGNORED) + status = add_constraint(lp, aRow, constr_type, rh); + FREE(aRow); + + return(status); +} + +STATIC MYBOOL del_constraintex(lprec *lp, LLrec *rowmap) +{ + int i; + + if(lp->equalities > 0) + for(i = firstInactiveLink(rowmap); i != 0; i = nextInactiveLink(rowmap, i)) { + if(is_constr_type(lp, i, EQ)) { +#ifdef Paranoia + if(lp->equalities == 0) + report(lp, SEVERE, "del_constraintex: Invalid count of equality constraints\n"); +#endif + lp->equalities--; + } + } + + varmap_delete(lp, 1, -1, rowmap); + shift_rowdata(lp, 1, -1, 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); + } + +#ifdef Paranoia + if(is_BasisReady(lp) && !verify_basis(lp)) + report(lp, SEVERE, "del_constraintex: Invalid basis detected\n"); +#endif + + return(TRUE); +} +MYBOOL __WINAPI del_constraint(lprec *lp, int rownr) +{ + MYBOOL preparecompact = (MYBOOL) (rownr < 0); + + if(preparecompact) + rownr = -rownr; + if((rownr < 1) || (rownr > lp->rows)) { + 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) + { + presolve_setOrig(lp, lp->rows, lp->columns); + if(lp->names_used) + del_varnameex(lp, lp->row_name, lp->rows, lp->rowname_hashtab, rownr, NULL); + } + +#ifdef Paranoia + if(is_BasisReady(lp) && !verify_basis(lp)) + report(lp, SEVERE, "del_constraint: Invalid basis detected at row %d\n", rownr); +#endif + + return(TRUE); +} + +MYBOOL __WINAPI add_lag_con(lprec *lp, LPSREAL *row, int con_type, LPSREAL rhs) +{ + int k; + LPSREAL sign; + + if(con_type == LE || con_type == EQ) + sign = 1; + else if(con_type == GE) + sign = -1; + else { + report(lp, IMPORTANT, "add_lag_con: Constraint type %d not implemented\n", con_type); + return(FALSE); + } + + inc_lag_space(lp, 1, FALSE); + + k = get_Lrows(lp); + lp->lag_rhs[k] = rhs * sign; + mat_appendrow(lp->matL, lp->columns, row, NULL, sign, TRUE); + lp->lambda[k] = 0; + lp->lag_con_type[k] = con_type; + + return(TRUE); +} + +MYBOOL __WINAPI str_add_lag_con(lprec *lp, char *row_string, int con_type, LPSREAL rhs) +{ + int i; + MYBOOL ret = TRUE; + LPSREAL *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); + if(p == new_p) { + report(lp, IMPORTANT, "str_add_lag_con: Bad string '%s'\n", p); + lp->spx_status = DATAIGNORED; + ret = FALSE; + break; + } + else + p = new_p; + } + if(lp->spx_status != DATAIGNORED) + ret = add_lag_con(lp, a_row, con_type, rhs); + FREE(a_row); + return( ret ); +} + +/* INLINE */ MYBOOL is_splitvar(lprec *lp, int colnr) +/* Two cases handled by var_is_free: + + 1) LB:-Inf / UB:var_is_free != NULL) && + (lp->var_is_free[colnr] < 0) && (-lp->var_is_free[colnr] != colnr))); +} + +void del_splitvars(lprec *lp) +{ + int j, jj, i; + + if(lp->var_is_free != NULL) { + for(j = lp->columns; j >= 1; j--) + if(is_splitvar(lp, j)) { + /* Check if we need to modify the basis */ + jj = lp->rows+abs(lp->var_is_free[j]); + i = lp->rows+j; + if(lp->is_basic[i] && !lp->is_basic[jj]) { + i = findBasisPos(lp, i, NULL); + set_basisvar(lp, i, jj); + } + /* Delete the helper column */ + del_column(lp, j); + } + FREE(lp->var_is_free); + } +} + +MYBOOL __WINAPI set_column(lprec *lp, int colnr, LPSREAL *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) +{ + return( mat_setcol(lp->matA, colnr, count, column, rowno, TRUE, TRUE) ); +} + +MYBOOL __WINAPI add_columnex(lprec *lp, int count, LPSREAL *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 + 2: Dense vector indicated by (rowno == NULL) over 0..count+get_Lrows() elements + 3: Sparse vector set over row vectors rowno, over 0..count-1 elements. + + NB! If the column has only one entry, this should be handled as + a bound, but this currently is not the case */ +{ + MYBOOL status = FALSE; + + /* Prepare and shift column vectors */ + if(!append_columns(lp, 1)) + return( status ); + + /* Append sparse regular constraint values */ + if(mat_appendcol(lp->matA, count, column, rowno, 1.0, TRUE) < 0) + report(lp, SEVERE, "add_columnex: Data column %d supplied in non-ascending row index order.\n", + lp->columns); + else +#ifdef Paranoia + if(lp->columns != (lp->matA->is_roworder ? lp->matA->rows : 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)); + } + else if(is_BasisReady(lp) && (lp->P1extraDim == 0) && !verify_basis(lp)) + report(lp, SEVERE, "add_columnex: Invalid basis detected for column %d\n", + lp->columns); + else +#endif + status = TRUE; + + if(!lp->varmap_locked) + presolve_setOrig(lp, lp->rows, lp->columns); + + return( status ); +} + +MYBOOL __WINAPI add_column(lprec *lp, LPSREAL *column) +{ + del_splitvars(lp); + return(add_columnex(lp, lp->rows, column, NULL)); +} + +MYBOOL __WINAPI str_add_column(lprec *lp, char *col_string) +{ + int i; + MYBOOL ret = TRUE; + LPSREAL *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); + if(p == newp) { + report(lp, IMPORTANT, "str_add_column: Bad string '%s'\n", p); + lp->spx_status = DATAIGNORED; + ret = FALSE; + break; + } + else + p = newp; + } + if(lp->spx_status != DATAIGNORED) + ret = add_column(lp, aCol); + FREE(aCol); + return( ret ); +} + +STATIC MYBOOL del_varnameex(lprec *lp, hashelem **namelist, int items, hashtable *ht, int varnr, LLrec *varmap) +{ + int i, n; + + /* First drop hash table entries of the deleted variables */ + if(varmap != NULL) + i = firstInactiveLink(varmap); + else + i = varnr; + while(i > 0) { + if(namelist[i] != NULL) { + if(namelist[i]->name != NULL) + drophash(namelist[i]->name, namelist, ht); + } + if(varmap != NULL) + i = nextInactiveLink(varmap, i); + else + i = 0; + } + + /* Then compress the name list */ + if(varmap != NULL) { + i = firstInactiveLink(varmap); + n = nextActiveLink(varmap, i); + varnr = i; + } + else { + i = varnr; + n = i + 1; + } + while(n != 0) { + namelist[i] = namelist[n]; + if((namelist[i] != NULL) && (namelist[i]->index > varnr)) + namelist[i]->index -= n - i; + i++; + if(varmap != NULL) + n = nextActiveLink(varmap, i); + else if(n <= items) /* items has been updated for the new count */ + n++; + else + n = 0; + } + + return( TRUE ); +} +STATIC MYBOOL del_columnex(lprec *lp, LLrec *colmap) +{ + varmap_delete(lp, lp->rows+1, -1, colmap); + shift_coldata(lp, 1, -1, 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); + } +#ifdef Paranoia + if(is_BasisReady(lp) && (lp->P1extraDim == 0) && !verify_basis(lp)) + report(lp, SEVERE, "del_columnex: Invalid basis detected\n"); +#endif + + return(TRUE); +} +MYBOOL __WINAPI del_column(lprec *lp, int colnr) +{ + MYBOOL preparecompact = (MYBOOL) (colnr < 0); + + if(preparecompact) + colnr = -colnr; + if((colnr > lp->columns) || (colnr < 1)) { + 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) */ + + varmap_delete(lp, my_chsign(preparecompact, lp->rows+colnr), -1, NULL); + shift_coldata(lp, my_chsign(preparecompact, colnr), -1, NULL); + 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); + } +#ifdef Paranoia + if(is_BasisReady(lp) && (lp->P1extraDim == 0) && !verify_basis(lp)) + report(lp, SEVERE, "del_column: Invalid basis detected at column %d (%d)\n", colnr, lp->columns); +#endif + + return(TRUE); +} + +void __WINAPI set_simplextype(lprec *lp, int simplextype) +{ + lp->simplex_strategy = simplextype; +} + +int __WINAPI get_simplextype(lprec *lp) +{ + return(lp->simplex_strategy); +} + +void __WINAPI set_preferdual(lprec *lp, MYBOOL dodual) +{ + if(dodual & TRUE) + lp->simplex_strategy = SIMPLEX_DUAL_DUAL; + else + lp->simplex_strategy = SIMPLEX_PRIMAL_PRIMAL; +} + +void __WINAPI set_bounds_tighter(lprec *lp, MYBOOL tighten) +{ + lp->tighten_on_set = tighten; +} +MYBOOL __WINAPI get_bounds_tighter(lprec *lp) +{ + return(lp->tighten_on_set); +} + +MYBOOL __WINAPI set_upbo(lprec *lp, int colnr, LPSREAL value) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "set_upbo: Column %d out of range\n", colnr); + return(FALSE); + } + +#ifdef DoBorderRounding + if(fabs(value) < lp->infinite) + value = my_avoidtiny(value, lp->matA->epsvalue); +#endif + value = scaled_value(lp, value, lp->rows + colnr); + if(lp->tighten_on_set) { + if(value < lp->orig_lowbo[lp->rows + colnr]) { + report(lp, IMPORTANT, "set_upbo: Upperbound must be >= lowerbound\n"); + return(FALSE); + } + if(value < lp->orig_upbo[lp->rows + colnr]) { + set_action(&lp->spx_action, ACTION_REBASE); + lp->orig_upbo[lp->rows + colnr] = value; + } + } + else + { + set_action(&lp->spx_action, ACTION_REBASE); + if(value > lp->infinite) + value = lp->infinite; + lp->orig_upbo[lp->rows + colnr] = value; + } + return(TRUE); +} + +LPSREAL __WINAPI get_upbo(lprec *lp, int colnr) +{ + LPSREAL value; + + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "get_upbo: Column %d out of range\n", colnr); + return(0); + } + + value = lp->orig_upbo[lp->rows + colnr]; + value = unscaled_value(lp, value, lp->rows + colnr); + return(value); +} + +MYBOOL __WINAPI set_lowbo(lprec *lp, int colnr, LPSREAL value) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "set_lowbo: Column %d out of range\n", colnr); + return(FALSE); + } + +#ifdef DoBorderRounding + if(fabs(value) < lp->infinite) + value = my_avoidtiny(value, lp->matA->epsvalue); +#endif + value = scaled_value(lp, value, lp->rows + colnr); + if(lp->tighten_on_set) { + if(value > lp->orig_upbo[lp->rows + colnr]) { + report(lp, IMPORTANT, "set_lowbo: Upper bound must be >= lower bound\n"); + return(FALSE); + } + if((value < 0) || (value > lp->orig_lowbo[lp->rows + colnr])) { + set_action(&lp->spx_action, ACTION_REBASE); + lp->orig_lowbo[lp->rows + colnr] = value; + } + } + else + { + set_action(&lp->spx_action, ACTION_REBASE); + if(value < -lp->infinite) + value = -lp->infinite; + lp->orig_lowbo[lp->rows + colnr] = value; + } + return(TRUE); +} + +LPSREAL __WINAPI get_lowbo(lprec *lp, int colnr) +{ + LPSREAL value; + + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "get_lowbo: Column %d out of range\n", colnr); + return(0); + } + + value = lp->orig_lowbo[lp->rows + colnr]; + value = unscaled_value(lp, value, lp->rows + colnr); + return(value); +} + +MYBOOL __WINAPI set_bounds(lprec *lp, int colnr, LPSREAL lower, LPSREAL upper) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "set_bounds: Column %d out of range\n", colnr); + return(FALSE); + } + if(fabs(upper - lower) < lp->epsvalue) { + if(lower < 0) + lower = upper; + else + upper = lower; + } + else if(lower > upper) { + report(lp, IMPORTANT, "set_bounds: Column %d upper bound must be >= lower bound\n", + colnr); + return( FALSE ); + } + + colnr += lp->rows; + + if(lower < -lp->infinite) + lower = -lp->infinite; + else if(lp->scaling_used) { + lower = scaled_value(lp, lower, colnr); +#ifdef DoBorderRounding + lower = my_avoidtiny(lower, lp->matA->epsvalue); +#endif + } + + if(upper > lp->infinite) + upper = lp->infinite; + else if(lp->scaling_used) { + upper = scaled_value(lp, upper, colnr); +#ifdef DoBorderRounding + upper = my_avoidtiny(upper, lp->matA->epsvalue); +#endif + } + + lp->orig_lowbo[colnr] = lower; + lp->orig_upbo[colnr] = upper; + set_action(&lp->spx_action, ACTION_REBASE); + + return(TRUE); +} + +MYBOOL get_bounds(lprec *lp, int column, LPSREAL *lower, LPSREAL *upper) +{ + if((column > lp->columns) || (column < 1)) { + report(lp, IMPORTANT, "get_bounds: Column %d out of range", column); + return(FALSE); + } + + if(lower != NULL) + *lower = get_lowbo(lp, column); + if(upper != NULL) + *upper = get_upbo(lp, column); + + return(TRUE); +} + +MYBOOL __WINAPI set_int(lprec *lp, int colnr, MYBOOL var_type) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "set_int: Column %d out of range\n", colnr); + return(FALSE); + } + + if((lp->var_type[colnr] & ISINTEGER) != 0) { + lp->int_vars--; + lp->var_type[colnr] &= ~ISINTEGER; + } + if(var_type) { + lp->var_type[colnr] |= ISINTEGER; + lp->int_vars++; + if(lp->columns_scaled && !is_integerscaling(lp)) + unscale_columns(lp); + } + return(TRUE); +} + +MYBOOL __WINAPI is_int(lprec *lp, int colnr) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "is_int: Column %d out of range\n", colnr); + return(FALSE); + } + + return((lp->var_type[colnr] & ISINTEGER) != 0); +} + +MYBOOL __WINAPI is_SOS_var(lprec *lp, int colnr) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "is_SOS_var: Column %d out of range\n", colnr); + return(FALSE); + } + + 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) +{ + SOSrec *SOS; + int k; + + if((sostype < 1) || (count < 0)) { + report(lp, IMPORTANT, "add_SOS: Invalid SOS type definition %d\n", sostype); + return( 0 ); + } + + /* Make sure SOSes of order 3 and higher are properly defined */ + if(sostype > 2) { + int j; + for(k = 0; 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"); + return( 0 ); + } + } + } + + /* Make size in the list to handle another SOS record */ + if(lp->SOS == NULL) + lp->SOS = create_SOSgroup(lp); + + /* Create and append SOS to list */ + SOS = create_SOSrec(lp->SOS, name, sostype, priority, count, sosvars, weights); + k = append_SOSgroup(lp->SOS, SOS); + + return(k); +} + +STATIC int add_GUB(lprec *lp, char *name, int priority, int count, int *gubvars) +{ + SOSrec *GUB; + int k; + +#ifdef Paranoia + if(count < 0) { + report(lp, IMPORTANT, "add_GUB: Invalid GUB member count %d\n", count); + return(FALSE); + } +#endif + + /* Make size in the list to handle another GUB record */ + if(lp->GUB == NULL) + lp->GUB = create_SOSgroup(lp); + + /* Create and append GUB to list */ + GUB = create_SOSrec(lp->GUB, name, 1, priority, count, gubvars, NULL); + GUB->isGUB = TRUE; + k = append_SOSgroup(lp->GUB, GUB); + + return(k); +} + +MYBOOL __WINAPI set_binary(lprec *lp, int colnr, MYBOOL must_be_bin) +{ + MYBOOL status = FALSE; + + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "set_binary: Column %d out of range\n", colnr); + return( status ); + } + + status = set_int(lp, colnr, must_be_bin); + if(status && must_be_bin) + status = set_bounds(lp, colnr, 0, 1); + return( status ); +} + +MYBOOL __WINAPI is_binary(lprec *lp, int colnr) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "is_binary: Column %d out of range\n", colnr); + return(FALSE); + } + + return((MYBOOL) (((lp->var_type[colnr] & ISINTEGER) != 0) && + (get_lowbo(lp, colnr) == 0) && + (fabs(get_upbo(lp, colnr) - 1) < lp->epsprimal))); +} + +MYBOOL __WINAPI set_unbounded(lprec *lp, int colnr) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "set_unbounded: Column %d out of range\n", colnr); + return( FALSE ); + } + + return( set_bounds(lp, colnr, -lp->infinite, lp->infinite) ); +} + +MYBOOL __WINAPI is_unbounded(lprec *lp, int colnr) +{ + MYBOOL test; + + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "is_unbounded: Column %d out of range\n", colnr); + return(FALSE); + } + + test = is_splitvar(lp, colnr); + if(!test) { + colnr += lp->rows; + test = (MYBOOL) ((lp->orig_lowbo[colnr] <= -lp->infinite) && + (lp->orig_upbo[colnr] >= lp->infinite)); + } + return( test ); +} + +MYBOOL __WINAPI is_negative(lprec *lp, int colnr) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "is_negative: Column %d out of range\n", colnr); + return( FALSE ); + } + + colnr += lp->rows; + return( (MYBOOL) ((lp->orig_upbo[colnr] <= 0) && + (lp->orig_lowbo[colnr] < 0)) ); +} + +MYBOOL __WINAPI set_var_weights(lprec *lp, LPSREAL *weights) +{ + if(lp->var_priority != NULL) { + FREE(lp->var_priority); + } + if(weights != NULL) { + int n; + allocINT(lp, &lp->var_priority, lp->columns_alloc, FALSE); + for(n = 0; n < lp->columns; n++) { + lp->var_priority[n] = n+1; + } + n = sortByREAL(lp->var_priority, weights, lp->columns, 0, FALSE); + } + return(TRUE); +} + +MYBOOL __WINAPI set_var_priority(lprec *lp) +/* Experimental automatic variable ordering/priority setting */ +{ + MYBOOL status = FALSE; + + if(is_bb_mode(lp, NODE_AUTOORDER) && + (lp->var_priority == NULL) && + (SOS_count(lp) == 0)) { + + LPSREAL *rcost = NULL; + int i, j, *colorder = NULL; + + allocINT(lp, &colorder, lp->columns+1, FALSE); + + /* Create an "optimal" B&B variable ordering; this MDO-based routine + returns column indeces in an increasing order of co-dependency. + It can be argued that arranging the columns in right-to-left + MDO order should tend to minimize the consequences of choosing the + wrong variable by reducing the average B&B depth. */ + colorder[0] = lp->columns; + for(j = 1; j <= lp->columns; j++) + colorder[j] = lp->rows+j; + i = getMDO(lp, NULL, colorder, NULL, FALSE); + + /* Map to variable weight */ + allocREAL(lp, &rcost, lp->columns+1, FALSE); + for(j = lp->columns; j > 0; j--) { + i = colorder[j]-lp->rows; + rcost[i] = -j; + } + + /* Establish the MIP variable priorities */ + set_var_weights(lp, rcost+1); + + FREE(rcost); + FREE(colorder); + status = TRUE; + } + + return( status ); +} + +int __WINAPI get_var_priority(lprec *lp, int colnr) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "get_var_priority: Column %d out of range\n", colnr); + return(FALSE); + } + + if(lp->var_priority == NULL) + return(colnr); + else + return(lp->var_priority[colnr - 1]); +} + +MYBOOL __WINAPI set_semicont(lprec *lp, int colnr, MYBOOL must_be_sc) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "set_semicont: Column %d out of range\n", colnr); + return(FALSE); + } + + if(lp->sc_lobound[colnr] != 0) { + lp->sc_vars--; + lp->var_type[colnr] &= ~ISSEMI; + } + lp->sc_lobound[colnr] = must_be_sc; + if(must_be_sc) { + lp->var_type[colnr] |= ISSEMI; + lp->sc_vars++; + } + return(TRUE); +} + +MYBOOL __WINAPI is_semicont(lprec *lp, int colnr) +{ + if((colnr > lp->columns) || (colnr < 1)) { + report(lp, IMPORTANT, "is_semicont: Column %d out of range\n", colnr); + return(FALSE); + } + + return((lp->var_type[colnr] & ISSEMI) != 0); +} + +MYBOOL __WINAPI set_rh(lprec *lp, int rownr, LPSREAL value) +{ + if((rownr > lp->rows) || (rownr < 0)) { + report(lp, IMPORTANT, "set_rh: Row %d out of range\n", rownr); + return(FALSE); + } + + if(((rownr == 0) && (!is_maxim(lp))) || + ((rownr > 0) && is_chsign(lp, rownr))) /* setting of RHS of OF IS meaningful */ + value = my_flipsign(value); + if(fabs(value) > lp->infinite) { + if(value < 0) + value = -lp->infinite; + else + value = lp->infinite; + } +#ifdef DoBorderRounding + else + value = my_avoidtiny(value, lp->matA->epsvalue); +#endif + value = scaled_value(lp, value, rownr); + lp->orig_rhs[rownr] = value; + set_action(&lp->spx_action, ACTION_RECOMPUTE); + return(TRUE); +} + +LPSREAL __WINAPI get_rh(lprec *lp, int rownr) +{ + LPSREAL value; + + if((rownr > lp->rows) || (rownr < 0)) { + report(lp, IMPORTANT, "get_rh: Row %d out of range", rownr); + return( 0.0 ); + } + + value = lp->orig_rhs[rownr]; + if (((rownr == 0) && !is_maxim(lp)) || + ((rownr > 0) && is_chsign(lp, rownr))) /* setting of RHS of OF IS meaningful */ + value = my_flipsign(value); + value = unscaled_value(lp, value, rownr); + return(value); +} + +LPSREAL get_rh_upper(lprec *lp, int rownr) +{ + LPSREAL value, valueR; + + value = lp->orig_rhs[rownr]; + if(is_chsign(lp, rownr)) { + valueR = lp->orig_upbo[rownr]; + if(is_infinite(lp, valueR)) + return(lp->infinite); + value = my_flipsign(value); + value += valueR; + } + value = unscaled_value(lp, value, rownr); + return(value); +} + +LPSREAL get_rh_lower(lprec *lp, int rownr) +{ + LPSREAL value, valueR; + + value = lp->orig_rhs[rownr]; + if(is_chsign(lp, rownr)) + value = my_flipsign(value); + else { + valueR = lp->orig_upbo[rownr]; + if(is_infinite(lp, valueR)) + return(-lp->infinite); + value -= valueR; + } + value = unscaled_value(lp, value, rownr); + return(value); +} + +MYBOOL set_rh_upper(lprec *lp, int rownr, LPSREAL value) +{ + if(rownr > lp->rows || rownr < 1) { + report(lp, IMPORTANT, "set_rh_upper: Row %d out of range", rownr); + return(FALSE); + } + + /* First scale the value */ + value = scaled_value(lp, value, rownr); + + /* orig_rhs stores the upper bound assuming a < constraint; + If we have a > constraint, we must adjust the range instead */ + if(is_chsign(lp, rownr)) { + if(is_infinite(lp, value)) + lp->orig_upbo[rownr] = lp->infinite; + else { +#ifdef Paranoia + if(value + lp->orig_rhs[rownr] < 0) { + report(lp, SEVERE, "set_rh_upper: Invalid negative range in row %d\n", + rownr); + return(FALSE); + } +#endif +#ifdef DoBorderRounding + lp->orig_upbo[rownr] = my_avoidtiny(value + lp->orig_rhs[rownr], lp->epsvalue); +#else + lp->orig_upbo[rownr] = value + lp->orig_rhs[rownr]; +#endif + } + } + else { + /* If there is a constraint range, then this has to be adjusted also */ + if(!is_infinite(lp, lp->orig_upbo[rownr])) { + lp->orig_upbo[rownr] -= lp->orig_rhs[rownr] - value; + my_roundzero(lp->orig_upbo[rownr], lp->epsvalue); + if(lp->orig_upbo[rownr] < 0) { + report(lp, IMPORTANT, "set_rh_upper: Negative bound set for constraint %d made 0\n", rownr); + lp->orig_upbo[rownr] = 0; + } + } + lp->orig_rhs[rownr] = value; + } + return(TRUE); +} + +MYBOOL set_rh_lower(lprec *lp, int rownr, LPSREAL value) +{ + if(rownr > lp->rows || rownr < 1) { + report(lp, IMPORTANT, "set_rh_lower: Row %d out of range", rownr); + return(FALSE); + } + + /* First scale the value */ + value = scaled_value(lp, value, rownr); + + /* orig_rhs stores the upper bound assuming a < constraint; + If we have a < constraint, we must adjust the range instead */ + if(!is_chsign(lp, rownr)) { + if(is_infinite(lp, value)) + lp->orig_upbo[rownr] = lp->infinite; + else { +#ifdef Paranoia + if(lp->orig_rhs[rownr] - value < 0) { + report(lp, SEVERE, "set_rh_lower: Invalid negative range in row %d\n", + rownr); + return(FALSE); + } +#endif +#ifdef DoBorderRounding + lp->orig_upbo[rownr] = my_avoidtiny(lp->orig_rhs[rownr] - value, lp->epsvalue); +#else + lp->orig_upbo[rownr] = lp->orig_rhs[rownr] - value; +#endif + } + } + else { + value = my_flipsign(value); + /* If there is a constraint range, then this has to be adjusted also */ + if(!is_infinite(lp, lp->orig_upbo[rownr])) { + lp->orig_upbo[rownr] -= lp->orig_rhs[rownr] - value; + my_roundzero(lp->orig_upbo[rownr], lp->epsvalue); + if(lp->orig_upbo[rownr] < 0) { + report(lp, IMPORTANT, "set_rh_lower: Negative bound set for constraint %d made 0\n", rownr); + lp->orig_upbo[rownr] = 0; + } + } + lp->orig_rhs[rownr] = value; + } + return(TRUE); +} + +MYBOOL __WINAPI set_rh_range(lprec *lp, int rownr, LPSREAL deltavalue) +{ + if((rownr > lp->rows) || (rownr < 1)) { + report(lp, IMPORTANT, "set_rh_range: Row %d out of range", rownr); + return(FALSE); + } + + deltavalue = scaled_value(lp, deltavalue, rownr); + if(deltavalue > lp->infinite) + deltavalue = lp->infinite; + else if(deltavalue < -lp->infinite) + deltavalue = -lp->infinite; +#ifdef DoBorderRounding + else + deltavalue = my_avoidtiny(deltavalue, lp->matA->epsvalue); +#endif + + if(fabs(deltavalue) < lp->epsprimal) { + /* Conversion to EQ */ + set_constr_type(lp, rownr, EQ); + } + else if(is_constr_type(lp, rownr, EQ)) { + /* EQ with a non-zero range */ + if(deltavalue > 0) + set_constr_type(lp, rownr, GE); + else + set_constr_type(lp, rownr, LE); + lp->orig_upbo[rownr] = fabs(deltavalue); + } + else { + /* Modify GE/LE ranges */ + lp->orig_upbo[rownr] = fabs(deltavalue); + } + + return(TRUE); +} + +LPSREAL __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); + return(FALSE); + } + + if(lp->orig_upbo[rownr] >= lp->infinite) + return(lp->orig_upbo[rownr]); + else + return(unscaled_value(lp, lp->orig_upbo[rownr], rownr)); +} + +void __WINAPI set_rh_vec(lprec *lp, LPSREAL *rh) +{ + int i; + LPSREAL rhi; + + for(i = 1; i <= lp->rows; i++) { + rhi = rh[i]; +#ifdef DoBorderRounding + rhi = my_avoidtiny(rhi, lp->matA->epsvalue); +#endif + lp->orig_rhs[i] = my_chsign(is_chsign(lp, i), scaled_value(lp, rhi, i)); + } + set_action(&lp->spx_action, ACTION_RECOMPUTE); +} + +MYBOOL __WINAPI str_set_rh_vec(lprec *lp, char *rh_string) +{ + int i; + MYBOOL ret = TRUE; + LPSREAL *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); + if(p == newp) { + report(lp, IMPORTANT, "str_set_rh_vec: Bad string %s\n", p); + lp->spx_status = DATAIGNORED; + ret = FALSE; + break; + } + else + p = newp; + } + if(!(lp->spx_status == DATAIGNORED)) + set_rh_vec(lp, newrh); + FREE(newrh); + return( ret ); +} + +void __WINAPI set_sense(lprec *lp, MYBOOL maximize) +{ + maximize = (MYBOOL) (maximize != FALSE); + if(is_maxim(lp) != maximize) { + int i; + if(is_infinite(lp, lp->bb_heuristicOF)) + lp->bb_heuristicOF = my_chsign(maximize, lp->infinite); + if(is_infinite(lp, lp->bb_breakOF)) + lp->bb_breakOF = my_chsign(maximize, -lp->infinite); + lp->orig_rhs[0] = my_flipsign(lp->orig_rhs[0]); + for(i = 1; i <= lp->columns; i++) + lp->orig_obj[i] = my_flipsign(lp->orig_obj[i]); + set_action(&lp->spx_action, ACTION_REINVERT | ACTION_RECOMPUTE); + } + if(maximize) + lp->row_type[0] = ROWTYPE_OFMAX; + else + lp->row_type[0] = ROWTYPE_OFMIN; +} + +void __WINAPI set_maxim(lprec *lp) +{ + set_sense(lp, TRUE); +} + +void __WINAPI set_minim(lprec *lp) +{ + set_sense(lp, FALSE); +} + +MYBOOL __WINAPI is_maxim(lprec *lp) +{ + return( (MYBOOL) ((lp->row_type != NULL) && + ((lp->row_type[0] & ROWTYPE_CHSIGN) == ROWTYPE_GE)) ); +} + +MYBOOL __WINAPI set_constr_type(lprec *lp, int rownr, int con_type) +{ + MYBOOL oldchsign; + + if(rownr > lp->rows+1 || rownr < 1) { + report(lp, IMPORTANT, "set_constr_type: Row %d out of range\n", rownr); + return( FALSE ); + } + + /* Prepare for a new row */ + if((rownr > lp->rows) && !append_rows(lp, rownr-lp->rows)) + return( FALSE ); + + /* Update the constraint type data */ + if(is_constr_type(lp, rownr, EQ)) + lp->equalities--; + + if((con_type & ROWTYPE_CONSTRAINT) == EQ) { + lp->equalities++; + lp->orig_upbo[rownr] = 0; + } + else if(((con_type & LE) > 0) || ((con_type & GE) > 0) || (con_type == FR)) + lp->orig_upbo[rownr] = lp->infinite; + else { + report(lp, IMPORTANT, "set_constr_type: Constraint type %d not implemented (row %d)\n", + con_type, rownr); + return( FALSE ); + } + + /* Change the signs of the row, if necessary */ + oldchsign = is_chsign(lp, rownr); + if(con_type == FR) + lp->row_type[rownr] = LE; + 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); + if(lp->orig_rhs[rownr] != 0) + lp->orig_rhs[rownr] *= -1; + set_action(&lp->spx_action, ACTION_RECOMPUTE); + } + if(con_type == FR) + lp->orig_rhs[rownr] = lp->infinite; + + set_action(&lp->spx_action, ACTION_REINVERT); + lp->basis_valid = FALSE; + + return( TRUE ); +} + +/* INLINE */ MYBOOL is_chsign(lprec *lp, int rownr) +{ + return( (MYBOOL) ((lp->row_type[rownr] & ROWTYPE_CONSTRAINT) == ROWTYPE_CHSIGN) ); +} + +MYBOOL __WINAPI is_constr_type(lprec *lp, int rownr, int mask) +{ + if((rownr < 0) || (rownr > lp->rows)) { + report(lp, IMPORTANT, "is_constr_type: Row %d out of range\n", rownr); + return( FALSE ); + } + return( (MYBOOL) ((lp->row_type[rownr] & ROWTYPE_CONSTRAINT) == mask)); +} + +int __WINAPI get_constr_type(lprec *lp, int rownr) +{ + if((rownr < 0) || (rownr > lp->rows)) { + report(lp, IMPORTANT, "get_constr_type: Row %d out of range\n", rownr); + return(-1); + } + return( lp->row_type[rownr] ); +} +LPSREAL __WINAPI get_constr_value(lprec *lp, int rownr, int count, LPSREAL *primsolution, int *nzindex) +{ + int i; + LPSREAL value = 0.0; + MATrec *mat = lp->matA; + + if((rownr < 0) || (rownr > get_Nrows(lp))) + return( value ); + + /* First do validation and initialization of applicable primal solution */ + if(!mat_validate(mat) || ((primsolution == NULL) && (lp->solvecount == 0))) + return( value ); + i = get_Ncolumns(lp); + if((primsolution != NULL) && (nzindex == NULL) && + ((count <= 0) || (count > i))) + count = i; + if(primsolution == NULL) { + get_ptr_variables(lp, &primsolution); + primsolution--; + nzindex = NULL; + count = i; + } + + /* Do objective or constraint, as specified */ + if(rownr == 0) { + value += get_rh(lp, 0); + if(nzindex != NULL) + for(i = 0; i < count; i++) + value += get_mat(lp, 0, nzindex[i]) * primsolution[i]; + else + for(i = 1; i <= count; i++) + value += get_mat(lp, 0, i) * primsolution[i]; + } + else { + if(nzindex != NULL) { + for(i = 0; i < count; i++) + value += get_mat(lp, rownr, nzindex[i]) * primsolution[i]; + } + else { + int j; + + for(i = mat->row_end[rownr-1]; i < mat->row_end[rownr]; i++) { + j = ROW_MAT_COLNR(i); + value += unscaled_mat(lp, ROW_MAT_VALUE(i), rownr, j) * primsolution[j]; + } + value = my_chsign(is_chsign(lp, rownr), value); + } + } + return( value ); +} + +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_GeneralMIP: return("General MIP"); + case ROWCLASS_GeneralINT: return("General INT"); + case ROWCLASS_GeneralBIN: return("General BIN"); + case ROWCLASS_KnapsackINT: return("Knapsack INT"); + case ROWCLASS_KnapsackBIN: return("Knapsack BIN"); + case ROWCLASS_SetPacking: return("Set packing"); + case ROWCLASS_SetCover: return("Set cover"); + case ROWCLASS_GUB: return("GUB"); + default: return("Error"); + } +} + +STATIC char *get_str_constr_type(lprec *lp, int con_type) +{ + switch(con_type) { + case FR: return("FR"); + case LE: return("LE"); + case GE: return("GE"); + case EQ: return("EQ"); + default: return("Error"); + } +} + +STATIC int get_constr_class(lprec *lp, int rownr) +{ + int aBIN = 0, aINT = 0, aREAL = 0, + xBIN = 0, xINT = 0, xREAL = 0; + int j, elmnr, elmend, nelm; + MYBOOL chsign; + LPSREAL a; + MATrec *mat = lp->matA; + + if((rownr < 1) || (rownr > lp->rows)) { + report(lp, IMPORTANT, "get_constr_class: Row %d out of range\n", rownr); + return( ROWCLASS_Unknown ); + } + mat_validate(mat); + + /* Tally counts of constraint variable types and coefficients */ + if(rownr == 0) { + elmnr = 1; + elmend = lp->columns; + nelm = 0; + } + else { + elmnr = mat->row_end[rownr - 1]; + elmend = mat->row_end[rownr]; + nelm = elmend - elmnr; + } + chsign = is_chsign(lp, rownr); + for(; elmnr < elmend; elmnr++) { + if(rownr == 0) { + a = lp->orig_obj[elmnr]; + if(a == 0) + continue; + j = elmnr; + } + else { + j = ROW_MAT_COLNR(elmnr); + a = ROW_MAT_VALUE(elmnr); + } + a = unscaled_mat(lp, my_chsign(chsign, a), rownr, j); + if(is_binary(lp, j)) + xBIN++; + else if((get_lowbo(lp, j) >= 0) && is_int(lp, j)) + xINT++; + else + xREAL++; /* Includes integer variables with negative lower bound */ + + if(fabs(a-1.0) < lp->epsvalue) + aBIN++; + else if((a > 0) && (fabs(floor(a+lp->epsvalue)-a) < lp->epsvalue)) + aINT++; + else + aREAL++; /* Includes negative integer-valued coefficients */ + } + + /* Get the constraint type and the RHS */ + if(rownr == 0) + return( ROWCLASS_Objective ); + j = get_constr_type(lp, rownr); + a = get_rh(lp, rownr); + + /* Determine the constraint class */ + if((aBIN == nelm) && (xBIN == nelm) && (a >= 1)) { + if(a > 1) + j = ROWCLASS_KnapsackBIN; + else if(j == EQ) + j = ROWCLASS_GUB; + else if(j == LE) + j = ROWCLASS_SetCover; + else + j = ROWCLASS_SetPacking; + } + else if((aINT == nelm) && (xINT == nelm) && (a >= 1)) + j = ROWCLASS_KnapsackINT; + else if(xBIN == nelm) + j = ROWCLASS_GeneralBIN; + else if(xINT == nelm) + j = ROWCLASS_GeneralINT; + else if((xREAL > 0) && (xINT+xBIN > 0)) + j = ROWCLASS_GeneralMIP; + else + j = ROWCLASS_GeneralREAL; + + return( j ); +} + +LPSREAL __WINAPI get_mat(lprec *lp, int rownr, int colnr) +{ + LPSREAL 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); + return(0); + } + if((colnr < 1) || (colnr > lp->columns)) { + report(lp, IMPORTANT, "get_mat: Column %d out of range", colnr); + 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); + if(elmnr >= 0) { + MATrec *mat = lp->matA; + value = my_chsign(is_chsign(lp, rownr), COL_MAT_VALUE(elmnr)); + value = unscaled_mat(lp, value, rownr, colnr); + } + else + value = 0; + } + return(value); +} + +LPSREAL __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; + + mat_get_data(lp, matindex, isrow, &rownr, &colnr, &value); + if(adjustsign) + result = (*value) * (is_chsign(lp, *rownr) ? -1 : 1); + else + result = *value; + if(lp->scaling_used) + return( unscaled_mat(lp, result, *rownr, *colnr) ); + else + return( result ); +} + +static int mat_getrow(lprec *lp, int rownr, LPSREAL *row, int *colno) +{ + MYBOOL isnz; + int j, countnz = 0; + LPSREAL a; + + if((rownr == 0) || !mat_validate(lp->matA)) { + for(j = 1; j <= lp->columns; j++) { + a = get_mat(lp,rownr,j); + isnz = (a != 0); + if(colno == NULL) + row[j] = a; + else if(isnz) { + row[countnz] = a; + colno[countnz] = j; + } + if(isnz) + countnz++; + } + } + else { + MYBOOL chsign = FALSE; + 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); + 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; + else { + row[countnz] = a; + colno[countnz] = j; + } + countnz++; + } + } + return( countnz ); +} + +static int mat_getcolumn(lprec *lp, int colnr, LPSREAL *column, int *nzrow) +{ + int n = 0, i, ii, ie, *rownr; + LPSREAL hold, *value; + MATrec *mat = lp->matA; + + 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; + n++; + } + } + + i = lp->matA->col_end[colnr - 1]; + ie = lp->matA->col_end[colnr]; + if(nzrow == NULL) + n += ie - i; + rownr = &COL_MAT_ROWNR(i); + value = &COL_MAT_VALUE(i); + for(; i < ie; + i++, rownr += matRowColStep, value += matValueStep) { + ii = *rownr; + + hold = my_chsign(is_chsign(lp, (mat->is_roworder) ? colnr : ii), *value); + hold = unscaled_mat(lp, hold, ii, colnr); + if(nzrow == NULL) + column[ii] = hold; + else if(hold != 0) { + column[n] = hold; + nzrow[n] = ii; + n++; + } + } + 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) +{ + 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) +/* 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().*/ +{ + lp->obj = ofVector; +} + +MYBOOL modifyOF1(lprec *lp, int index, LPSREAL *ofValue, LPSREAL mult) +/* Adjust objective function values for primal/dual phase 1, if appropriate */ +{ + MYBOOL accept = TRUE; + + /* Primal simplex: Set user variables to zero or BigM-scaled */ + if(((lp->simplex_mode & SIMPLEX_Phase1_PRIMAL) != 0) && (abs(lp->P1extraDim) > 0)) { +#ifndef Phase1EliminateRedundant + if(lp->P1extraDim < 0) { + if(index > lp->sum + lp->P1extraDim) + accept = FALSE; + } + else +#endif + if((index <= lp->sum - lp->P1extraDim) || (mult == 0)) { + if((mult == 0) || (lp->bigM == 0)) + accept = FALSE; + else + (*ofValue) /= lp->bigM; + } + } + + /* Dual simplex: Subtract P1extraVal from objective function values */ + else if(((lp->simplex_mode & SIMPLEX_Phase1_DUAL) != 0) && (index > lp->rows)) { +#if 1 /* This may help increase sparsity of the (extended) basis matrix; + Can it introduce degeneracy in some cases? */ + if((lp->P1extraVal != 0) && (lp->orig_obj[index - lp->rows] > 0)) + *ofValue = 0; + else +#endif + { + *ofValue -= lp->P1extraVal; +#if 0 + if(is_action(lp->anti_degen, ANTIDEGEN_RHSPERTURB)) + *ofValue -= rand_uniform(lp, lp->epsperturb); +#endif + } + } + + /* Do scaling and test for zero */ + if(accept) { + (*ofValue) *= mult; + if(fabs(*ofValue) < lp->epsmachine) { + (*ofValue) = 0; + accept = FALSE; + } + } + else + (*ofValue) = 0; + + return( accept ); +} + +STATIC void set_OF_p1extra(lprec *lp, LPSREAL p1extra) +{ + int i; + LPSREAL *value; + + if(lp->spx_trace) + report(lp, DETAILED, "set_OF_p1extra: Set dual objective offset to %g at iter %.0f.\n", + p1extra, (double) get_total_iter(lp)); + lp->P1extraVal = p1extra; + if(lp->obj == NULL) + allocREAL(lp, &lp->obj, lp->columns_alloc+1, TRUE); + for(i = 1, value = lp->obj+1; i <= lp->columns; i++, value++) { + *value = lp->orig_obj[i]; + modifyOF1(lp, lp->rows + i, value, 1.0); + } +} + +STATIC void unset_OF_p1extra(lprec *lp) +{ + lp->P1extraVal = 0; + FREE(lp->obj); +} + +LPSREAL __WINAPI get_OF_active(lprec *lp, int varnr, LPSREAL mult) +{ + int colnr = varnr - lp->rows; + LPSREAL holdOF = 0; + +#ifdef Paranoia + if((colnr <= 0) || (colnr > lp->columns)) { + report(lp, SEVERE, "get_OF_active: Invalid column index %d supplied\n", colnr); + } + else +#endif + if(lp->obj == NULL) { + if(colnr > 0) + holdOF = lp->orig_obj[colnr]; + modifyOF1(lp, varnr, &holdOF, mult); + } + else if(colnr > 0) + holdOF = lp->obj[colnr] * mult; + + return( holdOF ); +} + +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) +{ + int nz = 1; + + if(nzlist == NULL) { + MEMCLEAR(column, lp->rows + 1); + column[row_nr] = value; + } + else { + column[nz] = value; + nzlist[nz] = row_nr; + } + + if(maxabs != NULL) + *maxabs = row_nr; + return( nz ); +} + +STATIC int expand_column(lprec *lp, int col_nr, LPSREAL *column, int *nzlist, LPSREAL mult, int *maxabs) +{ + int i, ie, j, maxidx, nzcount; + LPSREAL value, maxval; + MATrec *mat = lp->matA; + LPSREAL *matValue; + int *matRownr; + + /* Retrieve a column from the user data matrix A */ + maxval = 0; + maxidx = -1; + if(nzlist == NULL) { + MEMCLEAR(column, lp->rows + 1); + i = mat->col_end[col_nr - 1]; + ie = mat->col_end[col_nr]; + matRownr = &COL_MAT_ROWNR(i); + matValue = &COL_MAT_VALUE(i); + nzcount = i; + for(; i < ie; + i++, matRownr += matRowColStep, matValue += matValueStep) { + j = *matRownr; + value = *matValue; + if(j > 0) { + value *= mult; + if(fabs(value) > maxval) { + maxval = fabs(value); + maxidx = j; + } + } + column[j] = value; + } + nzcount = i - nzcount; + + /* Get the objective as row 0, optionally adjusting the objective for phase 1 */ + if(lp->obj_in_basis) { + column[0] = get_OF_active(lp, lp->rows+col_nr, mult); + if(column[0] != 0) + nzcount++; + } + } + else { + nzcount = 0; + + /* Get the objective as row 0, optionally adjusting the objective for phase 1 */ + if(lp->obj_in_basis) { + value = get_OF_active(lp, lp->rows+col_nr, mult); + if(value != 0) { + nzcount++; + nzlist[nzcount] = 0; + column[nzcount] = value; + } + } + + /* Loop over the non-zero column entries */ + i = mat->col_end[col_nr - 1]; + ie = mat->col_end[col_nr]; + matRownr = &COL_MAT_ROWNR(i); + matValue = &COL_MAT_VALUE(i); + for(; i < ie; + i++, matRownr += matRowColStep, matValue += matValueStep) { + j = *matRownr; + value = (*matValue) * mult; + nzcount++; + nzlist[nzcount] = j; + column[nzcount] = value; + if(fabs(value) > maxval) { + maxval = fabs(value); + maxidx = nzcount; + } + } + } + + if(maxabs != NULL) + *maxabs = maxidx; + return( nzcount ); +} + + +/* 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) +{ + LPSREAL value = my_chsign(lp->is_lower[varin], -1); + if(varin > lp->rows) { + varin -= lp->rows; + varin = expand_column(lp, varin, pcol, nzlist, value, maxabs); + } + else if(lp->obj_in_basis || (varin > 0)) + varin = singleton_column(lp, varin, pcol, nzlist, value, maxabs); + else + varin = get_basisOF(lp, NULL, pcol, nzlist); + + return(varin); +} + +/* GENERAL INVARIANT CALLBACK FUNCTIONS */ +MYBOOL set_callbacks(lprec *lp) +{ + /* Assign API functions to lp structure (mainly for XLIs) */ + lp->add_column = add_column; + lp->add_columnex = add_columnex; + lp->add_constraint = add_constraint; + lp->add_constraintex = add_constraintex; + lp->add_lag_con = add_lag_con; + lp->add_SOS = add_SOS; + lp->column_in_lp = column_in_lp; + lp->copy_lp = copy_lp; + lp->default_basis = default_basis; + lp->del_column = del_column; + lp->del_constraint = del_constraint; + lp->delete_lp = delete_lp; + lp->dualize_lp = dualize_lp; + lp->free_lp = free_lp; + lp->get_anti_degen = get_anti_degen; + lp->get_basis = get_basis; + lp->get_basiscrash = get_basiscrash; + lp->get_bb_depthlimit = get_bb_depthlimit; + lp->get_bb_floorfirst = get_bb_floorfirst; + lp->get_bb_rule = get_bb_rule; + lp->get_bounds_tighter = get_bounds_tighter; + lp->get_break_at_value = get_break_at_value; + lp->get_col_name = get_col_name; + lp->get_columnex = get_columnex; + lp->get_constr_type = get_constr_type; + lp->get_constr_value = get_constr_value; + lp->get_constraints = get_constraints; + lp->get_dual_solution = get_dual_solution; + lp->get_epsb = get_epsb; + lp->get_epsd = get_epsd; + lp->get_epsel = get_epsel; + lp->get_epsint = get_epsint; + lp->get_epsperturb = get_epsperturb; + lp->get_epspivot = get_epspivot; + lp->get_improve = get_improve; + lp->get_infinite = get_infinite; + lp->get_lambda = get_lambda; + lp->get_lowbo = get_lowbo; + lp->get_lp_index = get_lp_index; + lp->get_lp_name = get_lp_name; + lp->get_Lrows = get_Lrows; + lp->get_mat = get_mat; + lp->get_mat_byindex = get_mat_byindex; + lp->get_max_level = get_max_level; + lp->get_maxpivot = get_maxpivot; + lp->get_mip_gap = get_mip_gap; + lp->get_multiprice = get_multiprice; + lp->get_nameindex = get_nameindex; + lp->get_Ncolumns = get_Ncolumns; + lp->get_negrange = get_negrange; + lp->get_nonzeros = get_nonzeros; + lp->get_Norig_columns = get_Norig_columns; + lp->get_Norig_rows = get_Norig_rows; + lp->get_Nrows = get_Nrows; + lp->get_obj_bound = get_obj_bound; + lp->get_objective = get_objective; + lp->get_orig_index = get_orig_index; + lp->get_origcol_name = get_origcol_name; + lp->get_origrow_name = get_origrow_name; + lp->get_partialprice = get_partialprice; + lp->get_pivoting = get_pivoting; + lp->get_presolve = get_presolve; + lp->get_presolveloops = get_presolveloops; + lp->get_primal_solution = get_primal_solution; + lp->get_print_sol = get_print_sol; + lp->get_pseudocosts = get_pseudocosts; + lp->get_ptr_constraints = get_ptr_constraints; + lp->get_ptr_dual_solution = get_ptr_dual_solution; + lp->get_ptr_lambda = get_ptr_lambda; + lp->get_ptr_primal_solution = get_ptr_primal_solution; + lp->get_ptr_sensitivity_obj = get_ptr_sensitivity_obj; + lp->get_ptr_sensitivity_objex = get_ptr_sensitivity_objex; + lp->get_ptr_sensitivity_rhs = get_ptr_sensitivity_rhs; + lp->get_ptr_variables = get_ptr_variables; + lp->get_rh = get_rh; + lp->get_rh_range = get_rh_range; + lp->get_row = get_row; + lp->get_rowex = get_rowex; + lp->get_row_name = get_row_name; + lp->get_scalelimit = get_scalelimit; + lp->get_scaling = get_scaling; + lp->get_sensitivity_obj = get_sensitivity_obj; + lp->get_sensitivity_objex = get_sensitivity_objex; + lp->get_sensitivity_rhs = get_sensitivity_rhs; + lp->get_simplextype = get_simplextype; + lp->get_solutioncount = get_solutioncount; + lp->get_solutionlimit = get_solutionlimit; + lp->get_status = get_status; + lp->get_statustext = get_statustext; + lp->get_timeout = get_timeout; + lp->get_total_iter = get_total_iter; + lp->get_total_nodes = get_total_nodes; + lp->get_upbo = get_upbo; + lp->get_var_branch = get_var_branch; + lp->get_var_dualresult = get_var_dualresult; + lp->get_var_primalresult = get_var_primalresult; + lp->get_var_priority = get_var_priority; + lp->get_variables = get_variables; + lp->get_verbose = get_verbose; + lp->get_working_objective = get_working_objective; + lp->has_BFP = has_BFP; + lp->has_XLI = has_XLI; + lp->is_add_rowmode = is_add_rowmode; + lp->is_anti_degen = is_anti_degen; + lp->is_binary = is_binary; + lp->is_break_at_first = is_break_at_first; + lp->is_constr_type = is_constr_type; + lp->is_debug = is_debug; + lp->is_feasible = is_feasible; + lp->is_unbounded = is_unbounded; + lp->is_infinite = is_infinite; + lp->is_int = is_int; + lp->is_integerscaling = is_integerscaling; + lp->is_lag_trace = is_lag_trace; + lp->is_maxim = is_maxim; + lp->is_nativeBFP = is_nativeBFP; + lp->is_nativeXLI = is_nativeXLI; + lp->is_negative = is_negative; + lp->is_obj_in_basis = is_obj_in_basis; + lp->is_piv_mode = is_piv_mode; + lp->is_piv_rule = is_piv_rule; + lp->is_presolve = is_presolve; + lp->is_scalemode = is_scalemode; + lp->is_scaletype = is_scaletype; + lp->is_semicont = is_semicont; + lp->is_SOS_var = is_SOS_var; + lp->is_trace = is_trace; + lp->lp_solve_version = lp_solve_version; + lp->make_lp = make_lp; + lp->print_constraints = print_constraints; + lp->print_debugdump = print_debugdump; + lp->print_duals = print_duals; + lp->print_lp = print_lp; + lp->print_objective = print_objective; + lp->print_scales = print_scales; + lp->print_solution = print_solution; + lp->print_str = print_str; + lp->print_tableau = print_tableau; + lp->put_abortfunc = put_abortfunc; + lp->put_bb_nodefunc = put_bb_nodefunc; + 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_XLI = read_XLI; + lp->read_basis = read_basis; + lp->reset_basis = reset_basis; + lp->read_params = read_params; + lp->reset_params = reset_params; + lp->resize_lp = resize_lp; + lp->set_action = set_action; + lp->set_add_rowmode = set_add_rowmode; + lp->set_anti_degen = set_anti_degen; + lp->set_basisvar = set_basisvar; + lp->set_basis = set_basis; + lp->set_basiscrash = set_basiscrash; + lp->set_bb_depthlimit = set_bb_depthlimit; + lp->set_bb_floorfirst = set_bb_floorfirst; + lp->set_bb_rule = set_bb_rule; + lp->set_BFP = set_BFP; + lp->set_binary = set_binary; + lp->set_bounds = set_bounds; + lp->set_bounds_tighter = set_bounds_tighter; + lp->set_break_at_first = set_break_at_first; + lp->set_break_at_value = set_break_at_value; + lp->set_col_name = set_col_name; + lp->set_constr_type = set_constr_type; + lp->set_debug = set_debug; + lp->set_epsb = set_epsb; + lp->set_epsd = set_epsd; + lp->set_epsel = set_epsel; + lp->set_epsint = set_epsint; + lp->set_epslevel = set_epslevel; + lp->set_epsperturb = set_epsperturb; + lp->set_epspivot = set_epspivot; + lp->set_unbounded = set_unbounded; + lp->set_improve = set_improve; + lp->set_infinite = set_infinite; + lp->set_int = set_int; + lp->set_lag_trace = set_lag_trace; + lp->set_lowbo = set_lowbo; + lp->set_lp_name = set_lp_name; + lp->set_mat = set_mat; + lp->set_maxim = set_maxim; + lp->set_maxpivot = set_maxpivot; + lp->set_minim = set_minim; + lp->set_mip_gap = set_mip_gap; + lp->set_multiprice = set_multiprice; + lp->set_negrange = set_negrange; + lp->set_obj = set_obj; + lp->set_obj_bound = set_obj_bound; + lp->set_obj_fn = set_obj_fn; + lp->set_obj_fnex = set_obj_fnex; + lp->set_obj_in_basis = set_obj_in_basis; + lp->set_outputfile = set_outputfile; + lp->set_outputstream = set_outputstream; + lp->set_partialprice = set_partialprice; + lp->set_pivoting = set_pivoting; + lp->set_preferdual = set_preferdual; + lp->set_presolve = set_presolve; + lp->set_print_sol = set_print_sol; + lp->set_pseudocosts = set_pseudocosts; + lp->set_rh = set_rh; + lp->set_rh_range = set_rh_range; + lp->set_rh_vec = set_rh_vec; + lp->set_row = set_row; + lp->set_rowex = set_rowex; + lp->set_row_name = set_row_name; + lp->set_scalelimit = set_scalelimit; + lp->set_scaling = set_scaling; + lp->set_semicont = set_semicont; + lp->set_sense = set_sense; + lp->set_simplextype = set_simplextype; + lp->set_solutionlimit = set_solutionlimit; + lp->set_timeout = set_timeout; + lp->set_trace = set_trace; + lp->set_upbo = set_upbo; + lp->set_var_branch = set_var_branch; + lp->set_var_weights = set_var_weights; + lp->set_verbose = set_verbose; + lp->set_XLI = set_XLI; + lp->solve = solve; + lp->str_add_column = str_add_column; + lp->str_add_constraint = str_add_constraint; + lp->str_add_lag_con = str_add_lag_con; + lp->str_set_obj_fn = str_set_obj_fn; + lp->str_set_rh_vec = str_set_rh_vec; + lp->time_elapsed = time_elapsed; + lp->unscale = unscale; + lp->write_lp = write_lp; + lp->write_LP = write_LP; + lp->write_mps = write_mps; + lp->write_freemps = write_freemps; + lp->write_MPS = write_MPS; + lp->write_freeMPS = write_freeMPS; + lp->write_XLI = write_XLI; + lp->write_basis = write_basis; + lp->write_params = write_params; + + /* Utility functions (mainly for BFPs) */ + lp->userabort = userabort; + lp->report = report; + lp->explain = explain; + lp->set_basisvar = set_basisvar; + lp->get_lpcolumn = obtain_column; + lp->get_basiscolumn = get_basiscolumn; + lp->get_OF_active = get_OF_active; + lp->getMDO = getMDO; + lp->invert = invert; + lp->set_action = set_action; + lp->clear_action = clear_action; + lp->is_action = is_action; + + return( TRUE ); +} + +/* SUPPORT FUNCTION FOR BASIS FACTORIZATION PACKAGES */ +MYBOOL __WINAPI has_BFP(lprec *lp) +{ + return( is_nativeBFP(lp) +#if LoadInverseLib == TRUE + || (MYBOOL) (lp->hBFP != NULL) +#endif + ); +} + +MYBOOL __WINAPI is_nativeBFP(lprec *lp) +{ +#ifdef ExcludeNativeInverse + return( FALSE ); +#elif LoadInverseLib == TRUE + return( (MYBOOL) (lp->hBFP == NULL) ); +#else + return( TRUE ); +#endif +} + +MYBOOL __WINAPI set_BFP(lprec *lp, char *filename) +/* (Re)mapping of basis factorization variant methods is done here */ +{ + int result = LIB_LOADED; + + /* Release the BFP and basis if we are active */ + if(lp->invB != NULL) + bfp_free(lp); + +#if LoadInverseLib == TRUE + if(lp->hBFP != NULL) { + #ifdef WIN32 + FreeLibrary(lp->hBFP); + #else + dlclose(lp->hBFP); + #endif + lp->hBFP = NULL; + } +#endif + + if(filename == NULL) { + if(!is_nativeBFP(lp)) + return( FALSE ); +#ifndef ExcludeNativeInverse + lp->bfp_name = bfp_name; + lp->bfp_compatible = bfp_compatible; + lp->bfp_free = bfp_free; + lp->bfp_resize = bfp_resize; + lp->bfp_nonzeros = bfp_nonzeros; + lp->bfp_memallocated = bfp_memallocated; + lp->bfp_restart = bfp_restart; + lp->bfp_mustrefactorize = bfp_mustrefactorize; + lp->bfp_preparefactorization = bfp_preparefactorization; + lp->bfp_factorize = bfp_factorize; + lp->bfp_finishupdate = bfp_finishupdate; + lp->bfp_ftran_normal = bfp_ftran_normal; + lp->bfp_ftran_prepare = bfp_ftran_prepare; + lp->bfp_btran_normal = bfp_btran_normal; + lp->bfp_status = bfp_status; + lp->bfp_implicitslack = bfp_implicitslack; + lp->bfp_indexbase = bfp_indexbase; + lp->bfp_rowoffset = bfp_rowoffset; + lp->bfp_pivotmax = bfp_pivotmax; + lp->bfp_init = bfp_init; + lp->bfp_pivotalloc = bfp_pivotalloc; + lp->bfp_colcount = bfp_colcount; + lp->bfp_canresetbasis = bfp_canresetbasis; + lp->bfp_finishfactorization = bfp_finishfactorization; + lp->bfp_updaterefactstats = bfp_updaterefactstats; + lp->bfp_prepareupdate = bfp_prepareupdate; + lp->bfp_pivotRHS = bfp_pivotRHS; + lp->bfp_btran_double = bfp_btran_double; + lp->bfp_efficiency = bfp_efficiency; + lp->bfp_pivotvector = bfp_pivotvector; + lp->bfp_pivotcount = bfp_pivotcount; + lp->bfp_refactcount = bfp_refactcount; + lp->bfp_isSetI = bfp_isSetI; + lp->bfp_findredundant = bfp_findredundant; +#endif + } + else { +#if LoadInverseLib == TRUE + #ifdef WIN32 + /* Get a handle to the Windows DLL module. */ + lp->hBFP = LoadLibrary(filename); + + /* If the handle is valid, try to get the function addresses. */ + if(lp->hBFP != NULL) { + lp->bfp_compatible = (BFPbool_lpintintint *) + GetProcAddress(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 *) + GetProcAddress(lp->hBFP, "bfp_name"); + lp->bfp_free = (BFP_lp *) + GetProcAddress(lp->hBFP, "bfp_free"); + lp->bfp_resize = (BFPbool_lpint *) + GetProcAddress(lp->hBFP, "bfp_resize"); + lp->bfp_nonzeros = (BFPint_lpbool *) + GetProcAddress(lp->hBFP, "bfp_nonzeros"); + lp->bfp_memallocated = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_memallocated"); + lp->bfp_restart = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_restart"); + lp->bfp_mustrefactorize = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_mustrefactorize"); + lp->bfp_preparefactorization = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_preparefactorization"); + lp->bfp_factorize = (BFPint_lpintintboolbool *) + GetProcAddress(lp->hBFP, "bfp_factorize"); + lp->bfp_finishupdate = (BFPbool_lpbool *) + GetProcAddress(lp->hBFP, "bfp_finishupdate"); + lp->bfp_ftran_normal = (BFP_lprealint *) + GetProcAddress(lp->hBFP, "bfp_ftran_normal"); + lp->bfp_ftran_prepare = (BFP_lprealint *) + GetProcAddress(lp->hBFP, "bfp_ftran_prepare"); + lp->bfp_btran_normal = (BFP_lprealint *) + GetProcAddress(lp->hBFP, "bfp_btran_normal"); + lp->bfp_status = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_status"); + lp->bfp_implicitslack = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_implicitslack"); + lp->bfp_indexbase = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_indexbase"); + lp->bfp_rowoffset = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_rowoffset"); + lp->bfp_pivotmax = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_pivotmax"); + lp->bfp_init = (BFPbool_lpintintchar *) + GetProcAddress(lp->hBFP, "bfp_init"); + lp->bfp_pivotalloc = (BFPbool_lpint *) + GetProcAddress(lp->hBFP, "bfp_pivotalloc"); + lp->bfp_colcount = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_colcount"); + lp->bfp_canresetbasis = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_canresetbasis"); + lp->bfp_finishfactorization = (BFP_lp *) + GetProcAddress(lp->hBFP, "bfp_finishfactorization"); + lp->bfp_updaterefactstats = (BFP_lp *) + GetProcAddress(lp->hBFP, "bfp_updaterefactstats"); + lp->bfp_prepareupdate = (BFPlreal_lpintintreal *) + GetProcAddress(lp->hBFP, "bfp_prepareupdate"); + lp->bfp_pivotRHS = (BFPreal_lplrealreal *) + GetProcAddress(lp->hBFP, "bfp_pivotRHS"); + lp->bfp_btran_double = (BFP_lprealintrealint *) + GetProcAddress(lp->hBFP, "bfp_btran_double"); + lp->bfp_efficiency = (BFPreal_lp *) + GetProcAddress(lp->hBFP, "bfp_efficiency"); + lp->bfp_pivotvector = (BFPrealp_lp *) + GetProcAddress(lp->hBFP, "bfp_pivotvector"); + lp->bfp_pivotcount = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_pivotcount"); + lp->bfp_refactcount = (BFPint_lpint *) + GetProcAddress(lp->hBFP, "bfp_refactcount"); + lp->bfp_isSetI = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_isSetI"); + lp->bfp_findredundant = (BFPint_lpintrealcbintint *) + GetProcAddress(lp->hBFP, "bfp_findredundant"); + } + else + result = LIB_VERINVALID; + } + #else + /* First standardize UNIX .SO library name format. */ + char bfpname[260], *ptr; + + strcpy(bfpname, filename); + if((ptr = strrchr(filename, '/')) == NULL) + ptr = filename; + else + ptr++; + bfpname[(int) (ptr - filename)] = 0; + if(strncmp(ptr, "lib", 3)) + strcat(bfpname, "lib"); + strcat(bfpname, ptr); + if(strcmp(bfpname + strlen(bfpname) - 3, ".so")) + strcat(bfpname, ".so"); + + /* Get a handle to the module. */ + lp->hBFP = dlopen(bfpname, RTLD_LAZY); + + /* If the handle is valid, try to get the function addresses. */ + if(lp->hBFP != 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"); + } + else + result = LIB_VERINVALID; + } + #endif + else + result = LIB_NOTFOUND; +#endif + /* Do validation */ + if((result != LIB_LOADED) || + ((lp->bfp_name == NULL) || + (lp->bfp_compatible == NULL) || + (lp->bfp_free == NULL) || + (lp->bfp_resize == NULL) || + (lp->bfp_nonzeros == NULL) || + (lp->bfp_memallocated == NULL) || + (lp->bfp_restart == NULL) || + (lp->bfp_mustrefactorize == NULL) || + (lp->bfp_preparefactorization == NULL) || + (lp->bfp_factorize == NULL) || + (lp->bfp_finishupdate == NULL) || + (lp->bfp_ftran_normal == NULL) || + (lp->bfp_ftran_prepare == NULL) || + (lp->bfp_btran_normal == NULL) || + (lp->bfp_status == NULL) || + (lp->bfp_implicitslack == NULL) || + (lp->bfp_indexbase == NULL) || + (lp->bfp_rowoffset == NULL) || + (lp->bfp_pivotmax == NULL) || + (lp->bfp_init == NULL) || + (lp->bfp_pivotalloc == NULL) || + (lp->bfp_colcount == NULL) || + (lp->bfp_canresetbasis == NULL) || + (lp->bfp_finishfactorization == NULL) || + (lp->bfp_updaterefactstats == NULL) || + (lp->bfp_prepareupdate == NULL) || + (lp->bfp_pivotRHS == NULL) || + (lp->bfp_btran_double == NULL) || + (lp->bfp_efficiency == NULL) || + (lp->bfp_pivotvector == NULL) || + (lp->bfp_pivotcount == NULL) || + (lp->bfp_refactcount == NULL) || + (lp->bfp_isSetI == NULL) || + (lp->bfp_findredundant == NULL) + )) { + set_BFP(lp, NULL); + if(result == LIB_LOADED) + result = LIB_NOFUNCTION; + } + } + if(filename != NULL) { + char info[LIB_STR_MAXLEN+1]; + switch(result) { + case LIB_NOTFOUND: strcpy(info, LIB_STR_NOTFOUND); + break; + case LIB_NOINFO: strcpy(info, LIB_STR_NOINFO); + break; + case LIB_NOFUNCTION: strcpy(info, LIB_STR_NOFUNCTION); + break; + case LIB_VERINVALID: strcpy(info, LIB_STR_VERINVALID); + break; + default: strcpy(info, LIB_STR_LOADED); + } + report(lp, IMPORTANT, "set_BFP: %s '%s'\n", + info, filename); + } + return( (MYBOOL) (result == LIB_LOADED)); +} + + +/* External language interface routines */ +/* DON'T MODIFY */ +lprec * __WINAPI read_XLI(char *xliname, char *modelname, char *dataname, char *options, int verbose) +{ + lprec *lp; + + lp = make_lp(0, 0); + if(lp != NULL) { + lp->source_is_file = TRUE; + lp->verbose = verbose; + if(!set_XLI(lp, xliname)) { + free_lp(&lp); + printf("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)) + free_lp(&lp); + } + } + return( lp ); +} + +MYBOOL __WINAPI write_XLI(lprec *lp, char *filename, char *options, MYBOOL results) +{ + return( has_XLI(lp) && mat_validate(lp->matA) && lp->xli_writemodel(lp, filename, options, results) ); +} + +MYBOOL __WINAPI has_XLI(lprec *lp) +{ + return( is_nativeXLI(lp) +#if LoadLanguageLib == TRUE + || (MYBOOL) (lp->hXLI != NULL) +#endif + ); +} + +MYBOOL __WINAPI is_nativeXLI(lprec *lp) +{ +#ifdef ExcludeNativeLanguage + return( FALSE ); +#elif LoadLanguageLib == TRUE + return( (MYBOOL) (lp->hXLI == NULL) ); +#else + return( TRUE ); +#endif +} + +MYBOOL __WINAPI set_XLI(lprec *lp, char *filename) +/* (Re)mapping of external language interface variant methods is done here */ +{ + int result = LIB_LOADED; + +#if LoadLanguageLib == TRUE + if(lp->hXLI != NULL) { + #ifdef WIN32 + FreeLibrary(lp->hXLI); + #else + dlclose(lp->hXLI); + #endif + lp->hXLI = NULL; + } +#endif + + if(filename == NULL) { + if(!is_nativeXLI(lp)) + return( FALSE ); +#ifndef ExcludeNativeLanguage + lp->xli_name = xli_name; + lp->xli_compatible = xli_compatible; + lp->xli_readmodel = xli_readmodel; + lp->xli_writemodel = xli_writemodel; +#endif + } + else { +#if LoadLanguageLib == TRUE + #ifdef WIN32 + /* Get a handle to the Windows DLL module. */ + lp->hXLI = LoadLibrary(filename); + + /* If the handle is valid, try to get the function addresses. */ + if(lp->hXLI != NULL) { + lp->xli_compatible = (XLIbool_lpintintint *) + GetProcAddress(lp->hXLI, "xli_compatible"); + if(lp->xli_compatible == NULL) + result = LIB_NOINFO; + else if(lp->xli_compatible(lp, XLIVERSION, MAJORVERSION, sizeof(LPSREAL))) { + + lp->xli_name = (XLIchar *) + GetProcAddress(lp->hXLI, "xli_name"); + lp->xli_readmodel = (XLIbool_lpcharcharcharint *) + GetProcAddress(lp->hXLI, "xli_readmodel"); + lp->xli_writemodel = (XLIbool_lpcharcharbool *) + GetProcAddress(lp->hXLI, "xli_writemodel"); + } + else + result = LIB_VERINVALID; + } + #else + /* First standardize UNIX .SO library name format. */ + char xliname[260], *ptr; + + strcpy(xliname, filename); + if((ptr = strrchr(filename, '/')) == NULL) + ptr = filename; + else + ptr++; + xliname[(int) (ptr - filename)] = 0; + if(strncmp(ptr, "lib", 3)) + strcat(xliname, "lib"); + strcat(xliname, ptr); + if(strcmp(xliname + strlen(xliname) - 3, ".so")) + strcat(xliname, ".so"); + + /* Get a handle to the module. */ + lp->hXLI = dlopen(xliname, RTLD_LAZY); + + /* If the handle is valid, try to get the function addresses. */ + if(lp->hXLI != 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))) { + + 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 + result = LIB_NOTFOUND; +#endif + /* Do validation */ + if((result != LIB_LOADED) || + ((lp->xli_name == NULL) || + (lp->xli_compatible == NULL) || + (lp->xli_readmodel == NULL) || + (lp->xli_writemodel == NULL) + )) { + set_XLI(lp, NULL); + if(result == LIB_LOADED) + result = LIB_NOFUNCTION; + } + } + if(filename != NULL) { + char info[LIB_STR_MAXLEN+1]; + switch(result) { + case LIB_NOTFOUND: strcpy(info, LIB_STR_NOTFOUND); + break; + case LIB_NOINFO: strcpy(info, LIB_STR_NOINFO); + break; + case LIB_NOFUNCTION: strcpy(info, LIB_STR_NOFUNCTION); + break; + case LIB_VERINVALID: strcpy(info, LIB_STR_VERINVALID); + break; + default: strcpy(info, LIB_STR_LOADED); + } + report(lp, IMPORTANT, "set_XLI: %s '%s'\n", + info, filename); + } + return( (MYBOOL) (result == LIB_LOADED)); +} + + +STATIC int get_basisOF(lprec *lp, int coltarget[], LPSREAL 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; + + /* Compute offset over the specified objective indeces (step 2) */ + if(coltarget != NULL) { + register int ix, m = coltarget[0]; + register LPSREAL 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]; + if(ix > n) + value += obj[ix - n]; +/* if(value != 0) { */ + if(fabs(value) > epsvalue) { + nz++; + if(colno != NULL) + colno[nz] = ix; + } + else + value = 0.0; + crow[ix] = value; + } + } + + /* Get the basic objective function values (step 1) */ + else { + register int *basvar = lp->var_basic; + + 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]; + if((*crow) != 0) { +/* if(fabs(*crow) > epsvalue) { */ + nz++; + if(colno != NULL) + colno[nz] = i; + } + } + } + if(colno != NULL) + colno[0] = nz; + return( nz ); +} + +int __WINAPI get_basiscolumn(lprec *lp, int j, int rn[], double bj[]) +/* This routine returns sparse vectors for all basis + columns, including the OF dummy (index 0) and slack columns. + NOTE that the index usage is nonstandard for lp_solve, since + the array offset is 1, not 0. */ +{ + int k = lp->bfp_rowoffset(lp), + matbase = lp->bfp_indexbase(lp); + + /* Do target index adjustment (etaPFI with matbase==0 is special case) */ + if(matbase > 0) + matbase += k - 1; + + /* Convert index of slack and user columns */ + j -= k; + if((j > 0) && !lp->bfp_isSetI(lp)) + j = lp->var_basic[j]; + + /* Process OF dummy and slack columns (always at lower bound) */ + if(j <= lp->rows) { + rn[1] = j + matbase; + bj[1] = 1.0; + k = 1; + } + /* Process user columns (negated if at lower bound) */ + else { + k = obtain_column(lp, j, bj, rn, NULL); + if(matbase != 0) + for(j = 1; j <= k; j++) + rn[j] += matbase; + } + + return( k ); +} + +MYBOOL __WINAPI get_primal_solution(lprec *lp, LPSREAL *pv) +{ + if(lp->spx_status == OPTIMAL) + ; + else if(!lp->basis_valid) { + report(lp, CRITICAL, "get_primal_solution: Not a valid basis"); + return(FALSE); + } + + MEMCOPY(pv, lp->best_solution, lp->sum + 1); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_primal_solution(lprec *lp, LPSREAL **pv) +{ + *pv = lp->best_solution; + return(TRUE); +} + +MYBOOL __WINAPI get_dual_solution(lprec *lp, LPSREAL *rc) +{ + LPSREAL *duals; + MYBOOL ret; + + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_dual_solution: Not a valid basis"); + return(FALSE); + } + + ret = get_ptr_sensitivity_rhs(lp, &duals, NULL, NULL); + + if(ret) + MEMCOPY(rc, duals - 1, lp->sum + 1); + return(ret); +} + +MYBOOL __WINAPI get_ptr_dual_solution(lprec *lp, LPSREAL **rc) +{ + MYBOOL ret = lp->basis_valid; + + /* Just return availability of dual information if rc is NULL */ + if(rc == NULL) + return( ret && ((MIP_count(lp) == 0) || (lp->bb_totalnodes > 0)) ); + + if(!ret) { + report(lp, CRITICAL, "get_ptr_dual_solution: Not a valid basis"); + return(ret); + } + + /* Otherwise, get the pointer to the dual information (and optionally produce it) */ + ret = get_ptr_sensitivity_rhs(lp, rc, NULL, NULL); + if(ret) + (*rc)--; + + return(ret); +} + +MYBOOL __WINAPI get_lambda(lprec *lp, LPSREAL *lambda) +{ + if(!lp->basis_valid || (get_Lrows(lp) == 0)) { + report(lp, CRITICAL, "get_lambda: Not a valid basis"); + return(FALSE); + } + + MEMCOPY(lambda, lp->lambda+1, get_Lrows(lp)); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_lambda(lprec *lp, LPSREAL **lambda) +{ + *lambda = lp->lambda; + return(TRUE); +} + +int __WINAPI get_orig_index(lprec *lp, int lp_index) +{ + if(lp->varmap_locked) + return(lp->presolve_undo->var_to_orig[lp_index]); + else if(lp_index <= lp->presolve_undo->orig_rows) + return(lp_index); + else + return(lp_index-lp->presolve_undo->orig_rows); +} +int __WINAPI get_lp_index(lprec *lp, int orig_index) +{ + if(lp->varmap_locked) + return(lp->presolve_undo->orig_to_var[orig_index]); + else if(orig_index <= lp->presolve_undo->orig_rows) + return(orig_index); + else + return(orig_index-lp->presolve_undo->orig_rows); +} + +MYBOOL __WINAPI is_feasible(lprec *lp, LPSREAL *values, LPSREAL threshold) +/* Recommend to use threshold = lp->epspivot */ +{ + int i, j, elmnr, ie; + LPSREAL *this_rhs, dist; + LPSREAL *value; + int *rownr; + MATrec *mat = lp->matA; + + for(i = lp->rows + 1; i <= lp->sum; i++) { + if(values[i - lp->rows] < unscaled_value(lp, lp->orig_lowbo[i], i) + || values[i - lp->rows] > unscaled_value(lp, lp->orig_upbo[i], i)) { + if(!((lp->sc_lobound[i - lp->rows]>0) && (values[i - lp->rows]==0))) + return(FALSE); + } + } + + this_rhs = (LPSREAL *) 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]; + ie = mat->col_end[j]; + rownr = &COL_MAT_ROWNR(elmnr); + value = &COL_MAT_VALUE(elmnr); + for(; elmnr < ie; elmnr++, rownr += matRowColStep, value += matValueStep) { + this_rhs[*rownr] += unscaled_mat(lp, *value, *rownr, j); + } + } + for(i = 1; i <= lp->rows; i++) { + dist = lp->orig_rhs[i] - this_rhs[i]; + my_roundzero(dist, threshold); + if((lp->orig_upbo[i] == 0 && dist != 0) ||( dist < 0)) { + FREE(this_rhs); + return(FALSE); + } + } + mempool_releaseVector(lp->workarrays, (char *) this_rhs, FALSE); +/* FREE(this_rhs); */ + return(TRUE); +} + +int __WINAPI column_in_lp(lprec *lp, LPSREAL *testcolumn) +{ + int i, j, je, colnr = 0; + int nz, ident = 1; + MATrec *mat = lp->matA; + int *matRownr; + LPSREAL value, *matValue; + + for(nz = 0, i = 1; i <= lp->rows; i++) + if(fabs(testcolumn[i]) > lp->epsvalue) nz++; + + for(i = 1; (i <= lp->columns) && (ident); i++) { + ident = nz; + value = fabs(get_mat(lp, 0, i)-testcolumn[0]); + if(value > lp->epsvalue) + continue; + j = mat->col_end[i - 1]; + je = mat->col_end[i]; + matRownr = &COL_MAT_ROWNR(j); + matValue = &COL_MAT_VALUE(j); + for(; (j < je) && (ident >= 0); + j++, ident--, matRownr += matRowColStep, matValue += matValueStep) { + value = *matValue; + if(is_chsign(lp, *matRownr)) + value = my_flipsign(value); + value = unscaled_mat(lp, value, *matRownr, i); + value -= testcolumn[*matRownr]; + if(fabs(value) > lp->epsvalue) + break; + } + if(ident == 0) + colnr = i; + } + return( colnr ); +} + +MYBOOL __WINAPI set_lp_name(lprec *lp, char *name) +{ + if (name == NULL) { + FREE(lp->lp_name); + lp->lp_name = NULL; + } + else { + allocCHAR(lp, &lp->lp_name, (int) (strlen(name) + 1), AUTOMATIC); + strcpy(lp->lp_name, name); + } + return(TRUE); +} + +char * __WINAPI get_lp_name(lprec *lp) +{ + return((lp->lp_name != NULL) ? lp->lp_name : (char *) ""); +} + +STATIC MYBOOL init_rowcol_names(lprec *lp) +{ + if(!lp->names_used) { + lp->row_name = (hashelem **) calloc(lp->rows_alloc + 1, sizeof(*lp->row_name)); + lp->col_name = (hashelem **) calloc(lp->columns_alloc + 1, sizeof(*lp->col_name)); + lp->rowname_hashtab = create_hash_table(lp->rows_alloc + 1, 0); + lp->colname_hashtab = create_hash_table(lp->columns_alloc + 1, 1); + lp->names_used = TRUE; + } + return(TRUE); +} + +MYBOOL rename_var(lprec *lp, int varindex, char *new_name, hashelem **list, hashtable **ht) +{ + hashelem *hp; + MYBOOL newitem; + + hp = list[varindex]; + newitem = (MYBOOL) (hp == NULL); + if(newitem) + hp = puthash(new_name, varindex, list, *ht); + else if((strlen(hp->name) != strlen(new_name)) || + (strcmp(hp->name, new_name) != 0)) { + hashtable *newht, *oldht; + + allocCHAR(lp, &hp->name, (int) (strlen(new_name) + 1), AUTOMATIC); + strcpy(hp->name, new_name); + oldht = *ht; + newht = copy_hash_table(oldht, list, oldht->size); + *ht = newht; + free_hash_table(oldht); + } + return(newitem); +} + +MYBOOL __WINAPI is_use_names(lprec *lp, MYBOOL isrow) +{ + if(isrow) + return( lp->use_row_names ); + else + return( lp->use_col_names ); +} + +void __WINAPI set_use_names(lprec *lp, MYBOOL isrow, MYBOOL use_names) +{ + if(isrow) + lp->use_row_names = use_names; + else + lp->use_col_names = use_names; +} + +int __WINAPI get_nameindex(lprec *lp, char *varname, MYBOOL isrow) +{ + if(isrow) + return( find_row(lp, varname, FALSE) ); + else + return( find_var(lp, varname, FALSE) ); +} + +MYBOOL __WINAPI set_row_name(lprec *lp, int rownr, char *new_name) +{ + if((rownr < 0) || (rownr > lp->rows+1)) { + report(lp, IMPORTANT, "set_row_name: Row %d out of range", rownr); + return(FALSE); + } + + /* Prepare for a new row */ + if((rownr > lp->rows) && !append_rows(lp, rownr-lp->rows)) + return( FALSE ); + if(!lp->names_used) { + if(!init_rowcol_names(lp)) + return(FALSE); + } + rename_var(lp, rownr, new_name, lp->row_name, &lp->rowname_hashtab); + + return(TRUE); +} + +char * __WINAPI get_row_name(lprec *lp, int rownr) +{ + if((rownr < 0) || (rownr > lp->rows+1)) { + report(lp, IMPORTANT, "get_row_name: Row %d out of range", rownr); + return(NULL); + } + + if((lp->presolve_undo->var_to_orig != NULL) && lp->wasPresolved) { + if(lp->presolve_undo->var_to_orig[rownr] == 0) + rownr = -rownr; + else + rownr = lp->presolve_undo->var_to_orig[rownr]; + } + return( get_origrow_name(lp, rownr) ); +} + +char * __WINAPI get_origrow_name(lprec *lp, int rownr) +{ + MYBOOL newrow; + char *ptr; + + newrow = (MYBOOL) (rownr < 0); + rownr = abs(rownr); +#ifdef Paranoia + if(((lp->presolve_undo->var_to_orig == NULL) && newrow) || + (rownr > MAX(lp->rows, lp->presolve_undo->orig_rows))) { + report(lp, IMPORTANT, "get_origrow_name: Row %d out of range", rownr); + return(NULL); + } +#endif + + if(lp->names_used && lp->use_row_names && (lp->row_name[rownr] != NULL) && + (lp->row_name[rownr]->name != NULL)) { +#ifdef Paranoia + if(lp->row_name[rownr]->index != rownr) + report(lp, SEVERE, "get_origrow_name: Inconsistent row ordinal %d vs %d\n", + rownr, lp->row_name[rownr]->index); +#endif + 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); + else + sprintf(ptr, ROWNAMEMASK, rownr); + } + return(ptr); +} + +MYBOOL __WINAPI set_col_name(lprec *lp, int colnr, char *new_name) +{ + if((colnr > lp->columns+1) || (colnr < 1)) { + report(lp, IMPORTANT, "set_col_name: Column %d out of range", colnr); + } + + if((colnr > lp->columns) && !append_columns(lp, colnr-lp->columns)) + return(FALSE); + + if(!lp->names_used) + init_rowcol_names(lp); + rename_var(lp, colnr, new_name, lp->col_name, &lp->colname_hashtab); + + return(TRUE); +} + +char * __WINAPI get_col_name(lprec *lp, int colnr) +{ + if((colnr > lp->columns+1) || (colnr < 1)) { + report(lp, IMPORTANT, "get_col_name: Column %d out of range", colnr); + return(NULL); + } + + if((lp->presolve_undo->var_to_orig != NULL) && lp->wasPresolved) { + if(lp->presolve_undo->var_to_orig[lp->rows + colnr] == 0) + colnr = -colnr; + else + colnr = lp->presolve_undo->var_to_orig[lp->rows + colnr]; + } + return( get_origcol_name(lp, colnr) ); +} + +char * __WINAPI get_origcol_name(lprec *lp, int colnr) +{ + MYBOOL newcol; + char *ptr; + + newcol = (MYBOOL) (colnr < 0); + colnr = abs(colnr); +#ifdef Paranoia + if(((lp->presolve_undo->var_to_orig == NULL) && newcol) || + (colnr > MAX(lp->columns, lp->presolve_undo->orig_columns))) { + report(lp, IMPORTANT, "get_origcol_name: Column %d out of range", colnr); + return(NULL); + } +#endif + + if(lp->names_used && lp->use_col_names && (lp->col_name[colnr] != NULL) && (lp->col_name[colnr]->name != NULL)) { +#ifdef Paranoia + if(lp->col_name[colnr]->index != colnr) + report(lp, SEVERE, "get_origcol_name: Inconsistent column ordinal %d vs %d\n", + colnr, lp->col_name[colnr]->index); +#endif + 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); + else + sprintf(ptr, COLNAMEMASK, colnr); + } + return(ptr); +} + +STATIC int MIP_count(lprec *lp) +{ + return( lp->int_vars+lp->sc_vars+SOS_count(lp) ); +} +STATIC int bin_count(lprec *lp, MYBOOL working) +{ + int i, n = 0; + if(working) { + for(i = lp->rows+1; i <= lp->sum; i++) + if(fabs(unscaled_value(lp, lp->upbo[i], i) - 1) < lp->epsvalue) + n++; + } + else { + for(i = 1; i <= lp->columns; i++) + if((fabs(get_upbo(lp, i) - 1) < lp->epsvalue) && + (fabs(get_lowbo(lp, i) - 0) < lp->epsvalue)) + n++; + } + return( n ); +} +STATIC int SOS_count(lprec *lp) +{ + if(lp->SOS == NULL) + return( 0 ); + else + return( lp->SOS->sos_count ); +} +STATIC int GUB_count(lprec *lp) +{ + if(lp->GUB == NULL) + return( 0 ); + else + return( lp->GUB->sos_count ); +} + +STATIC LPSREAL 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; + + value = lp->rhs[row_nr]; + row_nr = lp->var_basic[row_nr]; + test = value - my_lowbound(lp->lowbo[row_nr]); + my_roundzero(test, lp->epsprimal); + if(test > 0) { + test = value - lp->upbo[row_nr]; + my_roundzero(test, lp->epsprimal); + if(test < 0) + test = 0; + } + return( test ); +} + +STATIC LPSREAL feasibilityOffset(lprec *lp, MYBOOL isdual) +{ + int i, j; + LPSREAL f, Extra; + + Extra = 0; + if(isdual) { + /* This section computes a OF offset to ensure that the dual phase 1 is + feasible. It is used to compute a primal feasible base that can be + passed to the primal simplex in phase 2. */ +#if 0 + + /* This is the legacy (v3.2-) P1extraVal logic that sets Extra to be the + smallest negative reduced cost. Note that the reduced costs are the + values of the dual slacks, which are [0..Inf> for feasibility. + If we have negative reduced costs for bounded non-basic variables, we + can simply switch the bound to obtain feasibility and possibly avoid + having to set Extra. */ + if(!isDualFeasible(lp, lp->epsprimal, NULL, NULL, &f) + Extra = f; + +#else + /* Find the most negative of the objective coefficients. We will subtract this + value from every element of the objective row, making it non-negative and + the problem therefore dual feasible. */ + for(i = 1; i <= lp->columns; i++) { + f = lp->orig_obj[i]; + if(f < Extra) + Extra = f; + } +#endif + } + + else { + /* Set Extra to be the index of the most negative of the net RHS coefficients; + this approach can be used in the primal phase 1 followed by the dual phase 2 + and when there are no ranged constraints. When there are ranged constraints, + additional artificial variables must be introduced. */ + Extra = 0; + j = 0; + Extra = lp->infinite; + for(i = 1; i <= lp->rows; i++) { + f = lp->rhs[i]; + if(f < Extra) { + Extra = f; + j = i; + } + } + Extra = j; + } + + return(Extra); + +} + +STATIC LPSREAL compute_dualslacks(lprec *lp, int target, LPSREAL **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; + MYBOOL localREAL = (MYBOOL) (dvalues == NULL), + localINT = (MYBOOL) (nzdvalues == NULL); + + if(is_action(lp->spx_action, ACTION_REBASE) || + is_action(lp->spx_action, ACTION_REINVERT) || !lp->basis_valid) + return( g ); + + /* Initialize */ + if(!localREAL) { + duals = dvalues; + nzduals = nzdvalues; + } + else { + duals = &vtemp; + nzduals = &nzvtemp; + } + if(localINT || (*nzduals == NULL)) + allocINT(lp, nzduals, lp->columns + 1, AUTOMATIC); + if(localREAL || (*duals == NULL)) + allocREAL(lp, duals, lp->sum + 1, AUTOMATIC); + if(target == 0) + target = SCAN_ALLVARS+ USE_NONBASICVARS; + + /* Define variable target list and compute the reduced costs */ + coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->columns+1, sizeof(*coltarget)); + if(!get_colIndexA(lp, target, coltarget, FALSE)) { + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + return(FALSE); + } + bsolve(lp, 0, *duals, NULL, lp->epsmachine*DOUBLEROUND, 1.0); + prod_xA(lp, coltarget, *duals, NULL, lp->epsmachine, 1.0, + *duals, *nzduals, MAT_ROUNDDEFAULT | MAT_ROUNDRC); + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + + /* Compute sum or maximum infeasibility as specified */ + for(i = 1; i <= (*nzduals)[0]; i++) { + varnr = (*nzduals)[i]; + d = my_chsign(!lp->is_lower[varnr], (*duals)[varnr]); + if(d < 0) { + if(dosum) + g += -d; /* Compute sum as a positive number */ + else { + SETMIN(g, d); /* Compute gap as a negative number */ + } + } + } + + /* Clean up */ + if(localREAL) + FREE(*duals); + if(localINT) + FREE(*nzduals); + + return( g ); +} + +STATIC LPSREAL compute_feasibilitygap(lprec *lp, MYBOOL isdual, MYBOOL dosum) +{ + LPSREAL f = 0; + + /* This computes the primal feasibility gap (for use with the dual simplex phase 1) */ + if(isdual) { + int i; + LPSREAL g; + + for(i = 1; i <= lp->rows; i++) { + if(lp->rhs[i] < 0) + g = lp->rhs[i]; + else if(lp->rhs[i] > lp->upbo[lp->var_basic[i]]) + g = lp->rhs[i] - lp->upbo[lp->var_basic[i]]; + else + g = 0; + if(dosum) + f += g; + else { + SETMAX(f, g); + } + } + } + /* This computes the dual feasibility gap (for use with the primal simplex phase 1) */ + else + f = compute_dualslacks(lp, SCAN_USERVARS+USE_ALLVARS, NULL, NULL, dosum); + + return( f ); +} + +/* 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) +{ + int basi, i, j, ncols = lp->columns; + LPSREAL f, /* g, */ epsvalue = lp->epsprimal; + + basi = 0; + for(j = 1; j <= ncols; j++) { + if(intsonly && !is_int(lp, j)) { + if(intsonly == TRUE) + break; + else + continue; + } + f = fabs(get_mat(lp, rownr, j)); + /* f = fmod(f, 1); */ + f -= floor (f + epsvalue); +/* + if(f <= epsvalue) + continue; + g = f; +*/ + for(i = 0; (i <= MAX_FRACSCALE) && (/* g */ f > epsvalue); i++) { + f *= 10; + /* g = fmod(f, 1); */ + f -= floor (f + epsvalue); + } + if(i > MAX_FRACSCALE) + /* i = MAX_FRACSCALE */ break; + SETMAX(basi, i); + } + if(j > ncols) + *intscalar = pow(10.0, basi); + else { + basi = -1; + *intscalar = 1; + } + return( basi ); +} + +STATIC int row_intstats(lprec *lp, int rownr, int pivcolnr, int *maxndec, + int *plucount, int *intcount, int *intval, LPSREAL *valGCD, LPSREAL *pivcolval) +{ + int jb, je, jj, nn = 0, multA, multB, intGCD = 0; + LPSREAL rowval, inthold, intfrac; + MATrec *mat = lp->matA; + + /* Do we have a valid matrix? */ + if(mat_validate(mat)) { + + /* Get smallest fractional row value */ + *maxndec = row_decimals(lp, rownr, AUTOMATIC, &intfrac); + + /* Get OF row starting and ending positions, as well as the first column index */ + if(rownr == 0) { + jb = 1; + je = lp->columns+1; + } + else { + jb = mat->row_end[rownr-1]; + je = mat->row_end[rownr]; + } + nn = je - jb; + *pivcolval = 1.0; + *plucount = 0; + *intcount = 0; + *intval = 0; + for(; jb < je; jb++) { + + if(rownr == 0) { + if(lp->orig_obj[jb] == 0) { + nn--; + continue; + } + jj = jb; + } + else + jj = ROW_MAT_COLNR(jb); + + /* Pick up the value of the pivot column and continue */ + if(jj == pivcolnr) { + if(rownr == 0) + *pivcolval = unscaled_mat(lp, lp->orig_obj[jb], 0, jb); + else + *pivcolval = get_mat_byindex(lp, jb, TRUE, FALSE); + continue; + } + if(!is_int(lp, jj)) + continue; + + /* Update the count of integer columns */ + (*intcount)++; + + /* Update the count of positive parameter values */ + if(rownr == 0) + rowval = unscaled_mat(lp, lp->orig_obj[jb], 0, jb); + else + rowval = get_mat_byindex(lp, jb, TRUE, FALSE); + if(rowval > 0) + (*plucount)++; + + /* Check if the parameter value is integer and update the row's GCD */ + rowval = fabs(rowval) * intfrac; + rowval += rowval*lp->epsmachine; + rowval = modf(rowval, &inthold); + if(rowval < lp->epsprimal) { + (*intval)++; + if(*intval == 1) + intGCD = (int) inthold; + else + intGCD = gcd(intGCD, (LLONG) inthold, &multA, &multB); + } + } + *valGCD = intGCD; + *valGCD /= intfrac; + } + + return(nn); +} + +#if 0 +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, maxndec, + pluscount, intcount, intval; + LPSREAL 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)) + 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; + } + + /* Check if we found information for any real-valued variable; + if not, then we must set the iprovement delta to 0 */ + if(nrv == 0) + 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; + } + + /* 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; + } + + /* 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) || + ((lp->simplex_mode & SIMPLEX_Phase2_PRIMAL) != 0))); +} + +STATIC MYBOOL isPhase1(lprec *lp) +{ + return((MYBOOL) (((lp->simplex_mode & SIMPLEX_Phase1_PRIMAL) != 0) || + ((lp->simplex_mode & SIMPLEX_Phase1_DUAL) != 0))); +} + +STATIC MYBOOL isP1extra(lprec *lp) +{ + return((MYBOOL) ((lp->P1extraDim > 0) || (lp->P1extraVal != 0))); +} + +STATIC MYBOOL feasiblePhase1(lprec *lp, LPSREAL epsvalue) +{ + LPSREAL gap; + MYBOOL test; + + gap = fabs(lp->rhs[0] - lp->orig_rhs[0]); + test = (MYBOOL) (gap < epsvalue); + return( test) ; +} + +STATIC MYBOOL isDegenerateBasis(lprec *lp, int basisvar) +{ + int varindex; + + varindex = lp->var_basic[basisvar]; + if((fabs(lp->rhs[basisvar]) < lp->epsprimal) || + (fabs(lp->upbo[varindex]-lp->rhs[basisvar]) < lp->epsprimal)) + return( TRUE ); + else + return( FALSE ); +} + +STATIC int findBasicFixedvar(lprec *lp, int afternr, MYBOOL slacksonly) +{ + int varnr, delta = 1; + + if(afternr < 0) { + delta = -1; + afternr = -afternr; + } + afternr += delta; + if((afternr < 1) || (afternr > lp->rows)) + return( 0 ); + + for(; (afternr > 0) && (afternr <= lp->rows); afternr += delta) { + varnr = lp->var_basic[afternr]; + if(((varnr <= lp->rows) && is_constr_type(lp, varnr, EQ)) || + (!slacksonly && (varnr > lp->rows) && is_fixedvar(lp, varnr))) + break; + } + + if(afternr > lp->rows) + afternr = 0; + + return( afternr ); +} + +STATIC MYBOOL isBasisVarFeasible(lprec *lp, LPSREAL tol, int basis_row) +{ + int col; + LPSREAL x; + MYBOOL Ok = TRUE; + MYBOOL doSC = FALSE; + + col = lp->var_basic[basis_row]; + x = lp->rhs[basis_row]; /* The current solution of basic variables stored here! */ + if((x < -tol) || (x > lp->upbo[col]+tol)) + Ok = FALSE; + else if(doSC && (col > lp->rows) && (fabs(lp->sc_lobound[col - lp->rows]) > 0)) { + if((x > tol) && (x < fabs(lp->sc_lobound[col - lp->rows])-tol)) + Ok = FALSE; + } + return( Ok ); +} +STATIC MYBOOL isPrimalFeasible(lprec *lp, LPSREAL tol, int infeasibles[], LPSREAL *feasibilitygap) +{ + int i; + MYBOOL feasible = TRUE; + + /* This is a short-hand call to rowdual() to check for primal infeasibility */ + +#if 0 + /* Traditional indexing style */ + for(i = 1; i <= lp->rows; i++) { + feasible = isBasisVarFeasible(lp, tol, i); +#else + /* Fast array pointer style */ + LREAL *rhsptr; + int *idxptr; + + if(infeasibles != NULL) + infeasibles[0] = 0; + for(i = 1, rhsptr = lp->rhs+1, idxptr = lp->var_basic+1; + (i <= lp->rows); i++, rhsptr++, idxptr++) { + feasible = TRUE; +/* if(((*rhsptr) < lp->lowbo[*idxptr]-tol) || ((*rhsptr) > lp->upbo[*idxptr]+tol)) */ + if(((*rhsptr) < -tol) || ((*rhsptr) > lp->upbo[*idxptr]+tol)) + feasible = FALSE; +#endif + if(!feasible) { + if(infeasibles == NULL) + break; + infeasibles[0]++; + infeasibles[infeasibles[0]] = i; + } + } + + /* Compute feasibility gap (could actually do this calculation above) */ + if(feasibilitygap != NULL) { + if(feasible) + *feasibilitygap = 0.0; + else + *feasibilitygap = feasibilityOffset(lp, FALSE); + } + + return(feasible); +} + +STATIC MYBOOL isDualFeasible(lprec *lp, LPSREAL tol, int *boundflipcount, int infeasibles[], LPSREAL *feasibilitygap) +{ + int i, varnr, + n = 0, /* Number of infeasible duals corrected with bound-swaps */ + m = 0, + target = SCAN_ALLVARS+USE_NONBASICVARS; + LPSREAL f = 0; + MYBOOL feasible, islower; + + + /* The reduced costs are the values of the dual slacks, which + are [0..Inf> for feasibility. If we have negative reduced costs + for bounded non-basic variables, we can simply switch the bound + of bounded variables to obtain dual feasibility and possibly avoid + having to use dual simplex phase 1. */ + if((infeasibles != NULL) || (boundflipcount != NULL)) { + int *nzdcol = NULL; + LPSREAL d, *dcol = NULL; + + f = compute_dualslacks(lp, target, &dcol, &nzdcol, FALSE); + if(nzdcol != NULL) + for(i = 1; i <= nzdcol[0]; i++) { + varnr = nzdcol[i]; + islower = lp->is_lower[varnr]; + d = my_chsign(!islower, dcol[varnr]); + + /* Don't bother with uninteresting non-basic variables */ + if((d > -tol) || /* Positive reduced costs with a tolerance */ + my_unbounded(lp, varnr) || /* Free variables cannot change bound */ + is_fixedvar(lp, varnr)) /* Equality slack or a fixed variable ("type 3") */ + continue; + + /* Check if we have non-flippable bounds, i.e. an unbounded variable + (types 2+4), or bounded variables (type 3), and if the counter is NULL. */ + if( (boundflipcount == NULL) || + ((lp->bb_level <= 1) && (my_rangebo(lp, varnr) > fabs(lp->negrange))) || + (islower && my_infinite(lp, lp->upbo[varnr])) || + (!islower && my_infinite(lp, my_lowbo(lp, varnr))) ) { + m++; + if(infeasibles != NULL) + infeasibles[m] = varnr; + } + /* Only do bound flips if the user-provided counter is non-NULL */ + else { + lp->is_lower[varnr] = !islower; + n++; + } + } + if(infeasibles != NULL) + infeasibles[0] = m; + FREE(dcol); + FREE(nzdcol); + if(n > 0) { + set_action(&lp->spx_action, ACTION_RECOMPUTE); + if(m == 0) + f = 0; + } + } + else + f = compute_dualslacks(lp, target, NULL, NULL, FALSE); +/* f = feasibilityOffset(lp, TRUE); */ /* Safe legacy mode */ + + /* Do an extra scan to see if there are bounded variables in the OF not present in any constraint; + Most typically, presolve fixes such cases, so this is rarely encountered. */ + + 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++; + } + } + } + + /* Return status */ + + if(boundflipcount != NULL) + *boundflipcount = n; + if(feasibilitygap != NULL) { + my_roundzero(f, tol); + *feasibilitygap = f; + } + feasible = (MYBOOL) ((f == 0) && (m == 0)); + + return(feasible); +} + +void __WINAPI default_basis(lprec *lp) +{ + int i; + + /* Set the slack variables to be basic; note that the is_basic[] array + is a helper array filled in presolve() to match var_basic[]. */ + for(i = 1; i <= lp->rows; i++) { + lp->var_basic[i] = i; + lp->is_basic[i] = TRUE; + lp->is_lower[i] = TRUE; + } + lp->var_basic[0] = TRUE; /* Set to signal that this is the default basis */ + + /* Set user variables at their lower bound, including the + dummy slack for the objective "constraint" */ + for(; i <= lp->sum; i++) { + lp->is_basic[i] = FALSE; + lp->is_lower[i] = TRUE; + } + lp->is_lower[0] = TRUE; + + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + lp->basis_valid = TRUE; /* Do not re-initialize basis on entering Solve */ +} + +int __WINAPI get_basiscrash(lprec *lp) +{ + return(lp->crashmode); +} + +void __WINAPI set_basiscrash(lprec *lp, int mode) +{ + lp->crashmode = mode; +} + +MYBOOL __WINAPI set_basis(lprec *lp, int *bascolumn, MYBOOL nonbasic) /* Added by KE */ +{ + int i,s,k,n; + + /* Make sure we are consistent */ + if(lp->wasPresolved && ((lp->rows != lp->presolve_undo->orig_rows) || + (lp->columns != lp->presolve_undo->orig_columns))) + return( FALSE ); + + /* Initialize (lp->is_basic is set in preprocess); Note that as of v5 and before + it is an lp_solve convention that basic variables are at their lower bounds! + This routine provides for the a possible future case that basic variables + can be upper-bounded. */ + lp->is_lower[0] = TRUE; + for(i = 1; i <= lp->sum; i++) { + lp->is_lower[i] = TRUE; + lp->is_basic[i] = FALSE; + } + for(i = 1; i <= lp->rows; i++) + lp->var_basic[i] = FALSE; + + /* Set basic and optionally non-basic variables; + negative index means at lower bound, positive at upper bound */ + if(nonbasic) + n = lp->sum; + else + n = lp->rows; + for(i = 1; i <= n; i++) { + s = bascolumn[i]; + k = abs(s); + if(k <= 0 || k > lp->sum) + return( FALSE ); + if(i <= lp->rows) { + lp->var_basic[i] = k; + lp->is_basic[k] = TRUE; + } + else /* Remove this test if basic variables can be upper-bounded */ + if(s > 0) + lp->is_lower[k] = FALSE; + } + if(!verify_basis(lp)) + return( FALSE ); + + /* Invalidate basis */ + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + lp->basis_valid = TRUE; /* Do not re-initialize basis on entering Solve */ + lp->var_basic[0] = FALSE; /* Set to signal that this is a non-default basis */ + + return( TRUE ); +} + +void __WINAPI reset_basis(lprec *lp) +{ + lp->basis_valid = FALSE; /* Causes reinversion at next opportunity */ +} + +MYBOOL __WINAPI get_basis(lprec *lp, int *bascolumn, MYBOOL nonbasic) +{ + int k, i; + + if(!lp->basis_valid || + (lp->rows != lp->presolve_undo->orig_rows) || + (lp->columns != lp->presolve_undo->orig_columns)) + return( FALSE ); + + *bascolumn = 0; + + /* First save basic variable indexes */ + for(i = 1; i <= lp->rows; i++) { + k = lp->var_basic[i]; + bascolumn[i] = my_chsign(lp->is_lower[k], k); + } + + /* Then optionally save non-basic variable indeces */ + if(nonbasic) { + for(k = 1; (k <= lp->sum) && (i <= lp->sum); k++) { + if(lp->is_basic[k]) + continue; + bascolumn[i] = my_chsign(lp->is_lower[k], k); + i++; + } + } + return( TRUE ); +} + +STATIC MYBOOL is_BasisReady(lprec *lp) +{ + return( (MYBOOL) (lp->var_basic[0] != AUTOMATIC) ); +} + +STATIC MYBOOL is_slackbasis(lprec *lp) +{ + int n = 0, err = 0; + if(lp->basis_valid) { + int i, k; + MYBOOL *used = NULL; + + allocMYBOOL(lp, &used, lp->rows+1, TRUE); + for(i = 1; i <= lp->rows; i++) { + k = lp->var_basic[i]; + if(k <= lp->rows) { + if(used[k]) + err++; + else + used[k] = TRUE; + n++; + } + } + FREE(used); + if(err > 0) + report(lp, SEVERE, "is_slackbasis: %d inconsistencies found in slack basis\n", err); + } + return( (MYBOOL) (n == lp->rows) ); +} + +STATIC MYBOOL verify_basis(lprec *lp) +{ + int i, ii, k = 0; + MYBOOL result = FALSE; + + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i]; + if((ii < 1) || (ii > lp->sum) || !lp->is_basic[ii]) { + k = i; + ii = 0; + goto Done; + } + } + + ii = lp->rows; + for(i = 1; i <= lp->sum; i++) { + if(lp->is_basic[i]) + ii--; + } + result = (MYBOOL) (ii == 0); + +Done: +#if 0 /* For testing */ + if(!result) + ii = 0; +#endif + return(result); +} + +int __WINAPI set_basisvar(lprec *lp, int basisPos, int enteringCol) +{ + int leavingCol; + + leavingCol = lp->var_basic[basisPos]; + +#ifdef Paranoia + if((basisPos < 1) || (basisPos > lp->rows)) + report(lp, SEVERE, "set_basisvar: Invalid leaving basis position %d specified at iter %.0f\n", + basisPos, (double) get_total_iter(lp)); + if((leavingCol < 1) || (leavingCol > lp->sum)) + report(lp, SEVERE, "set_basisvar: Invalid leaving column %d referenced at iter %.0f\n", + leavingCol, (double) get_total_iter(lp)); + if((enteringCol < 1) || (enteringCol > lp->sum)) + report(lp, SEVERE, "set_basisvar: Invalid entering column %d specified at iter %.0f\n", + enteringCol, (double) get_total_iter(lp)); +#endif + +#ifdef ParanoiaXY + if(!lp->is_basic[leavingCol]) + report(lp, IMPORTANT, "set_basisvar: Leaving variable %d is not basic at iter %.0f\n", + leavingCol, (double) get_total_iter(lp)); + if(enteringCol > lp->rows && lp->is_basic[enteringCol]) + report(lp, IMPORTANT, "set_basisvar: Entering variable %d is already basic at iter %.0f\n", + enteringCol, (double) get_total_iter(lp)); +#endif + + lp->var_basic[0] = FALSE; /* Set to signal that this is a non-default basis */ + lp->var_basic[basisPos] = enteringCol; + lp->is_basic[leavingCol] = FALSE; + lp->is_basic[enteringCol] = TRUE; + if(lp->bb_basis != NULL) + lp->bb_basis->pivots++; + + return(leavingCol); +} + +/* Bounds updating and unloading routines; requires that the + current values for upbo and lowbo are in the original base. */ +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; + + if(perturbed == NULL) + return( n ); + + /* Map reference bounds to previous state, i.e. cumulate + perturbations in case of persistent problems */ + upbo = perturbed->upbo; + lowbo = perturbed->lowbo; + + /* Set appropriate target variable range */ + i = 1; + ii = lp->rows; + if(!doRows) + i += ii; + if(!doCols) + ii = lp->sum; + + /* Perturb (expand) finite variable bounds randomly */ + for(; i <= ii; i++) { + + /* Don't perturb regular slack variables */ + if((i <= lp->rows) && (lowbo[i] == 0) && (upbo[i] >= lp->infinite)) + continue; + + new_lb = lowbo[i]; + new_ub = upbo[i]; + + /* Don't perturb fixed variables if not specified */ + if(!includeFIXED && (new_ub == new_lb)) + continue; + + /* Lower bound for variables (consider implementing RHS here w/contentmode== AUTOMATIC) */ + if((i > lp->rows) && (new_lb < lp->infinite)) { + new_lb = rand_uniform(lp, RANDSCALE) + 1; + new_lb *= lp->epsperturb; + lowbo[i] -= new_lb; + n++; + } + + /* Upper bound */ + if(new_ub < lp->infinite) { + new_ub = rand_uniform(lp, RANDSCALE) + 1; + new_ub *= lp->epsperturb; + upbo[i] += new_ub; + n++; + } + } + + /* Make sure we start from scratch */ + set_action(&lp->spx_action, ACTION_REBASE); + + return( n ); +} + +STATIC MYBOOL impose_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo) +/* Explicitly set working bounds to given vectors without pushing or popping */ +{ + MYBOOL ok; + + ok = (MYBOOL) ((upbo != NULL) || (lowbo != NULL)); + if(ok) { + if((upbo != NULL) && (upbo != lp->upbo)) + MEMCOPY(lp->upbo, upbo, lp->sum + 1); + if((lowbo != NULL) && (lowbo != lp->lowbo)) + MEMCOPY(lp->lowbo, lowbo, lp->sum + 1); + if(lp->bb_bounds != NULL) + lp->bb_bounds->UBzerobased = FALSE; + set_action(&lp->spx_action, ACTION_REBASE); + } + set_action(&lp->spx_action, ACTION_RECOMPUTE); + return( ok ); +} + +STATIC MYBOOL validate_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo) +/* Check if all bounds are Explicitly set working bounds to given vectors without pushing or popping */ +{ + MYBOOL ok; + int i; + + ok = (MYBOOL) ((upbo != NULL) || (lowbo != NULL)); + if(ok) { + for(i = 1; i <= lp->sum; i++) + if((lowbo[i] > upbo[i]) || (lowbo[i] < lp->orig_lowbo[i]) || (upbo[i] > lp->orig_upbo[i])) + break; + ok = (MYBOOL) (i > lp->sum); + } + return( ok ); +} + +STATIC int unload_BB(lprec *lp) +{ + int levelsunloaded = 0; + + if(lp->bb_bounds != NULL) + while(pop_BB(lp->bb_bounds)) + levelsunloaded++; + return( levelsunloaded ); +} + + +#define LowerStorageModel 1 +#define BasisStorageModel 1 +STATIC basisrec *push_basis(lprec *lp, int *basisvar, MYBOOL *isbasic, MYBOOL *islower) +/* Save the ingoing basis and push it onto the stack */ +{ + int sum = lp->sum + 1; + basisrec *newbasis = NULL; + + newbasis = (basisrec *) calloc(sizeof(*newbasis), 1); + if((newbasis != NULL) && +#if LowerStorageModel == 0 + allocMYBOOL(lp, &newbasis->is_lower, sum, FALSE) && +#else + allocMYBOOL(lp, &newbasis->is_lower, (sum + 8) / 8, TRUE) && +#endif +#if BasisStorageModel == 0 + allocMYBOOL(lp, &newbasis->is_basic, sum, FALSE) && +#endif + allocINT(lp, &newbasis->var_basic, lp->rows + 1, FALSE)) { + + if(islower == NULL) + islower = lp->is_lower; + if(isbasic == NULL) + isbasic = lp->is_basic; + if(basisvar == NULL) + basisvar = lp->var_basic; + +#if LowerStorageModel == 0 + MEMCOPY(newbasis->is_lower, islower, sum); +#else + for(sum = 1; sum <= lp->sum; sum++) + if(islower[sum]) + set_biton(newbasis->is_lower, sum); +#endif +#if BasisStorageModel == 0 + MEMCOPY(newbasis->is_basic, isbasic, lp->sum + 1); +#endif + MEMCOPY(newbasis->var_basic, basisvar, lp->rows + 1); + + newbasis->previous = lp->bb_basis; + if(lp->bb_basis == NULL) + newbasis->level = 0; + else + newbasis->level = lp->bb_basis->level + 1; + newbasis->pivots = 0; + + lp->bb_basis = newbasis; + } + return( newbasis ); +} + +STATIC MYBOOL compare_basis(lprec *lp) +/* Compares the last pushed basis with the currently active basis */ +{ + int i, j; + MYBOOL same_basis = TRUE; + + if(lp->bb_basis == NULL) + return( FALSE ); + + /* Loop over basis variables until a mismatch (order can be different) */ + i = 1; + while(same_basis && (i <= lp->rows)) { + j = 1; + while(same_basis && (j <= lp->rows)) { + same_basis = (MYBOOL) (lp->bb_basis->var_basic[i] != lp->var_basic[j]); + j++; + } + same_basis = !same_basis; + i++; + } + /* Loop over bound status indicators until a mismatch */ + i = 1; + while(same_basis && (i <= lp->sum)) { + same_basis = (lp->bb_basis->is_lower[i] && lp->is_lower[i]); + i++; + } + + return( same_basis ); +} + +STATIC MYBOOL restore_basis(lprec *lp) +/* Restore values from the previously pushed / saved basis without popping it */ +{ + MYBOOL ok; + int i; + + ok = (MYBOOL) (lp->bb_basis != NULL); + if(ok) { + MEMCOPY(lp->var_basic, lp->bb_basis->var_basic, lp->rows + 1); +#if BasisStorageModel == 0 + MEMCOPY(lp->is_basic, lp->bb_basis->is_basic, lp->sum + 1); +#else + MEMCLEAR(lp->is_basic, lp->sum + 1); + for(i = 1; i <= lp->rows; i++) + lp->is_basic[lp->var_basic[i]] = TRUE; +#endif +#if LowerStorageModel == 0 + MEMCOPY(lp->is_lower, lp->bb_basis->is_lower, lp->sum + 1); +#else + for(i = 1; i <= lp->sum; i++) + lp->is_lower[i] = is_biton(lp->bb_basis->is_lower, i); +#endif + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT); + } + return( ok ); +} + +STATIC MYBOOL pop_basis(lprec *lp, MYBOOL restore) +/* Pop / free, and optionally restore the previously "pushed" / saved basis */ +{ + MYBOOL ok; + basisrec *oldbasis; + + ok = (MYBOOL) (lp->bb_basis != NULL); + if(ok) { + oldbasis = lp->bb_basis; + if(oldbasis != NULL) { + lp->bb_basis = oldbasis->previous; + FREE(oldbasis->var_basic); +#if BasisStorageModel == 0 + FREE(oldbasis->is_basic); +#endif + FREE(oldbasis->is_lower); + FREE(oldbasis); + } + if(restore && (lp->bb_basis != NULL)) + restore_basis(lp); + } + return( ok ); +} + +STATIC int unload_basis(lprec *lp, MYBOOL restorelast) +{ + int levelsunloaded = 0; + + if(lp->bb_basis != NULL) + while(pop_basis(lp, restorelast)) + levelsunloaded++; + return( levelsunloaded ); +} + + +STATIC LPSREAL scaled_floor(lprec *lp, int colnr, LPSREAL value, LPSREAL epsscale) +{ + value = floor(value); + if(value != 0) + if(lp->columns_scaled && is_integerscaling(lp)) { + value = scaled_value(lp, value, colnr); + if(epsscale != 0) + value += epsscale*lp->epsmachine; +/* value += epsscale*lp->epsprimal; */ +/* value = restoreINT(value, lp->epsint); */ + } + return(value); +} + +STATIC LPSREAL scaled_ceil(lprec *lp, int colnr, LPSREAL value, LPSREAL epsscale) +{ + value = ceil(value); + if(value != 0) + if(lp->columns_scaled && is_integerscaling(lp)) { + value = scaled_value(lp, value, colnr); + if(epsscale != 0) + value -= epsscale*lp->epsmachine; +/* value -= epsscale*lp->epsprimal; */ +/* value = restoreINT(value, lp->epsint); */ + } + return(value); +} + +/* Branch and bound variable selection functions */ + +STATIC MYBOOL is_sc_violated(lprec *lp, int column) +{ + int varno; + LPSREAL tmpreal; + + varno = lp->rows+column; + tmpreal = unscaled_value(lp, lp->sc_lobound[column], varno); + return( (MYBOOL) ((tmpreal > 0) && /* it is an (inactive) SC variable... */ + (lp->solution[varno] < tmpreal) && /* ...and the NZ lower bound is violated */ + (lp->solution[varno] > 0)) ); /* ...and the Z lowerbound is violated */ +} +STATIC int find_sc_bbvar(lprec *lp, int *count) +{ + int i, ii, n, bestvar; + int firstsc, lastsc; + LPSREAL hold, holdINT, bestval, OFval, randval, scval; + MYBOOL reversemode, greedymode, randomizemode, + pseudocostmode, pseudocostsel; + + bestvar = 0; + if((lp->sc_vars == 0) || (*count > 0)) + return(bestvar); + + reversemode = is_bb_mode(lp, NODE_WEIGHTREVERSEMODE); + greedymode = is_bb_mode(lp, NODE_GREEDYMODE); + randomizemode = is_bb_mode(lp, NODE_RANDOMIZEMODE); + pseudocostmode = is_bb_mode(lp, NODE_PSEUDOCOSTMODE); + pseudocostsel = is_bb_rule(lp, NODE_PSEUDOCOSTSELECT) || + is_bb_rule(lp, NODE_PSEUDONONINTSELECT) || + is_bb_rule(lp, NODE_PSEUDORATIOSELECT); + + bestvar = 0; + bestval = -lp->infinite; + hold = 0; + randval = 1; + firstsc = 0; + lastsc = lp->columns; + + for(n = 1; n <= lp->columns; n++) { + ii = get_var_priority(lp, n); + i = lp->rows + ii; + if(!lp->bb_varactive[ii] && is_sc_violated(lp, ii) && !SOS_is_marked(lp->SOS, 0, ii)) { + + /* Do tallies */ + (*count)++; + lastsc = i; + if(firstsc <= 0) + firstsc = i; + scval = get_pseudorange(lp->bb_PseudoCost, ii, BB_SC); + + /* Select default pricing/weighting mode */ + if(pseudocostmode) + OFval = get_pseudonodecost(lp->bb_PseudoCost, ii, BB_SC, lp->solution[i]); + else + OFval = my_chsign(is_maxim(lp), get_mat(lp, 0, ii)); + + if(randomizemode) + randval = exp(rand_uniform(lp, 1.0)); + + /* Find the maximum pseudo-cost of a variable (don't apply pseudocostmode here) */ + if(pseudocostsel) { + if(pseudocostmode) + hold = OFval; + else + hold = get_pseudonodecost(lp->bb_PseudoCost, ii, BB_SC, lp->solution[i]); + hold *= randval; + if(greedymode) { + if(pseudocostmode) /* Override! */ + OFval = my_chsign(is_maxim(lp), get_mat(lp, 0, ii)); + hold *= OFval; + } + hold = my_chsign(reversemode, hold); + } + else + /* Find the variable with the largest sc gap (closest to the sc mean) */ + if(is_bb_rule(lp, NODE_FRACTIONSELECT)) { + hold = modf(lp->solution[i]/scval, &holdINT); + holdINT = hold-1; + if(fabs(holdINT) > hold) + hold = holdINT; + if(greedymode) + hold *= OFval; + hold = my_chsign(reversemode, hold)*scval*randval; + } + else + /* Do first or last violated sc index selection (default) */ + /* if(is_bb_rule(lp, NODE_FIRSTSELECT)) */ + { + if(reversemode) + continue; + else { + bestvar = i; + break; + } + } + + /* Select better, check for ties, and split by proximity to 0.5*sc_lobound */ + if(hold > bestval) { + if( (bestvar == 0) || + (hold > bestval+lp->epsprimal) || + (fabs(modf(lp->solution[i]/scval, &holdINT) - 0.5) < + fabs(modf(lp->solution[bestvar]/ + get_pseudorange(lp->bb_PseudoCost, bestvar-lp->rows, BB_SC), &holdINT) - 0.5)) ) { + bestval = hold; + bestvar = i; + } + } + } + } + + if(is_bb_rule(lp, NODE_FIRSTSELECT) && reversemode) + bestvar = lastsc; + + return(bestvar); +} + +STATIC int find_sos_bbvar(lprec *lp, int *count, MYBOOL intsos) +{ + int k, i, j, var; + + var = 0; + if((lp->SOS == NULL) || (*count > 0)) + return(var); + + /* Check if the SOS'es happen to already be satisified */ + i = SOS_is_satisfied(lp->SOS, 0, lp->solution); + if((i == SOS_COMPLETE) || (i == SOS_INCOMPLETE)) + return(-1); + + /* Otherwise identify a SOS variable to enter B&B */ + for(k = 0; k < lp->sos_vars; k++) { + i = lp->sos_priority[k]; +#ifdef Paranoia + if((i < 1) || (i > lp->columns)) + report(lp, SEVERE, "find_sos_bbvar: Invalid SOS variable map %d at %d\n", + i, k); +#endif + j = lp->rows + i; + if(!SOS_is_marked(lp->SOS, 0, i) && !SOS_is_full(lp->SOS, 0, i, FALSE)) { +/* if(!SOS_is_marked(lp->SOS, 0, i) && !SOS_is_full(lp->SOS, 0, i, TRUE)) { */ + if(!intsos || is_int(lp, i)) { + (*count)++; + if(var == 0) { + var = j; + break; + } + } + } + } +#ifdef Paranoia + if((var > 0) && !SOS_is_member(lp->SOS, 0, var-lp->rows)) + report(lp, SEVERE, "find_sos_bbvar: Found variable %d, which is not a SOS!\n", var); +#endif + return(var); +} + +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, + *lowbo = BB->lowbo, *upbo = BB->upbo; + MYBOOL reversemode, greedymode, depthfirstmode, breadthfirstmode, + randomizemode, rcostmode, + pseudocostmode, pseudocostsel, pseudostrong, isINT, valINT; + + if((lp->int_vars == 0) || (*count > 0)) + return( 0 ); + if(lp->bb_usenode != NULL) { + i = lp->bb_usenode(lp, lp->bb_nodehandle, BB_INT); + if(i >= 0) { + if(i > 0) + (*count)++; + return( i ); + } + } + + reversemode = is_bb_mode(lp, NODE_WEIGHTREVERSEMODE); + greedymode = is_bb_mode(lp, NODE_GREEDYMODE); + randomizemode = is_bb_mode(lp, NODE_RANDOMIZEMODE); + 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 */ + pseudocostmode = is_bb_mode(lp, NODE_PSEUDOCOSTMODE); + pseudocostsel = is_bb_rule(lp, NODE_PSEUDOCOSTSELECT) || + is_bb_rule(lp, NODE_PSEUDONONINTSELECT) || + is_bb_rule(lp, NODE_PSEUDORATIOSELECT); + pseudostrong = FALSE && + pseudocostsel && !rcostmode && is_bb_mode(lp, NODE_STRONGINIT); + + /* Fill list of non-ints */ + allocINT(lp, &nonint, lp->columns + 1, FALSE); + n = 0; + depthmax = -1; + if(isfeasible != NULL) + *isfeasible = TRUE; + BB->lastrcf = 0; + for(k = 1; (k <= lp->columns); k++) { + ii = get_var_priority(lp, k); + isINT = is_int(lp,ii); + i = lp->rows + ii; + + /* Tally reduced cost fixing opportunities for ranged non-basic nonINTs */ + if(!isINT) { +#ifdef UseMilpExpandedRCF + if(rcostmode) { + bestvar = rcfbound_BB(BB, i, isINT, NULL, isfeasible); + if(bestvar != FR) + BB->lastrcf++; + } +#endif + } + else { + + valINT = solution_is_int(lp, i, FALSE); + + /* Skip already fixed variables */ + if(lowbo[i] == upbo[i]) { + + /* Check for validity */ +#ifdef Paranoia + if(!valINT) { + report(lp, IMPORTANT, + "find_int_bbvar: INT var %d was fixed at %d, but computed as %g at node %.0f\n", + ii, (int) lowbo[i], lp->solution[i], (double) lp->bb_totalnodes); + lp->bb_break = TRUE; + lp->spx_status = UNKNOWNERROR; + bestvar = 0; + goto Done; + } +#endif + } + + /* The variable has not yet been fixed */ + else { + + /* Tally reduced cost fixing opportunities (also when the + variables are integer-valued at the current relaxation) */ + if(rcostmode) { + bestvar = rcfbound_BB(BB, i, isINT, NULL, isfeasible); + if(bestvar != FR) + BB->lastrcf++; + } + else + bestvar = FR; + + /* Only qualify variable as branching node if it is non-integer and + it will not be subsequently fixed via reduced cost fixing logic */ + if(!valINT && (bestvar >= FR)) { + + n++; + nonint[n] = ii; + SETMAX(depthmax, lp->bb_varactive[ii]); + } + } + + } + } + +#ifdef UseMilpSlacksRCF + /* Optionally also tally slacks */ + if(rcostmode) { + for(i = 1; (i <= lp->rows) && (BB->lastrcf == 0); i++) { + /* Skip already fixed slacks (equalities) */ + if(lowbo[i] < upbo[i]) { + bestvar = rcfbound_BB(BB, i, FALSE, NULL, isfeasible); + if(bestvar != FR) + BB->lastrcf++; + } + } + } +#endif + nonint[0] = n; + *count = n; + bestvar = 0; + if(n == 0) /* No non-integers found */ + goto Done; + + bestval = -lp->infinite; + hold = 0; + randval = 1; + + /* Sort non-ints by depth in case we have breadthfirst or depthfirst modes */ + if((lp->bb_level > 1) && (depthmax > 0) && (depthfirstmode || breadthfirstmode)) { + int *depths = NULL; + + /* Fill attribute array and make sure ordinal order breaks ties during sort */ + allocINT(lp, &depths, n + 1, FALSE); + for(i = 1; i <= n; i++) + depths[i] = (depthfirstmode ? n+1-i : i) + (n+1)*lp->bb_varactive[nonint[i]]; + hpsortex(depths, n, 1, sizeof(*nonint), depthfirstmode, compareINT, nonint); + FREE(depths); + } + + /* Do simple firstselect handling */ + if(is_bb_rule(lp, NODE_FIRSTSELECT)) { + if(reversemode) + bestvar = lp->rows + nonint[nonint[0]]; + else + bestvar = lp->rows + nonint[1]; + } + + else for(n = 1; n <= nonint[0]; n++) { + ii = nonint[n]; + i = lp->rows + ii; + + /* Do the naive detection */ + if(n == 1) + bestvar = i; + + /* Should we do a "strong" pseudo-cost initialization or an incremental update? */ + if(pseudostrong && + (MAX(lp->bb_PseudoCost->LOcost[ii].rownr, + lp->bb_PseudoCost->UPcost[ii].rownr) < lp->bb_PseudoCost->updatelimit) && + (MAX(lp->bb_PseudoCost->LOcost[ii].colnr, + lp->bb_PseudoCost->UPcost[ii].colnr) < 5*lp->bb_PseudoCost->updatelimit)) { + strongbranch_BB(lp, BB, ii, BB_INT, nonint[0]); + } + + /* Select default pricing/weighting mode */ + if(pseudocostmode) + OFval = get_pseudonodecost(lp->bb_PseudoCost, ii, BB_INT, lp->solution[i]); + else + OFval = my_chsign(is_maxim(lp), get_mat(lp, 0, ii)); + + if(randomizemode) + randval = exp(rand_uniform(lp, 1.0)); + + /* Find the maximum pseudo-cost of a variable (don't apply pseudocostmode here) */ + if(pseudocostsel) { + if(pseudocostmode) + hold = OFval; + else + hold = get_pseudonodecost(lp->bb_PseudoCost, ii, BB_INT, lp->solution[i]); + hold *= randval; + if(greedymode) { + if(pseudocostmode) /* Override! */ + OFval = my_chsign(is_maxim(lp), get_mat(lp, 0, ii)); + hold *= OFval; + } + hold = my_chsign(reversemode, hold); + } + else + /* Find the variable with the largest gap to its bounds (distance from being fixed) */ + if(is_bb_rule(lp, NODE_GAPSELECT)) { + hold = lp->solution[i]; + holdINT = hold-unscaled_value(lp, upbo[i], i); + hold -= unscaled_value(lp, lowbo[i], i); + if(fabs(holdINT) > hold) + hold = holdINT; + if(greedymode) + hold *= OFval; + hold = my_chsign(reversemode, hold)*randval; + } + else + /* Find the variable with the largest integer gap (closest to 0.5) */ + if(is_bb_rule(lp, NODE_FRACTIONSELECT)) { + hold = modf(lp->solution[i], &holdINT); + holdINT = hold-1; + if(fabs(holdINT) > hold) + hold = holdINT; + if(greedymode) + hold *= OFval; + hold = my_chsign(reversemode, hold)*randval; + } + else + /* Find the "range", most flexible variable */ + if(is_bb_rule(lp, NODE_RANGESELECT)) { + hold = unscaled_value(lp, upbo[i]-lowbo[i], i); + if(greedymode) + hold *= OFval; + hold = my_chsign(reversemode, hold)*randval; + } + + /* Select better, check for ties, and split by proximity to 0.5 */ + if(hold > bestval) { + if( (hold > bestval+lp->epsprimal) || + (fabs(modf(lp->solution[i], &holdINT) - 0.5) < + fabs(modf(lp->solution[bestvar], &holdINT) - 0.5)) ) { + bestval = hold; + bestvar = i; + } + } + } + +Done: + FREE(nonint); + return(bestvar); +} + +STATIC BBPSrec *init_pseudocost(lprec *lp, int pseudotype) +{ + int i; + LPSREAL PSinitUP, PSinitLO; + BBPSrec *newitem; + MYBOOL isPSCount; + + /* Allocate memory */ + newitem = (BBPSrec*) malloc(sizeof(*newitem)); + newitem->lp = lp; + newitem->LOcost = (MATitem*) malloc((lp->columns+1) * sizeof(*newitem->LOcost)); + newitem->UPcost = (MATitem*) malloc((lp->columns+1) * sizeof(*newitem->UPcost)); + newitem->secondary = NULL; + + /* Initialize with OF values */ + newitem->pseodotype = (pseudotype & NODE_STRATEGYMASK); + isPSCount = ((pseudotype & NODE_PSEUDONONINTSELECT) != 0); + for(i = 1; i <= lp->columns; i++) { + newitem->LOcost[i].rownr = 1; /* Actual updates */ + newitem->LOcost[i].colnr = 1; /* Attempted updates */ + newitem->UPcost[i].rownr = 1; + newitem->UPcost[i].colnr = 1; + + /* Initialize with the plain OF value as conventional usage suggests, or + override in case of pseudo-nonint count strategy */ + PSinitUP = my_chsign(is_maxim(lp), get_mat(lp, 0, i)); + PSinitLO = -PSinitUP; + if(isPSCount) { + /* Set default assumed reduction in the number of non-ints by choosing this variable; + KE changed from 0 on 30 June 2004 and made two-sided selectable. Note that the + typical value range is <0..1>, with a positive bias for an "a priori" assumed + fast-converging (low "MIP-complexity") model. Very hard models may require + negative initialized values for one or both. */ + PSinitUP = 0.1*0; +#if 0 + PSinitUP = my_chsign(PSinitUP < 0, PSinitUP); + PSinitLO = -PSinitUP; +#else + PSinitLO = PSinitUP; +#endif + } + newitem->UPcost[i].value = PSinitUP; + newitem->LOcost[i].value = PSinitLO; + } + newitem->updatelimit = lp->bb_PseudoUpdates; + newitem->updatesfinished = 0; + newitem->restartlimit = DEF_PSEUDOCOSTRESTART; + + /* Let the user get an opportunity to initialize pseudocosts */ + if(userabort(lp, MSG_INITPSEUDOCOST)) + lp->spx_status = USERABORT; + + return( newitem ); +} + +STATIC MYBOOL free_pseudoclass(BBPSrec **PseudoClass) +{ + BBPSrec *target = *PseudoClass; + + FREE(target->LOcost); + FREE(target->UPcost); + target = target->secondary; + FREE(*PseudoClass); + *PseudoClass = target; + + return( (MYBOOL) (target != NULL) ); +} + +STATIC void free_pseudocost(lprec *lp) +{ + if((lp != NULL) && (lp->bb_PseudoCost != NULL)) { + while(free_pseudoclass(&(lp->bb_PseudoCost)) ); + } +} + +MYBOOL __WINAPI set_pseudocosts(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int *updatelimit) +{ + int i; + + if((lp->bb_PseudoCost == NULL) || ((clower == NULL) && (cupper == NULL))) + return(FALSE); + for(i = 1; i <= lp->columns; i++) { + if(clower != NULL) + lp->bb_PseudoCost->LOcost[i].value = clower[i]; + if(cupper != NULL) + lp->bb_PseudoCost->UPcost[i].value = cupper[i]; + } + if(updatelimit != NULL) + lp->bb_PseudoCost->updatelimit = *updatelimit; + return(TRUE); +} + +MYBOOL __WINAPI get_pseudocosts(lprec *lp, LPSREAL *clower, LPSREAL *cupper, int *updatelimit) +{ + int i; + + if((lp->bb_PseudoCost == NULL) || ((clower == NULL) && (cupper == NULL))) + return(FALSE); + for(i = 1; i <= lp->columns; i++) { + if(clower != NULL) + clower[i] = lp->bb_PseudoCost->LOcost[i].value; + if(cupper != NULL) + cupper[i] = lp->bb_PseudoCost->UPcost[i].value; + } + if(updatelimit != NULL) + *updatelimit = lp->bb_PseudoCost->updatelimit; + return(TRUE); +} + +STATIC LPSREAL 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) ); + else + return( 1.0 ); +} + +STATIC void update_pseudocost(BBPSrec *pc, int mipvar, int varcode, MYBOOL capupper, LPSREAL varsol) +{ + LPSREAL OFsol, uplim; + MATitem *PS; + MYBOOL nonIntSelect = is_bb_rule(pc->lp, NODE_PSEUDONONINTSELECT); + + /* Establish input values; + Note: The pseudocosts are normalized to the 0-1 range! */ + uplim = get_pseudorange(pc, mipvar, varcode); + varsol = modf(varsol/uplim, &OFsol); + + /* Set reference value according to pseudocost mode */ + if(nonIntSelect) + OFsol = pc->lp->bb_bounds->lastvarcus; /* The count of MIP infeasibilities */ + else + OFsol = pc->lp->solution[0]; /* The problem's objective function value */ + + if(isnan(varsol)) { + pc->lp->bb_parentOF = OFsol; + return; + } + + /* Point to the applicable (lower or upper) bound and increment attempted update count */ + if(capupper) { + PS = &pc->LOcost[mipvar]; + } + else { + PS = &pc->UPcost[mipvar]; + varsol = 1-varsol; + } + PS->colnr++; + + /* Make adjustment to divisor if we are using the ratio pseudo-cost approach */ + if(is_bb_rule(pc->lp, NODE_PSEUDORATIOSELECT)) + varsol *= capupper; + + /* Compute the update (consider weighting in favor of most recent) */ + mipvar = pc->updatelimit; + if(((mipvar <= 0) || (PS->rownr < mipvar)) && + (fabs(varsol) > pc->lp->epspivot)) { + /* We are interested in the change in the MIP measure (contribution to increase + or decrease, as the case may be) and not its last value alone. */ + PS->value = PS->value*PS->rownr + (pc->lp->bb_parentOF-OFsol) / (varsol*uplim); + PS->rownr++; + PS->value /= PS->rownr; + /* Check if we have enough information to restart */ + if(PS->rownr == mipvar) { + pc->updatesfinished++; + if(is_bb_mode(pc->lp, NODE_RESTARTMODE) && + (pc->updatesfinished/(2.0*pc->lp->int_vars) > + pc->restartlimit)) { + pc->lp->bb_break = AUTOMATIC; + pc->restartlimit *= 2.681; /* KE: Who can figure this one out? */ + if(pc->restartlimit > 1) + pc->lp->bb_rule -= NODE_RESTARTMODE; + report(pc->lp, NORMAL, "update_pseudocost: Restarting with updated pseudocosts\n"); + } + } + } + pc->lp->bb_parentOF = OFsol; +} + +STATIC LPSREAL get_pseudobranchcost(BBPSrec *pc, int mipvar, MYBOOL dofloor) +{ + if(dofloor) + return( pc->LOcost[mipvar].value ); + else + return( pc->UPcost[mipvar].value ); +} + +STATIC LPSREAL get_pseudonodecost(BBPSrec *pc, int mipvar, int vartype, LPSREAL varsol) +{ + LPSREAL hold, uplim; + + uplim = get_pseudorange(pc, mipvar, vartype); + varsol = modf(varsol/uplim, &hold); + if(isnan(varsol)) + varsol = 0; + + hold = pc->LOcost[mipvar].value*varsol + + pc->UPcost[mipvar].value*(1-varsol); + + return( hold*uplim ); +} + +STATIC int compute_theta(lprec *lp, int rownr, LREAL *theta, int isupbound, LPSREAL 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 */ + ub = lp->upbo[colnr], + eps = lp->epsprimal; /* Primal feasibility tolerance */ + + /* Compute theta for the primal simplex */ + HarrisScalar *= eps; + if(primal) { + + if(*theta > 0) + x -= lb - HarrisScalar; /* A positive number */ + else if(ub < lp->infinite) + x -= ub + HarrisScalar; /* A negative number */ + else { + *theta = -lp->infinite; + return( colnr ); + } + } + /* Compute theta for the dual simplex */ + else { + + if(isupbound) + *theta = -(*theta); + + /* Current value is below or equal to its lower bound */ + if(x < lb+eps) + x -= lb - HarrisScalar; + + /* Current value is above or equal to its upper bound */ + else if(x > ub-eps) { + if(ub >= lp->infinite) { + *theta = lp->infinite * my_sign(*theta); + return( colnr ); + } + else + x -= ub + HarrisScalar; + } + } + my_roundzero(x, lp->epsmachine); + *theta = x / *theta; + +#ifdef EnforcePositiveTheta + /* Check if we have negative theta due to rounding or an internal error */ + if(*theta < 0) { + if(primal && (ub == lb)) + lp->rhs[rownr] = lb; + else +#ifdef Paranoia + if(*theta < -eps) { + report(lp, DETAILED, "compute_theta: Negative theta (%g) not allowed in base-0 version of lp_solve\n", + *theta); + } +#endif + *theta = 0; + } +#endif + + return( colnr ); +} + +STATIC MYBOOL check_degeneracy(lprec *lp, LPSREAL *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; + + sdegen = 0; + ndegen = 0; + rhs = lp->rhs; + for(i = 1; i <= lp->rows; i++) { + rhs++; + pcol++; + if(fabs(*rhs) < epsmargin) { + sdegen += *pcol; + ndegen++; + } + else if(fabs((*rhs)-lp->upbo[lp->var_basic[i]]) < epsmargin) { + sdegen -= *pcol; + ndegen++; + } + } + if(degencount != NULL) + *degencount = ndegen; +/* sdegen += epsmargin*ndegen; */ + return( (MYBOOL) (sdegen <= 0) ); +} + +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) +{ + int varout; + LPSREAL pivot, epsmargin, leavingValue, leavingUB, enteringUB; + MYBOOL leavingToUB = FALSE, enteringFromUB, enteringIsFixed, leavingIsFixed; + MYBOOL *islower = &(lp->is_lower[varin]); + MYBOOL minitNow = FALSE, minitStatus = ITERATE_MAJORMAJOR; + LREAL deltatheta = theta; + + if(userabort(lp, MSG_ITERATION)) + return( minitNow ); + +#ifdef Paranoia + if(rownr > lp->rows) { + if (lp->spx_trace) + report(lp, IMPORTANT, "performiteration: Numeric instability encountered!\n"); + lp->spx_status = NUMFAILURE; + return( FALSE ); + } +#endif + varout = lp->var_basic[rownr]; +#ifdef Paranoia + if(!lp->is_lower[varout]) + report(lp, SEVERE, "performiteration: Leaving variable %d was at its upper bound at iter %.0f\n", + varout, (double) get_total_iter(lp)); +#endif + + /* Theta is the largest change possible (strictest constraint) for the entering + variable (Theta is Chvatal's "t", ref. Linear Programming, pages 124 and 156) */ + lp->current_iter++; + + /* Test if it is possible to do a cheap "minor iteration"; i.e. set entering + variable to its opposite bound, without entering the basis - which is + obviously not possible for fixed variables! */ + epsmargin = lp->epsprimal; + enteringFromUB = !(*islower); + enteringUB = lp->upbo[varin]; + 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", + varin, (double) get_total_iter(lp)); + if(leavingUB < 0) + report(lp, SEVERE, "performiteration: Negative range for leaving variable %d at iter %.0f\n", + varout, (double) get_total_iter(lp)); +#endif + + /* Handle batch bound swaps with the dual long-step algorithm; + Loop over specified bound swaps; update RHS and Theta for bound swaps */ + if((boundswaps != NULL) && (boundswaps[0] > 0)) { + + int i, boundvar; + LPSREAL *hold; + + /* Allocate and initialize accumulation array */ + allocREAL(lp, &hold, lp->rows + 1, TRUE); + + /* Accumulate effective bound swaps and update flag */ + for(i = 1; i <= boundswaps[0]; i++) { + boundvar = boundswaps[i]; + deltatheta = my_chsign(!lp->is_lower[boundvar], lp->upbo[boundvar]); + mat_multadd(lp->matA, hold, boundvar, deltatheta); + lp->is_lower[boundvar] = !lp->is_lower[boundvar]; + } + lp->current_bswap += boundswaps[0]; + lp->current_iter += boundswaps[0]; + + /* Solve for bound flip update vector (note that this does not + overwrite the stored update vector for the entering variable) */ + ftran(lp, hold, NULL, lp->epsmachine); + if(!lp->obj_in_basis) + hold[0] = 0; /* The correct reduced cost goes here (adjusted for bound state) ****** */ + + /* Update the RHS / basic variable values and set revised thetas */ + pivot = lp->bfp_pivotRHS(lp, 1, hold); + deltatheta = multi_enteringtheta(lp->longsteps); + theta = deltatheta; + + FREE(hold); + } + + /* Otherwise to traditional check for single bound swap */ + else if(allowminit && + !enteringIsFixed) { + +/* pivot = epsmargin; */ + pivot = lp->epsdual; +/* #define v51mode */ /* Enable this for v5.1 operation mode */ +#ifdef v51mode + if(((lp->simplex_mode & SIMPLEX_Phase1_DUAL) == 0) || + !is_constr_type(lp, rownr, EQ)) /* *** DEBUG CODE KE */ +#endif + if(enteringUB - theta < -pivot) { + +#ifndef v51mode + if(fabs(enteringUB - theta) < pivot) + minitStatus = ITERATE_MINORMAJOR; + else +#endif + minitStatus = ITERATE_MINORRETRY; + minitNow = (MYBOOL) (minitStatus != ITERATE_MAJORMAJOR); + } + } + + /* Process for traditional style single minor iteration */ + if(minitNow) { + + /* Set the new values (note that theta is set to always be positive) */ + theta = MIN(fabs(theta), enteringUB); + + /* Update the RHS / variable values and do bound-swap */ + pivot = lp->bfp_pivotRHS(lp, theta, NULL); + *islower = !(*islower); + + lp->current_bswap++; + + } + + /* Process for major iteration */ + else { + + /* Update the active pricer for the current pivot */ + updatePricer(lp, rownr, varin, lp->bfp_pivotvector(lp), prow, nzprow); + + /* Update the current basic variable values */ + pivot = lp->bfp_pivotRHS(lp, theta, NULL); + + /* See if the leaving variable goes directly to its upper bound. */ + leavingValue = lp->rhs[rownr]; + leavingToUB = (MYBOOL) (leavingValue > 0.5*leavingUB); + lp->is_lower[varout] = leavingIsFixed || !leavingToUB; + + /* Set the value of the entering varible (theta always set to be positive) */ + if(enteringFromUB) { + lp->rhs[rownr] = enteringUB - deltatheta; + *islower = TRUE; + } + else + lp->rhs[rownr] = deltatheta; + my_roundzero(lp->rhs[rownr], epsmargin); + + /* Update basis indeces */ + varout = set_basisvar(lp, rownr, varin); + + /* Finalize the update in preparation for next major iteration */ + lp->bfp_finishupdate(lp, enteringFromUB); + + } + + /* Show pivot tracking information, if specified */ + if((lp->verbose > NORMAL) && (MIP_count(lp) == 0) && + ((lp->current_iter % MAX(2, lp->rows / 10)) == 0)) + report(lp, NORMAL, "Objective value " RESULTVALUEMASK " at iter %10.0f.\n", + lp->rhs[0], (double) get_total_iter(lp)); + +#if 0 + if(verify_solution(lp, FALSE, my_if(minitNow, "MINOR", "MAJOR")) >= 0) { + if(minitNow) + pivot = get_obj_active(lp, varin); + else + pivot = get_obj_active(lp, varout); + } +#endif +#if 0 + if((lp->longsteps != NULL) && (boundswaps[0] > 0) && lp->longsteps->objcheck && + ((pivot = fabs(my_reldiff(lp->rhs[0], lp->longsteps->obj_last))) > lp->epssolution)) { + report(lp, IMPORTANT, "performiteration: Objective value gap %8.6f found at iter %6.0f (%d bound flips, %d)\n", + pivot, (double) get_total_iter(lp), boundswaps[0], enteringFromUB); + } +#endif + + if(lp->spx_trace) { + if(minitNow) + report(lp, NORMAL, "I:%5.0f - minor - %5d ignored, %5d flips from %s with THETA=%g and OBJ=%g\n", + (double) get_total_iter(lp), varout, varin, (enteringFromUB ? "UPPER" : "LOWER"), theta, lp->rhs[0]); + else + report(lp, NORMAL, "I:%5.0f - MAJOR - %5d leaves to %s, %5d enters from %s with THETA=%g and OBJ=%g\n", + (double) get_total_iter(lp), varout, (leavingToUB ? "UPPER" : "LOWER"), + varin, (enteringFromUB ? "UPPER" : "LOWER"), theta, lp->rhs[0]); + if(minitNow) { + if(!lp->is_lower[varin]) + report(lp, DETAILED, + "performiteration: Variable %d changed to its lower bound at iter %.0f (from %g)\n", + varin, (double) get_total_iter(lp), enteringUB); + else + report(lp, DETAILED, + "performiteration: Variable %d changed to its upper bound at iter %.0f (to %g)\n", + varin, (double) get_total_iter(lp), enteringUB); + } + else + report(lp, NORMAL, + "performiteration: Variable %d entered basis at iter %.0f at " RESULTVALUEMASK "\n", + varin, (double) get_total_iter(lp), lp->rhs[rownr]); + if(!primal) { + pivot = compute_feasibilitygap(lp, (MYBOOL)!primal, TRUE); + report(lp, NORMAL, "performiteration: Feasibility gap at iter %.0f is " RESULTVALUEMASK "\n", + (double) get_total_iter(lp), pivot); + } + else + report(lp, NORMAL, + "performiteration: Current objective function value at iter %.0f is " RESULTVALUEMASK "\n", + (double) get_total_iter(lp), lp->rhs[0]); + } + + return( minitStatus ); + +} /* performiteration */ + +STATIC LPSREAL get_refactfrequency(lprec *lp, MYBOOL final) +{ + COUNTER iters; + int refacts; + + /* Get numerator and divisor information */ + iters = (lp->total_iter+lp->current_iter) - (lp->total_bswap+lp->current_bswap); + refacts = lp->bfp_refactcount(lp, BFP_STAT_REFACT_TOTAL); + + /* Return frequency for different cases: + 1) Actual frequency in case final statistic is desired + 2) Dummy if we are in a B&B process + 3) Frequency with added initialization offsets which + are diluted in course of the solution process */ + if(final) + return( (LPSREAL) (iters) / MAX(1,refacts) ); + else if(lp->bb_totalnodes > 0) + return( (LPSREAL) lp->bfp_pivotmax(lp) ); + else + return( (LPSREAL) (lp->bfp_pivotmax(lp)+iters) / (1+refacts) ); +} + +#if 0 +/* INLINE */ 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 */ +#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) +{ +#if 1 + return( (MYBOOL) (isINT(lp, lp->solution[index]) && (!checkfixed || is_fixedvar(lp, index))) ); +#else + if(isINT(lp, lp->solution[index])) { + if(checkfixed) + return(is_fixedvar(lp, index)); + else + return(TRUE); + } + return(FALSE); +#endif +} /* solution_is_int */ + + +MYBOOL __WINAPI set_multiprice(lprec *lp, int multiblockdiv) +{ + /* See if we are resetting multiply priced column structures */ + if(multiblockdiv != lp->multiblockdiv) { + if(multiblockdiv < 1) + multiblockdiv = 1; + lp->multiblockdiv = multiblockdiv; + multi_free(&(lp->multivars)); + } + return( TRUE ); +} + +int __WINAPI get_multiprice(lprec *lp, MYBOOL getabssize) +{ + if((lp->multivars == NULL) || (lp->multivars->used == 0)) + return( 0 ); + if(getabssize) + return( lp->multivars->size ); + else + return( lp->multiblockdiv ); +} + +MYBOOL __WINAPI set_partialprice(lprec *lp, int blockcount, int *blockstart, MYBOOL isrow) +{ + int ne, i, items; + partialrec **blockdata; + + /* Determine partial target (rows or columns) */ + if(isrow) + blockdata = &(lp->rowblocks); + else + blockdata = &(lp->colblocks); + + /* See if we are resetting partial blocks */ + ne = 0; + items = IF(isrow, lp->rows, lp->columns); + if(blockcount == 1) + partial_freeBlocks(blockdata); + + /* Set a default block count if this was not specified */ + else if(blockcount <= 0) { + blockstart = NULL; + if(items < DEF_PARTIALBLOCKS*DEF_PARTIALBLOCKS) + blockcount = items / DEF_PARTIALBLOCKS + 1; + else + blockcount = DEF_PARTIALBLOCKS; + ne = items / blockcount; + if(ne * blockcount < items) + ne++; + } + + /* Fill partial block arrays; + Note: These will be modified during preprocess to reflect + presolved columns and the handling of slack variables. */ + if(blockcount > 1) { + MYBOOL isNew = (MYBOOL) (*blockdata == NULL); + + /* Provide for extra block with slack variables in the column mode */ + i = 0; + if(!isrow) + i++; + + /* (Re)-allocate memory */ + if(isNew) + *blockdata = partial_createBlocks(lp, isrow); + allocINT(lp, &((*blockdata)->blockend), blockcount+i+1, AUTOMATIC); + allocINT(lp, &((*blockdata)->blockpos), blockcount+i+1, AUTOMATIC); + + /* Copy the user-provided block start positions */ + if(blockstart != NULL) { + MEMCOPY((*blockdata)->blockend+i, blockstart, blockcount+i+1); + if(!isrow) { + blockcount++; + (*blockdata)->blockend[0] = 1; + for(i = 1; i < blockcount; i++) + (*blockdata)->blockend[i] += lp->rows; + } + } + + /* Fill the block ending positions if they were not specified */ + else { + (*blockdata)->blockend[0] = 1; + (*blockdata)->blockpos[0] = 1; + if(ne == 0) { + ne = items / blockcount; + /* Increase the block size if we have a fractional value */ + while(ne * blockcount < items) + ne++; + } + i = 1; + if(!isrow) { + (*blockdata)->blockend[i] = (*blockdata)->blockend[i-1]+lp->rows; + blockcount++; + i++; + items += lp->rows; + } + for(; i < blockcount; i++) + (*blockdata)->blockend[i] = (*blockdata)->blockend[i-1]+ne; + + /* Let the last block handle the "residual" */ + (*blockdata)->blockend[blockcount] = items+1; + } + + /* Fill starting positions (used in multiple partial pricing) */ + for(i = 1; i <= blockcount; i++) + (*blockdata)->blockpos[i] = (*blockdata)->blockend[i-1]; + + } + + /* Update block count */ + (*blockdata)->blockcount = blockcount; + + + return( TRUE ); +} /* set_partialprice */ + +void __WINAPI get_partialprice(lprec *lp, int *blockcount, int *blockstart, MYBOOL isrow) +{ + partialrec *blockdata; + + /* Determine partial target (rows or columns) */ + if(isrow) + blockdata = lp->rowblocks; + else + blockdata = lp->colblocks; + + *blockcount = partial_countBlocks(lp, isrow); + if((blockdata != NULL) && (blockstart != NULL)) { + int i = 0, k = *blockcount; + if(!isrow) + i++; + MEMCOPY(blockstart, blockdata->blockend + i, k - i); + if(!isrow) { + k -= i; + for(i = 0; i < k; i++) + blockstart[i] -= lp->rows; + } + } +} + + +/* Solution-related functions */ +STATIC MYBOOL bb_better(lprec *lp, int target, int mode) +/* Must handle four modes (logic assumes Min!): + -----|--.--|-----> + 1 ++++++----------- LHS exclusive test point is better + 2 +++++++++-------- LHS inclusive + 3 ++++++-----++++++ LHS+RHS exclusive + 4 --------+++++++++ RHS inclusive + 5 -----------++++++ RHS exclusive +*/ +{ + LPSREAL epsvalue, offset = lp->epsprimal, + refvalue = lp->infinite, testvalue = lp->solution[0]; + MYBOOL ismax = is_maxim(lp), + relgap = is_action(mode, OF_TEST_RELGAP), + fcast = is_action(target, OF_PROJECTED), + delta = is_action(target, OF_DELTA); + + if(relgap) { + epsvalue = lp->mip_relgap; + clear_action(&mode, OF_TEST_RELGAP); + } + else + epsvalue = lp->mip_absgap; + + if(delta) + clear_action(&target, OF_DELTA); + if(fcast) + clear_action(&target, OF_PROJECTED); +#ifdef Paranoia + if((mode < OF_TEST_BT) || (mode > OF_TEST_WT)) + report(lp, SEVERE, "bb_better: Passed invalid mode '%d'\n", mode); +#endif + + switch(target) { + case OF_RELAXED: refvalue = lp->real_solution; + 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) */ ); + 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) */); + break; + case OF_USERBREAK: refvalue = lp->bb_breakOF; + break; + case OF_HEURISTIC: refvalue = lp->bb_heuristicOF; + break; + case OF_DUALLIMIT: refvalue = lp->bb_limitOF; + break; + default : report(lp, SEVERE, "bb_better: Passed invalid test target '%d'\n", target); + return( FALSE ); + } + + /* Adjust the test value for the desired acceptability window */ + if(delta) { + SETMAX(epsvalue, lp->bb_deltaOF - epsvalue); + } + else + epsvalue = my_chsign(target >= OF_USERBREAK, epsvalue); /* *** This seems Ok, but should be verified */ + testvalue += my_chsign(ismax, epsvalue); + + /* Compute the raw test value */ + if(relgap) + testvalue = my_reldiff(testvalue, refvalue); + else + testvalue -= refvalue; + + /* Make test value adjustment based on the selected option */ + if(mode == OF_TEST_NE) + relgap = (MYBOOL) (fabs(testvalue) >= offset); + else { + testvalue = my_chsign(mode > OF_TEST_NE, testvalue); + testvalue = my_chsign(ismax, testvalue); + relgap = (MYBOOL) (testvalue < offset); + } + return( relgap ); +} + +STATIC void construct_solution(lprec *lp, LPSREAL *target) +{ + int i, j, basi; + LPSREAL f, epsvalue = lp->epsprimal; + LPSREAL *solution; + LPSREAL *value; + int *rownr; + MATrec *mat = lp->matA; + + if(target == NULL) + solution = lp->solution; + else + solution = target; + + /* Initialize OF and slack variables. */ + for(i = 0; i <= lp->rows; i++) { +#ifdef LegacySlackDefinition + if(i == 0) + f = unscaled_value(lp, -lp->orig_rhs[i], i); + else { + j = lp->presolve_undo->var_to_orig[i]; + if(j > 0) { + f = lp->presolve_undo->fixed_rhs[j]; + f = unscaled_value(lp, f, i); + } + else + f = 0; + } +#else + f = lp->orig_rhs[i]; + if((i > 0) && !lp->is_basic[i] && !lp->is_lower[i]) +#ifdef SlackInitMinusInf + f -= my_chsign(is_chsign(lp, i), fabs(lp->upbo[i])); +#else + f -= my_chsign(is_chsign(lp, i), fabs(lp->lowbo[i] + lp->upbo[i])); +#endif + f = unscaled_value(lp, -f, i); +#endif + solution[i] = f; + } + + /* Initialize user variables to their lower bounds. */ + for(i = lp->rows+1; i <= lp->sum; i++) + solution[i] = lp->lowbo[i]; + + /* Add values of user basic variables. */ + for(i = 1; i <= lp->rows; i++) { + basi = lp->var_basic[i]; + if(basi > lp->rows) { + solution[basi] += lp->rhs[i]; + } + } + + /* 1. Adjust non-basic variables at their upper bounds, + 2. Unscale all user variables, + 3. Optionally do precision management. */ + for(i = lp->rows + 1; i <= lp->sum; i++) { + if(!lp->is_basic[i] && !lp->is_lower[i]) + solution[i] += lp->upbo[i]; + solution[i] = unscaled_value(lp, solution[i], i); +#ifdef xImproveSolutionPrecision + if(is_int(lp, i-lp->rows)) + solution[i] = restoreINT(solution[i], lp->epsint); + else + solution[i] = restoreINT(solution[i], lp->epsprimal); +#endif + } + + /* Compute the OF and slack values "in extentio" */ + for(j = 1; j <= lp->columns; j++) { + f = solution[lp->rows + j]; + if(f != 0) { + solution[0] += f * unscaled_mat(lp, lp->orig_obj[j], 0, j); + i = mat->col_end[j-1]; + basi = mat->col_end[j]; + rownr = &COL_MAT_ROWNR(i); + value = &COL_MAT_VALUE(i); + for(; i < basi; + i++, rownr += matRowColStep, value += matValueStep) + solution[*rownr] += f * unscaled_mat(lp, *value, *rownr, j); + } + } + + /* Do slack precision management and sign reversal if necessary */ + for(i = 0; i <= lp->rows; i++) { +#ifdef ImproveSolutionPrecision + my_roundzero(solution[i], epsvalue); +#endif + if(is_chsign(lp, i)) + solution[i] = my_flipsign(solution[i]); + } + + /* Record the best real-valued solution and compute a simple MIP solution limit */ + if(target == NULL) { + if(is_infinite(lp, lp->real_solution)) { + lp->bb_workOF = lp->rhs[0]; + lp->real_solution = solution[0]; + if(is_infinite(lp, lp->bb_limitOF)) + lp->bb_limitOF = lp->real_solution; + else { + if(is_maxim(lp)) { + SETMIN(lp->bb_limitOF, lp->real_solution); + } + else { + SETMAX(lp->bb_limitOF, lp->real_solution); + } + } + + /* 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); + + /* 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; + } + } + + /* 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); + } + } + } + + /* 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)) { + lp->spx_status = INFEASIBLE; + lp->bb_break = TRUE; + } + } + } + +} /* construct_solution */ + +STATIC int check_solution(lprec *lp, int lastcolumn, LPSREAL *solution, + LPSREAL *upbo, LPSREAL *lowbo, LPSREAL tolerance) +{ +/*#define UseMaxValueInCheck*/ + MYBOOL isSC; + LPSREAL test, value, hold, diff, maxdiff = 0.0, maxerr = 0.0, *matValue, +#ifdef UseMaxValueInCheck + *maxvalue = NULL, +#else + *plusum = NULL, *negsum = NULL; +#endif + int i,j,n, errlevel = IMPORTANT, errlimit = 10, *matRownr, *matColnr; + MATrec *mat = lp->matA; + + 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"), + solution[0], (double) lp->total_iter, (double) lp->bb_totalnodes, + 100.0*fabs(my_reldiff(solution[0], lp->bb_limitOF))); + else + report(lp, NORMAL, "Optimal solution " RESULTVALUEMASK " after %10.0f iter.\n", + solution[0], (double) lp->total_iter); + + /* Find the signed sums and the largest absolute product in the matrix (exclude the OF for speed) */ +#ifdef UseMaxValueInCheck + allocREAL(lp, &maxvalue, lp->rows + 1, FALSE); + for(i = 0; i <= lp->rows; i++) + maxvalue[i] = fabs(get_rh(lp, i)); +#else + allocREAL(lp, &plusum, lp->rows + 1, TRUE); + allocREAL(lp, &negsum, lp->rows + 1, TRUE); +#endif + n = get_nonzeros(lp); + matRownr = &COL_MAT_ROWNR(0); + matColnr = &COL_MAT_COLNR(0); + matValue = &COL_MAT_VALUE(0); + for(i = 0; i < n; i++, matRownr += matRowColStep, + matColnr += matRowColStep, + matValue += matValueStep) { + test = unscaled_mat(lp, *matValue, *matRownr, *matColnr); + test *= solution[lp->rows + (*matColnr)]; +#ifdef UseMaxValueInCheck + test = fabs(test); + if(test > maxvalue[*matRownr]) + maxvalue[*matRownr] = test; +#else + if(test > 0) + plusum[*matRownr] += test; + else + negsum[*matRownr] += test; +#endif + } + + + /* Check if solution values are within the bounds; allowing a margin for numeric errors */ + n = 0; + for(i = lp->rows + 1; i <= lp->rows+lastcolumn; i++) { + + value = solution[i]; + + /* Check for case where we are testing an intermediate solution + (variables shifted to the origin) */ + if(lowbo == NULL) + test = 0; + else + test = unscaled_value(lp, lowbo[i], i); + + isSC = is_semicont(lp, i - lp->rows); + diff = my_reldiff(value, test); + if(diff < 0) { + if(isSC && (value < test/2)) + test = 0; + SETMAX(maxerr, fabs(value-test)); + SETMAX(maxdiff, fabs(diff)); + } + if((diff < -tolerance) && !isSC) { + if(n < errlimit) + report(lp, errlevel, + "check_solution: Variable %s = " RESULTVALUEMASK " is below its lower bound " RESULTVALUEMASK "\n", + get_col_name(lp, i-lp->rows), value, test); + n++; + } + + test = unscaled_value(lp, upbo[i], i); + diff = my_reldiff(value, test); + if(diff > 0) { + SETMAX(maxerr, fabs(value-test)); + SETMAX(maxdiff, fabs(diff)); + } + if(diff > tolerance) { + if(n < errlimit) + report(lp, errlevel, + "check_solution: Variable %s = " RESULTVALUEMASK " is above its upper bound " RESULTVALUEMASK "\n", + get_col_name(lp, i-lp->rows), value, test); + n++; + } + } + + /* Check if constraint values are within the bounds; allowing a margin for numeric errors */ + for(i = 1; i <= lp->rows; i++) { + + test = lp->orig_rhs[i]; + if(is_infinite(lp, test)) + continue; + +#ifdef LegacySlackDefinition + j = lp->presolve_undo->var_to_orig[i]; + if(j != 0) { + if(is_infinite(lp, lp->presolve_undo->fixed_rhs[j])) + continue; + test += lp->presolve_undo->fixed_rhs[j]; + } +#endif + + if(is_chsign(lp, i)) { + test = my_flipsign(test); + test += fabs(upbo[i]); + } + value = solution[i]; + test = unscaled_value(lp, test, i); +#ifndef LegacySlackDefinition + value += test; +#endif +/* diff = my_reldiff(value, test); */ +#ifdef UseMaxValueInCheck + hold = maxvalue[i]; +#else + hold = plusum[i] - negsum[i]; +#endif + if(hold < lp->epsvalue) + hold = 1; + diff = my_reldiff((value+1)/hold, (test+1)/hold); + if(diff > 0) { + SETMAX(maxerr, fabs(value-test)); + SETMAX(maxdiff, fabs(diff)); + } + if(diff > tolerance) { + if(n < errlimit) + report(lp, errlevel, + "check_solution: Constraint %s = " RESULTVALUEMASK " is above its %s " RESULTVALUEMASK "\n", + get_row_name(lp, i), value, + (is_constr_type(lp, i, EQ) ? "equality of" : "upper bound"), test); + n++; + } + + test = lp->orig_rhs[i]; +#ifdef LegacySlackDefinition + j = lp->presolve_undo->var_to_orig[i]; + if(j != 0) { + if(is_infinite(lp, lp->presolve_undo->fixed_rhs[j])) + continue; + test += lp->presolve_undo->fixed_rhs[j]; + } +#endif + + value = solution[i]; + if(is_chsign(lp, i)) + test = my_flipsign(test); + else { + if(is_infinite(lp, upbo[i])) + continue; + test -= fabs(upbo[i]); +#ifndef LegacySlackDefinition + value = fabs(upbo[i]) - value; +#endif + } + test = unscaled_value(lp, test, i); +#ifndef LegacySlackDefinition + value += test; +#endif +/* diff = my_reldiff(value, test); */ +#ifdef UseMaxValueInCheck + hold = maxvalue[i]; +#else + hold = plusum[i] - negsum[i]; +#endif + if(hold < lp->epsvalue) + hold = 1; + diff = my_reldiff((value+1)/hold, (test+1)/hold); + if(diff < 0) { + SETMAX(maxerr, fabs(value-test)); + SETMAX(maxdiff, fabs(diff)); + } + if(diff < -tolerance) { + if(n < errlimit) + report(lp, errlevel, + "check_solution: Constraint %s = " RESULTVALUEMASK " is below its %s " RESULTVALUEMASK "\n", + get_row_name(lp, i), value, + (is_constr_type(lp, i, EQ) ? "equality of" : "lower bound"), test); + n++; + } + } + +#ifdef UseMaxValueInCheck + FREE(maxvalue); +#else + FREE(plusum); + FREE(negsum); +#endif + + if(n > 0) { + report(lp, IMPORTANT, "\nSeriously low accuracy found ||*|| = %g (rel. error %g)\n", + maxerr, maxdiff); + return(NUMFAILURE); + } + else { + if(maxerr > 1.0e-7) + report(lp, NORMAL, "\nMarginal numeric accuracy ||*|| = %g (rel. error %g)\n", + maxerr, maxdiff); + else if(maxerr > 1.0e-9) + report(lp, NORMAL, "\nReasonable numeric accuracy ||*|| = %g (rel. error %g)\n", + maxerr, maxdiff); + else if(maxerr > 1.0e11) + report(lp, NORMAL, "\nVery good numeric accuracy ||*|| = %g\n", maxerr); + else + report(lp, NORMAL, "\nExcellent numeric accuracy ||*|| = %g\n", maxerr); + + return(OPTIMAL); + } + +} /* check_solution */ + +STATIC void transfer_solution_var(lprec *lp, int uservar) +{ + if(lp->varmap_locked && (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)) { + uservar += lp->rows; + lp->full_solution[lp->presolve_undo->orig_rows + + lp->presolve_undo->var_to_orig[uservar]] = lp->best_solution[uservar]; + } +} +STATIC void transfer_solution(lprec *lp, MYBOOL dofinal) +{ + int i, ii; + + MEMCOPY(lp->best_solution, lp->solution, lp->sum + 1); + + /* Round integer solution values to actual integers */ + if(is_integerscaling(lp) && (lp->int_vars > 0)) + for(i = 1; i <= lp->columns; i++) { + if(is_int(lp, i)) { + ii = lp->rows + i; + lp->best_solution[ii] = floor(lp->best_solution[ii] + 0.5); + } + } + + /* Transfer to full solution vector in the case of presolved eliminations */ + if(dofinal && lp->varmap_locked && + (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)) { + presolveundorec *psundo = lp->presolve_undo; + + lp->full_solution[0] = lp->best_solution[0]; + for(i = 1; i <= lp->rows; i++) { + ii = psundo->var_to_orig[i]; +#ifdef Paranoia + if((ii < 0) || (ii > lp->presolve_undo->orig_rows)) + report(lp, SEVERE, "transfer_solution: Invalid mapping of row index %d to original index '%d'\n", + i, ii); +#endif + lp->full_solution[ii] = lp->best_solution[i]; + } + for(i = 1; i <= lp->columns; i++) { + ii = psundo->var_to_orig[lp->rows+i]; +#ifdef Paranoia + if((ii < 0) || (ii > lp->presolve_undo->orig_columns)) + report(lp, SEVERE, "transfer_solution: Invalid mapping of column index %d to original index '%d'\n", + i, ii); +#endif + lp->full_solution[psundo->orig_rows+ii] = lp->best_solution[lp->rows+i]; + } + } + +} + +STATIC MYBOOL construct_duals(lprec *lp) +{ + int i, n, *coltarget; + LPSREAL scale0, value, dualOF; + + if(lp->duals != NULL) + free_duals(lp); + + if(is_action(lp->spx_action, ACTION_REBASE) || + is_action(lp->spx_action, ACTION_REINVERT) || (!lp->basis_valid) || + !allocREAL(lp, &(lp->duals), lp->sum + 1, AUTOMATIC)) + return(FALSE); + + /* Initialize */ + coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->columns+1, sizeof(*coltarget)); + if(!get_colIndexA(lp, SCAN_USERVARS+USE_NONBASICVARS, coltarget, FALSE)) { + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + return(FALSE); + } + bsolve(lp, 0, lp->duals, NULL, lp->epsmachine*DOUBLEROUND, 1.0); + prod_xA(lp, coltarget, lp->duals, NULL, lp->epsmachine, 1.0, + lp->duals, NULL, MAT_ROUNDDEFAULT | MAT_ROUNDRC); + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + + + /* The (Lagrangean) dual values are the reduced costs of the primal slacks; + when the slack is at its upper bound, change the sign. */ + n = lp->rows; + for(i = 1; i <= n; i++) { + if(lp->is_basic[i]) + lp->duals[i] = 0; + /* Added a test if variable is different from 0 because sometime you get -0 and this + is different from 0 on for example INTEL processors (ie 0 != -0 on INTEL !) PN */ + else if((is_chsign(lp, 0) == is_chsign(lp, i)) && lp->duals[i]) + lp->duals[i] = my_flipsign(lp->duals[i]); + } + if(is_maxim(lp)) { + n = lp->sum; + for(i = lp->rows + 1; i <= n; i++) + lp->duals[i] = my_flipsign(lp->duals[i]); + } + + /* If we presolved, then reconstruct the duals */ + n = lp->presolve_undo->orig_sum; + if(((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE) && + allocREAL(lp, &(lp->full_duals), n + 1, TRUE)) { + int ix, ii = lp->presolve_undo->orig_rows; + + n = lp->sum; + for(ix = 1; ix <= n; ix++) { + i = lp->presolve_undo->var_to_orig[ix]; + if(ix > lp->rows) + i += ii; +#ifdef Paranoia + /* Check for index out of range due to presolve */ + if(i > lp->presolve_undo->orig_sum) + report(lp, SEVERE, "construct_duals: Invalid presolve variable mapping found\n"); +#endif + lp->full_duals[i] = lp->duals[ix]; + } + presolve_rebuildUndo(lp, FALSE); + } + + /* Calculate the dual OF and do scaling adjustments to the duals */ + if(lp->scaling_used) + scale0 = lp->scalars[0]; + else + scale0 = 1; + dualOF = my_chsign(is_maxim(lp), lp->orig_rhs[0]) / scale0; + for(i = 1; i <= lp->sum; i++) { + value = scaled_value(lp, lp->duals[i] / scale0, i); + my_roundzero(value, lp->epsprimal); + lp->duals[i] = value; + if(i <= lp->rows) + dualOF += value * lp->solution[i]; + } + +#if 0 + /* See if we can make use of the dual OF; + note that we do not currently adjust properly for presolve */ + if(lp->rows == lp->presolve_undo->orig_rows) + if(MIP_count(lp) > 0) { + if(is_maxim(lp)) { + SETMIN(lp->bb_limitOF, dualOF); + } + else { + SETMAX(lp->bb_limitOF, dualOF); + } + } + else if(fabs(my_reldiff(dualOF, lp->solution[0])) > lp->epssolution) + report(lp, IMPORTANT, "calculate_duals: Check for possible suboptimal solution!\n"); +#endif + + return(TRUE); +} /* construct_duals */ + +/* Calculate sensitivity duals */ +STATIC MYBOOL construct_sensitivity_duals(lprec *lp) +{ + int k,varnr, ok = TRUE; + int *workINT = NULL; + LPSREAL *pcol,a,infinite,epsvalue,from,till,objfromvalue; + + /* one column of the matrix */ + FREE(lp->objfromvalue); + FREE(lp->dualsfrom); + FREE(lp->dualstill); + if(!allocREAL(lp, &pcol, lp->rows + 1, TRUE) || + !allocREAL(lp, &lp->objfromvalue, lp->columns + 1, AUTOMATIC) || + !allocREAL(lp, &lp->dualsfrom, lp->sum + 1, AUTOMATIC) || + !allocREAL(lp, &lp->dualstill, lp->sum + 1, AUTOMATIC)) { + FREE(pcol); + FREE(lp->objfromvalue); + FREE(lp->dualsfrom); + FREE(lp->dualstill); + ok = FALSE; + } + else { + infinite=lp->infinite; + epsvalue=lp->epsmachine; + for(varnr=1; varnr<=lp->sum; varnr++) { + from=infinite; + till=infinite; + objfromvalue=infinite; + if (!lp->is_basic[varnr]) { + if (!fsolve(lp, varnr, pcol, workINT, epsvalue, 1.0, FALSE)) { /* construct one column of the tableau */ + ok = FALSE; + break; + } + /* 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]; + 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]); + 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) && ( ais_lower[varnr]) { + a=from; + from=till; + till=a; + } + if ((varnr<=lp->rows) && (!is_chsign(lp, varnr))) { + a=from; + from=till; + till=a; + } + } + + if (from!=infinite) + lp->dualsfrom[varnr]=lp->solution[varnr]-unscaled_value(lp, from, varnr); + else + lp->dualsfrom[varnr]=-infinite; + if (till!=infinite) + lp->dualstill[varnr]=lp->solution[varnr]+unscaled_value(lp, till, varnr); + 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]; + } + objfromvalue += lp->lowbo[varnr]; + objfromvalue = unscaled_value(lp, objfromvalue, varnr); + } + else + objfromvalue = -infinite; + lp->objfromvalue[varnr - lp->rows] = objfromvalue; + } + + } + FREE(pcol); + } + return((MYBOOL) ok); +} /* construct_sensitivity_duals */ + +/* Calculate sensitivity objective function */ +STATIC MYBOOL construct_sensitivity_obj(lprec *lp) +{ + int i, l, varnr, row_nr, ok = TRUE; + LPSREAL *OrigObj = NULL, *drow = NULL, *prow = NULL, + sign, a, min1, min2, infinite, epsvalue, from, till; + + /* objective function */ + FREE(lp->objfrom); + FREE(lp->objtill); + if(!allocREAL(lp, &drow, lp->sum + 1, TRUE) || + !allocREAL(lp, &OrigObj, lp->columns + 1, FALSE) || + !allocREAL(lp, &prow, lp->sum + 1, TRUE) || + !allocREAL(lp, &lp->objfrom, lp->columns + 1, AUTOMATIC) || + !allocREAL(lp, &lp->objtill, lp->columns + 1, AUTOMATIC)) { +Abandon: + FREE(drow); + FREE(OrigObj); + FREE(prow); + FREE(lp->objfrom); + FREE(lp->objtill); + ok = FALSE; + } + else { + int *coltarget; + + infinite=lp->infinite; + epsvalue=lp->epsmachine; + + coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->columns+1, sizeof(*coltarget)); + if(!get_colIndexA(lp, SCAN_USERVARS+USE_NONBASICVARS, coltarget, FALSE)) { + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + goto Abandon; + } + bsolve(lp, 0, drow, NULL, epsvalue*DOUBLEROUND, 1.0); + prod_xA(lp, coltarget, drow, NULL, epsvalue, 1.0, + drow, NULL, MAT_ROUNDDEFAULT | MAT_ROUNDRC); + + /* original (unscaled) objective function */ + get_row(lp, 0, OrigObj); + for(i = 1; i <= lp->columns; i++) { + from=-infinite; + till= infinite; + varnr = lp->rows + i; + if(!lp->is_basic[varnr]) { + /* only the coeff of the objective function of column i changes. */ + a = unscaled_mat(lp, drow[varnr], 0, i); + if(is_maxim(lp)) + a = -a; + if ((!sensrejvar) && (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)) + from = OrigObj[i] - a; /* less than this value gives further iterations */ + else + till = OrigObj[i] - a; /* bigger than this value gives further iterations */ + } + else { + /* all the coeff of the objective function change. Search the minimal change needed for further iterations */ + for(row_nr=1; + (row_nr<=lp->rows) && (lp->var_basic[row_nr]!=varnr); row_nr++) + /* Search on which row the variable exists in the basis */ ; + if(row_nr<=lp->rows) { /* safety test; should always be found ... */ + /* Construct one row of the tableau */ + bsolve(lp, row_nr, prow, NULL, epsvalue*DOUBLEROUND, 1.0); + prod_xA(lp, coltarget, prow, NULL, epsvalue, 1.0, + prow, NULL, MAT_ROUNDDEFAULT); + /* sign = my_chsign(is_chsign(lp, row_nr), -1); */ + sign = my_chsign(lp->is_lower[row_nr], -1); + min1=infinite; + min2=infinite; + for(l=1; l<=lp->sum; l++) /* search for the column(s) which first results in further iterations */ + if ((!lp->is_basic[l]) && (lp->upbo[l]>0.0) && + (fabs(prow[l])>epsvalue) && (drow[l]*(lp->is_lower[l] ? -1 : 1)is_lower[l] ? 1 : -1) < 0.0) { + if(a < min1) + min1 = a; + } + else { + if(a < min2) + min2 = a; + } + } + if ((lp->is_lower[varnr] == 0) == (is_maxim(lp) == FALSE)) { + a = min1; + min1 = min2; + min2 = a; + } + if (min1solution[varnr]; + 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)) + 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)) + 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 ... */ + } + } + } + lp->objfrom[i]=from; + lp->objtill[i]=till; + } + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + } + FREE(prow); + FREE(OrigObj); + FREE(drow); + + return((MYBOOL) ok); +} /* construct_sensitivity_obj */ + +STATIC MYBOOL refactRecent(lprec *lp) +{ + int pivcount = lp->bfp_pivotcount(lp); + if(pivcount == 0) + return( AUTOMATIC ); + else if (pivcount < 2*DEF_MAXPIVOTRETRY) + return( TRUE ); + else + return( FALSE ); +} + +STATIC MYBOOL check_if_less(lprec *lp, LPSREAL x, LPSREAL y, int variable) +{ + if(y < x-scaled_value(lp, lp->epsint, variable)) { + if(lp->bb_trace) + report(lp, NORMAL, "check_if_less: Invalid new bound %g should be < %g for %s\n", + x, y, get_col_name(lp, variable)); + return(FALSE); + } + else + return(TRUE); +} + +/* Various basis utility routines */ + +STATIC int findNonBasicSlack(lprec *lp, MYBOOL *is_basic) +{ + int i; + + for(i = lp->rows; i > 0; i--) + if(!is_basic[i]) + break; + return( i ); +} + +STATIC int findBasisPos(lprec *lp, int notint, int *var_basic) +{ + int i; + + if(var_basic == NULL) + var_basic = lp->var_basic; + for(i = lp->rows; i > 0; i--) + if(var_basic[i] == notint) + break; + return( i ); +} + +STATIC void replaceBasisVar(lprec *lp, int rownr, int var, int *var_basic, MYBOOL *is_basic) +{ + int out; + + out = var_basic[rownr]; + var_basic[rownr] = var; + is_basic[out] = FALSE; + is_basic[var] = TRUE; +} + +STATIC void free_duals(lprec *lp) +{ + FREE(lp->duals); + FREE(lp->full_duals); + FREE(lp->dualsfrom); + FREE(lp->dualstill); + FREE(lp->objfromvalue); + FREE(lp->objfrom); + FREE(lp->objtill); +} + +/* Transform RHS by adjusting for the bound state of variables; + optionally rebase upper bound, and account for this in later calls */ +STATIC void initialize_solution(lprec *lp, MYBOOL shiftbounds) +{ + int i, k1, k2, *matRownr, colnr; + LREAL theta; + LPSREAL value, *matValue, loB, upB; + MATrec *mat = lp->matA; + + /* Set bounding status indicators */ + if(lp->bb_bounds != NULL) { + if(shiftbounds == INITSOL_SHIFTZERO) { + if(lp->bb_bounds->UBzerobased) + report(lp, SEVERE, "initialize_solution: The upper bounds are already zero-based at refactorization %d\n", + lp->bfp_refactcount(lp, BFP_STAT_REFACT_TOTAL)); + lp->bb_bounds->UBzerobased = TRUE; + } + else if(!lp->bb_bounds->UBzerobased) + report(lp, SEVERE, "initialize_solution: The upper bounds are not zero-based at refactorization %d\n", + lp->bfp_refactcount(lp, BFP_STAT_REFACT_TOTAL)); + } + + /* Initialize the working RHS/basic variable solution vector */ + i = is_action(lp->anti_degen, ANTIDEGEN_RHSPERTURB) && (lp->monitor != NULL) && lp->monitor->active; + if(sizeof(*lp->rhs) == sizeof(*lp->orig_rhs) && !i) { + MEMCOPY(lp->rhs, lp->orig_rhs, lp->rows+1); + } + else if(i) { + lp->rhs[0] = lp->orig_rhs[0]; + for(i = 1; i <= lp->rows; i++) { + if(is_constr_type(lp, i, EQ)) + theta = rand_uniform(lp, lp->epsvalue); + else { + theta = rand_uniform(lp, lp->epsperturb); +/* if(lp->orig_upbo[i] < lp->infinite) + lp->orig_upbo[i] += theta; */ + } + lp->rhs[i] = lp->orig_rhs[i] + theta; + } + } + else + for(i = 0; i <= lp->rows; i++) + lp->rhs[i] = lp->orig_rhs[i]; + +/* Adjust active RHS for variables at their active upper/lower bounds */ + for(i = 1; i <= lp->sum; i++) { + + upB = lp->upbo[i]; + loB = lp->lowbo[i]; + + /* Shift to "ranged" upper bound, tantamount to defining zero-based variables */ + if(shiftbounds == INITSOL_SHIFTZERO) { + if((loB > -lp->infinite) && (upB < lp->infinite)) + lp->upbo[i] -= loB; + if(lp->upbo[i] < 0) + report(lp, SEVERE, "initialize_solution: Invalid rebounding; variable %d at refact %d, iter %.0f\n", + i, lp->bfp_refactcount(lp, BFP_STAT_REFACT_TOTAL), (double) get_total_iter(lp)); + } + + /* Use "ranged" upper bounds */ + else if(shiftbounds == INITSOL_USEZERO) { + if((loB > -lp->infinite) && (upB < lp->infinite)) + upB += loB; + } + + /* Shift upper bound back to original value */ + else if(shiftbounds == INITSOL_ORIGINAL) { + if((loB > -lp->infinite) && (upB < lp->infinite)) { + lp->upbo[i] += loB; + upB += loB; + } + continue; + } + else + report(lp, SEVERE, "initialize_solution: Invalid option value '%d'\n", + shiftbounds); + + /* Set the applicable adjustment */ + if(lp->is_lower[i]) + theta = loB; + else + theta = upB; + + + /* Check if we need to pass through the matrix; + remember that basis variables are always lower-bounded */ + if(theta == 0) + continue; + + /* Do user and artificial variables */ + if(i > lp->rows) { + + /* Get starting and ending indeces in the NZ vector */ + colnr = i - lp->rows; + k1 = mat->col_end[colnr - 1]; + k2 = mat->col_end[colnr]; + matRownr = &COL_MAT_ROWNR(k1); + matValue = &COL_MAT_VALUE(k1); + + /* Get the objective as row 0, optionally adjusting the objective for phase 1 */ + value = get_OF_active(lp, i, theta); + lp->rhs[0] -= value; + + /* Do the normal case */ + for(; k1 < k2; + k1++, matRownr += matRowColStep, matValue += matValueStep) { + lp->rhs[*matRownr] -= theta * (*matValue); + } + } + + /* Do slack variables (constraint "bounds")*/ + else { + lp->rhs[i] -= theta; + } + + } + + /* Do final pass to get the maximum value */ + i = lps_idamax(lp->rows /* +1 */, lp->rhs, 1); + lp->rhsmax = fabs(lp->rhs[i]); + + if(shiftbounds == INITSOL_SHIFTZERO) + clear_action(&lp->spx_action, ACTION_REBASE); + +} + +/* This routine recomputes the basic variables using the full inverse */ +STATIC void recompute_solution(lprec *lp, MYBOOL shiftbounds) +{ + /* Compute RHS = b - A(n)*x(n) */ + initialize_solution(lp, shiftbounds); + + /* Compute x(b) = Inv(B)*RHS (Ref. lp_solve inverse logic and Chvatal p. 121) */ + lp->bfp_ftran_normal(lp, lp->rhs, NULL); + if(!lp->obj_in_basis) { + int i, ib, n = lp->rows; + for(i = 1; i <= n; i++) { + ib = lp->var_basic[i]; + if(ib > n) + lp->rhs[0] -= get_OF_active(lp, ib, lp->rhs[i]); + } + } + + /* Round the values (should not be greater than the factor used in bfp_pivotRHS) */ + roundVector(lp->rhs, lp->rows, lp->epsvalue); + + clear_action(&lp->spx_action, ACTION_RECOMPUTE); +} + +/* This routine compares an existing basic solution to a recomputed one; + Note that the routine must provide for the possibility that the order of the + basis variables can be changed by the inversion engine. */ +STATIC int verify_solution(lprec *lp, MYBOOL reinvert, char *info) +{ + int i, ii, n, *oldmap, *newmap, *refmap = NULL; + LPSREAL *oldrhs, err, errmax; + + allocINT(lp, &oldmap, lp->rows+1, FALSE); + allocINT(lp, &newmap, lp->rows+1, FALSE); + allocREAL(lp, &oldrhs, lp->rows+1, FALSE); + + /* Get sorted mapping of the old basis */ + for(i = 0; i <= lp->rows; i++) + oldmap[i] = i; + if(reinvert) { + allocINT(lp, &refmap, lp->rows+1, FALSE); + MEMCOPY(refmap, lp->var_basic, lp->rows+1); + sortByINT(oldmap, refmap, lp->rows, 1, TRUE); + } + + /* Save old and calculate the new RHS vector */ + MEMCOPY(oldrhs, lp->rhs, lp->rows+1); + if(reinvert) + invert(lp, INITSOL_USEZERO, FALSE); + else + recompute_solution(lp, INITSOL_USEZERO); + + /* Get sorted mapping of the new basis */ + for(i = 0; i <= lp->rows; i++) + newmap[i] = i; + if(reinvert) { + MEMCOPY(refmap, lp->var_basic, lp->rows+1); + sortByINT(newmap, refmap, lp->rows, 1, TRUE); + } + + /* Identify any gap */ + errmax = 0; + ii = -1; + n = 0; + for(i = lp->rows; i > 0; i--) { + err = fabs(my_reldiff(oldrhs[oldmap[i]], lp->rhs[newmap[i]])); + if(err > lp->epsprimal) { + n++; + if(err > errmax) { + ii = i; + errmax = err; + } + } + } + err = fabs(my_reldiff(oldrhs[i], lp->rhs[i])); + if(err < lp->epspivot) { + i--; + err = 0; + } + else { + n++; + if(ii < 0) { + ii = 0; + errmax = err; + } + } + if(n > 0) { + report(lp, IMPORTANT, "verify_solution: Iter %.0f %s - %d errors; OF %g, Max @row %d %g\n", + (double) get_total_iter(lp), my_if(info == NULL, "", info), n, err, newmap[ii], errmax); + } + /* Copy old results back (not possible for inversion) */ + if(!reinvert) + MEMCOPY(lp->rhs, oldrhs, lp->rows+1); + + FREE(oldmap); + FREE(newmap); + FREE(oldrhs); + if(reinvert) + FREE(refmap); + + return( ii ); + +} + +/* Preprocessing and postprocessing functions */ +STATIC int identify_GUB(lprec *lp, MYBOOL mark) +{ + int i, j, jb, je, k, knint, srh; + LPSREAL rh, mv, tv, bv; + MATrec *mat = lp->matA; + + if((lp->equalities == 0) || !mat_validate(mat)) + return( 0 ); + + k = 0; + for(i = 1; i <= lp->rows; i++) { + + /* Check if it is an equality constraint */ + if(!is_constr_type(lp, i, EQ)) + continue; + + rh = get_rh(lp, i); + srh = my_sign(rh); + knint = 0; + je = mat->row_end[i]; + for(jb = mat->row_end[i-1]; jb < je; jb++) { + j = ROW_MAT_COLNR(jb); + + /* Check for validity of the equation elements */ + if(!is_int(lp, j)) + knint++; + if(knint > 1) + break; + + mv = get_mat_byindex(lp, jb, TRUE, FALSE); + if(fabs(my_reldiff(mv, rh)) > lp->epsprimal) + break; + + tv = mv*get_upbo(lp, j); + bv = get_lowbo(lp, j); +#if 0 /* Requires 1 as upper bound */ + if((fabs(my_reldiff(tv, rh)) > lp->epsprimal) || (bv != 0)) +#else /* Can handle any upper bound >= 1 */ + if((srh*(tv-rh) < -lp->epsprimal) || (bv != 0)) +#endif + break; + } + + /* Update GUB count and optionally mark the GUB */ + if(jb == je) { + k++; + if(mark == TRUE) + lp->row_type[i] |= ROWTYPE_GUB; + else if(mark == AUTOMATIC) + break; + } + + } + return( k ); +} + +STATIC int prepare_GUB(lprec *lp) +{ + int i, j, jb, je, k, *members = NULL; + LPSREAL rh; + char GUBname[16]; + MATrec *mat = lp->matA; + + if((lp->equalities == 0) || + !allocINT(lp, &members, lp->columns+1, TRUE) || + !mat_validate(mat)) + return( 0 ); + + for(i = 1; i <= lp->rows; i++) { + + /* Check if it has been marked as a GUB */ + if(!(lp->row_type[i] & ROWTYPE_GUB)) + continue; + + /* Pick up the GUB column indeces */ + k = 0; + je = mat->row_end[i]; + for(jb = mat->row_end[i-1], k = 0; jb < je; jb++) { + members[k] = ROW_MAT_COLNR(jb); + k++; + } + + /* Add the GUB */ + j = GUB_count(lp) + 1; + sprintf(GUBname, "GUB_%d", i); + add_GUB(lp, GUBname, j, k, members); + + /* Unmark the GUBs */ + clear_action(&(lp->row_type[i]), ROWTYPE_GUB); + + /* Standardize coefficients to 1 if necessary */ + rh = get_rh(lp, i); + if(fabs(my_reldiff(rh, 1)) > lp->epsprimal) { + set_rh(lp, i, 1); + for(jb = mat->row_end[i-1]; jb < je; jb++) { + j = ROW_MAT_COLNR(jb); + set_mat(lp, i,j, 1); + } + } + + } + FREE(members); + return(GUB_count(lp)); +} + +/* Pre- and post processing functions, i.a. splitting free variables */ +STATIC MYBOOL pre_MIPOBJ(lprec *lp) +{ +#ifdef MIPboundWithOF + if(MIP_count(lp) > 0) { + int i = 1; + while((i <= lp->rows) && !mat_equalRows(lp->matA, 0, i) && !is_constr_type(lp, i, EQ)) + i++; + if(i <= lp->rows) + lp->constraintOF = i; + } +#endif + lp->bb_deltaOF = MIP_stepOF(lp); + return( TRUE ); +} +STATIC MYBOOL post_MIPOBJ(lprec *lp) +{ +#ifdef MIPboundWithOF +/* + if(lp->constraintOF) { + del_constraint(lp, lp->rows); + if(is_BasisReady(lp) && !verify_basis(lp)) + return( FALSE ); + } +*/ +#endif + return( TRUE ); +} + +int preprocess(lprec *lp) +{ + int i, j, k, ok = TRUE, *new_index = NULL; + LPSREAL hold, *new_column = NULL; + MYBOOL scaled, primal1, primal2; + + /* do not process if already preprocessed */ + if(lp->wasPreprocessed) + return( ok ); + + /* Write model statistics and optionally initialize partial pricing structures */ + if(lp->lag_status != RUNNING) { + MYBOOL doPP; + + /* Extract the user-specified simplex strategy choices */ + primal1 = (MYBOOL) (lp->simplex_strategy & SIMPLEX_Phase1_PRIMAL); + primal2 = (MYBOOL) (lp->simplex_strategy & SIMPLEX_Phase2_PRIMAL); + + /* Initialize partial pricing structures */ + doPP = is_piv_mode(lp, PRICE_PARTIAL | PRICE_AUTOPARTIAL); +/* doPP &= (MYBOOL) (lp->columns / 2 > lp->rows); */ + if(doPP) { + i = partial_findBlocks(lp, FALSE, FALSE); + if(i < 4) + i = (int) (5 * log((LPSREAL) 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); + } +/* doPP &= (MYBOOL) (lp->rows / 4 > lp->columns); */ + if(doPP) { + i = partial_findBlocks(lp, FALSE, TRUE); + if(i < 4) + i = (int) (5 * log((LPSREAL) 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); + } + + /* Check for presence of valid pricing blocks if partial pricing + is defined, but not autopartial is not set */ + if(!doPP && is_piv_mode(lp, PRICE_PARTIAL)) { + if((lp->rowblocks == NULL) || (lp->colblocks == NULL)) { + report(lp, IMPORTANT, "Ignoring partial pricing, since block structures are not defined.\n"); + clear_action(&lp->piv_strategy, PRICE_PARTIAL); + } + } + + /* Initialize multiple pricing block divisor */ +#if 0 + if(primal1 || primal2) + lp->piv_strategy |= PRICE_MULTIPLE | PRICE_AUTOMULTIPLE; +#endif + 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)); + SETMAX( i, 1); + set_multiprice(lp, i); + } + if(lp->multiblockdiv > 1) + report(lp, NORMAL, "Using %d-candidate primal simplex multiple pricing block.\n", + lp->columns / lp->multiblockdiv); + } + else + set_multiprice(lp, 1); + + report(lp, NORMAL, "Using %s simplex for phase 1 and %s simplex for phase 2.\n", + my_if(primal1, "PRIMAL", "DUAL"), my_if(primal2, "PRIMAL", "DUAL")); + i = get_piv_rule(lp); + if((i == PRICER_STEEPESTEDGE) && is_piv_mode(lp, PRICE_PRIMALFALLBACK)) + report(lp, NORMAL, "The pricing strategy is set to '%s' for the dual and '%s' for the primal.\n", + get_str_piv_rule(i), get_str_piv_rule(i-1)); + else + report(lp, NORMAL, "The primal and dual simplex pricing strategy set to '%s'.\n", + get_str_piv_rule(i)); + + report(lp, NORMAL, " \n"); + } + + /* Compute a minimum step improvement step requirement */ + pre_MIPOBJ(lp); + + /* First create extra columns for FR variables or flip MI variables */ + for (j = 1; j <= lp->columns; j++) { + +#ifdef Paranoia + if((lp->rows != lp->matA->rows) || (lp->columns != lp->matA->columns)) + report(lp, SEVERE, "preprocess: Inconsistent variable counts found\n"); +#endif + + /* First handle sign-flipping of variables: + 1) ... with a finite upper bound and a negative Inf-bound (since basis variables are lower-bounded) + 2) ... with bound assymetry within negrange limits (for stability reasons) */ + i = lp->rows + j; + hold = lp->orig_upbo[i]; +/* + if((hold <= 0) || (!is_infinite(lp, lp->negrange) && + (hold < -lp->negrange) && + (lp->orig_lowbo[i] <= lp->negrange)) ) { +*/ +#define fullybounded FALSE + if( ((hold < lp->infinite) && my_infinite(lp, lp->orig_lowbo[i])) || + (!fullybounded && !my_infinite(lp, lp->negrange) && + (hold < -lp->negrange) && (lp->orig_lowbo[i] <= lp->negrange)) ) { + /* Delete split sibling variable if one existed from before */ + 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); + if(lp->var_is_free == NULL) { + if(!allocINT(lp, &lp->var_is_free, MAX(lp->columns, lp->columns_alloc) + 1, TRUE)) + return(FALSE); + } + lp->var_is_free[j] = -j; /* Indicator UB and LB are switched, with no helper variable added */ + lp->orig_upbo[i] = my_flipsign(lp->orig_lowbo[i]); + lp->orig_lowbo[i] = my_flipsign(hold); + /* Check for presence of negative ranged SC variable */ + if(lp->sc_lobound[j] > 0) { + lp->sc_lobound[j] = lp->orig_lowbo[i]; + lp->orig_lowbo[i] = 0; + } + } + /* Then deal with -+, full-range/FREE variables by creating a helper variable */ + else if((lp->orig_lowbo[i] <= lp->negrange) && (hold >= -lp->negrange)) { + if(lp->var_is_free == NULL) { + if(!allocINT(lp, &lp->var_is_free, MAX(lp->columns,lp->columns_alloc) + 1, TRUE)) + return(FALSE); + } + if(lp->var_is_free[j] <= 0) { /* If this variable wasn't split yet ... */ + if(SOS_is_member(lp->SOS, 0, i - lp->rows)) { /* Added */ + report(lp, IMPORTANT, "preprocess: Converted negative bound for SOS variable %d to zero", + i - lp->rows); + lp->orig_lowbo[i] = 0; + continue; + } + if(new_column == NULL) { + if(!allocREAL(lp, &new_column, lp->rows + 1, FALSE) || + !allocINT(lp, &new_index, lp->rows + 1, FALSE)) { + ok = FALSE; + break; + } + } + /* Avoid precision loss by turning off unscaling and rescaling */ + /* in get_column and add_column operations; also make sure that */ + /* full scaling information is preserved */ + scaled = lp->scaling_used; + lp->scaling_used = FALSE; + k = get_columnex(lp, j, new_column, new_index); + if(!add_columnex(lp, k, new_column, new_index)) { + ok = FALSE; + break; + } + mat_multcol(lp->matA, lp->columns, -1, TRUE); + if(scaled) + lp->scalars[lp->rows+lp->columns] = lp->scalars[i]; + lp->scaling_used = (MYBOOL) scaled; + /* Only create name if we are not clearing a pre-used item, since this + variable could have been deleted by presolve but the name is required + for solution reconstruction. */ + if(lp->names_used && (lp->col_name[j] == NULL)) { + char fieldn[50]; + + sprintf(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; + break; + } + } + /* Set (positive) index to the original column's split / helper and back */ + lp->var_is_free[j] = lp->columns; + } + lp->orig_upbo[lp->rows + lp->var_is_free[j]] = my_flipsign(lp->orig_lowbo[i]); + lp->orig_lowbo[i] = 0; + + /* Negative index indicates x is split var and -var_is_free[x] is index of orig var */ + lp->var_is_free[lp->var_is_free[j]] = -j; + lp->var_type[lp->var_is_free[j]] = lp->var_type[j]; + } + /* Check for positive ranged SC variables */ + else if(lp->sc_lobound[j] > 0) { + lp->sc_lobound[j] = lp->orig_lowbo[i]; + lp->orig_lowbo[i] = 0; + } + + /* Tally integer variables in SOS'es */ + if(SOS_is_member(lp->SOS, 0, j) && is_int(lp, j)) + lp->sos_ints++; + } + + FREE(new_column); + FREE(new_index); + + /* Fill lists of GUB constraints, if appropriate */ + if((MIP_count(lp) > 0) && is_bb_mode(lp, NODE_GUBMODE) && (identify_GUB(lp, AUTOMATIC) > 0)) + prepare_GUB(lp); + + /* (Re)allocate reduced cost arrays */ + ok = allocREAL(lp, &(lp->drow), lp->sum+1, AUTOMATIC) && + allocINT(lp, &(lp->nzdrow), lp->sum+1, AUTOMATIC); + if(ok) + lp->nzdrow[0] = 0; + + /* Minimize memory usage */ + memopt_lp(lp, 0, 0, 0); + + lp->wasPreprocessed = TRUE; + + return(ok); +} + +void postprocess(lprec *lp) +{ + int i,ii,j; + LPSREAL hold; + + /* Check if the problem actually was preprocessed */ + if(!lp->wasPreprocessed) + return; + + /* 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"); + } + + /* Loop over all columns */ + for (j = 1; j <= lp->columns; j++) { + i = lp->rows + j; + /* Reconstruct strictly negative values */ + 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); + hold = lp->orig_upbo[i]; + lp->orig_upbo[i] = my_flipsign(lp->orig_lowbo[i]); + lp->orig_lowbo[i] = my_flipsign(hold); + lp->best_solution[i] = my_flipsign(lp->best_solution[i]); + transfer_solution_var(lp, j); + + /* hold = lp->objfrom[j]; + lp->objfrom[j] = my_flipsign(lp->objtill[j]); + lp->objtill[j] = my_flipsign(hold); */ /* under investigation */ + + /* lp->duals[i] = my_flipsign(lp->duals[i]); + hold = lp->dualsfrom[i]; + lp->dualsfrom[i] = my_flipsign(lp->dualstill[i]); + lp->dualstill[i] = my_flipsign(hold); */ /* under investigation */ + /* Bound switch undone, so clear the status */ + lp->var_is_free[j] = 0; + /* Adjust negative ranged SC */ + if(lp->sc_lobound[j] > 0) + lp->orig_lowbo[lp->rows + j] = -lp->sc_lobound[j]; + } + /* Ignore the split / helper columns (will be deleted later) */ + } + /* Condense values of extra columns of quasi-free variables split in two */ + else if((lp->var_is_free != NULL) && (lp->var_is_free[j] > 0)) { + ii = lp->var_is_free[j]; /* Index of the split helper var */ + /* if(lp->objfrom[j] == -lp->infinite) + lp->objfrom[j] = -lp->objtill[ii]; + lp->objtill[ii] = lp->infinite; + if(lp->objtill[j] == lp->infinite) + lp->objtill[j] = my_flipsign(lp->objfrom[ii]); + lp->objfrom[ii] = -lp->infinite; */ /* under investigation */ + + ii += lp->rows; + lp->best_solution[i] -= lp->best_solution[ii]; /* join the solution again */ + transfer_solution_var(lp, j); + lp->best_solution[ii] = 0; + + /* if(lp->duals[i] == 0) + lp->duals[i] = my_flipsign(lp->duals[ii]); + lp->duals[ii] = 0; + if(lp->dualsfrom[i] == -lp->infinite) + lp->dualsfrom[i] = my_flipsign(lp->dualstill[ii]); + lp->dualstill[ii] = lp->infinite; + if(lp->dualstill[i] == lp->infinite) + lp->dualstill[i] = my_flipsign(lp->dualsfrom[ii]); + lp->dualsfrom[ii] = -lp->infinite; */ /* under investigation */ + + /* Reset to original bound */ + lp->orig_lowbo[i] = my_flipsign(lp->orig_upbo[ii]); + } + /* Adjust for semi-continuous variables */ + else if(lp->sc_lobound[j] > 0) { + lp->orig_lowbo[i] = lp->sc_lobound[j]; + } + } + + /* Remove any split column helper variables */ + del_splitvars(lp); + post_MIPOBJ(lp); + + /* Do extended reporting, if specified */ + if(lp->verbose > NORMAL) { + REPORT_extended(lp); + + } + + lp->wasPreprocessed = FALSE; +} + diff --git a/src/external/lpsolve/build/lp_solve/lp_matrix.c b/src/external/lpsolve/build/lp_solve/lp_matrix.c new file mode 100644 index 00000000..ce585e21 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_matrix.c @@ -0,0 +1,3828 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_scale.h" +#include "lp_report.h" +#include "lp_price.h" +#include "lp_pricePSE.h" +#include "lp_matrix.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* ------------------------------------------------------------------------- + Basic matrix routines in lp_solve v5.0+ + ------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2), + Kjell Eikland (v4.0 and forward) + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, lp_pricerPSE.h, lp_matrix.h + + Release notes: + v5.0.0 1 January 2004 First integrated and repackaged version. + v5.0.1 7 May 2004 Added matrix transpose function. + v5.1.0 20 July 2004 Reworked with flexible matrix storage model. + v5.2.0 10 January 2005 Added fast deletion methods. + Added data extraction to matrix method. + Changed to explicit OF storage mode. + + ------------------------------------------------------------------------- */ + +STATIC MATrec *mat_create(lprec *lp, int rows, int columns, LPSREAL epsvalue) +{ + MATrec *newmat; + + newmat = (MATrec *) calloc(1, sizeof(*newmat)); + newmat->lp = lp; + + newmat->rows_alloc = 0; + newmat->columns_alloc = 0; + newmat->mat_alloc = 0; + + inc_matrow_space(newmat, rows); + newmat->rows = rows; + inc_matcol_space(newmat, columns); + newmat->columns = columns; + inc_mat_space(newmat, 0); + + newmat->epsvalue = epsvalue; + + return( newmat ); +} + +STATIC void mat_free(MATrec **matrix) +{ + if((matrix == NULL) || (*matrix == NULL)) + return; + +#if MatrixColAccess==CAM_Record + FREE((*matrix)->col_mat); +#else /*if MatrixColAccess==CAM_Vector*/ + FREE((*matrix)->col_mat_colnr); + FREE((*matrix)->col_mat_rownr); + FREE((*matrix)->col_mat_value); +#endif + FREE((*matrix)->col_end); + FREE((*matrix)->col_tag); + +#if MatrixRowAccess==RAM_Index + FREE((*matrix)->row_mat); +#elif MatrixColAccess==CAM_Record + FREE((*matrix)->row_mat); +#else /*if MatrixRowAccess==COL_Vector*/ + FREE((*matrix)->row_mat_colnr); + FREE((*matrix)->row_mat_rownr); + FREE((*matrix)->row_mat_value); +#endif + FREE((*matrix)->row_end); + FREE((*matrix)->row_tag); + + FREE((*matrix)->colmax); + FREE((*matrix)->rowmax); + + FREE(*matrix); +} + +STATIC MYBOOL mat_memopt(MATrec *mat, int rowextra, int colextra, int nzextra) +{ + MYBOOL status = TRUE; + 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 + +#if MatrixColAccess==CAM_Record + mat->col_mat = (MATitem *) realloc(mat->col_mat, matalloc * sizeof(*(mat->col_mat))); + status &= (mat->col_mat != NULL); +#else /*if MatrixColAccess==CAM_Vector*/ + status &= allocINT(mat->lp, &(mat->col_mat_colnr), matalloc, AUTOMATIC) && + allocINT(mat->lp, &(mat->col_mat_rownr), matalloc, AUTOMATIC) && + allocREAL(mat->lp, &(mat->col_mat_value), matalloc, AUTOMATIC); +#endif + status &= allocINT(mat->lp, &mat->col_end, colalloc, AUTOMATIC); + if(mat->col_tag != NULL) + status &= allocINT(mat->lp, &mat->col_tag, colalloc, AUTOMATIC); + +#if MatrixRowAccess==RAM_Index + status &= allocINT(mat->lp, &(mat->row_mat), matalloc, AUTOMATIC); +#elif MatrixColAccess==CAM_Record + mat->row_mat = (MATitem *) realloc(mat->row_mat, matalloc * sizeof(*(mat->row_mat))); + status &= (mat->row_mat != NULL); +#else /*if MatrixRowAccess==COL_Vector*/ + status &= allocINT(mat->lp, &(mat->row_mat_colnr), matalloc, AUTOMATIC) && + allocINT(mat->lp, &(mat->row_mat_rownr), matalloc, AUTOMATIC) && + allocREAL(mat->lp, &(mat->row_mat_value), matalloc, AUTOMATIC); +#endif + status &= allocINT(mat->lp, &mat->row_end, rowalloc, AUTOMATIC); + if(mat->row_tag != NULL) + status &= allocINT(mat->lp, &mat->row_tag, rowalloc, AUTOMATIC); + + if(mat->colmax != NULL) + status &= allocREAL(mat->lp, &(mat->colmax), colalloc, AUTOMATIC); + if(mat->rowmax != NULL) + status &= allocREAL(mat->lp, &(mat->rowmax), rowalloc, AUTOMATIC); + + return( status ); +} + +STATIC MYBOOL inc_mat_space(MATrec *mat, int mindelta) +{ + int spaceneeded, nz = mat_nonzeros(mat); + + if(mindelta <= 0) + mindelta = MAX(mat->rows, mat->columns) + 1; + spaceneeded = DELTA_SIZE(mindelta, nz); + SETMAX(mindelta, spaceneeded); + + if(mat->mat_alloc == 0) + spaceneeded = mindelta; + else + spaceneeded = nz + mindelta; + + if(spaceneeded >= mat->mat_alloc) { + /* Let's allocate at least MAT_START_SIZE entries */ + if(mat->mat_alloc < MAT_START_SIZE) + mat->mat_alloc = MAT_START_SIZE; + + /* Increase the size by RESIZEFACTOR each time it becomes too small */ + while(spaceneeded >= mat->mat_alloc) + mat->mat_alloc += mat->mat_alloc / RESIZEFACTOR; + +#if MatrixColAccess==CAM_Record + mat->col_mat = (MATitem *) realloc(mat->col_mat, (mat->mat_alloc) * sizeof(*(mat->col_mat))); +#else /*if MatrixColAccess==CAM_Vector*/ + allocINT(mat->lp, &(mat->col_mat_colnr), mat->mat_alloc, AUTOMATIC); + allocINT(mat->lp, &(mat->col_mat_rownr), mat->mat_alloc, AUTOMATIC); + allocREAL(mat->lp, &(mat->col_mat_value), mat->mat_alloc, AUTOMATIC); +#endif + +#if MatrixRowAccess==RAM_Index + allocINT(mat->lp, &(mat->row_mat), mat->mat_alloc, AUTOMATIC); +#elif MatrixColAccess==CAM_Record + mat->row_mat = (MATitem *) realloc(mat->row_mat, (mat->mat_alloc) * sizeof(*(mat->row_mat))); +#else /*if MatrixColAccess==CAM_Vector*/ + allocINT(mat->lp, &(mat->row_mat_colnr), mat->mat_alloc, AUTOMATIC); + allocINT(mat->lp, &(mat->row_mat_rownr), mat->mat_alloc, AUTOMATIC); + allocREAL(mat->lp, &(mat->row_mat_value), mat->mat_alloc, AUTOMATIC); +#endif + } + return(TRUE); +} + +STATIC MYBOOL inc_matrow_space(MATrec *mat, int deltarows) +{ + int rowsum, oldrowsalloc; + MYBOOL status = TRUE; + + /* Adjust lp row structures */ + if(mat->rows+deltarows >= mat->rows_alloc) { + + /* Update memory allocation and sizes */ + oldrowsalloc = mat->rows_alloc; + deltarows = DELTA_SIZE(deltarows, mat->rows); + SETMAX(deltarows, DELTAROWALLOC); + mat->rows_alloc += deltarows; + rowsum = mat->rows_alloc + 1; + + /* Update row pointers */ + status = allocINT(mat->lp, &mat->row_end, rowsum, AUTOMATIC); + mat->row_end_valid = FALSE; + } + return( status ); +} + +STATIC MYBOOL inc_matcol_space(MATrec *mat, int deltacols) +{ + int i, colsum, oldcolsalloc; + MYBOOL status = TRUE; + + /* Adjust lp column structures */ + if(mat->columns+deltacols >= mat->columns_alloc) { + + /* Update memory allocation and sizes */ + oldcolsalloc = mat->columns_alloc; + deltacols = DELTA_SIZE(deltacols, mat->columns); + SETMAX(deltacols, DELTACOLALLOC); + mat->columns_alloc += deltacols; + colsum = mat->columns_alloc + 1; + status = allocINT(mat->lp, &mat->col_end, colsum, AUTOMATIC); + + /* Update column pointers */ + if(oldcolsalloc == 0) + mat->col_end[0] = 0; + for(i = MIN(oldcolsalloc, mat->columns) + 1; i < colsum; i++) + mat->col_end[i] = mat->col_end[i-1]; + mat->row_end_valid = FALSE; + } + return( status ); +} + +STATIC int mat_collength(MATrec *mat, int colnr) +{ + return( mat->col_end[colnr] - mat->col_end[colnr-1] ); +} + +STATIC int mat_rowlength(MATrec *mat, int rownr) +{ + if(mat_validate(mat)) { + if(rownr <= 0) + return( mat->row_end[0] ); + else + return( mat->row_end[rownr] - mat->row_end[rownr-1] ); + } + else + return( 0 ); +} + +STATIC int mat_nonzeros(MATrec *mat) +{ + return( mat->col_end[mat->columns] ); +} + +STATIC MYBOOL mat_indexrange(MATrec *mat, int index, MYBOOL isrow, int *startpos, int *endpos) +{ +#ifdef Paranoia + if(isrow && ((index < 0) || (index > mat->rows))) + return( FALSE ); + else if(!isrow && ((index < 1) || (index > mat->columns))) + return( FALSE ); +#endif + + if(isrow && mat_validate(mat)) { + if(index == 0) + *startpos = 0; + else + *startpos = mat->row_end[index-1]; + *endpos = mat->row_end[index]; + } + else { + *startpos = mat->col_end[index-1]; + *endpos = mat->col_end[index]; + } + return( TRUE ); +} + +STATIC int mat_shiftrows(MATrec *mat, int *bbase, int delta, LLrec *varmap) +{ + int j, k, i, ii, thisrow, *colend, base; + MYBOOL preparecompact = FALSE; + int *rownr; + + if(delta == 0) + return( 0 ); + base = abs(*bbase); + + if(delta > 0) { + + /* Insert row by simply incrementing existing row indeces */ + if(base <= mat->rows) { + k = mat_nonzeros(mat); + rownr = &COL_MAT_ROWNR(0); + for(ii = 0; ii < k; ii++, rownr += matRowColStep) { + if(*rownr >= base) + *rownr += delta; + } + } + + /* Set defaults (actual basis set in separate procedure) */ + for(i = 0; i < delta; i++) { + ii = base + i; + mat->row_end[ii] = 0; + } + } + else if(base <= mat->rows) { + + /* Check for preparation of mass-deletion of rows */ + preparecompact = (MYBOOL) (varmap != NULL); + if(preparecompact) { + /* Create the offset array */ + int *newrowidx = NULL; + allocINT(mat->lp, &newrowidx, mat->rows+1, FALSE); + newrowidx[0] = 0; + delta = 0; + for(j = 1; j <= mat->rows; j++) { + if(isActiveLink(varmap, j)) { + delta++; + newrowidx[j] = delta; + } + else + newrowidx[j] = -1; + } + k = 0; + delta = 0; + base = mat_nonzeros(mat); + rownr = &COL_MAT_ROWNR(0); + for(i = 0; i < base; i++, rownr += matRowColStep) { + thisrow = newrowidx[*rownr]; + if(thisrow < 0) { + *rownr = -1; + delta++; + } + else + *rownr = thisrow; + } + FREE(newrowidx); + return(delta); + } + + /* Check if we should prepare for compacting later + (this is in order to speed up multiple row deletions) */ + preparecompact = (MYBOOL) (*bbase < 0); + if(preparecompact) + *bbase = my_flipsign((*bbase)); + + /* First make sure we don't cross the row count border */ + if(base-delta-1 > mat->rows) + delta = base - mat->rows - 1; + + /* Then scan over all entries shifting and updating rows indeces */ + if(preparecompact) { + k = 0; + for(j = 1, colend = mat->col_end + 1; + j <= mat->columns; j++, colend++) { + i = k; + k = *colend; + rownr = &COL_MAT_ROWNR(i); + for(; i < k; i++, rownr += matRowColStep) { + thisrow = *rownr; + if(thisrow < base) + continue; + else if(thisrow >= base-delta) + *rownr += delta; + else + *rownr = -1; + } + } + } + else { + k = 0; + ii = 0; + for(j = 1, colend = mat->col_end + 1; + j <= mat->columns; j++, colend++) { + i = k; + k = *colend; + rownr = &COL_MAT_ROWNR(i); + for(; i < k; i++, rownr += matRowColStep) { + thisrow = *rownr; + if(thisrow >= base) { + if(thisrow >= base-delta) + *rownr += delta; + else + continue; + } + if(ii != i) { + COL_MAT_COPY(ii, i); + } + ii++; + } + *colend = ii; + } + } + } + return( 0 ); +} + +/* Map-based compacting+insertion of matrix elements without changing row and column indeces. + When mat2 is NULL, a simple compacting of non-deleted rows and columns is done. */ +STATIC int mat_mapreplace(MATrec *mat, LLrec *rowmap, LLrec *colmap, MATrec *mat2) +{ + lprec *lp = mat->lp; + int i, ib, ie, ii, j, jj, jb, je, nz, *colend, *rownr, *rownr2, *indirect = NULL; + LPSREAL *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))) + return( 0 ); + + /* Create map and sort by increasing index in "mat" */ + if(mat2 != NULL) { + jj = mat2->col_tag[0]; + allocINT(lp, &indirect, jj+1, FALSE); + indirect[0] = jj; + for(i = 1; i <= jj; i++) + indirect[i] = i; + hpsortex(mat2->col_tag, jj, 1, sizeof(*indirect), FALSE, compareINT, indirect); + } + + /* Do the compacting */ + mat->row_end_valid = FALSE; + nz = mat->col_end[mat->columns]; + ie = 0; + ii = 0; + if((mat2 == NULL) || (indirect[0] == 0)) { + je = mat->columns + 1; + jj = 1; + jb = 0; + } + else { + je = indirect[0]; + jj = 0; + do { + jj++; + jb = mat2->col_tag[jj]; + } while(jb <= 0); + + } + for(j = 1, colend = mat->col_end + 1; + j <= mat->columns; j++, colend++) { + ib = ie; + ie = *colend; + + /* Always skip (condense) replacement columns */ + if(j == jb) { + jj++; + if(jj <= je) + jb = mat2->col_tag[jj]; + else + jb = mat->columns + 1; + } + + /* Only include active columns */ + else if(isActiveLink(colmap, j)) { + rownr = &COL_MAT_ROWNR(ib); + for(; ib < ie; ib++, rownr += matRowColStep) { + + /* Also make sure the row is active */ + if(isActiveLink(rowmap, *rownr)) { + if(ii != ib) { + COL_MAT_COPY(ii, ib); + } + ii++; + } + } + } + *colend = ii; + } + if(mat2 == NULL) + goto Finish; + + /* Tally non-zero insertions */ + i = 0; + for(j = 1; j <= mat2->col_tag[0]; j++) { + jj = mat2->col_tag[j]; + if((jj > 0) && isActiveLink(colmap, jj)) { + jj = indirect[j]; + je = mat2->col_end[jj]; + jb = mat2->col_end[jj-1]; + rownr2 = &COL_MAT2_ROWNR(jb); + for(; jb < je; jb++, rownr2 += matRowColStep) { + if((*rownr2 > 0) && isActiveLink(rowmap, *rownr2)) + i++; + } + } + } + + /* Make sure we have enough matrix space */ + ii = mat->col_end[mat->columns] + i; + if(mat->mat_alloc <= ii) + inc_mat_space(mat, i); + + /* Do shifting and insertion - loop from the end going forward */ + jj = indirect[0]; + jj = mat2->col_tag[jj]; + for(j = mat->columns, colend = mat->col_end + mat->columns, ib = *colend; + j > 0; j--) { + + /* Update indeces for this loop */ + ie = ib; + *colend = ii; + colend--; + ib = *colend; + + /* Insert new values */ + if(j == jj) { + /* Only include an active column */ + if(isActiveLink(colmap, j)) { + jj = indirect[0]; + jj = indirect[jj]; + rownr = &COL_MAT_ROWNR(ii-1); + value = &COL_MAT_VALUE(ii-1); + jb = mat2->col_end[jj-1]; + je = mat2->col_end[jj] - 1; + rownr2 = &COL_MAT2_ROWNR(je); + value2 = &COL_MAT2_VALUE(je); + + /* Process constraint coefficients */ + for(; je >= jb; je--, rownr2 -= matRowColStep, value2 -= matValueStep) { + i = *rownr2; + if(i == 0) { + i = -1; + break; + } + else if(isActiveLink(rowmap, i)) { + ii--; + *rownr = i; + rownr -= matRowColStep; + *value = my_chsign(is_chsign(lp, i), *value2); + value -= matValueStep; + } + } + + /* Then handle the objective */ + if(i == -1) { + lp->orig_obj[j] = my_chsign(is_maxim(lp), *value2); + rownr2 -= matRowColStep; + value2 -= matValueStep; + } + else + lp->orig_obj[j] = 0; + + } + /* Update replacement column index or break if no more candidates */ + jj = --indirect[0]; + if(jj == 0) + break; + jj = mat2->col_tag[jj]; + if(jj <= 0) + break; + } + /* Shift existing values down */ + else { + if(isActiveLink(colmap, j)) + while(ie > ib) { + ii--; + ie--; + if(ie != ii) { + COL_MAT_COPY(ii, ie); + } + } + } + } + + /* Return the delta number of non-zero elements */ +Finish: + nz -= mat->col_end[mat->columns]; + FREE(indirect); + + return( nz ); +} + +/* Routines to compact rows in matrix based on precoded entries */ +STATIC int mat_zerocompact(MATrec *mat) +{ + return( mat_rowcompact(mat, TRUE) ); +} +STATIC int mat_rowcompact(MATrec *mat, MYBOOL dozeros) +{ + int i, ie, ii, j, nn, *colend, *rownr; + LPSREAL *value; + + nn = 0; + ie = 0; + ii = 0; + for(j = 1, colend = mat->col_end + 1; + j <= mat->columns; j++, colend++) { + i = ie; + ie = *colend; + rownr = &COL_MAT_ROWNR(i); + value = &COL_MAT_VALUE(i); + for(; i < ie; + i++, rownr += matRowColStep, value += matValueStep) { + if((*rownr < 0) || (dozeros && (fabs(*value) < mat->epsvalue))) { + nn++; + continue; + } + if(ii != i) { + COL_MAT_COPY(ii, i); + } + ii++; + } + *colend = ii; + } + return( nn ); +} + +/* Routines to compact columns and their indeces based on precoded entries */ +STATIC int mat_colcompact(MATrec *mat, int prev_rows, int prev_cols) +{ + int i, ii, j, k, n_del, n_sum, *colend, *newcolend, *colnr, newcolnr; + MYBOOL deleted; + lprec *lp = mat->lp; + presolveundorec *lpundo = lp->presolve_undo; + + + n_sum = 0; + k = 0; + ii = 0; + newcolnr = 1; + for(j = 1, colend = newcolend = mat->col_end + 1; + j <= prev_cols; j++, colend++) { + n_del = 0; + i = k; + k = *colend; + for(colnr = &COL_MAT_COLNR(i); i < k; + i++, colnr += matRowColStep) { + if(*colnr < 0) { + n_del++; + n_sum++; + continue; + } + if(ii < i) { + COL_MAT_COPY(ii, i); + } + if(newcolnr < j) { + COL_MAT_COLNR(ii) = newcolnr; + } + ii++; + } + *newcolend = ii; + + deleted = (MYBOOL) (n_del > 0); +#if 1 + /* Do hoops in case there was an empty column */ + deleted |= (MYBOOL) (!lp->wasPresolved && (lpundo->var_to_orig[prev_rows+j] < 0)); + +#endif + /* Increment column variables if current column was not deleted */ + if(!deleted) { + newcolend++; + newcolnr++; + } + } + return(n_sum); +} + +STATIC int mat_shiftcols(MATrec *mat, int *bbase, int delta, LLrec *varmap) +{ + int i, ii, k, n, base; + + + k = 0; + if(delta == 0) + return( k ); + base = abs(*bbase); + + if(delta > 0) { + /* Shift pointers right */ + for(ii = mat->columns; ii > base; ii--) { + i = ii + delta; + mat->col_end[i] = mat->col_end[ii]; + } + /* Set defaults */ + for(i = 0; i < delta; i++) { + ii = base + i; + mat->col_end[ii] = mat->col_end[ii-1]; + } + } + else { + + /* Check for preparation of mass-deletion of columns */ + MYBOOL preparecompact = (MYBOOL) (varmap != NULL); + if(preparecompact) { + /* Create the offset array */ + int j, *colnr, *colend; + n = 0; + k = 0; + base = 0; + for(j = 1, colend = mat->col_end + 1; + j <= mat->columns; j++, colend++) { + i = k; + k = *colend; + if(isActiveLink(varmap, j)) { + base++; + ii = base; + } + else + ii = -1; + if(ii < 0) + n += k - i; + colnr = &COL_MAT_COLNR(i); + for(; i < k; i++, colnr += matRowColStep) + *colnr = ii; + } + return(n); + } + + /* Check if we should prepare for compacting later + (this is in order to speed up multiple column deletions) */ + preparecompact = (MYBOOL) (*bbase < 0); + if(preparecompact) + *bbase = my_flipsign((*bbase)); + + /* First make sure we don't cross the column count border */ + if(base-delta-1 > mat->columns) + delta = base - mat->columns - 1; + + /* Then scan over all entries shifting and updating column indeces */ + if(preparecompact) { + int *colnr; + n = 0; + i = mat->col_end[base-1]; + k = mat->col_end[base-delta-1]; + for(colnr = &COL_MAT_COLNR(i); i < k; + i++, colnr += matRowColStep) { + n++; + *colnr = -1; + } + k = n; + } + else { + /* Delete sparse matrix data, if required */ + if(base <= mat->columns) { + + i = mat->col_end[base-1]; /* Beginning of data to be deleted */ + ii = mat->col_end[base-delta-1]; /* Beginning of data to be shifted left */ + n = mat_nonzeros(mat); /* Total number of non-zeros */ + k = ii-i; /* Number of entries to be deleted */ + if((k > 0) && (n > i)) { + n -= ii; + COL_MAT_MOVE(i, ii, n); + } + + /* Update indexes */ + for(i = base; i <= mat->columns + delta; i++) { + ii = i - delta; + mat->col_end[i] = mat->col_end[ii] - k; + } + } + } + } + return( k ); +} + +STATIC MATrec *mat_extractmat(MATrec *mat, LLrec *rowmap, LLrec *colmap, MYBOOL negated) +{ + int *rownr, *colnr, xa, na; + LPSREAL *value; + MATrec *newmat = mat_create(mat->lp, mat->rows, mat->columns, mat->epsvalue); + + /* Initialize */ + na = mat_nonzeros(mat); + rownr = &COL_MAT_ROWNR(0); + colnr = &COL_MAT_COLNR(0); + value = &COL_MAT_VALUE(0); + + /* Loop over the indeces, picking out values in qualifying rows and colums + (note that the loop could be speeded up for dense matrices by making an + outer loop for columns and inner loop for rows) */ + for(xa = 0; xa < na; xa++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) { + if((isActiveLink(colmap, *colnr) ^ negated) && + (isActiveLink(rowmap, *rownr) ^ negated)) + mat_setvalue(newmat, *rownr, *colnr, *value, FALSE); + } + + /* Return the populated new matrix */ + return( newmat ); +} + +STATIC MYBOOL mat_setcol(MATrec *mat, int colno, int count, LPSREAL *column, int *rowno, MYBOOL doscale, MYBOOL checkrowmode) +{ + int i, jj = 0, elmnr, orignr, newnr, firstrow; + MYBOOL *addto = NULL, isA, isNZ; + LPSREAL value, saved = 0; + lprec *lp = mat->lp; + + /* Check if we are in row order mode and should add as row instead; + the matrix will be transposed at a later stage */ + if(checkrowmode && mat->is_roworder) + return( mat_setrow(mat, colno, count, column, rowno, doscale, FALSE) ); + + /* Initialize and validate */ + isA = (MYBOOL) (mat == mat->lp->matA); + isNZ = (MYBOOL) (rowno != NULL); + if(!isNZ) + count = mat->lp->rows; + else if((count < 0) || (count > mat->rows+((mat->is_roworder) ? 0 : 1))) + return( FALSE ); + if(isNZ && (count > 0)) { + if(count > 1) + sortREALByINT(column, rowno, count, 0, TRUE); + if((rowno[0] < 0) || (rowno[count-1] > mat->rows)) + return( FALSE ); + } + + /* Capture OF definition in column mode */ + if(isA && !mat->is_roworder) { + if(isNZ && (count > 0) && (rowno[0] == 0)) { + value = column[0]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif + if(doscale) + value = scaled_mat(lp, value, 0, colno); + value = my_chsign(is_maxim(lp), value); + lp->orig_obj[colno] = value; + count--; + column++; + rowno++; + } + else if(!isNZ && (column[0] != 0)) { + value = saved = column[0]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif + if(doscale) + value = scaled_mat(lp, value, 0, colno); + value = my_chsign(is_maxim(lp), value); + lp->orig_obj[colno] = value; + column[0] = 0; + } + else + lp->orig_obj[colno] = 0; + } + + /* Optionally tally and map the new non-zero values */ + firstrow = mat->rows + 1; + if(isNZ) { + newnr = count; + if(newnr) { + firstrow = rowno[0]; + jj = rowno[newnr - 1]; + } + } + else { + newnr = 0; + if(!allocMYBOOL(lp, &addto, mat->rows + 1, TRUE)) { + return( FALSE ); + } + for(i = mat->rows; i >= 0; i--) { + if(fabs(column[i]) > mat->epsvalue) { + addto[i] = TRUE; + firstrow = i; + newnr++; + } + } + } + + /* Make sure we have enough matrix space */ + if(!inc_mat_space(mat, newnr)) { + newnr = 0; + goto Done; + } + + /* Shift existing column data and adjust position indeces */ + orignr = mat_collength(mat, colno); + elmnr = newnr - orignr; + i = mat_nonzeros(mat) - mat->col_end[colno]; + if((elmnr != 0) && (i > 0)) { + COL_MAT_MOVE(mat->col_end[colno] + elmnr, mat->col_end[colno], i); + } + if(elmnr != 0) + for(i = colno; i <= mat->columns; i++) + mat->col_end[i] += elmnr; + + /* We are now ready to copy the new data */ + jj = mat->col_end[colno-1]; + if(isNZ) { + for(i = 0; i < count; jj++, i++) { + value = column[i]; +#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); + } + SET_MAT_ijA(jj, rowno[i], colno, value); + } + } + else { + for(i = firstrow; i <= mat->rows; i++) { + if(!addto[i]) + continue; + value = column[i]; +#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); + } + SET_MAT_ijA(jj, i, colno, value); + jj++; + } + } + mat->row_end_valid = FALSE; + + /* Finish and return */ +Done: + if(saved != 0) + column[0] = saved; + FREE(addto); + return( TRUE ); + +} + +STATIC MYBOOL mat_mergemat(MATrec *target, MATrec *source, MYBOOL usecolmap) +{ + lprec *lp = target->lp; + int i, ix, iy, n, *colmap = NULL; + LPSREAL *colvalue = NULL; + + if((target->rows < source->rows) || !allocREAL(lp, &colvalue, target->rows+1, FALSE)) + return( FALSE ); + + if(usecolmap) { + n = source->col_tag[0]; + allocINT(lp, &colmap, n+1, FALSE); + for(i = 1; i <= n; i++) + colmap[i] = i; + hpsortex(source->col_tag, n, 1, sizeof(*colmap), FALSE, compareINT, colmap); + } + else + n = source->columns; + for(i = 1; i <= n; i++) { + if(!usecolmap && (mat_collength(source, i) == 0)) + continue; + if(usecolmap) { + ix = colmap[i]; + if(ix <= 0) + continue; + iy = source->col_tag[i]; + if(iy <= 0) + continue; + } + else + ix = iy = i; + mat_expandcolumn(source, ix, colvalue, NULL, FALSE); + mat_setcol(target, iy, 0, colvalue, NULL, FALSE, FALSE); + } + + FREE( colvalue ); + FREE( colmap ); + + return( TRUE ); +} + +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) +{ + 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; + + /* 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); + isNZ = (MYBOOL) (colno != NULL); + if(!isNZ) + count = mat->columns; + else if((count < 0) || (count > mat->columns)) + return( FALSE ); + if(isNZ && (count > 0)) { + if(count > 1) + sortREALByINT(row, (int *) colno, count, 0, TRUE); + if((colno[0] < 1) || (colno[count-1] > mat->columns)) + return( FALSE ); + } + + /* Capture OF definition in row mode */ + if(isA && mat->is_roworder) { + lp->orig_obj[rowno] = 0; + if(isNZ && (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; + count--; + row++; + colno++; + } + 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 + lp->orig_obj[rowno] = value; + row[0] = 0; + } + 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) + 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(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)) { + newnr = 0; + goto Done; + } + + /* 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);*/ + 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; + 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 */ + 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]; + for( ; j < colnr; j++) { + /* Shift entries in current column */ + k = mat->col_end[j]; + for( ; jj < k; jj++) { + if(COL_MAT_ROWNR(jj) != rowno) { + COL_MAT_COPY(elmnr, jj); + elmnr++; + } + } + /* 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; + } + + /* 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); + if(jj_j > 0) { + if(!inc_mat_space(mat, jj_j)) { + FREE(addto); + return( FALSE ); + } + if(orignr-jj > 0) { + COL_MAT_MOVE(jj+jj_j, jj, orignr-jj); + } + jj += jj_j; + } + + /* Handle case where the matrix was empty before (or we can simply append) */ + if((delta >= 0) && (mat->col_end[firstcol] == orignr) && 0) { + if(isNZ) + elmnr = count; + else + elmnr = mat->columns; + jj_j = mat->col_end[firstcol]; + for(newnr = 0; newnr < elmnr; newnr++) { + if(isNZ) + colnr = colno[newnr]; + else + colnr = newnr + 1; + /* Update column start position if we have crossed a column */ + while(colnr > firstcol) { + mat->col_end[firstcol] = jj_j; + firstcol++; + } + if(isNZ || ((addto != NULL) && addto[colnr])) { + if(isNZ) + value = row[newnr]; + else + value = row[colnr]; + 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 */ + mat->col_end[firstcol] = jj_j; + firstcol++; + } + } + + /* Make sure we update tail empty column offsets */ + while(firstcol <= mat->columns) { + mat->col_end[firstcol] = jj_j; + firstcol++; + } + jj_j = 0; + } + + /* Start from the top of the first non-zero column of the new row */ + elmnr = orignr + jj_j; + if(jj <= elmnr) { + if(isNZ) + newnr = 0; + else + newnr = firstcol - 1; + j = jj - mat->col_end[firstcol-1]; + colnr = firstcol; + while((jj < elmnr) || (newnr < count)) { + + /* Update column start position if we have crossed a column */ + while(colnr > firstcol) { + mat->col_end[firstcol] = kk; + firstcol++; + } + + /* See if we have a row equal to or greater than the target row */ + jj_j = jj - j; + if(jj < elmnr) { + rownr = COL_MAT_ROWNR(jj); + colnr = COL_MAT_COLNR(jj); + } + else { + rownr = rowno; + if(!isNZ) /* KE added this conditional on 13.9.2006 */ + colnr = firstcol + 1; + else + colnr = mat->columns + 1; + } + + if(isNZ) { + if(newnr < count) + kk = colno[newnr]; + else + kk = mat->columns + 1; + } + else + kk = newnr + 1; + + /* Test if there is an available new item ... */ + 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)) + newnr++; + if(rownr == rowno) { + kk = jj_j; + j++; + jj++; + continue; + } + /* KEEP otherwise and move entry up */ + if(!isNZ && (colnr > kk)) { + colnr = kk; + kk = jj_j; + continue; + } + } + else if((colnr > kk) || /* Existing column index > new => INSERT */ + ((colnr == kk) && (rownr >= rowno)) ) { /* Same column index, existing row >= target row => INSERT/REPLACE */ + + if(isNZ) + value = row[newnr]; + 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 + SET_MAT_ijA(jj_j, rowno, kk, value); + + /* Adjust if we have inserted an element */ + if((colnr > kk) || (rownr > rowno)) { + j--; + jj--; + } + colnr = kk; + kk = jj_j; + jj++; + continue; + } + + /* Shift the matrix element up by the active difference */ + if(jj_j != jj) { + COL_MAT_COPY(jj_j, jj); + } + kk = jj_j; + jj++; + + } + + /* Update pending / incomplete column start position */ + while(colnr > firstcol) { + mat->col_end[firstcol] = kk; + firstcol++; + } + + /* Make sure we update tail column offsets */ + jj_j = jj - j; + while(firstcol <= mat->columns) { + mat->col_end[firstcol] = jj_j; + 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: + if(saved != 0) + row[0] = saved; + FREE(addto); + return( (MYBOOL) (newnr > 0) ); + +} + +#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) +{ + int i, j, jj = 0, stcol, elmnr, orignr, newnr, firstcol; + MYBOOL *addto = NULL, isA, isNZ; + LPSREAL 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 */ + if(checkrowmode && mat->is_roworder) + return( mat_appendcol(mat, count, row, colno, mult, FALSE) ); + + /* Do initialization and validation */ + isA = (MYBOOL) (mat == lp->matA); + isNZ = (MYBOOL) (colno != NULL); + if(isNZ && (count > 0)) { + if(count > 1) + sortREALByINT(row, colno, count, 0, TRUE); + 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) + row[0] = 0; + + /* Capture OF definition in row mode */ + if(isA && mat->is_roworder) { + if(isNZ && (colno[0] == 0)) { + value = row[0]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif + value = scaled_mat(lp, value, 0, lp->columns); + value = my_chsign(is_maxim(lp), value); + lp->orig_obj[lp->columns] = value; + count--; + row++; + colno++; + } + else if(!isNZ && (row != NULL) && (row[0] != 0)) { + value = saved = row[0]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif + value = scaled_mat(lp, value, 0, lp->columns); + value = my_chsign(is_maxim(lp), value); + lp->orig_obj[lp->columns] = value; + row[0] = 0; + } + else + lp->orig_obj[lp->columns] = 0; + } + + /* Optionally tally and map the new non-zero values */ + firstcol = mat->columns + 1; + if(isNZ) { + newnr = count; + if(newnr) { + firstcol = colno[0]; + jj = colno[newnr - 1]; + } + } + 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++; + } + } + } + } + + /* Make sure we have sufficient space */ + if(!inc_mat_space(mat, newnr)) { + newnr = 0; + goto Done; + } + + /* Insert the non-zero constraint values */ + orignr = mat_nonzeros(mat) - 1; + elmnr = orignr + newnr; + + for(j = mat->columns; j >= firstcol; j--) { + stcol = mat->col_end[j] - 1; + mat->col_end[j] = elmnr + 1; + + /* Add a new non-zero entry */ + if(((isNZ) && (j == jj)) || ((addto != NULL) && (addto[j]))) { + newnr--; + if(isNZ) { + value = row[newnr]; + if(newnr) + jj = colno[newnr - 1]; + else + jj = 0; + } + else + value = row[j]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif + value *= mult; + if(isA) { + if(mat->is_roworder) + value = my_chsign(is_chsign(lp, j), value); + value = scaled_mat(lp, value, mat->rows, j); + } + SET_MAT_ijA(elmnr, mat->rows, j, value); + elmnr--; + } + + /* Shift previous column entries down */ + i = stcol - mat->col_end[j-1] + 1; + if(i > 0) { + orignr -= i; + elmnr -= i; + COL_MAT_MOVE(elmnr+1, orignr+1, i); + } + } + +Done: + if(saved != 0) + row[0] = saved; + FREE(addto); + + return( newnr ); + +} + +STATIC int mat_appendcol(MATrec *mat, int count, LPSREAL *column, int *rowno, LPSREAL mult, MYBOOL checkrowmode) +{ + int i, row, elmnr, lastnr; + LPSREAL value; + MYBOOL isA, isNZ; + lprec *lp = mat->lp; + + /* Check if we are in row order mode and should add as row instead; + the matrix will be transposed at a later stage */ + if(checkrowmode && mat->is_roworder) + 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); + if(isNZ && (count > 0)) { + if(count > 1) + sortREALByINT(column, rowno, count, 0, TRUE); + if((rowno[0] < 0)) + return( 0 ); + } + if(rowno != NULL) + count--; + + /* Append sparse regular constraint values */ + elmnr = mat->col_end[mat->columns - 1]; + if(column != NULL) { + row = -1; + for(i = ((isNZ || !mat->is_roworder) ? 0 : 1); i <= count ; i++) { + value = column[i]; + if(fabs(value) > mat->epsvalue) { + if(isNZ) { + lastnr = row; + row = rowno[i]; + /* Check if we have come to the Lagrangean constraints */ + if(row > mat->rows) + break; + if(row <= lastnr) + return( -1 ); + } + else + row = i; +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif + if(mat->is_roworder) + value *= mult; + else if(isA) { + value = my_chsign(is_chsign(lp, row), value); + value = scaled_mat(lp, value, row, mat->columns); + if(!mat->is_roworder && (row == 0)) { + lp->orig_obj[mat->columns] = value; + continue; + } + } + + /* Store the item and update counters */ + SET_MAT_ijA(elmnr, row, mat->columns, value); + elmnr++; + } + } + + /* Fill dense Lagrangean constraints */ + if(get_Lrows(lp) > 0) + mat_appendcol(lp->matL, get_Lrows(lp), column+mat->rows, NULL, mult, checkrowmode); + + } + + /* Set end of data */ + mat->col_end[mat->columns] = elmnr; + + return( mat->col_end[mat->columns] - mat->col_end[mat->columns-1] ); +} + +STATIC int mat_checkcounts(MATrec *mat, int *rownum, int *colnum, MYBOOL freeonexit) +{ + int i, j, n; + int *rownr; + + if(rownum == NULL) + allocINT(mat->lp, &rownum, mat->rows + 1, TRUE); + if(colnum == NULL) + allocINT(mat->lp, &colnum, mat->columns + 1, TRUE); + + for(i = 1 ; i <= mat->columns; i++) { + j = mat->col_end[i - 1]; + n = mat->col_end[i]; + rownr = &COL_MAT_ROWNR(j); + for(; j < n; + j++, rownr += matRowColStep) { + colnum[i]++; + rownum[*rownr]++; + } + } + + n = 0; + if((mat->lp->do_presolve != PRESOLVE_NONE) && + (mat->lp->spx_trace || (mat->lp->verbose > NORMAL))) { + for(j = 1; j <= mat->columns; j++) + if(colnum[j] == 0) { + n++; + report(mat->lp, FULL, "mat_checkcounts: Variable %s is not used in any constraints\n", + get_col_name(mat->lp, j)); + } + for(i = 0; i <= mat->rows; i++) + if(rownum[i] == 0) { + n++; + report(mat->lp, FULL, "mat_checkcounts: Constraint %s empty\n", + get_row_name(mat->lp, i)); + } + } + + if(freeonexit) { + FREE(rownum); + FREE(colnum); + } + + return( n ); + +} + +STATIC MYBOOL mat_validate(MATrec *mat) +/* Routine to make sure that row mapping arrays are valid */ +{ + int i, j, je, *rownum; + int *rownr, *colnr; + + if(!mat->row_end_valid) { + + MEMCLEAR(mat->row_end, mat->rows + 1); + allocINT(mat->lp, &rownum, mat->rows + 1, TRUE); + + /* First tally row counts and then cumulate them */ + j = mat_nonzeros(mat); + rownr = &COL_MAT_ROWNR(0); + for(i = 0; i < j; i++, rownr += matRowColStep) + mat->row_end[*rownr]++; + for(i = 1; i <= mat->rows; i++) + mat->row_end[i] += mat->row_end[i - 1]; + + /* Calculate the column index for every non-zero */ + for(i = 1; i <= mat->columns; i++) { + j = mat->col_end[i - 1]; + je = mat->col_end[i]; + rownr = &COL_MAT_ROWNR(j); + colnr = &COL_MAT_COLNR(j); + for(; j < je; j++, rownr += matRowColStep, colnr += matRowColStep) { +#ifdef Paranoia + if(/*(*colnr < 0) || (*colnr > mat->columns) || (Normally violated in primal phase 1) */ + (*rownr < 0) || (*rownr > mat->rows)) { + report(mat->lp, SEVERE, "mat_validate: Matrix value storage error row %d [0..%d], column %d [1..%d]\n", + *rownr, mat->rows, *colnr, mat->columns); + mat->lp->spx_status = UNKNOWNERROR; + return(FALSE); + } +#endif + *colnr = i; + if(*rownr == 0) + mat_set_rowmap(mat, rownum[*rownr], + *rownr, i, j); + else + mat_set_rowmap(mat, mat->row_end[*rownr - 1] + rownum[*rownr], + *rownr, i, j); + rownum[*rownr]++; + } + } + + FREE(rownum); + mat->row_end_valid = TRUE; + } + + if(mat == mat->lp->matA) + mat->lp->model_is_valid = TRUE; + return( TRUE ); +} + +MYBOOL mat_get_data(lprec *lp, int matindex, MYBOOL isrow, int **rownr, int **colnr, LPSREAL **value) +{ + MATrec *mat = lp->matA; + +#if MatrixRowAccess == RAM_Index + if(isrow) + matindex = mat->row_mat[matindex]; + if(rownr != NULL) + *rownr = &COL_MAT_ROWNR(matindex); + if(colnr != NULL) + *colnr = &COL_MAT_COLNR(matindex); + if(value != NULL) + *value = &COL_MAT_VALUE(matindex); + +#else + if(isrow) { + if(rownr != NULL) + *rownr = &ROW_MAT_ROWNR(matindex); + if(colnr != NULL) + *colnr = &ROW_MAT_COLNR(matindex); + if(value != NULL) + *value = &ROW_MAT_VALUE(matindex); + } + else { + if(rownr != NULL) + *rownr = &COL_MAT_ROWNR(matindex); + if(colnr != NULL) + *colnr = &COL_MAT_COLNR(matindex); + if(value != NULL) + *value = &COL_MAT_VALUE(matindex); + } + +#endif + + return( TRUE ); +} + + +MYBOOL mat_set_rowmap(MATrec *mat, int row_mat_index, int rownr, int colnr, int col_mat_index) +{ +#if MatrixRowAccess == RAM_Index + mat->row_mat[row_mat_index] = col_mat_index; + +#elif MatrixColAccess==CAM_Record + mat->row_mat[row_mat_index].rownr = rownr; + mat->row_mat[row_mat_index].colnr = colnr; + mat->row_mat[row_mat_index].value = COL_MAT_VALUE(col_mat_index); + +#else /* if MatrixColAccess==CAM_Vector */ + mat->row_mat_rownr[row_mat_index] = rownr; + mat->row_mat_colnr[row_mat_index] = colnr; + mat->row_mat_value[row_mat_index] = COL_MAT_VALUE(col_mat_index); + +#endif + + return( TRUE ); +} + +/* Implement combined binary/linear sub-search for matrix look-up */ +int mat_findelm(MATrec *mat, int row, int column) +{ + int low, high, mid, item; + +#if 0 + if(mat->row_end_valid && (row > 0) && + (ROW_MAT_COLNR(mat->row_mat[(low = mat->row_end[row-1])]) == column)) + return(low); +#endif + + if((column < 1) || (column > mat->columns)) { + report(mat->lp, IMPORTANT, "mat_findelm: Column %d out of range\n", column); + return( -1 ); + } + if((row < 0) || (row > mat->rows)) { + report(mat->lp, IMPORTANT, "mat_findelm: Row %d out of range\n", row); + return( -1 ); + } + + low = mat->col_end[column - 1]; + high = mat->col_end[column] - 1; + if(low > high) + return( -2 ); + + /* Do binary search logic */ + mid = (low+high) / 2; + item = COL_MAT_ROWNR(mid); + while(high - low > LINEARSEARCH) { + if(item < row) { + low = mid + 1; + mid = (low+high) / 2; + item = COL_MAT_ROWNR(mid); + } + else if(item > row) { + high = mid - 1; + mid = (low+high) / 2; + item = COL_MAT_ROWNR(mid); + } + else { + low = mid; + high = mid; + } + } + + /* Do linear scan search logic */ + if((high > low) && (high - low <= LINEARSEARCH)) { + item = COL_MAT_ROWNR(low); + while((low < high) && (item < row)) { + low++; + item = COL_MAT_ROWNR(low); + } + if(item == row) + high = low; + } + + if((low == high) && (row == item)) + return( low ); + else + return( -2 ); +} + +int mat_findins(MATrec *mat, int row, int column, int *insertpos, MYBOOL validate) +{ + int low, high, mid, item, exitvalue, insvalue; + +#if 0 + if(mat->row_end_valid && (row > 0) && + (ROW_MAT_COLNR(mat->row_mat[(low = mat->row_end[row-1])]) == column)) { + insvalue = low; + exitvalue = low; + goto Done; + } +#endif + + insvalue = -1; + + if((column < 1) || (column > mat->columns)) { + if((column > 0) && !validate) { + insvalue = mat->col_end[mat->columns]; + exitvalue = -2; + goto Done; + } + report(mat->lp, IMPORTANT, "mat_findins: Column %d out of range\n", column); + exitvalue = -1; + goto Done; + } + if((row < 0) || (row > mat->rows)) { + if((row >= 0) && !validate) { + insvalue = mat->col_end[column]; + exitvalue = -2; + goto Done; + } + report(mat->lp, IMPORTANT, "mat_findins: Row %d out of range\n", row); + exitvalue = -1; + goto Done; + } + + low = mat->col_end[column - 1]; + insvalue = low; + high = mat->col_end[column] - 1; + if(low > high) { + exitvalue = -2; + goto Done; + } + + /* Do binary search logic */ + mid = (low+high) / 2; + item = COL_MAT_ROWNR(mid); + while(high - low > LINEARSEARCH) { + if(item < row) { + low = mid + 1; + mid = (low+high) / 2; + item = COL_MAT_ROWNR(mid); + } + else if(item > row) { + high = mid - 1; + mid = (low+high) / 2; + item = COL_MAT_ROWNR(mid); + } + else { + low = mid; + high = mid; + } + } + + /* Do linear scan search logic */ + if((high > low) && (high - low <= LINEARSEARCH)) { + item = COL_MAT_ROWNR(low); + while((low < high) && (item < row)) { + low++; + item = COL_MAT_ROWNR(low); + } + if(item == row) + high = low; + } + + insvalue = low; + if((low == high) && (row == item)) + exitvalue = low; + else { + if((low < mat->col_end[column]) && (COL_MAT_ROWNR(low) < row)) + insvalue++; + exitvalue = -2; + } + +Done: + if(insertpos != NULL) + (*insertpos) = insvalue; + return( exitvalue ); +} + +STATIC LPSREAL mat_getitem(MATrec *mat, int row, int column) +{ + int elmnr; + +#ifdef DirectOverrideOF + if((row == 0) && (mat == mat->lp->matA) && (mat->lp->OF_override != NULL)) + return( mat->lp->OF_override[column] ); + else +#endif + { + elmnr = mat_findelm(mat, row, column); + if(elmnr >= 0) + return( COL_MAT_VALUE(elmnr) ); + else + return( 0 ); + } +} + +STATIC MYBOOL mat_additem(MATrec *mat, int row, int column, LPSREAL delta) +{ + int elmnr; + +#ifdef DirectOverrideOF + if((row == 0) && (mat == mat->lp->matA) && (mat->lp->OF_override != NULL)) + return( mat->lp->OF_override[column] ); + else +#endif + { + elmnr = mat_findelm(mat, row, column); + if(elmnr >= 0) { + COL_MAT_VALUE(elmnr) += delta; + return( TRUE ); + } + else { + mat_setitem(mat, row, column, delta); + return( FALSE ); + } + } +} + +STATIC MYBOOL mat_setitem(MATrec *mat, int row, int column, LPSREAL value) +{ + return( mat_setvalue(mat, row, column, value, FALSE) ); +} + +STATIC void mat_multrow(MATrec *mat, int row_nr, LPSREAL mult) +{ + int i, k1, k2; + +#if 0 + if(row_nr == 0) { + k2 = mat->col_end[0]; + for(i = 1; i <= mat->columns; i++) { + k1 = k2; + k2 = mat->col_end[i]; + if((k1 < k2) && (COL_MAT_ROWNR(k1) == row_nr)) + COL_MAT_VALUE(k1) *= mult; + } + } + else if(mat_validate(mat)) { + if(row_nr == 0) + k1 = 0; + else +#else + if(mat_validate(mat)) { + if(row_nr == 0) + k1 = 0; + else +#endif + k1 = mat->row_end[row_nr-1]; + k2 = mat->row_end[row_nr]; + for(i = k1; i < k2; i++) + ROW_MAT_VALUE(i) *= mult; + } +} + +STATIC void mat_multcol(MATrec *mat, int col_nr, LPSREAL mult, MYBOOL DoObj) +{ + int i, ie; + MYBOOL isA; + +#ifdef Paranoia + if((col_nr < 1) || (col_nr > mat->columns)) { + report(mat->lp, IMPORTANT, "mult_column: Column %d out of range\n", col_nr); + return; + } +#endif + if(mult == 1.0) + return; + + isA = (MYBOOL) (mat == mat->lp->matA); + + ie = mat->col_end[col_nr]; + 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; + if(get_Lrows(mat->lp) > 0) + mat_multcol(mat->lp->matL, col_nr, mult, DoObj); + } +} + +STATIC void mat_multadd(MATrec *mat, LPSREAL *lhsvector, int varnr, LPSREAL mult) +{ + int colnr; + register int ib, ie, *matRownr; + register LPSREAL *matValue; + + /* Handle case of a slack variable */ + if(varnr <= mat->lp->rows) { + lhsvector[varnr] += mult; + return; + } + + /* Do operation on the objective */ + if(mat->lp->matA == mat) + lhsvector[0] += get_OF_active(mat->lp, varnr, mult); + + /* Scan the constraint matrix target columns */ + colnr = varnr - mat->lp->rows; + ib = mat->col_end[colnr - 1]; + ie = mat->col_end[colnr]; + if(ib < ie) { + + /* Initialize pointers */ + matRownr = &COL_MAT_ROWNR(ib); + matValue = &COL_MAT_VALUE(ib); + + /* Then loop over all regular rows */ + for(; ib < ie; + ib++, matValue += matValueStep, matRownr += matRowColStep) { + lhsvector[*matRownr] += mult * (*matValue); + } + } + +} + +STATIC MYBOOL mat_setvalue(MATrec *mat, int Row, int Column, LPSREAL Value, MYBOOL doscale) +{ + int elmnr, lastelm, i, RowA = Row, ColumnA = Column; + MYBOOL isA; + + /* This function is inefficient if used to add new matrix entries in + other places than at the end of the matrix. OK for replacing existing + a non-zero value with another non-zero value */ + isA = (MYBOOL) (mat == mat->lp->matA); + if(mat->is_roworder) + swapINT(&Row, &Column); + + /* Set small numbers to zero */ + if(fabs(Value) < mat->epsvalue) + Value = 0; +#ifdef DoMatrixRounding + else + Value = roundToPrecision(Value, mat->epsvalue); +#endif + + /* Check if we need to update column space */ + if(Column > mat->columns) { + if(isA) + inc_col_space(mat->lp, ColumnA - mat->columns); + else + inc_matcol_space(mat, Column - mat->columns); + } + + /* Find out if we already have such an entry, or return insertion point */ + i = mat_findins(mat, Row, Column, &elmnr, FALSE); + if(i == -1) + return(FALSE); + + if(isA) + set_action(&mat->lp->spx_action, ACTION_REBASE | ACTION_RECOMPUTE | ACTION_REINVERT); + + if(i >= 0) { + /* there is an existing entry */ + if(fabs(Value) > mat->epsvalue) { /* we replace it by something non-zero */ + if(isA) { + Value = my_chsign(is_chsign(mat->lp, RowA), Value); + if(doscale && mat->lp->scaling_used) + Value = scaled_mat(mat->lp, Value, RowA, ColumnA); + } + COL_MAT_VALUE(elmnr) = Value; + } + else { /* setting existing non-zero entry to zero. Remove the entry */ + /* This might remove an entire column, or leave just a bound. No + nice solution for that yet */ + + /* Shift up tail end of the matrix */ + lastelm = mat_nonzeros(mat); +#if 0 + for(i = elmnr; i < lastelm ; i++) { + COL_MAT_COPY(i, i + 1); + } +#else + lastelm -= elmnr; + COL_MAT_MOVE(elmnr, elmnr + 1, lastelm); +#endif + for(i = Column; i <= mat->columns; i++) + mat->col_end[i]--; + + mat->row_end_valid = FALSE; + } + } + else if(fabs(Value) > mat->epsvalue) { + /* no existing entry. make new one only if not nearly zero */ + /* check if more space is needed for matrix */ + if(!inc_mat_space(mat, 1)) + return(FALSE); + + if(Column > mat->columns) { + i = mat->columns + 1; + if(isA) + shift_coldata(mat->lp, i, ColumnA - mat->columns, NULL); + else + mat_shiftcols(mat, &i, Column - mat->columns, NULL); + } + + /* Shift down tail end of the matrix by one */ + lastelm = mat_nonzeros(mat); +#if 1 /* Does compiler optimization work better here? */ + for(i = lastelm; i > elmnr ; i--) { + COL_MAT_COPY(i, i - 1); + } +#else + lastelm -= elmnr - 1; + COL_MAT_MOVE(elmnr + 1, elmnr, lastelm); +#endif + + /* Set new element */ + if(isA) { + Value = my_chsign(is_chsign(mat->lp, RowA), Value); + if(doscale) + Value = scaled_mat(mat->lp, Value, RowA, ColumnA); + } + SET_MAT_ijA(elmnr, Row, Column, Value); + + /* Update column indexes */ + for(i = Column; i <= mat->columns; i++) + mat->col_end[i]++; + + mat->row_end_valid = FALSE; + } + + if(isA && (mat->lp->var_is_free != NULL) && (mat->lp->var_is_free[ColumnA] > 0)) + return( mat_setvalue(mat, RowA, mat->lp->var_is_free[ColumnA], -Value, doscale) ); + return(TRUE); +} + +STATIC MYBOOL mat_appendvalue(MATrec *mat, int Row, LPSREAL Value) +{ + int *elmnr, Column = mat->columns; + + /* Set small numbers to zero */ + if(fabs(Value) < mat->epsvalue) + Value = 0; +#ifdef DoMatrixRounding + else + Value = roundToPrecision(Value, mat->epsvalue); +#endif + + /* Check if more space is needed for matrix */ + if(!inc_mat_space(mat, 1)) + return(FALSE); + +#ifdef Paranoia + /* Check valid indeces */ + if((Row < 0) || (Row > mat->rows)) { + report(mat->lp, SEVERE, "mat_appendvalue: Invalid row index %d specified\n", Row); + return(FALSE); + } +#endif + + /* Get insertion point and set value */ + elmnr = mat->col_end + Column; + SET_MAT_ijA((*elmnr), Row, Column, Value); + + /* Update column count */ + (*elmnr)++; + mat->row_end_valid = FALSE; + + return(TRUE); +} + +STATIC MYBOOL mat_equalRows(MATrec *mat, int baserow, int comprow) +{ + MYBOOL status = FALSE; + + if(mat_validate(mat)) { + int bj1 = 0, ej1, bj2 = 0, ej2; + + /* Get starting and ending positions */ + if(baserow >= 0) + bj1 = mat->row_end[baserow-1]; + ej1 = mat->row_end[baserow]; + if(comprow >= 0) + bj2 = mat->row_end[comprow-1]; + ej2 = mat->row_end[comprow]; + /* Fail if row lengths are unequal */ + if((ej1-bj1) != (ej2-bj2)) + return( status ); + + /* Compare column index and value, element by element */ + for(; bj1 < ej1; bj1++, bj2++) { + if(COL_MAT_COLNR(bj1) != COL_MAT_COLNR(bj2)) + break; +#if 1 + if(fabs(get_mat_byindex(mat->lp, bj1, TRUE, FALSE)-get_mat_byindex(mat->lp, bj2, TRUE, FALSE)) > mat->lp->epsprimal) +#else + if(fabs(COL_MAT_VALUE(bj1)-COL_MAT_VALUE(bj2)) > mat->lp->epsprimal) +#endif + break; + } + status = (MYBOOL) (bj1 == ej1); + } + return( status ); +} + +STATIC int mat_findcolumn(MATrec *mat, int matindex) +{ + int j; + + for(j = 1; j <= mat->columns; j++) { + if(matindex < mat->col_end[j]) + break; + } + return(j); +} + +STATIC int mat_expandcolumn(MATrec *mat, int colnr, LPSREAL *column, int *nzlist, MYBOOL signedA) +{ + MYBOOL isA = (MYBOOL) (mat->lp->matA == mat); + int i, ie, j, nzcount = 0; + LPSREAL *matValue; + int *matRownr; + + signedA &= isA; + + /* Retrieve a column from the user data matrix A */ + MEMCLEAR(column, mat->rows + 1); + if(isA) { + column[0] = mat->lp->orig_obj[colnr]; + if(signedA && is_chsign(mat->lp, 0)) + column[0] = -column[0]; + } + + i = mat->col_end[colnr - 1]; + ie = mat->col_end[colnr]; + matRownr = &COL_MAT_ROWNR(i); + matValue = &COL_MAT_VALUE(i); + for(; i < ie; + i++, matRownr += matRowColStep, matValue += matValueStep) { + j = *matRownr; + column[j] = *matValue; + if(signedA && is_chsign(mat->lp, j)) + column[j] = -column[j]; + nzcount++; + if(nzlist != NULL) + nzlist[nzcount] = j; + } + if(nzlist != NULL) + nzlist[0] = nzcount; + return( nzcount ); +} + +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; + + /* Prepare arrays */ + if(!allocREAL(mat->lp, &mat->colmax, mat->columns_alloc+1, AUTOMATIC) || + !allocREAL(mat->lp, &mat->rowmax, mat->rows_alloc+1, AUTOMATIC)) + return( FALSE ); + MEMCLEAR(mat->colmax, mat->columns+1); + MEMCLEAR(mat->rowmax, mat->rows+1); + + /* Obtain the row and column maxima in one sweep */ + mat->dynrange = mat->lp->infinite; + for(; i < ie; + i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) { + absvalue = fabs(*value); + SETMAX(mat->colmax[*colnr], absvalue); + SETMAX(mat->rowmax[*rownr], absvalue); + SETMIN(mat->dynrange, absvalue); + if(absvalue < epsmachine) + ez++; + } + + /* Lastly, compute the global maximum and get the dynamic range */ + for(i = 1; i <= mat->rows; i++) + SETMAX(mat->rowmax[0], mat->rowmax[i]); + mat->infnorm = mat->colmax[0] = mat->rowmax[0]; + if(mat->dynrange == 0) { + report(mat->lp, SEVERE, "%d matrix contains zero-valued coefficients.\n", ez); + mat->dynrange = mat->lp->infinite; + } + else { + mat->dynrange = mat->infnorm / mat->dynrange; + if(ez > 0) + report(mat->lp, IMPORTANT, "%d matrix coefficients below machine precision were found.\n", ez); + } + + return( TRUE ); +} + +STATIC MYBOOL mat_transpose(MATrec *mat) +{ + int i, j, nz, k; + MYBOOL status; + + status = mat_validate(mat); + if(status) { + + /* Create a column-ordered sparse element list; "column" index must be shifted */ + nz = mat_nonzeros(mat); + if(nz > 0) { +#if MatrixColAccess==CAM_Record + MATitem *newmat; + newmat = (MATitem *) malloc((mat->mat_alloc) * sizeof(*(mat->col_mat))); + j = mat->row_end[0]; + for(i = nz-1; i >= j ; i--) { + k = i-j; + newmat[k] = mat->col_mat[mat->row_mat[i]]; + newmat[k].row_nr = newmat[k].col_nr; + } + for(i = j-1; i >= 0 ; i--) { + k = nz-j+i; + newmat[k] = mat->col_mat[mat->row_mat[i]]; + newmat[k].row_nr = newmat[k].col_nr; + } + swapPTR((void **) &mat->col_mat, (void **) &newmat); + FREE(newmat); +#else /*if MatrixColAccess==CAM_Vector*/ + LPSREAL *newValue = NULL; + int *newRownr = NULL; + allocREAL(mat->lp, &newValue, mat->mat_alloc, FALSE); + allocINT(mat->lp, &newRownr, mat->mat_alloc, FALSE); + + j = mat->row_end[0]; + for(i = nz-1; i >= j ; i--) { + k = i-j; + newValue[k] = ROW_MAT_VALUE(i); + newRownr[k] = ROW_MAT_COLNR(i); + } + for(i = j-1; i >= 0 ; i--) { + k = nz-j+i; + newValue[k] = ROW_MAT_VALUE(i); + newRownr[k] = ROW_MAT_COLNR(i); + } + + swapPTR((void **) &mat->col_mat_rownr, (void **) &newRownr); + swapPTR((void **) &mat->col_mat_value, (void **) &newValue); + FREE(newValue); + FREE(newRownr); +#endif + } + + /* Transfer row start to column start position; must adjust for different offsets */ + if(mat->rows == mat->rows_alloc) + inc_matcol_space(mat, 1); + j = mat->row_end[0]; + for(i = mat->rows; i >= 1; i--) + mat->row_end[i] -= j; + mat->row_end[mat->rows] = nz; + swapPTR((void **) &mat->row_end, (void **) &mat->col_end); + + /* Swap arrays of maximum values */ + swapPTR((void **) &mat->rowmax, (void **) &mat->colmax); + + /* Swap array sizes */ + swapINT(&mat->rows, &mat->columns); + swapINT(&mat->rows_alloc, &mat->columns_alloc); + + /* Finally set current storage mode */ + mat->is_roworder = (MYBOOL) !mat->is_roworder; + mat->row_end_valid = FALSE; + } + return(status); +} + + +/* ---------------------------------------------------------------------------------- */ +/* Change-tracking routines */ +/* ---------------------------------------------------------------------------------- */ +STATIC DeltaVrec *createUndoLadder(lprec *lp, int levelitems, int maxlevels) +{ + DeltaVrec *hold; + + hold = (DeltaVrec *) malloc(sizeof(*hold)); + hold->lp = lp; + hold->activelevel = 0; + hold->tracker = mat_create(lp, levelitems, 0, 0.0); + inc_matcol_space(hold->tracker, maxlevels); + return( hold ); +} +STATIC int incrementUndoLadder(DeltaVrec *DV) +{ + DV->activelevel++; + inc_matcol_space(DV->tracker, 1); + mat_shiftcols(DV->tracker, &(DV->activelevel), 1, NULL); + DV->tracker->columns++; + return(DV->activelevel); +} +STATIC MYBOOL modifyUndoLadder(DeltaVrec *DV, int itemno, LPSREAL target[], LPSREAL newvalue) +{ + MYBOOL status; + int varindex = itemno; + LPSREAL oldvalue = target[itemno]; + +#ifndef UseMilpSlacksRCF /* Check if we should include ranged constraints */ + varindex -= DV->lp->rows; +#endif + status = mat_appendvalue(DV->tracker, varindex, oldvalue); + target[itemno] = newvalue; + return(status); +} +STATIC int countsUndoLadder(DeltaVrec *DV) +{ + if(DV->activelevel > 0) + return( mat_collength(DV->tracker, DV->activelevel) ); + else + return( 0 ); +} +STATIC int restoreUndoLadder(DeltaVrec *DV, LPSREAL target[]) +{ + int iD = 0; + + if(DV->activelevel > 0) { + MATrec *mat = DV->tracker; + 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), + oldvalue; + + /* Restore the values */ + iD = iE-iB; + for(; iB < iE; iB++, matValue += matValueStep, matRownr += matRowColStep) { + oldvalue = *matValue; +#ifdef UseMilpSlacksRCF /* Check if we should include ranged constraints */ + target[(*matRownr)] = oldvalue; +#else + target[DV->lp->rows+(*matRownr)] = oldvalue; +#endif + } + + /* Get rid of the changes */ + mat_shiftcols(DV->tracker, &(DV->activelevel), -1, NULL); + } + + return(iD); +} +STATIC int decrementUndoLadder(DeltaVrec *DV) +{ + int deleted = 0; + + if(DV->activelevel > 0) { + deleted = mat_shiftcols(DV->tracker, &(DV->activelevel), -1, NULL); + DV->activelevel--; + DV->tracker->columns--; + } + return(deleted); +} +STATIC MYBOOL freeUndoLadder(DeltaVrec **DV) +{ + if((DV == NULL) || (*DV == NULL)) + return(FALSE); + + mat_free(&((*DV)->tracker)); + FREE(*DV); + return(TRUE); +} + +STATIC MYBOOL appendUndoPresolve(lprec *lp, MYBOOL isprimal, LPSREAL beta, int colnrDep) +{ + MATrec *mat; + + /* Point to correct undo structure */ + if(isprimal) + mat = lp->presolve_undo->primalundo->tracker; + else + mat = lp->presolve_undo->dualundo->tracker; + + /* Append the data */ + if((colnrDep > 0) && (beta != 0) && + (mat != NULL) && (mat->col_tag[0] > 0)) { + int ix = mat->col_tag[0]; +#if 0 + report(lp, NORMAL, "appendUndoPresolve: %s %g * x%d\n", + ( beta < 0 ? "-" : "+"), fabs(beta), colnrDep); +#endif + + /* Do normal user variable case */ + if(colnrDep <= lp->columns) + mat_setvalue(mat, colnrDep, ix, beta, FALSE); + + /* Handle case where a slack variable is referenced */ + else { + int ipos, jx = mat->col_tag[ix]; + mat_setvalue(mat, jx, ix, beta, FALSE); + jx = mat_findins(mat, jx, ix, &ipos, FALSE); + COL_MAT_ROWNR(ipos) = colnrDep; + } + return( TRUE ); + } + else + return( FALSE ); +} +STATIC MYBOOL addUndoPresolve(lprec *lp, MYBOOL isprimal, int colnrElim, LPSREAL alpha, LPSREAL beta, int colnrDep) +{ + int ix; + DeltaVrec **DV; + MATrec *mat; + presolveundorec *psdata = lp->presolve_undo; + + /* Point to and initialize undo structure at first call */ + if(isprimal) { + DV = &(psdata->primalundo); + if(*DV == NULL) { + *DV = createUndoLadder(lp, lp->columns+1, lp->columns); + mat = (*DV)->tracker; + mat->epsvalue = lp->matA->epsvalue; + allocINT(lp, &(mat->col_tag), lp->columns+1, FALSE); + mat->col_tag[0] = 0; + } + } + else { + DV = &(psdata->dualundo); + if(*DV == NULL) { + *DV = createUndoLadder(lp, lp->rows+1, lp->rows); + mat = (*DV)->tracker; + mat->epsvalue = lp->matA->epsvalue; + allocINT(lp, &(mat->col_tag), lp->rows+1, FALSE); + mat->col_tag[0] = 0; + } + } + mat = (*DV)->tracker; +#if 0 + report(lp, NORMAL, "addUndoPresolve: x%d = %g %s %g * x%d\n", + colnrElim, alpha, ( beta < 0 ? "-" : "+"), fabs(beta), colnrDep); +#endif + /* Add the data */ + ix = mat->col_tag[0] = incrementUndoLadder(*DV); + mat->col_tag[ix] = colnrElim; + if(alpha != 0) + mat_setvalue(mat, 0, ix, alpha, FALSE); +/* mat_appendvalue(*mat, 0, alpha);*/ + if((colnrDep > 0) && (beta != 0)) { + if(colnrDep > lp->columns) + return( appendUndoPresolve(lp, isprimal, beta, colnrDep) ); + else + mat_setvalue(mat, colnrDep, ix, beta, FALSE); + } + + return( TRUE ); +} + + + +/* ---------------------------------------------------------------------------------- */ +/* High level matrix inverse and product routines in lp_solve */ +/* ---------------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------------- */ +/* A brief description of the basis inverse and factorization logic in lp_solve */ +/* ---------------------------------------------------------------------------------- */ +/* + + In order to better understand the legacy code for operating with the + basis and its factorization in lp_solve I (KE) will briefly explain + the conventions and associated matrix algebra. Note that with lp_solve + version 5.5, it is also possible to direct lp_solve to use the traditional + (textbook) format by setting the obj_in_B parameter to FALSE. + + The matrix description of a linear program (as represented by lp_solve) goes + like this: + + maximize c'x + subject to r <= Ax <= b + where l <= x <= u + + The matrix A is partitioned into two column sets [B|N], where B is + a square matrix of "basis" variables containing non-fixed + variables of the linear program at any given stage and N is the + submatrix of corresponding non-basic, fixed variables. The + variables (columns) in N may be fixed at their lower or upper levels. + + Similarly, the c vector is partitioned into the basic and non-basic + parts [z|n]. + + While lp_solve stores the objective vector c in a dense format, and + the constraint matrix A in a (fairly standard) sparse format, the + column vectors passed to the factorization routine include the + objective coefficient at row index 0. (In versions of lp_solve + before v5.2, c was actually explicitly stored as the 0-th row of A). + The expanded matrix may be called the "A~" form and looks like this: + + A~ = [ c ] + [ A ] + + Linear programming involves solving linear equations based on the + square basis matrix B, which includes is a subset of columns from A~. + The implications of the common storage of c and A (e.g. A~) vs. the + inverse / factorization of B for the operations and updates performed + by the simplex routine therefore needs to be understood. As a consquence + of A~, in lp_solve B is stored in an expanded, bordered format using the + following (non-singular) representation: + + B~ = [ 1 z ] + [ 0 B ] + + Any basis inversion / factorization engine used by lp_solve must therefore + explicitly represent and handle the implications of this structure for + associated matrix operations. + + The standard matrix formula for computing the inverse of a bordered + matrix shows what the inversion of B~ actually produces: + + Inv(B~) = [ 1 -z*Inv(B) ] + [ 0 Inv(B) ] + + The A~ and B~ representations require awareness by the developer of the side + effects of the presence of the top row when doing product operations such as + b'N, btran and ftran. Note in particular z*Inv(B) in the top row of Inv(B~), + which is actually the dual solution vector of the given basis. This fact + makes a very common update in the simplex algorithm (reduced costs) returnable + as a vector simply by setting 1 at the top of a vector being pre-multiplied + with Inv(B~). + + However, if the objective vector (c) is changed, the expanded representation + requires that B / B~ be refactorized. Also, when doing FTRAN, BTRAN + and x'A-type operations, you will patently get the incorrect result + if you simply copy the operations given in textbooks. First I'll show the + results of an FTRAN operation: + + Bx = a ==> x = FTRAN(a) + + In lp_solve, this operation solves: + + [ 1 z ] [y] = [d] + [ 0 B ] [x] [a] + + Using the Inv(B~) expression earlier, the FTRAN result is therefore: + + [y] = [ 1 -z*Inv(B) ] [d] = [ d - z*Inv(B)*a ] + [x] [ 0 Inv(B) ] [a] [ Inv(B)*a ] + + As an example, the value of the dual objective can be returned at the + 0-th index by passing the active RHS vector with 0 at the 0-th position. + + Similarily, doing the left solve - performing the BTRAN calculation: + + [x y] [ 1 z ] = [d a'] + [ 0 B ] + + ... will produce the following result in lp_solve: + + [x y] = [d a'] [ 1 -z*Inv(B) ] = [ d | -d*z*Inv(B) + a'*Inv(B) ] + [ 0 Inv(B) ] + + So, if you thought you were simply computing "a'*Inv(B)", look again. + In order to produce the desired result, you have to set d to 0 before + the BTRAN operation. On the other hand, if you set d to 1 and a to 0, + then you are very conveniently on your way to obtain the reduced costs + (needs a further matrix premultiplication with non-basic variables). + + Incidentally, the BTRAN with [1 0] that yields [ 1 | -z*Inv(B) ] can + also be used as a fast way of checking the accuracy of the current + factorization. + + Equipped with this understanding, I hope that you see that + the approach in lp_solve is actually pretty convenient. It also + becomes easier to extend functionality in lp_solve by drawing on + formulas and expressions from LP literature that otherwise assume + the non-bordered syntax and representation. + + Kjell Eikland -- November 2003 + KE update -- April 2005 + KE update -- June 2005 + +*/ + +STATIC MYBOOL __WINAPI invert(lprec *lp, MYBOOL shiftbounds, MYBOOL final) +{ + MYBOOL *usedpos, resetbasis; + LPSREAL test; + int k, i, j; + int singularities, usercolB; + + /* Make sure the tags are correct */ + if(!mat_validate(lp->matA)) { + lp->spx_status = INFEASIBLE; + return(FALSE); + } + + /* Create the inverse management object at the first call to invert() */ + if(lp->invB == NULL) + lp->bfp_init(lp, lp->rows, 0, NULL); + else + lp->bfp_preparefactorization(lp); + singularities = 0; + + /* Must save spx_status since it is used to carry information about + the presence and handling of singular columns in the matrix */ + if(userabort(lp, MSG_INVERT)) + return(FALSE); + +#ifdef Paranoia + if(lp->spx_trace) + report(lp, DETAILED, "invert: Iter %10g, fact-length %7d, OF " RESULTVALUEMASK ".\n", + (double) get_total_iter(lp), lp->bfp_colcount(lp), (double) -lp->rhs[0]); +#endif + + /* Store state of pre-existing basis, and at the same time check if + the basis is I; in this case take the easy way out */ + if(!allocMYBOOL(lp, &usedpos, lp->sum + 1, TRUE)) { + lp->bb_break = TRUE; + return(FALSE); + } + usedpos[0] = TRUE; + usercolB = 0; + for(i = 1; i <= lp->rows; i++) { + k = lp->var_basic[i]; + if(k > lp->rows) + usercolB++; + usedpos[k] = TRUE; + } +#ifdef Paranoia + if(!verify_basis(lp)) + report(lp, SEVERE, "invert: Invalid basis detected (iter %g).\n", + (double) get_total_iter(lp)); +#endif + + /* Tally matrix nz-counts and check if we should reset basis + indicators to all slacks */ + resetbasis = (MYBOOL) ((usercolB > 0) && lp->bfp_canresetbasis(lp)); + k = 0; + for(i = 1; i <= lp->rows; i++) { + if(lp->var_basic[i] > lp->rows) + k += mat_collength(lp->matA, lp->var_basic[i] - lp->rows) + (is_OF_nz(lp,lp->var_basic[i] - lp->rows) ? 1 : 0); + if(resetbasis) { + j = lp->var_basic[i]; + if(j > lp->rows) + lp->is_basic[j] = FALSE; + lp->var_basic[i] = i; + lp->is_basic[i] = TRUE; + } + } + + /* Now do the refactorization */ + singularities = lp->bfp_factorize(lp, usercolB, k, usedpos, final); + + /* Do user reporting */ + if(userabort(lp, MSG_INVERT)) + goto Cleanup; + + /* Finalize factorization/inversion */ + 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 */ + test = get_refactfrequency(lp, FALSE); + if(test < MIN_REFACTFREQUENCY) { + test = get_refactfrequency(lp, TRUE); + report(lp, NORMAL, "invert: Refactorization frequency %.1g indicates numeric instability.\n", + test); + lp->spx_status = NUMFAILURE; + } + + FREE(usedpos); + return((MYBOOL) (singularities <= 0)); +} /* invert */ + + +STATIC MYBOOL fimprove(lprec *lp, LPSREAL *pcol, int *nzidx, LPSREAL roundzero) +{ + LPSREAL *errors, sdp; + int j; + MYBOOL Ok = TRUE; + + allocREAL(lp, &errors, lp->rows + 1, FALSE); + if(errors == NULL) { + Ok = FALSE; + return(Ok); + } + MEMCOPY(errors, pcol, lp->rows + 1); + lp->bfp_ftran_normal(lp, pcol, nzidx); + prod_Ax(lp, NULL, pcol, NULL, 0.0, -1, + errors, NULL, MAT_ROUNDDEFAULT); + lp->bfp_ftran_normal(lp, errors, NULL); + + sdp = 0; + for(j = 1; j <= lp->rows; j++) + if(fabs(errors[j])>sdp) + sdp = fabs(errors[j]); + if(sdp > lp->epsmachine) { + report(lp, DETAILED, "Iterative FTRAN correction metric %g", sdp); + for(j = 1; j <= lp->rows; j++) { + pcol[j] += errors[j]; + my_roundzero(pcol[j], roundzero); + } + } + FREE(errors); + return(Ok); +} + +STATIC MYBOOL bimprove(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero) +{ + int j; + LPSREAL *errors, err, maxerr; + MYBOOL Ok = TRUE; + + allocREAL(lp, &errors, lp->sum + 1, FALSE); + if(errors == NULL) { + Ok = FALSE; + return(Ok); + } + MEMCOPY(errors, rhsvector, lp->sum + 1); + + /* Solve Ax=b for x, compute b back */ + lp->bfp_btran_normal(lp, errors, nzidx); + prod_xA(lp, NULL, errors, NULL, 0.0, 1.0, + errors, NULL, + MAT_ROUNDDEFAULT); + + /* Take difference with ingoing values, while shifting the column values + to the rows section and zeroing the columns again */ + for(j = 1; j <= lp->rows; j++) + errors[j] = errors[lp->rows+lp->var_basic[j]] - rhsvector[j]; + for(j = lp->rows; j <= lp->sum; j++) + errors[j] = 0; + + /* Solve the b errors for the iterative x adjustment */ + lp->bfp_btran_normal(lp, errors, NULL); + + /* Generate the adjustments and compute statistic */ + maxerr = 0; + for(j = 1; j <= lp->rows; j++) { + if(lp->var_basic[j]<=lp->rows) continue; + err = errors[lp->rows+lp->var_basic[j]]; + if(fabs(err)>maxerr) + maxerr = fabs(err); + } + if(maxerr > lp->epsmachine) { + report(lp, DETAILED, "Iterative BTRAN correction metric %g", maxerr); + for(j = 1; j <= lp->rows; j++) { + if(lp->var_basic[j]<=lp->rows) continue; + rhsvector[j] += errors[lp->rows+lp->var_basic[j]]; + my_roundzero(rhsvector[j], roundzero); + } + } + FREE(errors); + return(Ok); +} + +STATIC void ftran(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero) +{ +#if 0 + if(is_action(lp->improve, IMPROVE_SOLUTION) && lp->bfp_pivotcount(lp)) + fimprove(lp, rhsvector, nzidx, roundzero); + else +#endif + lp->bfp_ftran_normal(lp, rhsvector, nzidx); +} + +STATIC void btran(lprec *lp, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero) +{ +#if 0 + if(is_action(lp->improve, IMPROVE_SOLUTION) && lp->bfp_pivotcount(lp)) + bimprove(lp, rhsvector, nzidx, roundzero); + else +#endif + lp->bfp_btran_normal(lp, rhsvector, nzidx); +} + +STATIC MYBOOL fsolve(lprec *lp, int varin, LPSREAL *pcol, int *nzidx, LPSREAL roundzero, LPSREAL ofscalar, MYBOOL prepareupdate) +/* Was setpivcol in versions earlier than 4.0.1.8 - KE */ +{ + MYBOOL ok = TRUE; + + if(varin > 0) + obtain_column(lp, varin, pcol, nzidx, NULL); + + /* Solve, adjusted for objective function scalar */ + pcol[0] *= ofscalar; + if(prepareupdate) + lp->bfp_ftran_prepare(lp, pcol, nzidx); + else + ftran(lp, pcol, nzidx, roundzero); + + return(ok); + +} /* fsolve */ + + +STATIC MYBOOL bsolve(lprec *lp, int row_nr, LPSREAL *rhsvector, int *nzidx, LPSREAL roundzero, LPSREAL ofscalar) +{ + MYBOOL ok = TRUE; + + if(row_nr >= 0) /* Note that row_nr == 0 returns the [1, 0...0 ] vector */ + row_nr = obtain_column(lp, row_nr, rhsvector, nzidx, NULL); + + /* Solve, adjusted for objective function scalar */ + rhsvector[0] *= ofscalar; + btran(lp, rhsvector, nzidx, roundzero); + + return(ok); + +} /* bsolve */ + + +/* Vector compression and expansion routines */ +STATIC MYBOOL vec_compress(LPSREAL *densevector, int startpos, int endpos, LPSREAL epsilon, + LPSREAL *nzvector, int *nzindex) +{ + int n; + + if((densevector == NULL) || (nzindex == NULL) || (startpos > endpos)) + return( FALSE ); + + n = 0; + densevector += startpos; + while(startpos <= endpos) { + if(fabs(*densevector) > epsilon) { /* Apply zero-threshold */ + if(nzvector != NULL) /* Only produce index if no nzvector is given */ + nzvector[n] = *densevector; + n++; + nzindex[n] = startpos; + } + startpos++; + densevector++; + } + nzindex[0] = n; + return( TRUE ); +} + +STATIC MYBOOL vec_expand(LPSREAL *nzvector, int *nzindex, LPSREAL *densevector, int startpos, int endpos) +{ + int i, n; + + n = nzindex[0]; + i = nzindex[n]; + densevector += endpos; + while(endpos >= startpos) { /* Loop from behind to allow densevector == nzvector */ + if(endpos == i) { + n--; + *densevector = nzvector[n]; + i = nzindex[n]; + } + else + *densevector = 0; + endpos--; + densevector--; + } + return( TRUE ); +} + + +/* ----------------------------------------------------------------------- */ +/* Sparse matrix product routines and utility */ +/* ----------------------------------------------------------------------- */ + +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; + + /* Find what variable range to scan - default is {SCAN_USERVARS} */ + /* First determine the starting position; add from the top, going down */ + P1extraDim = abs(lp->P1extraDim); + vb = nrows + 1; + if(varset & SCAN_ARTIFICIALVARS) + vb = nsum - P1extraDim + 1; + if(varset & SCAN_USERVARS) + vb = nrows + 1; + if(varset & SCAN_SLACKVARS) + vb = 1; + + /* Then determine the ending position, add from the bottom, going up */ + ve = nsum; + if(varset & SCAN_SLACKVARS) + ve = nrows; + if(varset & SCAN_USERVARS) + ve = nsum - P1extraDim; + if(varset & SCAN_ARTIFICIALVARS) + ve = nsum; + + /* Adjust for partial pricing */ + if(varset & SCAN_PARTIALBLOCK) { + SETMAX(vb, partial_blockStart(lp, FALSE)); + SETMIN(ve, partial_blockEnd(lp, FALSE)); + } + + /* Determine exclusion columns */ + omitfixed = (MYBOOL) ((varset & OMIT_FIXED) != 0); + omitnonfixed = (MYBOOL) ((varset & OMIT_NONFIXED) != 0); + if(omitfixed && omitnonfixed) + return(FALSE); + + /* Scan the target colums */ + if(append) + n = colindex[0]; + else + n = 0; + for(varnr = vb; varnr <= ve; varnr++) { + + /* Skip gap in the specified column scan range (possibly user variables) */ + if(varnr > nrows) { + if((varnr <= nsum-P1extraDim) && !(varset & SCAN_USERVARS)) + continue; +#if 1 + /* Skip empty columns */ + if(/*(lp->P1extraVal == 0) &&*/ + (mat_collength(lp->matA, varnr-nrows) == 0)) + continue; +#endif + } + + /* Find if the variable is in the scope - default is {�} */ + i = lp->is_basic[varnr]; + if((varset & USE_BASICVARS) > 0 && (i)) + ; + else if((varset & USE_NONBASICVARS) > 0 && (!i)) + ; + else + continue; + + v = lp->upbo[varnr]; + if((omitfixed && (v == 0)) || + (omitnonfixed && (v != 0))) + continue; + + /* Append to list */ + n++; + colindex[n] = varnr; + } + colindex[0] = n; + + return(TRUE); +} + +STATIC int prod_Ax(lprec *lp, int *coltarget, LPSREAL *input, int *nzinput, + LPSREAL roundzero, LPSREAL ofscalar, + LPSREAL *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, ve; + MYBOOL localset, localnz = FALSE, isRC; + MATrec *mat = lp->matA; + LPSREAL sdp; + LPSREAL *value; + int *rownr; + + /* Find what variable range to scan - default is {SCAN_USERVARS} */ + /* Define default column target if none was provided */ + isRC = (MYBOOL) ((roundmode & MAT_ROUNDRC) != 0); + localset = (MYBOOL) (coltarget == NULL); + if(localset) { + int varset = SCAN_SLACKVARS | SCAN_USERVARS | + USE_BASICVARS | OMIT_FIXED; + if(isRC && is_piv_mode(lp, PRICE_PARTIAL) && !is_piv_mode(lp, PRICE_FORCEFULL)) + varset |= SCAN_PARTIALBLOCK; + coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->sum+1, sizeof(*coltarget)); + if(!get_colIndexA(lp, varset, coltarget, FALSE)) { + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + return(FALSE); + } + } + localnz = (MYBOOL) (nzinput == NULL); + if(localnz) { + nzinput = (int *) mempool_obtainVector(lp->workarrays, lp->rows+1, sizeof(*nzinput)); + vec_compress(input, 0, lp->rows, lp->matA->epsvalue, NULL, nzinput); + } + + /* Scan the columns */ + vb = 1; + ve = coltarget[0]; + for(vb = 1; vb <= coltarget[0]; vb++) { + colnr = coltarget[vb]; + j = lp->is_basic[colnr]; + + /* Perform the multiplication */ + sdp = ofscalar*input[j]; + if(colnr <= lp->rows) /* A slack variable is in the basis */ + output[colnr] += sdp; + else { /* A normal variable is in the basis */ + colnr -= lp->rows; + ib = mat->col_end[colnr - 1]; + ie = mat->col_end[colnr]; + rownr = &COL_MAT_ROWNR(ib); + value = &COL_MAT_VALUE(ib); + for(; ib < ie; + ib++, rownr += matRowColStep, value += matValueStep) { + output[*rownr] += (*value)*sdp; + } + } + } + roundVector(output+1, lp->rows-1, roundzero); + + /* Clean up and return */ + if(localset) + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + if(localnz) + mempool_releaseVector(lp->workarrays, (char *) nzinput, FALSE); + + return(TRUE); +} + +STATIC int prod_xA(lprec *lp, int *coltarget, + LPSREAL *input, int *nzinput, LPSREAL roundzero, LPSREAL ofscalar, + LPSREAL *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. */ +{ + int colnr, rownr, varnr, ib, ie, vb, ve, nrows = lp->rows; + MYBOOL localset, localnz = FALSE, includeOF, isRC; + REALXP vmax; + register REALXP v; + int inz, *rowin, countNZ = 0; + MATrec *mat = lp->matA; + register LPSREAL *matValue; + register int *matRownr; + + /* Clean output area (only necessary if we are returning the full vector) */ + isRC = (MYBOOL) ((roundmode & MAT_ROUNDRC) != 0); + if(nzoutput == NULL) { + if(input == output) + MEMCLEAR(output+nrows+1, lp->columns); + else + MEMCLEAR(output, lp->sum+1); + } + + /* Find what variable range to scan - default is {SCAN_USERVARS} */ + /* Define default column target if none was provided */ + localset = (MYBOOL) (coltarget == NULL); + if(localset) { + int varset = SCAN_SLACKVARS | SCAN_USERVARS | + USE_NONBASICVARS | OMIT_FIXED; + if(isRC && is_piv_mode(lp, PRICE_PARTIAL) && !is_piv_mode(lp, PRICE_FORCEFULL)) + varset |= SCAN_PARTIALBLOCK; + coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->sum+1, sizeof(*coltarget)); + if(!get_colIndexA(lp, varset, coltarget, FALSE)) { + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + return(FALSE); + } + } +/*#define UseLocalNZ*/ +#ifdef UseLocalNZ + localnz = (MYBOOL) (nzinput == NULL); + if(localnz) { + nzinput = (int *) mempool_obtainVector(lp->workarrays, nrows+1, sizeof(*nzinput)); + vec_compress(input, 0, nrows, lp->matA->epsvalue, NULL, nzinput); + } +#endif + includeOF = (MYBOOL) (((nzinput == NULL) || (nzinput[1] == 0)) && + (input[0] != 0) && lp->obj_in_basis); + + /* Scan the target colums */ + vmax = 0; + ve = coltarget[0]; + for(vb = 1; vb <= ve; vb++) { + + varnr = coltarget[vb]; + + if(varnr <= nrows) { + v = input[varnr]; + } + else { + colnr = varnr - nrows; + v = 0; + ib = mat->col_end[colnr - 1]; + ie = mat->col_end[colnr]; + if(ib < ie) { + + /* Do dense input vector version */ +#ifdef UseLocalNZ + if(localnz || (nzinput == NULL)) { +#else + if(nzinput == NULL) { +#endif + /* Do the OF */ + if(includeOF) +#ifdef DirectArrayOF + v += input[0] * lp->obj[colnr] * ofscalar; +#else + v += input[0] * get_OF_active(lp, varnr, ofscalar); +#endif + + /* Initialize pointers */ + matRownr = &COL_MAT_ROWNR(ib); + matValue = &COL_MAT_VALUE(ib); + + /* Do extra loop optimization based on target window overlaps */ +#ifdef UseLocalNZ + if((ib < ie) + && (colnr <= *nzinput) + && (COL_MAT_ROWNR(ie-1) >= nzinput[colnr]) + && (*matRownr <= nzinput[*nzinput]) + ) +#endif +#ifdef NoLoopUnroll + /* Then loop over all regular rows */ + for(; ib < ie; ib++) { + v += input[*matRownr] * (*matValue); + matValue += matValueStep; + matRownr += matRowColStep; + } +#else + /* Prepare for simple loop unrolling */ + if(((ie-ib) % 2) == 1) { + v += input[*matRownr] * (*matValue); + ib++; + matValue += matValueStep; + matRownr += matRowColStep; + } + + /* Then loop over remaining pairs of regular rows */ + while(ib < ie) { + v += input[*matRownr] * (*matValue); + v += input[*(matRownr+matRowColStep)] * (*(matValue+matValueStep)); + ib += 2; + matValue += 2*matValueStep; + matRownr += 2*matRowColStep; + } +#endif + } + /* Do sparse input vector version */ + else { + + /* Do the OF */ + if(includeOF) +#ifdef DirectArrayOF + v += input[0] * lp->obj[colnr] * ofscalar; +#else + v += input[0] * get_OF_active(lp, varnr, ofscalar); +#endif + + /* Initialize pointers */ + inz = 1; + rowin = nzinput+inz; + matRownr = &COL_MAT_ROWNR(ib); + matValue = &COL_MAT_VALUE(ib); + ie--; + + /* Then loop over all non-OF rows */ + while((inz <= *nzinput) && (ib <= ie)) { + + /* Try to synchronize at right */ + while((*rowin > *matRownr) && (ib < ie)) { + ib++; + matValue += matValueStep; + matRownr += matRowColStep; + } + /* Try to synchronize at left */ + while((*rowin < *matRownr) && (inz < *nzinput)) { + inz++; + rowin++; + } + /* Perform dot product operation if there was a match */ + if(*rowin == *matRownr) { + v += input[*rowin] * (*matValue); + /* Step forward at left */ + inz++; + rowin++; + } + } + } + } + if((roundmode & MAT_ROUNDABS) != 0) { + my_roundzero(v, roundzero); + } + } + + /* Special handling of small reduced cost values */ + if(!isRC || (my_chsign(lp->is_lower[varnr], v) < 0)) { + SETMAX(vmax, fabs((LPSREAL) v)); + } + if(v != 0) { + countNZ++; + if(nzoutput != NULL) + nzoutput[countNZ] = varnr; + } + output[varnr] = (LPSREAL) v; + } + + /* Compute reduced cost if this option is active */ + if(isRC && !lp->obj_in_basis) + countNZ = get_basisOF(lp, coltarget, output, nzoutput); + + /* Check if we should do relative rounding */ + if((roundmode & MAT_ROUNDREL) != 0) { + if((roundzero > 0) && (nzoutput != NULL)) { + ie = 0; + if(isRC) { + SETMAX(vmax, MAT_ROUNDRCMIN); /* Make sure we don't use very small values */ + } + vmax *= roundzero; + for(ib = 1; ib <= countNZ; ib++) { + rownr = nzoutput[ib]; + if(fabs(output[rownr]) < vmax) + output[rownr] = 0; + else { + ie++; + nzoutput[ie] = rownr; + } + } + countNZ = ie; + } + } + + /* Clean up and return */ + if(localset) + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + if(localnz) + mempool_releaseVector(lp->workarrays, (char *) nzinput, FALSE); + + if(nzoutput != NULL) + *nzoutput = countNZ; + return(countNZ); +} + +STATIC MYBOOL prod_xA2(lprec *lp, int *coltarget, + LPSREAL *prow, LPSREAL proundzero, int *nzprow, + LPSREAL *drow, LPSREAL droundzero, int *nzdrow, + LPSREAL 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; + register int *matRownr; + MYBOOL localset; + + /* Find what variable range to scan - default is {SCAN_USERVARS} */ + /* First determine the starting position; add from the top, going down */ + localset = (MYBOOL) (coltarget == NULL); + if(localset) { + int varset = SCAN_SLACKVARS + SCAN_USERVARS + /*SCAN_ALLVARS +*/ + /*SCAN_PARTIALBLOCK+*/ + USE_NONBASICVARS+OMIT_FIXED; + coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->sum+1, sizeof(*coltarget)); + if(!get_colIndexA(lp, varset, coltarget, FALSE)) { + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + return(FALSE); + } + } + + /* Initialize variables */ + isRC = (MYBOOL) ((roundmode & MAT_ROUNDRC) != 0); + pmax = 0; + dmax = 0; + if(nzprow != NULL) + *nzprow = 0; + if(nzdrow != NULL) + *nzdrow = 0; + includeOF = (MYBOOL) (((prow[0] != 0) || (drow[0] != 0)) && + lp->obj_in_basis); + + /* Scan the target colums */ + ve = coltarget[0]; + for(vb = 1; vb <= ve; vb++) { + + varnr = coltarget[vb]; + + if(varnr <= nrows) { + p = prow[varnr]; + d = drow[varnr]; + } + else { + + colnr = varnr - nrows; + + p = 0; + d = 0; + ib = mat->col_end[colnr - 1]; + ie = mat->col_end[colnr]; + + if(ib < ie) { + + /* Do the OF */ + if(includeOF) { +#ifdef DirectArrayOF + value = lp->obj[colnr] * ofscalar; +#else + value = get_OF_active(lp, varnr, ofscalar); +#endif + p += prow[0] * value; + d += drow[0] * value; + } + + /* Then loop over all regular rows */ + matRownr = &COL_MAT_ROWNR(ib); + matValue = &COL_MAT_VALUE(ib); +#ifdef NoLoopUnroll + for( ; ib < ie; ib++) { + p += prow[*matRownr] * (*matValue); + d += drow[*matRownr] * (*matValue); + matValue += matValueStep; + matRownr += matRowColStep; + } +#else + /* Prepare for simple loop unrolling */ + if(((ie-ib) % 2) == 1) { + p += prow[*matRownr] * (*matValue); + d += drow[*matRownr] * (*matValue); + ib++; + matValue += matValueStep; + matRownr += matRowColStep; + } + + /* Then loop over remaining pairs of regular rows */ + while(ib < ie) { + p += prow[*matRownr] * (*matValue); + p += prow[*(matRownr+matRowColStep)] * (*(matValue+matValueStep)); + d += drow[*matRownr] * (*matValue); + d += drow[*(matRownr+matRowColStep)] * (*(matValue+matValueStep)); + ib += 2; + matValue += 2*matValueStep; + matRownr += 2*matRowColStep; + } +#endif + + } + if((roundmode & MAT_ROUNDABS) != 0) { + my_roundzero(p, proundzero); + my_roundzero(d, droundzero); + } + } + + SETMAX(pmax, fabs((LPSREAL) p)); + prow[varnr] = (LPSREAL) p; + if((nzprow != NULL) && (p != 0)) { + (*nzprow)++; + nzprow[*nzprow] = varnr; + } + + /* Special handling of reduced cost rounding */ + if(!isRC || (my_chsign(lp->is_lower[varnr], d) < 0)) { + SETMAX(dmax, fabs((LPSREAL) d)); + } + drow[varnr] = (LPSREAL) d; + if((nzdrow != NULL) && (d != 0)) { + (*nzdrow)++; + nzdrow[*nzdrow] = varnr; + } + } + + /* Compute reduced cost here if this option is active */ + if((drow != 0) && !lp->obj_in_basis) + get_basisOF(lp, coltarget, drow, nzdrow); + + /* Check if we should do relative rounding */ + if((roundmode & MAT_ROUNDREL) != 0) { + if((proundzero > 0) && (nzprow != NULL)) { + ie = 0; + pmax *= proundzero; + for(ib = 1; ib <= *nzprow; ib++) { + varnr = nzprow[ib]; + if(fabs(prow[varnr]) < pmax) + prow[varnr] = 0; + else { + ie++; + nzprow[ie] = varnr; + } + } + *nzprow = ie; + } + if((droundzero > 0) && (nzdrow != NULL)) { + ie = 0; + if(isRC) { + SETMAX(dmax, MAT_ROUNDRCMIN); /* Make sure we don't use very small values */ + } + dmax *= droundzero; + for(ib = 1; ib <= *nzdrow; ib++) { + varnr = nzdrow[ib]; + if(fabs(drow[varnr]) < dmax) + drow[varnr] = 0; + else { + ie++; + nzdrow[ie] = varnr; + } + } + *nzdrow = ie; + } + } + + /* Clean up and return */ + if(localset) + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + return( TRUE ); +} + +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) +{ + LPSREAL ofscalar = 1.0; + + /* Clear and initialize first vector */ + if(nzvector1 == NULL) + MEMCLEAR(vector1, lp->sum + 1); + else + MEMCLEAR(vector1, lp->rows + 1); + vector1[row_nr1] = 1; +/* workINT[0] = 1; + workINT[1] = row_nr1; */ + + if(vector2 == NULL) { + lp->bfp_btran_normal(lp, vector1, NULL); + prod_xA(lp, coltarget, vector1, NULL, roundzero1, ofscalar*0, + vector1, nzvector1, roundmode); + } + else { + + /* Clear and initialize second vector */ + if(nzvector2 == NULL) + MEMCLEAR(vector2, lp->sum + 1); + else + MEMCLEAR(vector2, lp->rows + 1); + if(lp->obj_in_basis || (row_nr2 > 0)) { + vector2[row_nr2] = 1; +/* workINT[2] = 1; + workINT[3] = row_nr2; */ + } + else + get_basisOF(lp, NULL, vector2, nzvector2); + + /* A double BTRAN equation solver process is implemented "in-line" below in + order to save time and to implement different rounding for the two */ + lp->bfp_btran_double(lp, vector1, NULL, vector2, NULL); + + /* Multiply solution vectors with matrix values */ + prod_xA2(lp, coltarget, vector1, roundzero1, nzvector1, + vector2, roundzero2, nzvector2, + ofscalar, roundmode); + } +} + diff --git a/src/external/lpsolve/build/lp_solve/lp_mipbb.c b/src/external/lpsolve/build/lp_solve/lp_mipbb.c new file mode 100644 index 00000000..1a893f0c --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_mipbb.c @@ -0,0 +1,1439 @@ + +/* + Mixed integer programming optimization drivers for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2) + Kjell Eikland (v4.0 and forward) + Contact: + License terms: LGPL. + + Requires: string.h, float.h, commonlib.h, lp_lib.h, lp_report.h, + lp_simplex.h + + Release notes: + v5.0.0 31 January 2004 New unit isolating B&B routines. + v5.0.1 01 February 2004 Complete rewrite into non-recursive version. + v5.0.2 05 April 2004 Expanded pseudocosting with options for MIP fraction + counts and "cost/benefit" ratio (KE special!). + Added GUB functionality based on SOS structures. + v5.0.3 1 May 2004 Changed routine names to be more intuitive. + v5.0.4 15 May 2004 Added functinality to pack bounds in order to + conserve memory in B&B-processing large MIP models. + v5.1.0 25 July 2004 Added functions for dynamic cut generation. + v5.2.0 15 December 2004 Added functions for reduced cost variable fixing + and converted to delta-model of B&B bound storage. + ---------------------------------------------------------------------------------- +*/ + +#include +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_scale.h" +#include "lp_report.h" +#include "lp_simplex.h" +#include "lp_mipbb.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* Allocation routine for the BB record structure */ +STATIC BBrec *create_BB(lprec *lp, BBrec *parentBB, MYBOOL dofullcopy) +{ + BBrec *newBB; + + newBB = (BBrec *) calloc(1, sizeof(*newBB)); + if(newBB != NULL) { + + if(parentBB == NULL) { + allocREAL(lp, &newBB->upbo, lp->sum + 1, FALSE); + allocREAL(lp, &newBB->lowbo, lp->sum + 1, FALSE); + MEMCOPY(newBB->upbo, lp->orig_upbo, lp->sum + 1); + MEMCOPY(newBB->lowbo, lp->orig_lowbo, lp->sum + 1); + } + else if(dofullcopy) { + allocREAL(lp, &newBB->upbo, lp->sum + 1, FALSE); + allocREAL(lp, &newBB->lowbo, lp->sum + 1, FALSE); + MEMCOPY(newBB->upbo, parentBB->upbo, lp->sum + 1); + MEMCOPY(newBB->lowbo, parentBB->lowbo, lp->sum + 1); + } + else { + newBB->upbo = parentBB->upbo; + newBB->lowbo = parentBB->lowbo; + } + newBB->contentmode = dofullcopy; + + newBB->lp = lp; + + /* Set parent by default, but not child */ + newBB->parent = parentBB; + + } + return( newBB ); +} + + +/* Pushing and popping routines for the B&B structure */ + +STATIC BBrec *push_BB(lprec *lp, BBrec *parentBB, int varno, int vartype, int varcus) +/* Push ingoing bounds and B&B data onto the stack */ +{ + BBrec *newBB; + + /* Do initialization and updates */ + if(parentBB == NULL) + parentBB = lp->bb_bounds; + newBB = create_BB(lp, parentBB, FALSE); + if(newBB != NULL) { + + newBB->varno = varno; + newBB->vartype = vartype; + newBB->lastvarcus = varcus; + incrementUndoLadder(lp->bb_lowerchange); + newBB->LBtrack++; + incrementUndoLadder(lp->bb_upperchange); + newBB->UBtrack++; + + /* Adjust variable fixing/bound tightening based on the last reduced cost */ + if((parentBB != NULL) && (parentBB->lastrcf > 0)) { + MYBOOL isINT; + int k, ii, nfixed = 0, ntighten = 0; + LPSREAL deltaUL; + + for(k = 1; k <= lp->nzdrow[0]; k++) { + ii = lp->nzdrow[k]; +#ifdef UseMilpSlacksRCF /* Check if we should include ranged constraints */ + isINT = FALSE; +#else + if(ii <= lp->rows) + continue; + isINT = is_int(lp, ii-lp->rows); +#endif +#ifndef UseMilpExpandedRCF /* Don't include non-integers if it is not defined */ + if(!isINT) + continue; +#endif + switch(abs(rcfbound_BB(newBB, ii, isINT, &deltaUL, NULL))) { + case LE: SETMIN(deltaUL, newBB->upbo[ii]); + SETMAX(deltaUL, newBB->lowbo[ii]); + modifyUndoLadder(lp->bb_upperchange, ii, newBB->upbo, deltaUL); + break; + case GE: SETMAX(deltaUL, newBB->lowbo[ii]); + SETMIN(deltaUL, newBB->upbo[ii]); + modifyUndoLadder(lp->bb_lowerchange, ii, newBB->lowbo, deltaUL); + break; + default: continue; + } + if(newBB->upbo[ii] == newBB->lowbo[ii]) + nfixed++; + else + ntighten++; + } + if(lp->bb_trace) { + report(lp, DETAILED, + "push_BB: Used reduced cost to fix %d variables and tighten %d bounds\n", + nfixed, ntighten); + } + } + + /* Handle case where we are pushing at the end */ + if(parentBB == lp->bb_bounds) + lp->bb_bounds = newBB; + /* Handle case where we are pushing in the middle */ + else + newBB->child = parentBB->child; + if(parentBB != NULL) + parentBB->child = newBB; + + lp->bb_level++; + if(lp->bb_level > lp->bb_maxlevel) + lp->bb_maxlevel = lp->bb_level; + + if(!initbranches_BB(newBB)) + newBB = pop_BB(newBB); + else if(MIP_count(lp) > 0) { + if( (lp->bb_level <= 1) && (lp->bb_varactive == NULL) && + (!allocINT(lp, &lp->bb_varactive, lp->columns+1, TRUE) || + !initcuts_BB(lp)) ) + newBB = pop_BB(newBB); + if(varno > 0) { + lp->bb_varactive[varno-lp->rows]++; + } + } + } + return( newBB ); +} + +STATIC MYBOOL free_BB(BBrec **BB) +{ + MYBOOL parentreturned = FALSE; + + if((BB != NULL) && (*BB != NULL)) { + BBrec *parent = (*BB)->parent; + + if((parent == NULL) || (*BB)->contentmode) { + FREE((*BB)->upbo); + FREE((*BB)->lowbo); + } + FREE((*BB)->varmanaged); + FREE(*BB); + + parentreturned = (MYBOOL) (parent != NULL); + if(parentreturned) + *BB = parent; + + } + return( parentreturned ); +} + +STATIC BBrec *pop_BB(BBrec *BB) +/* Pop / free the previously "pushed" / saved bounds */ +{ + int k; + BBrec *parentBB; + lprec *lp = BB->lp; + + if(BB == NULL) + return( BB ); + + /* Handle case where we are popping the end of the chain */ + parentBB = BB->parent; + if(BB == lp->bb_bounds) { + lp->bb_bounds = parentBB; + if(parentBB != NULL) + parentBB->child = NULL; + } + /* Handle case where we are popping inside or at the beginning of the chain */ + else { + if(parentBB != NULL) + parentBB->child = BB->child; + if(BB->child != NULL) + BB->child->parent = parentBB; + } + + /* 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); + } + } + 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); + } + } + lp->bb_level--; + k = BB->varno - lp->rows; + if(lp->bb_level == 0) { + if(lp->bb_varactive != NULL) { + FREE(lp->bb_varactive); + freecuts_BB(lp); + } + if(lp->int_vars+lp->sc_vars > 0) + free_pseudocost(lp); + pop_basis(lp, FALSE); + lp->rootbounds = NULL; + } + else + lp->bb_varactive[k]--; + + /* Undo SOS/GUB markers */ + if(BB->isSOS && (BB->vartype != BB_INT)) + SOS_unmark(lp->SOS, 0, k); + else if(BB->isGUB) + SOS_unmark(lp->GUB, 0, k); + + /* Undo the SC marker */ + if(BB->sc_canset) + lp->sc_lobound[k] *= -1; + + /* Pop the associated basis */ +#if 1 + /* Original version that does not restore previous basis */ + pop_basis(lp, FALSE); +#else + /* Experimental version that restores previous basis */ + pop_basis(lp, BB->isSOS); +#endif + + /* Finally free the B&B object */ + free_BB(&BB); + + /* Return the parent BB */ + return( parentBB ); +} + +/* Here are heuristic routines to see if we need bother with branching further + + 1. A probing routine to see of the best OF can be better than incumbent + 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) +{ + int i, ii; + LPSREAL coefOF, sum = 0; + lprec *lp = BB->lp; + + /* Loop over all ints to see if the best possible solution + stands any chance of being better than the incumbent solution */ + if(lp->solutioncount == 0) + return( lp->infinite ); + for(i = 1; i <= lp->columns; i++) { + if(!is_int(lp, i)) + continue; + ii = lp->rows + i; + coefOF = lp->obj[i]; + if(coefOF < 0) { + if(is_infinite(lp, BB->lowbo[ii])) + return( lp->infinite ); + sum += coefOF * (lp->solution[ii]-BB->lowbo[ii]); + } + else { + if(is_infinite(lp, BB->upbo[ii])) + return( lp->infinite ); + sum += coefOF * (BB->upbo[ii] - lp->solution[ii]); + } + } + return( sum ); +} + +STATIC LPSREAL presolve_BB(BBrec *BB) +{ + return( 0 ); +} + +/* Node and branch management routines */ +STATIC MYBOOL initbranches_BB(BBrec *BB) +{ + LPSREAL new_bound, temp; + int k; + lprec *lp = BB->lp; + + /* Create and initialize local bounds and basis */ + BB->nodestatus = NOTRUN; + BB->noderesult = lp->infinite; + push_basis(lp, NULL, NULL, NULL); + + /* Set default number of branches at the current B&B branch */ + if(BB->vartype == BB_REAL) + BB->nodesleft = 1; + + else { + /* The default is a binary up-low branching */ + BB->nodesleft = 2; + + /* Initialize the MIP status code pair and set reference values */ + k = BB->varno - lp->rows; + BB->lastsolution = lp->solution[BB->varno]; + + /* Determine if we must process in the B&B SOS mode */ + BB->isSOS = (MYBOOL) ((BB->vartype == BB_SOS) || SOS_is_member(lp->SOS, 0, k)); +#ifdef Paranoia + if((BB->vartype == BB_SOS) && !SOS_is_member(lp->SOS, 0, k)) + report(lp, SEVERE, "initbranches_BB: Inconsistent identification of SOS variable %s (%d)\n", + get_col_name(lp, k), k); +#endif + + /* Check if we have a GUB-member variable that needs a triple-branch */ + BB->isGUB = (MYBOOL) ((BB->vartype == BB_INT) && SOS_can_activate(lp->GUB, 0, k)); + if(BB->isGUB) { + /* Obtain variable index list from applicable GUB - now the first GUB is used, + but we could also consider selecting the longest */ + BB->varmanaged = SOS_get_candidates(lp->GUB, -1, k, TRUE, BB->upbo, BB->lowbo); + BB->nodesleft++; + } + + + /* Set local pruning info, automatic, or user-defined strategy */ + if(BB->vartype == BB_SOS) { + if(!SOS_can_activate(lp->SOS, 0, k)) { + BB->nodesleft--; + BB->isfloor = TRUE; + } + else + BB->isfloor = (MYBOOL) (BB->lastsolution == 0); + } + + /* First check if the user wishes to select the branching direction */ + else if(lp->bb_usebranch != NULL) + BB->isfloor = (MYBOOL) lp->bb_usebranch(lp, lp->bb_branchhandle, k); + + /* 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)) + new_bound = 0; + else if(new_bound < 0) + new_bound += 1.0; + BB->isfloor = (MYBOOL) (new_bound <= 0.5); + + /* Set direction by OF value; note that a zero-value in + the OF gives priority to floor_first = TRUE */ + if(is_bb_mode(lp, NODE_GREEDYMODE)) { + if(is_bb_mode(lp, NODE_PSEUDOCOSTMODE)) + BB->sc_bound = get_pseudonodecost(lp->bb_PseudoCost, k, BB->vartype, BB->lastsolution); + else + BB->sc_bound = mat_getitem(lp->matA, 0, k); + new_bound -= 0.5; + BB->sc_bound *= new_bound; + BB->isfloor = (MYBOOL) (BB->sc_bound > 0); + } + /* Set direction by pseudocost (normally used in tandem with NODE_PSEUDOxxxSELECT) */ + else if(is_bb_mode(lp, NODE_PSEUDOCOSTMODE)) { + BB->isfloor = (MYBOOL) (get_pseudobranchcost(lp->bb_PseudoCost, k, TRUE) > + get_pseudobranchcost(lp->bb_PseudoCost, k, FALSE)); + if(is_maxim(lp)) + BB->isfloor = !BB->isfloor; + } + + /* Check for reversal */ + if(is_bb_mode(lp, NODE_BRANCHREVERSEMODE)) + BB->isfloor = !BB->isfloor; + } + else + BB->isfloor = (MYBOOL) (get_var_branch(lp, k) == BRANCH_FLOOR); + + /* SC logic: If the current SC variable value is in the [0..NZLOBOUND> range, then + + UP: Set lower bound to NZLOBOUND, upper bound is the original + LO: Fix the variable by setting upper/lower bound to zero + + ... indicate that the variable is B&B-active by reversing sign of sc_lobound[]. */ + new_bound = fabs(lp->sc_lobound[k]); + BB->sc_bound = new_bound; + BB->sc_canset = (MYBOOL) (new_bound != 0); + + /* Must make sure that we handle fractional lower bounds properly; + also to ensure that we do a full binary tree search */ + new_bound = unscaled_value(lp, new_bound, BB->varno); + if(is_int(lp, k) && ((new_bound > 0) && + (BB->lastsolution > floor(new_bound)))) { + if(BB->lastsolution < ceil(new_bound)) + BB->lastsolution += 1; + modifyUndoLadder(lp->bb_lowerchange, BB->varno, BB->lowbo, + scaled_floor(lp, BB->varno, BB->lastsolution, 1)); + } + } + + /* Now initialize the brances and set to first */ + return( fillbranches_BB(BB) ); +} + +STATIC MYBOOL fillbranches_BB(BBrec *BB) +{ + int K, k; + LPSREAL ult_upbo, ult_lowbo; + LPSREAL new_bound, SC_bound, intmargin = BB->lp->epsprimal; + lprec *lp = BB->lp; + MYBOOL OKstatus = FALSE; + + if(lp->bb_break || userabort(lp, MSG_MILPSTRATEGY)) + return( OKstatus ); + + K = BB->varno; + if(K > 0) { + + /* Shortcut variables */ + k = BB->varno - lp->rows; + ult_upbo = lp->orig_upbo[K]; + ult_lowbo = lp->orig_lowbo[K]; + SC_bound = unscaled_value(lp, BB->sc_bound, K); + + /* First, establish the upper bound to be applied (when isfloor == TRUE) + --------------------------------------------------------------------- */ +/*SetUB:*/ + BB->UPbound = lp->infinite; + + /* Handle SC-variables for the [0-LoBound> range */ + if((SC_bound > 0) && (fabs(BB->lastsolution) < SC_bound-intmargin)) { + new_bound = 0; + } + /* Handle pure integers (non-SOS, non-SC) */ + else if(BB->vartype == BB_INT) { +#if 1 + if(((ult_lowbo >= 0) && + ((floor(BB->lastsolution) < /* Skip cases where the lower bound becomes violated */ + unscaled_value(lp, MAX(ult_lowbo, fabs(lp->sc_lobound[k])), K)-intmargin))) || + ((ult_upbo <= 0) && /* Was ((ult_lowbo < 0) && */ + ((floor(BB->lastsolution) > /* Skip cases where the upper bound becomes violated */ + unscaled_value(lp, MIN(ult_upbo, -fabs(lp->sc_lobound[k])), K)-intmargin)))) { +#else + if((floor(BB->lastsolution) < /* Skip cases where the lower bound becomes violated */ + unscaled_value(lp, MAX(ult_lowbo, fabs(lp->sc_lobound[k])), K)-intmargin)) { +#endif + BB->nodesleft--; + goto SetLB; + } + new_bound = scaled_floor(lp, K, BB->lastsolution, 1); + } + else if(BB->isSOS) { /* Handle all SOS variants */ + new_bound = ult_lowbo; + if(is_int(lp, k)) + new_bound = scaled_ceil(lp, K, unscaled_value(lp, new_bound, K), -1); + } + else /* Handle all other variable incarnations */ + new_bound = BB->sc_bound; + + /* 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); + 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_bound, BB->lowbo[K]); +#endif + BB->nodesleft--; + goto SetLB; + } +#ifdef Paranoia + /* Do additional consistency checking */ + else if(!check_if_less(lp, new_bound, BB->upbo[K], K)) { + BB->nodesleft--; + goto SetLB; + } +#endif + /* Bound (at least near) feasible */ + else { + /* Makes a difference with models like QUEEN + (note consistent use of epsint for scaled integer variables) */ + if(fabs(new_bound - BB->lowbo[K]) < intmargin*SCALEDINTFIXRANGE) + new_bound = BB->lowbo[K]; + } + + BB->UPbound = new_bound; + + + /* Next, establish the lower bound to be applied (when isfloor == FALSE) + --------------------------------------------------------------------- */ +SetLB: + BB->LObound = -lp->infinite; + + /* Handle SC-variables for the [0-LoBound> range */ + if((SC_bound > 0) && (fabs(BB->lastsolution) < SC_bound)) { + if(is_int(lp, k)) + new_bound = scaled_ceil(lp, K, SC_bound, 1); + else + new_bound = BB->sc_bound; + } + /* Handle pure integers (non-SOS, non-SC, but Ok for GUB!) */ + else if(BB->vartype == BB_INT) { + if(((ceil(BB->lastsolution) == BB->lastsolution)) || /* Skip branch 0 if the current solution is integer */ + (ceil(BB->lastsolution) > /* Skip cases where the upper bound becomes violated */ + unscaled_value(lp, ult_upbo, K)+intmargin) || + (BB->isSOS && (BB->lastsolution == 0))) { /* Don't branch 0 since this is handled in SOS logic */ + BB->nodesleft--; + goto Finish; + } + new_bound = scaled_ceil(lp, K, BB->lastsolution, 1); + } + else if(BB->isSOS) { /* Handle all SOS variants */ + if(SOS_is_member_of_type(lp->SOS, k, SOS3)) + new_bound = scaled_floor(lp, K, 1, 1); + else { + new_bound = ult_lowbo; + if(is_int(lp, k)) + new_bound = scaled_floor(lp, K, unscaled_value(lp, new_bound, K), 1); + /* If we have a high-order SOS (SOS3+) and this variable is "intermediate" + between members previously lower-bounded at a non-zero level, then we should + set this and similar neighbouring variables at non-zero lowbo-values (remember + that SOS3+ members are all either integers or semi-continuous). Flag this + situation and prune tree, since we cannot lower-bound. */ + if((lp->SOS->maxorder > 2) && (BB->lastsolution == 0) && + SOS_is_member_of_type(lp->SOS, k, SOSn)) { + BB->isSOS = AUTOMATIC; + } + } + } + else /* Handle all other variable incarnations */ + new_bound = BB->sc_bound; + + /* 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); + 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_bound, BB->upbo[K]); +#endif + BB->nodesleft--; + goto Finish; + } +#ifdef Paranoia + /* Do additional consistency checking */ + else if(!check_if_less(lp, BB->lowbo[K], new_bound, K)) { + BB->nodesleft--; + goto Finish; + } +#endif + /* Bound (at least near-)feasible */ + else { + /* Makes a difference with models like QUEEN + (note consistent use of lp->epsprimal for scaled integer variables) */ + if(fabs(BB->upbo[K]-new_bound) < intmargin*SCALEDINTFIXRANGE) + new_bound = BB->upbo[K]; + } + + BB->LObound = new_bound; + + /* Prepare for the first branch by making sure we are pointing correctly */ +Finish: + if(BB->nodesleft > 0) { + + /* Make sure the change tracker levels are "clean" for the B&B */ + if(countsUndoLadder(lp->bb_upperchange) > 0) { + incrementUndoLadder(lp->bb_upperchange); + BB->UBtrack++; + } + if(countsUndoLadder(lp->bb_lowerchange) > 0) { + incrementUndoLadder(lp->bb_lowerchange); + BB->LBtrack++; + } + + /* Do adjustments */ + if((BB->vartype != BB_SOS) && (fabs(BB->LObound-BB->UPbound) < intmargin)) { + BB->nodesleft--; + if(fabs(BB->lowbo[K]-BB->LObound) < intmargin) + BB->isfloor = FALSE; + else if(fabs(BB->upbo[K]-BB->UPbound) < intmargin) + BB->isfloor = TRUE; + else + report(BB->lp, IMPORTANT, "fillbranches_BB: Inconsistent equal-valued bounds for %s\n", + get_col_name(BB->lp, k)); + } + if((BB->nodesleft == 1) && + ((BB->isfloor && (BB->UPbound >= lp->infinite)) || + (!BB->isfloor && (BB->LObound <= -lp->infinite)))) + 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)) + OKstatus = nextbranch_BB( BB ); + } + + /* Set an SC variable active, if necessary */ + if(BB->sc_canset) + lp->sc_lobound[k] *= -1; + + } + else { + BB->nodesleft--; + OKstatus = TRUE; + } + + return( OKstatus ); +} + +STATIC MYBOOL nextbranch_BB(BBrec *BB) +{ + int k; + lprec *lp = BB->lp; + MYBOOL OKstatus = FALSE; + + /* Undo the most recently imposed B&B bounds using the data + in the last level of change tracker; this code handles changes + to both upper and lower bounds */ + if(BB->nodessolved > 0) { + restoreUndoLadder(lp->bb_upperchange, BB->upbo); + restoreUndoLadder(lp->bb_lowerchange, BB->lowbo); + } + + if(lp->bb_break || userabort(lp, MSG_MILPSTRATEGY)) { + /* Handle the special case of B&B restart; + (typically used with the restart after pseudocost initialization) */ + if((lp->bb_level == 1) && (lp->bb_break == AUTOMATIC)) { + lp->bb_break = FALSE; + OKstatus = TRUE; + } + return( OKstatus ); + } + + if(BB->nodesleft > 0) { + + /* Step and update remaining branch count */ + k = BB->varno - lp->rows; + BB->isfloor = !BB->isfloor; + BB->nodesleft--; + + /* Special SOS handling: + 1) Undo and set new marker for k, + 2) In case that previous branch was ceiling restore upper bounds of the + non-k variables outside of the SOS window set to 0 */ + if(BB->isSOS && (BB->vartype != BB_INT)) { + + /* First undo previous marker */ + if((BB->nodessolved > 0) || ((BB->nodessolved == 0) && (BB->nodesleft == 0))) { + if(BB->isfloor) { + if((BB->nodesleft == 0) && (lp->orig_lowbo[BB->varno] != 0)) + return( OKstatus ); + } + SOS_unmark(lp->SOS, 0, k); + } + + /* Set new SOS marker */ + if(BB->isfloor) { + SOS_set_marked(lp->SOS, 0, k, (MYBOOL) (BB->UPbound != 0)); + /* Do case of high-order SOS where intervening variables need to be set */ + if(BB->isSOS == AUTOMATIC) { + +/* SOS_fix_list(lp->SOS, 0, k, BB->lowbo, NULL, AUTOMATIC, lp->bb_lowerchange); */ + } + } + else { + SOS_set_marked(lp->SOS, 0, k, TRUE); + if(SOS_fix_unmarked(lp->SOS, 0, k, BB->upbo, 0, TRUE, + NULL, lp->bb_upperchange) < 0) + return( OKstatus ); + } + } + + /* Special GUB handling (three branches): + 1) Undo and set new marker for k, + 2) Restore upper bounds of the left/right/all non-k variables + set to 0 in the previous branch + 3) Set new upper bounds for the non-k variables (k is set later) */ + else if(BB->isGUB) { + + /* First undo previous marker */ + if(BB->nodessolved > 0) + SOS_unmark(lp->GUB, 0, k); + + /* Make sure we take floor bound twice */ + if((BB->nodesleft == 0) && !BB->isfloor) + BB->isfloor = !BB->isfloor; + + /* Handle two floor instances; + (selected variable and left/right halves of non-selected variables at 0) */ + SOS_set_marked(lp->GUB, 0, k, (MYBOOL) !BB->isfloor); + if(BB->isfloor) { + if(SOS_fix_list(lp->GUB, 0, k, BB->upbo, + BB->varmanaged, (MYBOOL) (BB->nodesleft > 0), lp->bb_upperchange) < 0) + return( OKstatus ); + } + /* Handle one ceil instance; + (selected variable at 1, all other at 0) */ + else { + if(SOS_fix_unmarked(lp->GUB, 0, k, BB->upbo, 0, TRUE, + NULL, lp->bb_upperchange) < 0) + return( OKstatus ); + } + } + + OKstatus = TRUE; + + } + /* Initialize simplex status variables */ + if(OKstatus) { + lp->bb_totalnodes++; + BB->nodestatus = NOTRUN; + BB->noderesult = lp->infinite; + } + return( OKstatus ); +} + + +/* Cut generation and management routines */ +STATIC MYBOOL initcuts_BB(lprec *lp) +{ + return( TRUE ); +} + +STATIC int updatecuts_BB(lprec *lp) +{ + return( 0 ); +} + +STATIC MYBOOL freecuts_BB(lprec *lp) +{ + if(lp->bb_cuttype != NULL) + FREE(lp->bb_cuttype); + return( TRUE ); +} + +/* B&B solver routines */ +STATIC int solve_LP(lprec *lp, BBrec *BB) +{ + int tilted, restored, status; + LPSREAL testOF, *upbo = BB->upbo, *lowbo = BB->lowbo; + BBrec *perturbed = NULL; + + if(lp->bb_break) + return(PROCBREAK); + +#ifdef Paranoia + debug_print(lp, "solve_LP: Starting solve for iter %.0f, B&B node level %d.\n", + (double) lp->total_iter, lp->bb_level); + if(lp->bb_trace && + !validate_bounds(lp, upbo, lowbo)) + report(lp, SEVERE, "solve_LP: Inconsistent bounds at iter %.0f, B&B node level %d.\n", + (double) lp->total_iter, lp->bb_level); +#endif + + /* Copy user-specified entering bounds into lp_solve working bounds */ + impose_bounds(lp, upbo, lowbo); + + /* Restore previously pushed / saved basis for this level if we are in + the B&B mode and it is not the first call of the binary tree */ + if(BB->nodessolved > 1) + restore_basis(lp); + + /* Solve and possibly handle degeneracy cases via bound relaxations */ + status = RUNNING; + tilted = 0; + restored = 0; + + while(status == RUNNING) { + + /* Copy user-specified entering bounds into lp_solve working bounds and run */ + status = spx_run(lp, (MYBOOL) (tilted+restored > 0)); + lp->bb_status = status; + lp->spx_perturbed = FALSE; + + if(tilted < 0) + break; + + else if((status == OPTIMAL) && (tilted > 0)) { + if(lp->spx_trace) + report(lp, DETAILED, "solve_LP: Restoring relaxed bounds at level %d.\n", + tilted); + + /* Restore original pre-perturbed problem bounds, and solve again using the basis + found for the perturbed problem; also make sure we rebase and recompute. */ + free_BB(&perturbed); + if((perturbed == NULL) || (perturbed == BB)) { + perturbed = NULL; + impose_bounds(lp, upbo, lowbo); + } + else + impose_bounds(lp, perturbed->upbo, perturbed->lowbo); + set_action(&lp->spx_action, ACTION_REBASE | ACTION_RECOMPUTE); + BB->UBzerobased = FALSE; + if(lp->bb_totalnodes == 0) + lp->real_solution = lp->infinite; + status = RUNNING; + tilted--; + restored++; + lp->spx_perturbed = TRUE; + } + + else if(((lp->bb_level <= 1) || is_anti_degen(lp, ANTIDEGEN_DURINGBB)) && + (((status == LOSTFEAS) && is_anti_degen(lp, ANTIDEGEN_LOSTFEAS)) || + ((status == INFEASIBLE) && is_anti_degen(lp, ANTIDEGEN_INFEASIBLE)) || + ((status == NUMFAILURE) && is_anti_degen(lp, ANTIDEGEN_NUMFAILURE)) || + ((status == DEGENERATE) && is_anti_degen(lp, ANTIDEGEN_STALLING)))) { + /* Allow up to .. consecutive relaxations for non-B&B phases */ + if((tilted <= DEF_MAXRELAX) && /* Conventional recovery case,... */ + !((tilted == 0) && (restored > DEF_MAXRELAX))) { /* but not iterating infeasibility */ + + /* Create working copy of ingoing bounds if this is the first perturbation */ + if(tilted == 0) + perturbed = BB; + perturbed = create_BB(lp, perturbed, TRUE); + + /* Perturb/shift variable bounds; also make sure we rebase and recompute + (no refactorization is necessary, since the basis is unchanged) */ +#if 1 + perturb_bounds(lp, perturbed, TRUE, TRUE, TRUE); +#else + perturb_bounds(lp, perturbed, TRUE, TRUE, FALSE); +#endif + impose_bounds(lp, perturbed->upbo, perturbed->lowbo); + set_action(&lp->spx_action, ACTION_REBASE | ACTION_RECOMPUTE); + BB->UBzerobased = FALSE; + status = RUNNING; + tilted++; + lp->perturb_count++; + lp->spx_perturbed = TRUE; + if(lp->spx_trace) + report(lp, DETAILED, "solve_LP: Starting bound relaxation #%d ('%s')\n", + tilted, get_statustext(lp, status)); + } + else { + if(lp->spx_trace) + report(lp, DETAILED, "solve_LP: Relaxation limit exceeded in resolving infeasibility\n"); + while((perturbed != NULL) && (perturbed != BB)) + free_BB(&perturbed); + perturbed = NULL; + } + } + } + + /* Handle the different simplex outcomes */ + if(status != OPTIMAL) { + if(lp->bb_level <= 1) + 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); + transfer_solution(lp, TRUE); + } + /* Return messages */ + report(lp, NORMAL, "\nlp_solve optimization was stopped %s.\n", + ((status == USERABORT) ? "by the user" : "due to time-out")); + } + else if(BB->varno == 0) + 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 */ + construct_solution(lp, NULL); + if((lp->bb_level <= 1) && (restored > 0)) + report(lp, DETAILED, "%s numerics encountered; validate accuracy\n", + (restored == 1) ? "Difficult" : "Severe"); + /* Handle case where a user bound on the OF was found to + have been set too aggressively, giving an infeasible model */ + if(lp->spx_status != OPTIMAL) + status = lp->spx_status; + + 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, " \n"); + } + if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPOPTIMAL)) { + LPSREAL *best_solution = lp->best_solution; + + /* transfer_solution(lp, TRUE); */ + lp->best_solution = lp->solution; + lp->usermessage(lp, lp->msghandle, MSG_LPOPTIMAL); + lp->best_solution = best_solution; + } + set_var_priority(lp); + } + + /* Check if we have a numeric problem (an earlier version of this code used the + absolute difference, but it is not robust for large-valued OFs) */ + testOF = my_chsign(is_maxim(lp), my_reldiff(lp->solution[0], lp->real_solution)); + if(testOF < -lp->epsprimal) { + report(lp, DETAILED, "solve_LP: A MIP subproblem returned a value better than the base.\n"); + status = INFEASIBLE; + lp->spx_status = status; + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + } + else if(testOF < 0) /* Avoid problems later (could undo integer roundings, but usually Ok) */ + lp->solution[0] = lp->real_solution; + + } + + /* status can have the following values: + OPTIMAL, SUBOPTIMAL, TIMEOUT, USERABORT, PROCFAIL, UNBOUNDED and INFEASIBLE. */ + + return( status ); +} /* solve_LP */ + +STATIC BBrec *findself_BB(BBrec *BB) +{ + int varno = BB->varno, vartype = BB->vartype; + + BB = BB->parent; + while((BB != NULL) && (BB->vartype != vartype) && (BB->varno != varno)) + BB = BB->parent; + return( 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) +{ + int i = FR; + lprec *lp = BB->lp; + LPSREAL deltaRC, rangeLU, deltaOF, lowbo, upbo; + + /* Make sure we only accept non-basic variables */ + if(lp->is_basic[varno]) + return( i ); + + /* Make sure we only accept non-fixed variables */ + lowbo = BB->lowbo[varno]; + upbo = BB->upbo[varno]; + 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 */ + if(deltaRC < lp->epspivot) + return( i ); + deltaRC = deltaOF / deltaRC; /* Should always be a positive number! */ +#ifdef Paranoia + if(deltaRC <= 0) + report(lp, SEVERE, "rcfbound_BB: A negative bound fixing level was encountered after node %.0f\n", + (double) lp->bb_totalnodes); +#endif + + /* Check if bound implied by the reduced cost is less than existing range */ + if(deltaRC < rangeLU + lp->epsint) { + if(lp->is_lower[varno]) { + if(isINT) + deltaRC = scaled_floor(lp, varno, unscaled_value(lp, deltaRC, varno)+lp->epsprimal, 1); + upbo = lowbo + deltaRC; + deltaRC = upbo; + i = LE; /* Sets the upper bound */ + } + else { + if(isINT) + deltaRC = scaled_ceil(lp, varno, unscaled_value(lp, deltaRC, varno)+lp->epsprimal, 1); + lowbo = upbo - deltaRC; + deltaRC = lowbo; + i = GE; /* Sets the lower bound */ + } + + /* Check and set feasibility status */ + if((isfeasible != NULL) && (upbo - lowbo < -lp->epsprimal)) + *isfeasible = FALSE; + + /* Flag that we can fix the variable by returning the relation code negated */ + else if(fabs(upbo - lowbo) < lp->epsprimal) + i = -i; + if(newbound != NULL) { + my_roundzero(deltaRC, lp->epsprimal); + *newbound = deltaRC; + } + } + + } + return( i ); +} + + +STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus) +{ + int countsossc, countnint, k, reasonmsg = MSG_NONE; + LPSREAL varsol; + MYBOOL is_better = FALSE, is_equal = FALSE, is_feasible = TRUE; + lprec *lp = BB->lp; + + /* Initialize result and return variables */ + *varno = 0; + *vartype = BB_REAL; + *varcus = 0; + countnint = 0; + BB->nodestatus = lp->spx_status; + BB->noderesult = lp->solution[0]; + + /* If this solution is worse than the best so far, this branch dies. + If we can only have integer OF values, and we only need the first solution + then the OF must be at least (unscaled) 1 better than the best so far */ + if((lp->bb_limitlevel != 1) && (MIP_count(lp) > 0)) { + + /* Check that we don't have a limit on the recursion level; two versions supported: + 1) Absolute B&B level (bb_limitlevel > 0), and + 2) B&B level relative to the "B&B order" (bb_limitlevel < 0). */ + countsossc = lp->sos_vars + lp->sc_vars; + if((lp->bb_limitlevel > 0) && (lp->bb_level > lp->bb_limitlevel+countsossc)) + return( FALSE ); + else if((lp->bb_limitlevel < 0) && + (lp->bb_level > 2*(lp->int_vars+countsossc)*abs(lp->bb_limitlevel))) { + if(lp->bb_limitlevel == DEF_BB_LIMITLEVEL) + report(lp, IMPORTANT, "findnode_BB: Default B&B limit reached at %d; optionally change strategy or limit.\n\n", + lp->bb_level); + return( FALSE ); + } + + /* First initialize or update pseudo-costs from previous optimal solution */ + if(BB->varno == 0) { + varsol = lp->infinite; + if((lp->int_vars+lp->sc_vars > 0) && (lp->bb_PseudoCost == NULL)) + lp->bb_PseudoCost = init_pseudocost(lp, get_bb_rule(lp)); + } + else { + varsol = lp->solution[BB->varno]; + if( ((lp->int_vars > 0) && (BB->vartype == BB_INT)) || + ((lp->sc_vars > 0) && (BB->vartype == BB_SC) && !is_int(lp, BB->varno-lp->rows)) ) + update_pseudocost(lp->bb_PseudoCost, BB->varno-lp->rows, BB->vartype, BB->isfloor, varsol); + } + + /* Make sure we don't have numeric problems (typically due to integer scaling) */ + if((lp->bb_totalnodes > 0) && !bb_better(lp, OF_RELAXED, OF_TEST_WE)) { + if(lp->bb_trace) + report(lp, IMPORTANT, "findnode_BB: Simplex failure due to loss of numeric accuracy\n"); + lp->spx_status = NUMFAILURE; + return( FALSE ); + } + + /* Abandon this branch if the solution is "worse" than a heuristically + determined limit or the previous best MIP solution */ + if(((lp->solutioncount == 0) && !bb_better(lp, OF_HEURISTIC, OF_TEST_BE)) || + ((lp->solutioncount > 0) && + (!bb_better(lp, OF_INCUMBENT | OF_DELTA, OF_TEST_BE | OF_TEST_RELGAP) || + !bb_better(lp, OF_INCUMBENT | OF_DELTA, OF_TEST_BE)))) { + return( FALSE ); + } + + /* Collect violated SC variables (since they can also be real-valued); the + approach is to get them out of the way, since a 0-value is assumed to be "cheap" */ + if(lp->sc_vars > 0) { + *varno = find_sc_bbvar(lp, &countnint); + if(*varno > 0) + *vartype = BB_SC; + } + + /* Look among SOS variables if no SC candidate was found */ + if((SOS_count(lp) > 0) && (*varno == 0)) { + *varno = find_sos_bbvar(lp, &countnint, FALSE); + if(*varno < 0) + *varno = 0; + else if(*varno > 0) + *vartype = BB_SOS; + } + + /* Then collect INTS that are not integer valued, and verify bounds */ + if((lp->int_vars > 0) && (*varno == 0)) { + *varno = find_int_bbvar(lp, &countnint, BB, &is_feasible); + if(*varno > 0) { + *vartype = BB_INT; + if((countnint == 1) && !is_feasible) { + BB->lastrcf = 0; + return( FALSE ); + } + } + } + +#if 1 /* peno */ + /* 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(!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", + lp->bb_varactive[k], k); +*/ + /* set_action(&lp->nomessage, NOMSG_BBLIMIT); */ + /* } */ + return( FALSE ); + } +#endif + + /* 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; + } + } + + /* Current solution is better */ + else if(is_better) { + + /* Update grand total solution count and check if we should go from + depth-first to best-first variable selection mode */ + if(lp->bb_varactive != NULL) { + lp->bb_varactive[0]++; + if((lp->bb_varactive[0] == 1) && + is_bb_mode(lp, NODE_DEPTHFIRSTMODE) && is_bb_mode(lp, NODE_DYNAMICMODE)) + lp->bb_rule &= !NODE_DEPTHFIRSTMODE; + } + + if(lp->bb_trace || + ((lp->verbose >= NORMAL) && (lp->print_sol == FALSE) && (lp->lag_status != RUNNING))) { + report(lp, IMPORTANT, + "%s solution " RESULTVALUEMASK " after %10.0f iter, %9.0f nodes (gap %.1f%%)\n", + (lp->bb_improvements == 0) ? "Feasible" : "Improved", + 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; + } + + lp->bb_status = FEASFOUND; + lp->bb_solutionlevel = lp->bb_level; + lp->solutioncount = 1; + lp->bb_improvements++; + lp->bb_workOF = lp->rhs[0]; + + if(lp->bb_breakfirst || + (!is_infinite(lp, lp->bb_breakOF) && bb_better(lp, OF_USERBREAK, OF_TEST_BE))) + lp->bb_break = TRUE; + } + } + } + else { + is_better = TRUE; + lp->solutioncount = 1; + } + + /* Transfer the successful solution vector */ + if(is_better || is_equal) { +#ifdef ParanoiaMIP + if((lp->bb_level > 0) && + (check_solution(lp, lp->columns, lp->solution, + lp->orig_upbo, lp->orig_lowbo, lp->epssolution) != OPTIMAL)) { + lp->solutioncount = 0; + lp->spx_status = NUMFAILURE; + lp->bb_status = lp->spx_status; + lp->bb_break = TRUE; + return( FALSE ); + } +#endif + transfer_solution(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)); + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + if ((!construct_duals(lp)) || + (is_presolve(lp, PRESOLVE_SENSDUALS) && + (!construct_sensitivity_duals(lp) || !construct_sensitivity_obj(lp)) + ) + ) { + } + } + 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); + } + } + + /* Do tracing and determine if we have arrived at the estimated lower MIP limit */ + *varcus = countnint; + if(MIP_count(lp) > 0) { + if((countnint == 0) && (lp->solutioncount == 1) && (lp->solutionlimit == 1) && + (bb_better(lp, OF_DUALLIMIT, OF_TEST_BE) || bb_better(lp, OF_USERBREAK, OF_TEST_BE | OF_TEST_RELGAP))) { + lp->bb_break = (MYBOOL) (countnint == 0); + return( FALSE ); + } + else if(lp->bb_level > 0) { +#ifdef MIPboundWithOF + if((lp->constraintOF > 0) && (countnint == 0)) + set_rh(lp, lp->constraintOF, lp->solution[0] + my_chsign(!is_maxim(lp), lp->bb_deltaOF)); +#endif + if(lp->spx_trace) + report(lp, DETAILED, "B&B level %5d OPT %16s value " RESULTVALUEMASK "\n", + lp->bb_level, (*varno) ? " " : "INT", lp->solution[0]); + } + return( (MYBOOL) (*varno > 0)); + } + else + return( FALSE ); + +} + +STATIC int solve_BB(BBrec *BB) +{ + int K, status; + lprec *lp = BB->lp; + + /* Protect against infinite recursions do to integer rounding effects */ + status = PROCFAIL; + + /* Shortcut variables, set default bounds */ + K = BB->varno; + + /* Load simple MIP bounds */ + if(K > 0) { + + /* Update cuts, if specified */ + updatecuts_BB(lp); + + /* BRANCH_FLOOR: Force the variable to be smaller than the B&B upper bound */ + if(BB->isfloor) + modifyUndoLadder(lp->bb_upperchange, K, BB->upbo, BB->UPbound); + + /* BRANCH_CEILING: Force the variable to be greater than the B&B lower bound */ + else + modifyUndoLadder(lp->bb_lowerchange, K, BB->lowbo, BB->LObound); + + /* Update MIP node count */ + BB->nodessolved++; + + } + + /* Solve! */ + status = solve_LP(lp, BB); + + /* Do special feasibility assessment of high order SOS'es */ +#if 1 + if((status == OPTIMAL) && (BB->vartype == BB_SOS) && !SOS_is_feasible(lp->SOS, 0, lp->solution)) + status = INFEASIBLE; +#endif + + return( status ); +} + +/* Routine to compute a "strong" pseudo-cost update for a node */ +STATIC MYBOOL strongbranch_BB(lprec *lp, BBrec *BB, int varno, int vartype, int varcus) +{ + MYBOOL success = FALSE; + int i; + BBrec *strongBB; + + /* Create new B&B level and solve each of the branches */ + lp->is_strongbranch = TRUE; + push_basis(lp, lp->var_basic, lp->is_basic, lp->is_lower); + strongBB = push_BB(lp, BB, lp->rows+varno, vartype, varcus); + if(strongBB == BB) + return( success ); + + do { + + /* Solve incremental problem to local optimality */ + lp->bb_strongbranches++; +/* set_action(&lp->spx_action, ACTION_REBASE | ACTION_RECOMPUTE); */ + if(solve_BB(strongBB) == OPTIMAL) { + + /* Update result indicator*/ + success |= 1 << strongBB->isfloor; + + /* Compute new count of non-ints */ + strongBB->lastvarcus = 0; + for(i = 1; i <= lp->columns; i++) { + if(is_int(lp, i) && !solution_is_int(lp, lp->rows+i, FALSE)) + strongBB->lastvarcus++; + } + + /* Perform the pseudo-cost update */ + update_pseudocost(lp->bb_PseudoCost, varno, strongBB->vartype, strongBB->isfloor, + lp->solution[strongBB->varno]); + } + } + while(nextbranch_BB(strongBB)); + + strongBB = pop_BB(strongBB); + if(strongBB != BB) + report(lp, SEVERE, "strongbranch_BB: Invalid bound settings restored for variable %d\n", + varno); + pop_basis(lp, TRUE); + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + + lp->is_strongbranch = FALSE; + + return( success ); +} + +/* Future functions */ +STATIC MYBOOL pre_BB(lprec *lp) +{ + return( TRUE ); +} +STATIC MYBOOL post_BB(lprec *lp) +{ + return( TRUE ); +} + +/* This is the non-recursive B&B driver routine - beautifully simple, yet so subtle! */ +STATIC int run_BB(lprec *lp) +{ + BBrec *currentBB; + int varno, vartype, varcus, prevsolutions; + int status = NOTRUN; + + /* Initialize */ + pre_BB(lp); + prevsolutions = lp->solutioncount; +#ifdef UseMilpSlacksRCF /* Check if we should include ranged constraints */ + varno = lp->sum; +#else + varno = lp->columns; +#endif + lp->bb_upperchange = createUndoLadder(lp, varno, 2*MIP_count(lp)); + lp->bb_lowerchange = createUndoLadder(lp, varno, 2*MIP_count(lp)); + lp->rootbounds = currentBB = push_BB(lp, NULL, 0, BB_REAL, 0); + + /* 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 + + if((status == OPTIMAL) && findnode_BB(currentBB, &varno, &vartype, &varcus)) + currentBB = push_BB(lp, currentBB, varno, vartype, varcus); + + else while((lp->bb_level > 0) && !nextbranch_BB(currentBB)) + currentBB = pop_BB(currentBB); + + } + + /* Finalize */ + freeUndoLadder(&(lp->bb_upperchange)); + freeUndoLadder(&(lp->bb_lowerchange)); + + /* Check if we should adjust status */ + if(lp->solutioncount > prevsolutions) { + if((status == PROCBREAK) || (status == USERABORT) || (status == TIMEOUT) || userabort(lp, -1)) + status = SUBOPTIMAL; + else + status = OPTIMAL; + if(lp->bb_totalnodes > 0) + lp->spx_status = OPTIMAL; + } + post_BB(lp); + return( status ); +} + diff --git a/src/external/lpsolve/build/lp_solve/lp_params.c b/src/external/lpsolve/build/lp_solve/lp_params.c new file mode 100644 index 00000000..ef961376 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_params.c @@ -0,0 +1,700 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#include +#include +#include +#include + +#include "commonlib.h" +#include "lp_lib.h" +#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); + +#define intfunction 1 +#define longfunction 2 +#define MYBOOLfunction 3 +#define REALfunction 4 + +#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 WRITE_COMMENTED 0 +#define WRITE_ACTIVE 1 + +struct _values { + int value; + char *svalue; +}; + +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 */ + } 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 */ + } set_function; + int type; /* set via set*function */ + struct _values *values; /* set via setvalues to a structure of _values */ + int elements; /* or via setNULLvalues if the value is shown as is */ + unsigned int basemask; + int mask; /* WRITE_ACTIVE or WRITE_COMMENTED */ +}; + +static struct _values anti_degen[] = +{ + { setvalue(ANTIDEGEN_NONE) }, + { setvalue(ANTIDEGEN_FIXEDVARS) }, + { setvalue(ANTIDEGEN_COLUMNCHECK) }, + { setvalue(ANTIDEGEN_STALLING) }, + { setvalue(ANTIDEGEN_NUMFAILURE) }, + { setvalue(ANTIDEGEN_LOSTFEAS) }, + { setvalue(ANTIDEGEN_INFEASIBLE) }, + { setvalue(ANTIDEGEN_DYNAMIC) }, + { setvalue(ANTIDEGEN_DURINGBB) }, + { setvalue(ANTIDEGEN_RHSPERTURB) }, + { setvalue(ANTIDEGEN_BOUNDFLIP) }, +}; + +static struct _values basiscrash[] = +{ + { setvalue(CRASH_NONE) }, + /* { setvalue(CRASH_NONBASICBOUNDS) }, */ /* not yet implemented */ + { setvalue(CRASH_MOSTFEASIBLE) }, + { setvalue(CRASH_LEASTDEGENERATE) }, +}; + +static struct _values bb_floorfirst[] = +{ + { setvalue(BRANCH_CEILING) }, + { setvalue(BRANCH_FLOOR) }, + { setvalue(BRANCH_AUTOMATIC) }, +}; + +static struct _values bb_rule[] = +{ + { setvalue(NODE_FIRSTSELECT) }, + { setvalue(NODE_GAPSELECT) }, + { setvalue(NODE_RANGESELECT) }, + { setvalue(NODE_FRACTIONSELECT) }, + { setvalue(NODE_PSEUDOCOSTSELECT) }, + { setvalue(NODE_PSEUDONONINTSELECT) }, + { setvalue(NODE_PSEUDORATIOSELECT) }, + { setvalue(NODE_USERSELECT) }, + { setvalue(NODE_WEIGHTREVERSEMODE) }, + { setvalue(NODE_BRANCHREVERSEMODE) }, + { setvalue(NODE_GREEDYMODE) }, + { setvalue(NODE_PSEUDOCOSTMODE) }, + { setvalue(NODE_DEPTHFIRSTMODE) }, + { setvalue(NODE_RANDOMIZEMODE) }, + { setvalue(NODE_GUBMODE) }, + { setvalue(NODE_DYNAMICMODE) }, + { setvalue(NODE_RESTARTMODE) }, + { setvalue(NODE_BREADTHFIRSTMODE) }, + { setvalue(NODE_AUTOORDER) }, + { setvalue(NODE_RCOSTFIXING) }, + { setvalue(NODE_STRONGINIT) }, +}; + +static struct _values improve[] = +{ + { setvalue(IMPROVE_NONE) }, + { setvalue(IMPROVE_SOLUTION) }, + { setvalue(IMPROVE_DUALFEAS) }, + { setvalue(IMPROVE_THETAGAP) }, + { setvalue(IMPROVE_BBSIMPLEX) }, +}; + +static LPSREAL __WINAPI get_mip_gap_abs(lprec *lp) +{ + return(get_mip_gap(lp, TRUE)); +} + +static LPSREAL __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) +{ + set_mip_gap(lp, TRUE, mip_gap); +} + +static void __WINAPI set_mip_gap_rel(lprec *lp, LPSREAL mip_gap) +{ + set_mip_gap(lp, FALSE, mip_gap); +} + +static struct _values pivoting[] = +{ + { setvalue(PRICER_FIRSTINDEX) }, + { setvalue(PRICER_DANTZIG) }, + { setvalue(PRICER_DEVEX) }, + { setvalue(PRICER_STEEPESTEDGE) }, + { setvalue(PRICE_PRIMALFALLBACK) }, + { setvalue(PRICE_MULTIPLE) }, + { setvalue(PRICE_PARTIAL) }, + { setvalue(PRICE_ADAPTIVE) }, + { setvalue(PRICE_RANDOMIZE) }, + { setvalue(PRICE_AUTOPARTIAL) }, + { setvalue(PRICE_LOOPLEFT) }, + { setvalue(PRICE_LOOPALTERNATE) }, + { setvalue(PRICE_HARRISTWOPASS) }, + { setvalue(PRICE_TRUENORMINIT) }, +}; + +static struct _values presolving[] = +{ + { setvalue(PRESOLVE_NONE) }, + { setvalue(PRESOLVE_ROWS) }, + { setvalue(PRESOLVE_COLS) }, + { setvalue(PRESOLVE_LINDEP) }, + { setvalue(PRESOLVE_AGGREGATE) }, + { setvalue(PRESOLVE_SPARSER) }, + { setvalue(PRESOLVE_SOS) }, + { setvalue(PRESOLVE_REDUCEMIP) }, + { setvalue(PRESOLVE_KNAPSACK) }, + { setvalue(PRESOLVE_ELIMEQ2) }, + { setvalue(PRESOLVE_IMPLIEDFREE) }, + { setvalue(PRESOLVE_REDUCEGCD) }, + { setvalue(PRESOLVE_PROBEFIX) }, + { setvalue(PRESOLVE_PROBEREDUCE) }, + { setvalue(PRESOLVE_ROWDOMINATE) }, + { setvalue(PRESOLVE_COLDOMINATE) }, + { setvalue(PRESOLVE_MERGEROWS) }, + { setvalue(PRESOLVE_IMPLIEDSLK) }, + { setvalue(PRESOLVE_COLFIXDUAL) }, + { setvalue(PRESOLVE_BOUNDS) }, + { setvalue(PRESOLVE_DUALS) }, + { setvalue(PRESOLVE_SENSDUALS) }, +}; + +static char *STRLWR(char *str) +{ + char *ptr; + + for(ptr = str; *ptr; ptr++) + *ptr = (char) tolower((unsigned char) *ptr); + + return(str); +} + +static char *STRUPR(char *str) +{ + char *ptr; + + for(ptr = str; *ptr; ptr++) + *ptr = (char) toupper((unsigned char) *ptr); + + return(str); +} + +static void __WINAPI set_presolve1(lprec *lp, int do_presolve) +{ + set_presolve(lp, do_presolve, get_presolveloops(lp)); +} + +static void __WINAPI set_presolve2(lprec *lp, int maxloops) +{ + set_presolve(lp, get_presolve(lp), maxloops); +} + +static struct _values print_sol[] = +{ + { FALSE, "0" }, + { TRUE, "1" }, + { setvalue(AUTOMATIC) }, +}; + +static struct _values scaling[] = +{ + { setvalue(SCALE_NONE) }, + { setvalue(SCALE_EXTREME) }, + { setvalue(SCALE_RANGE) }, + { setvalue(SCALE_MEAN) }, + { setvalue(SCALE_GEOMETRIC) }, + { setvalue(SCALE_CURTISREID) }, + { setvalue(SCALE_QUADRATIC) }, + { setvalue(SCALE_LOGARITHMIC) }, + { setvalue(SCALE_USERWEIGHT) }, + { setvalue(SCALE_POWER2) }, + { setvalue(SCALE_EQUILIBRATE) }, + { setvalue(SCALE_INTEGERS) }, + { setvalue(SCALE_DYNUPDATE) }, + { setvalue(SCALE_ROWSONLY) }, + { setvalue(SCALE_COLSONLY) }, +}; + +static struct _values simplextype[] = +{ + { setvalue(SIMPLEX_PRIMAL_PRIMAL) }, + { setvalue(SIMPLEX_DUAL_PRIMAL) }, + { setvalue(SIMPLEX_PRIMAL_DUAL) }, + { setvalue(SIMPLEX_DUAL_DUAL) }, +}; + +static struct _values verbose[] = +{ + { setvalue(NEUTRAL) }, + { setvalue(CRITICAL) }, + { setvalue(SEVERE) }, + { setvalue(IMPORTANT) }, + { setvalue(NORMAL) }, + { setvalue(DETAILED) }, + { setvalue(FULL) }, +}; + +static struct _functions functions[] = +{ + /* solve options */ + { "ANTI_DEGEN", setintfunction(get_anti_degen, set_anti_degen), setvalues(anti_degen, ~0), WRITE_ACTIVE }, + { "BASISCRASH", setintfunction(get_basiscrash, set_basiscrash), setvalues(basiscrash, ~0), WRITE_ACTIVE }, + { "IMPROVE", setintfunction(get_improve, set_improve), setvalues(improve, ~0), WRITE_ACTIVE }, + { "MAXPIVOT", setintfunction(get_maxpivot, set_maxpivot), setNULLvalues, WRITE_ACTIVE }, + { "NEGRANGE", setREALfunction(get_negrange, set_negrange), setNULLvalues, WRITE_ACTIVE }, + { "PIVOTING", setintfunction(get_pivoting, set_pivoting), setvalues(pivoting, PRICER_LASTOPTION), WRITE_ACTIVE }, + { "PRESOLVE", setintfunction(get_presolve, set_presolve1), setvalues(presolving, ~0), WRITE_ACTIVE }, + { "PRESOLVELOOPS", setintfunction(get_presolveloops, set_presolve2), setNULLvalues, WRITE_ACTIVE }, + { "SCALELIMIT", setREALfunction(get_scalelimit, set_scalelimit), setNULLvalues, WRITE_ACTIVE }, + { "SCALING", setintfunction(get_scaling, set_scaling), setvalues(scaling, SCALE_CURTISREID), WRITE_ACTIVE }, + { "SIMPLEXTYPE", setintfunction(get_simplextype, set_simplextype), setvalues(simplextype, ~0), WRITE_ACTIVE }, + { "OBJ_IN_BASIS", setMYBOOLfunction(is_obj_in_basis, set_obj_in_basis), setNULLvalues, WRITE_COMMENTED }, + + /* B&B options */ + { "BB_DEPTHLIMIT", setintfunction(get_bb_depthlimit, set_bb_depthlimit), setNULLvalues, WRITE_ACTIVE }, + { "BB_FLOORFIRST", setintfunction(get_bb_floorfirst, set_bb_floorfirst), setvalues(bb_floorfirst, ~0), WRITE_ACTIVE }, + { "BB_RULE", setintfunction(get_bb_rule, set_bb_rule), setvalues(bb_rule, NODE_STRATEGYMASK), WRITE_ACTIVE }, + { "BREAK_AT_FIRST", setMYBOOLfunction(is_break_at_first, set_break_at_first), setNULLvalues, WRITE_COMMENTED }, + { "BREAK_AT_VALUE", setREALfunction(get_break_at_value, set_break_at_value), setNULLvalues, WRITE_COMMENTED }, + { "MIP_GAP_ABS", setREALfunction(get_mip_gap_abs, set_mip_gap_abs), setNULLvalues, WRITE_ACTIVE }, + { "MIP_GAP_REL", setREALfunction(get_mip_gap_rel, set_mip_gap_rel), setNULLvalues, WRITE_ACTIVE }, + { "EPSINT", setREALfunction(get_epsint, set_epsint), setNULLvalues, WRITE_ACTIVE }, + + /* tolerances, values */ + { "EPSB", setREALfunction(get_epsb, set_epsb), setNULLvalues, WRITE_ACTIVE }, + { "EPSD", setREALfunction(get_epsd, set_epsd), setNULLvalues, WRITE_ACTIVE }, + { "EPSEL", setREALfunction(get_epsel, set_epsel), setNULLvalues, WRITE_ACTIVE }, + { "EPSPERTURB", setREALfunction(get_epsperturb, set_epsperturb), setNULLvalues, WRITE_ACTIVE }, + { "EPSPIVOT", setREALfunction(get_epspivot, set_epspivot), setNULLvalues, WRITE_ACTIVE }, + { "INFINITE", setREALfunction(get_infinite, set_infinite), setNULLvalues, WRITE_ACTIVE }, + + /* read-only options */ + { "DEBUG", setMYBOOLfunction(is_debug, set_debug), setNULLvalues, WRITE_COMMENTED }, + { "OBJ_BOUND", setREALfunction(get_obj_bound, set_obj_bound), setNULLvalues, WRITE_COMMENTED }, + { "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 }, +}; + +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; + 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); + ini_writecomment(fp, buf); + for(i = 0; i < sizeof(functions) / sizeof(*functions); i++) { + switch(functions[i].type) { + case intfunction: + if(functions[i].get_function.int_get_function == NULL) + continue; + ret = functions[i].get_function.int_get_function(lp); + break; + case longfunction: + if(functions[i].get_function.long_get_function == NULL) + continue; + ret = functions[i].get_function.long_get_function(lp); + break; + case MYBOOLfunction: + if(functions[i].get_function.MYBOOL_get_function == NULL) + continue; + ret = (int) functions[i].get_function.MYBOOL_get_function(lp); + break; + case REALfunction: + if(functions[i].get_function.REAL_get_function == NULL) + continue; + a = functions[i].get_function.REAL_get_function(lp); + break; + } + buf[0] = 0; + if(functions[i].values == NULL) { + switch(functions[i].type) { + case intfunction: + case longfunction: + case MYBOOLfunction: + sprintf(buf, "%d", ret); + break; + case REALfunction: + sprintf(buf, "%g", a); + break; + } + } + else { + elements = functions[i].elements; + 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; + if(value == 0) { + if(ret2 == 0) { + if(*buf) + strcat(buf, " + "); + strcat(buf, functions[i].values[j].svalue); + } + } + else if((ret2 & value) == value) { + for(k = 0; k < elements; k++) { + value2 = functions[i].values[k].value; + if((k != j) && (value2 > value) && ((value2 & value) == value) && ((ret2 & value2) == value2)) + break; + } + if(k == elements) { + if(*buf) + strcat(buf, " + "); + strcat(buf, functions[i].values[j].svalue); + } + } + } + } + if(functions[i].mask & WRITE_ACTIVE) + par[0] = 0; + else + strcpy(par, ";"); + strcat(par, functions[i].par); + ini_writedata(fp, STRLWR(par), buf); + } +} + +static void readoptions(char *options, char **header) +{ + char *ptr1, *ptr2; + + if(options != NULL) { + ptr1 = options; + while(*ptr1) { + ptr2 = strchr(ptr1, '-'); + if(ptr2 == NULL) + break; + ptr2++; + if(tolower((unsigned char) *ptr2) == 'h') { + for(++ptr2; (*ptr2) && (isspace(*ptr2)); ptr2++); + for(ptr1 = ptr2; (*ptr1) && (!isspace(*ptr1)); ptr1++); + *header = (char *) calloc(1 + (int) (ptr1 - ptr2), 1); + memcpy(*header, ptr2, (int) (ptr1 - ptr2)); + } + } + } + + if(*header == NULL) + *header = strdup("Default"); +} + +MYBOOL __WINAPI write_params(lprec *lp, char *filename, char *options) +{ + int k, ret, params_written; + FILE *fp, *fp0; + int state = 0, looping, newline; + char buf[4096], *filename0, *ptr1, *ptr2, *header = NULL; + + readoptions(options, &header); + + k = (int) strlen(filename); + filename0 = (char *) malloc(k + 1 + 1); + strcpy(filename0, filename); + ptr1 = strrchr(filename0, '.'); + ptr2 = strrchr(filename0, '\\'); + if((ptr1 == NULL) || ((ptr2 != NULL) && (ptr1 < ptr2))) + ptr1 = filename0 + k; + memmove(ptr1 + 1, ptr1, k + 1 - (int) (ptr1 - filename0)); + ptr1[0] = '_'; + if(rename(filename, filename0)) { + switch(errno) { + case ENOENT: /* File or path specified by oldname not found */ + FREE(filename0); + filename0 = NULL; + break; + case EACCES: /* File or directory specified by newname already exists or could not be created (invalid path); or oldname is a directory and newname specifies a different path. */ + FREE(filename0); + FREE(header); + return(FALSE); + break; + } + } + + if((fp = ini_create(filename)) == NULL) + ret = FALSE; + else { + params_written = FALSE; + newline = TRUE; + if(filename0 != NULL) { + fp0 = ini_open(filename0); + if(fp0 == NULL) { + rename(filename0, filename); + FREE(filename0); + FREE(header); + return(FALSE); + } + looping = TRUE; + while(looping) { + switch(ini_readdata(fp0, buf, sizeof(buf), TRUE)) { + case 0: /* End of file */ + looping = FALSE; + break; + case 1: /* header */ + ptr1 = strdup(buf); + STRUPR(buf); + ptr2 = strdup(header); + STRUPR(ptr2); + if(strcmp(buf, ptr2) == 0) { + write_params1(lp, fp, ptr1, newline); + params_written = TRUE; + newline = TRUE; + state = 1; + } + else { + state = 0; + ini_writeheader(fp, ptr1, newline); + newline = TRUE; + } + FREE(ptr2); + FREE(ptr1); + break; + case 2: /* data */ + if(state == 0) { + ini_writedata(fp, NULL, buf); + newline = (*buf != 0); + } + break; + } + } + ini_close(fp0); + } + + if(!params_written) + write_params1(lp, fp, header, newline); + + ini_close(fp); + ret = TRUE; + } + + if(filename0 != NULL) { + remove(filename0); + FREE(filename0); + } + + FREE(header); + + return( (MYBOOL) ret ); +} + + +MYBOOL __WINAPI read_params(lprec *lp, char *filename, char *options) +{ + int ret, looping, line; + FILE *fp; + hashtable *hashfunctions, *hashparameters; + hashelem *hp; + int i, j, elements, n, intvalue, state = 0; + LPSREAL REALvalue; + char buf[4096], *header = NULL, *ptr, *ptr1, *ptr2; + + if((fp = ini_open(filename)) == NULL) + ret = FALSE; + else { + /* create hashtable of all callable commands to find them quickly */ + hashfunctions = create_hash_table(sizeof(functions) / sizeof(*functions), 0); + for (n = 0, i = 0; i < (int) (sizeof(functions)/sizeof(*functions)); i++) { + puthash(functions[i].par, i, NULL, hashfunctions); + if(functions[i].values != NULL) + n += functions[i].elements; + } + /* create hashtable of all arguments to find them quickly */ + hashparameters = create_hash_table(n, 0); + for (n = 0, i = 0; i < (int) (sizeof(functions)/sizeof(*functions)); i++) { + if(functions[i].values != NULL) { + elements = functions[i].elements; + for(j = 0; j < elements; j++) + if((strcmp(functions[i].values[j].svalue, "0") != 0) && + (strcmp(functions[i].values[j].svalue, "1") != 0)) + puthash(functions[i].values[j].svalue, j, NULL, hashparameters); + } + } + + readoptions(options, &header); + + STRUPR(header); + ret = looping = TRUE; + line = 0; + while((ret) && (looping)) { + line++; + switch(ini_readdata(fp, buf, sizeof(buf), FALSE)) { + case 0: /* End of file */ + looping = FALSE; + break; + case 1: /* header */ + switch(state) { + case 0: + STRUPR(buf); + if(strcmp(buf, header) == 0) + state = 1; + break; + case 1: + looping = FALSE; + break; + } + break; + case 2: /* data */ + if(state == 1) { + for(ptr = buf; (*ptr) && (isspace(*ptr)); ptr++); + } + else + ptr = NULL; + if((ptr != NULL) && (*ptr)) { + STRUPR(buf); + ptr = strchr(buf, '='); + if(ptr == NULL) { + report(lp, IMPORTANT, "read_params: No equal sign on line %d\n", line); + ret = FALSE; + } + else { + *ptr = 0; + for(ptr1 = buf; isspace(*ptr1); ptr1++); + for(ptr2 = ptr - 1; (ptr2 >= ptr1) && (isspace(*ptr2)); ptr2--); + if(ptr2 <= ptr1) { + report(lp, IMPORTANT, "read_params: No parameter name before equal sign on line %d\n", line); + ret = FALSE; + } + else { + ptr2[1] = 0; + hp = findhash(ptr1, hashfunctions); + if(hp == NULL) { + report(lp, IMPORTANT, "read_params: Unknown parameter name (%s) before equal sign on line %d\n", ptr1, line); + ret = FALSE; + } + else { + i = hp->index; + ptr1 = ++ptr; + intvalue = 0; + REALvalue = 0; + if(functions[i].values == NULL) { + switch(functions[i].type) { + case intfunction: + case longfunction: + case MYBOOLfunction: + intvalue = strtol(ptr1, &ptr2, 10); + while((*ptr2) && (isspace(*ptr2))) + ptr2++; + if(*ptr2) { + report(lp, IMPORTANT, "read_params: Invalid integer value on line %d\n", line); + ret = FALSE; + } + break; + case REALfunction: + REALvalue = strtod(ptr1, &ptr2); + while((*ptr2) && (isspace(*ptr2))) + ptr2++; + if(*ptr2) { + report(lp, IMPORTANT, "read_params: Invalid real value on line %d\n", line); + ret = FALSE; + } + break; + } + } + else { + while(ret) { + ptr = strchr(ptr1, '+'); + if(ptr == NULL) + ptr = ptr1 + strlen(ptr1); + for(; isspace(*ptr1); ptr1++); + for(ptr2 = ptr - 1; (ptr2 >= ptr1) && (isspace(*ptr2)); ptr2--); + if(ptr2 <= ptr1) + break; + else { + ptr2[1] = 0; + hp = findhash(ptr1, hashparameters); + if (hp == NULL) { + report(lp, IMPORTANT, "read_params: Invalid parameter name (%s) on line %d\n", ptr1, line); + ret = FALSE; + } + else { + j = hp->index; + if((j >= functions[i].elements) || + (strcmp(functions[i].values[j].svalue, ptr1))) { + report(lp, IMPORTANT, "read_params: Inappropriate parameter name (%s) on line %d\n", ptr1, line); + ret = FALSE; + } + else { + intvalue += functions[i].values[j].value; + } + } + ptr1 = ptr + 1; + } + } + } + if(ret) { + switch(functions[i].type) { + case intfunction: + functions[i].set_function.int_set_function(lp, intvalue); + break; + case longfunction: + functions[i].set_function.long_set_function(lp, intvalue); + break; + case MYBOOLfunction: + functions[i].set_function.MYBOOL_set_function(lp, (MYBOOL) intvalue); + break; + case REALfunction: + functions[i].set_function.REAL_set_function(lp, REALvalue); + break; + } + } + } + } + } + } + break; + } + } + + FREE(header); + free_hash_table(hashfunctions); + free_hash_table(hashparameters); + + ini_close(fp); + } + + return( (MYBOOL) ret ); +} diff --git a/src/external/lpsolve/build/lp_solve/lp_presolve.c b/src/external/lpsolve/build/lp_solve/lp_presolve.c new file mode 100644 index 00000000..0a98e421 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_presolve.c @@ -0,0 +1,5901 @@ + +/* ------------------------------------------------------------------------- + Presolve routines for lp_solve v5.0+ + ------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, lp_presolve, lp_crash.h, lp_scale.h, lp_report.h + + Release notes: + v1.0.0 1 January 2003 Initial crude version used with lp_solve v4. + v5.0.0 1 January 2004 Significantly expanded and repackaged + presolve routines for lp_solve v5 release. + v5.0.1 1 April 2004 Added reference to new crash module + v5.1.0 20 August 2004 Reworked infeasibility detection. + Added encapsulation of presolve undo logic. + v5.1.1 10 September 2004 Added variable bound tightening based on + full-constraint information, as well as + variable fixing by duality. + v5.2.0 1 January 2005 Fixes to bound fixing handling. + Added fast batch compression after presolve. + Restructured calls by adding presolve wrapper. + Major optimization of identification logic + along with bug fixes. + Enabled storage of eliminated matrix data. + Added function to report on constraint classes. + v5.5.0 1 June 2005 Added implied slack presolve, restructured + looping logic to be more modular, and made + active row/column selection logic faster. + v5.5.1 18 June 2005 Finished sparsity-enhancing logic and added + initial version of column aggregation code. + ------------------------------------------------------------------------- */ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_presolve.h" +#include "lp_crash.h" +#include "lp_scale.h" +#include "lp_report.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +#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)) { + report(psdata->lp, +#ifdef Paranoia + NORMAL, +#else + DETAILED, +#endif + "presolve_setstatus: Status set to '%s' on code line %d, file '%s'\n", + (status == INFEASIBLE ? "INFEASIBLE" : "UNBOUNDED"), lineno, (filename == NULL ? "Unknown" : filename)); + } + return( status ); +} + +STATIC MYBOOL presolve_statuscheck(presolverec *psdata, int *status) +{ + if(*status == RUNNING) { + lprec *lp = psdata->lp; + if(!mat_validate(lp->matA)) + *status = MATRIXERROR; + else if(userabort(lp, -1)) + *status = lp->spx_status; + } + return( (MYBOOL) (*status == RUNNING) ); +} + +STATIC MYBOOL presolve_createUndo(lprec *lp) +{ + if(lp->presolve_undo != NULL) + presolve_freeUndo(lp); + lp->presolve_undo = (presolveundorec *) calloc(1, sizeof(presolveundorec)); + lp->presolve_undo->lp = lp; + if(lp->presolve_undo == NULL) + return( FALSE ); + return( TRUE ); +} +STATIC MYBOOL inc_presolve_space(lprec *lp, int delta, MYBOOL isrows) +{ + int i, ii, + oldrowcolalloc, rowcolsum, oldrowalloc, oldcolalloc; + presolveundorec *psundo = lp->presolve_undo; + + if(psundo == NULL) { + presolve_createUndo(lp); + psundo = lp->presolve_undo; + } + + /* Set constants */ + oldrowalloc = lp->rows_alloc-delta; + oldcolalloc = lp->columns_alloc-delta; + oldrowcolalloc = lp->sum_alloc-delta; + rowcolsum = lp->sum_alloc + 1; + + /* Reallocate lp memory */ + if(isrows) + allocREAL(lp, &psundo->fixed_rhs, lp->rows_alloc+1, AUTOMATIC); + else + allocREAL(lp, &psundo->fixed_obj, lp->columns_alloc+1, AUTOMATIC); + allocINT(lp, &psundo->var_to_orig, rowcolsum, AUTOMATIC); + allocINT(lp, &psundo->orig_to_var, rowcolsum, AUTOMATIC); + + /* Fill in default values, where appropriate */ + if(isrows) + ii = oldrowalloc+1; + else + ii = oldcolalloc+1; + for(i = oldrowcolalloc+1; i < rowcolsum; i++, ii++) { + psundo->var_to_orig[i] = 0; + psundo->orig_to_var[i] = 0; + if(isrows) + psundo->fixed_rhs[ii] = 0; + else + psundo->fixed_obj[ii] = 0; + } + + return(TRUE); +} +STATIC MYBOOL presolve_setOrig(lprec *lp, int orig_rows, int orig_cols) +{ + presolveundorec *psundo = lp->presolve_undo; + + if(psundo == NULL) + return( FALSE ); + psundo->orig_rows = orig_rows; + psundo->orig_columns = orig_cols; + psundo->orig_sum = orig_rows + orig_cols; + if(lp->wasPresolved) + presolve_fillUndo(lp, orig_rows, orig_cols, FALSE); + return( TRUE ); +} +STATIC MYBOOL presolve_fillUndo(lprec *lp, int orig_rows, int orig_cols, MYBOOL setOrig) +{ + int i; + presolveundorec *psundo = lp->presolve_undo; + + for(i = 0; i <= orig_rows; i++) { + psundo->var_to_orig[i] = i; + psundo->orig_to_var[i] = i; + psundo->fixed_rhs[i] = 0; + } + for(i = 1; i <= orig_cols; i++) { + psundo->var_to_orig[orig_rows + i] = i; + psundo->orig_to_var[orig_rows + i] = i; + psundo->fixed_obj[i] = 0; + } + if(setOrig) + presolve_setOrig(lp, orig_rows, orig_cols); + + return( TRUE ); +} +STATIC MYBOOL presolve_rebuildUndo(lprec *lp, MYBOOL isprimal) +{ + int ik, ie, ix, j, k, *colnrDep; + LPSREAL hold, *value, *slacks, *solution; + presolveundorec *psdata = lp->presolve_undo; + MATrec *mat = NULL; + + /* Point to and initialize undo structure at first call */ + 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; + } + if(mat == NULL) + return( FALSE ); + + /* Loop backward over the undo chain */ + for(j = mat->col_tag[0]; j > 0; j--) { + ix = mat->col_tag[j]; + ik = mat->col_end[j-1]; + ie = mat->col_end[j]; + colnrDep = &COL_MAT_ROWNR(ik); + value = &COL_MAT_VALUE(ik); + hold = 0; + k = 0; + for(; ik < ie; ik++, colnrDep += matRowColStep, value += matValueStep) { + + /* Constant term */ + if(*colnrDep == 0) + hold += *value; + + /* Special case with dependence on a slack variable */ + else if(isprimal && (*colnrDep > lp->presolve_undo->orig_columns)) { + k = (*colnrDep) - lp->presolve_undo->orig_columns; + hold -= (*value) * slacks[k]; + slacks[k] = 0; + } + else if(!isprimal && (*colnrDep > lp->presolve_undo->orig_rows)) { + k = (*colnrDep) - lp->presolve_undo->orig_rows; + hold -= (*value) * slacks[k]; + slacks[k] = 0; + } + + /* Dependence on other user variable */ + else + hold -= (*value) * solution[*colnrDep]; + + *value = 0; + } + if(fabs(hold) > lp->epsvalue) + solution[ix] = hold; + } + + return( TRUE ); +} +STATIC MYBOOL presolve_freeUndo(lprec *lp) +{ + presolveundorec *psundo = lp->presolve_undo; + + if(psundo == NULL) + return( FALSE ); + FREE(psundo->orig_to_var); + FREE(psundo->var_to_orig); + FREE(psundo->fixed_rhs); + FREE(psundo->fixed_obj); + if(psundo->deletedA != NULL) + freeUndoLadder(&(psundo->deletedA)); + if(psundo->primalundo != NULL) + freeUndoLadder(&(psundo->primalundo)); + if(psundo->dualundo != NULL) + freeUndoLadder(&(psundo->dualundo)); + FREE(lp->presolve_undo); + return( TRUE ); +} + +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); + MATrec *mat = lp->matA; + + if(presolve_collength(psdata, colnr) == 0) + return; + + /* Add undo information for the dual of the deleted constraint */ + item = 0; + for(ix = presolve_nextrow(psdata, colnr, &item); ix >= 0; + ix = presolve_nextrow(psdata, colnr, &item)) { + iix = COL_MAT_ROWNR(ix); + if(iix == rownr) + continue; + if(!firstdone) + firstdone = addUndoPresolve(lp, FALSE, rownr, get_mat(lp, 0, colnr)/Aij, + get_mat_byindex(lp, ix, FALSE, TRUE)/Aij, iix); + else + appendUndoPresolve(lp, FALSE, get_mat_byindex(lp, ix, FALSE, TRUE)/Aij, iix); + } +} + +/* ----------------------------------------------------------------------------- */ +/* Presolve debugging routines */ +/* ----------------------------------------------------------------------------- */ +STATIC MYBOOL presolve_SOScheck(presolverec *psdata) +{ + MYBOOL status = TRUE; + lprec *lp = psdata->lp; + int *list, i, j, n, k, nk, colnr, nSOS = SOS_count(lp), nerr = 0; + SOSrec *SOS; + + if(nSOS == 0) + return( status ); + + /* For each SOS and each member check validity */ + for(i = 1; i<= nSOS; i++) { + SOS = lp->SOS->sos_list[i-1]; + list = SOS->members; + n = list[0]; + for(j = 1; j<= n; j++) { + colnr = list[j]; + /* Check valid range */ + if((colnr < 1) || (colnr > lp->columns)) { + nerr++; + report(lp, IMPORTANT, "presolve_SOScheck: A - Column index %d is outside of valid range\n", + colnr); + } + /* Check for deletion */ + if(!isActiveLink(psdata->cols->varmap, colnr)) { + nerr++; + report(lp, IMPORTANT, "presolve_SOScheck: B - Column index %d has been marked for deletion\n", + colnr); + } + /* Check if sorted member array is Ok */ + if(SOS_member_index(lp->SOS, i, colnr) != j) { + nerr++; + report(lp, IMPORTANT, "presolve_SOScheck: C - Column index %d not found in fast search array\n", + colnr); + } + /* Check for variable membership in this SOS record of the sparse storage */ + k = lp->SOS->memberpos[colnr-1]; + nk = lp->SOS->memberpos[colnr]; + while((k < nk) && (lp->SOS->membership[k] != i)) + k++; + if(k >= nk) { + nerr++; + report(lp, IMPORTANT, "presolve_SOScheck: D - Column index %d was not found in sparse array\n", + colnr); + } + } + } + + /* Check that all members in the sparse array can be validated as SOS members */ + for(colnr = 1; colnr <= lp->columns; colnr++) { + k = lp->SOS->memberpos[colnr-1]; + nk = lp->SOS->memberpos[colnr]; + for(; k < nk; k++) { + if(!SOS_is_member(lp->SOS, lp->SOS->membership[k], colnr)) { + nerr++; + report(lp, IMPORTANT, "presolve_SOScheck: E - Sparse array did not indicate column index %d as member of SOS %d\n", + colnr, lp->SOS->membership[k]); + } + } + } + status = (MYBOOL) (nerr == 0); + if(!status) + report(lp, IMPORTANT, "presolve_SOScheck: There were %d errors\n", + nerr); + + + return( status ); +} + +/* ----------------------------------------------------------------------------- */ +/* Presolve routines for tightening the model */ +/* ----------------------------------------------------------------------------- */ + +INLINE LPSREAL presolve_roundrhs(lprec *lp, LPSREAL 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) + value = testout; + else if(value != testout) + value += my_chsign(isGE, (value-testout)/2); + /* value = testout + my_chsign(isGE, (value-testout)/2); */ +#else + if(testout != value) + value += my_chsign(isGE, eps*1000); /* BASE OPTION */ +#endif + +#endif + return( value ); +} + +INLINE LPSREAL presolve_roundval(lprec *lp, LPSREAL value) +{ +#ifdef DoPresolveRounding + /* value = my_precision(value, PRESOLVE_EPSVALUE*MAX(1,log10(1+fabs(value)))); */ + value = my_precision(value, PRESOLVE_EPSVALUE); /* BASE OPTION */ +#endif + return( value ); +} + +INLINE MYBOOL presolve_mustupdate(lprec *lp, int colnr) +{ +#if 0 + return( my_infinite(lp, get_lowbo(lp, colnr)) || + my_infinite(lp, get_upbo(lp, colnr)) ); +#else + return( my_infinite(lp, lp->orig_lowbo[lp->rows+colnr]) || + my_infinite(lp, lp->orig_upbo[lp->rows+colnr]) ); +#endif +} + +INLINE LPSREAL presolve_sumplumin(lprec *lp, int item, psrec *ps, MYBOOL doUpper) +{ + LPSREAL *plu = (doUpper ? ps->pluupper : ps->plulower), + *neg = (doUpper ? ps->negupper : ps->neglower); + + if(fabs(plu[item]) >= lp->infinite) + return( plu[item] ); + else if(fabs(neg[item]) >= lp->infinite) + return( neg[item] ); + else + return( plu[item]+neg[item] ); +} + +INLINE void presolve_range(lprec *lp, int rownr, psrec *ps, LPSREAL *loValue, LPSREAL *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) +{ + delta = my_chsign(is_chsign(lp, rownr), lp->presolve_undo->fixed_rhs[rownr] + delta); + *loValue = presolve_sumplumin(lp, rownr, ps, FALSE) + delta; + *hiValue = presolve_sumplumin(lp, rownr, ps, TRUE) + delta; +} + +STATIC MYBOOL presolve_rowfeasible(presolverec *psdata, int rownr, MYBOOL userowmap) +{ + lprec *lp = psdata->lp; + MYBOOL status = TRUE; + int contype, origrownr = rownr; + LPSREAL LHS, RHS, value; + + /* Optionally loop across all active rows in the provided map (debugging) */ + if(userowmap) + rownr = firstActiveLink(psdata->rows->varmap); + + /* Now do once for ingoing rownr or loop across rowmap */ + while((status == TRUE) && (rownr != 0)) { + + /* Check the lower bound */ + value = presolve_sumplumin(lp, rownr, psdata->rows, TRUE); + LHS = get_rh_lower(lp, rownr); + if(value < LHS-lp->epssolution) { + contype = get_constr_type(lp, rownr); + report(lp, NORMAL, "presolve_rowfeasible: Lower bound infeasibility in %s row %s (%g << %g)\n", + get_str_constr_type(lp, contype), get_row_name(lp, rownr), value, LHS); + if(rownr != origrownr) + report(lp, NORMAL, " ... Input row base used for testing was %s\n", + get_row_name(lp, origrownr)); + status = FALSE; + } + + /* Check the upper bound */ + value = presolve_sumplumin(lp, rownr, psdata->rows, FALSE); + RHS = get_rh_upper(lp, rownr); + if(value > RHS+lp->epssolution) { + contype = get_constr_type(lp, rownr); + report(lp, NORMAL, "presolve_rowfeasible: Upper bound infeasibility in %s row %s (%g >> %g)\n", + get_str_constr_type(lp, contype), get_row_name(lp, rownr), value, RHS); + status = FALSE; + } + if(userowmap) + rownr = nextActiveLink(psdata->rows->varmap, rownr); + else + rownr = 0; + } + return( status ); +} + +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, n; + int nz = mat->col_end[lp->columns] - 1; + MYBOOL status = FALSE; + + for(colnr = 1; colnr <= lp->columns; colnr++) { + rows = psdata->cols->next[colnr]; + if(!isActiveLink(psdata->cols->varmap, colnr)) { + if(rows != NULL) { + report(lp, SEVERE, "presolve_debugmap: Inactive column %d is non-empty\n", + colnr); + goto Done; + } + else + continue; + } + if(rows == NULL) + report(lp, SEVERE, "presolve_debugmap: Active column %d is empty\n", + colnr); + je = *rows; + rows++; + for(jx = 1; jx <= je; jx++, rows++) { + if((*rows < 0) || (*rows > nz)) { + report(lp, SEVERE, "presolve_debugmap: NZ index %d for column %d out of range (index %d<=%d)\n", + *rows, colnr, jx, je); + goto Done; + } + 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)) { + report(lp, SEVERE, "presolve_debugmap: NZ index %d for column %d to row %d out of range\n", + nx, colnr, jx); + goto Done; + } + } + } + } + status = TRUE; +Done: + if(!status && (caption != NULL)) + report(lp, SEVERE, "...caller was '%s'\n", caption); + return( status ); +} + +STATIC MYBOOL presolve_validate(presolverec *psdata, MYBOOL forceupdate) +{ + int i, ie, j, je, k, rownr, *items; + LPSREAL upbound, lobound, value; + lprec *lp = psdata->lp; + MATrec *mat = lp->matA; + MYBOOL status = mat->row_end_valid && !forceupdate; + + if(status) + return( status ); + else if(!mat->row_end_valid) + status = mat_validate(mat); + else + status = forceupdate; + if(status) { + + /* First update rows... */ + for(i = 1; i <= lp->rows; i++) { + + psdata->rows->plucount[i] = 0; + psdata->rows->negcount[i] = 0; + psdata->rows->pluneg[i] = 0; + + if(!isActiveLink(psdata->rows->varmap, i)) { + FREE(psdata->rows->next[i]); + } + else { + /* Create next column pointers by row */ + k = mat_rowlength(mat, i); + allocINT(lp, &(psdata->rows->next[i]), k+1, AUTOMATIC); + items = psdata->rows->next[i]; + je = mat->row_end[i]; + k = 0; + for(j = mat->row_end[i-1]; j < je; j++) + if(isActiveLink(psdata->cols->varmap, ROW_MAT_COLNR(j))) { + k++; + items[k] = j; + } + items[0] = k; + } + } + + /* ...then update columns */ + for(j = 1; j <= lp->columns; j++) { + + psdata->cols->plucount[j] = 0; + psdata->cols->negcount[j] = 0; + psdata->cols->pluneg[j] = 0; + + if(!isActiveLink(psdata->cols->varmap, j)) { + FREE(psdata->cols->next[j]); + } + else { + upbound = get_upbo(lp, j); + lobound = get_lowbo(lp, j); + if(is_semicont(lp, j) && (upbound > lobound)) { + if(lobound > 0) + lobound = 0; + else if(upbound < 0) + upbound = 0; + } + + /* Create next row pointers by column */ + k = mat_collength(mat, j); + allocINT(lp, &(psdata->cols->next[j]), k+1, AUTOMATIC); + items = psdata->cols->next[j]; + ie = mat->col_end[j]; + k = 0; + for(i = mat->col_end[j-1]; i < ie; i++) { + rownr = COL_MAT_ROWNR(i); + if(isActiveLink(psdata->rows->varmap, rownr)) { + k++; + items[k] = i; + + /* Cumulate counts */ + value = COL_MAT_VALUE(i); + if(my_chsign(is_chsign(lp, rownr), value) > 0) { + psdata->rows->plucount[rownr]++; + psdata->cols->plucount[j]++; + } + else { + psdata->rows->negcount[rownr]++; + psdata->cols->negcount[j]++; + } + if((lobound < 0) && (upbound >= 0)) { + psdata->rows->pluneg[rownr]++; + psdata->cols->pluneg[j]++; + } + } + } + items[0] = k; + } + } +#ifdef Paranoia + presolve_debugmap(psdata, "presolve_validate"); +#endif + } + return( status ); +} + +STATIC MYBOOL presolve_rowtallies(presolverec *psdata, int rownr, int *plu, int *neg, int *pluneg) +{ + LPSREAL value; + lprec *lp = psdata->lp; + MATrec *mat = lp->matA; + int ix, jx, ib = 0; + MYBOOL chsign = is_chsign(lp, rownr); + + /* Initialize */ + *plu = 0; + *neg = 0; + *pluneg = 0; + + /* Loop over still active row members */ + for(ix = presolve_nextcol(psdata, rownr, &ib); ix >= 0; ix = presolve_nextcol(psdata, rownr, &ib)) { + + /* Get matrix column and value */ + jx = ROW_MAT_COLNR(ix); + value = ROW_MAT_VALUE(ix); + + /* Cumulate counts */ + if(my_chsign(chsign, value) > 0) + (*plu)++; + else + (*neg)++; + if((get_lowbo(lp, jx) < 0) && (get_upbo(lp, jx) >= 0)) + (*pluneg)++; + } + return( TRUE ); +} +STATIC MYBOOL presolve_debugrowtallies(presolverec *psdata) +{ + lprec *lp = psdata->lp; + int i, plu, neg, pluneg, nerr = 0; + + for(i = 1; i <= lp->rows; i++) + if(isActiveLink(psdata->rows->varmap, i) && + presolve_rowtallies(psdata, i, &plu, &neg, &pluneg)) { + if((psdata->rows->plucount[i] != plu) || + (psdata->rows->negcount[i] != neg) || + (psdata->rows->pluneg[i] != pluneg)) { + nerr++; + report(lp, SEVERE, "presolve_debugrowtallies: Detected inconsistent count for row %d\n", i); + } + } + return( (MYBOOL) (nerr == 0) ); +} + +STATIC int presolve_debugcheck(lprec *lp, LLrec *rowmap, LLrec *colmap) +{ + int i, j, errc = 0; + + /* Validate constraint bounds */ + for(i = 1; i < lp->rows; i++) { + if((rowmap != NULL) && !isActiveLink(rowmap, i)) + continue; + /* Check if we have a negative range */ + if(lp->orig_upbo[i] < 0) { + errc++; + report(lp, SEVERE, "presolve_debugcheck: Detected negative range %g for row %d\n", + lp->orig_upbo[i], i); + } + } + /* Validate variables */ + for(j = 1; j < lp->columns; j++) { + if((colmap != NULL) && !isActiveLink(colmap, j)) + continue; + i = lp->rows+j; + /* Check if we have infeasible bounds */ + if(lp->orig_lowbo[i] > lp->orig_upbo[i]) { + errc++; + report(lp, SEVERE, "presolve_debugcheck: Detected UB < LB for column %d\n", + j); + } + } + /* Return total number of errors */ + return( errc ); +} + +STATIC MYBOOL presolve_candeletevar(presolverec *psdata, int colnr) +{ + lprec *lp = psdata->lp; + int usecount = SOS_memberships(lp->SOS, colnr); + + return( (MYBOOL) ((lp->SOS == NULL) || (usecount == 0) || + (/*is_presolve(lp, PRESOLVE_SOS) &&*/ + (((lp->SOS->sos1_count == lp->SOS->sos_count)) || + (usecount == SOS_is_member_of_type(lp->SOS, colnr, SOS1))))) ); +} + +STATIC int presolve_rowlengthex(presolverec *psdata, int rownr) +{ + int j1 = psdata->rows->plucount[rownr] + psdata->rows->negcount[rownr]; +#ifdef Paranoia + int j2 = presolve_rowlength(psdata, rownr); + + if(j1 != j2) { + report(psdata->lp, SEVERE, "presolve_rowlengthex: Expected row length %d, but found %d in row %s\n", + j2, j1, get_row_name(psdata->lp, rownr)); + j1 = -j1; + } +#endif + + return( j1 ); +} +STATIC int presolve_rowlengthdebug(presolverec *psdata) +{ + int rownr, n = 0; + + for(rownr = firstActiveLink(psdata->rows->varmap); rownr != 0; + rownr = nextActiveLink(psdata->rows->varmap, rownr)) + n += presolve_rowlengthex(psdata, rownr); + return( n ); +} + +INLINE int presolve_nextrecord(psrec *ps, int recnr, int *previtem) +{ + int *nzlist = ps->next[recnr], nzcount = nzlist[0], status = -1; + + /* Check if we simply wish the last active column */ + if(previtem == NULL) { + if(nzlist != NULL) + status = nzlist[*nzlist]; + return( status ); + } + + /* Step to next */ +#ifdef Paranoia + else if((*previtem < 0) || (*previtem > nzcount)) + return( status ); +#endif + (*previtem)++; + + /* Set the return values */ + if(*previtem > nzcount) + (*previtem) = 0; + else + status = nzlist[*previtem]; + + return( status ); +} +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) ); +} +INLINE int presolve_lastcol(presolverec *psdata, int rownr) +{ + return( presolve_nextrecord(psdata->rows, rownr, NULL) ); +} +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) ); +} +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) +{ + lprec *lp = psdata->lp; + + lp->orig_rhs[rownr] -= fixdelta; + if(epsvalue > 0) +#if 1 + my_roundzero(lp->orig_rhs[rownr], epsvalue); +#else + lp->orig_rhs[rownr] = presolve_roundrhs(lp, lp->orig_rhs[rownr], FALSE); +#endif + lp->presolve_undo->fixed_rhs[rownr] += fixdelta; +} + +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; + + /* Remove empty rows */ + list = psdata->rows->empty; + if(list != NULL) { + n = list[0]; + for(i = 1; i <= n; i++) + if(isActiveLink(psdata->rows->varmap, list[i])) { + presolve_rowremove(psdata, list[i], FALSE); + countR++; + } + if(nConRemove != NULL) + (*nConRemove) += countR; + list[0] = 0; + } + + /* Fix and remove empty columns (unless they are in a SOS) */ + list = psdata->cols->empty; + if(list != NULL) { + n = list[0]; + for(i = 1; i <= n; i++) { + ix = list[i]; + if(isActiveLink(psdata->cols->varmap, ix)) { + if(presolve_colfixdual(psdata, ix, &fixValue, &status)) { + if(!presolve_colfix(psdata, ix, fixValue, TRUE, nVarRemove)) { + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } + presolve_colremove(psdata, ix, FALSE); + countC++; + } + else if(SOS_is_member(SOS, 0, ix)) + report(psdata->lp, DETAILED, "presolve_shrink: Empty column %d is member of a SOS\n", ix); + } + } + list[0] = 0; + } + + return( status ); +} + +STATIC void presolve_rowremove(presolverec *psdata, int rownr, MYBOOL allowcoldelete) +{ + lprec *lp = psdata->lp; + MATrec *mat = lp->matA; + int ix, ie, nx, jx, je, *cols, *rows, n, colnr; + +#ifdef Paranoia + if((rownr < 1) || (rownr > lp->rows)) + report(lp, SEVERE, "presolve_rowremove: Row %d out of range\n", rownr); +#endif + + /* Remove this row for each column that is active in the row */ + cols = psdata->rows->next[rownr]; + ie = *cols; + cols++; + for(ix = 1; ix <= ie; ix++, cols++) { + n = 0; + colnr = ROW_MAT_COLNR(*cols); + rows = psdata->cols->next[colnr]; + je = rows[0]; + /* See if we can narrow the search window */ + jx = je / 2; + if((jx > 5) && (rownr >= COL_MAT_ROWNR(rows[jx]))) + n = jx-1; + else + jx = 1; + /* Do the compression loop */ + for(; jx <= je; jx++) { + nx = rows[jx]; + if(COL_MAT_ROWNR(nx) != rownr) { + n++; + rows[n] = nx; + } + } + rows[0] = n; + + /* Make sure we delete columns that have become empty */ +#if 1 + if((n == 0) && allowcoldelete) { + int *list = psdata->cols->empty; + n = ++list[0]; + list[n] = colnr; + } +#endif + + } + FREE(psdata->rows->next[rownr]); + + removeLink(psdata->rows->varmap, rownr); + switch(get_constr_type(lp, rownr)) { + case LE: removeLink(psdata->LTmap, rownr); + break; + case EQ: removeLink(psdata->EQmap, rownr); + break; + } + if(isActiveLink(psdata->INTmap, rownr)) + removeLink(psdata->INTmap, rownr); +} + +STATIC int presolve_colremove(presolverec *psdata, int colnr, MYBOOL allowrowdelete) +{ + lprec *lp = psdata->lp; + +#ifdef Paranoia + if((colnr < 1) || (colnr > lp->columns)) + report(lp, SEVERE, "presolve_colremove: Column %d out of range\n", colnr); + if(!isActiveLink(psdata->cols->varmap, colnr) || !presolve_candeletevar(psdata, colnr)) + colnr = -1; + else +#endif + { + MATrec *mat = lp->matA; + int ix, ie, nx, jx, je, *cols, *rows, n, rownr; + + /* Remove this column for each row that is active in the column */ + rows = psdata->cols->next[colnr]; + je = *rows; + rows++; + for(jx = 1; jx <= je; jx++, rows++) { + n = 0; + rownr = COL_MAT_ROWNR(*rows); + cols = psdata->rows->next[rownr]; + ie = cols[0]; + /* See if we can narrow the search window */ + ix = ie / 2; + if((ix > 5) && (colnr >= ROW_MAT_COLNR(cols[ix]))) + n = ix-1; + else + ix = 1; + /* Do the compression loop */ + for(; ix <= ie; ix++) { + nx = cols[ix]; + if(ROW_MAT_COLNR(nx) != colnr) { + n++; + cols[n] = nx; + } + } + cols[0] = n; + + /* Make sure we delete rows that become empty */ +#if 1 + if((n == 0) && allowrowdelete) { + int *list = psdata->rows->empty; + n = ++list[0]; + list[n] = rownr; + } +#endif + + } + FREE(psdata->cols->next[colnr]); + + /* Update other counts */ + if(SOS_is_member(lp->SOS, 0, colnr)) { + if(lp->sos_priority != NULL) { + lp->sos_vars--; + if(is_int(lp, colnr)) + lp->sos_ints--; + } + SOS_member_delete(lp->SOS, 0, colnr); + clean_SOSgroup(lp->SOS, TRUE); + if(SOS_count(lp) == 0) + free_SOSgroup(&(lp->SOS)); + } + + /* Finally remove the column from the active column list */ + colnr = removeLink(psdata->cols->varmap, colnr); + } + return( colnr ); +} + +STATIC int presolve_redundantSOS(presolverec *psdata, int *nb, int *nSum) +{ + lprec *lp = psdata->lp; + int i, ii, k, kk, j, nrows = lp->rows, *fixed = NULL, + iBoundTighten = 0, status = RUNNING; + SOSrec *SOS; + + /* Is there anything to do? */ + i = ii = SOS_count(lp); + if(ii == 0) + return( status ); + + /* Allocate working member list */ + if(!allocINT(lp, &fixed, lp->columns+1, FALSE) ) + return( lp->spx_status ); + + /* Check if we have SOS'es that are already satisfied or fixable/satisfiable */ + while(i > 0) { + SOS = lp->SOS->sos_list[i-1]; + kk = SOS->members[0]; + fixed[0] = 0; + for(k = 1; k <= kk; k++) { + j = SOS->members[k]; + if((get_lowbo(lp, j) > 0) && !is_semicont(lp, j)) { + fixed[++fixed[0]] = k; + /* Abort if we have identified SOS infeasibility */ + if(fixed[0] > SOS->type) { + status = presolve_setstatus(psdata, INFEASIBLE); + goto Done; + } + } + } + /* If there were an exact number of non-zero SOS members, check their sequentiality */ + if(fixed[0] == SOS->type) { + /* Check sequentiality of members with non-zero lower bounds */ + for(k = 2; k <= fixed[0]; k++) { + if(fixed[k] != fixed[k-1]+1) { + status = presolve_setstatus(psdata, INFEASIBLE); + goto Done; + } + } + /* Fix other member variables to zero, if necessary */ + for(k = kk; k > 0; k--) { + j = SOS->members[k]; + if((get_lowbo(lp, j) > 0) && !is_semicont(lp, j)) + continue; + if(!presolve_colfix(psdata, j, 0.0, AUTOMATIC, &iBoundTighten)) { + status = presolve_setstatus(psdata, INFEASIBLE); + goto Done; + } + } + /* Remove the SOS */ + delete_SOSrec(lp->SOS, i /* , FALSE */); + } + /* Otherwise, try to fix variables outside the SOS type window */ + else if(fixed[0] > 0) { + for(k = kk; k > 0; k--) { + if((k > fixed[fixed[0]]-SOS->type) && /* After leading entries */ + (k < fixed[1]+SOS->type)) /* Before trailing entries */ + 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)) { + status = presolve_setstatus(psdata, INFEASIBLE); + goto Done; + } + } + } + i--; + } + + /* Update the sparse member map if there were SOS deletions; + Remember that delete_SOSrec() above specified deferred updating! */ + i = SOS_count(lp); + if((i < ii) || (iBoundTighten > 0)) { + SOS_member_updatemap(lp->SOS); + } + + /* Update tag orders */ + for(; i > 0; i--) + lp->SOS->sos_list[i-1]->tagorder = i; + +Done: + FREE(fixed); + (*nb) += iBoundTighten; + (*nSum) += iBoundTighten; + + return( status ); +} + +STATIC MYBOOL presolve_fixSOS1(presolverec *psdata, int colnr, LPSREAL fixvalue, int *nr, int *nv) +{ + lprec *lp = psdata->lp; + int i, k, j; + SOSrec *SOS; + LPSREAL newvalue; + MYBOOL *fixed = NULL, status = FALSE; + + /* Allocate working member list */ + if(!allocMYBOOL(lp, &fixed, lp->columns+1, TRUE) ) + return(FALSE); + + /* Fix variables in SOS's where colnr is a member */ + i = SOS_count(lp); + while(i > 0) { + /* Set next SOS target (note that colnr has been tested earlier as not being a member of a higher order SOS) */ + SOS = lp->SOS->sos_list[i-1]; + if(SOS_is_member(lp->SOS, i, colnr)) { + for(k = SOS->members[0]; k > 0; k--) { + j = SOS->members[k]; + if(fixed[j]) + continue; + if(j == colnr) { + fixed[j] = TRUE; + newvalue = fixvalue; + } + else { + fixed[j] = AUTOMATIC; + newvalue = 0.0; + } + /* If it is a member of a higher order SOS then just change bounds */ + if(!presolve_candeletevar(psdata, j)) { + set_bounds(lp, j, newvalue, newvalue); + fixed[j] = TRUE | AUTOMATIC; + psdata->forceupdate = TRUE; + } + /* Otherwise fix it in preparation for removal */ + else if(!presolve_colfix(psdata, j, newvalue, TRUE, nv)) + goto Done; + } + } + i--; + } + + /* Delete SOS'es or SOS member variables where we can */ + k = i = SOS_count(lp); + while(i > 0) { + /* Set next SOS target */ + SOS = lp->SOS->sos_list[i-1]; + if(SOS_is_member(lp->SOS, i, colnr)) { + /* Always delete SOS1's */ + if(SOS->type == SOS1) + delete_SOSrec(lp->SOS, i /* , FALSE */); + /* Only delete leading or trailing SOS members in higher-order SOS'es that are fixed at 0; + (note that this section of the code will never be called in the current setup) */ + else { + /* First the leading entries... */ + for(j = 1; j <= SOS->members[0]; j++) { + if(fixed[SOS->members[j]] == AUTOMATIC) + SOS_member_delete(lp->SOS, i, SOS->members[j]); + } + /* ...then trailing entries */ + for(j = SOS->members[0]; j > 0; j--) { + if(fixed[SOS->members[j]] == AUTOMATIC) + SOS_member_delete(lp->SOS, i, SOS->members[j]); + } + } + } + i--; + } + + /* Update the sparse member map if there were SOS deletions; delete_SOSrec() above + specified deferred updating */ + i = SOS_count(lp); + if(i < k) + SOS_member_updatemap(lp->SOS); + + /* Delete the variables that have been fixed */ + k = 0; + for(j = lp->columns; j > 0; j--) { + if((fixed[j] == TRUE) || (fixed[j] == AUTOMATIC)) { + presolve_colremove(psdata, j, TRUE); + k++; + } + } + + /* Update tag orders */ + i = SOS_count(lp); + for(; i > 0; i--) + lp->SOS->sos_list[i-1]->tagorder = i; + + status = TRUE; + +Done: + FREE(fixed); + return( status ); +} + +STATIC void presolve_setEQ(presolverec *psdata, int rownr) +{ + lprec *lp = psdata->lp; + + if(is_constr_type(lp, rownr, LE)) + removeLink(psdata->LTmap, rownr); + setLink(psdata->EQmap, rownr); + set_constr_type(lp, rownr, EQ); + psdata->dv_lobo[rownr] = -lp->infinite; + psdata->dv_upbo[rownr] = lp->infinite; +} + +STATIC MYBOOL presolve_singletonbounds(presolverec *psdata, int rownr, int colnr, LPSREAL *lobound, LPSREAL *upbound, LPSREAL *aval) +{ + lprec *lp = psdata->lp; + LPSREAL coeff_a, epsvalue = psdata->epsvalue; + MYBOOL isneg; + + /* Compute row singleton variable range */ + if(is_constr_type(lp, rownr, EQ) && (fabs(*lobound) < epsvalue)) + *lobound = *upbound = 0; + else { + if(aval == NULL) + coeff_a = get_mat(lp, rownr, colnr); + else + coeff_a = *aval; + isneg = (MYBOOL) (coeff_a < 0); + if(*lobound > -lp->infinite) + *lobound /= coeff_a; + else if(isneg) + *lobound = -(*lobound); + if(*upbound < lp->infinite) + *upbound /= coeff_a; + else if(isneg) + *upbound = -(*upbound); + if(isneg) + swapREAL(lobound, upbound); + } + + /* Check against bound - handle SC variables specially */ + if(is_semicont(lp, colnr)) { + coeff_a = get_lowbo(lp, colnr); + if(coeff_a > 0) { + SETMAX(*lobound, 0.0); + SETMIN(*upbound, get_upbo(lp, colnr)); + } + else { + coeff_a = get_upbo(lp, colnr); + if(coeff_a > 0) { + SETMAX(*lobound, get_lowbo(lp, colnr)); + SETMIN(*upbound, 0.0); + } + } + } + else { + SETMAX(*lobound, get_lowbo(lp, colnr)); + SETMIN(*upbound, get_upbo(lp, colnr)); + } + + /* Return with consistency status */ +#ifdef DoPresolveRelativeTest + isneg = (MYBOOL) (my_reldiff(*upbound, *lobound) >= - epsvalue); +#else + isneg = (MYBOOL) (*upbound >= *lobound - epsvalue); +#endif + if(!isneg) { + /* Attempt bound-related error correction */ + if(fabs(my_reldiff(*lobound, get_upbo(lp, colnr))) < PRESOLVE_BOUNDSLACK*epsvalue) + *lobound = get_upbo(lp, colnr); + else if(fabs(my_reldiff(*upbound, get_lowbo(lp, colnr))) < PRESOLVE_BOUNDSLACK*epsvalue) + *upbound = get_lowbo(lp, colnr); +#ifdef DoPresolveRelativeTest + isneg = (MYBOOL) (my_reldiff(*upbound, *lobound) >= - epsvalue); +#else + isneg = (MYBOOL) (*upbound >= *lobound - epsvalue); +#endif + if(!isneg) + report(lp, NORMAL, "presolve_singletonbounds: Singleton variable %s in row %s infeasibility (%g << %g)\n", + get_col_name(lp, colnr), get_row_name(lp, rownr), *lobound, *upbound); + } + return( isneg ); +} + +STATIC MYBOOL presolve_altsingletonvalid(presolverec *psdata, int rownr, int colnr, LPSREAL reflotest, LPSREAL refuptest) +{ + lprec *lp = psdata->lp; + LPSREAL coeff_bl, coeff_bu, epsvalue = psdata->epsvalue; + + coeff_bl = get_rh_lower(lp, rownr); + coeff_bu = get_rh_upper(lp, rownr); + + /* Check base data validity */ +#ifdef DoPresolveRelativeTest + if((my_reldiff(refuptest, reflotest) < -epsvalue) || +#else + if((reflotest > refuptest + epsvalue) || +#endif + !presolve_singletonbounds(psdata, rownr, colnr, &coeff_bl, &coeff_bu, NULL)) + return( FALSE ); + + /* Base data is Ok, now check against against each other */ + epsvalue = MAX(reflotest-coeff_bu, coeff_bl-refuptest) / epsvalue; + if(epsvalue > PRESOLVE_BOUNDSLACK) { + report(lp, NORMAL, "presolve_altsingletonvalid: Singleton variable %s in row %s infeasible (%g)\n", + get_col_name(lp, colnr), get_row_name(lp, rownr), MAX(reflotest-coeff_bu, coeff_bl-refuptest)); + return( FALSE ); + } + else + return( TRUE ); +} + +STATIC MYBOOL presolve_multibounds(presolverec *psdata, int rownr, int colnr, + LPSREAL *lobound, LPSREAL *upbound, LPSREAL *aval, MYBOOL *rowbinds) +{ + lprec *lp = psdata->lp; + MYBOOL rowbindsvar = FALSE, status = FALSE; + LPSREAL coeff_a, LHS, RHS, netX, Xupper, Xlower, epsvalue = psdata->epsvalue; + + /* Get variable bounds for netting */ + LHS = *lobound; + RHS = *upbound; + Xlower = get_lowbo(lp, colnr); + Xupper = get_upbo(lp, colnr); + + /* Identify opportunity for bound tightening */ + if(aval == NULL) + coeff_a = get_mat(lp, rownr, colnr); + else + coeff_a = *aval; + + netX = presolve_sumplumin(lp, rownr, psdata->rows, TRUE); + if(!my_infinite(lp, LHS) && !my_infinite(lp, netX)) { + if(coeff_a > 0) { + LHS -= netX-coeff_a*Xupper; + LHS /= coeff_a; + if(LHS > Xlower + epsvalue) { + Xlower = presolve_roundrhs(lp, LHS, TRUE); + status = TRUE; + } + else if(LHS > Xlower - epsvalue) + rowbindsvar = TRUE; + } + else { + LHS -= netX-coeff_a*Xlower; + LHS /= coeff_a; + if(LHS < Xupper - epsvalue) { + Xupper = presolve_roundrhs(lp, LHS, FALSE); + status = AUTOMATIC; + } + else if(LHS < Xupper + epsvalue) + rowbindsvar = AUTOMATIC; + } + } + + netX = presolve_sumplumin(lp, rownr, psdata->rows, FALSE); + if(!my_infinite(lp, RHS) && !my_infinite(lp, netX)) { + if(coeff_a < 0) { + if(!my_infinite(lp, Xupper)) { + RHS -= netX-coeff_a*Xupper; + RHS /= coeff_a; + if(RHS > Xlower + epsvalue) { + Xlower = presolve_roundrhs(lp, RHS, TRUE); + status |= TRUE; + } + else if(RHS > Xlower - epsvalue) + rowbindsvar |= TRUE; + } + } + else if(!my_infinite(lp, Xlower)) { + RHS -= netX-coeff_a*Xlower; + RHS /= coeff_a; + if(RHS < Xupper - epsvalue) { + Xupper = presolve_roundrhs(lp, RHS, FALSE); + status |= AUTOMATIC; + } + else if(RHS < Xupper + epsvalue) + rowbindsvar |= AUTOMATIC; + } + } + + *lobound = Xlower; + *upbound = Xupper; + if(rowbinds != NULL) + *rowbinds = rowbindsvar; + + return(status); +} + +STATIC MYBOOL isnz_origobj(lprec *lp, int colnr) +{ + return( (MYBOOL) (lp->orig_obj[colnr] != 0) ); +} + +STATIC MYBOOL presolve_testrow(presolverec *psdata, int lastrow) +{ + if(psdata->forceupdate) { + presolve_updatesums(psdata); + psdata->forceupdate = FALSE; + } + if(!presolve_rowfeasible(psdata, 0, TRUE)) + return( FALSE ); + else + return( TRUE ); +} + +STATIC MYBOOL presolve_coltighten(presolverec *psdata, int colnr, LPSREAL LOnew, LPSREAL UPnew, int *count) +{ + lprec *lp = psdata->lp; + int elmnr, elmend, k, oldcount = 0, newcount = 0, deltainf; + LPSREAL LOold, UPold, Value, margin = psdata->epsvalue; + MATrec *mat = lp->matA; + LPSREAL *value; + int *rownr; + + /* Attempt correction of marginally equal, but inconsistent input values */ + Value = UPnew - LOnew; + if((Value <= -margin) && (Value > -lp->epsprimal)) { + if(fabs(fmod(UPnew, 1.0)) < margin) + LOnew = UPnew; + else + UPnew = LOnew; + } + + /* Check if there is anything to do */ + LOold = get_lowbo(lp, colnr); + UPold = get_upbo(lp, colnr); +#ifdef Paranoia + if(((LOold > LOnew) && !is_semicont(lp, colnr)) || (UPold < UPnew)) { + report(lp, SEVERE, "presolve_coltighten: Inconsistent new bounds requested for column %d\n", colnr); + return( FALSE ); + } +#endif + if(count != NULL) + newcount = *count; + oldcount = newcount; + + /* Modify inf-count */ + deltainf = 0; + if((UPold < lp->infinite) || (LOold > -lp->infinite)) + deltainf -= 1; + if((UPnew < lp->infinite) || (LOnew > -lp->infinite)) + deltainf += 1; + if(isnz_origobj(lp, colnr)) + psdata->rows->infcount[0] += deltainf; + elmnr = mat->col_end[colnr-1]; + elmend = mat->col_end[colnr]; + rownr = &COL_MAT_ROWNR(elmnr); + for(; elmnr < elmend; elmnr++, rownr += matRowColStep) { + k = *rownr; + if(isActiveLink(psdata->rows->varmap, k)) + psdata->rows->infcount[k] += deltainf; + } + + /* Look for opportunity to tighten upper variable bound */ + if((UPnew < lp->infinite) && (UPnew+margin < UPold)) { + if(is_int(lp, colnr)) + UPnew = floor(UPnew+margin); + if(UPold < lp->infinite) { + /* First do OF */ + k = 0; + Value = my_chsign(is_chsign(lp, k), lp->orig_obj[colnr]); + if((Value > 0) && (psdata->rows->pluupper[k] < lp->infinite)) + psdata->rows->pluupper[k] += (UPnew-UPold)*Value; + else if((Value < 0) && (psdata->rows->negupper[k] < lp->infinite)) + psdata->rows->negupper[k] += (LOnew-LOold)*Value; + psdata->rows->infcount[k] += deltainf; + + /* Then scan the constraint rows */ + elmnr = mat->col_end[colnr-1]; + elmend = mat->col_end[colnr]; + rownr = &COL_MAT_ROWNR(elmnr); + value = &COL_MAT_VALUE(elmnr); + for(; elmnr < elmend; + elmnr++, rownr += matRowColStep, value += matValueStep) { + k = *rownr; + if(!isActiveLink(psdata->rows->varmap, k)) + continue; + Value = my_chsign(is_chsign(lp, k), *value); + if((Value > 0) && (psdata->rows->pluupper[k] < lp->infinite)) + psdata->rows->pluupper[k] += (UPnew-UPold)*Value; + else if((Value < 0) && (psdata->rows->negupper[k] < lp->infinite)) + psdata->rows->negupper[k] += (LOnew-LOold)*Value; + } + } + else + psdata->forceupdate = TRUE; + if(UPnew < UPold) { + UPold = UPnew; + newcount++; + } + } + + /* Look for opportunity to tighten lower variable bound */ + if((LOnew > -lp->infinite) && (LOnew-margin > LOold)) { + if(is_int(lp, colnr)) + LOnew = ceil(LOnew-margin); + if(LOold > -lp->infinite) { + /* First do OF */ + k = 0; + Value = my_chsign(is_chsign(lp, k), lp->orig_obj[colnr]); + if((Value > 0) && (psdata->rows->plulower[k] > -lp->infinite)) + psdata->rows->plulower[k] += (LOnew-LOold)*Value; + else if((Value < 0) && (psdata->rows->neglower[k] > -lp->infinite)) + psdata->rows->neglower[k] += (UPnew-UPold)*Value; + + /* Then scan the constraint rows */ + elmnr = mat->col_end[colnr-1]; + elmend = mat->col_end[colnr]; + rownr = &COL_MAT_ROWNR(elmnr); + value = &COL_MAT_VALUE(elmnr); + for(; elmnr < elmend; + elmnr++, rownr += matRowColStep, value += matValueStep) { + k = *rownr; + if(!isActiveLink(psdata->rows->varmap, k)) + continue; + Value = my_chsign(is_chsign(lp, k), *value); + if((Value > 0) && (psdata->rows->plulower[k] > -lp->infinite)) + psdata->rows->plulower[k] += (LOnew-LOold)*Value; + else if((Value < 0) && (psdata->rows->neglower[k] > -lp->infinite)) + psdata->rows->neglower[k] += (UPnew-UPold)*Value; + } + } + else + psdata->forceupdate = TRUE; + if(LOnew > LOold) { + LOold = LOnew; + newcount++; + } + } + + /* Now set the new variable bounds, if they are tighter */ + if(newcount > oldcount) { + UPnew = presolve_roundval(lp, UPnew); + LOnew = presolve_roundval(lp, LOnew); + if(LOnew > UPnew) { + if(LOnew-UPnew < margin) { + LOnew = UPnew; + } + else { + report(lp, NORMAL, "presolve_coltighten: Found column %s with LB %g > UB %g\n", + get_col_name(lp, colnr), LOnew, UPnew); + return( FALSE ); + } + } + if(lp->spx_trace || (lp->verbose > DETAILED)) + report(lp, NORMAL, "presolve_coltighten: Replaced bounds on column %s to [%g ... %g]\n", + get_col_name(lp, colnr), LOnew, UPnew); + set_bounds(lp, colnr, LOnew, UPnew); + } + if(count != NULL) + *count = newcount; + + return( TRUE ); +} + +STATIC int presolve_rowtighten(presolverec *psdata, int rownr, int *tally, MYBOOL intsonly) +{ + 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), + VARlo, VARup, Aval; + MATrec *mat = lp->matA; + + jx = presolve_rowlength(psdata, rownr); + allocREAL(lp, &newbound, 2*jx, TRUE); + allocINT (lp, &idxbound, 2*jx, TRUE); + + /* Identify bound tightening for each active variable in the constraint */ + for(jx = presolve_nextcol(psdata, rownr, &item); jx >= 0; + jx = presolve_nextcol(psdata, rownr, &item)) { + jjx = ROW_MAT_COLNR(jx); + Aval = ROW_MAT_VALUE(jx); + Aval = my_chsign(rownr, Aval); + + VARlo = RHlo; + VARup = RHup; + presolve_multibounds(psdata, rownr,jjx, &VARlo, &VARup, &Aval, &rowbinds); + if(rowbinds & TRUE) { + idxbound[idxn] = -jjx; + newbound[idxn] = VARlo; + idxn++; + } + if(rowbinds & AUTOMATIC) { + idxbound[idxn] = jjx; + newbound[idxn] = VARup; + idxn++; + } + } + + /* Loop over the bounds identified for tightening and perform update */ + ix = 0; + while(ix < idxn) { + jjx = idxbound[ix]; + jx = abs(jjx); + + /* Skip free variables and non-ints, if specified */ + if(is_unbounded(lp, jx) || + (intsonly && !is_int(lp, jx))) + continue; + + VARlo = get_lowbo(lp, jx); + VARup = get_upbo(lp, jx); + /* while((ix < idxn) && (jx == abs(jjx))) { */ + while((ix < idxn) && (jx == abs((jjx = idxbound[ix])))) { + if(jjx < 0) + VARlo = newbound[ix]; + else + VARup = newbound[ix]; + ix++; + } + if(!presolve_coltighten(psdata, jx, VARlo, VARup, tally)) { + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } + } + + FREE(newbound); + FREE(idxbound); + + return(status); +} + +STATIC void set_dv_bounds(presolverec *psdata, int rownr, LPSREAL lowbo, LPSREAL upbo) +{ + psdata->dv_lobo[rownr] = lowbo; + psdata->dv_upbo[rownr] = upbo; +} +STATIC LPSREAL get_dv_lower(presolverec *psdata, int rownr) +{ + return( psdata->dv_lobo[rownr] ); +} + +STATIC LPSREAL 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) +{ + lprec *lp = psdata->lp; + int i, ix, ie; + MYBOOL isneg, lofinite, upfinite, doupdate = FALSE, chsign = is_chsign(lp, rownr); + LPSREAL lobound, upbound, lovalue, upvalue, + Value, fixvalue, fixprod, mult; + MATrec *mat = lp->matA; + psrec *ps = psdata->cols; + + /* Set "fixed" value in case we are deleting a variable */ + upbound = get_dv_upper(psdata, rownr); + lobound = get_dv_lower(psdata, rownr); + if(remove) { + if(upbound-lobound < psdata->epsvalue) { + if((newvalue > lobound) && (newvalue < upbound)) + fixvalue = newvalue; + else + fixvalue = lobound; + } + else { + if(my_infinite(lp, newvalue) && (get_rh(lp, rownr) == 0)) + fixvalue = ((lobound <= 0) && (upbound >= 0) ? 0 : MIN(upbound, lobound)); + else + fixvalue = newvalue; + } + set_dv_bounds(psdata, rownr, fixvalue, fixvalue); + if(fixvalue != 0) + addUndoPresolve(lp, FALSE, rownr, fixvalue, 0, 0); + mult = -1; + } + else { + mult = 1; + fixvalue = 0; + } + + /* Loop over rows to update statistics */ + ix = mat->row_end[rownr - 1]; + ie = mat->row_end[rownr]; + for(; ix < ie; ix++) { + + /* Retrieve row data and adjust RHS if we are deleting a variable */ + i = ROW_MAT_COLNR(ix); + Value = ROW_MAT_VALUE(ix); + if(Value == 0) + continue; + + if(remove && (fixvalue != 0)) { + fixprod = Value*fixvalue; + lp->orig_obj[i] -= fixprod; + my_roundzero(lp->orig_obj[i], psdata->epsvalue); + lp->presolve_undo->fixed_obj[i] += fixprod; + } + + /* Prepare for further processing */ + Value = my_chsign(chsign, Value); + isneg = (MYBOOL) (Value < 0); + + /* Reduce row variable counts if we are removing the variable */ + if(!isActiveLink(ps->varmap, i)) + continue; + if(remove) { + if(isneg) { + ps->negcount[i]--; + } + else { + ps->plucount[i]--; + } + if((lobound < 0) && (upbound >= 0)) { + ps->pluneg[i]--; + } + } + + /* Compute associated constraint contribution values */ + upfinite = (MYBOOL) (upbound < lp->infinite); + lofinite = (MYBOOL) (lobound > -lp->infinite); + if(upfinite || lofinite) { + if(remove) + ps->infcount[i]--; + else + ps->infcount[i]++; + } + upvalue = my_if(upfinite, Value*upbound, my_chsign(isneg, lp->infinite)); + lovalue = my_if(lofinite, Value*lobound, my_chsign(isneg, -lp->infinite)); + + /* Cumulate effective upper column bound (only bother with non-finite bound) */ + if(isneg) { + if((ps->negupper[i] < lp->infinite) && lofinite) { + ps->negupper[i] += mult*lovalue; + ps->negupper[i] = presolve_roundrhs(lp, ps->negupper[i], FALSE); + } + else if(remove && !lofinite) + doupdate = TRUE; + else + ps->negupper[i] = lp->infinite; + } + else { + if((ps->pluupper[i] < lp->infinite) && upfinite) { + ps->pluupper[i] += mult*upvalue; + ps->pluupper[i] = presolve_roundrhs(lp, ps->pluupper[i], FALSE); + } + else if(remove && !upfinite) + doupdate = TRUE; + else + ps->pluupper[i] = lp->infinite; + } + + /* Cumulate effective lower column bound (only bother with non-finite bound) */ + if(isneg) { + if((ps->neglower[i] > -lp->infinite) && upfinite) { + ps->neglower[i] += mult*upvalue; + ps->neglower[i] = presolve_roundrhs(lp, ps->neglower[i], TRUE); + } + else if(remove && !upfinite) + doupdate = TRUE; + else + ps->neglower[i] = -lp->infinite; + } + else { + if((ps->plulower[i] > -lp->infinite) && lofinite) { + ps->plulower[i] += mult*lovalue; + ps->plulower[i] = presolve_roundrhs(lp, ps->plulower[i], TRUE); + } + else if(remove && !lofinite) + doupdate = TRUE; + else + ps->plulower[i] = -lp->infinite; + } + + /* Validate consistency of eliminated singleton */ + if(remove && ((i == 0) || (ps->next[i][0] == 1)) && !psdata->forceupdate) { + presolve_range(lp, i, ps, &lovalue, &upvalue); + Value = get_mat(lp, 0, i); + if((upvalue < Value) || + (lovalue > Value)) { + report(lp, IMPORTANT, "presolve: Row %s (%g << %g) infeasibility in column %s (OF=%g)\n", + get_row_name(lp, rownr), lovalue, upvalue, get_col_name(lp, i), Value); + return( FALSE ); + } + } + } + if(remove) { + psdata->forceupdate |= doupdate; + if(tally != NULL) + (*tally)++; + } + return( TRUE ); +} + + +STATIC int presolve_colsingleton(presolverec *psdata, int i, int j, int *count) +{ + lprec *lp = psdata->lp; + LPSREAL RHlow, RHup, LObound, UPbound, Value; + +#ifdef Paranoia + if(!isActiveLink(psdata->cols->varmap, j)) + report(lp, SEVERE, "presolve_colsingleton: Nothing to do, column %d was eliminated earlier\n", + j); +#endif + + Value = get_mat(lp,i,j); + if(Value == 0) + return( RUNNING ); + + /* Initialize and identify semicontinuous variable */ + LObound = get_lowbo(lp, j); + UPbound = get_upbo(lp, j); + if(is_semicont(lp, j) && (UPbound > LObound)) { + if(LObound > 0) + LObound = 0; + else if(UPbound < 0) + UPbound = 0; + } + + /* Get singleton variable bounds */ + RHlow = get_rh_lower(lp, i); + RHup = get_rh_upper(lp, i); + if(!presolve_singletonbounds(psdata, i,j, &RHlow, &RHup, &Value)) + return( presolve_setstatus(psdata, INFEASIBLE) ); + + if(presolve_coltighten(psdata, j, RHlow, RHup, count)) + return( RUNNING ); + else + return( presolve_setstatus(psdata, INFEASIBLE) ); +} + +STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, LPSREAL 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, + Value, fixvalue, mult; + MATrec *mat = lp->matA; + psrec *ps = psdata->rows; + LPSREAL *value; + int *rownr; + + /* Set "fixed" value in case we are deleting a variable */ + upbound = get_upbo(lp, colnr); + lobound = get_lowbo(lp, colnr); + if(remove) { + if(upbound-lobound < psdata->epsvalue) { + if((newvalue > lobound) && (newvalue < upbound)) + fixvalue = newvalue; + else + fixvalue = lobound; + } + else { + if(my_infinite(lp, newvalue) && (get_mat(lp, 0, colnr) == 0)) + fixvalue = ((lobound <= 0) && (upbound >= 0) ? 0 : MIN(upbound, lobound)); + else + fixvalue = newvalue; + } +#if 1 /* Fast normal version */ + set_bounds(lp, colnr, fixvalue, fixvalue); +#else /* Slower version that can be used for debugging/control purposes */ + presolve_coltighten(psdata, colnr, fixvalue, fixvalue, NULL); + lobound = fixvalue; + upbound = fixvalue; +#endif + if(fixvalue != 0) + addUndoPresolve(lp, TRUE, colnr, fixvalue, 0, 0); + mult = -1; + } + else { + mult = 1; + fixvalue = 0; + } + + /* Adjust semi-continuous variable bounds to zero-base */ + if(is_semicont(lp, colnr) && (upbound > lobound)) { + if(lobound > 0) + lobound = 0; + else if(upbound < 0) + upbound = 0; + } + + /* Loop over rows to update statistics */ + ix = mat->col_end[colnr - 1]; + ie = mat->col_end[colnr]; + rownr = &COL_MAT_ROWNR(ix); + value = &COL_MAT_VALUE(ix); + for(; doOF || (ix < ie); + ix++, rownr += matRowColStep, value += matValueStep) { + + /* Retrieve row data and adjust RHS if we are deleting a variable */ +Restart: + if(doOF) { + i = 0; + Value = lp->orig_obj[colnr]; + } + else { + i = *rownr; + Value = *value; + if(!isActiveLink(ps->varmap, i)) + continue; + } + if(Value == 0) + goto BlockEnd; + + if(remove && (fixvalue != 0)) + presolve_adjustrhs(psdata, i, Value*fixvalue, psdata->epsvalue); + + /* Prepare for further processing */ + Value = my_chsign(is_chsign(lp, i), Value); + isneg = (MYBOOL) (Value < 0); + + /* Reduce row variable counts if we are removing the variable */ + if(remove == TRUE) { + if(isneg) { + ps->negcount[i]--; + } + else { + ps->plucount[i]--; + } + if((lobound < 0) && (upbound >= 0)) { + ps->pluneg[i]--; + } + } + + /* Compute associated constraint contribution values */ + upfinite = (MYBOOL) (upbound < lp->infinite); + lofinite = (MYBOOL) (lobound > -lp->infinite); + if(upfinite || lofinite) { + if(remove) + ps->infcount[i]--; + else + ps->infcount[i]++; + } + upvalue = my_if(upfinite, Value*upbound, my_chsign(isneg, lp->infinite)); + lovalue = my_if(lofinite, Value*lobound, my_chsign(isneg, -lp->infinite)); + + /* Cumulate effective upper row bound (only bother with non-finite bound) */ + if(isneg) { + if((ps->negupper[i] < lp->infinite) && lofinite) { + ps->negupper[i] += mult*lovalue; + ps->negupper[i] = presolve_roundrhs(lp, ps->negupper[i], FALSE); + } + else if(remove && !lofinite) + doupdate = TRUE; + else + ps->negupper[i] = lp->infinite; + } + else { + if((ps->pluupper[i] < lp->infinite) && upfinite) { + ps->pluupper[i] += mult*upvalue; + ps->pluupper[i] = presolve_roundrhs(lp, ps->pluupper[i], FALSE); + } + else if(remove && !upfinite) + doupdate = TRUE; + else + ps->pluupper[i] = lp->infinite; + } + + /* Cumulate effective lower row bound (only bother with non-finite bound) */ + if(isneg) { + if((ps->neglower[i] > -lp->infinite) && upfinite) { + ps->neglower[i] += mult*upvalue; + ps->neglower[i] = presolve_roundrhs(lp, ps->neglower[i], TRUE); + } + else if(remove && !upfinite) + doupdate = TRUE; + else + ps->neglower[i] = -lp->infinite; + } + else { + if((ps->plulower[i] > -lp->infinite) && lofinite) { + ps->plulower[i] += mult*lovalue; + ps->plulower[i] = presolve_roundrhs(lp, ps->plulower[i], TRUE); + } + else if(remove && !lofinite) + doupdate = TRUE; + else + ps->plulower[i] = -lp->infinite; + } + + /* Validate consistency of eliminated singleton */ + if(remove && ((i == 0) || (ps->next[i][0] == 1)) && !psdata->forceupdate) { + if(i == 0) { + lovalue = get_rh_lower(lp, i); + upvalue = get_rh_upper(lp, i); + report(lp, DETAILED, "presolve_colfix: Objective determined by presolve as %18g\n", + (is_maxim(lp) ? upvalue : lovalue)); + } + else { + presolve_range(lp, i, ps, &lovalue, &upvalue); +#if 1 + Value = 0; +#else + Value = MAX(fabs(upvalue), fabs(lovalue)); + Value = psdata->epsvalue * MAX(1, Value); +#endif + if((upvalue < get_rh_lower(lp, i)-Value) || + (lovalue > get_rh_upper(lp, i)+Value)) { + report(lp, NORMAL, "presolve_colfix: Variable %s (%g << %g) infeasibility in row %s (%g << %g)\n", + get_col_name(lp, colnr), lovalue, upvalue, + get_row_name(lp, i), get_rh_lower(lp,i), get_rh_upper(lp, i)); + return( FALSE ); + } + } + } +BlockEnd: + if(doOF) { + doOF = FALSE; + if(ix < ie) + goto Restart; + } + + } + if(remove) { + psdata->forceupdate |= doupdate; + if(tally != NULL) + (*tally)++; + } + return( TRUE ); +} + +/* Delete the columns of the specified row, but make sure we don't delete SOS variables. + Note that we cannot use presolve_nextcol() here, since the variables are deleted. */ +STATIC int presolve_rowfixzero(presolverec *psdata, int rownr, int *nv) +{ + lprec *lp = psdata->lp; + MATrec *mat = lp->matA; + int ix, jx, ib = mat->row_end[rownr-1]; + for(ix = mat->row_end[rownr]-1; ix >= ib; ix--) { + jx = ROW_MAT_COLNR(ix); + if(isActiveLink(psdata->cols->varmap, jx)) { + if(!presolve_colfix(psdata, jx, 0.0, TRUE, nv)) + return( presolve_setstatus(psdata, INFEASIBLE) ); + if(presolve_candeletevar(psdata, jx)) + presolve_colremove(psdata, jx, TRUE); + } + } +#ifdef xxParanoia + if(!presolve_debugrowtallies(psdata)) + return( INFEASIBLE ); +#endif + return( RUNNING ); +} + +/* 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) +{ + lprec *lp = psdata->lp; + MYBOOL hasOF, isMI, isDualFREE = TRUE; + int i, ix, ie, *rownr, signOF; + LPSREAL *value, loX, upX, eps = psdata->epsvalue; + MATrec *mat = lp->matA; + + /* First check basic variable range */ + loX = get_lowbo(lp, colnr); + upX = get_upbo(lp, colnr); + if(((loX < 0) && (upX > 0)) || + (fabs(upX-loX) < lp->epsvalue) || + SOS_is_member_of_type(lp->SOS, colnr, SOSn)) + return( FALSE ); + isMI = (MYBOOL) (upX <= 0); + + /* Retrieve OF (standard form assuming maximization) */ + ix = mat->col_end[colnr - 1]; + ie = mat->col_end[colnr]; + rownr = &COL_MAT_ROWNR(ix); + value = &COL_MAT_VALUE(ix); + hasOF = isnz_origobj(lp, colnr); + if(hasOF) + signOF = my_sign(lp->orig_obj[colnr]); + else + signOF = 0; + + /* Loop over all constraints involving active variable (standard form with LE constraints)*/ + for(; (ix < ie) && isDualFREE; + ix++, rownr += matRowColStep, value += matValueStep) { + i = *rownr; + if(!isActiveLink(psdata->rows->varmap, i)) + continue; + if(presolve_rowlength(psdata, i) == 1) { + LPSREAL 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)) { + *status = presolve_setstatus(psdata, INFEASIBLE); + return( FALSE ); + } + if(loR > loX + psdata->epsvalue) + loX = presolve_roundrhs(lp, loR, TRUE); + if(upR < upX - psdata->epsvalue) + upX = presolve_roundrhs(lp, upR, FALSE); + continue; + } + else + isDualFREE = my_infinite(lp, get_rh_range(lp, i)) || /* Explicitly free */ + ((presolve_sumplumin(lp, i, psdata->rows, TRUE)-eps <= get_rh_upper(lp, i)) && /* Implicitly free */ + (presolve_sumplumin(lp, i, psdata->rows, FALSE)+eps >= get_rh_lower(lp, i))); + if(isDualFREE) { + if(signOF == 0) /* Test on the basis of identical signs in the constraints */ + signOF = my_sign(*value); + else /* Test on the basis of constraint sign equal to OF sign */ + isDualFREE = (MYBOOL) (signOF == my_sign(*value)); + } + } + + /* Set fixing value if we were successful */ + if(isDualFREE) { + if(signOF == 0) { + SETMAX(loX, 0); + *fixValue = MIN(loX, upX); + } + else if(signOF > 0) { + if(my_infinite(lp, loX)) + isDualFREE = FALSE; + else { + if(is_int(lp, colnr)) + *fixValue = ceil(loX-PRESOLVE_EPSVALUE); + else + *fixValue = loX; + } + } + else { + if(my_infinite(lp, upX)) + isDualFREE = FALSE; + else { + if(is_int(lp, colnr) && (upX != 0)) + *fixValue = floor(upX+PRESOLVE_EPSVALUE); + else + *fixValue = upX; + } + } + if((*fixValue != 0) && SOS_is_member(lp->SOS, 0, colnr)) + return( FALSE ); + + } + + return( isDualFREE ); +} + +#if 0 +STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, LPSREAL *fixvalue) +{ + lprec *lp = psdata->lp; + int i, ix, item; + LPSREAL loLim, absvalue, epsvalue = psdata->epsvalue; + MATrec *mat = lp->matA; + MYBOOL chsign, canfix = FALSE; + + if(!is_binary(lp, colnr)) + return( canfix ); + + /* Loop over all active rows to search for fixing opportunity */ + item = 0; + for(ix = presolve_nextrow(psdata, colnr, &item); + (ix >= 0) && !canfix; + ix = presolve_nextrow(psdata, colnr, &item)) { + i = COL_MAT_ROWNR(ix); + *fixvalue = COL_MAT_VALUE(ix); + chsign = is_chsign(lp, i); + + /* First check the lower bound of the normalized constraint */ + loLim = presolve_sumplumin(lp, i, psdata->rows, chsign); + loLim = my_chsign(chsign, loLim); + absvalue = fabs(*fixvalue); + canfix = (MYBOOL) ((loLim + absvalue > lp->orig_rhs[i]+epsvalue*MAX(1, absvalue))); + + /* If we were unsuccessful in fixing above, try the upper bound + of the normalized constraint - if it is finite */ + if(!canfix && !my_infinite(lp, get_rh_range(lp, i))) { + loLim = presolve_sumplumin(lp, i, psdata->rows, (MYBOOL) !chsign); + loLim = my_chsign(!chsign, loLim); + *fixvalue = -(*fixvalue); + canfix = (MYBOOL) ((loLim + absvalue > get_rh_range(lp, i)-lp->orig_rhs[i]+epsvalue*MAX(1, absvalue))); + } + } + + /* Check if we were successful in identifying fixing opportunity */ + if(canfix) { + if(*fixvalue < 0) + *fixvalue = 1; + else + *fixvalue = 0; + } + 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; + MATrec *mat = lp->matA; + +#if 0 /* Handled in calling routine */ + if(!is_binary(lp, colnr)) + return( n ); +#endif + + /* Loop over all active rows and do coefficient tightening for qualifying constraints */ + item = 0; + for(ix = presolve_nextrow(psdata, colnr, &item); ix >= 0; + ix = presolve_nextrow(psdata, colnr, &item)) { + i = COL_MAT_ROWNR(ix); + value = COL_MAT_VALUE(ix); + chsign = is_chsign(lp, i); + upLim = presolve_sumplumin(lp, i, psdata->rows, (MYBOOL) !chsign); + upLim = my_chsign(chsign, upLim); + + /* 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; + lp->orig_rhs[i] = upLim; + upLim = value - my_chsign(value < 0, delta); + COL_MAT_VALUE(ix) = upLim; + if(my_sign(value) != my_sign(upLim)) { + if(chsign) { + psdata->rows->negcount[i]--; + psdata->rows->plucount[i]++; + } + else { + psdata->rows->negcount[i]++; + psdata->rows->plucount[i]--; + } + } + n++; + } + } + return( n ); +} + +STATIC int presolve_mergerows(presolverec *psdata, int *nRows, int *nSum) +{ + lprec *lp = psdata->lp; + MYBOOL candelete; + int status = RUNNING, item1, item2, + firstix, RT1, RT2, i, ix, iix, j, jjx, n = 0; + LPSREAL Value1, Value2, bound; + MATrec *mat = lp->matA; + + for(i = lastActiveLink(psdata->rows->varmap); (i > 0) && (status == RUNNING); ) { + + /* First scan for rows with identical row lengths */ + ix = prevActiveLink(psdata->rows->varmap, i); + if(ix == 0) + break; + + /* Don't bother about empty rows or row singletons, since they are + handled by PRESOLVE_ROWS */ + j = presolve_rowlength(psdata, i); + if(j <= 1) { + i = ix; + continue; + } + +#if 0 + /* Enable this to scan all rows back */ + RT2 = lp->rows; + + /* Check abort since this section can be pretty "expensive" */ + if(!presolve_statuscheck(psdata, &status)) + return( status ); +#else + RT2 = 2+1; +#endif + firstix = ix; + for(RT1 = 0; (ix > 0) && (RT1 < RT2) && (status == RUNNING); + ix = prevActiveLink(psdata->rows->varmap, ix), RT1++) { + candelete = FALSE; + if(presolve_rowlength(psdata, ix) != j) + continue; + + /* Check if the beginning columns are identical; if not, continue */ + item1 = 0; + iix = presolve_nextcol(psdata, ix, &item1); + item2 = 0; + jjx = presolve_nextcol(psdata, i, &item2); + + if(ROW_MAT_COLNR(iix) != ROW_MAT_COLNR(jjx)) + continue; + + /* We have a candidate row; check if the entries have a fixed non-zero ratio */ + Value1 = get_mat_byindex(lp, iix, TRUE, FALSE); + Value2 = get_mat_byindex(lp, jjx, TRUE, FALSE); + bound = Value1 / Value2; + Value1 = bound; + + /* Loop over remaining entries */ + jjx = presolve_nextcol(psdata, i, &item2); + for(; (jjx >= 0) && (Value1 == bound); + jjx = presolve_nextcol(psdata, i, &item2)) { + iix = presolve_nextcol(psdata, ix, &item1); + if(ROW_MAT_COLNR(iix) != ROW_MAT_COLNR(jjx)) + break; + Value1 = get_mat_byindex(lp, iix, TRUE, FALSE); + Value2 = get_mat_byindex(lp, jjx, TRUE, FALSE); + + /* If the ratio is different from the reference value we have a mismatch */ + Value1 = Value1 / Value2; + if(bound == lp->infinite) + bound = Value1; + else if(fabs(Value1 - bound) > psdata->epsvalue) + break; + } + + /* Check if we found a match (we traversed all active columns without a break) */ + if(jjx < 0) { + + /* Get main reference values */ + Value1 = lp->orig_rhs[ix]; + Value2 = lp->orig_rhs[i] * bound; + + /* First check for inconsistent equalities */ + if((fabs(Value1 - Value2) > psdata->epsvalue) && + ((get_constr_type(lp, ix) == EQ) && (get_constr_type(lp, i) == EQ))) { + report(lp, NORMAL, "presolve_mergerows: Inconsistent equalities %d and %d found\n", + ix, i); + status = presolve_setstatus(psdata, INFEASIBLE); + } + + else { + + /* Update lower and upper bounds */ + if(is_chsign(lp, i) != is_chsign(lp, ix)) + bound = -bound; + + Value1 = get_rh_lower(lp, i); + if(Value1 <= -lp->infinite) + Value1 *= my_sign(bound); + else + Value1 *= bound; + my_roundzero(Value1, lp->epsdual); /* Extra rounding tolerance *** */ + + Value2 = get_rh_upper(lp, i); + if(Value2 >= lp->infinite) + Value2 *= my_sign(bound); + else + Value2 *= bound; + my_roundzero(Value2, lp->epsdual); /* Extra rounding tolerance *** */ + + if((bound < 0)) + swapREAL(&Value1, &Value2); + + bound = get_rh_lower(lp, ix); + if(Value1 > bound + psdata->epsvalue) + set_rh_lower(lp, ix, Value1); + else + Value1 = bound; + bound = get_rh_upper(lp, ix); + if(Value2 < bound - psdata->epsvalue) + set_rh_upper(lp, ix, Value2); + else + Value2 = bound; + + /* Check results and make equality if appropriate */ + if(fabs(Value2-Value1) < psdata->epsvalue) + presolve_setEQ(psdata, ix); + else if(Value2 < Value1) { + status = presolve_setstatus(psdata, INFEASIBLE); + } + + /* Verify if we can continue */ + candelete = (MYBOOL) (status == RUNNING); + if(!candelete) { + report(lp, NORMAL, "presolve: Range infeasibility found involving rows %s and %s\n", + get_row_name(lp, ix), get_row_name(lp, i)); + } + } + } + /* Perform i-row deletion if authorized */ + if(candelete) { + presolve_rowremove(psdata, i, TRUE); + n++; + break; + } + } + i = firstix; + } + (*nRows) += n; + (*nSum) += n; + + return( status ); +} + +STATIC MYBOOL presolve_reduceGCD(presolverec *psdata, int *nn, int *nb, int *nsum) +{ + lprec *lp = psdata->lp; + MYBOOL status = TRUE; + int i, jx, je, in = 0, ib = 0; + LLONG GCDvalue; + LPSREAL *Avalue, Rvalue, epsvalue = psdata->epsvalue; + MATrec *mat = lp->matA; + + for(i = firstActiveLink(psdata->INTmap); i != 0; i = nextActiveLink(psdata->INTmap, i)) { + + /* Obtain the row GCD */ + jx = mat->row_end[i - 1]; + je = mat->row_end[i]; + Rvalue = ROW_MAT_VALUE(jx); + GCDvalue = abs((int) Rvalue); + jx++; + if(jx < je) + for(; (jx < je) && (GCDvalue > 1); jx++) { + Rvalue = fabs(ROW_MAT_VALUE(jx)); + GCDvalue = gcd((LLONG) Rvalue, GCDvalue, NULL, NULL); + } + + /* Reduce the coefficients, if possible */ + if(GCDvalue > 1) { + jx = mat->row_end[i - 1]; + je = mat->row_end[i]; + for(; jx < je; jx++) { + Avalue = &ROW_MAT_VALUE(jx); + *Avalue /= GCDvalue; + in++; + } + Rvalue = (lp->orig_rhs[i] / GCDvalue) + epsvalue; + lp->orig_rhs[i] = floor(Rvalue); + Rvalue = fabs(lp->orig_rhs[i]-Rvalue); + if(is_constr_type(lp, i, EQ) && (Rvalue > epsvalue)) { + report(lp, NORMAL, "presolve_reduceGCD: Infeasible equality constraint %d\n", i); + status = FALSE; + break; + } + if(!my_infinite(lp, lp->orig_upbo[i])) + lp->orig_upbo[i] = floor(lp->orig_upbo[i] / GCDvalue); + ib++; + } + } + if(status && (in > 0)) + report(lp, DETAILED, "presolve_reduceGCD: Did %d constraint coefficient reductions.\n", in); + + (*nn) += in; + (*nb) += ib; + (*nsum) += in + ib; + + return( status ); +} + +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; + LLrec *map = psdata->EQmap; + MATrec *mat = lp->matA; + + /* Check if it is worth trying */ + m = mat->row_end[0]; + if((map->count == 0) || (m < 2)) + return( status ); + + /* Get the OF row */ + allocINT(lp, &rownr, map->count+1, FALSE); + allocREAL(lp, &ratio, map->count+1, FALSE); + + /* Loop over each row trying to find equal entries in the OF */ + rownr[0] = 0; + for(i = firstActiveLink(map); i != 0; i = nextActiveLink(map, i)) { + if(get_rh(lp, i) <= 0) + continue; + jx = mat->row_end[i]; + n = 0; + for(j = mat->row_end[i-1]; j < jx; j++, n++) { + colnr = ROW_MAT_COLNR(j); + value = ROW_MAT_VALUE(j); + if(colOF[colnr] == 0) + break; + if(n == 0) { + ratio[0] = colOF[colnr] / value; + } + else if(fabs(value * ratio[0] - colOF[colnr]) > psdata->epsvalue) { + n = -1; + break; + } + } + /* Register row if we were successful (and row long enough) */ + if(n >= 2) { + ix = ++rownr[0]; + rownr[ix] = i; + ratio[ix] = ratio[0]; + } + } + n = rownr[0]; + if(n == 0) + goto Finish; + + /* Process the identified rows, eliminating the OF value */ + for(ix = 1; ix <= n; ix++) { + i = rownr[ix]; + jx = mat->row_end[i]; + for(j = mat->row_end[i-1]; j < jx; j++) { + colnr = ROW_MAT_COLNR(j); + colOF[colnr] = 0; + } + } + + /* Update key mapper structures */ + j = lp->columns; + psdata->cols->varmap = cloneLink(psdata->cols->varmap, j+n, TRUE); + psdata->forceupdate = TRUE; + + /* Finally, add helper columns */ + for(ix = 1; ix <= n; ix++) { + i = rownr[ix]; + rownr[0] = 0; + colOF[0] = my_chsign(is_maxim(lp), ratio[ix]); + rownr[1] = i; + colOF[1] = -1; + value = get_rh(lp, i); +/* j = get_constr_type(lp, i); */ + add_columnex(lp, 2, colOF, rownr); + set_bounds(lp, lp->columns, value, value); +/* presolve_setEQ(psdata, i); */ + set_rh(lp, i, 0); + appendLink(psdata->cols->varmap, j+ix); + } + presolve_validate(psdata, TRUE); + + /* Clean up before returning */ +Finish: + FREE(rownr); + FREE(ratio); + (*nn) += n; + + return( status ); +} + +STATIC MYBOOL presolve_invalideq2(lprec *lp, presolverec *psdata) +{ + int jx, jjx, i = 0, item; + MATrec *mat = lp->matA; + MYBOOL error = FALSE; + + do { + + if(i == 0) + i = firstActiveLink(psdata->EQmap); + else + i = nextActiveLink(psdata->EQmap, i); + if(i == 0) + return( error ); + + /* Get the row index of the first 2-element equality */ + for(; i > 0; i = nextActiveLink(psdata->EQmap, i)) + if(presolve_rowlength(psdata, i) == 2) + break; + if(i == 0) + return( error ); + + /* Get the first column */ + item = 0; + jx = presolve_nextcol(psdata, i, &item); + if(jx < 0) + error = TRUE; + jx = ROW_MAT_COLNR(jx); + + /* Get the second column */ + jjx = presolve_nextcol(psdata, i, &item); + if(jjx < 0) + error = AUTOMATIC; + } while(!error); + + return( error ); +} + +/* 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 i, ib, ie, nn = 0; + MATrec *mat = lp->matA; + + ib = mat->col_end[colnr-1]; + ie = mat->col_end[colnr]; + for(; ib < ie; ib++) { + i = COL_MAT_ROWNR(ib); + if(!is_constr_type(lp, i, EQ) || /* It has to be an equality constraint */ + (mapin[i] == 0)) /* And it should not already have been deleted */ + continue; + if(nzvalues != NULL) { + nzrows[nn] = mapin[i]; + nzvalues[nn] = COL_MAT_VALUE(ib); + } + nn++; + } + return( nn ); +} +STATIC int presolve_singularities(presolverec *psdata, int *nn, int *nr, int *nv, int *nSum) +{ + lprec *lp = psdata->lp; + int i, j, n, *rmapin = NULL, *rmapout = NULL, *cmapout = NULL; + + if(lp->bfp_findredundant(lp, 0, NULL, NULL, NULL) == 0) + return( 0 ); + + /* Create condensed row map */ + allocINT(lp, &rmapin, lp->rows+1, TRUE); + allocINT(lp, &rmapout, psdata->EQmap->count+1, FALSE); + allocINT(lp, &cmapout, lp->columns+1, FALSE); + n = 0; + for(i = firstActiveLink(psdata->EQmap); i != 0; i = nextActiveLink(psdata->EQmap, i)) { + n++; + rmapout[n] = i; + rmapin[i] = n; + } + rmapout[0] = n; + n = 0; + for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i)) { + n++; + cmapout[n] = i; + } + cmapout[0] = n; + + /* Do the rank-revealing factorization */ + n = lp->bfp_findredundant(lp, psdata->EQmap->count, presolve_getcolumnEQ, rmapin, cmapout); + + /* Delete the redundant rows */ + for(i = 1; i <= n; i++) { + j = rmapin[i]; + j = rmapout[j]; + presolve_rowremove(psdata, j, TRUE); + } + (*nn) += n; + (*nr) += n; + (*nSum) += n; + + /* Clean up */ + FREE(rmapout); + FREE(rmapin); + FREE(cmapout); + + return( n ); +} + +STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int *nSum) +{ + lprec *lp = psdata->lp; + int n, i, jx, jjx, k, item, *plucount, *negcount, colplu, colneg, + 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, + *colvalue = NULL, *delvalue = NULL, *colitem; + MATrec *mat = lp->matA, *rev = NULL; + DeltaVrec *DV = NULL; + LLrec *EQ2 = NULL; + + /* See if there is anything to do */ + if(psdata->EQmap->count == 0) { + (*nSum) = 0; + return( status ); + } + + /* Tally counts */ + createLink(lp->rows, &EQ2, NULL); + if((EQ2 == NULL) || !allocREAL(lp, &colvalue, nrows+1, FALSE) || + !allocREAL(lp, &delvalue, nrows+1, FALSE)) + goto Finish; + for(i = firstActiveLink(psdata->EQmap); i > 0; i = nextActiveLink(psdata->EQmap, i)) { + if(presolve_rowlength(psdata, i) == 2) + appendLink(EQ2, i); + } + if(EQ2->count == 0) + goto Finish; + n = 0; + + /* Do the elimination loop for all identified 2-element equalities */ + for(i = firstActiveLink(EQ2); i > 0; i = nextActiveLink(EQ2, i)) { + + /* Check if the constraint has been modified by a previous elimination */ + if(presolve_rowlength(psdata, i) != 2) + continue; + + /* Get the column indeces of NZ-values of the "pivot" row */ + item = 0; + jx = presolve_nextcol(psdata, i, &item); /* Eliminated variable coefficient b */ +#ifdef Paranoia + if(jx < 0) + report(lp, SEVERE, "presolve_elimeq2: No qualifying %dst column was found in row %s (ostensible length %d)\n", + 1, get_row_name(lp, i), presolve_rowlength(psdata, i)); +#endif + Coeff2 = ROW_MAT_VALUE(jx); + jx = ROW_MAT_COLNR(jx); + jjx = presolve_nextcol(psdata, i, &item); /* Non-eliminated variable coefficient a */ +#ifdef Paranoia + if(jjx < 0) + report(lp, SEVERE, "presolve_elimeq2: No qualifying %dnd column was found in row %s (ostensible length %d)\n", + 2, get_row_name(lp, i), presolve_rowlength(psdata, i)); +#endif + Coeff1 = ROW_MAT_VALUE(jjx); + jjx = ROW_MAT_COLNR(jjx); + + /* Check if at least one of the coefficients is large enough to preserve stability; + use opposing maximum column values for stability testing. */ + if((fabs(Coeff1) < psdata->epspivot*mat->colmax[jx]) && + ((fabs(Coeff1) != 1) && (fabs(Coeff2) != 1)) && + (fabs(Coeff2) < psdata->epspivot*mat->colmax[jjx])) + continue; + + /* Cannot eliminate a variable if both are SOS members or SC variables */ + if((is_semicont(lp, jx) && is_semicont(lp, jjx)) || + (SOS_is_member(lp->SOS, 0, jx) && SOS_is_member(lp->SOS, 0, jjx))) + continue; + + /* First check if we are allowed to swap; set swap "blockers" */ + k = 0; + if(!is_int(lp, jx) && is_int(lp, jjx)) + k += 1; + else if(!is_semicont(lp, jx) && is_semicont(lp, jjx)) + k += 2; + else if(!SOS_is_member(lp->SOS, 0, jx) && SOS_is_member(lp->SOS, 0, jjx)) + k += 4; + + /* If there were no blockers, determine if we MUST swap the variable to be eliminated */ + if(k == 0) { + if(is_int(lp, jx) && !is_int(lp, jjx)) + k += 8; + else if(is_semicont(lp, jx) && !is_semicont(lp, jjx)) + k += 16; + else if(SOS_is_member(lp->SOS, 0, jx) && !SOS_is_member(lp->SOS, 0, jjx)) + k += 32; + + /* If we are not forced to swap, decide if it otherwise makes sense - high order */ + if(k == 0) { + if((fabs(Coeff2) < psdata->epspivot*mat->colmax[jjx]) && + (fabs(Coeff1) > psdata->epspivot*mat->colmax[jx])) + k += 64; + else if(presolve_collength(psdata, jx) > presolve_collength(psdata, jjx)) + k += 128; + } + + /* If we are not forced to swap, decide if it otherwise makes sense - low order */ + if(k == 0) { + Value2 = Coeff1/Coeff2; +#ifdef DualFeasibilityLogicEQ2 + if((Value2*lp->orig_obj[jx] < 0) && + (Value2*lp->orig_obj[jjx] > 0)) /* Seek increased dual feasibility */ + k += 256; +#endif +#ifdef DivisorIntegralityLogicEQ2 + if((fabs(modf(Coeff2, &Value2)) >= lp->epsvalue) && /* Seek integrality of result */ + (fabs(modf(Coeff1, &Value2)) < lp->epsvalue)) + k += 512; + else if((fabs(fabs(Coeff2)-1) >= lp->epsvalue) && /* Seek integrality of divisor */ + (fabs(fabs(Coeff1)-1) < lp->epsvalue)) + k += 1024; +#endif + } + + } + else + k = 0; + + /* Perform variable index swap if indicated */ + if(k != 0) { + swapINT(&jx, &jjx); + swapREAL(&Coeff1, &Coeff2); + } + + Value1 = lp->orig_rhs[i]/Coeff2; /* Delta constant term */ + Value2 = Coeff1/Coeff2; /* Delta variable term */ + upbound = lp->orig_upbo[lp->rows+jx]; + lobound = lp->orig_lowbo[lp->rows+jx]; + if(lp->spx_trace) { + report(lp, DETAILED, "Row %3d : Elim %g %s - %d\n", i, Coeff2, get_col_name(lp, jx), jx); + report(lp, DETAILED, " Keep %g %s - %d\n", Coeff1, get_col_name(lp, jjx), jjx); + } + + /* Get the coefficient vectors of the independent (jjx) and dependent (jx) columns; + the dependent column will be deleted and reconstructed during postsolve. */ + freshupdate = (MYBOOL) ((colindex == NULL) || (colindex[jjx] == 0)); + if(freshupdate) + mat_expandcolumn(mat, jjx, colvalue, NULL, TRUE); + else + mat_expandcolumn(rev, colindex[jjx], colvalue, NULL, FALSE); + if((colindex == NULL) || (colindex[jx] == 0)) + mat_expandcolumn(mat, jx, delvalue, NULL, TRUE); + else + mat_expandcolumn(rev, colindex[jx], delvalue, NULL, FALSE); + + /* Add variable reconstruction information */ + addUndoPresolve(lp, TRUE, jx, Value1, Value2, jjx); + + /* If possible, tighten the bounds of the uneliminated variable based + on the bounds of the eliminated variable. Also handle roundings + and attempt precision management. */ + bound = lobound; + k = nrows+jjx; + if(bound > -lp->infinite) { + bound = (lp->orig_rhs[i] - Coeff2*bound) / Coeff1; + if(Value2 > 0) { + test = lp->orig_upbo[k]; + if(bound < test - psdata->epsvalue) { + if(is_int(lp, jjx)) + lp->orig_upbo[k] = floor(bound + lp->epsint); + else + lp->orig_upbo[k] = presolve_roundrhs(lp, bound, FALSE); + } + } + else { + test = lp->orig_lowbo[k]; + if(bound > test + psdata->epsvalue) { + if(is_int(lp, jjx)) + lp->orig_lowbo[k] = ceil(bound - lp->epsint); + else + lp->orig_lowbo[k] = presolve_roundrhs(lp, bound, TRUE); + } + } + } + bound = upbound; + if(bound < lp->infinite) { + bound = (lp->orig_rhs[i] - Coeff2*bound) / Coeff1; + if(Value2 < 0) { + test = lp->orig_upbo[k]; + if(bound < test - psdata->epsvalue) { + if(is_int(lp, jjx)) + lp->orig_upbo[k] = floor(bound + lp->epsint); + else + lp->orig_upbo[k] = presolve_roundrhs(lp, bound, FALSE); + } + } + else { + test = lp->orig_lowbo[k]; + if(bound > test + psdata->epsvalue) { + if(is_int(lp, jjx)) + lp->orig_lowbo[k] = ceil(bound - lp->epsint); + else + lp->orig_lowbo[k] = presolve_roundrhs(lp, bound, TRUE); + } + } + } + +#ifdef Eq2Reldiff + test = 2*lp->epsvalue; +#else + test = psdata->epsvalue; +#endif + if(/*(lp->orig_upbo[k] < lp->orig_lowbo[k]) ||*/ +#ifdef Eq2Reldiff + (fabs(my_reldiff(lp->orig_upbo[k],lp->orig_lowbo[k])) < test)) { +#else + (fabs(lp->orig_upbo[k] - lp->orig_lowbo[k]) < test)) { +#endif + my_roundzero(lp->orig_lowbo[k], test); + lp->orig_upbo[k] = lp->orig_lowbo[k]; + } + else { + my_roundzero(lp->orig_upbo[k], test); + my_roundzero(lp->orig_lowbo[k], test); + } + + if(/*(upbound < lobound) ||*/ +#ifdef Eq2Reldiff + (fabs(my_reldiff(upbound, lobound)) < test)) { +#else + (fabs(upbound - lobound) < test)) { +#endif + my_roundzero(lobound, test); + lp->orig_upbo[nrows+jx] = lobound; + upbound = lobound; + } + + /* Loop over the non-zero rows of the column (jx) to be eliminated; + substitute jx-variable by updating rhs and jjx coefficients */ + colitem = colvalue; + plucount = psdata->rows->plucount; + negcount = psdata->rows->negcount; + colplu = 0; + colneg = 0; + /* Count of non-zeros in the independent column jjx */ + item = presolve_collength(psdata, jjx) - 1; + if(isnz_origobj(lp, jjx)) + item++; + for(k = 0; k <= nrows; k++, colitem++) { + + bound = delvalue[k]; + if((k == i) || (bound == 0) || + ((k > 0) && !isActiveLink(psdata->rows->varmap, k))) + continue; + + /* Do constraint and nz-count updates for the substituted variable */ + product = bound*Value1; + + /* "Raw"/unsigned data */ + presolve_adjustrhs(psdata, k, my_chsign(is_chsign(lp, k), product), test); + + /* Change back to signed part */ + if(*colitem != 0) { + if(*colitem > 0) { + colplu--; + plucount[k]--; + } + else { + colneg--; + negcount[k]--; + } + if((lobound < 0) && (upbound >= 0)) { + psdata->cols->pluneg[jjx]--; + psdata->rows->pluneg[k]--; + } + item--; + } + (*colitem) -= bound*Value2; + iCoeffChanged++; + + /* Update counts */ + if(fabs(*colitem) >= mat->epsvalue) { + if(*colitem > 0) { + colplu++; + plucount[k]++; + } + else { + colneg++; + negcount[k]++; + } + if((lobound < 0) && (upbound >= 0)) { + psdata->cols->pluneg[jjx]++; + psdata->rows->pluneg[k]++; + } + item++; + } + else { + *colitem = 0; + } + + /* Also reduce count if the row contains the deleted variable */ + if(bound > 0) + plucount[k]--; + else + negcount[k]--; + } + psdata->cols->plucount[jjx] += colplu; + psdata->cols->negcount[jjx] += colneg; + + /* Save the new column */ + if(rev == NULL) { + DV = createUndoLadder(lp, nrows, lp->columns / RESIZEFACTOR); + rev = DV->tracker; + rev->epsvalue = mat->epsvalue; + allocINT(lp, &(rev->col_tag), mat->columns_alloc+1, FALSE); + allocINT(lp, &colindex, lp->columns+1, TRUE); + rev->col_tag[0] = 0; + } + n = rev->col_tag[0] = incrementUndoLadder(DV); + mat_setcol(rev, n, 0, colvalue, NULL, FALSE, FALSE); + rev->col_tag[n] = jjx; + + /* Save index to updated vector, but specially handle case where we have + the same independent variable for multiple equations! */ + if(!freshupdate) + rev->col_tag[colindex[jjx]] *= -1; + colindex[jjx] = n; + + /* Delete the column dependent variable */ + jx = presolve_colremove(psdata, jx, FALSE); + iVarsFixed++; + + /* Check if we have been lucky enough to have eliminated the independent + variable via substitution of the dependent variable */ + if(item == 0) { +#ifdef Paranoia + report(lp, DETAILED, "presolve_elimeq2: Was able to remove variables %d and %d in row %s\n", + jx, jjx, get_row_name(lp, i)); +#endif + if(presolve_colfix(psdata, jjx, 0.0, TRUE, nc)) + jjx = presolve_colremove(psdata, jjx, FALSE); + } + + /* Delete the row */ + presolve_rowremove(psdata, i, FALSE); + iRowsRemoved++; + } + + /* Perform the column updates collected above */ + if(n > 0) { + mat_mapreplace(mat, psdata->rows->varmap, psdata->cols->varmap, rev); + presolve_validate(psdata, TRUE); +#ifdef PresolveForceUpdateMax + mat_computemax(mat /* , FALSE */); +#endif + psdata->forceupdate = TRUE; + } + + /* Free work arrays */ +Finish: + if(DV != NULL) + freeUndoLadder(&DV); + freeLink(&EQ2); + FREE(colvalue); + FREE(delvalue); + FREE(colindex); + + /* Update counters */ + (*nn) += iCoeffChanged; + (*nr) += iRowsRemoved; + (*nc) += iVarsFixed; + (*nSum) += iCoeffChanged + iRowsRemoved + iVarsFixed; + + return( status ); +} + +STATIC MYBOOL presolve_impliedfree(lprec *lp, presolverec *psdata, int colnr) +{ + int i, ix, ie; + LPSREAL Tlower, Tupper; + MYBOOL status, rowbinds, isfree = FALSE; + MATrec *mat = lp->matA; + + if(my_infinite(lp, get_lowbo(lp, colnr)) && my_infinite(lp, get_upbo(lp, colnr))) + return( TRUE ); + + ie = mat->col_end[colnr]; + for(ix = mat->col_end[colnr-1]; (isfree != (TRUE | AUTOMATIC)) && (ix < ie); ix++) { + i = COL_MAT_ROWNR(ix); + if(!isActiveLink(psdata->rows->varmap, i)) + continue; + Tlower = get_rh_lower(lp, i); + Tupper = get_rh_upper(lp, i); + status = presolve_multibounds(psdata, i, colnr, &Tlower, &Tupper, NULL, &rowbinds); + isfree = isfree | status | rowbinds; + } + + return( (MYBOOL) (isfree == (TRUE | AUTOMATIC)) ); +} + +STATIC MYBOOL presolve_impliedcolfix(presolverec *psdata, int rownr, int colnr, MYBOOL isfree) +{ + lprec *lp = psdata->lp; + 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], + pivot, matAij = mat_getitem(mat, rownr, colnr), *vecOF = lp->orig_obj; + + /* We cannot have semi-continuous or non-qualifying integers */ + if(is_semicont(lp, colnr) || is_SOS_var(lp, colnr)) + return( FALSE ); + if(is_int(lp, colnr)) { + if(!isActiveLink(psdata->INTmap, rownr) || !is_presolve(lp, PRESOLVE_KNAPSACK)) + return( FALSE ); + /* colnr must have a coefficient equal to the smallest in the row */ + varRange = lp->infinite; + i = 0; + pivot = 0; + for(ib = presolve_nextcol(psdata, rownr, &i); i != 0; ib = presolve_nextcol(psdata, rownr, &i)) { + jx = ROW_MAT_COLNR(ib); + dual = fabs(ROW_MAT_VALUE(ib)); + /* Check if we have the target column and save the pivot value */ + if(jx == colnr) { + /* Always accept unit coefficient */ + if(fabs(dual - 1) < psdata->epsvalue) + break; + pivot = dual; + /* Otherwise continue scan */ + } + /* Cannot accept case where result can be fractional */ + else if((pivot > dual + psdata->epsvalue) || + ((pivot > 0) && (fabs(fmod(dual, pivot)) > psdata->epsvalue))) + return( FALSE ); + } + } + + /* Ascertain that the pivot value is large enough to preserve stability */ + pivot = matAij; + if(fabs(pivot) < psdata->epspivot*mat->colmax[colnr]) + return( FALSE ); + + /* Must ascertain that the row variables are not SOS'es; this is because + the eliminated variable will be a function of another variable. */ + if(SOS_count(lp) > 0) { + for(ib = mat->row_end[rownr-1]; ib < ie; ib++) + if(SOS_is_member(lp->SOS, 0, ROW_MAT_COLNR(ib))) + return( FALSE ); + } + + /* Calculate the dual value */ + dual = vecOF[colnr]/pivot; + + /* Here we have free variable in an equality constraint; this means we can + can adjust the OF for the deleted variable and also delete the constraint. */ + if(isfree && is_constr_type(lp, rownr, EQ)) { + matValue = RHS/pivot; + if(matValue != 0) + undoadded = addUndoPresolve(lp, TRUE, colnr, matValue, 0.0, 0); + } + + else { + + /* IMPLIEDFREE: For simplicity, ensure that we can keep the slack based at 0, + and not its upper bound. Effectively, we consider the constraint + an equality, using the information of the sign of the dual. + IMPLIEDSLK: Since we already have an equality constraint, we wish to make sure + that the ensuing inequality constraint will have an RHS that is + non-infinite. */ + if(isfree) { + SETMIN(RHS, presolve_sumplumin(lp, rownr, psdata->rows, TRUE)); + matValue = presolve_sumplumin(lp, rownr, psdata->rows, FALSE); + conRange = get_rh_lower(lp, rownr); + conRange = RHS - MAX(matValue, conRange); + signflip = (MYBOOL) ((dual > 0) && + !my_infinite(lp, conRange)); + } + else { + varLo = get_lowbo(lp, colnr); + varLo *= (my_infinite(lp, varLo) ? my_sign(pivot) : pivot); + varHi = get_upbo(lp, colnr); + varHi *= (my_infinite(lp, varHi) ? my_sign(pivot) : pivot); + if(pivot < 0) + swapREAL(&varHi, &varLo); + signflip = my_infinite(lp, varLo); + } + if(signflip) { + mat_multrow(mat, rownr, -1); + RHS -= conRange; + RHS = -RHS; + lp->orig_rhs[rownr] = RHS; + pivot = -pivot; + dual = -dual; + if(!isfree) { + varLo = -varLo; + varHi = -varHi; + swapREAL(&varHi, &varLo); + } + } + matValue = RHS/pivot; + + /* Prepare for deleting free or implied free variable in inequality constraint. + Different strategies need to be used: + + ACTUAL: Find the proper constraint bound and store undo information for + recovering the value of the implied free variable. The constraint + is then deleted. We have to adjust the objective function if the + OF coefficient for the implied free variable is non-zero. + IMPLIED: Convert the constraint to an inequality at the proper bound. + For given models, the new equality constraint can later provide + an implied slack, which means that a further variable is eliminated, + and the constraint again becomes an inequality constraint. + + Note that this version only implements the ACTUAL mode */ + if(isfree) { + /* Add undo information connecting the deleted variable to the RHS */ + if(matValue != 0) + undoadded = addUndoPresolve(lp, TRUE, colnr, matValue, 0.0, 0); + /* Add undo information for the dual of the deleted constraint */ + if(dual != 0) + addUndoPresolve(lp, FALSE, rownr, dual, 0.0, 0); + } + + /* Prepare for deleting implied slack variable. The following two cases are + handled: + + 1. Equality constraint: Convert the constraint to an inequality constraint + that is possibly ranged + 2. Other constraints: Expand existing slack variable / constraint + range, if required. */ + else { + if(my_infinite(lp, varHi)) + varRange = lp->infinite; +#ifdef Paranoia + else if(my_infinite(lp, varLo)) { + report(lp, SEVERE, "presolve_impliedcolfix: Negative infinite limit for variable %d\n", colnr); + varRange = lp->infinite; + } +#endif + else + varRange = my_precision(fabs(varHi - varLo) + lp->epsvalue, psdata->epsvalue); + presolve_adjustrhs(psdata, rownr, varLo, psdata->epsvalue); + + /* Handle case 1 of an equality constraint */ + if(is_constr_type(lp, rownr, EQ)) { + /* Make sure we actually have a ranged constraint */ + if(varRange > 0) { + set_constr_type(lp, rownr, LE); + if(!my_infinite(lp, varRange)) + lp->orig_upbo[rownr] = varRange; + setLink(psdata->LTmap, rownr); + removeLink(psdata->EQmap, rownr); + } + } + /* Handle case 2 of an inequality constraint (UNDER CONSTRUCTION!)*/ + else { + if(!my_infinite(lp, lp->orig_upbo[rownr])) { + if(my_infinite(lp, varRange)) + lp->orig_upbo[rownr] = lp->infinite; + else + lp->orig_upbo[rownr] += varHi - varLo; + } + } + /* Update counts */ + if(matAij > 0) + psdata->rows->plucount[rownr]--; + else + psdata->rows->negcount[rownr]--; + if(my_sign(varLo) != my_sign(varHi)) + psdata->rows->pluneg[rownr]--; + + /* Add undo information for the deleted variable; note that we cannot link the + deleted variable to the slack, since it may not be available during undo. + We really should have a mini LP to compute this allocation ex-post. */ + if(RHS != 0) + undoadded = addUndoPresolve(lp, TRUE, colnr, RHS/pivot, 0.0, 0); + } + } + + /* Update the OF constant */ + if(dual != 0) { + presolve_adjustrhs(psdata, 0, dual * RHS, 0); +/* lp->orig_rhs[0] -= dual * RHS; */ + vecOF[colnr] = 0; + } + + /* Do affine transformation with the constraint row */ + i = 0; + for(ib = presolve_nextcol(psdata, rownr, &i); ib >= 0; + ib = presolve_nextcol(psdata, rownr, &i)) { + + /* Get the constraint element */ + jx = ROW_MAT_COLNR(ib); + if(jx == colnr) + continue; + matValue = ROW_MAT_VALUE(ib); + + /* Adjust OF for the variable to be deleted */ + if(dual != 0) + vecOF[jx] -= dual * matValue; + + /* Add reconstruction/undo parameters for the deleted variable */ + if(!undoadded) + undoadded = addUndoPresolve(lp, TRUE, colnr, 0.0, matValue/pivot, jx); + else + appendUndoPresolve(lp, TRUE, matValue/pivot, jx); + } + + return( TRUE ); +} + +STATIC psrec *presolve_initpsrec(lprec *lp, int size) +{ + psrec *ps = (psrec *) calloc(1, sizeof(*ps)); + + createLink(size, &ps->varmap, NULL); + fillLink(ps->varmap); + + size++; + + allocINT(lp, &ps->empty, size, FALSE); + ps->empty[0] = 0; + + allocREAL(lp, &ps->pluupper, size, FALSE); + allocREAL(lp, &ps->negupper, size, FALSE); + allocREAL(lp, &ps->plulower, size, FALSE); + allocREAL(lp, &ps->neglower, size, FALSE); + allocINT(lp, &ps->infcount, size, FALSE); + + ps->next = (int **) calloc(size, sizeof(*(ps->next))); + + allocINT(lp, &ps->plucount, size, TRUE); + allocINT(lp, &ps->negcount, size, TRUE); + allocINT(lp, &ps->pluneg, size, TRUE); + + ps->allocsize = size; + + return( ps ); +} +STATIC void presolve_freepsrec(psrec **ps) +{ + FREE((*ps)->plucount); + FREE((*ps)->negcount); + FREE((*ps)->pluneg); + FREE((*ps)->infcount); + + if((*ps)->next != NULL) { + int i, n = (*ps)->allocsize; + for(i = 0; i < n; i++) + FREE((*ps)->next[i]); + FREE((*ps)->next); + } + + FREE((*ps)->plulower); + FREE((*ps)->neglower); + FREE((*ps)->pluupper); + FREE((*ps)->negupper); + + FREE((*ps)->empty); + + freeLink(&(*ps)->varmap); + + FREE(*ps); +} + +STATIC presolverec *presolve_init(lprec *lp) +{ + int k, i, ix, ixx, colnr, + ncols = lp->columns, + nrows = lp->rows; + LPSREAL hold; + MATrec *mat = lp->matA; + presolverec *psdata = NULL; + + /* Optimize memory usage if we have a very large model; + this is to reduce the risk of out-of-memory situations. */ + ix = get_nonzeros(lp); + ixx = lp->matA->mat_alloc; + if((ixx - ix > MAT_START_SIZE) && ((ixx - ix) * 20 > ixx)) + mat_memopt(lp->matA, nrows / 20, ncols / 20, ix / 20); + + psdata = (presolverec *) calloc(1, sizeof(*psdata)); + + psdata->lp = lp; + psdata->rows = presolve_initpsrec(lp, nrows); + psdata->cols = presolve_initpsrec(lp, ncols); + + psdata->epsvalue = PRESOLVE_EPSVALUE; + psdata->epspivot = PRESOLVE_EPSPIVOT; + psdata->forceupdate = TRUE; + + /* Save incoming primal bounds */ + k = lp->sum + 1; + allocREAL(lp, &psdata->pv_lobo, k, FALSE); + MEMCOPY(psdata->pv_lobo, lp->orig_lowbo, k); + allocREAL(lp, &psdata->pv_upbo, k, FALSE); + MEMCOPY(psdata->pv_upbo, lp->orig_upbo, k); + + /* Create and initialize dual value (Langrangean and slack) limits */ + allocREAL(lp, &psdata->dv_lobo, k, FALSE); + allocREAL(lp, &psdata->dv_upbo, k, FALSE); + for(i = 0; i <= nrows; i++) { + psdata->dv_lobo[i] = (is_constr_type(lp, i, EQ) ? -lp->infinite : 0); + psdata->dv_upbo[i] = lp->infinite; + } + k--; + for(; i <= k; i++) { + psdata->dv_lobo[i] = 0; + psdata->dv_upbo[i] = lp->infinite; + } + + /* Create NZ count and sign arrays, and do general initialization of row bounds */ + createLink(nrows, &psdata->EQmap, NULL); + createLink(nrows, &psdata->LTmap, NULL); + createLink(nrows, &psdata->INTmap, NULL); + for(i = 1; i <= nrows; i++) { + switch (get_constr_type(lp, i)) { + case LE: appendLink(psdata->LTmap, i); + break; + case EQ: appendLink(psdata->EQmap, i); + break; + } + k = mat_rowlength(mat, i); + if((lp->int_vars > 0) && (k > 0)) + appendLink(psdata->INTmap, i); + } + + /* Seek to reduce set of sum(INT*INT) rows (mainly for GCD coefficient reductions) */ + if(psdata->INTmap->count > 0) + for(i = 1; i <= nrows; i++) { + if(!isActiveLink(psdata->INTmap, i)) + continue; + /* Disqualify if there is a non-int variable, otherwise find smallest absolute fractional row value */ + ix = mat->row_end[i - 1]; + ixx = mat->row_end[i]; + colnr = 0; + for(; ix < ixx; ix++) { + if(!is_int(lp, ROW_MAT_COLNR(ix))) { + removeLink(psdata->INTmap, i); + break; + } + hold = fabs(ROW_MAT_VALUE(ix)); + hold = fmod(hold, 1); + /* Adjust colnr to be a decimal scalar */ + for(k = 0; (k <= MAX_FRACSCALE) && (hold+psdata->epsvalue < 1); k++) + hold *= 10; + if(k > MAX_FRACSCALE) { + removeLink(psdata->INTmap, i); + break; + } + SETMAX(colnr, k); + } + if(!isActiveLink(psdata->INTmap, i)) + continue; + hold = pow(10.0, colnr); + /* Also disqualify if the RHS is fractional after scaling */ + if(fabs(fmod(lp->orig_rhs[i] * hold, 1)) > psdata->epsvalue) { + removeLink(psdata->INTmap, i); + continue; + } + /* We have an all-int constraint, see if we should scale it up */ + if(k > 0) { + ix = mat->row_end[i - 1]; + for(; ix < ixx; ix++) { + 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 */ + } + } + + /* Do the real tallying and ordering work */ + presolve_validate(psdata, TRUE); + + return( psdata ); +} + +STATIC void presolve_free(presolverec **psdata) +{ + presolve_freepsrec(&(*psdata)->rows); + presolve_freepsrec(&(*psdata)->cols); + FREE((*psdata)->dv_lobo); + FREE((*psdata)->dv_upbo); + FREE((*psdata)->pv_lobo); + FREE((*psdata)->pv_upbo); + freeLink(&(*psdata)->EQmap); + freeLink(&(*psdata)->LTmap); + freeLink(&(*psdata)->INTmap); + FREE(*psdata); +} + +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; + MATrec *mat = lp->matA; + LLrec *colLL = NULL; + + /* First see if we can relax ranged constraints */ + for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) { + if(is_constr_type(lp, i, EQ)) + continue; + presolve_range(lp, i, psdata->rows, &losum, &upsum); + lorhs = get_rh_lower(lp, i); + uprhs = get_rh_upper(lp, i); + + /* Look for opportunity to relax constraint bounds */ + if(presolve_rowlength(psdata, i) > 1) { + if((is_constr_type(lp, i, GE) && (upsum <= uprhs)) || + (is_constr_type(lp, i, LE) && (losum >= lorhs))) + set_rh_range(lp, i, lp->infinite); + } + } + + /* Collect columns available for bound relaxation (find implied free variables) + (consider sorting the list in decending order of column lengths or do call to + COLAMD to maximize impact) */ + createLink(lp->columns, &colLL, NULL); + for(j = firstActiveLink(psdata->cols->varmap); j != 0; j = nextActiveLink(psdata->cols->varmap, j)) + if(presolve_impliedfree(lp, psdata, j)) + appendLink(colLL, j); + + /* Find what columns to relax (ideally one per row) */ + if(colLL->count > 0) { + LLrec *rowLL = NULL; + MYBOOL canfree; + + /* Create row tracker */ + createLink(lp->rows, &rowLL, NULL); + fillLink(rowLL); + + /* Loop over all column candidates */ + for(j = firstActiveLink(colLL); (j > 0) && (rowLL->count > 0); j = nextActiveLink(colLL, j)) { + + /* Verify that the variable is applicable */ + canfree = TRUE; + for(ix = mat->col_end[j-1]; canfree && (ix < mat->col_end[j]); ix++) + canfree = isActiveLink(rowLL, COL_MAT_ROWNR(ix)); + + /* If so, then open the bounds and update the row availability mapper */ + if(canfree) { + nn++; + Xlower = get_lowbo(lp, j); + Xupper = get_upbo(lp, j); + if(Xlower >= 0) + set_bounds(lp, j, 0, freeinf); + else if(Xupper <= 0) + set_bounds(lp, j, -freeinf, 0); + else +/* set_bounds(lo, j, -freeinf, freeinf); */ + set_unbounded(lp, j); + for(ix = mat->col_end[j-1]; ix < mat->col_end[j]; ix++) + removeLink(rowLL, COL_MAT_ROWNR(ix)); + } + } + freeLink(&rowLL); + } + + /* Free list and return */ + freeLink(&colLL); + return( nn ); +} + +STATIC MYBOOL presolve_updatesums(presolverec *psdata) +{ + lprec *lp = psdata->lp; + int j; + + /* Initialize row accumulation arrays */ + MEMCLEAR(psdata->rows->pluupper, lp->rows + 1); + MEMCLEAR(psdata->rows->negupper, lp->rows + 1); + MEMCLEAR(psdata->rows->plulower, lp->rows + 1); + MEMCLEAR(psdata->rows->neglower, lp->rows + 1); + MEMCLEAR(psdata->rows->infcount, lp->rows + 1); + + /* Loop over active columns */ + for(j = firstActiveLink(psdata->cols->varmap); j != 0; + j = nextActiveLink(psdata->cols->varmap, j)) { + presolve_colfix(psdata, j, lp->infinite, FALSE, NULL); + } + +#ifdef UseDualPresolve + /* Initialize column accumulation arrays */ + MEMCLEAR(psdata->cols->pluupper, lp->columns + 1); + MEMCLEAR(psdata->cols->negupper, lp->columns + 1); + MEMCLEAR(psdata->cols->plulower, lp->columns + 1); + MEMCLEAR(psdata->cols->neglower, lp->columns + 1); + MEMCLEAR(psdata->cols->infcount, lp->columns + 1); + + /* Loop over active rows */ + for(j = firstActiveLink(psdata->rows->varmap); j != 0; + j = nextActiveLink(psdata->rows->varmap, j)) { + presolve_rowfix(psdata, j, lp->infinite, FALSE, NULL); + } +#endif + + return( TRUE ); +} + +STATIC MYBOOL presolve_finalize(presolverec *psdata) +{ + lprec *lp = psdata->lp; + MYBOOL compactvars = FALSE; + int ke, n; + + /* Save eliminated rows and columns for restoration purposes */ +#ifdef SavePresolveEliminated + psdata->deletedA = mat_extractmat(lp->matA, rowmap, colmap, TRUE); + if(!mat_validate(psdata->deletedA)) + report(lp, SEVERE, "presolve_finalize: Could not validate matrix with undo data\n"); +#endif + + /* Check if OF columns are to be deleted */ + lp->presolve_undo->OFcolsdeleted = FALSE; + for(n = firstInactiveLink(psdata->cols->varmap); (n != 0) && !lp->presolve_undo->OFcolsdeleted; + n = nextInactiveLink(psdata->cols->varmap, n)) + lp->presolve_undo->OFcolsdeleted = (MYBOOL) (lp->orig_obj[n] != 0); + + /* Delete eliminated columns */ + ke = lastInactiveLink(psdata->cols->varmap); + n = countInactiveLink(psdata->cols->varmap); + if((n > 0) && (ke > 0)) { + del_columnex(lp, psdata->cols->varmap); + mat_colcompact(lp->matA, lp->presolve_undo->orig_rows, + lp->presolve_undo->orig_columns); + compactvars = TRUE; + } + + /* Delete eliminated rows */ + ke = lastInactiveLink(psdata->rows->varmap); + n = countInactiveLink(psdata->rows->varmap); + if((n > 0) && (ke > 0)) { + del_constraintex(lp, psdata->rows->varmap); + mat_rowcompact(lp->matA, TRUE); + compactvars = TRUE; + } + else if(psdata->nzdeleted > 0) + mat_zerocompact(lp->matA); + + /* Do compacting and updating of variable maps */ + if(compactvars) + varmap_compact(lp, lp->presolve_undo->orig_rows, + lp->presolve_undo->orig_columns); + + /* Reduce memory usage of postsolve matrices */ + if(lp->presolve_undo->primalundo != NULL) + mat_memopt(lp->presolve_undo->primalundo->tracker, 0, 0, 0); + if(lp->presolve_undo->dualundo != NULL) + mat_memopt(lp->presolve_undo->dualundo->tracker, 0, 0, 0); + + /* Round near-zero objective function coefficients and RHS values */ + ke = lp->columns; + for(n = 1; n <= ke; n++) + my_roundzero(lp->orig_obj[n], lp->epsvalue); + ke = lp->rows; + 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; + int size; + MYBOOL ok; + + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename, my_if(doappend, "a", "w"))) != NULL)); + if(!ok) + return(ok); + if((filename == NULL) && (lp->outstream != NULL)) + output = lp->outstream; + + fprintf(output, "\nPRESOLVE - Status at loop %d:%d:%d\n", + psdata->outerloops, psdata->middleloops, psdata->innerloops); + fprintf(output, "Model size: %d rows (%d equalities, %d less than), %d columns\n", + psdata->rows->varmap->count, psdata->EQmap->count, psdata->LTmap->count, psdata->cols->varmap->count); + + fprintf(output, "\nMAPPERS\n-------\n\n"); + size = 1; + blockWriteINT(output, "colmap", psdata->cols->varmap->map, 0, size*psdata->cols->varmap->size); + blockWriteINT(output, "rowmap", psdata->rows->varmap->map, 0, size*psdata->rows->varmap->size); + blockWriteINT(output, "EQmap", psdata->EQmap->map, 0, size*psdata->EQmap->size); + blockWriteINT(output, "LTmap", psdata->LTmap->map, 0, size*psdata->LTmap->size); + + fprintf(output, "\nCOUNTS\n------\n\n"); + blockWriteINT(output, "plucount", psdata->rows->plucount, 0, lp->rows); + blockWriteINT(output, "negcount", psdata->rows->negcount, 0, lp->rows); + blockWriteINT(output, "pluneg", psdata->rows->pluneg, 0, lp->rows); + + fprintf(output, "\nSUMS\n----\n\n"); + blockWriteREAL(output, "pluupper", psdata->rows->pluupper, 0, lp->rows); + blockWriteREAL(output, "negupper", psdata->rows->negupper, 0, lp->rows); + blockWriteREAL(output, "plulower", psdata->rows->pluupper, 0, lp->rows); + blockWriteREAL(output, "neglower", psdata->rows->negupper, 0, lp->rows); + + if(filename != NULL) + fclose(output); + return(ok); +} + +int CMP_CALLMODEL compRedundant(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate) +{ + int start1 = (int) (current->int4.intpar1), + start2 = (int) (candidate->int4.intpar1), + result = CMP_COMPARE(start1, start2); + + if(result == 0) { + start1 = (int) (current->int4.intpar2); + start2 = (int) (candidate->int4.intpar2); + result = -CMP_COMPARE(start1, start2); + } + return( result ); +} +int CMP_CALLMODEL compSparsity(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate) +{ + int start1 = (int) (current->int4.intpar1), + start2 = (int) (candidate->int4.intpar1), + result = CMP_COMPARE(start1, start2); + + if(result == 0) { + start1 = (int) (current->int4.intpar2); + start2 = (int) (candidate->int4.intpar2); + result = -CMP_COMPARE(start1, start2); + } + + if(result == 0) { + start1 = (int) (current->int4.intval); + start2 = (int) (candidate->int4.intval); + result = CMP_COMPARE(start1, start2); + } + return( result ); +} +int CMP_CALLMODEL compAggregate(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate) +{ + int index1 = (int) (current->pvoidint2.intval), + index2 = (int) (candidate->pvoidint2.intval); + lprec *lp = (lprec *) current->pvoidint2.ptr; + LPSREAL value1 = lp->orig_obj[index1], + value2 = lp->orig_obj[index2]; + + /* Smallest objective coefficient (largest contribution to OF) */ + int result = CMP_COMPARE(value1, value2); + + /* Smallest lower variable bound */ + if(result == 0) { + index1 += lp->rows; + index2 += lp->rows; + value1 = lp->orig_lowbo[index1]; + value2 = lp->orig_lowbo[index2]; + result = CMP_COMPARE(value1, value2); + } + + /* Largest upper variable bound */ + if(result == 0) { + value1 = lp->orig_upbo[index1]; + value2 = lp->orig_upbo[index2]; + result = -CMP_COMPARE(value1, value2); + } + return( result ); +} + +STATIC int presolve_rowdominance(presolverec *psdata, int *nCoeffChanged, int *nRowsRemoved, int *nVarsFixed, int *nSum) +{ + lprec *lp = psdata->lp; + 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; + UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->rows+1, sizeof(*QS)); + + /* Check if we were able to obtain working memory */ + if(QS == NULL) + return( status); + + /* A dominating row of variables always satisfy the following criteria: + 1) The starting column position is never lower, but could be the same + 2) The non-zero row count is always lower */ + n = 0; + for(i = firstActiveLink(psdata->EQmap); i != 0; i = nextActiveLink(psdata->EQmap, i)) { + /* Make sure we have no SOS or semi-continuous variables */ + jb = je = 0; + if((SOS_count(lp) > 0) || (lp->sc_vars > 0)) { + item = 0; + for(jb = presolve_nextcol(psdata, i, &item); jb >= 0; + jb = presolve_nextcol(psdata, i, &item)) { + jx = ROW_MAT_COLNR(jb); + if(SOS_is_member(lp->SOS, 0, jx) || is_semicont(lp, jx)) + break; + } + } + + /* Add to list if we are Ok */ + if(jb < 0) { + QS[n].int4.intval = i; + item = 0; + ii = presolve_nextcol(psdata, i, &item); + QS[n].int4.intpar1 = ROW_MAT_COLNR(ii); + QS[n].int4.intpar2 = presolve_rowlength(psdata, i); + n++; + } + } + if(n <= 1) + goto Finish; + 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 dominating row */ + if(!allocREAL(lp, &rowvalues, lp->columns + 1, TRUE) || + !allocINT(lp, &coldel, lp->columns + 1, FALSE)) + goto Finish; + + for(ib = 0; ib < n; ib++) { + + /* Get row and check if it was previously eliminated */ + i = QS[ib].int4.intval; + if(i < 0) + continue; + + /* Load the non-zero row values */ + item = 0; + for(jb = presolve_nextcol(psdata, i, &item); jb >= 0; + jb = presolve_nextcol(psdata, i, &item)) { + jx = ROW_MAT_COLNR(jb); + rowvalues[jx] = ROW_MAT_VALUE(jb); + } + + for(ie = ib+1; ie < n; ie++) { + + /* Get row and check if it was previously eliminated */ + ii = QS[ie].int4.intval; + if(ii < 0) + continue; + +#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_rowdominance: Invalid sorted row order\n"); +#endif + + /* Loop over every row member to confirm that the candidate + actually dominates in every position */ + if((lp->orig_rhs[i] == 0) && (lp->orig_rhs[ii] == 0)) + ratio = 0; + else if((lp->orig_rhs[i] != 0) && (lp->orig_rhs[ii] != 0)) + ratio = lp->orig_rhs[i] / lp->orig_rhs[ii]; + else + continue; + item = 0; + for(jb = presolve_nextcol(psdata, ii, &item); jb >= 0; + jb = presolve_nextcol(psdata, ii, &item)) { + jx = ROW_MAT_COLNR(jb); + if(rowvalues[jx] == 0) + break; + if(ratio == 0) + ratio = rowvalues[jx] / ROW_MAT_VALUE(jb); + else if(fabs(rowvalues[jx] - ratio*ROW_MAT_VALUE(jb)) > psdata->epsvalue) + break; + } + + /* "We have contact" */ + if(jb < 0) { + int sign_1 = 0, sign_j = 0; + + /* Need to fix any superset columns, but require that they have equal signs */ + coldel[0] = 0; + item = 0; + for(jb = presolve_nextcol(psdata, i, &item); jb >= 0; + jb = presolve_nextcol(psdata, i, &item)) { + jx = ROW_MAT_COLNR(jb); + if(mat_findelm(mat, ii, jx) <= 0) { + + /* Cancel if we detect a free or "quasi-free" variable */ + if((lp->orig_lowbo[lp->rows + jx] < 0) && + (lp->orig_upbo[lp->rows + jx] > 0)) { + coldel[0] = -1; + break; + } + + /* Ensure that we are feasible */ + else if((lp->orig_lowbo[lp->rows + jx] > 0) || + (lp->orig_upbo[lp->rows + jx] < 0)) { + report(lp, DETAILED, "presolve_rowdominate: Column %s is infeasible due to conflict in rows %s and %s\n", + get_col_name(lp, jx), get_row_name(lp, i), get_row_name(lp, ii)); + coldel[0] = -1; + break; + } + + /* Check consistency / uniformity of signs */ + sign_j = my_sign(ROW_MAT_VALUE(jb)); + sign_j = my_chsign(is_negative(lp, jx), sign_j); + if(coldel[0] == 0) { + sign_1 = sign_j; + coldel[++coldel[0]] = jx; + } + else if(sign_j == sign_1) { + coldel[++coldel[0]] = jx; + } + else { + coldel[0] = -1; + break; + } + } + } + + /* Force break / continuation if the superset columns were incompatible */ + if(coldel[0] < 0) + continue; + + /* Do the column fixing and deletion (check for infeasibility in the process) */ + for(jb = 1; jb <= coldel[0]; jb++) { + jx = coldel[jb]; + if(!presolve_colfix(psdata, jx, 0, TRUE, &iVarFixed)) { + status = presolve_setstatus(psdata, INFEASIBLE); + goto Finish; + } + presolve_colremove(psdata, jx, TRUE); + rowvalues[jx] = 0; + } + + /* Then delete the row */ + presolve_rowremove(psdata, ii, TRUE); + iRowRemoved++; + QS[ie].int4.intval = -ii; + } + } + + /* Clear the non-zero row values ahead of the next row candidate */ + ie = mat->row_end[i-1]; + ii = mat->row_end[i]; + for(; ie < ii; ie++) + rowvalues[ROW_MAT_COLNR(ie)] = 0; + + } +Finish: + FREE(QS); + FREE(rowvalues); + FREE(coldel); + + (*nCoeffChanged) += iCoeffChanged; + (*nRowsRemoved) += iRowRemoved; + (*nVarsFixed) += iVarFixed; + (*nSum) += iCoeffChanged + iRowRemoved + iVarFixed; + + 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 */ +{ + lprec *lp = psdata->lp; + MATrec *mat = lp->matA; + 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; + UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->columns+1, sizeof(*QS)); + + /* Check if we were able to obtain working memory */ + if(QS == NULL) + return( status); + if(lp->int_vars == 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 we have an all-binary, unit-coefficient row */ + je = mat->col_end[i]; + item = 0; + for(jb = presolve_nextrow(psdata, i, &item); jb >= 0; + jb = presolve_nextrow(psdata, i, &item)) { + jx = COL_MAT_ROWNR(jb); + if(COL_MAT_VALUE(jb) != 1) + break; + } + + /* Add to list if we are 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, lp->rows + 1, TRUE) || + !allocINT(lp, &coldel, lp->columns + 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(i < 0) + 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); + } + + coldel[0] = 0; + for(ie = ib+1; ie < n; ie++) { + + /* 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; + if(ii < 0) + continue; + + /* Also make sure that the variables have "compatible" bounds */ +#if 1 + if((fabs(my_reldiff(lp->orig_lowbo[lp->rows + i], lp->orig_lowbo[lp->rows + ii])) > psdata->epsvalue) || + (fabs(my_reldiff(lp->orig_upbo[lp->rows + i], lp->orig_upbo[lp->rows + ii] )) > psdata->epsvalue)) + continue; +#endif + +#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 + relatively identical in every position */ + first = TRUE; + item = 0; + item2 = 0; + scale = 1; + 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(first) { + first = !first; + scale = colvalues[jx] / COL_MAT_VALUE(jb); + } + else { + if(fabs(colvalues[jx] - scale * COL_MAT_VALUE(jb)) > psdata->epsvalue) + break; + } + /* 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) + break; + } + + /* "We have contact" */ + if(jb < 0) { + coldel[++coldel[0]] = ii; + QS[ie].int4.intval = -ii; + } + } + + /* Find the dominant column and delete / fix the others; + if there is a tie, simply delete the second candidate */ + ii = i; + for(jb = 1; jb <= coldel[0]; jb++) { + jx = coldel[jb]; + if(lp->orig_obj[jx] < lp->orig_obj[ii]) + swapINT(&ii, &coldel[jb]); + } + for(jb = 1; jb <= coldel[0]; jb++) { + jx = coldel[jb]; + if(!presolve_colfix(psdata, jx, lp->orig_lowbo[lp->rows+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: + FREE(QS); + FREE(colvalues); + FREE(coldel); + + (*nVarsFixed) += iVarFixed; + (*nSum) += iVarFixed; + + 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 */ +{ + lprec *lp = psdata->lp; + MATrec *mat = lp->matA; + 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; + UNIONTYPE QSORTrec *QScand = (UNIONTYPE QSORTrec *) calloc(lp->columns+1, sizeof(*QScand)); + + /* Check if we were able to obtain working memory */ + if(QScand == NULL) + return( status); + + /* Obtain the list of qualifying columns to be sorted */ + n = 0; + for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i)) + if(!is_semicont(lp, i) && !SOS_is_member(lp->SOS, 0, i)) { + QScand[n].int4.intval = i; + item = 0; + ii = presolve_nextrow(psdata, i, &item); + QScand[n].int4.intpar1 = COL_MAT_ROWNR(ii); + ii = presolve_collength(psdata, i); + QScand[n].int4.intpar2 = ii; + n++; + } + if(n <= 1) { + FREE(QScand); + return( status ); + } + QS_execute(QScand, n, (findCompare_func *) compRedundant, NULL); + + /* Let us start from the top of the list, going forward and looking + for the longest possible identical column */ + if(!allocREAL(lp, &colvalues, lp->rows + 1, TRUE) || + !allocINT(lp, &coldel, lp->columns + 1, FALSE)) + goto Finish; + + for(ib = 0; ib < n; ib++) { + + /* Get column and check if it was previously eliminated */ + i = QScand[ib].int4.intval; + if(i < 0) + continue; + + /* Load the non-zero column values of this active/reference column */ + 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); + } + + coldel[0] = 0; + for(ie = ib+1; ie < n; ie++) { + + /* Insist on identical column lengths (sort is decending in column lengths) */ + ii = QScand[ib].int4.intpar2 - QScand[ie].int4.intpar2; + if(ii != 0) + break; + + /* Also insist on identical starting positions */ + ii = QScand[ib].int4.intpar1 - QScand[ie].int4.intpar1; + if(ii != 0) + break; + + /* Get column and check if it was previously eliminated */ + ii = QScand[ie].int4.intval; + if(ii < 0) + continue; + + /* Loop over every column member to confirm that the candidate is + relatively identical in every position */ + first = TRUE; + item = 0; + item2 = 0; + scale = 1; + 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(first) { + first = !first; + scale = colvalues[jx] / COL_MAT_VALUE(jb); + } + else { + if(fabs(colvalues[jx] - scale * COL_MAT_VALUE(jb)) > psdata->epsvalue) + break; + } + } + + /* "We have contact", store the column in the aggregation list */ + if(jb < 0) { + coldel[++coldel[0]] = ii; + QScand[ie].int4.intval = -ii; + } + } + + /* Sort the aggregation list if we have aggregation candidates */ + if(coldel[0] > 1) { + LPSREAL of, ofelim, fixvalue; + MYBOOL isint; + UNIONTYPE QSORTrec *QSagg = (UNIONTYPE QSORTrec *) calloc(coldel[0], sizeof(*QSagg)); + + for(jb = 1; jb <= coldel[0]; jb++) { + ii = jb - 1; + QSagg[ii].pvoidint2.intval = coldel[jb]; + QSagg[ii].pvoidint2.ptr = (void *) lp; + } + QS_execute(QSagg, coldel[0], (findCompare_func *) compAggregate, NULL); + + /* Process columns with identical OF coefficients */ + jb = 0; + while((status == RUNNING) && (jb < coldel[0])) { + ii = QSagg[jb].pvoidint2.intval; + of = lp->orig_obj[ii]; + isint = is_int(lp, ii); + je = jb + 1; + while((status == RUNNING) && (je < coldel[0]) && + (fabs(lp->orig_obj[ix = QSagg[je].pvoidint2.intval] - of) < psdata->epsvalue)) { + /* We now have two columns with equal OFs; the following cases are possible: + + 1) The first column has Inf upper bound, which means that it can + "absorb" compatible columns, which are then fixed at the appropriate + bounds (or zero in case of free variables). + 2) The first column has a -Inf lower bound, and further columns are + Inf upper bounds, which means steps towards forming a free variable + can be made. + 3) The first column is a non-Inf upper bound, in which case the bounds + are summed into a helper variable and the variable simply deleted. + The deleted variables' value are allocated/distributed via a simple + linear programming routine at postsolve. + + In the current version of this code, we only handle case 1. */ + if(is_int(lp, ix) == isint) { + ofelim = lp->orig_obj[ix]; + if(of == 0) + scale = 1; + else + scale = ofelim / of; + + if(my_infinite(lp, lp->orig_upbo[lp->rows+ii])) { /* Case 1 (recipe.mps) */ + if(is_unbounded(lp, ix)) + fixvalue = 0; + else if(ofelim < 0) + fixvalue = lp->orig_upbo[lp->rows+ix]; + else + fixvalue = lp->orig_lowbo[lp->rows+ix]; + if(my_infinite(lp, fixvalue)) + status = presolve_setstatus(psdata, UNBOUNDED); + else if(!presolve_colfix(psdata, ix, fixvalue, TRUE, &iVarFixed)) + status = presolve_setstatus(psdata, INFEASIBLE); + else + presolve_colremove(psdata, ix, TRUE); + } + + else if(my_infinite(lp, lp->orig_lowbo[lp->rows+ii])) { /* Case 2 */ + /* Do nothing */ + } + + else { /* Case 3 */ +#if 0 + /* Do nothing */ +#else + if(ofelim >= 0) { + fixvalue = lp->orig_lowbo[lp->rows+ix]; + lp->orig_upbo[lp->rows+ii] += scale * (lp->orig_upbo[lp->rows+ix] - fixvalue); + } + else { + fixvalue = lp->orig_upbo[lp->rows+ix]; + lp->orig_upbo[lp->rows+ii] -= scale * (fixvalue - lp->orig_lowbo[lp->rows+ix]); + } + if(my_infinite(lp, fixvalue)) + status = presolve_setstatus(psdata, UNBOUNDED); + else if(!presolve_colfix(psdata, ix, fixvalue, TRUE, &iVarFixed)) + status = presolve_setstatus(psdata, INFEASIBLE); + else + presolve_colremove(psdata, ix, TRUE); +#ifdef xxParanoia + if(presolve_rowlengthdebug(psdata) > 0) + report(lp, SEVERE, "presolve_aggregate: Invalid row count\n"); +#endif + psdata->forceupdate = TRUE; +#endif + } + } + je++; + } + jb = je; + } + FREE(QSagg); + } + + /* 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: + FREE(QScand); + FREE(colvalues); + FREE(coldel); + + (*nVarsFixed) += iVarFixed; + (*nSum) += iVarFixed; + + return( status ); +} + +STATIC int presolve_makesparser(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSum) +{ + lprec *lp = psdata->lp; + MATrec *mat = lp->matA; + 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; + LLrec *EQlist = NULL; + UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->rows, sizeof(*QS)); + + /* Check if we were able to obtain working memory */ + if((QS == NULL) || (psdata->rows->varmap->count == 0) || (psdata->EQmap->count == 0)) + return( status); + + /* Sort rows in 1) increasing order of start index, 2) decreasing length, and + 3) non-equalities (i.e. equalities last) */ + n = 0; + for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) { + k = presolve_rowlength(psdata, i); + if(k >= 2) { + item = 0; + ii = presolve_nextcol(psdata, i, &item); +#ifdef Paranoia + if((ii < 0) || (item == 0)) { + report(lp, SEVERE, "presolve_makesparser: Unexpected zero-length row %d\n", i); + continue; + } +#endif + QS[n].int4.intval = my_chsign(is_constr_type(lp, i, EQ), i); + QS[n].int4.intpar1 = ROW_MAT_COLNR(ii); + QS[n].int4.intpar2 = k; + n++; + } + } + if(n <= 1) { + FREE(QS); + return( status ); + } + QS_execute(QS, n, (findCompare_func *) compSparsity, NULL); + + /* Create associated sorted map of indeces to equality constraints; + note that we need to have a unit offset for compatibility. */ + allocINT(lp, &nzidx, lp->columns + 1, FALSE); + createLink(lp->rows, &EQlist, NULL); + for(ib = 0; ib < n; ib++) { + i = QS[ib].int4.intval; + if(i < 0) + appendLink(EQlist, ib + 1); + } + + /* Loop over all equality masks */ + for(ix = firstActiveLink(EQlist); ix != 0; ) { + + /* Get row starting and ending positions of the mask */ + ii = abs(QS[ix-1].int4.intval); + jjb = QS[ix-1].int4.intpar1; + jje = presolve_lastcol(psdata, ii); + jje = ROW_MAT_COLNR(jje); + jjl = QS[ix-1].int4.intpar2; + + /* Scan the OF */ + i = 0; + chsign = is_chsign(lp, i); + test = ratio = 0.0; + itemEQ = 0; + nzidx[0] = 0; + while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && /*(itemEQ > 0) && */ + (fabs(test-ratio) < psdata->epsvalue)) { + valueEQ = ROW_MAT_VALUE(jjx); + if(valueEQ == 0) + continue; + k = ROW_MAT_COLNR(jjx); + value = lp->orig_obj[k]; + if(fabs(value) < psdata->epsvalue) + break; + if(ratio == 0.0) { + test = ratio = value / valueEQ; + } + else + test = value / valueEQ; + /* Store nz index */ + nzidx[++nzidx[0]] = k; + } + + /* We were successful if the equality was completely traversed; we will + then zero-out the OF coefficients and update the constant term. */ + if((itemEQ == 0) && (nzidx[0] > 0) && (fabs(test-ratio) < psdata->epsvalue)) { + for(k = 1; k <= nzidx[0]; k++) { + /* We should add recovery data for the zero'ed coefficient here */ + jx = nzidx[k]; + value = lp->orig_obj[jx]; + lp->orig_obj[jx] = 0.0; + /* Update counts */ + value = my_chsign(chsign, value); + if(value < 0) { + psdata->rows->negcount[i]--; + psdata->cols->negcount[jx]--; + } + else { + psdata->rows->plucount[i]--; + psdata->cols->plucount[jx]--; + } + iObjChanged++; + } + value = ratio * lp->orig_rhs[ii]; + presolve_adjustrhs(psdata, i, value, psdata->epsvalue); + } + + /* Scan for compatible constraints that can be masked for sparsity elimination */ + for(ib = 1; ib < ix; ib++) { + + /* Get row starting and ending positions of the target constraint */ + i = abs(QS[ib-1].int4.intval); + jb = QS[ib-1].int4.intpar1; + je = presolve_lastcol(psdata, i); + je = ROW_MAT_COLNR(je); + jl = QS[ib-1].int4.intpar2; + + /* Check if there is a window mismatch */ + if((jjb < jb) || (jje > je) || (jjl > jl)) + goto NextEQ; + + /* We have a window match; now check if there is a (scalar) member-by-member + match as well. We approach this in the following manner: + 1) Get first (or next) member of active equality + 2) Loop to matching member in the target constraint, but abandon if no match + 3) Set ratio if this is the first match, otherwise compare ratio and abandon + on mismatch + 4) Go to 1) of there are more elements in the active equality + 5) Proceed to do sparsity elimination if we were successful. */ + chsign = is_chsign(lp, i); + test = ratio = 0.0; + itemEQ = 0; + item = 0; + nzidx[0] = 0; + while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && /*(itemEQ > 0) &&*/ + (fabs(test-ratio) < psdata->epsvalue)) { + valueEQ = ROW_MAT_VALUE(jjx); + if(valueEQ == 0) + continue; + jx = 0; + jjx = ROW_MAT_COLNR(jjx); + for(k = presolve_nextcol(psdata, i, &item); + (jx < jjx) && (item > 0); + k = presolve_nextcol(psdata, i, &item)) { + jx = ROW_MAT_COLNR(k); + /* Do we have a column index match? */ + if(jx == jjx) { + value = ROW_MAT_VALUE(k); + /* Abandon if we have a zero value */ + if(value == 0) + goto NextEQ; + if(ratio == 0.0) { + test = ratio = value / valueEQ; + } + else + test = value / valueEQ; + /* Store nz index */ + nzidx[++nzidx[0]] = k; + break; + } + /* Give up matching if there is overshooting */ + else if(jx > jjx) + goto NextEQ; + } + } + + /* We were successful if the equality was completely traversed */ + if((itemEQ == 0) && (nzidx[0] > 0) && (fabs(test-ratio) < psdata->epsvalue)) { + + /* Check if we have found parametrically indentical constraints */ + if(presolve_rowlength(psdata, i) == presolve_rowlength(psdata,ii)) { + + value = lp->orig_rhs[i]; + valueEQ = lp->orig_rhs[ii]; + + /* Are they both equalities? */ + if(is_constr_type(lp, i, EQ)) { + /* Determine applicable ratio for the RHS */ + if(fabs(valueEQ) < psdata->epsvalue) { + if(fabs(value) < psdata->epsvalue) + test = ratio; + else + test = lp->infinite; + } + else + test = value / valueEQ; + /* Check for infeasibility */ + if(fabs(test-ratio) > psdata->epsvalue) { + report(lp, NORMAL, "presolve_sparser: Infeasibility of relatively equal constraints %d and %d\n", + i, ii); + status = presolve_setstatus(psdata, INFEASIBLE); + goto Finish; + } + /* Otherwise we can delete a redundant constraint */ + else { + removeLink(EQlist, i); + presolve_rowremove(psdata, i, TRUE); + MEMCOPY(&QS[ib-1], &QS[ib], n-ib); + n--; + iConRemove++; + } + } + /* ... if not, then delete the inequality, since the equality dominates */ + else { + /* First verify feasibility of the RHS */ + if((value+psdata->epsvalue < valueEQ) || + (value-get_rh_range(lp, i)-psdata->epsvalue > valueEQ)) { + report(lp, NORMAL, "presolve_sparser: Infeasibility of relatively equal RHS values for %d and %d\n", + i, ii); + status = presolve_setstatus(psdata, INFEASIBLE); + goto Finish; + } + presolve_rowremove(psdata, i, TRUE); + MEMCOPY(&QS[ib-1], &QS[ib], n-ib); + n--; + iConRemove++; + } + } + + /* Otherwise zero-out the target constraint coefficients and update the RHS */ + else { + for(k = 1; k <= nzidx[0]; k++) { + /* We should add recovery data for the zero'ed coefficient here */ + jjx = nzidx[k]; + jx = ROW_MAT_COLNR(jjx); + valptr = &ROW_MAT_VALUE(jjx); + value = *valptr; + *valptr = 0.0; + /* Update counts */ + value = my_chsign(chsign, value); + if(value < 0) { + psdata->rows->negcount[i]--; + psdata->cols->negcount[jx]--; + } + else { + psdata->rows->plucount[i]--; + psdata->cols->plucount[jx]--; + } + iCoeffChanged++; + } + value = ratio * lp->orig_rhs[ii]; + presolve_adjustrhs(psdata, i, value, psdata->epsvalue); + } + } + + } + /* Get next equality index */ +NextEQ: + ix = nextActiveLink(EQlist, ix); + } + +Finish: + FREE(QS); + freeLink(&EQlist); + FREE(nzidx); + + /* Let us condense the matrix if we modified the constraint matrix */ + if(iCoeffChanged > 0) { + mat->row_end_valid = FALSE; + mat_zerocompact(mat); + presolve_validate(psdata, TRUE); +#ifdef PresolveForceUpdateMax + mat_computemax(mat /* , FALSE */); +#endif + psdata->forceupdate = TRUE; + } + + (*nConRemove) += iConRemove; + (*nCoeffChanged) += iCoeffChanged + iObjChanged; + (*nSum) += iCoeffChanged + iObjChanged + iConRemove; + + return( status ); +} + +STATIC int presolve_SOS1(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSOS, int *nSum) +{ + lprec *lp = psdata->lp; + MYBOOL candelete, SOS_GUBactive = FALSE; + int iCoeffChanged = 0, iConRemove = 0, iSOS = 0, + i,ix,iix, j,jx,jjx, status = RUNNING; + LPSREAL Value1; + MATrec *mat = lp->matA; + + for(i = lastActiveLink(psdata->rows->varmap); i > 0; ) { + candelete = FALSE; + Value1 = get_rh(lp, i); + jx = get_constr_type(lp, i); + if((Value1 == 1) && (presolve_rowlength(psdata, i) >= MIN_SOS1LENGTH) && + ((SOS_GUBactive && (jx != GE)) || (!SOS_GUBactive && (jx == LE)))) { + jjx = mat->row_end[i-1]; + iix = mat->row_end[i]; + for(; jjx < iix; jjx++) { + j = ROW_MAT_COLNR(jjx); + if(!isActiveLink(psdata->cols->varmap, j)) + continue; + if(!is_binary(lp, j) || (ROW_MAT_VALUE(jjx) != 1)) + break; + } + if(jjx >= iix) { + char SOSname[16]; + + /* Define a new SOS instance */ + ix = SOS_count(lp) + 1; + sprintf(SOSname, "SOS_%d", ix); + ix = add_SOS(lp, SOSname, 1, ix, 0, NULL, NULL); + if(jx == EQ) + SOS_set_GUB(lp->SOS, ix, TRUE); + Value1 = 0; + jjx = mat->row_end[i-1]; + for(; jjx < iix; jjx++) { + j = ROW_MAT_COLNR(jjx); + if(!isActiveLink(psdata->cols->varmap, j)) + continue; + Value1 += 1; + append_SOSrec(lp->SOS->sos_list[ix-1], 1, &j, &Value1); + } + candelete = TRUE; + iSOS++; + } + } + + /* Get next row and do the deletion of the previous, if indicated */ + ix = i; + i = prevActiveLink(psdata->rows->varmap, i); + if(candelete) { + presolve_rowremove(psdata, ix, TRUE); + iConRemove++; + } + } + if(iSOS) + report(lp, DETAILED, "presolve_SOS1: Converted %5d constraints to SOS1.\n", iSOS); + clean_SOSgroup(lp->SOS, (MYBOOL) (iSOS > 0)); + + (*nCoeffChanged) += iCoeffChanged; + (*nConRemove) += iConRemove; + (*nSOS) += iSOS; + (*nSum) += iCoeffChanged+iConRemove+iSOS; + + return( status ); +} + +STATIC int presolve_boundconflict(presolverec *psdata, int baserowno, int colno) +{ + LPSREAL Value1, Value2; + lprec *lp = psdata->lp; + MATrec *mat = lp->matA; + int ix, item = 0, + status = RUNNING; + + if(baserowno <= 0) do { + ix = presolve_nextrow(psdata, colno, &item); + if(ix < 0) + return( status ); + baserowno = COL_MAT_ROWNR(ix); + } while(presolve_rowlength(psdata, baserowno) != 1); + Value1 = get_rh_upper(lp, baserowno), + Value2 = get_rh_lower(lp, baserowno); + + if(presolve_singletonbounds(psdata, baserowno, colno, &Value2, &Value1, NULL)) { + int iix; + item = 0; + for(ix = presolve_nextrow(psdata, colno, &item); + ix >= 0; ix = presolve_nextrow(psdata, colno, &item)) { + iix = COL_MAT_ROWNR(ix); + if((iix != baserowno) && + (presolve_rowlength(psdata, iix) == 1) && + !presolve_altsingletonvalid(psdata, iix, colno, Value2, Value1)) { + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } + } + } + else + status = presolve_setstatus(psdata, INFEASIBLE); + return( status ); +} + +STATIC int presolve_columns(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nBoundTighten, int *nSum) +{ + lprec *lp = psdata->lp; + 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, item; + LPSREAL Value1; + + for(j = firstActiveLink(psdata->cols->varmap); (j != 0) && (status == RUNNING); ) { + + /* Don't presolve members SOS'es */ + if(SOS_is_member(lp->SOS, 0, j)) { + j = nextActiveLink(psdata->cols->varmap, j); + continue; + } + + /* Initialize */ + countNZ = presolve_collength(psdata, j); + isOFNZ = isnz_origobj(lp, j); + Value1 = get_lowbo(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; + item = 0; + ix = lp->rows + j; + + /* Check if the variable is unused */ + if((countNZ == 0) && !isOFNZ) { + if(Value1 != 0) + report(lp, DETAILED, "presolve_columns: Eliminated unused variable %s\n", + get_col_name(lp,j)); + candelete = TRUE; + } + + /* Check if the variable has a cost, but is not limited by constraints */ + else if((countNZ == 0) && isOFNZ) { + if(lp->orig_obj[j] < 0) + Value1 = get_upbo(lp, j); + if(fabs(Value1) >= lp->infinite) { + report(lp, DETAILED, "presolve_columns: Unbounded variable %s\n", + get_col_name(lp,j)); + status = presolve_setstatus(psdata, UNBOUNDED); + } + else { + /* Fix the value at its best bound */ + report(lp, DETAILED, "presolve_columns: Eliminated trivial variable %s fixed at %g\n", + get_col_name(lp,j), Value1); + candelete = TRUE; + } + } + + /* Check if the variable can be eliminated because it is fixed */ + else if(isOrigFixed(lp, ix)) { + if(countNZ > 0) { + status = presolve_boundconflict(psdata, -1, j); + if(status != RUNNING) + break; + } + report(lp, DETAILED, "presolve_columns: Eliminated variable %s fixed at %g\n", + get_col_name(lp,j), Value1); + candelete = TRUE; + } + +#if 0 + /* Merge OF-constraint column doubleton in equality constraint (if it has + not been captured by the singleton free variable rule above) */ + else if((countNZ == 1) && isOFNZ && + ((i = presolve_nextrow(psdata, j, &item)) >= 0) && + is_constr_type(lp, i = COL_MAT_ROWNR(i), EQ)) { + MATrec *mat = lp->matA; + + /* Merge the constraint into the OF */ + Value1 = lp->orig_obj[j] / get_mat(lp, i, j); + for(jx = mat->row_end[i-1]; jx < mat->row_end[i]; jx++) { + jjx = ROW_MAT_COLNR(jx); + lp->orig_obj[jjx] -= Value1 * ROW_MAT_VALUE(jx); + } + Value2 = lp->orig_rhs[i]; + presolve_adjustrhs(psdata, 0, Value1 * Value2, 0.0); + + /* Verify feasibility */ + Value2 /= get_mat(lp, i, j); + if((Value2 < get_lowbo(lp, j)) || (Value2 > get_upbo(lp, j))) { + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } + + /* Do column (and flag row) deletion */ + presolve_rowremove(psdata, i, TRUE); + psdata->forceupdate = TRUE; + iConRemove++; + candelete = TRUE; + } +#endif + /* Look for opportunity to fix column based on the dual */ + else if(colfixdual && presolve_colfixdual(psdata, j, &Value1, &status)) { + if(my_infinite(lp, Value1)) { + report(lp, DETAILED, "presolve_columns: Unbounded variable %s\n", + get_col_name(lp,j)); + status = presolve_setstatus(psdata, UNBOUNDED); + } + else { + /* Fix the value at its best bound */ + report(lp, DETAILED, "presolve_columns: Eliminated dual-zero variable %s fixed at %g\n", + get_col_name(lp,j), Value1); + candelete = TRUE; + } + } + + /* Do probing of binary variables to see if we can fix them */ + else if(probefix && is_binary(lp, j) && + presolve_probefix01(psdata, j, &Value1)) { + report(lp, DETAILED, "presolve_columns: Fixed binary variable %s at %g\n", + get_col_name(lp,j), Value1); + candelete = TRUE; + } +#if 0 + /* Do probing of binary variables to see if we can tighten their coefficients */ + else if(probereduce && is_binary(lp, j) && + (ix = presolve_probetighten01(psdata, j) > 0)) { + report(lp, DETAILED, "presolve_columns: Tightened coefficients for binary variable %s in %d rows\n", + get_col_name(lp,j), ix); + iCoeffChanged += ix; + psdata->forceupdate = TRUE; + } +#endif + + /* Perform fixing and deletion, if indicated */ + if(candelete) { + + /* If we have a SOS1 member variable fixed at a non-zero value, then we + must fix the other member variables at zero and delete the SOS(es) */ + if((Value1 != 0) && SOS_is_member(lp->SOS, 0, j)) { + ix = iVarFixed; + if(!presolve_fixSOS1(psdata, j, Value1, &iConRemove, &iVarFixed)) + status = presolve_setstatus(psdata, INFEASIBLE); + if(iVarFixed > ix) + psdata->forceupdate = TRUE; + break; + } + else { + if(!presolve_colfix(psdata, j, Value1, TRUE, &iVarFixed)) { + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } + j = presolve_colremove(psdata, j, TRUE); + } + } + else + j = nextActiveLink(psdata->cols->varmap, j); + } + + /* Remove any "hanging" empty row and columns */ + if(status == RUNNING) + status = presolve_shrink(psdata, &iConRemove, &iVarFixed); + + (*nCoeffChanged) += iCoeffChanged; + (*nConRemove) += iConRemove; + (*nVarFixed) += iVarFixed; + (*nBoundTighten) += iBoundTighten; + (*nSum) += iCoeffChanged+iConRemove+iVarFixed+iBoundTighten; + + return( status ); +} + +STATIC int presolve_freeandslacks(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSum) +{ + lprec *lp = psdata->lp; + MYBOOL isOFNZ, unbounded, + impliedfree = is_presolve(lp, PRESOLVE_IMPLIEDFREE), + impliedslack = is_presolve(lp, PRESOLVE_IMPLIEDSLK); + int iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, + status = RUNNING, i, ix, j, countNZ; + LPSREAL coeff_bl, coeff_bu; + MATrec *mat = lp->matA; + + if(impliedfree || impliedslack) + for(j = firstActiveLink(psdata->cols->varmap); j != 0; ) { + + /* Check and initialize */ + if((presolve_collength(psdata, j) != 1) || + is_int(lp, j) || is_semicont(lp, j) || + !presolve_candeletevar(psdata, j)) { + j = nextActiveLink(psdata->cols->varmap, j); + continue; + } + ix = 0; + i = COL_MAT_ROWNR(presolve_nextrow(psdata, j, &ix)); + isOFNZ = isnz_origobj(lp, j); + countNZ = presolve_rowlength(psdata, i); + coeff_bu = get_upbo(lp, j); + coeff_bl = get_lowbo(lp, j); + unbounded = my_infinite(lp, coeff_bl) && my_infinite(lp, coeff_bu); + ix = lp->rows + j; + + /* Eliminate singleton free variable and its associated constraint */ + if(impliedfree && unbounded && + presolve_impliedcolfix(psdata, i, j, TRUE)) { + report(lp, DETAILED, "presolve_freeandslacks: Eliminated free variable %s and row %s\n", + get_col_name(lp, j), get_row_name(lp, i)); + presolve_rowremove(psdata, i, TRUE); + iConRemove++; + j = presolve_colremove(psdata, j, TRUE); + iVarFixed++; + } + + /* Check for implied slack variable in equality constraint */ + else if(impliedslack && + (countNZ > 1) && + is_constr_type(lp, i, EQ) && + presolve_impliedcolfix(psdata, i, j, FALSE)) { + report(lp, DETAILED, "presolve_freeandslacks: Eliminated implied slack variable %s via row %s\n", + get_col_name(lp, j), get_row_name(lp, i)); + psdata->forceupdate = TRUE; + j = presolve_colremove(psdata, j, TRUE); + iVarFixed++; + } + + /* Check for implied (generalized) slack variable in inequality constraint */ + else if(impliedslack && !isOFNZ && + my_infinite(lp, coeff_bu) && /* Consider removing this test */ +#if 0 /* Force zero-bounded implicit slack */ + (coeff_bl == 0)) && +#else + !my_infinite(lp, coeff_bl) && +#endif + (countNZ > 1) && + !is_constr_type(lp, i, EQ)) { + LPSREAL *target, + ValueA = COL_MAT_VALUE(presolve_lastrow(psdata, j)); +#if 0 + coeff_bu = get_rh_upper(lp, i); + coeff_bl = get_rh_lower(lp, i); + if(!presolve_singletonbounds(psdata, i, j, &coeff_bl, &coeff_bu, &ValueA)) { + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } +#endif + if((coeff_bl != 0) && !my_infinite(lp, coeff_bl) && !my_infinite(lp, coeff_bu)) + coeff_bu -= coeff_bl; + + /* If the coefficient is negative, reduce the lower bound / increase range */ + if(ValueA > 0) { + target = &lp->orig_upbo[i]; + if(!my_infinite(lp, *target)) { + if(my_infinite(lp, coeff_bu)) { + *target = lp->infinite; + psdata->forceupdate = TRUE; + } + else { + *target += ValueA * coeff_bu; + *target = presolve_roundrhs(lp, *target, FALSE); + } + } + } + /* Otherwise see if the upper bound should be changed */ + else { + target = &lp->orig_rhs[i]; + if(my_infinite(lp, coeff_bu) || my_infinite(lp, *target)) { + /* Do we suddenly find that the constraint becomes redundant? (e226.mps) */ + if(my_infinite(lp, lp->orig_upbo[i])) { + presolve_rowremove(psdata, i, TRUE); + iConRemove++; + } + /* Or does the upper bound of a ranged constraint become Inf? */ + else { + *target -= lp->orig_upbo[i]; + *target = -(*target); + mat_multrow(mat, i, -1); + lp->orig_upbo[i] = lp->infinite; + psdata->forceupdate = TRUE; + } + } + else { + *target -= ValueA * coeff_bu; + *target = presolve_roundrhs(lp, *target, FALSE); + } + } + presolve_colfix(psdata, j, coeff_bl, TRUE, &iVarFixed); + report(lp, DETAILED, "presolve_freeandslacks: Eliminated duplicate slack variable %s via row %s\n", + get_col_name(lp, j), get_row_name(lp, i)); + j = presolve_colremove(psdata, j, TRUE); + } + + /* Go to next column */ + else + j = nextActiveLink(psdata->cols->varmap, j); + } + + (*nCoeffChanged) += iCoeffChanged; + (*nConRemove) += iConRemove; + (*nVarFixed) += iVarFixed; + (*nSum) += iCoeffChanged+iConRemove+iVarFixed; + + return( status ); +} + +STATIC int presolve_preparerows(presolverec *psdata, int *nBoundTighten, int *nSum) +{ + lprec *lp = psdata->lp; + 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; + MATrec *mat = lp->matA; + + for(i = lastActiveLink(psdata->rows->varmap); i > 0; i = prevActiveLink(psdata->rows->varmap, i)) { + + /* First identify any full row infeasibilities */ + j = presolve_rowlengthex(psdata, i); +#ifdef Paranoia + if(!presolve_testrow(psdata, nextActiveLink(psdata->rows->varmap, i))) { +#else + if((j > 1) && !psdata->forceupdate && !presolve_rowfeasible(psdata, i, FALSE)) { +#endif + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } + + /* Do bound (LHS) or constraint range (RHS) tightening if we will later identify + implied free variables (tends to produce degeneracy otherwise) */ + if(impliedfree && (j > 1) && mat_validate(mat)){ + + /* Look for opportunity to tighten constraint bounds (and check for feasibility again) */ + presolve_range(lp, i, psdata->rows, &losum, &upsum); + lorhs = get_rh_lower(lp, i); + uprhs = get_rh_upper(lp, i); + if((losum > MIN(upsum, uprhs)+epsvalue) || + (upsum < MAX(losum, lorhs)-epsvalue)) { + report(lp, NORMAL, "presolve_preparerows: Variable bound / constraint value infeasibility in row %s.\n", + get_row_name(lp, i)); + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } + + if(losum > lorhs+epsvalue) { + set_rh_lower(lp, i, presolve_roundrhs(lp, losum, TRUE)); + iRangeTighten++; + } + if(upsum < uprhs-epsvalue) { + set_rh_upper(lp, i, presolve_roundrhs(lp, upsum, FALSE)); + iRangeTighten++; + } + } + + /* Seek to tighten bounds on individual variables */ + if(tightenbounds && mat_validate(mat)) { +#if 1 + if(j > 1) + status = presolve_rowtighten(psdata, i, &iBoundTighten, FALSE); +#else + if((MIP_count(lp) > 0) && (j > 1)) + status = presolve_rowtighten(psdata, i, &iBoundTighten, TRUE); +#endif + } + + /* Look for opportunity to convert ranged constraint to equality-type */ + if(!is_constr_type(lp, i, EQ) && (get_rh_range(lp, i) < epsvalue)) { + presolve_setEQ(psdata, i); + iRangeTighten++; + } + } + + psdata->forceupdate |= (MYBOOL) (iBoundTighten > 0); + (*nBoundTighten) += iBoundTighten+iRangeTighten; + (*nSum) += iBoundTighten+iRangeTighten; + + return( status ); +} + +STATIC int presolve_rows(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nBoundTighten, int *nSum) +{ + lprec *lp = psdata->lp; + 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; + MATrec *mat = lp->matA; + + for(i = lastActiveLink(psdata->rows->varmap); (i > 0) && (status == RUNNING); ) { + + candelete = FALSE; + + /* First identify any full row infeasibilities + Note: Handle singletons below to ensure that conflicting multiple singleton + rows with this variable do not provoke notice of infeasibility */ + j = presolve_rowlengthex(psdata, i); + if((j > 1) && + !psdata->forceupdate && !presolve_rowfeasible(psdata, i, FALSE)) { + status = presolve_setstatus(psdata, INFEASIBLE); + break; + } + presolve_range(lp, i, psdata->rows, &losum, &upsum); + lorhs = get_rh_lower(lp, i); + uprhs = get_rh_upper(lp, i); +#ifdef Paranoia + if((losum>uprhs+epsvalue) || (upsum= -epsvalue)) { +#else /* Version that deletes bound-fixed columns here */ + if((j == 1) && (uprhs-lorhs >= -epsvalue)) { +#endif + item = 0; + jx = presolve_nextcol(psdata, i, &item); + j = ROW_MAT_COLNR(jx); + + /* Make sure we don't have conflicting other singleton rows with this variable */ + Value1 = lp->infinite; + Value2 = -Value1; + if(presolve_collength(psdata, j) > 1) + status = presolve_boundconflict(psdata, i, j); + else if(is_constr_type(lp, i, EQ)) { + Value2 = ROW_MAT_VALUE(jx); + Value1 = lp->orig_rhs[i] / Value2; + if(Value2 < 0) + swapREAL(&losum, &upsum); + if((Value1 < losum / my_if(my_infinite(lp, losum), my_sign(Value2), Value2) - epsvalue) || + (Value1 > upsum / my_if(my_infinite(lp, upsum), my_sign(Value2), Value2) + epsvalue)) + status = presolve_setstatus(psdata, INFEASIBLE); + Value2 = Value1; + } + + /* Proceed to fix and remove variable (if it is not a SOS member) */ + if(status == RUNNING) { + if((fabs(Value2-Value1) < epsvalue) && (fabs(Value2) > epsvalue)) { + 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)) + status = presolve_setstatus(psdata, INFEASIBLE); + psdata->forceupdate = TRUE; + } + else { + if(!presolve_colfix(psdata, j, Value1, (MYBOOL) !isSOS, NULL)) + status = presolve_setstatus(psdata, INFEASIBLE); + else if(isSOS && !deleteSOS) + iBoundTighten++; + else { + presolve_colremove(psdata, j, TRUE); + iVarFixed++; + } + } + } + else + status = presolve_colsingleton(psdata, i, j, &iBoundTighten); + } + if(status == INFEASIBLE) { + break; + } + if(psdata->forceupdate != AUTOMATIC) { + /* Store dual recovery information and code for deletion */ + presolve_storeDualUndo(psdata, i, j); + candelete = TRUE; + } + } + + /* Delete non-empty rows and variables that are completely determined at zero */ + else if((j > 0) /* Only examine non-empty rows, */ + && (fabs(lp->orig_rhs[i]) < epsvalue) /* .. and the current RHS is zero, */ + && ((psdata->rows->plucount[i] == 0) || + (psdata->rows->negcount[i] == 0)) /* .. and the parameter signs are all equal, */ + && (psdata->rows->pluneg[i] == 0) /* .. and no (quasi) free variables, */ + && (is_constr_type(lp, i, EQ) +#ifdef FindImpliedEqualities + || (fabs(lorhs-upsum) < epsvalue) /* Convert to equalities */ + || (fabs(uprhs-losum) < epsvalue) /* Convert to equalities */ +#endif + ) + ) { + + /* Delete the columns we can delete */ + status = presolve_rowfixzero(psdata, i, &iVarFixed); + + /* Then delete the row, which is redundant */ + if(status == RUNNING) + candelete = TRUE; + } + + + /* Check if we have a constraint made redundant through bounds on individual + variables; such constraints are often referred to as "forcing constraints" */ + else if((losum >= lorhs-epsvalue) && + (upsum <= uprhs+epsvalue)) { + + /* Check if we can also fix all the variables */ + if(fabs(losum-upsum) < epsvalue) { + item = 0; + jx = presolve_nextcol(psdata, i, &item); + while((status == RUNNING) && (jx >= 0)) { + j = ROW_MAT_COLNR(jx); + Value1 = get_lowbo(lp, j); + if(presolve_colfix(psdata, j, Value1, TRUE, &iVarFixed)) { + presolve_colremove(psdata, j, TRUE); + iVarFixed++; + jx = presolve_nextcol(psdata, i, &item); + } + else + status = presolve_setstatus(psdata, INFEASIBLE); + } + } + candelete = TRUE; + } + + /* Get next row and do the deletion of the previous, if indicated */ + ix = i; + i = prevActiveLink(psdata->rows->varmap, i); + if(candelete) { + presolve_rowremove(psdata, ix, TRUE); + iConRemove++; + } + } + + /* Remove any "hanging" empty row and columns */ + if(status == RUNNING) + status = presolve_shrink(psdata, &iConRemove, &iVarFixed); + + (*nCoeffChanged) += iCoeffChanged; + (*nConRemove) += iConRemove; + (*nVarFixed) += iVarFixed; + (*nBoundTighten) += iBoundTighten; + (*nSum) += iCoeffChanged+iConRemove+iVarFixed+iBoundTighten; + + return( status ); +} + +/* Top level presolve routine */ +STATIC int presolve(lprec *lp) +{ + int status = RUNNING, + 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]; + presolverec *psdata = NULL; + MATrec *mat = lp->matA; + +#if 0 + lp->do_presolve = PRESOLVE_ROWS; + report(lp, IMPORTANT, "presolve: Debug override of presolve setting to %d\n", lp->do_presolve); +#endif + + /* Lock the variable mapping arrays and counts ahead of any row/column + deletion or creation in the course of presolve, solvelp or postsolve */ + if(!lp->varmap_locked) + varmap_lock(lp); + + /* Check if we have already done presolve */ + mat_validate(mat); + if(lp->wasPresolved) { + if(SOS_count(lp) > 0) { + SOS_member_updatemap(lp->SOS); + make_SOSchain(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)); + } + if((lp->solvecount > 1) && (lp->bb_level < 1) && + ((lp->scalemode & SCALE_DYNUPDATE) != 0)) + auto_scale(lp); + if(!lp->basis_valid) { + crash_basis(lp); + report(lp, DETAILED, "presolve: Had to repair broken basis.\n"); + } + lp->timepresolved = timeNow(); + return(status); + } + + /* 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); + lp->sos_vars = SOS_memberships(lp->SOS, 0); + } + REPORT_modelinfo(lp, TRUE, "SUBMITTED"); + report(lp, NORMAL, " \n"); + if(i > 0) + lp->sos_vars = 0; + + /* Finalize basis indicators; if no basis was created earlier via + set_basis or crash_basis then simply set the default basis. */ + if(!lp->basis_valid) + lp->var_basic[0] = AUTOMATIC; /* Flag that we are presolving */ + +#if 0 +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 + Value1 = fabs(lp->negrange); + if(is_obj_in_basis(lp) && (mat->dynrange < Value1) && vec_computeext(lp->orig_obj, 1, lp->columns, TRUE, &i, &j)) { + + /* Compute relative scale metric */ + Value2 = fabs(lp->orig_obj[j]/lp->orig_obj[i]) / mat->dynrange; + if(Value2 < 1.0) + Value2 = 1.0 / Value2; + + /* Determine if we should alert modeler and possibly move the OF out of the coefficient matrix */ + if((Value2 > Value1) /* Case with extreme scale difference */ +#if 1 + || (mat->dynrange == 1.0) /* Case where we have an all-unit coefficient matrix, possibly totally unimodular */ +#endif + ) + 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"); + } + else if(mat->dynrange > 1.0) + report(lp, IMPORTANT, "Warning: Objective/matrix coefficient magnitude differences will cause inaccuracy!\n"); + } +#endif + + /* Do traditional simple presolve */ + yieldformessages(lp); + if((lp->do_presolve & PRESOLVE_LASTMASKMODE) == PRESOLVE_NONE) { + mat_checkcounts(mat, NULL, NULL, TRUE); + i = 0; + } + else { + + if(lp->full_solution == NULL) + allocREAL(lp, &lp->full_solution, lp->sum_alloc+1, TRUE); + + /* Identify infeasible SOS'es prior to any pruning */ + j = 0; + 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); + j++; + } + } + if(j > 0) + goto Finish; + + /* Create and initialize the presolve data structures */ + psdata = presolve_init(lp); + + /* Reentry point for the outermost, computationally expensive presolve loop */ + psdata->outerloops = 0; + do { + psdata->outerloops++; + iCoeffChanged = 0; + iConRemove = 0; + iVarFixed = 0; + iBoundTighten = 0; + iSOS = 0; + oSum = nSum; + + /* Do the middle elimination loop */ + do { + psdata->middleloops++; + nSum += iSum; + iSum = 0; + + /* Accumulate constraint bounds based on bounds on individual variables. */ + j = 0; + while(presolve_statuscheck(psdata, &status) && psdata->forceupdate) { + psdata->forceupdate = FALSE; + /* Update sums, but limit iteration count to avoid possible + "endless" loops with only marginal bound improvements */ + if(presolve_updatesums(psdata) && (j < MAX_PSBOUNDTIGHTENLOOPS)) { + /* Do row preparation useful for subsequent column and row presolve operations */ + if((psdata->outerloops == 1) && (psdata->middleloops == 1)) + status = presolve_preparerows(psdata, &iBoundTighten, &iSum); + nBoundTighten += iBoundTighten; + iBoundTighten = 0; + nSum += iSum; + iSum = 0; + j++; + if(status != RUNNING) + report(lp, NORMAL, "presolve: Break after bound tightening iteration %d.\n", j); + } + } + if(status != RUNNING) + break; + + /* Do the relatively cheap innermost elimination loop */ + do { + + psdata->innerloops++; + nSum += iSum; + iSum = 0; + + /* Eliminate empty rows, convert row singletons to bounds, + tighten bounds, and remove always satisfied rows */ + if(presolve_statuscheck(psdata, &status) && + is_presolve(lp, PRESOLVE_ROWS)) + status = presolve_rows(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iBoundTighten, &iSum); + + /* Eliminate empty or fixed columns (including trivial OF column singletons) */ + if(presolve_statuscheck(psdata, &status) && + is_presolve(lp, PRESOLVE_COLS)) + status = presolve_columns(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iBoundTighten, &iSum); + + /* Presolve SOS'es if possible (always do this) */ + if(presolve_statuscheck(psdata, &status)) + status = presolve_redundantSOS(psdata, &iBoundTighten, &iSum); + + } while((status == RUNNING) && (iSum > 0)); + if(status != RUNNING) + break; + + /* Merge compatible similar rows; loop backwards over every row */ + if(presolve_statuscheck(psdata, &status) && + (psdata->outerloops == 1) && (psdata->middleloops <= MAX_PSMERGELOOPS) && + is_presolve(lp, PRESOLVE_MERGEROWS)) + status = presolve_mergerows(psdata, &iConRemove, &iSum); + + /* Eliminate dominated rows */ + if(presolve_statuscheck(psdata, &status) && + is_presolve(lp, PRESOLVE_ROWDOMINATE)) + presolve_rowdominance(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum); + + /* See if we can convert some constraints to SOSes (only SOS1 handled) */ + if(presolve_statuscheck(psdata, &status) && (MIP_count(lp) > 0) && + is_presolve(lp, PRESOLVE_SOS)) + status = presolve_SOS1(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSOS, &iSum); + + /* Eliminate dominated columns in set coverage models */ + if(presolve_statuscheck(psdata, &status) && (lp->int_vars > 1) && + is_presolve(lp, PRESOLVE_COLDOMINATE)) + presolve_coldominance01(psdata, &iConRemove, &iVarFixed, &iSum); + + /* Aggregate compatible columns */ + if(presolve_statuscheck(psdata, &status) && /*TRUE ||*/ + is_presolve(lp, PRESOLVE_AGGREGATE)) + presolve_aggregate(psdata, &iConRemove, &iVarFixed, &iSum); + + /* Eliminate free variables and implied slacks */ + if(presolve_statuscheck(psdata, &status) && +/* !is_presolve(lp, PRESOLVE_ELIMEQ2) && */ + is_presolve(lp, PRESOLVE_IMPLIEDSLK | PRESOLVE_IMPLIEDFREE)) + status = presolve_freeandslacks(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum); + + } while((status == RUNNING) && (iSum > 0)); + if(status != RUNNING) + break; + + /* Check if we can do elimination of rank-deficient equality constraints */ + if(presolve_statuscheck(psdata, &status) && (psdata->EQmap->count > 1) && + is_presolve(lp, PRESOLVE_LINDEP)) { +#if 0 + REPORT_mat_mmsave(lp, "A.mtx", NULL, FALSE, "Constraint matrix A"); +#endif + presolve_singularities(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum); + } + + /* Eliminate variable and tighten bounds using 2-element EQs; + note that this involves modifying the coefficients of A and + can therefore be a slow operation. */ + if(presolve_statuscheck(psdata, &status) && + is_presolve(lp, PRESOLVE_ELIMEQ2)) { + jjx = 0; + do { + jjx += iSum; + status = presolve_elimeq2(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum); + } while((status == RUNNING) && (iSum > jjx)); + iSum = jjx; + +#if 0 + /* Eliminate free variables and implied slacks */ + if(presolve_statuscheck(psdata, &status) && + is_presolve(lp, PRESOLVE_IMPLIEDSLK | PRESOLVE_IMPLIEDFREE)) + status = presolve_freeandslacks(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum); +#endif + } + + /* Increase A matrix sparsity by discovering common subsets using EQs */ + if(presolve_statuscheck(psdata, &status) && (psdata->EQmap->count > 0) && + is_presolve(lp, PRESOLVE_SPARSER)) + status = presolve_makesparser(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum); + + /* Do GCD-based coefficient reductions (also does row scaling, + even if no rhs INT truncations are possible) */ + if(presolve_statuscheck(psdata, &status) && (psdata->INTmap->count > 0) && + is_presolve(lp, PRESOLVE_REDUCEGCD)) + if(!presolve_reduceGCD(psdata, &iCoeffChanged, &iBoundTighten, &iSum)) + status = presolve_setstatus(psdata, INFEASIBLE); + + /* Simplify knapsack or set coverage models where OF coefficients are + duplicated in the constraints. At the cost of adding helper columns, this + increases sparsity and facilitates identification of lower and upper bounds. */ + if(presolve_statuscheck(psdata, &status) && + is_presolve(lp, PRESOLVE_KNAPSACK)) { + i = iCoeffChanged; + status = presolve_knapsack(psdata, &iCoeffChanged); + } + + /* Remove any "hanging" empty row and columns */ + if(status == RUNNING) + status = presolve_shrink(psdata, &iConRemove, &iVarFixed); + + nCoeffChanged += iCoeffChanged; + nConRemove += iConRemove; + nVarFixed += iVarFixed; + nBoundTighten += iBoundTighten; + 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)) && + (psdata->outerloops < get_presolveloops(lp)) && + (psdata->rows->varmap->count+psdata->cols->varmap->count > 0)); + + /* Finalize presolve */ +#ifdef Paranoia + 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)) + jjx = presolve_makefree(psdata); + else + jjx = 0; + + + /* Finalize the presolve */ + if(!presolve_finalize(psdata)) + report(lp, SEVERE, "presolve: Unable to construct internal data representation\n"); + + /* Report summary information */ + i = NORMAL; + iVarFixed = lp->presolve_undo->orig_columns - psdata->cols->varmap->count; + iConRemove = lp->presolve_undo->orig_rows - psdata->rows->varmap->count; + if(nSum > 0) + report(lp, i, "PRESOLVE Elimination loops performed.......... O%d:M%d:I%d\n", + psdata->outerloops, psdata->middleloops, psdata->innerloops); + if(nVarFixed) + report(lp, i, " %8d empty or fixed variables............. %s.\n", nVarFixed, "REMOVED"); + if(nConRemove) + report(lp, i, " %8d empty or redundant constraints....... %s.\n", nConRemove, "REMOVED"); + if(nBoundTighten) + report(lp, i, " %8d bounds............................... %s.\n", nBoundTighten, "TIGHTENED"); + if(nCoeffChanged) + report(lp, i, " %8d matrix coefficients.................. %s.\n", nCoeffChanged, "CHANGED"); + if(jjx > 0) + report(lp, i, " %8d variables' final bounds.............. %s.\n", jjx, "RELAXED"); + if(nSOS) + report(lp, i, " %8d constraints detected as SOS1......... %s.\n", nSOS, "CONVERTED"); + + /* Report optimality or infeasibility */ + if(status == UNBOUNDED) + report(lp, NORMAL, "%20s Solution status detected............. %s.\n", "", "UNBOUNDED"); + else if(status == INFEASIBLE) + report(lp, NORMAL, "%20s Solution status detected............. %s.\n", "", "INFEASIBLE"); + else { + if(psdata->cols->varmap->count == 0) + Value1 = Value2 = lp->presolve_undo->fixed_rhs[0] -initrhs0; + else + presolve_rangeorig(lp, 0, psdata->rows, &Value1, &Value2, -initrhs0); + if((fabs(Value1 - Value2) < psdata->epsvalue) || (fabs(my_reldiff(Value1, Value2)) < psdata->epsvalue)) { + 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"); + else + sprintf(lonum, "%+12g", Value1); + if(my_infinite(lp, Value2)) + sprintf(upnum, "%-13s", "Inf"); + else + sprintf(upnum, "%+-12g", Value2); + report(lp, i, "%20s [ %s < Z < %s ]\n", "", lonum, upnum); + } + + /* Update values for dual limit and best heuristic values */ + if((MIP_count(lp) > 0) || (get_Lrows(lp) > 0)) { + if(is_maxim(lp)) { + SETMAX(lp->bb_heuristicOF, Value1); + SETMIN(lp->bb_limitOF, Value2); + } + else { + SETMIN(lp->bb_heuristicOF, Value2); + SETMAX(lp->bb_limitOF, Value1); + } + } + } + report(lp, NORMAL, " \n"); + + /* Clean up (but save counts of constraint types for display later) */ + j = psdata->LTmap->count; + jx = psdata->EQmap->count; + jjx = lp->rows - j - jx; + presolve_free(&psdata); + + } + + /* Signal that we are done presolving */ + if((lp->usermessage != NULL) && + ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != 0) && (lp->msgmask & MSG_PRESOLVE)) + lp->usermessage(lp, lp->msghandle, MSG_PRESOLVE); + + /* Create master SOS variable list */ + if(SOS_count(lp) > 0) { + /*SOS_member_updatemap(lp->SOS); */ + make_SOSchain(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)); + } + + /* Finalize model not identified as infeasible or unbounded */ + if(status == RUNNING) { + + /* Resolve GUBs */ + if(is_bb_mode(lp, NODE_GUBMODE)) + identify_GUB(lp, TRUE); + +#if 0 + /* Mark rows containing hidden identity matrices so that supporting factorization + engines can use this structural information to boost efficiency */ + if(is_algopt(lp, ALGOPT_COMPACTBPF)) + lp->bfpoptimize = (MYBOOL) (assist_factorization(lp, ROWTYPE_LOGICAL, + &lp->rowset1, &lp->rowno1) > 0); +#endif + + /* Scale the model based on current settings */ + auto_scale(lp); + + /* Crash the basis, if specified */ + crash_basis(lp); + + /* Produce presolved model statistics */ + if(nConRemove+nVarFixed+nBoundTighten+nVarFixed+nCoeffChanged > 0) { + REPORT_modelinfo(lp, FALSE, "REDUCED"); + if(nSum > 0) { + report(lp, NORMAL, "Row-types: %7d LE, %7d GE, %7d EQ.\n", + j, jjx, jx); + report(lp, NORMAL, " \n"); + } + } + } + + /* Optionally produce data on constraint classes */ + if(lp->verbose > NORMAL) { + report(lp, NORMAL, " \n"); + REPORT_constraintinfo(lp, "CONSTRAINT CLASSES"); + report(lp, NORMAL, " \n"); + } + +Finish: + lp->wasPresolved = TRUE; + lp->timepresolved = timeNow(); + +#if 0 +/* write_mps(lp, "test_out.mps"); */ /* Must put here due to variable name mapping */ + write_lp(lp, "test_out.lp"); /* Must put here due to variable name mapping */ +#endif +#if 0 + REPORT_debugdump(lp, "testint2.txt", FALSE); +#endif + + return( status ); + +} + +STATIC MYBOOL postsolve(lprec *lp, int status) +{ + /* Verify solution */ + if(lp->lag_status != RUNNING) { + int itemp; + + if(status == PRESOLVED) + status = OPTIMAL; + + if((status == OPTIMAL) || (status == SUBOPTIMAL)) { + itemp = check_solution(lp, lp->columns, lp->best_solution, + lp->orig_upbo, lp->orig_lowbo, lp->epssolution); + if((itemp != OPTIMAL) && (lp->spx_status == OPTIMAL)) + lp->spx_status = itemp; + else if((itemp == OPTIMAL) && ((status == SUBOPTIMAL) || (lp->spx_status == PRESOLVED))) + lp->spx_status = status; + } + else if(status != PRESOLVED) { + report(lp, NORMAL, "lp_solve unsuccessful after %.0f iter and a last best value of %g\n", + (double) get_total_iter(lp), lp->best_solution[0]); + if(lp->bb_totalnodes > 0) + report(lp, NORMAL, "lp_solve explored %.0f nodes before termination\n", + (double) get_total_nodes(lp)); + } + else + lp->spx_status = OPTIMAL; + + /* Only rebuild primal solution here, since the dual is only computed on request */ + presolve_rebuildUndo(lp, TRUE); + } + + /* Check if we can clear the variable map */ + if(varmap_canunlock(lp)) + lp->varmap_locked = FALSE; +#if 0 + REPORT_mat_mmsave(lp, "basis.mtx", NULL, FALSE); /* Write the current basis matrix (no OF) */ +#endif + + return( TRUE ); +} diff --git a/src/external/lpsolve/build/lp_solve/lp_price.c b/src/external/lpsolve/build/lp_solve/lp_price.c new file mode 100644 index 00000000..3eb5142d --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_price.c @@ -0,0 +1,2107 @@ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_report.h" +#include "lp_pricePSE.h" +#include "lp_price.h" + +#if libBLAS > 0 + #include "myblas.h" +#endif + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +/* Simplex pricing utility module - w/interface for lp_solve v5.0+ + ------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, commonlib.h + + Release notes: + v1.0.0 1 July 2004 Routines extracted from lp_lib. + v1.0.1 10 July 2004 Added comparison operators for determination + of entering and leaving variables. + Added routines for multiple and partial + pricing and made corresponding changes to + colprim and rowdual. + v1.0.2 20 August 2004 Implemented relative pivot size control in + rowprim and rowdual. + v1.1.0 15 October 2004 Added dual long step logic. + v1.1.1 22 October 2004 Added bound sort order to variable selections. + v1.2.0 24 March 2005 Completed multiple pricing logic. + ------------------------------------------------------------------------- */ + + +/* Comparison operators for entering and leaving variables for both the primal and + dual simplexes. The functions compare a candidate variable with an incumbent. */ +int CMP_CALLMODEL compareImprovementVar(const pricerec *current, const pricerec *candidate) +{ + register int result = COMP_PREFERNONE; + register lprec *lp = current->lp; + register LPSREAL 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) { + + MYBOOL candbetter; + + /* Find the largest value - normalize in case of the dual, since + constraint violation is expressed as a negative number. */ + /* Use absolute test for "small numbers", relative otherwise */ + testvalue = candidate->pivot; + if(fabs(testvalue) < LIMIT_ABS_REL) + testvalue -= current->pivot; + else + testvalue = my_reldiff(testvalue, current->pivot); + if(isdual) + testvalue = -testvalue; + + candbetter = (MYBOOL) (testvalue > 0); + if(candbetter) { + if(testvalue > margin) + result = COMP_PREFERCANDIDATE; + } +#if 0 /* Give more opportunity to optimize on non-primary criteria */ + else if (testvalue < -margin) +#else /* Give reduced opportunity to optimize on non-primary criteria */ + else if (testvalue < -lp->epsvalue) +#endif + result = COMP_PREFERINCUMBENT; + +#ifdef UseSortOnBound + /* Extra selection criterion based on the variable's range; + variable with - DUAL: small bound out; PRIMAL: large bound in */ + if(result == COMP_PREFERNONE) { + testvalue = lp->upbo[candidatevarno] - lp->upbo[currentvarno]; + if(testvalue < -margin) + result = COMP_PREFERINCUMBENT; + else if(testvalue > margin) + result = COMP_PREFERCANDIDATE; + result = my_chsign(isdual, result); + } +#endif + +#ifdef UseSortOnColumnLength + /* Prevent long columns from entering the basis */ + if(result == COMP_PREFERNONE) { + if(candidatecolno > 0) + testvalue = mat_collength(lp->matA, candidatecolno) + + (is_obj_in_basis(lp) && (lp->obj[candidatecolno] != 0) ? 1 : 0); + else + testvalue = 1; + if(currentcolno > 0) + testvalue -= mat_collength(lp->matA, currentcolno) + + (is_obj_in_basis(lp) && (lp->obj[currentcolno] != 0) ? 1 : 0); + else + testvalue -= 1; + if(testvalue > 0) + result = COMP_PREFERINCUMBENT; + else if(testvalue < 0) + result = COMP_PREFERCANDIDATE; + result = my_chsign(isdual, result); + } +#endif + + /* Select absolute best if the non-primary criteria failed to separate */ + if((result == COMP_PREFERNONE) && candbetter) { + result = COMP_PREFERCANDIDATE; + goto Finish; + } + } + + /* Final tie-breakers */ + if(result == COMP_PREFERNONE) { + + /* Add randomization tie-braker */ + if(lp->piv_strategy & PRICE_RANDOMIZE) { + result = my_sign(PRICER_RANDFACT - rand_uniform(lp, 1.0)); + if(candidatevarno < currentvarno) + result = -result; + } + + /* Resolve ties via index ordinal */ + if(result == COMP_PREFERNONE) { + if(candidatevarno < currentvarno) + result = COMP_PREFERCANDIDATE; + else /* if(candidatevarno > currentvarno) */ + result = COMP_PREFERINCUMBENT; + if(lp->_piv_left_) + result = -result; + } + } + +Finish: + return( result ); + +} + +int CMP_CALLMODEL compareSubstitutionVar(const pricerec *current, const pricerec *candidate) +{ + register int result = COMP_PREFERNONE; + register lprec *lp = current->lp; + register LPSREAL testvalue = candidate->theta, + margin = current->theta; + MYBOOL isdual = candidate->isdual, candbetter; + 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) { + testvalue = fabs(testvalue); + margin = fabs(margin); + } + + /* Use absolute test for "small numbers", relative otherwise */ + if(fabs(testvalue) < LIMIT_ABS_REL) + testvalue -= margin; + else + testvalue = my_reldiff(testvalue, margin); + + /* Find if the new Theta is smaller or near equal (i.e. testvalue <= eps) + compared to the previous best; ties will be broken by pivot size or index + NB! The margin below is essential in maintaining primal/dual feasibility + during the primal/dual simplex, respectively. Sometimes a small + value prevents the selection of a suitable pivot, thereby weakening + the numerical stability of some models */ + margin = PREC_SUBSTFEASGAP; + candbetter = (MYBOOL) (testvalue < 0); + if(candbetter) { + if(testvalue < -margin) + result = COMP_PREFERCANDIDATE; + } + else if(testvalue > margin) + result = COMP_PREFERINCUMBENT; + + /* Resolve a tie */ + if(result == COMP_PREFERNONE) { + LPSREAL currentpivot = fabs(current->pivot), + candidatepivot = fabs(candidate->pivot); + + /* Handle first index / Bland's rule specially */ + if(lp->_piv_rule_ == PRICER_FIRSTINDEX) { +#if 1 + /* Special secondary selection by pivot size (limited stability protection) */ + margin = candidate->epspivot; + if((candidatepivot >= margin) && (currentpivot < margin)) + result = COMP_PREFERCANDIDATE; +#endif + } + + else { + + /* General secondary selection based on pivot size */ +#if 0 + if(candidatepivot > MIN_STABLEPIVOT) + testvalue = my_reldiff(testvalue, currentpivot); + else +#endif + testvalue = candidatepivot - currentpivot; + if(testvalue > margin) + result = COMP_PREFERCANDIDATE; + else if(testvalue < -margin) + result = COMP_PREFERINCUMBENT; + +#ifdef UseSortOnBound + /* Extra selection criterion based on the variable's range; + variable with - PRIMAL: small bound out; DUAL: large bound in */ + if(result == COMP_PREFERNONE) { + testvalue = lp->upbo[candidatevarno] - lp->upbo[currentvarno]; + if(testvalue < -margin) + result = COMP_PREFERCANDIDATE; + else if(testvalue > margin) + result = COMP_PREFERINCUMBENT; + result = my_chsign(isdual, result); + } +#endif + +#ifdef UseSortOnColumnLength + /* Prevent long columns from entering the basis */ + if(result == COMP_PREFERNONE) { + if(candidatecolno > 0) + testvalue = mat_collength(lp->matA, candidatecolno) + + (is_obj_in_basis(lp) && (lp->obj[candidatecolno] != 0) ? 1 : 0); + else + testvalue = 1; + if(currentcolno > 0) + testvalue -= mat_collength(lp->matA, currentcolno) + + (is_obj_in_basis(lp) && (lp->obj[currentcolno] != 0) ? 1 : 0); + else + testvalue -= 1; + if(testvalue > 0) + result = COMP_PREFERCANDIDATE; + else if(testvalue < 0) + result = COMP_PREFERINCUMBENT; + result = my_chsign(isdual, result); + } +#endif + + } + } + + /* Select absolute best if the non-primary criteria failed to separate */ + if((result == COMP_PREFERNONE) && candbetter) { + result = COMP_PREFERCANDIDATE; + goto Finish; + } + + /* Final tie-breakers */ + if(result == COMP_PREFERNONE) { + + /* Add randomization tie-braker */ + if(lp->piv_strategy & PRICE_RANDOMIZE) { + result = my_sign(PRICER_RANDFACT - rand_uniform(lp, 1.0)); + if(candidatevarno < currentvarno) + result = -result; + } + + /* Resolve ties via index ordinal (also prefers slacks over user variables) */ + if(result == COMP_PREFERNONE) { + if(candidatevarno < currentvarno) + result = COMP_PREFERCANDIDATE; + else /* if(candidatevarno > currentvarno) */ + result = COMP_PREFERINCUMBENT; + if(lp->_piv_left_) + result = -result; + } + } + +Finish: + return( result ); +} +int CMP_CALLMODEL compareBoundFlipVar(const pricerec *current, const pricerec *candidate) +{ + register LPSREAL testvalue, margin; + register int result = COMP_PREFERNONE; + register lprec *lp = current->lp; + MYBOOL candbetter; + int currentvarno = current->varno, + candidatevarno = candidate->varno; + + if(!current->isdual) { + candidatevarno = lp->var_basic[candidatevarno]; + currentvarno = lp->var_basic[currentvarno]; + } + + /* Compute the ranking test metric. */ + testvalue = candidate->theta; + margin = current->theta; + if(candidate->isdual) { + testvalue = fabs(testvalue); + margin = fabs(margin); + } + if(fabs(margin) < LIMIT_ABS_REL) + testvalue -= margin; + else + testvalue = my_reldiff(testvalue, margin); + + /* Find if the new Theta is smaller or near equal (i.e. testvalue <= eps) + compared to the previous best; ties will be broken by pivot size or index */ + margin = PREC_SUBSTFEASGAP; + candbetter = (MYBOOL) (testvalue < 0); + if(candbetter) { + if(testvalue < -margin) + result = COMP_PREFERCANDIDATE; + } + else if(testvalue > margin) + result = COMP_PREFERINCUMBENT; + + /* Resolve a tie */ + if(result == COMP_PREFERNONE) { + + /* Tertiary selection based on priority for large pivot sizes */ + if(result == COMP_PREFERNONE) { + LPSREAL currentpivot = fabs(current->pivot), + candidatepivot = fabs(candidate->pivot); + if(candidatepivot > currentpivot+margin) + result = COMP_PREFERCANDIDATE; + else if(candidatepivot < currentpivot-margin) + result = COMP_PREFERINCUMBENT; + } + + /* Secondary selection based on priority for narrow-bounded variables */ + if(result == COMP_PREFERNONE) + result = compareREAL(&(lp->upbo[currentvarno]), + &(lp->upbo[candidatevarno])); + + } + + /* Select absolute best if the non-primary criteria failed to separate */ + if((result == COMP_PREFERNONE) && candbetter) { + result = COMP_PREFERCANDIDATE; + goto Finish; + } + + /* Quaternary selection by index value */ + if(result == COMP_PREFERNONE) { + if(candidatevarno < currentvarno) + result = COMP_PREFERCANDIDATE; + else + result = COMP_PREFERINCUMBENT; + if(lp->_piv_left_) + result = -result; + } + +Finish: + return( result ); +} + +/* Validity operators for entering and leaving columns for both the primal and dual + simplex. All candidates must satisfy these tests to qualify to be allowed to be + a subject for the comparison functions/operators. */ +STATIC MYBOOL validImprovementVar(pricerec *candidate) +{ + register LPSREAL candidatepivot = fabs(candidate->pivot); + +#ifdef Paranoia + return( (MYBOOL) ((candidate->varno > 0) && (candidatepivot > candidate->lp->epsvalue)) ); +#else + return( (MYBOOL) (candidatepivot > candidate->lp->epsvalue) ); +#endif +} + +STATIC MYBOOL validSubstitutionVar(pricerec *candidate) +{ + register lprec *lp = candidate->lp; + register LPSREAL theta = (candidate->isdual ? fabs(candidate->theta) : candidate->theta); + +#ifdef Paranoia + if(candidate->varno <= 0) + return( FALSE ); + else +#endif + if(fabs(candidate->pivot) >= lp->infinite) + return( (MYBOOL) (theta < lp->infinite) ); + else + return( (MYBOOL) ((theta < lp->infinite) && + (fabs(candidate->pivot) >= candidate->epspivot)) ); +} + +int CMP_CALLMODEL compareImprovementQS(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate) +{ + return( compareImprovementVar((pricerec *) current->pvoidint2.ptr, (pricerec *) candidate->pvoidint2.ptr) ); +} +int CMP_CALLMODEL compareSubstitutionQS(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate) +{ + return( compareBoundFlipVar((pricerec *) current->pvoidint2.ptr, (pricerec *) candidate->pvoidint2.ptr) ); +/* return( compareSubstitutionVar((pricerec *) current->self, (pricerec *) candidate->self) ); */ +} + +/* Function to add a valid pivot candidate into the specified list */ +STATIC int addCandidateVar(pricerec *candidate, multirec *multi, findCompare_func findCompare, MYBOOL allowSortedExpand) +{ + int insertpos, delta = 1; + pricerec *targetrec; + + /* Find the insertion point (if any) */ + if((multi->freeList[0] == 0) || + (multi->sorted && allowSortedExpand) || + (candidate->isdual && (multi->used == 1) && ((multi->step_last >= multi->epszero) || + multi_truncatingvar(multi, ((pricerec *) (multi->sortedList[0].pvoidreal.ptr))->varno))) + ) { + UNIONTYPE QSORTrec searchTarget; + + /* Make sure that the list is sorted before the search for an insertion point */ + if((multi->freeList[0] == 0) && !multi->sorted) { + multi->sorted = QS_execute(multi->sortedList, multi->used, findCompare, &insertpos); + multi->dirty = (MYBOOL) (insertpos > 0); + } + + /* Perform the search */ + searchTarget.pvoidint2.ptr = (void *) candidate; + insertpos = sizeof(searchTarget); + insertpos = findIndexEx(&searchTarget, multi->sortedList-delta, multi->used, delta, insertpos, findCompare, TRUE); + if(insertpos > 0) + return( -1 ); + insertpos = -insertpos - delta; + + /* Check if the candidate is worse than the worst of the list */ + if(((insertpos >= multi->size) && (multi->freeList[0] == 0)) || + ((insertpos == multi->used) && (!allowSortedExpand || + (multi->step_last >= multi->epszero)))) + return( -1 ); + +#ifdef Paranoia + /* Do validation */ + if((insertpos < 0) || (insertpos > multi->used)) + return( -1 ); +#endif + + /* Define the target for storing the candidate; + Case 1: List is full and we must discard the previously worst candidate + Case 2: List is not full and we simply use the next free position */ + if(multi->freeList[0] == 0) + targetrec = (pricerec *) multi->sortedList[multi->used-1].pvoidreal.ptr; + else { + delta = multi->freeList[0]--; + delta = multi->freeList[delta]; + targetrec = &(multi->items[delta]); + } + } + else { + delta = multi->freeList[0]--; + delta = multi->freeList[delta]; + targetrec = &(multi->items[delta]); + insertpos = multi->used; + } + + /* Insert the new candidate record in the data store */ + MEMCOPY(targetrec, candidate, 1); + + /* Store the pointer data and handle tree cases: + Case 1: The list is unsorted and not full; simply append pointer to list, + Case 2: The list is sorted and full; insert the pointer by discarding previous last, + Case 3: The list is sorted and not full; shift the inferior items down, and increment count */ + if((multi->used < multi->size) && (insertpos >= multi->used)) { + QS_append(multi->sortedList, insertpos, targetrec); + multi->used++; + } + else { + if(multi->used == multi->size) + QS_insert(multi->sortedList, insertpos, targetrec, multi->size-1); /* Discard previous last */ + else { + QS_insert(multi->sortedList, insertpos, targetrec, multi->used); /* Keep previous last */ + multi->used++; + } + } + multi->active = insertpos; + +#ifdef Paranoia + if((insertpos >= multi->size) || (insertpos >= multi->used)) + report(multi->lp, SEVERE, "addCandidateVar: Insertion point beyond limit!\n"); +#endif + + return( insertpos ); +} + +STATIC MYBOOL findImprovementVar(pricerec *current, pricerec *candidate, MYBOOL collectMP, int *candidatecount) +/* PRIMAL: Find a variable to enter the basis + DUAL: Find a variable to leave the basis + + Allowed variable set: Any pivot PRIMAL:larger or DUAL:smaller than threshold value of 0 */ +{ + MYBOOL Action = FALSE, +#ifdef ExtractedValidityTest + Accept = TRUE; +#else /* Check for validity and compare result with previous best */ + Accept = validImprovementVar(candidate); +#endif + if(Accept) { + if(candidatecount != NULL) + (*candidatecount)++; + if(collectMP) { + if(addCandidateVar(candidate, current->lp->multivars, (findCompare_func *) compareImprovementQS, FALSE) < 0) + return(Action); + } + if(current->varno > 0) + Accept = (MYBOOL) (compareImprovementVar(current, candidate) > 0); + } + + /* Apply candidate if accepted */ + if(Accept) { + (*current) = *candidate; + + /* Force immediate acceptance for Bland's rule using the primal simplex */ + if(!candidate->isdual) + Action = (MYBOOL) (candidate->lp->_piv_rule_ == PRICER_FIRSTINDEX); + } + return(Action); +} + +/* Bound flip variable accumulation routine */ +STATIC MYBOOL collectMinorVar(pricerec *candidate, multirec *longsteps, MYBOOL isphase2, MYBOOL isbatch) +{ + int inspos; + + /* 1. Check for ratio and pivot validity (to have the extra flexibility that all + bound-flip candidates are also possible as basis-entering variables */ + if(!validSubstitutionVar(candidate)) + return( FALSE ); + + /* 2. If the free-list is empty we need to see if we have a better candidate, + and for this the candidate list has to be sorted by merit */ + if(!isbatch && + !longsteps->sorted && (longsteps->used > 1) && + ((longsteps->freeList[0] == 0) || + multi_truncatingvar(longsteps, candidate->varno) || + (longsteps->step_last >= longsteps->epszero) )) { + longsteps->sorted = QS_execute(longsteps->sortedList, longsteps->used, + (findCompare_func *) compareSubstitutionQS, &inspos); + longsteps->dirty = (MYBOOL) (inspos > 0); + if(longsteps->dirty) + multi_recompute(longsteps, 0, isphase2, TRUE); + } + + /* 3. Now handle three cases... + - Add to the list when the list is not full and there is opportunity for improvement, + - Check if we should replace an incumbent when the list is full, + - Check if we should replace an incumbent when the list is not full, there is no room + for improvement, but the current candidate is better than an incumbent. */ + inspos = addCandidateVar(candidate, longsteps, (findCompare_func *) compareSubstitutionQS, TRUE); + + /* 4. Recompute steps and objective, and (if relevant) determine if we + may be suboptimal in relation to an incumbent MILP solution. */ + return( (MYBOOL) (inspos >= 0) && + ((isbatch == TRUE) || multi_recompute(longsteps, inspos, isphase2, TRUE)) ); +} + +STATIC MYBOOL findSubstitutionVar(pricerec *current, pricerec *candidate, int *candidatecount) +/* PRIMAL: Find a variable to leave the basis + DUAL: Find a variable to enter the basis + + Allowed variable set: "Equal-valued" smallest thetas! */ +{ + MYBOOL Action = FALSE, +#ifdef ExtractedValidityTest + Accept = TRUE; +#else /* Check for validity and comparison result with previous best */ + Accept = validSubstitutionVar(candidate); +#endif + if(Accept) { + if(candidatecount != NULL) + (*candidatecount)++; + if(current->varno != 0) + Accept = (MYBOOL) (compareSubstitutionVar(current, candidate) > 0); + } + + /* Apply candidate if accepted */ + if(Accept) { + (*current) = *candidate; + + /* Force immediate acceptance for Bland's rule using the dual simplex */ +#ifdef ForceEarlyBlandRule + if(candidate->isdual) + Action = (MYBOOL) (candidate->lp->_piv_rule_ == PRICER_FIRSTINDEX); +#endif + } + return(Action); +} + +/* Partial pricing management routines */ +STATIC partialrec *partial_createBlocks(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = (partialrec *) calloc(1, sizeof(*blockdata)); + blockdata->lp = lp; + blockdata->blockcount = 1; + blockdata->blocknow = 1; + blockdata->isrow = isrow; + + return(blockdata); +} +STATIC int partial_countBlocks(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + + if(blockdata == NULL) + return( 1 ); + else + return( blockdata->blockcount ); +} +STATIC int partial_activeBlocks(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + + if(blockdata == NULL) + return( 1 ); + else + return( blockdata->blocknow ); +} +STATIC void partial_freeBlocks(partialrec **blockdata) +{ + if((blockdata == NULL) || (*blockdata == NULL)) + return; + FREE((*blockdata)->blockend); + FREE((*blockdata)->blockpos); + FREE(*blockdata); +} + + +/* Function to provide for left-right or right-left scanning of entering/leaving + variables; note that *end must have been initialized by the calling routine! */ +STATIC void makePriceLoop(lprec *lp, int *start, int *end, int *delta) +{ + int offset = is_piv_mode(lp, PRICE_LOOPLEFT); + + if((offset) || + (((lp->total_iter+offset) % 2 == 0) && is_piv_mode(lp, PRICE_LOOPALTERNATE))) { + *delta = -1; /* Step backwards - "left" */ + swapINT(start, end); + lp->_piv_left_ = TRUE; + } + else { + *delta = 1; /* Step forwards - "right" */ + lp->_piv_left_ = FALSE; + } +} + +/* Routine to verify accuracy of the current basis factorization */ +STATIC MYBOOL serious_facterror(lprec *lp, LPSREAL *bvector, int maxcols, LPSREAL tolerance) +{ + int i, j, ib, ie, nz, nc; + LPSREAL sum, tsum = 0, err = 0; + MATrec *mat = lp->matA; + + if(bvector == 0) + bvector = lp->bsolveVal; + nc =0; + nz = 0; + for(i = 1; (i <= lp->rows) && (nc <= maxcols); i++) { + + /* Do we have a non-slack variable? (we choose to skip slacks, + since they have "natural" good accuracy properties) */ + j = lp->var_basic[i] - lp->rows; + if(j <= 0) + continue; + nc++; + + /* Compute cross product for basic, non-slack column */ + ib = mat->col_end[j-1]; + ie = mat->col_end[j]; + nz += ie - ib; + sum = get_OF_active(lp, j+lp->rows, bvector[0]); + for(; ib < ie; ib++) + sum += COL_MAT_VALUE(ib)*bvector[COL_MAT_ROWNR(ib)]; + + /* Catch high precision early, so we don't to uneccessary work */ + tsum += sum; + SETMAX(err, fabs(sum)); + if((tsum / nc > tolerance / 100) && (err < tolerance / 100)) + break; + } + err /= mat->infnorm; + return( (MYBOOL) (err >= tolerance) ); +} + +/* Computation of reduced costs */ +STATIC void update_reducedcosts(lprec *lp, MYBOOL isdual, int leave_nr, int enter_nr, LPSREAL *prow, LPSREAL *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; + + if(isdual) { + hold = -drow[enter_nr]/prow[enter_nr]; + for(i=1; i <= lp->sum; i++) + if(!lp->is_basic[i]) { + if(i == leave_nr) + drow[i] = hold; + else { + drow[i] += hold*prow[i]; + my_roundzero(drow[i], lp->epsmachine); + } + } + } + else + report(lp, SEVERE, "update_reducedcosts: Cannot update primal reduced costs!\n"); +} + + +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) +{ + LPSREAL epsvalue = lp->epsvalue; /* Any larger value can produce a suboptimal result */ + roundmode |= MAT_ROUNDRC; + + if(isdual) { + bsolve_xA2(lp, coltarget, + row_nr, prow, epsvalue, nzprow, /* Calculate net sensitivity given a leaving variable */ + 0, drow, epsvalue, nzdrow, /* Calculate the net objective function values */ + roundmode); + } + else { + LPSREAL *bVector; + +#if 1 /* Legacy mode, that is possibly a little faster */ + if((lp->multivars == NULL) && (lp->P1extraDim == 0)) + bVector = drow; + else +#endif + bVector = lp->bsolveVal; + if(dosolve) { + bsolve(lp, 0, bVector, lp->bsolveIdx, epsvalue*DOUBLEROUND, 1.0); + if(!isdual && (row_nr == 0) && (lp->improve & IMPROVE_SOLUTION) && !refactRecent(lp) && + serious_facterror(lp, bVector, lp->rows, lp->epsvalue)) + set_action(&lp->spx_action, ACTION_REINVERT); + } + prod_xA(lp, coltarget, + bVector, lp->bsolveIdx, epsvalue, 1.0, + drow, nzdrow, roundmode); + } +} + + +/* Primal: Prevent acceptance of an entering variable when the magnitude of + other candidates is also very small. + Dual: Prevent acceptance of a leaving variable when the magnitude of + other candidates is also very small. + + 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) +{ + MYBOOL testOK = TRUE; + return( testOK ); + +#if 1 + /* Try to make dual feasibility as tight as possible */ + if(!isprimal) +/* if(lp->P1extraVal == 0) */ + { + xfeas /= (1+lp->rhsmax); + sfeas /= (1+lp->rhsmax); + } +#endif + xfeas = fabs(xfeas); /* Maximum (positive) infeasibility */ +/* if(xfeas < lp->epspivot) { */ + if(xfeas < lp->epssolution) { + LPSREAL 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 */ + /* 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. */ + if((sfeas-xfeas) < f*lp->epsprimal) + testOK = FALSE; + } + return( testOK ); +} + + +/* 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) +/* The logic in this section generally follows Chvatal: Linear Programming, p. 130 + Basically, the function is a specialized coldual(). */ +{ + int i, bestindex; + LPSREAL bestvalue; + + /* Solve for "local reduced cost" */ + set_action(&lp->piv_strategy, PRICE_FORCEFULL); + compute_reducedcosts(lp, TRUE, rownr, NULL, TRUE, + prow, nzprow, NULL, NULL, MAT_ROUNDDEFAULT); + clear_action(&lp->piv_strategy, PRICE_FORCEFULL); + + /* Find a suitably non-singular variable to enter ("most orthogonal") */ + bestindex = 0; + bestvalue = 0; + for(i = 1; i <= lp->sum-abs(lp->P1extraDim); i++) { + if(!lp->is_basic[i] && !is_fixedvar(lp, i) && + (fabs(prow[i]) > bestvalue)) { + bestindex = i; + bestvalue = fabs(prow[i]); + } + } + + /* Prepare to update inverse and pivot/iterate (compute Bw=a) */ + if(i > lp->sum-abs(lp->P1extraDim)) + bestindex = 0; + else + fsolve(lp, bestindex, prow, nzprow, lp->epsmachine, 1.0, TRUE); + + return( bestindex ); +} + +/* 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) +{ + int i, ix, iy, iz, ninfeas, nloop = 0; + LPSREAL f, sinfeas, xinfeas, epsvalue = lp->epsdual; + pricerec current, candidate; + MYBOOL collectMP = FALSE; + int *coltarget = NULL; + + /* Identify pivot column according to pricing strategy; set + entering variable initial threshold reduced cost value to "0" */ + current.pivot = lp->epsprimal; /* Minimum acceptable improvement */ + current.varno = 0; + current.lp = lp; + current.isdual = FALSE; + candidate.lp = lp; + candidate.isdual = FALSE; + *candidatecount = 0; + + /* Update local value of pivot setting and determine active multiple pricing set */ + lp->_piv_rule_ = get_piv_rule(lp); +doLoop: + nloop++; + if((lp->multivars != NULL) && ((lp->simplex_mode & SIMPLEX_PRIMAL_PRIMAL) != 0)) { + collectMP = multi_mustupdate(lp->multivars); + if(collectMP) { + multi_restart(lp->multivars); + coltarget = NULL; + } + else + coltarget = multi_indexSet(lp->multivars, FALSE); + } + + /* Compute reduced costs c - c*Inv(B), if necessary + (i.e. the previous iteration was not a "minor" iteration/bound flip) */ + if(!skipupdate) { +#ifdef UsePrimalReducedCostUpdate + /* Recompute from scratch only at the beginning, otherwise update */ + if((lp->current_iter > 0) && (refactRecent(lp) == AUTOMATIC)) +#endif + compute_reducedcosts(lp, FALSE, 0, coltarget, (MYBOOL) ((nloop <= 1) || (partialloop > 1)), + NULL, NULL, + drow, nzdrow, + MAT_ROUNDDEFAULT); + } + + /* Loop over active partial column set; we presume that reduced costs + have only been updated for columns in the active partial range. */ + ix = 1; + iy = nzdrow[0]; + ninfeas = 0; + xinfeas = 0; + sinfeas = 0; + makePriceLoop(lp, &ix, &iy, &iz); + iy *= iz; + for(; ix*iz <= iy; ix += iz) { + i = nzdrow[ix]; +#if 0 /* Not necessary since we masked them out in compute_reducedcosts() */ + if(i > lp->sum-abs(lp->P1extraDim)) + continue; +#endif + + /* Check if the pivot candidate is on the block-list */ + if(lp->rejectpivot[0] > 0) { + int kk; + for(kk = 1; (kk <= lp->rejectpivot[0]) && (i != lp->rejectpivot[kk]); kk++); + if(kk <= lp->rejectpivot[0]) + continue; + } + + /* Retrieve the applicable reduced cost - threshold should not be smaller than 0 */ + f = my_chsign(lp->is_lower[i], drow[i]); + if(f <= epsvalue) + continue; + + /* Find entering variable according to strategy (largest positive f) */ + ninfeas++; + SETMAX(xinfeas, f); + sinfeas += f; + candidate.pivot = normalizeEdge(lp, i, f, FALSE); + candidate.varno = i; + if(findImprovementVar(¤t, &candidate, collectMP, candidatecount)) + break; + } + + /* Check if we should loop again after a multiple pricing update */ + if(lp->multivars != NULL) { + if(collectMP) { + if(!lp->multivars->sorted) + lp->multivars->sorted = QS_execute(lp->multivars->sortedList, lp->multivars->used, + (findCompare_func *) compareImprovementQS, NULL); + coltarget = multi_indexSet(lp->multivars, TRUE); + } + else if((current.varno == 0) && (lp->multivars->retries == 0)) { + ix = partial_blockStart(lp, FALSE); + iy = partial_blockEnd(lp, FALSE); + lp->multivars->used = 0; + lp->multivars->retries++; + goto doLoop; + } + /* Shrink the candidate list */ + lp->multivars->retries = 0; + if(current.varno != 0) + multi_removevar(lp->multivars, current.varno); + } + + /* Check for optimality */ + if(xviol != NULL) + *xviol = xinfeas; + if(updateinfeas) + lp->suminfeas = fabs(sinfeas); + if((lp->multivars == NULL) && (current.varno > 0) && + !verify_stability(lp, TRUE, xinfeas, sinfeas, ninfeas)) + current.varno = 0; + + /* Produce statistics */ + if(lp->spx_trace) { + if(current.varno > 0) + report(lp, DETAILED, "colprim: Column %d reduced cost = " RESULTVALUEMASK "\n", + current.varno, current.pivot); + else + report(lp, DETAILED, "colprim: No positive reduced costs found, optimality!\n"); + } + + return( current.varno ); +} /* 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) +{ + int i, ii, iy, iz, Hpass, k, *nzlist; + LREAL f, savef; + LPSREAL Heps, Htheta, Hlimit, epsvalue, epspivot, p = 0.0; + pricerec current, candidate; + MYBOOL isupper = !lp->is_lower[colnr], HarrisTwoPass = FALSE; + + /* Update local value of pivot setting */ + lp->_piv_rule_ = get_piv_rule(lp); + if(nzpcol == NULL) + nzlist = (int *) mempool_obtainVector(lp->workarrays, lp->rows+1, sizeof(*nzlist)); + else + nzlist = nzpcol; + + /* Find unconditional non-zeros and optionally compute relative size of epspivot */ + epspivot = lp->epspivot; + epsvalue = lp->epsvalue; + Hlimit = 0; + Htheta = 0; + k = 0; + for(i = 1; i <= lp->rows; i++) { + p = fabs(pcol[i]); + if(p > Hlimit) + Hlimit = p; + if(p > epsvalue) { + k++; + nzlist[k] = i; + SETMAX(Htheta, p); + } +#ifdef Paranoia + else { + if(lp->spx_trace) + report(lp, FULL, "rowprim: Row %d with pivot " RESULTVALUEMASK " rejected as too small\n", + i, p); + } +#endif + } + if(xviol != NULL) + *xviol = Htheta; + Htheta = 0; + + /* Update non-zero list based on the new pivot threshold */ +#ifdef UseRelativePivot_Primal +/* epspivot *= sqrt(lp->matA->dynrange) / lp->matA->infnorm; */ + epspivot /= MAX(1, sqrt(lp->matA->colmax[colnr])); + iy = k; + k = 0; + p = 0; + for(ii = 1; ii <= iy; ii++) { + i = nzlist[ii]; + p = fabs(pcol[i]); + + /* Compress the list of valid alternatives, if appropriate */ + if(p > epspivot) { + k++; + nzlist[k] = i; + } +#ifdef Paranoia + else { + if(lp->spx_trace) + report(lp, FULL, "rowprim: Row %d with pivot " RESULTVALUEMASK " rejected as too small\n", + i, p); + } +#endif + } +#endif + + /* Initialize counters */ + nzlist[0] = k; + k = 0; + +Retry: + k++; + HarrisTwoPass = is_piv_mode(lp, PRICE_HARRISTWOPASS); + if(HarrisTwoPass) + Hpass = 1; + else + Hpass = 2; + current.theta = lp->infinite; + current.pivot = 0; + current.varno = 0; + current.isdual = FALSE; + current.epspivot = epspivot; + current.lp = lp; + candidate.epspivot = epspivot; + candidate.isdual = FALSE; + candidate.lp = lp; + savef = 0; + for(; Hpass <= 2; Hpass++) { + Htheta = lp->infinite; + if(Hpass == 1) { + Hlimit = lp->infinite; /* Don't apply any limit in the first pass */ + Heps = epspivot/lp->epsprimal; /* Scaled to lp->epsprimal used in compute_theta() */ + } + else { + Hlimit = current.theta; /* This is the smallest Theta of the first pass */ + Heps = 0.0; + } + current.theta = lp->infinite; + current.pivot = 0; + current.varno = 0; + savef = 0; + + ii = 1; + iy = nzlist[0]; + makePriceLoop(lp, &ii, &iy, &iz); + iy *= iz; + for(; ii*iz <= iy; ii += iz) { + i = nzlist[ii]; + f = pcol[i]; + candidate.theta = f; + candidate.pivot = f; + candidate.varno = i; + + /*i =*/ compute_theta(lp, i, &candidate.theta, isupper, + my_if(lp->upbo[lp->var_basic[i]] < lp->epsprimal, Heps/10, Heps), TRUE); + + if(fabs(candidate.theta) >= lp->infinite) { + savef = f; + candidate.theta = 2*lp->infinite; + continue; + } + + /* Find the candidate leaving variable according to strategy (smallest theta) */ + if((Hpass == 2) && (candidate.theta > Hlimit)) + continue; + + /* Give a slight preference to fixed variables (mainly equality slacks) */ + if(forceoutEQ) { + p = candidate.pivot; + if(lp->upbo[lp->var_basic[i]] < lp->epsprimal) { + /* Give an extra early boost to equality slack elimination, if specified */ + if(forceoutEQ == AUTOMATIC) + candidate.pivot *= 1.0+lp->epspivot; + else + candidate.pivot *= 10.0; + + } + } + if(HarrisTwoPass) { + f = candidate.theta; + if(Hpass == 2) + candidate.theta = 1; + if(findSubstitutionVar(¤t, &candidate, NULL)) + break; + if((Hpass == 2) && (current.varno == candidate.varno)) + Htheta = f; + } + else + if(findSubstitutionVar(¤t, &candidate, NULL)) + break; + /* Restore temporarily modified pivot */ + if(forceoutEQ && (current.varno == candidate.varno)) + current.pivot = p; + } + } + if(HarrisTwoPass) + current.theta = Htheta; + + /* Handle case of no available leaving variable */ + if(current.varno == 0) { + if(lp->upbo[colnr] >= lp->infinite) { + /* Optionally try again with reduced pivot threshold level */ + if(k < 2) { + epspivot = epspivot / 10; + goto Retry; + } + } + else { +#if 1 + i = 1; + while((pcol[i] >= 0) && (i <= lp->rows)) + i++; + if(i > lp->rows) { /* Empty column with upper bound! */ + lp->is_lower[colnr] = !lp->is_lower[colnr]; +/* lp->is_lower[colnr] = FALSE; */ + lp->rhs[0] += lp->upbo[colnr]*pcol[0]; + } + else /* if(pcol[i]<0) */ + { + current.varno = i; + } +#endif + } + } + else if(current.theta >= lp->infinite) { + report(lp, IMPORTANT, "rowprim: Numeric instability pcol[%d] = %g, rhs[%d] = %g, upbo = %g\n", + current.varno, savef, current.varno, lp->rhs[current.varno], + lp->upbo[lp->var_basic[current.varno]]); + } + + /* Return working array to pool */ + if(nzpcol == NULL) + mempool_releaseVector(lp->workarrays, (char *) nzlist, FALSE); + + if(lp->spx_trace) + report(lp, DETAILED, "row_prim: %d, pivot size = " RESULTVALUEMASK "\n", + current.varno, current.pivot); + +/* *theta = current.theta; */ + *theta = fabs(current.theta); + + return(current.varno); +} /* rowprim */ + + +/* Find the dual simplex leaving basic variable */ +STATIC int rowdual(lprec *lp, LPSREAL *rhvec, MYBOOL forceoutEQ, MYBOOL updateinfeas, LPSREAL *xviol) +{ + int k, i, iy, iz, ii, ninfeas; + register LPSREAL rh; + LPSREAL up, lo = 0, + epsvalue, sinfeas, xinfeas; + pricerec current, candidate; + MYBOOL collectMP = FALSE; + + /* Initialize */ + if(rhvec == NULL) + rhvec = lp->rhs; + epsvalue = lp->epsdual; + current.pivot = -epsvalue; /* Initialize leaving variable threshold; "less than 0" */ + current.theta = 0; + current.varno = 0; + current.isdual = TRUE; + current.lp = lp; + candidate.isdual = TRUE; + candidate.lp = lp; + + /* Loop over active partial row set */ + if(is_action(lp->piv_strategy, PRICE_FORCEFULL)) { + k = 1; + iy = lp->rows; + } + else { + k = partial_blockStart(lp, TRUE); + iy = partial_blockEnd(lp, TRUE); + } + ninfeas = 0; + xinfeas = 0; + sinfeas = 0; + makePriceLoop(lp, &k, &iy, &iz); + iy *= iz; + for(; k*iz <= iy; k += iz) { + + /* Map loop variable to target */ + i = k; + + /* Check if the pivot candidate is on the block-list */ + if(lp->rejectpivot[0] > 0) { + int kk; + for(kk = 1; (kk <= lp->rejectpivot[0]) && (i != lp->rejectpivot[kk]); kk++); + if(kk <= lp->rejectpivot[0]) + continue; + } + + /* Set local variables - express violation as a negative number */ + ii = lp->var_basic[i]; + up = lp->upbo[ii]; + lo = 0; + rh = rhvec[i]; + if(rh > up) + rh = up - rh; + else + rh -= lo; + up -= lo; + + /* Analyze relevant constraints ... + KE version skips uninteresting alternatives and gives a noticeable speedup */ +/* if((rh < -epsvalue*sqrt(lp->matA->rowmax[i])) || */ + if((rh < -epsvalue) || + ((forceoutEQ == TRUE) && (up < epsvalue))) { /* It causes instability to remove the "TRUE" test */ + + /* Accumulate stats */ + ninfeas++; + SETMIN(xinfeas, rh); + sinfeas += rh; + + /* Give a slight preference to fixed variables (mainly equality slacks) */ + if(up < epsvalue) { + /* Break out immediately if we are directed to force slacks out of the basis */ + if(forceoutEQ == TRUE) { + current.varno = i; + current.pivot = -1; + break; + } + /* Give an extra early boost to equality slack elimination, if specified */ + if(forceoutEQ == AUTOMATIC) + rh *= 10.0; + else /* .. or just the normal. marginal boost */ + rh *= 1.0+lp->epspivot; + } + + /* Select leaving variable according to strategy (the most negative/largest violation) */ + candidate.pivot = normalizeEdge(lp, i, rh, TRUE); + candidate.varno = i; + if(findImprovementVar(¤t, &candidate, collectMP, NULL)) + break; + } + } + + /* Verify infeasibility */ + if(updateinfeas) + lp->suminfeas = fabs(sinfeas); + if((ninfeas > 1) && + !verify_stability(lp, FALSE, xinfeas, sinfeas, ninfeas)) { + report(lp, IMPORTANT, "rowdual: Check for reduced accuracy and tolerance settings.\n"); + current.varno = 0; + } + + /* Produce statistics */ + if(lp->spx_trace) { + report(lp, NORMAL, "rowdual: Infeasibility sum " RESULTVALUEMASK " in %7d constraints.\n", + sinfeas, ninfeas); + if(current.varno > 0) { + report(lp, DETAILED, "rowdual: rhs[%d] = " RESULTVALUEMASK "\n", + current.varno, lp->rhs[current.varno]); + } + else + report(lp, FULL, "rowdual: Optimality - No primal infeasibilities found\n"); + } + if(xviol != NULL) + *xviol = fabs(xinfeas); + + return(current.varno); +} /* rowdual */ + + +STATIC void longdual_testset(lprec *lp, int which, int rownr, LPSREAL *prow, int *nzprow, + LPSREAL *drow, int *nzdrow) +{ + int i,j; + LPSREAL 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; + j = 3; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = 1; drow[i] = 5; + j = 4; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = 3; drow[i] = -6; + j = 5; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = -4; drow[i] = -2; + j = 6; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -1; drow[i] = 0; + j = 7; i = lp->rows+j; lp->upbo[i] = 2; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = 1; drow[i] = 0; + j = 8; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = -2; drow[i] = 0; + j = 9; i = lp->rows+j; lp->upbo[i] = 5; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -1; drow[i] = 4; + j = 10; i = lp->rows+j; lp->upbo[i] = F; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -2; drow[i] = 10; + nzprow[0] = i-lp->rows; + lp->rhs[rownr] = -11; + lp->upbo[lp->var_basic[rownr]] = F; + lp->rhs[0] = 1; + } + else if(which == 1) { /* Maros Example-1 - presorted in correct order */ + 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] = 1; drow[i] = 5; + j = 3; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = -4; drow[i] = -2; + j = 4; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = -2; drow[i] = 0; + + j = 5; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -1; drow[i] = 0; + j = 6; i = lp->rows+j; lp->upbo[i] = 2; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = 1; drow[i] = 0; + j = 7; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -2; drow[i] = 2; + j = 8; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = 3; drow[i] = -6; + j = 9; i = lp->rows+j; lp->upbo[i] = 5; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -1; drow[i] = 4; + j = 10; i = lp->rows+j; lp->upbo[i] = F; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -2; drow[i] = 10; + nzprow[0] = i-lp->rows; + lp->rhs[rownr] = -11; + lp->upbo[lp->var_basic[rownr]] = F; + lp->rhs[0] = 1; + } + + else if(which == 10) { /* Maros Example-2 - raw data */ + j = 1; i = lp->rows+j; lp->upbo[i] = 5; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = -2; drow[i] = 2; + j = 2; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = 3; drow[i] = 3; + j = 3; i = lp->rows+j; lp->upbo[i] = 1; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = -2; drow[i] = 0; + j = 4; i = lp->rows+j; lp->upbo[i] = 2; lp->is_lower[i] = FALSE; nzprow[j] = i; prow[i] = -1; drow[i] = -2; + j = 5; i = lp->rows+j; lp->upbo[i] = 2; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = 1; drow[i] = 0; + j = 6; i = lp->rows+j; lp->upbo[i] = F; lp->is_lower[i] = TRUE; nzprow[j] = i; prow[i] = 3; drow[i] = 9; + nzprow[0] = i-lp->rows; + lp->rhs[rownr] = 14; + lp->upbo[lp->var_basic[rownr]] = 2; + lp->rhs[0] = 6; + } +} + + +/* Find the dual simplex entering non-basic variable */ +STATIC int coldual(lprec *lp, int row_nr, LPSREAL *prow, int *nzprow, + LPSREAL *drow, int *nzdrow, + MYBOOL dualphase1, MYBOOL skipupdate, + int *candidatecount, LPSREAL *xviol) +{ + int i, iy, iz, ix, k, nbound; + LREAL w, g, quot; + LPSREAL viol, p, epspivot = lp->epspivot; +#ifdef MachinePrecRoundRHS + LPSREAL epsvalue = lp->epsmachine; +#else + LPSREAL 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; + current.pivot = 0; + current.varno = 0; + current.epspivot = epspivot; + current.isdual = TRUE; + current.lp = lp; + candidate.epspivot = epspivot; + candidate.isdual = TRUE; + candidate.lp = lp; + *candidatecount = 0; + + /* Compute reduced costs */ + if(!skipupdate) { +#ifdef UseDualReducedCostUpdate + /* Recompute from scratch only at the beginning, otherwise update */ + if((lp->current_iter > 0) && (refactRecent(lp) < AUTOMATIC)) + compute_reducedcosts(lp, TRUE, row_nr, NULL, TRUE, + prow, nzprow, + NULL, NULL, + MAT_ROUNDDEFAULT); + else +#endif + compute_reducedcosts(lp, TRUE, row_nr, NULL, TRUE, + prow, nzprow, + drow, nzdrow, + MAT_ROUNDDEFAULT); + } + +#if 0 + /* Override all above to do in-line testing with fixed test set */ + if(lp->rows > 1 && lp->columns > 10) + longdual_testset(lp, 10, row_nr, prow, nzprow, drow, nzdrow); +#endif + + /* Compute the current violation of the bounds of the outgoing variable, + negative for violation of lower bound, positive for upper bound violation. + (Basic variables are always lower-bounded, by lp_solve convention) */ + g = 1; + viol = lp->rhs[row_nr]; + if(viol > 0) { /* Check if the leaving variable is >= its upper bound */ + p = lp->upbo[lp->var_basic[row_nr]]; + if(p < lp->infinite) { + viol -= p; + my_roundzero(viol, epsvalue); + if(viol > 0) + g = -1; + } + /* Do validation of numerics */ + if(g == 1) { + if(viol >= lp->infinite) { + report(lp, IMPORTANT, "coldual: Large basic solution value %g at iter %.0f indicates numerical instability\n", + lp->rhs[row_nr], (double) get_total_iter(lp)); + lp->spx_status = NUMFAILURE; + return( 0 ); + + } + if(skipupdate) + report(lp, DETAILED, "coldual: Inaccurate bound-flip accuracy at iter %.0f\n", + (double) get_total_iter(lp)); + else + report(lp, SEVERE, "coldual: Leaving variable %d does not violate bounds at iter %.0f\n", + row_nr, (double) get_total_iter(lp)); + return( -1 ); + } + } + + /* Update local value of pivot setting */ + lp->_piv_rule_ = get_piv_rule(lp); + + /* Condense list of relevant targets */ + p = 0; + k = 0; + nbound = 0; + ix = 1; + iy = nzprow[0]; + 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); + + /* Check if the candidate is worth using for anything */ + if(w < -epsvalue) { + /* Tally bounded variables */ + if(lp->upbo[i] < lp->infinite) + nbound++; + + /* Update the nz-index */ + k++; + nzprow[k] = i; + SETMAX(p, -w); + } +#ifdef Paranoia + else { + if(lp->spx_trace) { + report(lp, FULL, "coldual: Candidate variable prow[%d] rejected with %g too small\n", + i, w); + } + } +#endif + + } + nzprow[0] = k; + if(xviol != NULL) + *xviol = p; + +#ifdef UseRelativePivot_Dual +/* epspivot *= sqrt(lp->matA->dynrange) / lp->matA->infnorm; */ + epspivot /= MAX(1, sqrt(lp->matA->rowmax[row_nr])); +#endif + current.epspivot = epspivot; + candidate.epspivot = epspivot; + + /* Initialize the long-step structures if indicated */ + if(dolongsteps) { + if((nzprow[0] <= 1) || (nbound == 0)) { /* Don't bother */ + dolongsteps = FALSE; + lp->longsteps->indexSet[0] = 0; + } + else { + multi_restart(lp->longsteps); + multi_valueInit(lp->longsteps, g*viol, lp->rhs[0]); + } + } + + /* Loop over all entering column candidates */ + ix = 1; + iy = nzprow[0]; + makePriceLoop(lp, &ix, &iy, &iz); + iy *= iz; + for(; ix*iz <= iy; ix += iz) { + i = nzprow[ix]; + + /* Compute the dual ratio (prow = w and drow = cbar in Chvatal's "nomenclatura") */ + w = prow[i] * g; /* Change sign if upper bound of the leaving variable is violated */ + quot = -drow[i] / w; /* Remember this sign-reversal in multi_recompute! */ + + /* Apply the selected pivot strategy (smallest theta) */ + candidate.theta = quot; /* Note that abs() is applied in findSubstitutionVar */ + candidate.pivot = w; + candidate.varno = i; + + /* Collect candidates for minor iterations/bound flips */ + if(dolongsteps) { + if(isbatch && (ix == iy)) + isbatch = AUTOMATIC; + if(collectMinorVar(&candidate, lp->longsteps, (MYBOOL) (dolongsteps == AUTOMATIC), isbatch) && + lp->spx_trace) + report(lp, DETAILED, "coldual: Long-dual break point with %d bound-flip variables\n", + lp->longsteps->used); + if(lp->spx_status == FATHOMED) + return( 0 ); + } + + /* We have a candidate for entering the basis; check if it is better than the incumbent */ + else if(findSubstitutionVar(¤t, &candidate, candidatecount)) + break; + } + + /* Set entering variable and long-step bound swap variables */ + if(dolongsteps) { + *candidatecount = lp->longsteps->used; + i = multi_enteringvar(lp->longsteps, NULL, 3); + } + else + i = current.varno; + + if(lp->spx_trace) + report(lp, NORMAL, "coldual: Entering column %d, reduced cost %g, pivot value %g, bound swaps %d\n", + i, drow[i], prow[i], multi_used(lp->longsteps)); + + return( i ); +} /* coldual */ + + +INLINE LPSREAL normalizeEdge(lprec *lp, int item, LPSREAL edge, MYBOOL isdual) +{ +#if 1 + /* Don't use the pricer "close to home", since this can possibly + worsen the final feasibility picture (mainly a Devex issue?) */ + if(fabs(edge) > lp->epssolution) +#endif + edge /= getPricer(lp, item, isdual); + if((lp->piv_strategy & PRICE_RANDOMIZE) != 0) + edge *= (1.0-PRICER_RANDFACT) + PRICER_RANDFACT*rand_uniform(lp, 1.0); + return( edge ); + +} + +/* 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; + MATrec *mat = lp->matA; + partialrec *blockdata; + + if(!mat_validate(mat)) + return( 1 ); + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + items = IF(isrow, lp->rows, lp->columns); + allocREAL(lp, &sum, items+1, FALSE); + + /* Loop over items and compute the average column index for each */ + sum[0] = 0; + for(i = 1; i <= items; i++) { + n = 0; + if(isrow) { + nb = mat->row_end[i-1]; + ne = mat->row_end[i]; + } + else { + nb = mat->col_end[i-1]; + ne = mat->col_end[i]; + } + n = ne-nb; + sum[i] = 0; + if(n > 0) { + if(isrow) + for(jj = nb; jj < ne; jj++) + sum[i] += ROW_MAT_COLNR(jj); + else + for(jj = nb; jj < ne; jj++) + sum[i] += COL_MAT_ROWNR(jj); + sum[i] /= n; + } + else + sum[i] = sum[i-1]; + } + + /* Loop over items again, find largest difference and make monotone */ + hold = 0; + biggest = 0; + for(i = 2; i <= items; i++) { + hold = sum[i] - sum[i-1]; + if(hold > 0) { + if(hold > biggest) + biggest = hold; + } + else + hold = 0; + sum[i-1] = hold; + } + + /* Loop over items again and find differences exceeding threshold; + the discriminatory power of this routine depends strongly on the + magnitude of the scaling factor - from empirical evidence > 0.9 */ + biggest = MAX(1, 0.9*biggest); + n = 0; + nb = 0; + ne = 0; + for(i = 1; i < items; i++) + if(sum[i] > biggest) { + ne += i-nb; /* Compute sum of index gaps between maxima */ + nb = i; + n++; /* Increment count */ + } + + /* Clean up */ + FREE(sum); + + /* Require that the maxima are spread "nicely" across the columns, + otherwise return that there is only one monolithic block. + (This is probably an area for improvement in the logic!) */ + if(n > 0) { + ne /= n; /* Average index gap between maxima */ + i = IF(isrow, lp->columns, lp->rows); + nb = i / ne; /* Another estimated block count */ + if(abs(nb - n) > 2) /* Probably Ok to require equality (nb==n)*/ + n = 1; + else if(autodefine) /* Generate row/column break-indeces for partial pricing */ + set_partialprice(lp, nb, NULL, isrow); + } + else + n = 1; + + return( n ); +} +STATIC int partial_blockStart(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + if(blockdata == NULL) + return( 1 ); + else { + if((blockdata->blocknow < 1) || (blockdata->blocknow > blockdata->blockcount)) + blockdata->blocknow = 1; + return( blockdata->blockend[blockdata->blocknow-1] ); + } +} +STATIC int partial_blockEnd(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + if(blockdata == NULL) + return( IF(isrow, lp->rows, lp->sum) ); + else { + if((blockdata->blocknow < 1) || (blockdata->blocknow > blockdata->blockcount)) + blockdata->blocknow = 1; + return( blockdata->blockend[blockdata->blocknow]-1 ); + } +} +STATIC int partial_blockNextPos(lprec *lp, int block, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); +#ifdef Paranoia + if((blockdata == NULL) || (block <= 1) || (block > blockdata->blockcount)) { + report(lp, SEVERE, "partial_blockNextPos: Invalid block %d specified.\n", + block); + return( -1 ); + } +#endif + block--; + if(blockdata->blockpos[block] == blockdata->blockend[block+1]) + blockdata->blockpos[block] = blockdata->blockend[block]; + else + blockdata->blockpos[block]++; + return( blockdata->blockpos[block] ); +} +STATIC MYBOOL partial_blockStep(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + if(blockdata == NULL) + return( FALSE ); + else if(blockdata->blocknow < blockdata->blockcount) { + blockdata->blocknow++; + return( TRUE); + } + else { + blockdata->blocknow = 1; + return( TRUE ); + } +} +STATIC MYBOOL partial_isVarActive(lprec *lp, int varno, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + if(blockdata == NULL) + return( TRUE ); + else { + return( (MYBOOL) ((varno >= blockdata->blockend[blockdata->blocknow-1]) && + (varno < blockdata->blockend[blockdata->blocknow])) ); + } +} + + +/* Multiple pricing routines */ +STATIC multirec *multi_create(lprec *lp, MYBOOL truncinf) +{ + multirec *multi; + + multi = (multirec *) calloc(1, sizeof(*multi)); + if(multi != NULL) { + multi->active = 1; + multi->lp = lp; + multi->epszero = lp->epsprimal; + multi->truncinf = truncinf; + } + + return(multi); +} +STATIC void multi_free(multirec **multi) +{ + if((multi == NULL) || (*multi == NULL)) + return; + FREE((*multi)->items); + FREE((*multi)->valueList); + FREE((*multi)->indexSet); + FREE((*multi)->freeList); + FREE((*multi)->sortedList); + FREE(*multi); +} +STATIC MYBOOL multi_mustupdate(multirec *multi) +{ + return( (MYBOOL) ((multi != NULL) && + (multi->used < multi->limit)) ); +} +STATIC MYBOOL multi_resize(multirec *multi, int blocksize, int blockdiv, MYBOOL doVlist, MYBOOL doIset) +{ + MYBOOL ok = TRUE; + + if((blocksize > 1) && (blockdiv > 0)) { + int oldsize = multi->size; + + multi->size = blocksize; + if(blockdiv > 1) + multi->limit += (multi->size-oldsize) / blockdiv; + + multi->items = (pricerec *) realloc(multi->items, (multi->size+1)*sizeof(*(multi->items))); + multi->sortedList = (UNIONTYPE QSORTrec *) realloc(multi->sortedList, (multi->size+1)*sizeof(*(multi->sortedList))); + ok = (multi->items != NULL) && (multi->sortedList != NULL) && + allocINT(multi->lp, &(multi->freeList), multi->size+1, AUTOMATIC); + if(ok) { + int i, n; + + if(oldsize == 0) + i = 0; + else + i = multi->freeList[0]; + multi->freeList[0] = i + (multi->size-oldsize); + for(n = multi->size - 1, i++; i <= multi->freeList[0]; i++, n--) + multi->freeList[i] = n; + } + if(doVlist) + ok &= allocREAL(multi->lp, &(multi->valueList), multi->size+1, AUTOMATIC); + if(doIset) { + ok &= allocINT(multi->lp, &(multi->indexSet), multi->size+1, AUTOMATIC); + if(ok && (oldsize == 0)) + multi->indexSet[0] = 0; + } + if(!ok) + goto Undo; + + } + else { +Undo: + multi->size = 0; + FREE(multi->items); + FREE(multi->valueList); + FREE(multi->indexSet); + FREE(multi->freeList); + FREE(multi->sortedList); + } + multi->active = 1; + + return( ok ); +} + +STATIC int multi_size(multirec *multi) +{ + if(multi == NULL) + return( 0 ); + else + return( multi->size ); +} + +STATIC int multi_used(multirec *multi) +{ + if(multi == NULL) + return( 0 ); + else + return( multi->used ); +} + +STATIC int multi_restart(multirec *multi) +{ + int i, n = multi->used; + + multi->used = 0; + multi->sorted = FALSE; + multi->dirty = FALSE; + if(multi->freeList != NULL) { + for(i = 1; i <= multi->size; i++) + multi->freeList[i] = multi->size - i; + multi->freeList[0] = multi->size; + } +#if 0 + if(multi->indexSet != NULL) + multi->indexSet[0] = 0; +#endif + return( n ); +} + +STATIC void multi_valueInit(multirec *multi, LPSREAL step_base, LPSREAL obj_base) +{ + multi->step_base = multi->step_last = step_base; + multi->obj_base = multi->obj_last = obj_base; +#ifdef Paranoia + if(step_base > 0) + report(multi->lp, SEVERE, "multi_valueInit: Positive constraint violation %g provided at iteration %6.0f\n", + step_base, (double) get_total_iter(multi->lp)); +#endif +} + +STATIC LPSREAL *multi_valueList(multirec *multi) +{ + return(multi->valueList); +} + +STATIC int *multi_indexSet(multirec *multi, MYBOOL regenerate) +{ + if(regenerate) + multi_populateSet(multi, NULL, -1); + return(multi->indexSet); +} + +STATIC int multi_getvar(multirec *multi, int item) +{ +#ifdef Paranoia + if((item < 1) || (item >= multi->size)) + return(-1); +#endif + return( ((pricerec *) &(multi->sortedList[item].pvoidreal.ptr))->varno ); +} + +STATIC MYBOOL multi_recompute(multirec *multi, int index, MYBOOL isphase2, MYBOOL fullupdate) +{ + int i, n; + LPSREAL lB, uB, Alpha, this_theta, prev_theta; + lprec *lp = multi->lp; + pricerec *thisprice; + + /* Define target update window */ + if(multi->dirty) { + index = 0; + n = multi->used - 1; + } + else if(fullupdate) + n = multi->used - 1; + else + n = index; + + /* Initialize accumulators from the specified update index */ + if(index == 0) { + multi->maxpivot = 0; + multi->maxbound = 0; + multi->step_last = multi->step_base; + multi->obj_last = multi->obj_base; + thisprice = NULL; + this_theta = 0; + } + else { + multi->obj_last = multi->valueList[index-1]; + multi->step_last = multi->sortedList[index-1].pvoidreal.realval; + thisprice = (pricerec *) (multi->sortedList[index-1].pvoidreal.ptr); + this_theta = thisprice->theta; + } + + /* Update step lengths and objective values */ + while((index <= n) && (multi->step_last < multi->epszero)) { + + /* Update parameters for this loop */ + prev_theta = this_theta; + thisprice = (pricerec *) (multi->sortedList[index].pvoidreal.ptr); + this_theta = thisprice->theta; + Alpha = fabs(thisprice->pivot); + uB = lp->upbo[thisprice->varno]; + lB = 0; + SETMAX(multi->maxpivot, Alpha); + SETMAX(multi->maxbound, uB); + + /* Do the value updates */ + if(isphase2) { + multi->obj_last += (this_theta - prev_theta) * multi->step_last; /* Sign-readjusted from coldual()/Maros */ + if(uB >= lp->infinite) + multi->step_last = lp->infinite; + else + multi->step_last += Alpha*(uB-lB); + } + else { + multi->obj_last += (this_theta - prev_theta) * multi->step_last; /* Sign-readjusted from coldual()/Maros */ + multi->step_last += Alpha; + } + + /* Store updated values at the indexed locations */ + multi->sortedList[index].pvoidreal.realval = multi->step_last; + multi->valueList[index] = multi->obj_last; +#ifdef Paranoia + if(lp->spx_trace && + (multi->step_last > lp->infinite)) + report(lp, SEVERE, "multi_recompute: A very large step-size %g was generated at iteration %6.0f\n", + multi->step_last, (double) get_total_iter(lp)); +#endif + index++; + } + + /* Discard candidates entered earlier that now make the OF worsen, and + make sure that the released positions are added to the free list. */ + n = index; + while(n < multi->used) { + i = ++multi->freeList[0]; + multi->freeList[i] = (int) (((pricerec *) multi->sortedList[n].pvoidreal.ptr) - multi->items); + n++; + } + multi->used = index; + if(multi->sorted && (index == 1)) + multi->sorted = FALSE; + multi->dirty = FALSE; + + /* Return TRUE if the step is now positive */ + return( (MYBOOL) (multi->step_last >= multi->epszero) ); +} + +STATIC MYBOOL multi_truncatingvar(multirec *multi, int varnr) +{ + return( multi->truncinf && is_infinite(multi->lp, multi->lp->upbo[varnr]) ); +} + +STATIC MYBOOL multi_removevar(multirec *multi, int varnr) +{ + int i = 1; + int *coltarget = multi->indexSet; + + if(coltarget == NULL) + return( FALSE ); + + while((i <= multi->used) && (coltarget[i] != varnr)) + i++; + if(i > multi->used) + return( FALSE ); + + for(; i < multi->used; i++) + coltarget[i] = coltarget[i+1]; + coltarget[0]--; + multi->used--; + multi->dirty = TRUE; + return( TRUE ); +} + +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; + pricerec *candidate, *bestcand; + + /* Check that we have a candidate */ + multi->active = bestindex = 0; + if((multi == NULL) || (multi->used == 0)) + return( bestindex ); + + /* Check for pruning possibility of the B&B tree */ + if(multi->objcheck && (lp->solutioncount > 0) && + bb_better(lp, OF_WORKING | OF_PROJECTED, OF_TEST_WE)) { + lp->spx_status = FATHOMED; + return( bestindex ); + } + + /* Check the trivial case */ + if(multi->used == 1) { + bestcand = (pricerec *) (multi->sortedList[bestindex].pvoidreal.ptr); + goto Finish; + } + + /* Set priority weights */ +Redo: + switch(priority) { + case 0: b1 = 0.0, b2 = 0.0, b3 = 1.0; /* Only OF */ + bestindex = multi->used - 2; break; + case 1: b1 = 0.2, b2 = 0.3, b3 = 0.5; break; /* Emphasize OF */ + case 2: b1 = 0.3, b2 = 0.5, b3 = 0.2; break; /* Emphasize bound */ + case 3: b1 = 0.6, b2 = 0.2, b3 = 0.2; break; /* Emphasize pivot */ + case 4: b1 = 1.0, b2 = 0.0, b3 = 0.0; break; /* Only pivot */ + default: b1 = 0.4, b2 = 0.2, b3 = 0.4; /* Balanced default */ + } + bestcand = (pricerec *) (multi->sortedList[bestindex].pvoidreal.ptr); + + /* Loop over all candidates to get the best entering candidate; + start at the end to try to maximize the chain length */ + for(i = multi->used - 1; i >= 0; i--) { + candidate = (pricerec *) (multi->sortedList[i].pvoidreal.ptr); + colnr = candidate->varno; + bound = lp->upbo[colnr]; + 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); + if(score > bestscore) { + bestscore = score; + bestindex = i; + bestcand = candidate; + } + } + + /* Do pivot protection */ + if((priority < 4) && (fabs(bestcand->pivot) < lp->epssolution)) { + bestindex = 0; + priority++; + goto Redo; + } + +Finish: + /* Make sure we shrink the list and update */ + multi->active = colnr = bestcand->varno; + if(bestindex < multi->used - 1) { +#if 0 +/* if(lp->upbo[colnr] >= lp->infinite) */ + QS_swap(multi->sortedList, bestindex, multi->used-1); + multi_recompute(multi, bestindex, (bestcand->isdual == AUTOMATIC), TRUE); +#else + multi->used = i + 1; +#endif + } + multi_populateSet(multi, NULL, multi->active); + + /* Compute the entering theta and update parameters */ + score = (multi->used == 1 ? multi->step_base : multi->sortedList[multi->used-2].pvoidreal.realval); + score /= bestcand->pivot; + score = my_chsign(!lp->is_lower[multi->active], score); + + if(lp->spx_trace && + (fabs(score) > 1/lp->epsprimal)) + report(lp, IMPORTANT, "multi_enteringvar: A very large Theta %g was generated (pivot %g)\n", + score, bestcand->pivot); + multi->step_base = score; + if(current != NULL) + *current = *bestcand; + + return( multi->active ); +} + +STATIC LPSREAL multi_enteringtheta(multirec *multi) +{ + return( multi->step_base ); +} + +STATIC int multi_populateSet(multirec *multi, int **list, int excludenr) +{ + int n = 0; + if(list == NULL) + list = &(multi->indexSet); + if((multi->used > 0) && + ((*list != NULL) || allocINT(multi->lp, list, multi->size+1, FALSE))) { + int i, colnr; + + for(i = 0; i < multi->used; i++) { + colnr = ((pricerec *) (multi->sortedList[i].pvoidreal.ptr))->varno; + if((colnr != excludenr) && + /* Prevent an unbounded variable from "bound-flip"; this could + actually indicate that we should let the entering variable be + bound-swapped (in the case that it is bounded), but we + disregard this possibility here, since it brings with it + issues of pivot size, etc. */ + ((excludenr > 0) && (multi->lp->upbo[colnr] < multi->lp->infinite))) { + n++; + (*list)[n] = colnr; + } + } + (*list)[0] = n; + } + return( n ); +} + diff --git a/src/external/lpsolve/build/lp_solve/lp_pricePSE.c b/src/external/lpsolve/build/lp_solve/lp_pricePSE.c new file mode 100644 index 00000000..955abf05 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_pricePSE.c @@ -0,0 +1,539 @@ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_report.h" +#include "lp_pricePSE.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* + Advanced simplex price scaling modules - w/interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h + + Release notes: + v1.0.0 1 September 2003 Implementation of DEVEX and STEEPEST EDGE + routines for the primal and dual simplex. + v1.0.1 1 January 2004 Made initial value of weight of ingoing + variable for the standard mode of DEVEX + consistent with the initialization at restart; + original version could at worst contribute + to cycling. + v1.0.2 23 March 2004 Added floors to Steepest Edge updates and + moved tests for tiny update higher. Previous + logic can be simulated by disabling the compiler + define ApplySteepestEdgeMinimum. + v1.1.0 1 July 2004 Renamed from lp_pricerPSE to lp_pricePSE in + conjuction with the creation of a separate + price library. + v1.2.0 1 March 2005 Changed memory allocation routines to use + standard lp_solve functions, improve error handling + and return boolean status values. + + ---------------------------------------------------------------------------------- +*/ + +INLINE MYBOOL applyPricer(lprec *lp) +{ + int rule = get_piv_rule(lp); + return( (MYBOOL) ((rule == PRICER_DEVEX) || (rule == PRICER_STEEPESTEDGE)) ); +} + + +STATIC void simplexPricer(lprec *lp, MYBOOL isdual) +{ + if(lp->edgeVector != NULL) + lp->edgeVector[0] = (LPSREAL) isdual; +} + + +STATIC void freePricer(lprec *lp) +{ + FREE(lp->edgeVector); +} + + +STATIC MYBOOL resizePricer(lprec *lp) +{ + if(!applyPricer(lp)) + return( TRUE ); + + /* Reallocate vector for new size */ + if(!allocREAL(lp, &(lp->edgeVector), lp->sum_alloc+1, AUTOMATIC)) + return( FALSE ); + + /* Signal that we have not yet initialized the price vector */ + MEMCLEAR(lp->edgeVector, lp->sum_alloc+1); + lp->edgeVector[0] = -1; + return( TRUE ); +} + + +STATIC MYBOOL initPricer(lprec *lp) +{ + if(!applyPricer(lp)) + return( FALSE ); + + /* Free any pre-existing pricer */ + freePricer(lp); + + /* Allocate vector to fit current problem size */ + return( resizePricer(lp) ); +} + + +STATIC LPSREAL getPricer(lprec *lp, int item, MYBOOL isdual) +{ + LPSREAL value = 1.0; + + if(!applyPricer(lp)) + return( value ); + + value = *lp->edgeVector; + + /* Make sure we have a price vector to use */ + if(value < 0) { +#ifdef Paranoia + report(lp, SEVERE, "getPricer: Called without having being initialized!\n"); +#endif + return( 1.0 ); + } + /* We may be calling the primal from the dual (and vice-versa) for validation + of feasibility; ignore calling origin and simply return 1 */ + else if(isdual != value) { + return( 1.0 ); + } + /* Do the normal norm retrieval */ + else { + + if(isdual) + item = lp->var_basic[item]; + + value = lp->edgeVector[item]; + + if(value == 0) { + value = 1.0; + report(lp, SEVERE, "getPricer: Detected a zero-valued price at index %d\n", item); + } +#ifdef Paranoia + else if(value < 0) + report(lp, SEVERE, "getPricer: Invalid %s reduced cost norm %g at index %d\n", + my_if(isdual, "dual", "primal"), value, item); +#endif + + /* Return the norm */ + return( sqrt(value) ); + } +} + +STATIC MYBOOL restartPricer(lprec *lp, MYBOOL isdual) +{ + LPSREAL *sEdge = NULL, seNorm, hold; + int i, j, m; + MYBOOL isDEVEX, ok = applyPricer(lp); + + if(ok && (lp->edgeVector[0] < 0) && (isdual == AUTOMATIC)) + ok = FALSE; + + if(!ok) + return( ok ); + + /* Store the active/current pricing type */ + if(isdual == AUTOMATIC) + isdual = (MYBOOL) lp->edgeVector[0]; + else + lp->edgeVector[0] = isdual; + + m = lp->rows; + + /* Determine strategy and check if we have strategy fallback for the primal */ + isDEVEX = is_piv_rule(lp, PRICER_DEVEX); + if(!isDEVEX && !isdual) + isDEVEX = is_piv_mode(lp, PRICE_PRIMALFALLBACK); + + /* Check if we only need to do the simple DEVEX initialization */ + if(!is_piv_mode(lp, PRICE_TRUENORMINIT)) { + if(isdual) { + for(i = 1; i <= m; i++) + lp->edgeVector[lp->var_basic[i]] = 1.0; + } + else { + for(i = 1; i <= lp->sum; i++) + if(!lp->is_basic[i]) + lp->edgeVector[i] = 1.0; + } + return( ok ); + } + + /* Otherwise do the full Steepest Edge norm initialization */ + ok = allocREAL(lp, &sEdge, m+1, FALSE); + if(!ok) + return( ok ); + + if(isdual) { + + /* Extract the rows of the basis inverse and compute their squared norms */ + + for(i = 1; i <= m; i++) { + + bsolve(lp, i, sEdge, NULL, 0, 0.0); + + /* Compute the edge norm */ + seNorm = 0; + for(j = 1; j <= m; j++) { + hold = sEdge[j]; + seNorm += hold*hold; + } + + j = lp->var_basic[i]; + lp->edgeVector[j] = seNorm; + } + + } + else { + + /* Solve a=Bb for b over all non-basic variables and compute their squared norms */ + + for(i = 1; i <= lp->sum; i++) { + if(lp->is_basic[i]) + continue; + + fsolve(lp, i, sEdge, NULL, 0, 0.0, FALSE); + + /* Compute the edge norm */ + seNorm = 1; + for(j = 1; j <= m; j++) { + hold = sEdge[j]; + seNorm += hold*hold; + } + + lp->edgeVector[i] = seNorm; + } + + } + + FREE(sEdge); + + return( ok ); + +} + + +STATIC MYBOOL formWeights(lprec *lp, int colnr, LPSREAL *pcol, LPSREAL **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); + + if(ok) { + if(pcol == NULL) + fsolve(lp, colnr, *w, NULL, 0.0, 0.0, FALSE); + else { + MEMCOPY(*w, pcol, lp->rows+1); +/* *w[0] = 0; */ /* Test */ + } + } +/* + if(pcol != NULL) { + LPSREAL cEdge, hold; + int i; + + cEdge = 0; + for(i = 1; i <= m; i++) { + hold = *w[i]-pcol[i]; + cEdge += hold*hold; + } + cEdge /= m; + cEdge = sqrt(cEdge); + if(cEdge > lp->epspivot) + report(lp, SEVERE, "updatePricer: MRS error is %g\n", cEdge); + } +*/ + return(ok); +} +STATIC void freeWeights(LPSREAL *w) +{ + FREE(w); +} + + +STATIC MYBOOL updatePricer(lprec *lp, int rownr, int colnr, LPSREAL *pcol, LPSREAL *prow, int *nzprow) +{ + LPSREAL *vEdge = NULL, cEdge, hold, *newEdge, *w = NULL; + int i, m, n, exitcol, errlevel = DETAILED; + MYBOOL forceRefresh = FALSE, isDual, isDEVEX, ok = FALSE; + + if(!applyPricer(lp)) + return(ok); + + /* Make sure we have something to update */ + hold = lp->edgeVector[0]; + if(hold < 0) + return(ok); + isDual = (MYBOOL) (hold > 0); + + /* Do common initializations and computations */ + m = lp->rows; + n = lp->sum; + isDEVEX = is_piv_rule(lp, PRICER_DEVEX); + exitcol = lp->var_basic[rownr]; + + /* Solve/copy Bw = a */ +#if 0 + ok = formWeights(lp, colnr, NULL, &w); /* Compute from scratch - Experimental */ +#else + ok = formWeights(lp, colnr, pcol, &w); /* Use previously computed values */ +#endif + if(!ok) + return( ok ); + + /* Price norms for the dual simplex - the basic columns */ + if(isDual) { + LPSREAL rw; + int targetcol; + + /* Don't need to compute cross-products with DEVEX */ + if(!isDEVEX) { + ok = allocREAL(lp, &vEdge, m+1, FALSE); + if(!ok) + return( ok ); + + /* Extract the row of the inverse containing the leaving variable + and then form the dot products against the other variables, i.e. "Tau" */ +#if 0 /* Extract row explicitly */ + bsolve(lp, rownr, vEdge, 0, 0.0); +#else /* Reuse previously extracted row data */ + MEMCOPY(vEdge, prow, m+1); + vEdge[0] = 0; +#endif + lp->bfp_ftran_normal(lp, vEdge, NULL); + } + + /* Update the squared steepest edge norms; first store some constants */ + 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; + +#ifdef Paranoia + if(lp->edgeVector[colnr] <= lp->epsmachine) + report(lp, errlevel, "updatePricer: Invalid dual norm %g at entering index %d - iteration %.0f\n", + lp->edgeVector[colnr], rownr, (double) (lp->total_iter+lp->current_iter)); +#endif + + /* Then loop over all basic variables, but skip the leaving row */ + for(i = 1; i <= m; i++) { + if(i == rownr) + continue; + targetcol = lp->var_basic[i]; + hold = w[i]; + if(hold == 0) + continue; + hold /= rw; + if(fabs(hold) < lp->epsmachine) + continue; + + newEdge = &(lp->edgeVector[targetcol]); + *newEdge += (hold*hold) * cEdge; + if(isDEVEX) { + if((*newEdge) > DEVEX_RESTARTLIMIT) { + forceRefresh = TRUE; + break; + } + } + else { + *newEdge -= 2*hold*vEdge[i]; +#ifdef xxApplySteepestEdgeMinimum + SETMAX(*newEdge, hold*hold+1); /* Kludge; use the primal lower bound */ +#else + if(*newEdge <= 0) { + report(lp, errlevel, "updatePricer: Invalid dual norm %g at index %d - iteration %.0f\n", + *newEdge, i, (double) (lp->total_iter+lp->current_iter)); + forceRefresh = TRUE; + break; + } +#endif + } + } + + + } + /* Price norms for the primal simplex - the non-basic columns */ + else { + + LPSREAL *vTemp = NULL, *vAlpha = NULL, cAlpha; + int *coltarget; + + ok = allocREAL(lp, &vTemp, m+1, TRUE) && + allocREAL(lp, &vAlpha, n+1, TRUE); + if(!ok) + return( ok ); + + /* Check if we have strategy fallback for the primal */ + if(!isDEVEX) + isDEVEX = is_piv_mode(lp, PRICE_PRIMALFALLBACK); + + /* Initialize column target array */ + coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->sum+1, sizeof(*coltarget)); + ok = get_colIndexA(lp, SCAN_SLACKVARS+SCAN_USERVARS+USE_NONBASICVARS, coltarget, FALSE); + if(!ok) { + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + return( ok ); + } + + /* Don't need to compute cross-products with DEVEX */ + if(!isDEVEX) { + ok = allocREAL(lp, &vEdge, n+1, TRUE); + if(!ok) + return( ok ); + + /* Compute v and then N'v */ + MEMCOPY(vTemp, w, m+1); + bsolve(lp, -1, vTemp, NULL, lp->epsmachine*DOUBLEROUND, 0.0); + vTemp[0] = 0; + prod_xA(lp, coltarget, vTemp, NULL, lp->epsmachine, 0.0, + vEdge, NULL, MAT_ROUNDDEFAULT); + } + + /* Compute Sigma and then Alpha */ + bsolve(lp, rownr, vTemp, NULL, 0*DOUBLEROUND, 0.0); + vTemp[0] = 0; + prod_xA(lp, coltarget, vTemp, NULL, lp->epsmachine, 0.0, + vAlpha, NULL, MAT_ROUNDDEFAULT); + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + + /* 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; + lp->edgeVector[exitcol] = (hold*hold) * cEdge; + +#ifdef Paranoia + if(lp->edgeVector[exitcol] <= lp->epsmachine) + report(lp, errlevel, "updatePricer: Invalid primal norm %g at leaving index %d - iteration %.0f\n", + lp->edgeVector[exitcol], exitcol, (double) (lp->total_iter+lp->current_iter)); +#endif + + /* Then loop over all non-basic variables, but skip the entering column */ + for(i = 1; i <= lp->sum; i++) { + if(lp->is_basic[i] || (i == colnr)) + continue; + hold = vAlpha[i]; + if(hold == 0) + continue; + hold /= cAlpha; + if(fabs(hold) < lp->epsmachine) + continue; + + newEdge = &(lp->edgeVector[i]); + *newEdge += (hold*hold) * cEdge; + if(isDEVEX) { + if((*newEdge) > DEVEX_RESTARTLIMIT) { + forceRefresh = TRUE; + break; + } + } + else { + *newEdge -= 2*hold*vEdge[i]; +#ifdef ApplySteepestEdgeMinimum + SETMAX(*newEdge, hold*hold+1); +#else + if(*newEdge < 0) { + report(lp, errlevel, "updatePricer: Invalid primal norm %g at index %d - iteration %.0f\n", + *newEdge, i, (double) (lp->total_iter+lp->current_iter)); + if(lp->spx_trace) + report(lp, errlevel, "Error detail: (RelAlpha=%g, vEdge=%g, cEdge=%g)\n", hold, vEdge[i], cEdge); + forceRefresh = TRUE; + break; + } +#endif + } + } + +Finish1: + FREE(vAlpha); + FREE(vTemp); + + } + +Finish2: + FREE(vEdge); + freeWeights(w); + + if(forceRefresh) + ok = restartPricer(lp, AUTOMATIC); + else + ok = TRUE; + + return( ok ); + +} + + +STATIC MYBOOL verifyPricer(lprec *lp) +{ + LPSREAL value; + int i, n; + MYBOOL ok = applyPricer(lp); + + if(!ok) + return( ok ); + ok = FALSE; + + /* Verify */ + if(lp->edgeVector == NULL) + return( ok ); + value = *lp->edgeVector; + if(value < 0) + return( ok ); + + /* Check the primal */ + n = 1; + if(value == 0) { + + for(n = lp->sum; n > 0; n--) { + if(lp->is_basic[n]) + continue; + value = lp->edgeVector[n]; + if(value <= 0) + break; + } + } + /* Check the dual */ + else { + for(i = lp->rows; i > 0; i--) { + n = lp->var_basic[i]; + value = lp->edgeVector[n]; + if(value <= 0) + break; + } + } + + ok = (MYBOOL) (n == 0); +#ifdef Paranoia + if(!ok) + report(lp, SEVERE, "verifyPricer: Invalid norm %g at index %d\n", + value, n); +#endif + return( ok ); +} + diff --git a/src/external/lpsolve/build/lp_solve/lp_report.c b/src/external/lpsolve/build/lp_solve/lp_report.c new file mode 100644 index 00000000..f38fa3b9 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_report.c @@ -0,0 +1,790 @@ + +/* + Mixed integer programming optimization drivers for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2), + Kjell Eikland + Contact: + License terms: LGPL. + + Requires: stdarg.h, lp_lib.h + + Release notes: + v5.0.0 3 1 January 2004 New unit isolating reporting routines. + v5.2.0.0 1 December 2005 Addition of Matrix Market writing function. + + ---------------------------------------------------------------------------------- +*/ + +#include +#include +#include + +#include "lp_lib.h" +#include "lp_scale.h" +#include "commonlib.h" +#include "lp_report.h" + +#include "mmio.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +/* Define buffer-size controled function mapping */ +# if defined _MSC_VER +# define vsnprintf _vsnprintf +# endif + +/* Various reporting functions for lp_solve */ +/* ------------------------------------------------------------------------- */ + +/* First define general utilties for reporting and output */ +char * __VACALL explain(lprec *lp, char *format, ...) +{ + char buff[DEF_STRBUFSIZE+1]; + va_list ap; + + va_start(ap, format); + vsnprintf(buff, DEF_STRBUFSIZE, format, ap); + 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; + + if(lp == NULL) { + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + } + else if(level <= lp->verbose) { + 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) + fflush(lp->outstream); + } + } +#ifdef xParanoia + if(level == CRITICAL) + raise(SIGSEGV); +#endif +} + +STATIC void print_indent(lprec *lp) +{ + int i; + + report(lp, NEUTRAL, "%2d", lp->bb_level); + if(lp->bb_level < 50) /* useless otherwise */ + for(i = lp->bb_level; i > 0; i--) + report(lp, NEUTRAL, "--"); + else + report(lp, NEUTRAL, " *** too deep ***"); + report(lp, NEUTRAL, "> "); +} /* print_indent */ + +STATIC void debug_print(lprec *lp, char *format, ...) +{ + va_list ap; + + if(lp->bb_trace) { + print_indent(lp); + if (lp == NULL) + { + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(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); + } + } +} /* debug_print */ + +STATIC void debug_print_solution(lprec *lp) +{ + int i; + + if(lp->bb_trace) + for (i = lp->rows + 1; i <= lp->sum; i++) { + print_indent(lp); + report(lp, NEUTRAL, "%s " RESULTVALUEMASK "\n", + get_col_name(lp, i - lp->rows), + (double)lp->solution[i]); + } +} /* debug_print_solution */ + +STATIC void debug_print_bounds(lprec *lp, LPSREAL *upbo, LPSREAL *lowbo) +{ + int i; + + if(lp->bb_trace) + for(i = lp->rows + 1; i <= lp->sum; i++) { + if(lowbo[i] == upbo[i]) { + print_indent(lp); + report(lp, NEUTRAL, "%s = " RESULTVALUEMASK "\n", get_col_name(lp, i - lp->rows), + (double)lowbo[i]); + } + else { + if(lowbo[i] != 0) { + print_indent(lp); + report(lp, NEUTRAL, "%s > " RESULTVALUEMASK "\n", get_col_name(lp, i - lp->rows), + (double)lowbo[i]); + } + if(upbo[i] != lp->infinite) { + print_indent(lp); + report(lp, NEUTRAL, "%s < " RESULTVALUEMASK "\n", get_col_name(lp, i - lp->rows), + (double)upbo[i]); + } + } + } +} /* debug_print_bounds */ + +/* List a vector of LREAL values for the given index range */ +void blockWriteLREAL(FILE *output, char *label, LREAL *vector, int first, int last) +{ + int i, k = 0; + + fprintf(output, "%s", label); + fprintf(output, "\n"); + for(i = first; i <= last; i++) { + fprintf(output, " %18g", vector[i]); + k++; + if(my_mod(k, 4) == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) + fprintf(output, "\n"); +} + +/* List the current user data matrix columns over the selected row range */ +void blockWriteAMAT(FILE *output, const char *label, lprec* lp, int first, int last) +{ + int i, j, k = 0; + int nzb, nze, jb; + double hold; + MATrec *mat = lp->matA; + + if(!mat_validate(mat)) + return; + if(first < 0) + first = 0; + if(last < 0) + last = lp->rows; + + fprintf(output, "%s", label); + fprintf(output, "\n"); + + if(first == 0) { + for(j = 1; j <= lp->columns; j++) { + hold = get_mat(lp, 0, j); + fprintf(output, " %18g", hold); + k++; + if(my_mod(k, 4) == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) { + fprintf(output, "\n"); + k = 0; + } + first++; + } + nze = mat->row_end[first-1]; + for(i = first; i <= last; i++) { + nzb = nze; + nze = mat->row_end[i]; + if(nzb >= nze) + jb = lp->columns+1; + else + jb = ROW_MAT_COLNR(nzb); + for(j = 1; j <= lp->columns; j++) { + if(j < jb) + hold = 0; + else { + hold = get_mat(lp, i, j); + nzb++; + if(nzb < nze) + jb = ROW_MAT_COLNR(nzb); + else + jb = lp->columns+1; + } + fprintf(output, " %18g", hold); + k++; + if(my_mod(k, 4) == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) + fprintf(output, "\n"); +} + +/* List the current basis matrix columns over the selected row range */ +void blockWriteBMAT(FILE *output, const char *label, lprec* lp, int first, int last) +{ + int i, j, jb, k = 0; + double hold; + + if(first < 0) + first = 0; + if(last < 0) + last = lp->rows; + + fprintf(output, "%s", label); + fprintf(output, "\n"); + + for(i = first; i <= last; i++) { + for(j = 1; j <= lp->rows; j++) { + jb = lp->var_basic[j]; + if(jb <= lp->rows) { + if(jb == i) + hold = 1; + else + hold = 0; + } + else + hold = get_mat(lp, i, j); + if(i == 0) + modifyOF1(lp, jb, &hold, 1); + hold = unscaled_mat(lp, hold, i, jb); + fprintf(output, " %18g", hold); + k++; + if(my_mod(k, 4) == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) + fprintf(output, "\n"); +} + +/* Do a generic readable data dump of key lp_solve model variables; + principally for run difference and debugging purposes */ +MYBOOL REPORT_debugdump(lprec *lp, char *filename, MYBOOL livedata) +{ + FILE *output = stdout; + MYBOOL ok; + + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); + if(!ok) + return(ok); + if((filename == NULL) && (lp->outstream != NULL)) + output = lp->outstream; + + fprintf(output, "\nGENERAL INFORMATION\n-------------------\n\n"); + fprintf(output, "Model size: %d rows (%d equalities, %d Lagrangean), %d columns (%d integers, %d SC, %d SOS, %d GUB)\n", + lp->rows, lp->equalities, get_Lrows(lp), lp->columns, + lp->int_vars, lp->sc_vars, SOS_count(lp), GUB_count(lp)); + fprintf(output, "Data size: %d model non-zeros, %d invB non-zeros (engine is %s)\n", + get_nonzeros(lp), my_if(lp->invB == NULL, 0, lp->bfp_nonzeros(lp, FALSE)), lp->bfp_name()); + fprintf(output, "Internal sizes: %d rows allocated, %d columns allocated, %d columns used, %d eta length\n", + lp->rows_alloc, lp->columns_alloc, lp->columns, my_if(lp->invB == NULL, 0, lp->bfp_colcount(lp))); + fprintf(output, "Memory use: %d sparse matrix, %d eta\n", + lp->matA->mat_alloc, my_if(lp->invB == NULL, 0, lp->bfp_memallocated(lp))); + fprintf(output, "Parameters: Maximize=%d, Names used=%d, Scalingmode=%d, Presolve=%d, SimplexPivot=%d\n", + is_maxim(lp), lp->names_used, lp->scalemode, lp->do_presolve, lp->piv_strategy); + fprintf(output, "Precision: EpsValue=%g, EpsPrimal=%g, EpsDual=%g, EpsPivot=%g, EpsPerturb=%g\n", + lp->epsvalue, lp->epsprimal, lp->epsdual, lp->epspivot, lp->epsperturb); + fprintf(output, "Stability: AntiDegen=%d, Improvement=%d, Split variables at=%g\n", + lp->improve, lp->anti_degen, lp->negrange); + fprintf(output, "B&B settings: BB pivot rule=%d, BB branching=%s, BB strategy=%d, Integer precision=%g, MIP gaps=%g,%g\n", + lp->bb_rule, my_boolstr(lp->bb_varbranch), lp->bb_floorfirst, lp->epsint, lp->mip_absgap, lp->mip_relgap); + + fprintf(output, "\nCORE DATA\n---------\n\n"); + blockWriteINT(output, "Column starts", lp->matA->col_end, 0, lp->columns); + blockWriteINT(output, "row_type", lp->row_type, 0, lp->rows); + blockWriteREAL(output, "orig_rhs", lp->orig_rhs, 0, lp->rows); + blockWriteREAL(output, "orig_lowbo", lp->orig_lowbo, 0, lp->sum); + blockWriteREAL(output, "orig_upbo", lp->orig_upbo, 0, lp->sum); + blockWriteINT(output, "row_type", lp->row_type, 0, lp->rows); + blockWriteBOOL(output, "var_type", lp->var_type, 0, lp->columns, TRUE); + blockWriteAMAT(output, "A", lp, 0, lp->rows); + + if(livedata) { + fprintf(output, "\nPROCESS DATA\n------------\n\n"); + blockWriteREAL(output, "Active rhs", lp->rhs, 0, lp->rows); + blockWriteINT(output, "Basic variables", lp->var_basic, 0, lp->rows); + blockWriteBOOL(output, "is_basic", lp->is_basic, 0, lp->sum, TRUE); + blockWriteREAL(output, "lowbo", lp->lowbo, 0, lp->sum); + blockWriteREAL(output, "upbo", lp->upbo, 0, lp->sum); + if(lp->scalars != NULL) + blockWriteREAL(output, "scalars", lp->scalars, 0, lp->sum); + } + + if(filename != NULL) + fclose(output); + return(ok); +} + + +/* High level reports for model results */ + +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]); + fflush(lp->outstream); +} + +void REPORT_solution(lprec *lp, int columns) +{ + int i, j, n; + LPSREAL value; + presolveundorec *psundo = lp->presolve_undo; + MYBOOL NZonly = (MYBOOL) ((lp->print_sol & AUTOMATIC) > 0); + + if(lp->outstream == NULL) + return; + + fprintf(lp->outstream, "\nActual values of the variables:\n"); + if(columns <= 0) + columns = 2; + n = 0; + for(i = 1; i <= psundo->orig_columns; i++) { + j = psundo->orig_rows + i; + value = get_var_primalresult(lp, j); + if(NZonly && (fabs(value) < lp->epsprimal)) + continue; + n = (n+1) % columns; + fprintf(lp->outstream, "%-20s %12g", get_origcol_name(lp, i), (double) value); + if(n == 0) + fprintf(lp->outstream, "\n"); + else + fprintf(lp->outstream, " "); + } + + fflush(lp->outstream); +} /* REPORT_solution */ + +void REPORT_constraints(lprec *lp, int columns) +{ + int i, n; + LPSREAL value; + MYBOOL NZonly = (MYBOOL) ((lp->print_sol & AUTOMATIC) > 0); + + if(lp->outstream == NULL) + return; + + if(columns <= 0) + columns = 2; + + fprintf(lp->outstream, "\nActual values of the constraints:\n"); + n = 0; + for(i = 1; i <= lp->rows; i++) { + value = (double)lp->best_solution[i]; + if(NZonly && (fabs(value) < lp->epsprimal)) + continue; + n = (n+1) % columns; + fprintf(lp->outstream, "%-20s %12g", get_row_name(lp, i), value); + if(n == 0) + fprintf(lp->outstream, "\n"); + else + fprintf(lp->outstream, " "); + } + + fflush(lp->outstream); +} + +void REPORT_duals(lprec *lp) +{ + int i; + LPSREAL *duals, *dualsfrom, *dualstill, *objfrom, *objtill, *objfromvalue; + MYBOOL ret; + + if(lp->outstream == NULL) + return; + + ret = get_ptr_sensitivity_objex(lp, &objfrom, &objtill, &objfromvalue, NULL); + if(ret) { + fprintf(lp->outstream, "\nObjective function limits:\n"); + fprintf(lp->outstream, " From Till FromValue\n"); + for(i = 1; i <= lp->columns; i++) + if(!is_splitvar(lp, i)) + fprintf(lp->outstream, "%-20s %15.7g %15.7g %15.7g\n", get_col_name(lp, i), + (double)objfrom[i - 1], (double)objtill[i - 1], (double)objfromvalue[i - 1]); + } + + ret = get_ptr_sensitivity_rhs(lp, &duals, &dualsfrom, &dualstill); + if(ret) { + fprintf(lp->outstream, "\nDual values with from - till limits:\n"); + fprintf(lp->outstream, " Dual value From Till\n"); + for(i = 1; i <= lp->sum; i++) + fprintf(lp->outstream, "%-20s %15.7g %15.7g %15.7g\n", + (i <= lp->rows) ? get_row_name(lp, i) : get_col_name(lp, i - lp->rows), + (double)duals[i - 1], (double)dualsfrom[i - 1], (double)dualstill[i - 1]); + fflush(lp->outstream); + } +} + +/* Printing of sensitivity analysis reports */ +void REPORT_extended(lprec *lp) +{ + int i, j; + LPSREAL hold; + LPSREAL *duals, *dualsfrom, *dualstill, *objfrom, *objtill; + MYBOOL ret; + + ret = get_ptr_sensitivity_obj(lp, &objfrom, &objtill); + report(lp, NORMAL, " \n"); + report(lp, NORMAL, "Primal objective:\n"); + report(lp, NORMAL, " \n"); + report(lp, NORMAL, " Column name Value Objective Min Max\n"); + report(lp, NORMAL, " --------------------------------------------------------------------------\n"); + for(j = 1; j <= lp->columns; j++) { + hold = get_mat(lp,0,j); + report(lp, NORMAL, " %-25s " MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK "\n", + get_col_name(lp,j), + my_precision(hold,lp->epsprimal), + my_precision(hold*lp->best_solution[lp->rows+j],lp->epsprimal), + my_precision((ret) ? objfrom[j - 1] : 0.0,lp->epsprimal), + my_precision((ret) ? objtill[j - 1] : 0.0,lp->epsprimal)); + } + report(lp, NORMAL, " \n"); + + ret = get_ptr_sensitivity_rhs(lp, &duals, &dualsfrom, &dualstill); + report(lp, NORMAL, "Primal variables:\n"); + report(lp, NORMAL, " \n"); + report(lp, NORMAL, " Column name Value Slack Min Max\n"); + report(lp, NORMAL, " --------------------------------------------------------------------------\n"); + for(j = 1; j <= lp->columns; j++) + report(lp, NORMAL, " %-25s " MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK "\n", + get_col_name(lp,j), + my_precision(lp->best_solution[lp->rows+j],lp->epsprimal), + my_precision(my_inflimit(lp, (ret) ? duals[lp->rows+j-1] : 0.0),lp->epsprimal), + my_precision((ret) ? dualsfrom[lp->rows+j-1] : 0.0,lp->epsprimal), + my_precision((ret) ? dualstill[lp->rows+j-1] : 0.0,lp->epsprimal)); + + report(lp, NORMAL, " \n"); + report(lp, NORMAL, "Dual variables:\n"); + report(lp, NORMAL, " \n"); + report(lp, NORMAL, " Row name Value Slack Min Max\n"); + report(lp, NORMAL, " --------------------------------------------------------------------------\n"); + for(i = 1; i <= lp->rows; i++) + report(lp, NORMAL, " %-25s " MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK "\n", + get_row_name(lp,i), + my_precision((ret) ? duals[i - 1] : 0.0, lp->epsprimal), + my_precision(lp->best_solution[i], lp->epsprimal), + my_precision((ret) ? dualsfrom[i - 1] : 0.0,lp->epsprimal), + my_precision((ret) ? dualstill[i - 1] : 0.0,lp->epsprimal)); + + report(lp, NORMAL, " \n"); +} + +/* A more readable lp-format report of the model; antiquated and not updated */ +void REPORT_lp(lprec *lp) +{ + int i, j; + + if(lp->outstream == NULL) + return; + + fprintf(lp->outstream, "Model name: %s\n", get_lp_name(lp)); + fprintf(lp->outstream, " "); + + for(j = 1; j <= lp->columns; j++) + fprintf(lp->outstream, "%8s ", get_col_name(lp,j)); + + fprintf(lp->outstream, "\n%simize ", (is_maxim(lp) ? "Max" : "Min")); + for(j = 1; j <= lp->columns; j++) + fprintf(lp->outstream, "%8g ", get_mat(lp, 0, j)); + fprintf(lp->outstream, "\n"); + + for(i = 1; i <= lp->rows; i++) { + fprintf(lp->outstream, "%-9s ", get_row_name(lp, i)); + for(j = 1; j <= lp->columns; j++) + fprintf(lp->outstream, "%8g ", get_mat(lp, i, j)); + if(is_constr_type(lp, i, GE)) + fprintf(lp->outstream, ">= "); + else if(is_constr_type(lp, i, LE)) + fprintf(lp->outstream, "<= "); + else + fprintf(lp->outstream, " = "); + fprintf(lp->outstream, "%8g", get_rh(lp, i)); + + if(is_constr_type(lp, i, GE)) { + if(get_rh_upper(lp, i) < lp->infinite) + fprintf(lp->outstream, " %s = %8g", "upbo", get_rh_upper(lp, i)); + } + else if(is_constr_type(lp, i, LE)) { + if(get_rh_lower(lp, i) > -lp->infinite) + fprintf(lp->outstream, " %s = %8g", "lowbo", get_rh_lower(lp, i)); + } + fprintf(lp->outstream, "\n"); + } + + fprintf(lp->outstream, "Type "); + for(i = 1; i <= lp->columns; i++) { + if(is_int(lp,i)) + fprintf(lp->outstream, " Int "); + else + fprintf(lp->outstream, " Real "); + } + + fprintf(lp->outstream, "\nupbo "); + for(i = 1; i <= lp->columns; i++) + if(get_upbo(lp, i) >= lp->infinite) + fprintf(lp->outstream, " Inf "); + else + fprintf(lp->outstream, "%8g ", get_upbo(lp, i)); + fprintf(lp->outstream, "\nlowbo "); + for(i = 1; i <= lp->columns; i++) + if(get_lowbo(lp, i) <= -lp->infinite) + fprintf(lp->outstream, " -Inf "); + else + fprintf(lp->outstream, "%8g ", get_lowbo(lp, i)); + fprintf(lp->outstream, "\n"); + + fflush(lp->outstream); +} + +/* Report the scaling factors used; extremely rarely used */ +void REPORT_scales(lprec *lp) +{ + int i, colMax; + + colMax = lp->columns; + + if(lp->outstream == NULL) + return; + + if(lp->scaling_used) { + fprintf(lp->outstream, "\nScale factors:\n"); + for(i = 0; i <= lp->rows + colMax; i++) + fprintf(lp->outstream, "%-20s scaled at %g\n", + (i <= lp->rows) ? get_row_name(lp, i) : get_col_name(lp, i - lp->rows), + (double)lp->scalars[i]); + } + fflush(lp->outstream); +} + +/* Report the traditional tableau corresponding to the current basis */ +MYBOOL REPORT_tableau(lprec *lp) +{ + int j, row_nr, *coltarget; + LPSREAL *prow = NULL; + FILE *stream = lp->outstream; + + if(lp->outstream == NULL) + return(FALSE); + + if(!lp->model_is_valid || !has_BFP(lp) || + (get_total_iter(lp) == 0) || (lp->spx_status == NOTRUN)) { + lp->spx_status = NOTRUN; + return(FALSE); + } + if(!allocREAL(lp, &prow,lp->sum + 1, TRUE)) { + lp->spx_status = NOMEMORY; + return(FALSE); + } + + fprintf(stream, "\n"); + fprintf(stream, "Tableau at iter %.0f:\n", (double) get_total_iter(lp)); + + for(j = 1; j <= lp->sum; j++) + if (!lp->is_basic[j]) + fprintf(stream, "%15d", (j <= lp->rows ? + (j + lp->columns) * ((lp->orig_upbo[j] == 0) || + (is_chsign(lp, j)) ? 1 : -1) : j - lp->rows) * + (lp->is_lower[j] ? 1 : -1)); + fprintf(stream, "\n"); + + coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->columns+1, sizeof(*coltarget)); + if(!get_colIndexA(lp, SCAN_USERVARS+USE_NONBASICVARS, coltarget, FALSE)) { + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + return(FALSE); + } + for(row_nr = 1; (row_nr <= lp->rows + 1); row_nr++) { + if (row_nr <= lp->rows) + fprintf(stream, "%3d", (lp->var_basic[row_nr] <= lp->rows ? + (lp->var_basic[row_nr] + lp->columns) * ((lp->orig_upbo[lp->var_basic [row_nr]] == 0) || + (is_chsign(lp, lp->var_basic[row_nr])) ? 1 : -1) : lp->var_basic[row_nr] - lp->rows) * + (lp->is_lower[lp->var_basic [row_nr]] ? 1 : -1)); + else + fprintf(stream, " "); + bsolve(lp, row_nr <= lp->rows ? row_nr : 0, prow, NULL, lp->epsmachine*DOUBLEROUND, 1.0); + prod_xA(lp, coltarget, prow, NULL, lp->epsmachine, 1.0, + prow, NULL, MAT_ROUNDDEFAULT); + + for(j = 1; j <= lp->rows + lp->columns; j++) + if (!lp->is_basic[j]) + fprintf(stream, "%15.7f", prow[j] * (lp->is_lower[j] ? 1 : -1) * + (row_nr <= lp->rows ? 1 : -1)); + fprintf(stream, "%15.7f", lp->rhs[row_nr <= lp->rows ? row_nr : 0] * + (double) ((row_nr <= lp->rows) || (is_maxim(lp)) ? 1 : -1)); + fprintf(stream, "\n"); + } + fflush(stream); + + mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); + FREE(prow); + return(TRUE); +} + +void REPORT_constraintinfo(lprec *lp, char *datainfo) +{ + int i, tally[ROWCLASS_MAX+1]; + + MEMCLEAR(tally, ROWCLASS_MAX+1); + for(i = 1; i <= lp->rows; i++) + tally[get_constr_class(lp, i)]++; + + if(datainfo != NULL) + report(lp, NORMAL, "%s\n", datainfo); + + for(i = 0; i <= ROWCLASS_MAX; i++) + if(tally[i] > 0) + report(lp, NORMAL, "%-15s %4d\n", get_str_constr_class(lp, i), tally[i]); +} + +void REPORT_modelinfo(lprec *lp, MYBOOL doName, char *datainfo) +{ + if(doName) { + report(lp, NORMAL, "\nModel name: '%s' - run #%-5d\n", + get_lp_name(lp), lp->solvecount); + report(lp, NORMAL, "Objective: %simize(%s)\n", + my_if(is_maxim(lp), "Max", "Min"), get_row_name(lp, 0)); + report(lp, NORMAL, " \n"); + } + if(datainfo != NULL) + report(lp, NORMAL, "%s\n", datainfo); + + report(lp, NORMAL, "Model size: %7d constraints, %7d variables, %12d non-zeros.\n", + lp->rows, lp->columns, get_nonzeros(lp)); + if(GUB_count(lp)+SOS_count(lp) > 0) + report(lp, NORMAL, "Var-types: %7d integer, %7d semi-cont., %7d SOS.\n", + lp->int_vars, lp->sc_vars, lp->sos_vars); + report(lp, NORMAL, "Sets: %7d GUB, %7d SOS.\n", + GUB_count(lp), SOS_count(lp)); +} + +/* Save a matrix column subset to a MatrixMarket formatted file, + say to export the basis matrix for further numerical analysis. + If colndx is NULL, then the full constraint matrix is assumed. */ +MYBOOL REPORT_mat_mmsave(lprec *lp, char *filename, int *colndx, MYBOOL includeOF, char *infotext) +{ + int n, m, nz, i, j, k, kk; + MATrec *mat = lp->matA; + MM_typecode matcode; + FILE *output = stdout; + MYBOOL ok; + LPSREAL *acol = NULL; + int *nzlist = NULL; + + /* Open file */ + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); + if(!ok) + return(ok); + if((filename == NULL) && (lp->outstream != NULL)) + output = lp->outstream; + + /* Compute column and non-zero counts */ + if(colndx == lp->var_basic) { + if(!lp->basis_valid) + return( FALSE ); + m = lp->rows; + } + else if(colndx != NULL) + m = colndx[0]; + else + m = lp->columns; + n = lp->rows; + nz = 0; + + for(j = 1; j <= m; j++) { + k = (colndx == NULL ? n + j : colndx[j]); + if(k > n) { + k -= lp->rows; + nz += mat_collength(mat, k); + if(includeOF && is_OF_nz(lp, k)) + nz++; + } + else + nz++; + } + kk = 0; + if(includeOF) { + n++; /* Row count */ + kk++; /* Row index offset */ + } + + /* Initialize */ + mm_initialize_typecode(&matcode); + mm_set_matrix(&matcode); + mm_set_coordinate(&matcode); + mm_set_real(&matcode); + + mm_write_banner(output, matcode); + mm_write_mtx_crd_size(output, n+kk, m, nz+(colndx == lp->var_basic ? 1 : 0)); + + /* Allocate working arrays for sparse column storage */ + allocREAL(lp, &acol, n+2, FALSE); + allocINT(lp, &nzlist, n+2, FALSE); + + /* Write the matrix non-zero values column-by-column. + NOTE: matrixMarket files use 1-based indeces, + i.e. first row of a vector has index 1, not 0. */ + if(infotext != NULL) { + fprintf(output, "%%\n"); + fprintf(output, "%% %s\n", infotext); + fprintf(output, "%%\n"); + } + if(includeOF && (colndx == lp->var_basic)) + fprintf(output, "%d %d %g\n", 1, 1, 1.0); + for(j = 1; j <= m; j++) { + k = (colndx == NULL ? lp->rows + j : colndx[j]); + if(k == 0) + continue; + nz = obtain_column(lp, k, acol, nzlist, NULL); + for(i = 1; i <= nz; i++) { + if(!includeOF && (nzlist[i] == 0)) + continue; + fprintf(output, "%d %d %g\n", nzlist[i]+kk, j+kk, acol[i]); + } + } + fprintf(output, "%% End of MatrixMarket file\n"); + + /* Finish */ + FREE(acol); + FREE(nzlist); + fclose(output); + + return(ok); +} + diff --git a/src/external/lpsolve/build/lp_solve/lp_rlp.c b/src/external/lpsolve/build/lp_solve/lp_rlp.c new file mode 100644 index 00000000..e2ec7268 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_rlp.c @@ -0,0 +1,2484 @@ +/* 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/external/lpsolve/build/lp_solve/lp_scale.c b/src/external/lpsolve/build/lp_solve/lp_scale.c new file mode 100644 index 00000000..7e344830 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_scale.c @@ -0,0 +1,1075 @@ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_report.h" +#include "lp_scale.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* + Scaling routines for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, lp_scale.h + + Release notes: + v5.0.0 1 January 2004 Significantly expanded and repackaged scaling + routines. + v5.0.1 20 February 2004 Modified rounding behaviour in several areas. + v5.1.0 20 July 2004 Reworked with flexible matrix storage model. + v5.2.0 20 February 2005 Converted to matrix storage model without the OF. + + ---------------------------------------------------------------------------------- +*/ + +/* First define scaling and unscaling primitives */ + +LPSREAL scaled_value(lprec *lp, LPSREAL value, int index) +{ + if(fabs(value) < lp->infinite) { + if(lp->scaling_used) { + if(index > lp->rows) + value /= lp->scalars[index]; + else + value *= lp->scalars[index]; + } + } + else + value = my_sign(value)*lp->infinite; + return(value); +} + +LPSREAL unscaled_value(lprec *lp, LPSREAL value, int index) +{ + if(fabs(value) < lp->infinite) { + if(lp->scaling_used) { + if(index > lp->rows) + value *= lp->scalars[index]; + else + value /= lp->scalars[index]; + } + } + else + value = my_sign(value)*lp->infinite; + return(value); +} + +STATIC LPSREAL scaled_mat(lprec *lp, LPSREAL 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) +{ + if(lp->scaling_used) + value /= lp->scalars[rownr] * lp->scalars[lp->rows + colnr]; + return( value ); +} + +/* 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) +{ + int i, nz; + LPSREAL absvalue, logvalue; + register LPSREAL result; + MATrec *mat = lp->matA; + LPSREAL *value; + int *rownr, *colnr; + + /* Do OF part */ + result = 0; + for(i = 1; i <= lp->columns; i++) { + absvalue = fabs(lp->orig_obj[i]); + if(absvalue > 0) { + logvalue = log(absvalue); + if(_Advanced) + logvalue -= FRowScale[0] + FColScale[i]; + result += logvalue*logvalue; + } + } + + /* Do constraint matrix part */ + mat_validate(mat); + value = &(COL_MAT_VALUE(0)); + rownr = &(COL_MAT_ROWNR(0)); + colnr = &(COL_MAT_COLNR(0)); + nz = get_nonzeros(lp); + for(i = 0; i < nz; + i++, value += matValueStep, rownr += matRowColStep, colnr += matRowColStep) { + absvalue = fabs(*value); + if(absvalue > 0) { + logvalue = log(absvalue); + if(_Advanced) + logvalue -= FRowScale[*rownr] + FColScale[*colnr]; + result += logvalue*logvalue; + } + } + return( result ); +} + +/* Implementation of the Curtis-Reid scaling based on the paper + "On the Automatic Scaling of Matrices for Gaussian + Elimination," Journal of the Institute of Mathematics and + Its Applications (1972) 10, 118-124. + + Solve the system | M E | (r) (sigma) + | | ( ) = ( ) + | E^T N | (c) ( tau ) + + by the conjugate gradient method (clever recurrences). + + E is the matrix A with all elements = 1 + + M is diagonal matrix of row counts (RowCount) + N is diagonal matrix of column counts (ColCount) + + sigma is the vector of row logarithm sums (RowSum) + tau is the vector of column logarithm sums (ColSum) + + r, c are resulting row and column scalings (RowScale, ColScale) */ + +int CurtisReidScales(lprec *lp, MYBOOL _Advanced, LPSREAL *FRowScale, LPSREAL *FColScale) +{ + int i, row, col, ent, nz; + LPSREAL *RowScalem2, *ColScalem2, + *RowSum, *ColSum, + *residual_even, *residual_odd; + LPSREAL sk, qk, ek, + skm1, qkm1, ekm1, + qkm2, qkqkm1, ekm2, ekekm1, + absvalue, logvalue, + StopTolerance; + int *RowCount, *ColCount, colMax; + int Result; + MATrec *mat = lp->matA; + LPSREAL *value; + int *rownr, *colnr; + + if(CurtisReidMeasure(lp, _Advanced, FRowScale, FColScale)<0.1*get_nonzeros(lp)) + return(0); + + /* Allocate temporary memory and find RowSum and ColSum measures */ + nz = get_nonzeros(lp); + colMax = lp->columns; + + allocREAL(lp, &RowSum, lp->rows+1, TRUE); + allocINT(lp, &RowCount, lp->rows+1, TRUE); + allocREAL(lp, &residual_odd, lp->rows+1, TRUE); + + allocREAL(lp, &ColSum, colMax+1, TRUE); + allocINT(lp, &ColCount, colMax+1, TRUE); + allocREAL(lp, &residual_even, colMax+1, TRUE); + + allocREAL(lp, &RowScalem2, lp->rows+1, FALSE); + allocREAL(lp, &ColScalem2, colMax+1, FALSE); + + /* Set origin for row scaling */ + for(i = 1; i <= colMax; i++) { + absvalue=fabs(lp->orig_obj[i]); + if(absvalue>0) { + logvalue = log(absvalue); + ColSum[i] += logvalue; + RowSum[0] += logvalue; + ColCount[i]++; + RowCount[0]++; + } + } + + value = &(COL_MAT_VALUE(0)); + rownr = &(COL_MAT_ROWNR(0)); + colnr = &(COL_MAT_COLNR(0)); + for(i = 0; i < nz; + i++, value += matValueStep, rownr += matRowColStep, colnr += matRowColStep) { + absvalue=fabs(*value); + if(absvalue>0) { + logvalue = log(absvalue); + ColSum[*colnr] += logvalue; + RowSum[*rownr] += logvalue; + ColCount[*colnr]++; + RowCount[*rownr]++; + } + } + + /* Make sure we dont't have division by zero errors */ + for(row = 0; row <= lp->rows; row++) + if(RowCount[row] == 0) + RowCount[row] = 1; + for(col = 1; col <= colMax; col++) + if(ColCount[col] == 0) + ColCount[col] = 1; + + /* Initialize to RowScale = RowCount-1 RowSum + ColScale = 0.0 + residual = ColSum - ET RowCount-1 RowSum */ + + StopTolerance= MAX(lp->scalelimit-floor(lp->scalelimit), DEF_SCALINGEPS); + StopTolerance *= (LPSREAL) nz; + for(row = 0; row <= lp->rows; row++) { + FRowScale[row] = RowSum[row] / (LPSREAL) RowCount[row]; + RowScalem2[row] = FRowScale[row]; + } + + /* Compute initial residual */ + for(col = 1; col <= colMax; col++) { + FColScale[col] = 0; + ColScalem2[col] = 0; + residual_even[col] = ColSum[col]; + + if(lp->orig_obj[col] != 0) + residual_even[col] -= RowSum[0] / (LPSREAL) 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]; + } + } + + /* Compute sk */ + sk = 0; + skm1 = 0; + for(col = 1; col <= colMax; col++) + sk += (residual_even[col]*residual_even[col]) / (LPSREAL) ColCount[col]; + + Result = 0; + qk=1; qkm1=0; qkm2=0; + ek=0; ekm1=0; ekm2=0; + + while(sk>StopTolerance) { + /* Given the values of residual and sk, construct + ColScale (when pass is even) + RowScale (when pass is odd) */ + + qkqkm1 = qk * qkm1; + ekekm1 = ek * ekm1; + if((Result % 2) == 0) { /* pass is even; construct RowScale[pass+1] */ + if(Result != 0) { + for(row = 0; row <= lp->rows; row++) + RowScalem2[row] = FRowScale[row]; + if(qkqkm1 != 0) { + 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]) - + RowScalem2[row] * ekekm1 / qkqkm1); + } + } + } + else { /* pass is odd; construct ColScale[pass+1] */ + for(col = 1; col <= colMax; col++) + ColScalem2[col] = FColScale[col]; + if(qkqkm1 != 0) { + 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) - + ColScalem2[col] * ekekm1 / qkqkm1); + } + } + + /* update residual and sk (pass + 1) */ + if((Result % 2) == 0) { /* even */ + /* residual */ + for(row = 0; row <= lp->rows; row++) + residual_odd[row] *= ek; + + for(i = 1; i <= colMax; i++) + if(lp->orig_obj[i] != 0) + residual_odd[0] += (residual_even[i] / (LPSREAL) 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]); + } + for(row = 0; row <= lp->rows; row++) + residual_odd[row] *= (-1 / qk); + + /* sk */ + skm1 = sk; + sk = 0; + for(row = 0; row <= lp->rows; row++) + sk += (residual_odd[row]*residual_odd[row]) / (LPSREAL) RowCount[row]; + } + else { /* odd */ + /* residual */ + for(col = 1; col <= colMax; col++) + residual_even[col] *= ek; + + for(i = 1; i <= colMax; i++) + if(lp->orig_obj[i] != 0) + residual_even[i] += (residual_odd[0] / (LPSREAL) 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]); + } + for(col = 1; col <= colMax; col++) + residual_even[col] *= (-1 / qk); + + /* sk */ + skm1 = sk; + sk = 0; + for(col = 1; col <= colMax; col++) + sk += (residual_even[col]*residual_even[col]) / (LPSREAL) ColCount[col]; + } + + /* Compute ek and qk */ + ekm2=ekm1; + ekm1=ek; + ek=qk * sk / skm1; + + qkm2=qkm1; + qkm1=qk; + qk=1-ek; + + Result++; + } + + /* Synchronize the RowScale and ColScale vectors */ + ekekm1 = ek * ekm1; + if(qkm1 != 0) { + if((Result % 2) == 0) { /* pass is even, compute RowScale */ + 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]) - + 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) - + ColScalem2[col] * ekekm1 / qkm1); + } + } + + /* Do validation, if indicated */ + if(FALSE && mat_validate(mat)){ + double check, error; + + /* CHECK: M RowScale + E ColScale = RowSum */ + error = 0; + for(row = 0; row <= lp->rows; row++) { + check = (LPSREAL) RowCount[row] * FRowScale[row]; + if(row == 0) { + for(i = 1; i <= colMax; i++) { + if(lp->orig_obj[i] != 0) + check += FColScale[i]; + } + } + else { + i = mat->row_end[row-1]; + ent = mat->row_end[row]; + for(; i < ent; i++) { + col = ROW_MAT_COLNR(i); + check += FColScale[col]; + } + } + check -= RowSum[row]; + error += check*check; + } + + /* CHECK: E^T RowScale + N ColScale = ColSum */ + error = 0; + for(col = 1; col <= colMax; col++) { + check = (LPSREAL) ColCount[col] * FColScale[col]; + + if(lp->orig_obj[col] != 0) + check += FRowScale[0]; + + i = mat->col_end[col-1]; + ent = mat->col_end[col]; + rownr = &(COL_MAT_ROWNR(i)); + for(; i < ent; + i++, rownr += matRowColStep) { + check += FRowScale[*rownr]; + } + check -= ColSum[col]; + error += check*check; + } + } + + /* Convert to scaling factors (rounding to nearest power + of 2 can optionally be done as a separate step later) */ + for(col = 1; col <= colMax; col++) { + absvalue = exp(-FColScale[col]); + if(absvalue < MIN_SCALAR) absvalue = MIN_SCALAR; + if(absvalue > MAX_SCALAR) absvalue = MAX_SCALAR; + if(!is_int(lp,col) || is_integerscaling(lp)) + FColScale[col] = absvalue; + else + FColScale[col] = 1; + } + for(row = 0; row <= lp->rows; row++) { + absvalue = exp(-FRowScale[row]); + if(absvalue < MIN_SCALAR) absvalue = MIN_SCALAR; + if(absvalue > MAX_SCALAR) absvalue = MAX_SCALAR; + FRowScale[row] = absvalue; + } + + /* free temporary memory */ + FREE(RowSum); + FREE(ColSum); + FREE(RowCount); + FREE(ColCount); + FREE(residual_even); + FREE(residual_odd); + FREE(RowScalem2); + FREE(ColScalem2); + + return(Result); + +} + +STATIC MYBOOL scaleCR(lprec *lp, LPSREAL *scaledelta) +{ + LPSREAL *scalechange = NULL; + int Result; + + if(!lp->scaling_used) { + allocREAL(lp, &lp->scalars, lp->sum_alloc + 1, FALSE); + for(Result = 0; Result <= lp->sum; Result++) + lp->scalars[Result] = 1; + lp->scaling_used = TRUE; + } + + if(scaledelta == NULL) + allocREAL(lp, &scalechange, lp->sum + 1, FALSE); + else + scalechange = scaledelta; + + Result=CurtisReidScales(lp, FALSE, scalechange, &scalechange[lp->rows]); + if(Result>0) { + + /* Do the scaling*/ + if(scale_updaterows(lp, scalechange, TRUE) || + scale_updatecolumns(lp, &scalechange[lp->rows], TRUE)) + lp->scalemode |= SCALE_CURTISREID; + + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + } + + if(scaledelta == NULL) + FREE(scalechange); + + return((MYBOOL) (Result > 0)); +} + +STATIC MYBOOL transform_for_scale(lprec *lp, LPSREAL *value) +{ + MYBOOL Accept = TRUE; + *value = fabs(*value); +#ifdef Paranoia + if(*value < lp->epsmachine) { + Accept = FALSE; + report(lp, SEVERE, "transform_for_scale: A zero-valued entry was passed\n"); + } + else +#endif + if(is_scalemode(lp, SCALE_LOGARITHMIC)) + *value = log(*value); + else if(is_scalemode(lp, SCALE_QUADRATIC)) + (*value) *= (*value); + return( Accept ); +} + +STATIC void accumulate_for_scale(lprec *lp, LPSREAL *min, LPSREAL *max, LPSREAL value) +{ + if(transform_for_scale(lp, &value)) { + if(is_scaletype(lp, SCALE_MEAN)) { + *max += value; + *min += 1; + } + else { + SETMAX(*max, value); + SETMIN(*min, value); + } + } +} + +STATIC LPSREAL minmax_to_scale(lprec *lp, LPSREAL min, LPSREAL max, int itemcount) +{ + LPSREAL scale; + + /* Initialize according to transformation / weighting model */ + if(is_scalemode(lp, SCALE_LOGARITHMIC)) + scale = 0; + else + scale = 1; + if(itemcount <= 0) + return(scale); + + /* Compute base scalar according to chosen scaling type */ + if(is_scaletype(lp, SCALE_MEAN)) { + if(min > 0) + scale = max / min; + } + else if(is_scaletype(lp, SCALE_RANGE)) + scale = (max + min) / 2; + else if(is_scaletype(lp, SCALE_GEOMETRIC)) + scale = sqrt(min*max); + else if(is_scaletype(lp, SCALE_EXTREME)) + scale = max; + + /* Compute final scalar according to transformation / weighting model */ + if(is_scalemode(lp, SCALE_LOGARITHMIC)) + scale = exp(-scale); + else if(is_scalemode(lp, SCALE_QUADRATIC)) { + if(scale == 0) + scale = 1; + else + scale = 1 / sqrt(scale); + } + else { + if(scale == 0) + scale = 1; + else + scale = 1 / scale; + } + + /* Make sure we are within acceptable scaling ranges */ + SETMAX(scale, MIN_SCALAR); + SETMIN(scale, MAX_SCALAR); + + return(scale); +} + +STATIC LPSREAL roundPower2(LPSREAL 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 */ +{ + long int power2; + MYBOOL isSmall = FALSE; + + if(scale == 1) + return( scale ); + + /* Obtain the fractional power of 2 */ + if(scale < 2) { + scale = 2 / scale; + isSmall = TRUE; + } + else + scale /= 2; + scale = log(scale)/log(2.0); + + /* Find the desired nearest power of two and compute the associated scalar */ + power2 = (long) ceil(scale-0.5); + scale = 1 << power2; + if(isSmall) + scale = 1.0 / scale; + + return( scale ); + +} + +STATIC MYBOOL scale_updatecolumns(lprec *lp, LPSREAL *scalechange, MYBOOL updateonly) +{ + int i, j; + + /* Verify that the scale change is significant (different from the unit) */ + for(i = lp->columns; i > 0; i--) + if(fabs(scalechange[i]-1) > lp->epsprimal) + break; + if(i <= 0) + return( FALSE ); + + /* Update the pre-existing column scalar */ + if(updateonly) + for(i = 1, j = lp->rows + 1; j <= lp->sum; i++, j++) + lp->scalars[j] *= scalechange[i]; + else + for(i = 1, j = lp->rows + 1; j <= lp->sum; i++, j++) + lp->scalars[j] = scalechange[i]; + + return( TRUE ); +} + +STATIC MYBOOL scale_updaterows(lprec *lp, LPSREAL *scalechange, MYBOOL updateonly) +{ + int i; + + /* Verify that the scale change is significant (different from the unit) */ + for(i = lp->rows; i >= 0; i--) { + if(fabs(scalechange[i]-1) > lp->epsprimal) + break; + } + if(i < 0) + return( FALSE ); + + /* Update the pre-existing row scalar */ + if(updateonly) + for(i = 0; i <= lp->rows; i++) + lp->scalars[i] *= scalechange[i]; + else + for(i = 0; i <= lp->rows; i++) + lp->scalars[i] = scalechange[i]; + + return( TRUE ); +} + +STATIC MYBOOL scale_columns(lprec *lp, LPSREAL *scaledelta) +{ + int i,j, colMax, nz; + LPSREAL *scalechange; + LPSREAL *value; + int *colnr; + MATrec *mat = lp->matA; + + /* Check that columns are in fact targeted */ + if((lp->scalemode & SCALE_ROWSONLY) != 0) + return( TRUE ); + + if(scaledelta == NULL) + scalechange = &lp->scalars[lp->rows]; + else + scalechange = &scaledelta[lp->rows]; + + colMax = lp->columns; + + /* Scale matrix entries (including any Lagrangean constraints) */ + for(i = 1; i <= lp->columns; i++) { + lp->orig_obj[i] *= scalechange[i]; + } + + mat_validate(lp->matA); + nz = get_nonzeros(lp); + value = &(COL_MAT_VALUE(0)); + colnr = &(COL_MAT_COLNR(0)); + for(i = 0; i < nz; + i++, value += matValueStep, colnr += matRowColStep) { + (*value) *= scalechange[*colnr]; + } + + /* Scale variable bounds as well */ + for(i = 1, j = lp->rows + 1; j <= lp->sum; i++, j++) { + if(lp->orig_lowbo[j] > -lp->infinite) + lp->orig_lowbo[j] /= scalechange[i]; + if(lp->orig_upbo[j] < lp->infinite) + lp->orig_upbo[j] /= scalechange[i]; + if(lp->sc_lobound[i] != 0) + lp->sc_lobound[i] /= scalechange[i]; + } + + lp->columns_scaled = TRUE; + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + + return( TRUE ); +} + +STATIC MYBOOL scale_rows(lprec *lp, LPSREAL *scaledelta) +{ + int i, j, nz, colMax; + LPSREAL *scalechange; + LPSREAL *value; + int *rownr; + MATrec *mat = lp->matA; + + + /* Check that rows are in fact targeted */ + if((lp->scalemode & SCALE_COLSONLY) != 0) + return( TRUE ); + + if(scaledelta == NULL) + scalechange = lp->scalars; + else + scalechange = scaledelta; + + colMax = lp->columns; + + /* First row-scale the matrix (including the objective function) */ + for(i = 1; i <= colMax; i++) { + lp->orig_obj[i] *= scalechange[0]; + } + + nz = get_nonzeros(lp); + value = &(COL_MAT_VALUE(0)); + rownr = &(COL_MAT_ROWNR(0)); + for(i = 0; i < nz; + i++, value += matValueStep, rownr += matRowColStep) { + (*value) *= scalechange[*rownr]; + } + + /* ...and scale the rhs and the row bounds (RANGES in MPS!!) */ + for(i = 0; i <= lp->rows; i++) { + if(fabs(lp->orig_rhs[i]) < lp->infinite) + lp->orig_rhs[i] *= scalechange[i]; + + j = lp->presolve_undo->var_to_orig[i]; + if(j != 0) + lp->presolve_undo->fixed_rhs[j] *= scalechange[i]; + + if(lp->orig_upbo[i] < lp->infinite) /* This is the range */ + lp->orig_upbo[i] *= scalechange[i]; + + if((lp->orig_lowbo[i] != 0) && (fabs(lp->orig_lowbo[i]) < lp->infinite)) + lp->orig_lowbo[i] *= scalechange[i]; + } + + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + + return( TRUE ); +} + +STATIC LPSREAL scale(lprec *lp, LPSREAL *scaledelta) +{ + int i, j, nz, row_count, nzOF = 0; + LPSREAL *row_max, *row_min, *scalechange = NULL, absval; + LPSREAL col_max, col_min; + MYBOOL rowscaled, colscaled; + MATrec *mat = lp->matA; + LPSREAL *value; + int *rownr; + + if(is_scaletype(lp, SCALE_NONE)) + return(0.0); + + if(!lp->scaling_used) { + allocREAL(lp, &lp->scalars, lp->sum_alloc + 1, FALSE); + for(i = 0; i <= lp->sum; i++) { + lp->scalars[i] = 1; + } + lp->scaling_used = TRUE; + } +#ifdef Paranoia + else + for(i = 0; i <= lp->sum; i++) { + if(lp->scalars[i] == 0) + report(lp, SEVERE, "scale: Zero-valued scalar found at index %d\n", i); + } +#endif + if(scaledelta == NULL) + allocREAL(lp, &scalechange, lp->sum + 1, FALSE); + else + scalechange = scaledelta; + + /* Must initialize due to computation of scaling statistic - KE */ + for(i = 0; i <= lp->sum; i++) + scalechange[i] = 1; + + row_count = lp->rows; + allocREAL(lp, &row_max, row_count + 1, TRUE); + allocREAL(lp, &row_min, row_count + 1, FALSE); + + /* Initialise min and max values of rows */ + for(i = 0; i <= row_count; i++) { + if(is_scaletype(lp, SCALE_MEAN)) + row_min[i] = 0; /* Carries the count of elements */ + else + row_min[i] = lp->infinite; /* Carries the minimum element */ + } + + /* Calculate row scaling data */ + for(j = 1; j <= lp->columns; j++) { + + absval = lp->orig_obj[j]; + if(absval != 0) { + absval = scaled_mat(lp, absval, 0, j); + accumulate_for_scale(lp, &row_min[0], &row_max[0], absval); + nzOF++; + } + + i = mat->col_end[j - 1]; + value = &(COL_MAT_VALUE(i)); + rownr = &(COL_MAT_ROWNR(i)); + nz = mat->col_end[j]; + for(; i < nz; + i++, value += matValueStep, rownr += matRowColStep) { + absval = scaled_mat(lp, *value, *rownr, j); + accumulate_for_scale(lp, &row_min[*rownr], &row_max[*rownr], absval); + } + } + + /* Calculate scale factors for rows */ + i = 0; + for(; i <= lp->rows; i++) { + if(i == 0) + 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 */ + if(absval == 0) + absval = 1; + scalechange[i] = absval; + } + + FREE(row_max); + FREE(row_min); + + /* Row-scale the matrix (including the objective function and Lagrangean constraints) */ + rowscaled = scale_updaterows(lp, scalechange, TRUE); + + /* Calculate column scales */ + i = 1; + for(j = 1; j <= lp->columns; j++) { + if(is_int(lp,j) && !is_integerscaling(lp)) { /* do not scale integer columns */ + scalechange[lp->rows + j] = 1; + } + else { + col_max = 0; + if(is_scaletype(lp, SCALE_MEAN)) + col_min = 0; + else + col_min = lp->infinite; + + absval = lp->orig_obj[j]; + if(absval != 0) { + absval = scaled_mat(lp, absval, 0, j); + accumulate_for_scale(lp, &col_min, &col_max, absval); + } + + i = mat->col_end[j - 1]; + value = &(COL_MAT_VALUE(i)); + rownr = &(COL_MAT_ROWNR(i)); + nz = mat->col_end[j]; + for(; i < nz; + i++, value += matValueStep, rownr += matRowColStep) { + absval = scaled_mat(lp, *value, *rownr, j); + accumulate_for_scale(lp, &col_min, &col_max, absval); + } + nz = mat_collength(lp->matA, j); + if(fabs(lp->orig_obj[j]) > 0) + nz++; + scalechange[lp->rows + j] = minmax_to_scale(lp, col_min, col_max, nz); + } + } + + /* ... and then column-scale the already row-scaled matrix */ + colscaled = scale_updatecolumns(lp, &scalechange[lp->rows], TRUE); + + /* Create a geometric mean-type measure of the extent of scaling performed; */ + /* ideally, upon successive calls to scale() the value should converge to 0 */ + if(rowscaled || colscaled) { + col_max = 0; + for(j = 1; j <= lp->columns; j++) + col_max += log(scalechange[lp->rows + j]); + col_max = exp(col_max/lp->columns); + + i = 0; + col_min = 0; + for(; i <= lp->rows; i++) + col_min += log(scalechange[i]); + col_min = exp(col_min/row_count); + } + else { + col_max = 1; + col_min = 1; + } + + if(scaledelta == NULL) + FREE(scalechange); + + return(1 - sqrt(col_max*col_min)); +} + +STATIC MYBOOL finalize_scaling(lprec *lp, LPSREAL *scaledelta) +{ + int i; + + /* Check if we should equilibrate */ + if(is_scalemode(lp, SCALE_EQUILIBRATE) && !is_scaletype(lp, SCALE_CURTISREID)) { + int oldmode; + + oldmode = lp->scalemode; + lp->scalemode = SCALE_LINEAR + SCALE_EXTREME; + scale(lp, scaledelta); + lp->scalemode = oldmode; + } + + /* Check if we should prevent rounding errors */ + if(is_scalemode(lp, SCALE_POWER2)) { + LPSREAL *scalars; + if(scaledelta == NULL) + scalars = lp->scalars; + else + scalars = scaledelta; + + for(i = 0; i <= lp->sum; i++) + scalars[i] = roundPower2(scalars[i]); + } + + /* Then transfer the scalars to the model's data */ + return( scale_rows(lp, scaledelta) && scale_columns(lp, scaledelta) ); + +} + +STATIC LPSREAL auto_scale(lprec *lp) +{ + int n = 1; + LPSREAL scalingmetric = 0, *scalenew = NULL; + + if(lp->scaling_used && + ((((lp->scalemode & SCALE_DYNUPDATE) == 0)) || (lp->bb_level > 0))) + return( scalingmetric); + + if(lp->scalemode != SCALE_NONE) { + + /* Allocate array for incremental scaling if appropriate */ + if((lp->solvecount > 1) && (lp->bb_level < 1) && + ((lp->scalemode & SCALE_DYNUPDATE) != 0)) + allocREAL(lp, &scalenew, lp->sum + 1, FALSE); + + if(is_scaletype(lp, SCALE_CURTISREID)) { + scalingmetric = scaleCR(lp, scalenew); + } + else { + LPSREAL scalinglimit, scalingdelta; + int count; + + /* Integer value of scalelimit holds the maximum number of iterations; default to 1 */ + count = (int) floor(lp->scalelimit); + scalinglimit = lp->scalelimit; + if((count == 0) || (scalinglimit == 0)) { + if(scalinglimit > 0) + count = DEF_SCALINGLIMIT; /* A non-zero convergence has been given, default to max 5 iterations */ + else + count = 1; + } + else + scalinglimit -= count; + + /* Scale to desired relative convergence or iteration limit */ + n = 0; + scalingdelta = 1.0; + scalingmetric = 1.0; + while((n < count) && (fabs(scalingdelta) > scalinglimit)) { + n++; + scalingdelta = scale(lp, scalenew); + scalingmetric = scalingmetric*(1+scalingdelta); + } + scalingmetric -= 1; + } + } + + /* Update the inf norm of the elements of the matrix (excluding the OF) */ + mat_computemax(lp->matA); + + /* Check if we really have to do scaling */ + if(lp->scaling_used && (fabs(scalingmetric) >= lp->epsprimal)) + /* Ok, do it */ + finalize_scaling(lp, scalenew); + + else { + + /* Otherwise reset scaling variables */ + if(lp->scalars != NULL) { + FREE(lp->scalars); + } + lp->scaling_used = FALSE; + lp->columns_scaled = FALSE; + } + if(scalenew != NULL) + FREE(scalenew); + + return(scalingmetric); +} + +STATIC void unscale_columns(lprec *lp) +{ + int i, j, nz; + MATrec *mat = lp->matA; + LPSREAL *value; + int *rownr, *colnr; + + if(!lp->columns_scaled) + return; + + /* Unscale OF */ + for(j = 1; j <= lp->columns; j++) { + lp->orig_obj[j] = unscaled_mat(lp, lp->orig_obj[j], 0, j); + } + + /* Unscale mat */ + mat_validate(mat); + nz = get_nonzeros(lp); + value = &(COL_MAT_VALUE(0)); + rownr = &(COL_MAT_ROWNR(0)); + colnr = &(COL_MAT_COLNR(0)); + for(j = 0; j < nz; + j++, value += matValueStep, rownr += matRowColStep, colnr += matRowColStep) { + *value = unscaled_mat(lp, *value, *rownr, *colnr); + } + + /* Unscale bounds as well */ + for(i = lp->rows + 1, j = 1; i <= lp->sum; i++, j++) { + lp->orig_lowbo[i] = unscaled_value(lp, lp->orig_lowbo[i], i); + lp->orig_upbo[i] = unscaled_value(lp, lp->orig_upbo[i], i); + lp->sc_lobound[j] = unscaled_value(lp, lp->sc_lobound[j], i); + } + + for(i = lp->rows + 1; i<= lp->sum; i++) + lp->scalars[i] = 1; + + lp->columns_scaled = FALSE; + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); +} + +void undoscale(lprec *lp) +{ + int i, j, nz; + MATrec *mat = lp->matA; + LPSREAL *value; + int *rownr, *colnr; + + if(lp->scaling_used) { + + /* Unscale the OF */ + for(j = 1; j <= lp->columns; j++) { + lp->orig_obj[j] = unscaled_mat(lp, lp->orig_obj[j], 0, j); + } + + /* Unscale the matrix */ + mat_validate(mat); + nz = get_nonzeros(lp); + value = &(COL_MAT_VALUE(0)); + rownr = &(COL_MAT_ROWNR(0)); + colnr = &(COL_MAT_COLNR(0)); + for(j = 0; j < nz; + j++, value += matValueStep, rownr += matRowColStep, colnr += matRowColStep) { + *value = unscaled_mat(lp, *value, *rownr, *colnr); + } + + /* Unscale variable bounds */ + for(i = lp->rows + 1, j = 1; i <= lp->sum; i++, j++) { + lp->orig_lowbo[i] = unscaled_value(lp, lp->orig_lowbo[i], i); + lp->orig_upbo[i] = unscaled_value(lp, lp->orig_upbo[i], i); + lp->sc_lobound[j] = unscaled_value(lp, lp->sc_lobound[j], i); + } + + /* Unscale the rhs, upper and lower bounds... */ + for(i = 0; i <= lp->rows; i++) { + lp->orig_rhs[i] = unscaled_value(lp, lp->orig_rhs[i], i); + j = lp->presolve_undo->var_to_orig[i]; + if(j != 0) + lp->presolve_undo->fixed_rhs[j] = unscaled_value(lp, lp->presolve_undo->fixed_rhs[j], i); + lp->orig_lowbo[i] = unscaled_value(lp, lp->orig_lowbo[i], i); + lp->orig_upbo[i] = unscaled_value(lp, lp->orig_upbo[i], i); + } + + FREE(lp->scalars); + lp->scaling_used = FALSE; + lp->columns_scaled = FALSE; + + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); + } +} + diff --git a/src/external/lpsolve/build/lp_solve/lp_simplex.c b/src/external/lpsolve/build/lp_solve/lp_simplex.c new file mode 100644 index 00000000..8a0ee82f --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_simplex.c @@ -0,0 +1,2206 @@ + +/* + Core optimization drivers for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2), + Kjell Eikland (v4.0 and forward) + Contact: + License terms: LGPL. + + Requires: lp_lib.h, lp_simplex.h, lp_presolve.h, lp_pricerPSE.h + + Release notes: + v5.0.0 1 January 2004 New unit applying stacked basis and bounds storage. + v5.0.1 31 January 2004 Moved B&B routines to separate file and implemented + a new runsolver() general purpose call method. + v5.0.2 1 May 2004 Changed routine names to be more intuitive. + v5.1.0 10 January 2005 Created modular stalling/cycling functions. + Rewrote dualloop() to optimize long dual and + also streamlined primloop() correspondingly. + v5.2.0 20 March 2005 Reimplemented primal phase 1 logic. + Made multiple pricing finally work (primal simplex). + + ---------------------------------------------------------------------------------- +*/ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_BFP.h" +#include "lp_simplex.h" +#include "lp_crash.h" +#include "lp_presolve.h" +#include "lp_price.h" +#include "lp_pricePSE.h" +#include "lp_report.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +STATIC void stallMonitor_update(lprec *lp, LPSREAL newOF) +{ + int newpos; + OBJmonrec *monitor = lp->monitor; + + if(monitor->countstep < OBJ_STEPS) + monitor->countstep++; + else + monitor->startstep = mod(monitor->startstep + 1, OBJ_STEPS); + newpos = mod(monitor->startstep + monitor->countstep - 1, OBJ_STEPS); + monitor->objstep[newpos] = newOF; + monitor->idxstep[newpos] = monitor->Icount; + monitor->currentstep = newpos; +} + +STATIC MYBOOL stallMonitor_creepingObj(lprec *lp) +{ + OBJmonrec *monitor = lp->monitor; + + if(monitor->countstep > 1) { + LPSREAL deltaOF = (monitor->objstep[monitor->currentstep] - + monitor->objstep[monitor->startstep]) / monitor->countstep; + deltaOF /= MAX(1, (monitor->idxstep[monitor->currentstep] - + monitor->idxstep[monitor->startstep])); + deltaOF = my_chsign(monitor->isdual, deltaOF); + return( (MYBOOL) (deltaOF < monitor->epsvalue) ); + } + else + return( FALSE ); +} + +STATIC MYBOOL stallMonitor_shortSteps(lprec *lp) +{ + OBJmonrec *monitor = lp->monitor; + + if(monitor->countstep == OBJ_STEPS) { + LPSREAL 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]) ); + } + else + return( FALSE ); +} + +STATIC void stallMonitor_reset(lprec *lp) +{ + OBJmonrec *monitor = lp->monitor; + + monitor->ruleswitches = 0; + monitor->Ncycle = 0; + monitor->Mcycle = 0; + monitor->Icount = 0; + monitor->startstep = 0; + monitor->objstep[monitor->startstep] = lp->infinite; + monitor->idxstep[monitor->startstep] = monitor->Icount; + monitor->prevobj = 0; + monitor->countstep = 1; +} + +STATIC MYBOOL stallMonitor_create(lprec *lp, MYBOOL isdual, char *funcname) +{ + OBJmonrec *monitor = NULL; + if(lp->monitor != NULL) + return( FALSE ); + + monitor = (OBJmonrec *) calloc(sizeof(*monitor), 1); + if(monitor == NULL) + return( FALSE ); + + monitor->lp = lp; + strcpy(monitor->spxfunc, funcname); + monitor->isdual = isdual; + monitor->pivdynamic = is_piv_mode(lp, PRICE_ADAPTIVE); + monitor->oldpivstrategy = lp->piv_strategy; + monitor->oldpivrule = get_piv_rule(lp); + if(MAX_STALLCOUNT <= 1) + monitor->limitstall[FALSE] = 0; + else + monitor->limitstall[FALSE] = MAX(MAX_STALLCOUNT, + (int) pow((LPSREAL) (lp->rows+lp->columns)/2, 0.667)); +#if 1 + monitor->limitstall[FALSE] *= 2+2; /* Expand degeneracy/stalling tolerance range */ +#endif + monitor->limitstall[TRUE] = monitor->limitstall[FALSE]; + if(monitor->oldpivrule == PRICER_DEVEX) /* Increase tolerance since primal Steepest Edge is expensive */ + monitor->limitstall[TRUE] *= 2; + if(MAX_RULESWITCH <= 0) + monitor->limitruleswitches = MAXINT32; + else + monitor->limitruleswitches = MAX(MAX_RULESWITCH, + lp->rows/MAX_RULESWITCH); + monitor->epsvalue = lp->epsprimal; /* lp->epsvalue; */ + lp->monitor = monitor; + stallMonitor_reset(lp); + lp->suminfeas = lp->infinite; + return( TRUE ); +} + +STATIC MYBOOL stallMonitor_check(lprec *lp, int rownr, int colnr, int lastnr, + MYBOOL minit, MYBOOL approved, MYBOOL *forceoutEQ) +{ + OBJmonrec *monitor = lp->monitor; + MYBOOL isStalled, isCreeping, acceptance = TRUE; + int altrule, +#ifdef Paranoia + msglevel = NORMAL; +#else + msglevel = DETAILED; +#endif + LPSREAL deltaobj = lp->suminfeas; + + /* Accept unconditionally if this is the first or second call */ + monitor->active = FALSE; + if(monitor->Icount <= 1) { + if(monitor->Icount == 1) { + monitor->prevobj = lp->rhs[0]; + monitor->previnfeas = deltaobj; + } + monitor->Icount++; + return( acceptance ); + } + + /* Define progress as primal objective less sum of (primal/dual) infeasibilities */ + monitor->thisobj = lp->rhs[0]; + monitor->thisinfeas = deltaobj; + if(lp->spx_trace && + (lastnr > 0)) + report(lp, NORMAL, "%s: Objective at iter %10.0f is " RESULTVALUEMASK " (%4d: %4d %s- %4d)\n", + monitor->spxfunc, + (double) get_total_iter(lp), monitor->thisobj, rownr, lastnr, + my_if(minit == ITERATE_MAJORMAJOR, "<","|"), colnr); + monitor->pivrule = get_piv_rule(lp); + + /* Check if we have a stationary solution at selected tolerance level; + allow some difference in case we just refactorized the basis. */ + deltaobj = my_reldiff(monitor->thisobj, monitor->prevobj); + deltaobj = fabs(deltaobj); /* Pre v5.2 version */ + isStalled = (MYBOOL) (deltaobj < monitor->epsvalue); + + /* Also require that we have a measure of infeasibility-stalling */ + if(isStalled) { + LPSREAL testvalue, refvalue = monitor->epsvalue; +#if 1 + 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; +#else + isCreeping |= stallMonitor_creepingObj(lp); +/* isCreeping |= stallMonitor_shortSteps(lp); */ +#endif + if(isStalled || isCreeping) { + + /* Update counters along with specific tolerance for bound flips */ +#if 1 + if(minit != ITERATE_MAJORMAJOR) { + if(++monitor->Mcycle > 2) { + monitor->Mcycle = 0; + monitor->Ncycle++; + } + } + else +#endif + monitor->Ncycle++; + + /* Start to monitor for variable cycling if this is the initial stationarity */ + if(monitor->Ncycle <= 1) { + monitor->Ccycle = colnr; + monitor->Rcycle = rownr; + } + + /* 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->active = TRUE; + + /* Try to force out equality slacks to combat degeneracy */ + if((lp->fixedvars > 0) && (*forceoutEQ != TRUE)) { + *forceoutEQ = TRUE; + goto Proceed; + } + + /* Our options are now to select an alternative rule or to do bound perturbation; + check if these options are available to us or if we must signal failure and break out. */ + approved &= monitor->pivdynamic && (monitor->ruleswitches < monitor->limitruleswitches); + if(!approved && !is_anti_degen(lp, ANTIDEGEN_STALLING)) { + lp->spx_status = DEGENERATE; + report(lp, msglevel, "%s: Stalling at iter %10.0f; no alternative strategy left.\n", + monitor->spxfunc, (double) get_total_iter(lp)); + acceptance = FALSE; + return( acceptance ); + } + + /* See if we can do the appropriate alternative rule. */ + switch (monitor->oldpivrule) { + case PRICER_FIRSTINDEX: altrule = PRICER_DEVEX; + break; + case PRICER_DANTZIG: altrule = PRICER_DEVEX; + break; + case PRICER_DEVEX: altrule = PRICER_STEEPESTEDGE; + break; + case PRICER_STEEPESTEDGE: altrule = PRICER_DEVEX; + break; + default: altrule = PRICER_FIRSTINDEX; + } + if(approved && + (monitor->pivrule != altrule) && (monitor->pivrule == monitor->oldpivrule)) { + + /* Switch rule to combat degeneracy. */ + monitor->ruleswitches++; + lp->piv_strategy = altrule; + monitor->Ccycle = 0; + monitor->Rcycle = 0; + monitor->Ncycle = 0; + monitor->Mcycle = 0; + report(lp, msglevel, "%s: Stalling at iter %10.0f; changed to '%s' rule.\n", + monitor->spxfunc, (double) get_total_iter(lp), + get_str_piv_rule(get_piv_rule(lp))); + if((altrule == PRICER_DEVEX) || (altrule == PRICER_STEEPESTEDGE)) + restartPricer(lp, AUTOMATIC); + } + + /* If not, code for bound relaxation/perturbation */ + else { + report(lp, msglevel, "%s: Stalling at iter %10.0f; proceed to bound relaxation.\n", + monitor->spxfunc, (double) get_total_iter(lp)); + acceptance = FALSE; + lp->spx_status = DEGENERATE; + return( acceptance ); + } + } + } + + /* Otherwise change back to original selection strategy as soon as possible */ + else { + if(monitor->pivrule != monitor->oldpivrule) { + lp->piv_strategy = monitor->oldpivstrategy; + altrule = monitor->oldpivrule; + if((altrule == PRICER_DEVEX) || (altrule == PRICER_STEEPESTEDGE)) + restartPricer(lp, AUTOMATIC); + report(lp, msglevel, "...returned to original pivot selection rule at iter %.0f.\n", + (double) get_total_iter(lp)); + } + stallMonitor_update(lp, monitor->thisobj); + monitor->Ccycle = 0; + monitor->Rcycle = 0; + monitor->Ncycle = 0; + monitor->Mcycle = 0; + } + + /* Update objective progress tracker */ +Proceed: + monitor->Icount++; + if(deltaobj >= monitor->epsvalue) + monitor->prevobj = monitor->thisobj; + monitor->previnfeas = monitor->thisinfeas; + + return( acceptance ); +} + +STATIC void stallMonitor_finish(lprec *lp) +{ + OBJmonrec *monitor = lp->monitor; + if(monitor == NULL) + return; + if(lp->piv_strategy != monitor->oldpivstrategy) + lp->piv_strategy = monitor->oldpivstrategy; + FREE(monitor); + lp->monitor = NULL; +} + + +STATIC MYBOOL add_artificial(lprec *lp, int forrownr, LPSREAL *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 + objective function values to populate primal phase 1. */ +{ + MYBOOL add; + + /* Make sure we don't add unnecessary artificials, i.e. avoid + cases where the slack variable is enough */ + add = !isBasisVarFeasible(lp, lp->epspivot, forrownr); + + if(add) { + int *rownr = NULL, i, bvar, ii; + LPSREAL *avalue = NULL, rhscoef, acoef; + MATrec *mat = lp->matA; + + /* Check the simple case where a slack is basic */ + for(i = 1; i <= lp->rows; i++) { + if(lp->var_basic[i] == forrownr) + break; + } + acoef = 1; + + /* If not, look for any basic user variable that has a + non-zero coefficient in the current constraint row */ + if(i > lp->rows) { + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i] - lp->rows; + if((ii <= 0) || (ii > (lp->columns-lp->P1extraDim))) + continue; + ii = mat_findelm(mat, forrownr, ii); + if(ii >= 0) { + acoef = COL_MAT_VALUE(ii); + break; + } + } + } + + /* If no candidate was found above, gamble on using the densest column available */ +#if 0 + if(i > lp->rows) { + int len = 0; + bvar = 0; + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i] - lp->rows; + if((ii <= 0) || (ii > (lp->columns-lp->P1extraDim))) + continue; + if(mat_collength(mat, ii) > len) { + len = mat_collength(mat, ii); + bvar = i; + } + } + i = bvar; + acoef = 1; + } +#endif + + bvar = i; + + add = (MYBOOL) (bvar <= lp->rows); + if(add) { + rhscoef = lp->rhs[forrownr]; + + /* Create temporary sparse array storage */ + if(nzarray == NULL) + allocREAL(lp, &avalue, 2, FALSE); + else + avalue = nzarray; + if(idxarray == NULL) + allocINT(lp, &rownr, 2, FALSE); + else + rownr = idxarray; + + /* Set the objective coefficient */ + rownr[0] = 0; + avalue[0] = my_chsign(is_chsign(lp, 0), 1); + + /* Set the constraint row coefficient */ + rownr[1] = forrownr; + avalue[1] = my_chsign(is_chsign(lp, forrownr), my_sign(rhscoef/acoef)); + + /* Add the column of artificial variable data to the user data matrix */ + add_columnex(lp, 2, avalue, rownr); + + /* Free the temporary sparse array storage */ + if(idxarray == NULL) + FREE(rownr); + if(nzarray == NULL) + FREE(avalue); + + /* Now set the artificial variable to be basic */ + set_basisvar(lp, bvar, lp->sum); + lp->P1extraDim++; + } + else { + report(lp, CRITICAL, "add_artificial: Could not find replacement basis variable for row %d\n", + forrownr); + lp->basis_valid = FALSE; + } + + } + + return(add); + +} + +STATIC int get_artificialRow(lprec *lp, int colnr) +{ + MATrec *mat = lp->matA; + +#ifdef Paranoia + if((colnr <= lp->columns-abs(lp->P1extraDim)) || (colnr > lp->columns)) + report(lp, SEVERE, "get_artificialRow: Invalid column index %d\n", colnr); + if(mat->col_end[colnr] - mat->col_end[colnr-1] != 1) + report(lp, SEVERE, "get_artificialRow: Invalid column non-zero count\n"); +#endif + + /* Return the row index of the singleton */ + colnr = mat->col_end[colnr-1]; + colnr = COL_MAT_ROWNR(colnr); + return( colnr ); +} + +STATIC int findAnti_artificial(lprec *lp, int colnr) +/* Primal simplex: Find a basic artificial variable to swap + against the non-basic slack variable, if possible */ +{ + int i, k, rownr = 0, P1extraDim = abs(lp->P1extraDim); + + if((P1extraDim == 0) || (colnr > lp->rows) || !lp->is_basic[colnr]) + return( rownr ); + + for(i = 1; i <= lp->rows; i++) { + k = lp->var_basic[i]; + if((k > lp->sum-P1extraDim) && (lp->rhs[i] == 0)) { + rownr = get_artificialRow(lp, k-lp->rows); + + /* Should we find the artificial's slack direct "antibody"? */ + if(rownr == colnr) + break; + rownr = 0; + } + } + return( rownr ); +} + +STATIC int findBasicArtificial(lprec *lp, int before) +{ + int i = 0, P1extraDim = abs(lp->P1extraDim); + + if(P1extraDim > 0) { + if(before > lp->rows || before <= 1) + i = lp->rows; + else + i = before; + + while((i > 0) && (lp->var_basic[i] <= lp->sum-P1extraDim)) + i--; + } + + return(i); +} + +STATIC void eliminate_artificials(lprec *lp, LPSREAL *prow) +{ + int i, j, colnr, rownr, P1extraDim = abs(lp->P1extraDim); + + for(i = 1; (i <= lp->rows) && (P1extraDim > 0); i++) { + j = lp->var_basic[i]; + if(j <= lp->sum-P1extraDim) + continue; + j -= lp->rows; + rownr = get_artificialRow(lp, j); + colnr = find_rowReplacement(lp, rownr, prow, NULL); +#if 0 + performiteration(lp, rownr, colnr, 0.0, TRUE, FALSE, prow, NULL, + NULL, NULL, NULL); +#else + set_basisvar(lp, rownr, colnr); +#endif + del_column(lp, j); + P1extraDim--; + } + lp->P1extraDim = 0; +} + +STATIC void clear_artificials(lprec *lp) +{ + int i, j, n, P1extraDim; + + /* Substitute any basic artificial variable for its slack counterpart */ + n = 0; + P1extraDim = abs(lp->P1extraDim); + for(i = 1; (i <= lp->rows) && (n < P1extraDim); i++) { + j = lp->var_basic[i]; + if(j <= lp->sum-P1extraDim) + continue; + j = get_artificialRow(lp, j-lp->rows); + set_basisvar(lp, i, j); + n++; + } +#ifdef Paranoia + if(n != lp->P1extraDim) + report(lp, SEVERE, "clear_artificials: Unable to clear all basic artificial variables\n"); +#endif + + /* Delete any remaining non-basic artificial variables */ + while(P1extraDim > 0) { + i = lp->sum-lp->rows; + del_column(lp, i); + P1extraDim--; + } + lp->P1extraDim = 0; + if(n > 0) { + set_action(&lp->spx_action, ACTION_REINVERT); + lp->basis_valid = TRUE; + } +} + + +STATIC int primloop(lprec *lp, MYBOOL primalfeasible, LPSREAL 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, + *prow = NULL, *pcol = NULL, + *drow = lp->drow; + int *workINT = NULL, + *nzdrow = lp->nzdrow; + + if(lp->spx_trace) + report(lp, DETAILED, "Entered primal simplex algorithm with feasibility %s\n", + my_boolstr(primalfeasible)); + + /* Add sufficent number of artificial variables to make the problem feasible + through the first phase; delete when primal feasibility has been achieved */ + lp->P1extraDim = 0; + if(!primalfeasible) { + lp->simplex_mode = SIMPLEX_Phase1_PRIMAL; +#ifdef Paranoia + if(!verify_basis(lp)) + report(lp, SEVERE, "primloop: No valid basis for artificial variables\n"); +#endif +#if 0 + /* First check if we can get away with a single artificial variable */ + if(lp->equalities == 0) { + i = (int) feasibilityOffset(lp, !primal); + add_artificial(lp, i, prow, (int *) pcol); + } + else +#endif + /* Otherwise add as many artificial variables as is necessary + to force primal feasibility. */ + for(i = 1; i <= lp->rows; i++) { + add_artificial(lp, i, NULL, NULL); + } + + /* Make sure we update the working objective */ + if(lp->P1extraDim > 0) { +#if 1 /* v5.1 code: Not really necessary since we do not price the artificial + variables (stored at the end of the column list, they are initially + basic and are never allowed to enter the basis, once they exit) */ + ok = allocREAL(lp, &(lp->drow), lp->sum+1, AUTOMATIC) && + allocINT(lp, &(lp->nzdrow), lp->sum+1, AUTOMATIC); + if(!ok) + goto Finish; + lp->nzdrow[0] = 0; + drow = lp->drow; + nzdrow = lp->nzdrow; +#endif + mat_validate(lp->matA); + set_OF_p1extra(lp, 0.0); + } + if(lp->spx_trace) + report(lp, DETAILED, "P1extraDim count = %d\n", lp->P1extraDim); + + simplexPricer(lp, (MYBOOL)!primal); + invert(lp, INITSOL_USEZERO, TRUE); + } + else { + lp->simplex_mode = SIMPLEX_Phase2_PRIMAL; + restartPricer(lp, (MYBOOL)!primal); + } + + /* Create work arrays and optionally the multiple pricing structure */ + ok = allocREAL(lp, &(lp->bsolveVal), lp->rows + 1, FALSE) && + allocREAL(lp, &prow, lp->sum + 1, TRUE) && + allocREAL(lp, &pcol, lp->rows + 1, TRUE); + if(is_piv_mode(lp, PRICE_MULTIPLE) && (lp->multiblockdiv > 1)) { + lp->multivars = multi_create(lp, FALSE); + ok &= (lp->multivars != NULL) && + multi_resize(lp->multivars, lp->sum / lp->multiblockdiv, 2, FALSE, TRUE); + } + if(!ok) + goto Finish; + + /* Initialize regular primal simplex algorithm variables */ + lp->spx_status = RUNNING; + minit = ITERATE_MAJORMAJOR; + epsvalue = lp->epspivot; + pendingunbounded = FALSE; + + ok = stallMonitor_create(lp, FALSE, "primloop"); + if(!ok) + goto Finish; + + lp->rejectpivot[0] = 0; + + /* Iterate while we are successful; exit when the model is infeasible/unbounded, + or we must terminate due to numeric instability or user-determined reasons */ + while((lp->spx_status == RUNNING) && !userabort(lp, -1)) { + + primalphase1 = (MYBOOL) (lp->P1extraDim > 0); + clear_action(&lp->spx_action, ACTION_REINVERT | ACTION_ITERATE); + + /* Check if we have stalling (from numerics or degenerate cycling) */ + pricerCanChange = !primalphase1; + stallaccept = stallMonitor_check(lp, rownr, colnr, lastnr, minit, pricerCanChange, &forceoutEQ); + if(!stallaccept) + break; + + /* Find best column to enter the basis */ +RetryCol: +#if 0 + if(verify_solution(lp, FALSE, "spx_loop") > 0) + i = 1; /* This is just a debug trap */ +#endif + if(!changedphase) { + i = 0; + do { + i++; + colnr = colprim(lp, drow, nzdrow, (MYBOOL) (minit == ITERATE_MINORRETRY), i, &candidatecount, TRUE, &xviolated); + } while ((colnr == 0) && (i < partial_countBlocks(lp, (MYBOOL) !primal)) && + partial_blockStep(lp, (MYBOOL) !primal)); + + /* Handle direct outcomes */ + if(colnr == 0) + lp->spx_status = OPTIMAL; + if(lp->rejectpivot[0] > 0) + minit = ITERATE_MAJORMAJOR; + + /* See if accuracy check during compute_reducedcosts flagged refactorization */ + if(is_action(lp->spx_action, ACTION_REINVERT)) + bfpfinal = TRUE; + + } + + /* Make sure that we do not erroneously conclude that an unbounded model is optimal */ +#ifdef primal_UseRejectionList + if((colnr == 0) && (lp->rejectpivot[0] > 0)) { + lp->spx_status = UNBOUNDED; + if((lp->spx_trace && (lp->bb_totalnodes == 0)) || + (lp->bb_trace && (lp->bb_totalnodes > 0))) + report(lp, DETAILED, "The model is primal unbounded.\n"); + colnr = lp->rejectpivot[1]; + rownr = 0; + lp->rejectpivot[0] = 0; + ok = FALSE; + break; + } +#endif + + /* Check if we found an entering variable (indicating that we are still dual infeasible) */ + if(colnr > 0) { + changedphase = FALSE; + fsolve(lp, colnr, pcol, NULL, lp->epsmachine, 1.0, TRUE); /* Solve entering column for Pi */ + + /* Do special anti-degeneracy column selection, if specified */ + if(is_anti_degen(lp, ANTIDEGEN_COLUMNCHECK) && !check_degeneracy(lp, pcol, NULL)) { + if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY/3) { + i = ++lp->rejectpivot[0]; + lp->rejectpivot[i] = colnr; + report(lp, DETAILED, "Entering column %d found to be non-improving due to degeneracy.\n", + colnr); + minit = ITERATE_MINORRETRY; + goto RetryCol; + } + else { + lp->rejectpivot[0] = 0; + report(lp, DETAILED, "Gave up trying to find a strictly improving entering column.\n"); + } + } + + /* Find the leaving variable that gives the most stringent bound on the entering variable */ + theta = drow[colnr]; + rownr = rowprim(lp, colnr, &theta, pcol, workINT, forceoutEQ, &cviolated); + +#ifdef AcceptMarginalAccuracy + /* Check for marginal accuracy */ + if((rownr > 0) && (xviolated+cviolated < lp->epspivot)) { + if(lp->bb_trace || (lp->bb_totalnodes == 0)) + report(lp, DETAILED, "primloop: Assuming convergence with reduced accuracy %g.\n", + MAX(xviolated, cviolated)); + rownr = 0; + colnr = 0; + goto Optimality; + } + else +#endif + + /* See if we can do a straight artificial<->slack replacement (when "colnr" is a slack) */ + if((lp->P1extraDim != 0) && (rownr == 0) && (colnr <= lp->rows)) + rownr = findAnti_artificial(lp, colnr); + + if(rownr > 0) { + pendingunbounded = FALSE; + lp->rejectpivot[0] = 0; + set_action(&lp->spx_action, ACTION_ITERATE); + if(!lp->obj_in_basis) /* We must manually copy the reduced cost for RHS update */ + pcol[0] = my_chsign(!lp->is_lower[colnr], drow[colnr]); + lp->bfp_prepareupdate(lp, rownr, colnr, pcol); + } + + /* We may be unbounded... */ + else { + /* First make sure that we are not suffering from precision loss */ +#ifdef primal_UseRejectionList + if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY) { + lp->spx_status = RUNNING; + lp->rejectpivot[0]++; + lp->rejectpivot[lp->rejectpivot[0]] = colnr; + report(lp, DETAILED, "...trying to recover via another pivot column.\n"); + minit = ITERATE_MINORRETRY; + goto RetryCol; + } + else +#endif + /* Check that we are not having numerical problems */ + if(!refactRecent(lp) && !pendingunbounded) { + bfpfinal = TRUE; + pendingunbounded = TRUE; + set_action(&lp->spx_action, ACTION_REINVERT); + } + + /* Conclude that the model is unbounded */ + else { + lp->spx_status = UNBOUNDED; + report(lp, DETAILED, "The model is primal unbounded.\n"); + break; + } + } + } + + /* We handle optimality and phase 1 infeasibility ... */ + else { + +Optimality: + /* Handle possible transition from phase 1 to phase 2 */ + if(!primalfeasible || isP1extra(lp)) { + + if(feasiblePhase1(lp, epsvalue)) { + lp->spx_status = RUNNING; + if(lp->bb_totalnodes == 0) { + report(lp, NORMAL, "Found feasibility by primal simplex after %10.0f iter.\n", + (double) get_total_iter(lp)); + if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPFEASIBLE)) + lp->usermessage(lp, lp->msghandle, MSG_LPFEASIBLE); + } + changedphase = FALSE; + primalfeasible = TRUE; + lp->simplex_mode = SIMPLEX_Phase2_PRIMAL; + set_OF_p1extra(lp, 0.0); + + /* We can do two things now; + 1) delete the rows belonging to those variables, since they are redundant, OR + 2) drive out the existing artificial variables via pivoting. */ + if(lp->P1extraDim > 0) { + +#ifdef Phase1EliminateRedundant + /* If it is not a MIP model we can try to delete redundant rows */ + if((lp->bb_totalnodes == 0) && (MIP_count(lp) == 0)) { + while(lp->P1extraDim > 0) { + i = lp->rows; + while((i > 0) && (lp->var_basic[i] <= lp->sum-lp->P1extraDim)) + i--; +#ifdef Paranoia + if(i <= 0) { + report(lp, SEVERE, "primloop: Could not find redundant artificial.\n"); + break; + } +#endif + /* Obtain column and row indeces */ + j = lp->var_basic[i]-lp->rows; + k = get_artificialRow(lp, j); + + /* Delete row before column due to basis "compensation logic" */ + if(lp->is_basic[k]) { + lp->is_basic[lp->rows+j] = FALSE; + del_constraint(lp, k); + } + else + set_basisvar(lp, i, k); + del_column(lp, j); + lp->P1extraDim--; + } + lp->basis_valid = TRUE; + } + /* Otherwise we drive out the artificials by elimination pivoting */ + else + eliminate_artificials(lp, prow); + +#else + /* Indicate phase 2 with artificial variables by negating P1extraDim */ + lp->P1extraDim = my_flipsign(lp->P1extraDim); +#endif + } + + /* We must refactorize since the OF changes from phase 1 to phase 2 */ + set_action(&lp->spx_action, ACTION_REINVERT); + bfpfinal = TRUE; + } + + /* We are infeasible in phase 1 */ + else { + lp->spx_status = INFEASIBLE; + minit = ITERATE_MAJORMAJOR; + if(lp->spx_trace) + report(lp, NORMAL, "Model infeasible by primal simplex at iter %10.0f.\n", + (double) get_total_iter(lp)); + } + } + + /* Handle phase 1 optimality */ + else { + /* (Do nothing special) */ + } + + /* 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 */ + set_action(&lp->piv_strategy, PRICE_FORCEFULL); + i = rowdual(lp, lp->rhs, FALSE, FALSE, NULL); + clear_action(&lp->piv_strategy, PRICE_FORCEFULL); + if(i > 0) { + lp->spx_status = LOSTFEAS; + if(lp->total_iter == 0) + report(lp, DETAILED, "primloop: Lost primal feasibility at iter %10.0f: will try to recover.\n", + (double) get_total_iter(lp)); + } + } + } + + /* Pivot row/col and update the inverse */ + if(is_action(lp->spx_action, ACTION_ITERATE)) { + lastnr = lp->var_basic[rownr]; + + if(refactRecent(lp) == AUTOMATIC) + minitcount = 0; + else if(minitcount > MAX_MINITUPDATES) { + recompute_solution(lp, INITSOL_USEZERO); + minitcount = 0; + } + minit = performiteration(lp, rownr, colnr, theta, primal, + (MYBOOL) (/*(candidatecount > 1) && */ + (stallaccept != AUTOMATIC)), + NULL, NULL, + pcol, NULL, NULL); + if(minit != ITERATE_MAJORMAJOR) + minitcount++; + + if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) + break; + else if(minit == ITERATE_MINORMAJOR) + continue; +#ifdef UsePrimalReducedCostUpdate + /* Do a fast update of the reduced costs in preparation for the next iteration */ + if(minit == ITERATE_MAJORMAJOR) + update_reducedcosts(lp, primal, lastnr, colnr, pcol, drow); +#endif + + /* Detect if an auxiliary variable has left the basis and delete it; if + the non-basic variable only changed bound (a "minor iteration"), the + basic artificial variable did not leave and there is nothing to do */ + if((minit == ITERATE_MAJORMAJOR) && (lastnr > lp->sum - abs(lp->P1extraDim))) { +#ifdef Paranoia + if(lp->is_basic[lastnr] || !lp->is_basic[colnr]) + report(lp, SEVERE, "primloop: Invalid basis indicator for variable %d at iter %10.0f.\n", + lastnr, (double) get_total_iter(lp)); +#endif + del_column(lp, lastnr-lp->rows); + if(lp->P1extraDim > 0) + lp->P1extraDim--; + else + lp->P1extraDim++; + if(lp->P1extraDim == 0) { + colnr = 0; + changedphase = TRUE; + stallMonitor_reset(lp); + } + } + } + + if(lp->spx_status == SWITCH_TO_DUAL) + ; + else if(!changedphase && lp->bfp_mustrefactorize(lp)) { +#ifdef ResetMinitOnReinvert + minit = ITERATE_MAJORMAJOR; +#endif + if(!invert(lp, INITSOL_USEZERO, bfpfinal)) + lp->spx_status = SINGULAR_BASIS; + bfpfinal = FALSE; + } + } + + /* Remove any remaining artificial variables (feasible or infeasible model) */ + lp->P1extraDim = abs(lp->P1extraDim); +/* if((lp->P1extraDim > 0) && (lp->spx_status != DEGENERATE)) { */ + if(lp->P1extraDim > 0) { + clear_artificials(lp); + if(lp->spx_status != OPTIMAL) + restore_basis(lp); + i = invert(lp, INITSOL_USEZERO, TRUE); + } +#ifdef Paranoia + if(!verify_basis(lp)) + report(lp, SEVERE, "primloop: Invalid basis detected due to internal error\n"); +#endif + + /* Switch to dual phase 1 simplex for MIP models during + B&B phases, since this is typically far more efficient */ +#ifdef ForceDualSimplexInBB + if((lp->bb_totalnodes == 0) && (MIP_count(lp) > 0) && + ((lp->simplex_strategy & SIMPLEX_Phase1_DUAL) == 0)) { + lp->simplex_strategy &= ~SIMPLEX_Phase1_PRIMAL; + lp->simplex_strategy += SIMPLEX_Phase1_DUAL; + } +#endif + +Finish: + stallMonitor_finish(lp); + multi_free(&(lp->multivars)); + FREE(prow); + FREE(pcol); + FREE(lp->bsolveVal); + + return(ok); +} /* primloop */ + +STATIC int dualloop(lprec *lp, MYBOOL dualfeasible, int dualinfeasibles[], LPSREAL dualoffset) +{ + MYBOOL primal = FALSE, inP1extra, dualphase1 = FALSE, changedphase = TRUE, + pricerCanChange, minit, stallaccept, longsteps, + forceoutEQ = FALSE, bfpfinal = FALSE; + int i, colnr = 0, rownr = 0, lastnr = 0, + candidatecount = 0, minitcount = 0, +#ifdef FixInaccurateDualMinit + minitcolnr = 0, +#endif + ok = TRUE; + int *boundswaps = NULL; + LREAL theta = 0.0; + LPSREAL epsvalue, xviolated, cviolated, + *prow = NULL, *pcol = NULL, + *drow = lp->drow; + int *nzprow = NULL, *workINT = NULL, + *nzdrow = lp->nzdrow; + + if(lp->spx_trace) + report(lp, DETAILED, "Entered dual simplex algorithm with feasibility %s.\n", + my_boolstr(dualfeasible)); + + /* Allocate work arrays */ + ok = allocREAL(lp, &prow, lp->sum + 1, TRUE) && + allocINT (lp, &nzprow, lp->sum + 1, FALSE) && + allocREAL(lp, &pcol, lp->rows + 1, TRUE); + if(!ok) + goto Finish; + + /* Set non-zero P1extraVal value to force dual feasibility when the dual + simplex is used as a phase 1 algorithm for the primal simplex. + The value will be reset when primal feasibility has been achieved, or + a dual non-feasibility has been encountered (no candidate for a first + leaving variable) */ + inP1extra = (MYBOOL) (dualoffset != 0); + if(inP1extra) { + set_OF_p1extra(lp, dualoffset); + simplexPricer(lp, (MYBOOL)!primal); + invert(lp, INITSOL_USEZERO, TRUE); + } + else + restartPricer(lp, (MYBOOL)!primal); + + /* Prepare dual long-step structures */ +#if 0 + longsteps = TRUE; +#elif 0 + longsteps = (MYBOOL) ((MIP_count(lp) > 0) && (lp->bb_level > 1)); +#elif 0 + longsteps = (MYBOOL) ((MIP_count(lp) > 0) && (lp->solutioncount >= 1)); +#else + longsteps = FALSE; +#endif +#ifdef UseLongStepDualPhase1 + longsteps = !dualfeasible && (MYBOOL) (dualinfeasibles != NULL); +#endif + + if(longsteps) { + lp->longsteps = multi_create(lp, TRUE); + ok = (lp->longsteps != NULL) && + multi_resize(lp->longsteps, MIN(lp->boundedvars+2, 11), 1, TRUE, TRUE); + if(!ok) + goto Finish; +#ifdef UseLongStepPruning + lp->longsteps->objcheck = TRUE; +#endif + boundswaps = multi_indexSet(lp->longsteps, FALSE); + } + + /* Do regular dual simplex variable initializations */ + lp->spx_status = RUNNING; + minit = ITERATE_MAJORMAJOR; + epsvalue = lp->epspivot; + + ok = stallMonitor_create(lp, TRUE, "dualloop"); + if(!ok) + goto Finish; + + lp->rejectpivot[0] = 0; + if(dualfeasible) + lp->simplex_mode = SIMPLEX_Phase2_DUAL; + else + lp->simplex_mode = SIMPLEX_Phase1_DUAL; + + /* Check if we have equality slacks in the basis and we should try to + drive them out in order to reduce chance of degeneracy in Phase 1. + forceoutEQ = FALSE : Only eliminate assured "good" violated + equality constraint slacks + AUTOMATIC: Seek more elimination of equality constraint + slacks (but not as aggressive as the rule + used in lp_solve v4.0 and earlier) + TRUE: Force remaining equality slacks out of the + basis */ + if(dualphase1 || inP1extra || + ((lp->fixedvars > 0) && is_anti_degen(lp, ANTIDEGEN_FIXEDVARS))) { + forceoutEQ = AUTOMATIC; + } +#if 1 + if(is_anti_degen(lp, ANTIDEGEN_DYNAMIC) && (bin_count(lp, TRUE)*2 > lp->columns)) { + switch (forceoutEQ) { + case FALSE: forceoutEQ = AUTOMATIC; + break; + /* case AUTOMATIC: forceoutEQ = TRUE; + break; + default: forceoutEQ = TRUE; */ + } + } +#endif + + while((lp->spx_status == RUNNING) && !userabort(lp, -1)) { + + /* Check if we have stalling (from numerics or degenerate cycling) */ + pricerCanChange = !dualphase1 && !inP1extra; + stallaccept = stallMonitor_check(lp, rownr, colnr, lastnr, minit, pricerCanChange, &forceoutEQ); + if(!stallaccept) + break; + + /* Store current LP index for reference at next iteration */ + changedphase = FALSE; + + /* Compute (pure) dual phase1 offsets / reduced costs if appropriate */ + dualphase1 &= (MYBOOL) (lp->simplex_mode == SIMPLEX_Phase1_DUAL); + if(longsteps && dualphase1 && !inP1extra) { + obtain_column(lp, dualinfeasibles[1], pcol, NULL, NULL); + i = 2; + for(i = 2; i <= dualinfeasibles[0]; i++) + mat_multadd(lp->matA, pcol, dualinfeasibles[i], 1.0); + /* Solve (note that solved pcol will be used instead of lp->rhs) */ + ftran(lp, pcol, NULL, lp->epsmachine); + } + + /* Do minor iterations (non-basic variable bound flips) for as + long as possible since this is a cheap way of iterating */ +#if (defined dual_Phase1PriceEqualities) || (defined dual_UseRejectionList) +RetryRow: +#endif + if(minit != ITERATE_MINORRETRY) { + i = 0; + do { + i++; + rownr = rowdual(lp, my_if(dualphase1, pcol, NULL), forceoutEQ, TRUE, &xviolated); + } while ((rownr == 0) && (i < partial_countBlocks(lp, (MYBOOL) !primal)) && + partial_blockStep(lp, (MYBOOL) !primal)); + } + + /* Make sure that we do not erroneously conclude that an infeasible model is optimal */ +#ifdef dual_UseRejectionList + if((rownr == 0) && (lp->rejectpivot[0] > 0)) { + lp->spx_status = INFEASIBLE; + if((lp->spx_trace && (lp->bb_totalnodes == 0)) || + (lp->bb_trace && (lp->bb_totalnodes > 0))) + report(lp, DETAILED, "The model is primal infeasible.\n"); + rownr = lp->rejectpivot[1]; + colnr = 0; + lp->rejectpivot[0] = 0; + ok = FALSE; + break; + } +#endif + + /* If we found a leaving variable, find a matching entering one */ + clear_action(&lp->spx_action, ACTION_ITERATE); + if(rownr > 0) { + colnr = coldual(lp, rownr, prow, nzprow, drow, nzdrow, + (MYBOOL) (dualphase1 && !inP1extra), + (MYBOOL) (minit == ITERATE_MINORRETRY), &candidatecount, &cviolated); + if(colnr < 0) { + minit = ITERATE_MAJORMAJOR; + continue; + } +#ifdef AcceptMarginalAccuracy + else if(xviolated+cviolated < lp->epspivot) { + if(lp->bb_trace || (lp->bb_totalnodes == 0)) + report(lp, DETAILED, "dualloop: Assuming convergence with reduced accuracy %g.\n", + MAX(xviolated, cviolated)); + rownr = 0; + colnr = 0; + } +#endif + /* Check if the long-dual found reason to prune the B&B tree */ + if(lp->spx_status == FATHOMED) + break; + } + else + colnr = 0; + + /* Process primal-infeasible row */ + if(rownr > 0) { + + if(colnr > 0) { +#ifdef Paranoia + if((rownr > lp->rows) || (colnr > lp->sum)) { + report(lp, SEVERE, "dualloop: Invalid row %d(%d) and column %d(%d) pair selected at iteration %.0f\n", + rownr, lp->rows, colnr-lp->columns, lp->columns, (double) get_total_iter(lp)); + lp->spx_status = UNKNOWNERROR; + break; + } +#endif + fsolve(lp, colnr, pcol, workINT, lp->epsmachine, 1.0, TRUE); + +#ifdef FixInaccurateDualMinit + /* Prevent bound flip-flops during minor iterations; used to detect + infeasibility after triggering of minor iteration accuracy management */ + if(colnr != minitcolnr) + minitcolnr = 0; +#endif + + /* Getting division by zero here; catch it and try to recover */ + if(pcol[rownr] == 0) { + if(lp->spx_trace) + report(lp, DETAILED, "dualloop: Attempt to divide by zero (pcol[%d])\n", rownr); + if(!refactRecent(lp)) { + report(lp, DETAILED, "...trying to recover by refactorizing basis.\n"); + set_action(&lp->spx_action, ACTION_REINVERT); + bfpfinal = FALSE; + } + else { + if(lp->bb_totalnodes == 0) + report(lp, DETAILED, "...cannot recover by refactorizing basis.\n"); + lp->spx_status = NUMFAILURE; + ok = FALSE; + } + } + else { + set_action(&lp->spx_action, ACTION_ITERATE); + lp->rejectpivot[0] = 0; + if(!lp->obj_in_basis) /* We must manually copy the reduced cost for RHS update */ + pcol[0] = my_chsign(!lp->is_lower[colnr], drow[colnr]); + theta = lp->bfp_prepareupdate(lp, rownr, colnr, pcol); + + /* Verify numeric accuracy of the basis factorization and change to + the "theoretically" correct version of the theta */ + if((lp->improve & IMPROVE_THETAGAP) && !refactRecent(lp) && + (my_reldiff(fabs(theta), fabs(prow[colnr])) > + lp->epspivot*10.0*log(2.0+50.0*lp->rows))) { /* This is my kludge - KE */ + set_action(&lp->spx_action, ACTION_REINVERT); + bfpfinal = TRUE; +#ifdef IncreasePivotOnReducedAccuracy + lp->epspivot = MIN(1.0e-4, lp->epspivot*2.0); +#endif + report(lp, DETAILED, "dualloop: Refactorizing at iter %.0f due to loss of accuracy.\n", + (double) get_total_iter(lp)); + } + theta = prow[colnr]; + compute_theta(lp, rownr, &theta, !lp->is_lower[colnr], 0, primal); + } + } + +#ifdef FixInaccurateDualMinit + /* Force reinvertion and try another row if we did not find a bound-violated leaving column */ + else if(!refactRecent(lp) && (minit != ITERATE_MAJORMAJOR) && (colnr != minitcolnr)) { + minitcolnr = colnr; + i = invert(lp, INITSOL_USEZERO, TRUE); + if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) + break; + else if(!i) { + lp->spx_status = SINGULAR_BASIS; + break; + } + minit = ITERATE_MAJORMAJOR; + continue; + } +#endif + + /* We may be infeasible, have lost dual feasibility, or simply have no valid entering + variable for the selected row. The strategy is to refactorize if we suspect numerical + problems and loss of dual feasibility; this is done if it has been a while since + refactorization. If not, first try to select a different row/leaving variable to + see if a valid entering variable can be found. Otherwise, determine this model + as infeasible. */ + else { + + /* As a first option, try to recover from any numerical trouble by refactorizing */ + if(!refactRecent(lp)) { + set_action(&lp->spx_action, ACTION_REINVERT); + bfpfinal = TRUE; + } + +#ifdef dual_UseRejectionList + /* Check for pivot size issues */ + else if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY) { + lp->spx_status = RUNNING; + lp->rejectpivot[0]++; + lp->rejectpivot[lp->rejectpivot[0]] = rownr; + if(lp->bb_totalnodes == 0) + report(lp, DETAILED, "...trying to find another pivot row!\n"); + goto RetryRow; + } +#endif + /* Check if we may have lost dual feasibility if we also did phase 1 here */ + else if(dualphase1 && (dualoffset != 0)) { + lp->spx_status = LOSTFEAS; + if((lp->spx_trace && (lp->bb_totalnodes == 0)) || + (lp->bb_trace && (lp->bb_totalnodes > 0))) + report(lp, DETAILED, "dualloop: Model lost dual feasibility.\n"); + ok = FALSE; + break; + } + + /* Otherwise just determine that we are infeasible */ + else { + if(lp->spx_status == RUNNING) { +#if 1 + if(xviolated < lp->epspivot) { + if(lp->bb_trace || (lp->bb_totalnodes == 0)) + report(lp, NORMAL, "The model is primal optimal, but marginally infeasible.\n"); + lp->spx_status = OPTIMAL; + break; + } +#endif + lp->spx_status = INFEASIBLE; + if((lp->spx_trace && (lp->bb_totalnodes == 0)) || + (lp->bb_trace && (lp->bb_totalnodes > 0))) + report(lp, DETAILED, "The model is primal infeasible.\n"); + } + ok = FALSE; + break; + } + } + } + + /* Make sure that we enter the primal simplex with a high quality solution */ + else if(inP1extra && !refactRecent(lp) && is_action(lp->improve, IMPROVE_INVERSE)) { + set_action(&lp->spx_action, ACTION_REINVERT); + bfpfinal = TRUE; + } + + /* High quality solution with no leaving candidates available ... */ + else { + + bfpfinal = TRUE; + +#ifdef dual_RemoveBasicFixedVars + /* See if we should try to eliminate basic fixed variables; + can be time-consuming for some models */ + if(inP1extra && (colnr == 0) && (lp->fixedvars > 0) && is_anti_degen(lp, ANTIDEGEN_FIXEDVARS)) { + report(lp, DETAILED, "dualloop: Trying to pivot out %d fixed basic variables at iter %.0f\n", + lp->fixedvars, (double) get_total_iter(lp)); + rownr = 0; + while(lp->fixedvars > 0) { + rownr = findBasicFixedvar(lp, rownr, TRUE); + if(rownr == 0) { + colnr = 0; + break; + } + colnr = find_rowReplacement(lp, rownr, prow, nzprow); + if(colnr > 0) { + theta = 0; + performiteration(lp, rownr, colnr, theta, TRUE, FALSE, prow, NULL, + NULL, NULL, NULL); + lp->fixedvars--; + } + } + } +#endif + + /* Check if we are INFEASIBLE for the case that the dual is used + as phase 1 before the primal simplex phase 2 */ + if(inP1extra && (colnr < 0) && !isPrimalFeasible(lp, lp->epsprimal, NULL, NULL)) { + if(lp->bb_totalnodes == 0) { + if(dualfeasible) + report(lp, DETAILED, "The model is primal infeasible and dual feasible.\n"); + else + report(lp, DETAILED, "The model is primal infeasible and dual unbounded.\n"); + } + set_OF_p1extra(lp, 0); + inP1extra = FALSE; + set_action(&lp->spx_action, ACTION_REINVERT); + lp->spx_status = INFEASIBLE; + lp->simplex_mode = SIMPLEX_UNDEFINED; + ok = FALSE; + } + + /* Check if we are FEASIBLE (and possibly also optimal) for the case that the + dual is used as phase 1 before the primal simplex phase 2 */ + else if(inP1extra) { + + /* Set default action; force an update of the rhs vector, adjusted for + the new P1extraVal=0 (set here so that usermessage() behaves properly) */ + if(lp->bb_totalnodes == 0) { + report(lp, NORMAL, "Found feasibility by dual simplex after %10.0f iter.\n", + (double) get_total_iter(lp)); + if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPFEASIBLE)) + lp->usermessage(lp, lp->msghandle, MSG_LPFEASIBLE); + } + set_OF_p1extra(lp, 0); + inP1extra = FALSE; + set_action(&lp->spx_action, ACTION_REINVERT); + +#if 1 + /* Optionally try another dual loop, if so selected by the user */ + if((lp->simplex_strategy & SIMPLEX_DUAL_PRIMAL) && (lp->fixedvars == 0)) + lp->spx_status = SWITCH_TO_PRIMAL; +#endif + changedphase = TRUE; + + } + + /* We are primal feasible and also optimal if we were in phase 2 */ + else { + + lp->simplex_mode = SIMPLEX_Phase2_DUAL; + + /* Check if we still have equality slacks stuck in the basis; drive them out? */ + if((lp->fixedvars > 0) && (lp->bb_totalnodes == 0)) { +#ifdef dual_Phase1PriceEqualities + if(forceoutEQ != TRUE) { + forceoutEQ = TRUE; + goto RetryRow; + } +#endif +#ifdef Paranoia + report(lp, NORMAL, +#else + report(lp, DETAILED, +#endif + "Found dual solution with %d fixed slack variables left basic.\n", + lp->fixedvars); + } + /* 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 */ + set_action(&lp->piv_strategy, PRICE_FORCEFULL); + colnr = colprim(lp, drow, nzdrow, FALSE, 1, &candidatecount, FALSE, NULL); + clear_action(&lp->piv_strategy, PRICE_FORCEFULL); + if((dualoffset == 0) && (colnr > 0)) { + lp->spx_status = LOSTFEAS; + if(lp->total_iter == 0) + report(lp, DETAILED, "Recovering lost dual feasibility at iter %10.0f.\n", + (double) get_total_iter(lp)); + break; + } + } + + if(colnr == 0) + lp->spx_status = OPTIMAL; + else { + lp->spx_status = SWITCH_TO_PRIMAL; + if(lp->total_iter == 0) + report(lp, DETAILED, "Use primal simplex for finalization at iter %10.0f.\n", + (double) get_total_iter(lp)); + } + if((lp->total_iter == 0) && (lp->spx_status == OPTIMAL)) + report(lp, DETAILED, "Optimal solution with dual simplex at iter %10.0f.\n", + (double) get_total_iter(lp)); + } + + /* Determine if we are ready to break out of the loop */ + if(!changedphase) + break; + } + + /* Check if we are allowed to iterate on the chosen column and row */ + if(is_action(lp->spx_action, ACTION_ITERATE)) { + + lastnr = lp->var_basic[rownr]; + if(refactRecent(lp) == AUTOMATIC) + minitcount = 0; + else if(minitcount > MAX_MINITUPDATES) { + recompute_solution(lp, INITSOL_USEZERO); + minitcount = 0; + } + minit = performiteration(lp, rownr, colnr, theta, primal, + (MYBOOL) (/*(candidatecount > 1) && */ + (stallaccept != AUTOMATIC)), + prow, nzprow, + pcol, NULL, boundswaps); + + /* Check if we should abandon iterations on finding that there is no + hope that this branch can improve on the incumbent B&B solution */ + if(!lp->is_strongbranch && (lp->solutioncount >= 1) && !lp->spx_perturbed && !inP1extra && + bb_better(lp, OF_WORKING, OF_TEST_WE)) { + lp->spx_status = FATHOMED; + ok = FALSE; + break; + } + + if(minit != ITERATE_MAJORMAJOR) + minitcount++; + + /* Update reduced costs for (pure) dual long-step phase 1 */ + if(longsteps && dualphase1 && !inP1extra) { + dualfeasible = isDualFeasible(lp, lp->epsprimal, NULL, dualinfeasibles, NULL); + if(dualfeasible) { + dualphase1 = FALSE; + changedphase = TRUE; + lp->simplex_mode = SIMPLEX_Phase2_DUAL; + } + } +#ifdef UseDualReducedCostUpdate + /* Do a fast update of reduced costs in preparation for the next iteration */ + else if(minit == ITERATE_MAJORMAJOR) + update_reducedcosts(lp, primal, lastnr, colnr, prow, drow); +#endif + if((minit == ITERATE_MAJORMAJOR) && (lastnr <= lp->rows) && is_fixedvar(lp, lastnr)) + lp->fixedvars--; + } + + /* Refactorize if required to */ + if(lp->bfp_mustrefactorize(lp)) { + if(invert(lp, INITSOL_USEZERO, bfpfinal)) { + +#if 0 + /* Verify dual feasibility in case we are attempting the extra dual loop */ + if(changedphase && (dualoffset != 0) && !inP1extra && (lp->spx_status != SWITCH_TO_PRIMAL)) { +#if 1 + if(!isDualFeasible(lp, lp->epsdual, &colnr, NULL, NULL)) { +#else + set_action(&lp->piv_strategy, PRICE_FORCEFULL); + colnr = colprim(lp, drow, nzdrow, FALSE, 1, &candidatecount, FALSE, NULL); + clear_action(&lp->piv_strategy, PRICE_FORCEFULL); + if(colnr > 0) { +#endif + lp->spx_status = SWITCH_TO_PRIMAL; + colnr = 0; + } + } +#endif + + bfpfinal = FALSE; +#ifdef ResetMinitOnReinvert + minit = ITERATE_MAJORMAJOR; +#endif + } + else + lp->spx_status = SINGULAR_BASIS; + } + } + +Finish: + stallMonitor_finish(lp); + multi_free(&(lp->longsteps)); + FREE(prow); + FREE(nzprow); + FREE(pcol); + + return(ok); +} + +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; + + lp->current_iter = 0; + lp->current_bswap = 0; + lp->spx_status = RUNNING; + lp->bb_status = lp->spx_status; + lp->P1extraDim = 0; + set_OF_p1extra(lp, 0); + singular_count = 0; + lost_feas_count = 0; + lost_feas_state = FALSE; + lp->simplex_mode = SIMPLEX_DYNAMIC; + + /* Compute the number of fixed basic and bounded variables (used in long duals) */ + lp->fixedvars = 0; + lp->boundedvars = 0; + for(i = 1; i <= lp->rows; i++) { + j = lp->var_basic[i]; + if((j <= lp->rows) && is_fixedvar(lp, j)) + lp->fixedvars++; + if((lp->upbo[i] < lp->infinite) && (lp->upbo[i] > lp->epsprimal)) + lp->boundedvars++; + } + for(; i <= lp->sum; i++){ + if((lp->upbo[i] < lp->infinite) && (lp->upbo[i] > lp->epsprimal)) + lp->boundedvars++; + } +#ifdef UseLongStepDualPhase1 + allocINT(lp, &infeasibles, lp->columns + 1, FALSE); + infeasibles[0] = 0; +#endif + + /* Reinvert for initialization, if necessary */ + isbb = (MYBOOL) ((MIP_count(lp) > 0) && (lp->bb_level > 1)); + if(is_action(lp->spx_action, ACTION_REINVERT)) { + if(isbb && (lp->bb_bounds->nodessolved == 0)) +/* if(isbb && (lp->bb_basis->pivots == 0)) */ + recompute_solution(lp, INITSOL_SHIFTZERO); + else { + i = my_if(is_action(lp->spx_action, ACTION_REBASE), INITSOL_SHIFTZERO, INITSOL_USEZERO); + invert(lp, (MYBOOL) i, TRUE); + } + } + else if(is_action(lp->spx_action, ACTION_REBASE)) + recompute_solution(lp, INITSOL_SHIFTZERO); + + /* Optionally try to do bound flips to obtain dual feasibility */ + if(is_action(lp->improve, IMPROVE_DUALFEAS) || (lp->rows == 0)) + boundflip_count = &i; + else + boundflip_count = NULL; + + /* Loop for as long as is needed */ + while(lp->spx_status == RUNNING) { + + /* Check for dual and primal feasibility */ + dualfeasible = isbb || + isDualFeasible(lp, lp->epsprimal, boundflip_count, infeasibles, &dualoffset); + + /* Recompute if the dual feasibility check included bound flips */ + if(is_action(lp->spx_action, ACTION_RECOMPUTE)) + recompute_solution(lp, INITSOL_USEZERO); + primalfeasible = isPrimalFeasible(lp, lp->epsprimal, NULL, &primaloffset); + + if(userabort(lp, -1)) + break; + + if(lp->spx_trace) { + if(primalfeasible) + report(lp, NORMAL, "Start at primal feasible basis\n"); + else if(dualfeasible) + report(lp, NORMAL, "Start at dual feasible basis\n"); + else if(lost_feas_count > 0) + report(lp, NORMAL, "Continuing at infeasible basis\n"); + else + report(lp, NORMAL, "Start at infeasible basis\n"); + } + + /* Now do the simplex magic */ + if(((lp->simplex_strategy & SIMPLEX_Phase1_DUAL) == 0) || + ((MIP_count(lp) > 0) && (lp->total_iter == 0) && + is_presolve(lp, PRESOLVE_REDUCEMIP))) { + if(!lost_feas_state && primalfeasible && ((lp->simplex_strategy & SIMPLEX_Phase2_DUAL) > 0)) + lp->spx_status = SWITCH_TO_DUAL; + else + primloop(lp, primalfeasible, 0.0); + if(lp->spx_status == SWITCH_TO_DUAL) + dualloop(lp, TRUE, NULL, 0.0); + } + else { + if(!lost_feas_state && primalfeasible && ((lp->simplex_strategy & SIMPLEX_Phase2_PRIMAL) > 0)) + lp->spx_status = SWITCH_TO_PRIMAL; + else + dualloop(lp, dualfeasible, infeasibles, dualoffset); + if(lp->spx_status == SWITCH_TO_PRIMAL) + primloop(lp, TRUE, 0.0); + } + + /* Check for simplex outcomes that always involve breaking out of the loop; + this includes optimality, unboundedness, pure infeasibility (i.e. not + loss of feasibility), numerical failure and perturbation-based degeneracy + handling */ + i = lp->spx_status; + primalfeasible = (MYBOOL) (i == OPTIMAL); + if(primalfeasible || (i == UNBOUNDED)) + break; + else if(((i == INFEASIBLE) && is_anti_degen(lp, ANTIDEGEN_INFEASIBLE)) || + ((i == LOSTFEAS) && is_anti_degen(lp, ANTIDEGEN_LOSTFEAS)) || + ((i == NUMFAILURE) && is_anti_degen(lp, ANTIDEGEN_NUMFAILURE)) || + ((i == DEGENERATE) && is_anti_degen(lp, ANTIDEGEN_STALLING))) { + /* Check if we should not loop here, but do perturbations */ + if((lp->bb_level <= 1) || is_anti_degen(lp, ANTIDEGEN_DURINGBB)) + break; + + /* Assume that accuracy during B&B is high and that infeasibility is "real" */ +#ifdef AssumeHighAccuracyInBB + if((lp->bb_level > 1) && (i == INFEASIBLE)) + break; +#endif + } + + /* Check for outcomes that may involve trying another simplex loop */ + if(lp->spx_status == SINGULAR_BASIS) { + lost_feas_state = FALSE; + singular_count++; + if(singular_count >= DEF_MAXSINGULARITIES) { + report(lp, IMPORTANT, "spx_run: Failure due to too many singular bases.\n"); + lp->spx_status = NUMFAILURE; + break; + } + if(lp->spx_trace || (lp->verbose > DETAILED)) + report(lp, NORMAL, "spx_run: Singular basis; attempting to recover.\n"); + lp->spx_status = RUNNING; + /* Singular pivots are simply skipped by the inversion, leaving a row's + slack variable in the basis instead of the singular user variable. */ + } + else { + lost_feas_state = (MYBOOL) (lp->spx_status == LOSTFEAS); +#if 0 + /* Optionally handle loss of numerical accuracy as loss of feasibility, + but only attempt a single loop to try to recover from this. */ + lost_feas_state |= (MYBOOL) ((lp->spx_status == NUMFAILURE) && (lost_feas_count < 1)); +#endif + if(lost_feas_state) { + lost_feas_count++; + if(lost_feas_count < DEF_MAXSINGULARITIES) { + report(lp, DETAILED, "spx_run: Recover lost feasibility at iter %10.0f.\n", + (double) get_total_iter(lp)); + lp->spx_status = RUNNING; + } + else { + report(lp, IMPORTANT, "spx_run: Lost feasibility %d times - iter %10.0f and %9.0f nodes.\n", + lost_feas_count, (double) get_total_iter(lp), (double) lp->bb_totalnodes); + lp->spx_status = NUMFAILURE; + } + } + } + } + + /* Update iteration tallies before returning */ + lp->total_iter += lp->current_iter; + lp->current_iter = 0; + lp->total_bswap += lp->current_bswap; + lp->current_bswap = 0; + FREE(infeasibles); + + return(lp->spx_status); +} /* spx_run */ + +lprec *make_lag(lprec *lpserver) +{ + int i; + lprec *hlp; + MYBOOL ret; + LPSREAL *duals; + + /* Create a Lagrangean solver instance */ + hlp = make_lp(0, lpserver->columns); + + if(hlp != NULL) { + + /* First create and core variable data */ + set_sense(hlp, is_maxim(lpserver)); + hlp->lag_bound = lpserver->bb_limitOF; + for(i = 1; i <= lpserver->columns; i++) { + set_mat(hlp, 0, i, get_mat(lpserver, 0, i)); + if(is_binary(lpserver, i)) + set_binary(hlp, i, TRUE); + else { + set_int(hlp, i, is_int(lpserver, i)); + set_bounds(hlp, i, get_lowbo(lpserver, i), get_upbo(lpserver, i)); + } + } + /* Then fill data for the Lagrangean constraints */ + hlp->matL = lpserver->matA; + inc_lag_space(hlp, lpserver->rows, TRUE); + ret = get_ptr_sensitivity_rhs(hlp, &duals, NULL, NULL); + for(i = 1; i <= lpserver->rows; i++) { + hlp->lag_con_type[i] = get_constr_type(lpserver, i); + hlp->lag_rhs[i] = lpserver->orig_rhs[i]; + hlp->lambda[i] = (ret) ? duals[i - 1] : 0.0; + } + } + + return(hlp); +} + +STATIC int heuristics(lprec *lp, int mode) +/* Initialize / bound a MIP problem */ +{ + lprec *hlp; + int status = PROCFAIL; + + if(lp->bb_level > 1) + return( status ); + + status = RUNNING; + lp->bb_limitOF = my_chsign(is_maxim(lp), -lp->infinite); + if(FALSE && (lp->int_vars > 0)) { + + /* 1. Copy the problem into a new relaxed instance, extracting Lagrangean constraints */ + hlp = make_lag(lp); + + /* 2. Run the Lagrangean relaxation */ + status = solve(hlp); + + /* 3. Copy the key results (bound) into the original problem */ + lp->bb_heuristicOF = hlp->best_solution[0]; + + /* 4. Delete the helper heuristic */ + hlp->matL = NULL; + delete_lp(hlp); + } + + lp->timeheuristic = timeNow(); + return( status ); +} + +STATIC int lag_solve(lprec *lp, LPSREAL 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; + + /* Make sure we have something to work with */ + if(lp->spx_status != OPTIMAL) { + lp->lag_status = NOTRUN; + return( lp->lag_status ); + } + + /* Allocate iteration arrays */ + if(!allocREAL(lp, &OrigObj, lp->columns + 1, FALSE) || + !allocREAL(lp, &ModObj, lp->columns + 1, TRUE) || + !allocREAL(lp, &SubGrad, get_Lrows(lp) + 1, TRUE) || + !allocREAL(lp, &BestFeasSol, lp->sum + 1, TRUE)) { + lp->lag_status = NOMEMORY; + return( lp->lag_status ); + } + lp->lag_status = RUNNING; + + /* Prepare for Lagrangean iterations using results from relaxed problem */ + oldpresolve = lp->do_presolve; + lp->do_presolve = PRESOLVE_NONE; + push_basis(lp, NULL, NULL, NULL); + + /* Initialize variables (assume minimization problem in overall structure) */ + Zlb = lp->best_solution[0]; + Zub = start_bound; + Zbest = Zub; + Znow = Zlb; + Zprev = lp->infinite; + rhsmod = 0; + + Phi = DEF_LAGCONTRACT; /* In the range 0-2.0 to guarantee convergence */ +/* Phi = 0.15; */ + LagFeas = FALSE; + Converged= FALSE; + AnyFeas = FALSE; + citer = 0; + nochange = 0; + + /* Initialize reference and solution vectors; don't bother about the + original OF offset since we are maintaining an offset locally. */ + +/* #define DirectOverrideOF */ + + get_row(lp, 0, OrigObj); +#ifdef DirectOverrideOF + set_OF_override(lp, ModObj); +#endif + OrigObj[0] = get_rh(lp, 0); + for(i = 1 ; i <= get_Lrows(lp); i++) + lp->lambda[i] = 0; + + /* Iterate to convergence, failure or user-specified termination */ + while((lp->lag_status == RUNNING) && (citer < num_iter)) { + + citer++; + + /* Compute constraint feasibility gaps and associated sum of squares, + and determine feasibility over the Lagrangean constraints; + SubGrad is the subgradient, which here is identical to the slack. */ + LagFeas = TRUE; + Converged = TRUE; + SqrsumSubGrad = 0; + for(i = 1; i <= get_Lrows(lp); i++) { + hold = lp->lag_rhs[i]; + for(j = 1; j <= lp->columns; j++) + hold -= mat_getitem(lp->matL, i, j) * lp->best_solution[lp->rows + j]; + if(LagFeas) { + if(lp->lag_con_type[i] == EQ) { + if(fabs(hold) > lp->epsprimal) + LagFeas = FALSE; + } + else if(hold < -lp->epsprimal) + LagFeas = FALSE; + } + /* Test for convergence and update */ + if(Converged && (fabs(my_reldiff(hold , SubGrad[i])) > lp->lag_accept)) + Converged = FALSE; + SubGrad[i] = hold; + SqrsumSubGrad += hold * hold; + } + SqrsumSubGrad = sqrt(SqrsumSubGrad); +#if 1 + Converged &= LagFeas; +#endif + if(Converged) + break; + + /* Modify step parameters and initialize ahead of next iteration */ + Znow = lp->best_solution[0] - rhsmod; + if(Znow > Zub) { + /* Handle exceptional case where we overshoot */ + Phi *= DEF_LAGCONTRACT; + StepSize *= (Zub-Zlb) / (Znow-Zlb); + } + else +#define LagBasisContract +#ifdef LagBasisContract +/* StepSize = Phi * (Zub - Znow) / SqrsumSubGrad; */ + StepSize = Phi * (2-DEF_LAGCONTRACT) * (Zub - Znow) / SqrsumSubGrad; +#else + StepSize = Phi * (Zub - Znow) / SqrsumSubGrad; +#endif + + /* Compute the new dual price vector (Lagrangean multipliers, lambda) */ + for(i = 1; i <= get_Lrows(lp); i++) { + lp->lambda[i] += StepSize * SubGrad[i]; + if((lp->lag_con_type[i] != EQ) && (lp->lambda[i] > 0)) { + /* Handle case where we overshoot and need to correct (see above) */ + if(Znow < Zub) + lp->lambda[i] = 0; + } + } +/* normalizeVector(lp->lambda, get_Lrows(lp)); */ + + /* Save the current vector if it is better */ + if(LagFeas && (Znow < Zbest)) { + + /* Recompute the objective function value in terms of the original values */ + MEMCOPY(BestFeasSol, lp->best_solution, lp->sum+1); + hold = OrigObj[0]; + for(i = 1; i <= lp->columns; i++) + hold += lp->best_solution[lp->rows + i] * OrigObj[i]; + BestFeasSol[0] = hold; + if(lp->lag_trace) + report(lp, NORMAL, "lag_solve: Improved feasible solution at iteration %d of %g\n", + citer, hold); + + /* Reset variables */ + Zbest = Znow; + AnyFeas = TRUE; + nochange = 0; + } + else if(Znow == Zprev) { + nochange++; + if(nochange > LAG_SINGULARLIMIT) { + Phi *= 0.5; + nochange = 0; + } + } + Zprev = Znow; + + /* Recompute the objective function values for the next iteration */ + for(j = 1; j <= lp->columns; j++) { + hold = 0; + for(i = 1; i <= get_Lrows(lp); i++) + hold += lp->lambda[i] * mat_getitem(lp->matL, i, j); + ModObj[j] = OrigObj[j] - my_chsign(is_maxim(lp), hold); +#ifndef DirectOverrideOF + set_mat(lp, 0, j, ModObj[j]); +#endif + } + + /* Recompute the fixed part of the new objective function */ + rhsmod = my_chsign(is_maxim(lp), get_rh(lp, 0)); + for(i = 1; i <= get_Lrows(lp); i++) + rhsmod += lp->lambda[i] * lp->lag_rhs[i]; + + /* Print trace/debugging information, if specified */ + if(lp->lag_trace) { + report(lp, IMPORTANT, "Zub: %10g Zlb: %10g Stepsize: %10g Phi: %10g Feas %d\n", + (double) Zub, (double) Zlb, (double) StepSize, (double) Phi, LagFeas); + for(i = 1; i <= get_Lrows(lp); i++) + report(lp, IMPORTANT, "%3d SubGrad %10g lambda %10g\n", + i, (double) SubGrad[i], (double) lp->lambda[i]); + if(lp->sum < 20) + print_lp(lp); + } + + /* Solve the Lagrangean relaxation, handle failures and compute + the Lagrangean objective value, if successful */ + i = spx_solve(lp); + if(lp->spx_status == UNBOUNDED) { + if(lp->lag_trace) { + report(lp, NORMAL, "lag_solve: Unbounded solution encountered with this OF:\n"); + for(i = 1; i <= lp->columns; i++) + report(lp, NORMAL, RESULTVALUEMASK " ", (double) ModObj[i]); + } + goto Leave; + } + else if((lp->spx_status == NUMFAILURE) || (lp->spx_status == PROCFAIL) || + (lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT) || + (lp->spx_status == INFEASIBLE)) { + lp->lag_status = lp->spx_status; + } + + /* Compare optimal bases and contract if we have basis stationarity */ +#ifdef LagBasisContract + same_basis = compare_basis(lp); + if(LagFeas && + !same_basis) { + pop_basis(lp, FALSE); + push_basis(lp, NULL, NULL, NULL); + Phi *= DEF_LAGCONTRACT; + } + if(lp->lag_trace) { + report(lp, DETAILED, "lag_solve: Simplex status code %d, same basis %s\n", + lp->spx_status, my_boolstr(same_basis)); + print_solution(lp, 1); + } +#endif + } + + /* Transfer solution values */ + if(AnyFeas) { + lp->lag_bound = my_chsign(is_maxim(lp), Zbest); + for(i = 0; i <= lp->sum; i++) + lp->solution[i] = BestFeasSol[i]; + transfer_solution(lp, TRUE); + if(!is_maxim(lp)) + for(i = 1; i <= get_Lrows(lp); i++) + lp->lambda[i] = my_flipsign(lp->lambda[i]); + } + + /* Do standard postprocessing */ +Leave: + + /* Set status variables and report */ + if(citer >= num_iter) { + if(AnyFeas) + lp->lag_status = FEASFOUND; + else + lp->lag_status = NOFEASFOUND; + } + else + lp->lag_status = lp->spx_status; + if(lp->lag_status == OPTIMAL) { + report(lp, NORMAL, "\nLagrangean convergence achieved in %d iterations\n", citer); + i = check_solution(lp, lp->columns, + lp->best_solution, lp->orig_upbo, lp->orig_lowbo, lp->epssolution); + } + else { + report(lp, NORMAL, "\nUnsatisfactory convergence achieved over %d Lagrangean iterations.\n", + citer); + if(AnyFeas) + report(lp, NORMAL, "The best feasible Lagrangean objective function value was %g\n", + lp->best_solution[0]); + } + + /* Restore the original objective function */ +#ifdef DirectOverrideOF + set_OF_override(lp, NULL); +#else + for(i = 1; i <= lp->columns; i++) + set_mat(lp, 0, i, OrigObj[i]); +#endif + + /* ... and then free memory */ + FREE(BestFeasSol); + FREE(SubGrad); + FREE(OrigObj); + FREE(ModObj); + pop_basis(lp, FALSE); + + lp->do_presolve = oldpresolve; + + return( lp->lag_status ); +} + +STATIC int spx_solve(lprec *lp) +{ + int status; + MYBOOL iprocessed; + + lp->total_iter = 0; + lp->total_bswap = 0; + lp->perturb_count = 0; + lp->bb_maxlevel = 1; + lp->bb_totalnodes = 0; + lp->bb_improvements = 0; + lp->bb_strongbranches= 0; + lp->is_strongbranch = FALSE; + lp->bb_level = 0; + lp->bb_solutionlevel = 0; + lp->best_solution[0] = my_chsign(is_maxim(lp), lp->infinite); + if(lp->invB != NULL) + 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) + goto Leave; + + iprocessed = !lp->wasPreprocessed; + if(!preprocess(lp) || userabort(lp, -1)) + goto Leave; + + if(mat_validate(lp->matA)) { + + /* Do standard initializations */ + lp->solutioncount = 0; + lp->real_solution = lp->infinite; + set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT); + lp->bb_break = FALSE; + + /* Do the call to the real underlying solver (note that + run_BB is replaceable with any compatible MIP solver) */ + status = run_BB(lp); + + /* Restore modified problem */ + if(iprocessed) + 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"); + + goto Leave; + } + + /* If we get here, mat_validate(lp) failed. */ + if(lp->bb_trace || lp->spx_trace) + report(lp, CRITICAL, "spx_solve: The current LP seems to be invalid\n"); + lp->spx_status = NUMFAILURE; + +Leave: + lp->timeend = timeNow(); + + if((lp->lag_status != RUNNING) && (lp->invB != NULL)) { + int itemp; + LPSREAL test; + + itemp = lp->bfp_nonzeros(lp, TRUE); + test = 100; + if(lp->total_iter > 0) + test *= (LPSREAL) 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, " 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", + lp->bfp_refactcount(lp, BFP_STAT_REFACT_TOTAL), + lp->bfp_refactcount(lp, BFP_STAT_REFACT_TIMED), + lp->bfp_refactcount(lp, BFP_STAT_REFACT_DENSE)); + report(lp, NORMAL, " ... on average %.1f major pivots per refactorization.\n", + get_refactfrequency(lp, TRUE)); + report(lp, NORMAL, " The largest [%s] fact(B) had %d NZ entries, %.1fx largest basis.\n", + lp->bfp_name(), itemp, lp->bfp_efficiency(lp)); + if(lp->perturb_count > 0) + report(lp, NORMAL, " The bounds were relaxed via perturbations %d times.\n", + lp->perturb_count); + if(MIP_count(lp) > 0) { + if(lp->bb_solutionlevel > 0) + report(lp, NORMAL, " The maximum B&B level was %d, %.1fx MIP order, %d at the optimal solution.\n", + lp->bb_maxlevel, (double) lp->bb_maxlevel / (MIP_count(lp)+lp->int_vars), lp->bb_solutionlevel); + else + report(lp, NORMAL, " The maximum B&B level was %d, %.1fx MIP order, with %.0f nodes explored.\n", + lp->bb_maxlevel, (double) lp->bb_maxlevel / (MIP_count(lp)+lp->int_vars), (double) get_total_nodes(lp)); + if(GUB_count(lp) > 0) + report(lp, NORMAL, " %d general upper-bounded (GUB) structures were employed during B&B.\n", + GUB_count(lp)); + } + report(lp, NORMAL, " The constraint matrix inf-norm is %g, with a dynamic range of %g.\n", + lp->matA->infnorm, lp->matA->dynrange); + report(lp, NORMAL, " Time to load data was %.3f seconds, presolve used %.3f seconds,\n", + lp->timestart-lp->timecreate, lp->timepresolved-lp->timestart); + report(lp, NORMAL, " ... %.3f seconds in simplex solver, in total %.3f seconds.\n", + lp->timeend-lp->timepresolved, lp->timeend-lp->timecreate); + } + return( lp->spx_status ); + +} /* spx_solve */ + +int lin_solve(lprec *lp) +{ + int status = NOTRUN; + + /* Don't do anything in case of an empty model */ + lp->lag_status = NOTRUN; + /* if(get_nonzeros(lp) == 0) { */ + if(lp->columns == 0) { + default_basis(lp); + lp->spx_status = NOTRUN; + return( /* OPTIMAL */ lp->spx_status); + } + + /* Otherwise reset selected arrays before solving */ + unset_OF_p1extra(lp); + free_duals(lp); + FREE(lp->drow); + FREE(lp->nzdrow); + if(lp->bb_cuttype != NULL) + freecuts_BB(lp); + + /* Reset/initialize timers */ + lp->timestart = timeNow(); + lp->timeheuristic = 0; + lp->timepresolved = 0; + lp->timeend = 0; + + /* Do heuristics ahead of solving the model */ + if(heuristics(lp, AUTOMATIC) != RUNNING) + return( INFEASIBLE ); + + /* Solve the full, prepared model */ + status = spx_solve(lp); + if((get_Lrows(lp) > 0) && (lp->lag_status == NOTRUN)) { + if(status == OPTIMAL) + status = lag_solve(lp, lp->bb_heuristicOF, DEF_LAGMAXITERATIONS); + else + report(lp, IMPORTANT, "\nCannot do Lagrangean optimization since root model was not solved.\n"); + } + + /* 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/external/lpsolve/build/lp_solve/lp_utils.c b/src/external/lpsolve/build/lp_solve/lp_utils.c new file mode 100644 index 00000000..46f3c067 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_utils.c @@ -0,0 +1,1060 @@ +#define CODE_lp_utils + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_utils.h" +#include +#include +#include "lp_bit.h" +#include "R_ext/Random.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* + Miscellaneous utilities as implemented for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: GLPL. + + Requires: lp_utils.h, lp_lib.h + + Release notes: + v1.0.0 1 January 2003 Memory allocation, sorting, searching, time and + doubly linked list functions. + v1.1.0 15 May 2004 Added vector packing functionality + v1.2.0 10 January 2005 Added vector pushing/popping functionality + Modified return values and fixed problem in + linked list functions. + + ---------------------------------------------------------------------------------- +*/ + +STATIC MYBOOL allocCHAR(lprec *lp, char **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (char *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (char *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + MEMCLEAR(*ptr, size); + } + else + *ptr = (char *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d 'char' failed\n", size); + lp->spx_status = NOMEMORY; + return( FALSE ); + } + else + return( TRUE ); +} +STATIC MYBOOL allocMYBOOL(lprec *lp, MYBOOL **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (MYBOOL *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (MYBOOL *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + MEMCLEAR(*ptr, size); + } + else + *ptr = (MYBOOL *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d 'MYBOOL' failed\n", size); + lp->spx_status = NOMEMORY; + return( FALSE ); + } + else + return( TRUE ); +} +STATIC MYBOOL allocINT(lprec *lp, int **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (int *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (int *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + MEMCLEAR(*ptr, size); + } + else + *ptr = (int *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d 'INT' failed\n", size); + lp->spx_status = NOMEMORY; + return( FALSE ); + } + else + return( TRUE ); +} +STATIC MYBOOL allocREAL(lprec *lp, LPSREAL **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (LPSREAL *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (LPSREAL *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + MEMCLEAR(*ptr, size); + } + else + *ptr = (LPSREAL *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d 'LPSREAL' failed\n", size); + lp->spx_status = NOMEMORY; + return( FALSE ); + } + else + return( TRUE ); +} +STATIC MYBOOL allocLREAL(lprec *lp, LREAL **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (LREAL *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (LREAL *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + MEMCLEAR(*ptr, size); + } + else + *ptr = (LREAL *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d 'LREAL' failed\n", size); + lp->spx_status = NOMEMORY; + return( FALSE ); + } + else + return( TRUE ); +} + +STATIC MYBOOL allocFREE(lprec *lp, void **ptr) +{ + MYBOOL status = TRUE; + + if(*ptr != NULL) { + free(*ptr); + *ptr = NULL; + } + else { + status = FALSE; + lp->report(lp, CRITICAL, "free() failed on line %d of file %s\n", + __LINE__, __FILE__); + } + return(status); +} + +/* Do hoops to provide debugging info with FORTIFY */ +#undef CODE_lp_utils +#include "lp_utils.h" +/* alloc-routines should always be before this line! */ + +int comp_bits(MYBOOL *bitarray1, MYBOOL *bitarray2, int items) +{ + int i, items4, left = 0, right = 0; + MYBOOL comp1; + unsigned long comp4; + + /* Convert items count to 8-bit representation, if necessary */ + if(items > 0) { + i = items % 8; + items /= 8; + if(i) + items++; + } + else + items = -items; + + /* Do the wide unsigned integer part for speed */ + items4 = items / sizeof(unsigned long); + i = 0; + while(i < items4) { + comp4 = ((unsigned long *) bitarray1)[i] & ~((unsigned long *) bitarray2)[i]; + if(comp4) + left++; + comp4 = ((unsigned long *) bitarray2)[i] & ~((unsigned long *) bitarray1)[i]; + if(comp4) + right++; + i++; + } + + /* Do the trailing slow narrow unsigned integer part */ + i *= sizeof(unsigned long); + i++; + while(i < items) { + comp1 = bitarray1[i] & ~bitarray2[i]; + if(comp1) + left++; + comp1 = bitarray2[i] & ~bitarray1[i]; + if(comp1) + right++; + i++; + } + + /* Determine set comparison outcomes */ + if((left > 0) && (right == 0)) /* array1 is a superset of array2 */ + i = 1; + else if((left == 0) && (right > 0)) /* array2 is a superset of array1 */ + i = -1; + else if((left == 0) && (right == 0)) /* array1 and array2 are identical */ + i = 0; + else + i = -2; /* indicate all other outcomes */ + return( i ); +} + + +STATIC workarraysrec *mempool_create(lprec *lp) +{ + workarraysrec *temp; + temp = (workarraysrec *) calloc(1, sizeof(workarraysrec)); + temp->lp = lp; + return( temp ); +} +STATIC char *mempool_obtainVector(workarraysrec *mempool, int count, int unitsize) +{ + char *newmem = NULL; + MYBOOL *bnewmem = NULL; + int *inewmem = NULL, size, i, ib, ie, memMargin = 0; + LPSREAL *rnewmem = NULL; + + /* First find the iso-sized window (binary search) */ + size = count*unitsize; + memMargin += size; + ib = 0; + ie = mempool->count-1; + while(ie >= ib) { + i = (ib+ie) / 2; + if(abs(mempool->vectorsize[i]) > memMargin) + ie = i-1; + else if(abs(mempool->vectorsize[i]) < size) + ib = i+1; + else { + /* Find the beginning of the exact-sized array group */ + do { + ib = i; + i--; + } while((i >= 0) && (abs(mempool->vectorsize[i]) >= size)); + break; + } + } + + /* Check if we have a preallocated unused array of sufficient size */ + ie = mempool->count-1; + for(i = ib; i <= ie; i++) + if(mempool->vectorsize[i] < 0) + break; + + /* Obtain and activate existing, unused vector if we are permitted */ + if(i <= ie) { +#ifdef Paranoia + if((mempool->vectorsize[i] > 0) || (abs(mempool->vectorsize[i]) < size)) { + lprec *lp = mempool->lp; + lp->report(lp, SEVERE, "mempool_obtainVector: Invalid %s existing vector selected\n", + (ie < 0 ? "too small" : "occupied")); + lp->spx_status = NOMEMORY; + lp->bb_break = TRUE; + return( newmem ); + } +#endif + newmem = mempool->vectorarray[i]; + mempool->vectorsize[i] *= -1; + } + + /* Otherwise allocate a new vector */ + else if(unitsize == sizeof(MYBOOL)) { + allocMYBOOL(mempool->lp, &bnewmem, count, TRUE); + newmem = (char *) bnewmem; + } + else if(unitsize == sizeof(int)) { + allocINT(mempool->lp, &inewmem, count, TRUE); + newmem = (char *) inewmem; + } + else if(unitsize == sizeof(LPSREAL)) { + allocREAL(mempool->lp, &rnewmem, count, TRUE); + newmem = (char *) rnewmem; + } + + /* Insert into master array if necessary (maintain sort by ascending size) */ + if((i > ie) && (newmem != NULL)) { + mempool->count++; + if(mempool->count >= mempool->size) { + mempool->size += 10; + mempool->vectorarray = (char **) realloc(mempool->vectorarray, + sizeof(*(mempool->vectorarray))*mempool->size); + mempool->vectorsize = (int *) realloc(mempool->vectorsize, + sizeof(*(mempool->vectorsize))*mempool->size); + } + ie++; + i = ie + 1; + if(i < mempool->count) { + MEMMOVE(mempool->vectorarray+i, mempool->vectorarray+ie, 1); + MEMMOVE(mempool->vectorsize+i, mempool->vectorsize+ie, 1); + } + mempool->vectorarray[ie] = newmem; + mempool->vectorsize[ie] = size; + } + + return( newmem ); +} +STATIC MYBOOL mempool_releaseVector(workarraysrec *mempool, char *memvector, MYBOOL forcefree) +{ + int i; + +#if 0 + forcefree = TRUE; +#endif + + for(i = mempool->count-1; i >= 0; i--) + if(mempool->vectorarray[i] == memvector) + break; + + if((i < 0) || (mempool->vectorsize[i] < 0)) + return( FALSE ); + + if(forcefree) { + FREE(mempool->vectorarray[i]); + mempool->count--; + for(; i < mempool->count; i++) + mempool->vectorarray[i] = mempool->vectorarray[i+1]; + } + else + mempool->vectorsize[i] *= -1; + + return( TRUE ); +} +STATIC MYBOOL mempool_free(workarraysrec **mempool) +{ + int i = (*mempool)->count; + + while(i > 0) { + i--; + if((*mempool)->vectorsize[i] < 0) /* Handle unused vectors */ + (*mempool)->vectorsize[i] *= -1; + mempool_releaseVector(*mempool, (*mempool)->vectorarray[i], TRUE); + } + FREE((*mempool)->vectorarray); + FREE((*mempool)->vectorsize); + FREE(*mempool); + return( TRUE ); +} + +LPSREAL *cloneREAL(lprec *lp, LPSREAL *origlist, int size) +{ + LPSREAL *newlist; + + size += 1; + if(allocREAL(lp, &newlist, size, FALSE)) + MEMCOPY(newlist, origlist, size); + return(newlist); +} +MYBOOL *cloneMYBOOL(lprec *lp, MYBOOL *origlist, int size) +{ + MYBOOL *newlist; + + size += 1; + if(allocMYBOOL(lp, &newlist, size, FALSE)) + MEMCOPY(newlist, origlist, size); + return(newlist); +} +int *cloneINT(lprec *lp, int *origlist, int size) +{ + int *newlist; + + size += 1; + if(allocINT(lp, &newlist, size, FALSE)) + MEMCOPY(newlist, origlist, size); + return(newlist); +} + +STATIC void roundVector(LREAL *myvector, int endpos, LREAL roundzero) +{ + if(roundzero > 0) + for(; endpos >= 0; myvector++, endpos--) + if(fabs(*myvector) < roundzero) + *myvector = 0; +} + +STATIC LPSREAL normalizeVector(LPSREAL *myvector, int endpos) +/* Scale the ingoing vector so that its norm is unit, and return the original length */ +{ + int i; + LPSREAL SSQ; + + /* Cumulate squares */ + SSQ = 0; + for(i = 0; i <= endpos; myvector++, i++) + SSQ += (*myvector) * (*myvector); + + /* Normalize */ + SSQ = sqrt(SSQ); + if(SSQ > 0) + for(myvector--; i > 0; myvector--, i--) + (*myvector) /= SSQ; + + return( SSQ ); +} + +/* ---------------------------------------------------------------------------------- */ +/* Other general utilities */ +/* ---------------------------------------------------------------------------------- */ + +STATIC void swapINT(int *item1, int *item2) +{ + int hold = *item1; + *item1 = *item2; + *item2 = hold; +} + +STATIC void swapREAL(LPSREAL *item1, LPSREAL *item2) +{ + LPSREAL hold = *item1; + *item1 = *item2; + *item2 = hold; +} + +STATIC void swapPTR(void **item1, void **item2) +{ + void *hold; + hold = *item1; + *item1 = *item2; + *item2 = hold; +} + + +STATIC LPSREAL restoreINT(LPSREAL valREAL, LPSREAL epsilon) +{ + LPSREAL valINT, fracREAL, fracABS; + + fracREAL = modf(valREAL, &valINT); + fracABS = fabs(fracREAL); + if(fracABS < epsilon) + return(valINT); + else if(fracABS > 1-epsilon) { + if(fracREAL < 0) + return(valINT-1); + else + return(valINT+1); + } + return(valREAL); +} + +STATIC LPSREAL roundToPrecision(LPSREAL value, LPSREAL precision) +{ +#if 1 + LPSREAL vmod; + int vexp2, vexp10; + LLONG sign; + + if(precision == 0) + return(value); + + sign = my_sign(value); + value = fabs(value); + + /* Round to integer if possible */ + if(value < 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 ); + } + + /* Optionally round with base 2 representation for additional precision */ +#define roundPrecisionBase2 +#ifdef roundPrecisionBase2 + value = frexp(value, &vexp2); +#else + vexp2 = 0; +#endif + + /* Convert to desired precision */ + vexp10 = (int) log10(value); + precision *= pow(10.0, vexp10); + modf(value/precision+0.5, &value); + value *= sign*precision; + + /* Restore base 10 representation if base 2 was active */ + if(vexp2 != 0) + value = ldexp(value, vexp2); +#endif + + return( value ); +} + + +/* ---------------------------------------------------------------------------------- */ +/* Searching function specialized for lp_solve */ +/* ---------------------------------------------------------------------------------- */ +STATIC int searchFor(int target, int *attributes, int size, int offset, MYBOOL absolute) +{ + int beginPos, endPos; + int newPos, match; + + /* Set starting and ending index offsets */ + beginPos = offset; + endPos = beginPos + size - 1; + + /* Do binary search logic based on a sorted attribute vector */ + newPos = (beginPos + endPos) / 2; + match = attributes[newPos]; + if(absolute) + match = abs(match); + while(endPos - beginPos > LINEARSEARCH) { + if(match < target) { + beginPos = newPos + 1; + newPos = (beginPos + endPos) / 2; + match = attributes[newPos]; + if(absolute) + match = abs(match); + } + else if(match > target) { + endPos = newPos - 1; + newPos = (beginPos + endPos) / 2; + match = attributes[newPos]; + if(absolute) + match = abs(match); + } + else { + beginPos = newPos; + endPos = newPos; + } + } + + /* Do linear (unsorted) search logic */ + if(endPos - beginPos <= LINEARSEARCH) { + match = attributes[beginPos]; + if(absolute) + match = abs(match); + 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 */ + if((beginPos == endPos) && (match == target)) + return(beginPos); + else + return(-1); + +} + + +/* ---------------------------------------------------------------------------------- */ +/* Other supporting math routines */ +/* ---------------------------------------------------------------------------------- */ + +STATIC MYBOOL isINT(lprec *lp, LPSREAL value) +{ +#if 0 + return( (MYBOOL) (modf(fabs(value)+lp->epsint, &value) < 2*lp->epsint) ); +#elif 1 + value = fabs(value)+lp->epsint; + return( (MYBOOL) (my_reldiff(value, floor(value)) < 2*lp->epsint) ); +#elif 0 + LPSREAL 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); + return( (MYBOOL) ((value < lp->epsint) || (value > (1 - lp->epsint)) ); +#else + value += lp->epsint; + return( (MYBOOL) (fabs(value-floor(value)) < 2*lp->epsint) ); +#endif +} + +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) +{ + LPSREAL temp; + temp = *upbound; + if(fabs(*lobound) > 0) + *upbound = -(*lobound); + else + *upbound = 0; + if(fabs(temp) > 0) + *lobound = -temp; + else + *lobound = 0; +} + + +/* ---------------------------------------------------------------------------------- */ +/* Define randomization routine */ +/* ---------------------------------------------------------------------------------- */ +STATIC LPSREAL rand_uniform(lprec *lp, LPSREAL range) +{ + static MYBOOL randomized = FALSE; /* static ok here for reentrancy/multithreading */ + + if(!randomized) { + randomized = TRUE; + } + GetRNGstate(); + range *= (LPSREAL) unif_rand(); + PutRNGstate(); + return( range ); +} + + +/* ---------------------------------------------------------------------------------- */ +/* Define routines for doubly linked lists of integers */ +/* ---------------------------------------------------------------------------------- */ + +STATIC int createLink(int size, LLrec **linkmap, MYBOOL *usedpos) +{ + int i, j; + MYBOOL reverse; + + *linkmap = (LLrec *) calloc(1, sizeof(**linkmap)); + if(*linkmap == NULL) + return( -1 ); + + reverse = (MYBOOL) (size < 0); + if(reverse) + size = -size; + (*linkmap)->map = (int *) calloc(2*(size + 1), sizeof(int)); + if((*linkmap)->map == NULL) + return( -1 ); + + (*linkmap)->size = size; + j = 0; + if(usedpos == NULL) + (*linkmap)->map[0] = 0; + else { + for(i = 1; i <= size; i++) + if(!usedpos[i] ^ reverse) { + /* Set the forward link */ + (*linkmap)->map[j] = i; + /* Set the backward link */ + (*linkmap)->map[size+i] = j; + j = i; + if((*linkmap)->count == 0) + (*linkmap)->firstitem = i; + (*linkmap)->lastitem = i; + (*linkmap)->count++; + } + } + (*linkmap)->map[2*size+1] = j; + + return( (*linkmap)->count ); +} + +STATIC MYBOOL freeLink(LLrec **linkmap) +{ + MYBOOL status = TRUE; + + if((linkmap == NULL) || (*linkmap == NULL)) + status = FALSE; + else { + if((*linkmap)->map != NULL) + free((*linkmap)->map); + free(*linkmap); + *linkmap = NULL; + } + return( status ); +} + +STATIC int sizeLink(LLrec *linkmap) +{ + return(linkmap->size); +} + +STATIC MYBOOL isActiveLink(LLrec *linkmap, int itemnr) +{ + if((linkmap->map[itemnr] != 0) || + (linkmap->map[linkmap->size+itemnr] != 0) || + (linkmap->map[0] == itemnr)) + return( TRUE ); + else + return( FALSE ); +} + +STATIC int countActiveLink(LLrec *linkmap) +{ + return(linkmap->count); +} + +STATIC int countInactiveLink(LLrec *linkmap) +{ + return(linkmap->size-linkmap->count); +} + +STATIC int firstActiveLink(LLrec *linkmap) +{ + return(linkmap->map[0]); +} + +STATIC int lastActiveLink(LLrec *linkmap) +{ + return(linkmap->map[2*linkmap->size+1]); +} + +STATIC MYBOOL appendLink(LLrec *linkmap, int newitem) +{ + int k, size; + size = linkmap->size; + + if(linkmap->map[newitem] != 0) + return( FALSE ); + + /* Link forward */ + k = linkmap->map[2*size+1]; + linkmap->map[k] = newitem; + + /* Link backward */ + linkmap->map[size+newitem] = k; + linkmap->map[2*size+1] = newitem; + + /* Update count and return */ + if(linkmap->count == 0) + linkmap->firstitem = newitem; + linkmap->lastitem = newitem; + linkmap->count++; + + return( TRUE ); +} + +STATIC MYBOOL insertLink(LLrec *linkmap, int afteritem, int newitem) +{ + int k, size; + + size = linkmap->size; + + if(linkmap->map[newitem] != 0) + return( FALSE ); + + if(afteritem == linkmap->map[2*size+1]) + appendLink(linkmap, newitem); + else { + /* Link forward */ + k = linkmap->map[afteritem]; + linkmap->map[afteritem] = newitem; + linkmap->map[newitem] = k; + + /* Link backward */ + linkmap->map[size+k] = newitem; + linkmap->map[size+newitem] = afteritem; + + /* Update count */ + SETMIN(linkmap->firstitem, newitem); + SETMAX(linkmap->lastitem, newitem); + linkmap->count++; + } + + return( TRUE ); +} + +STATIC MYBOOL setLink(LLrec *linkmap, int newitem) +{ + if(isActiveLink(linkmap, newitem)) + return( FALSE ); + else + return( insertLink(linkmap, prevActiveLink(linkmap, newitem), newitem) ); +} + +STATIC MYBOOL fillLink(LLrec *linkmap) +{ + int k, size; + size = linkmap->size; + + k = firstActiveLink(linkmap); + if(k != 0) + return( FALSE ); + for(k = 1; k <= size; k++) + appendLink(linkmap, k); + return( TRUE ); +} + +STATIC int nextActiveLink(LLrec *linkmap, int backitemnr) +{ + if((backitemnr < 0) || (backitemnr > linkmap->size)) + return( -1 ); + else { + if(backitemnr < linkmap->lastitem) + while((backitemnr > linkmap->firstitem) && (linkmap->map[backitemnr] == 0)) + backitemnr--; + return(linkmap->map[backitemnr]); + } +} + +STATIC int prevActiveLink(LLrec *linkmap, int forwitemnr) +{ + if((forwitemnr <= 0) || (forwitemnr > linkmap->size+1)) + return( -1 ); + else { + if(forwitemnr > linkmap->lastitem) + return( linkmap->lastitem); + if(forwitemnr > linkmap->firstitem) { + forwitemnr += linkmap->size; + while((forwitemnr < linkmap->size + linkmap->lastitem) && (linkmap->map[forwitemnr] == 0)) + forwitemnr++; + } + else + forwitemnr += linkmap->size; + return(linkmap->map[forwitemnr]); + } +} + +STATIC int firstInactiveLink(LLrec *linkmap) +{ + int i, n; + + if(countInactiveLink(linkmap) == 0) + return( 0 ); + n = 1; + i = firstActiveLink(linkmap); + while(i == n) { + n++; + i = nextActiveLink(linkmap, i); + } + return( n ); +} + +STATIC int lastInactiveLink(LLrec *linkmap) +{ + int i, n; + + if(countInactiveLink(linkmap) == 0) + return( 0 ); + n = linkmap->size; + i = lastActiveLink(linkmap); + while(i == n) { + n--; + i = prevActiveLink(linkmap, i); + } + return( n ); +} + +STATIC int nextInactiveLink(LLrec *linkmap, int backitemnr) +{ + do { + backitemnr++; + } while((backitemnr <= linkmap->size) && isActiveLink(linkmap, backitemnr)); + if(backitemnr <= linkmap->size) + return( backitemnr ); + else + return( 0 ); +} + +STATIC int prevInactiveLink(LLrec *linkmap, int forwitemnr) +{ + return( 0 ); +} + +STATIC int removeLink(LLrec *linkmap, int itemnr) +{ + int size, prevnr, nextnr = -1; + + size = linkmap->size; + if((itemnr <= 0) || (itemnr > size)) + return( nextnr ); +#ifdef Paranoia + if(!isActiveLink(linkmap, itemnr)) + return( nextnr ); +#endif + + /* Get link data at the specified position */ + nextnr = linkmap->map[itemnr]; + prevnr = linkmap->map[size+itemnr]; + if(itemnr == linkmap->firstitem) + linkmap->firstitem = nextnr; + if(itemnr == linkmap->lastitem) + linkmap->lastitem = prevnr; + + /* Update forward link */ + linkmap->map[prevnr] = linkmap->map[itemnr]; + linkmap->map[itemnr] = 0; + + /* Update backward link */ + if(nextnr == 0) + linkmap->map[2*size+1] = prevnr; + else + linkmap->map[size+nextnr] = linkmap->map[size+itemnr]; + linkmap->map[size+itemnr] = 0; + + /* Decrement the count */ + linkmap->count--; + + /* Return the next active item */ + return( nextnr ); +} + +STATIC LLrec *cloneLink(LLrec *sourcemap, int newsize, MYBOOL freesource) +{ + LLrec *testmap = NULL; + + if((newsize == sourcemap->size) || (newsize <= 0)) { + createLink(sourcemap->size, &testmap, NULL); + MEMCOPY(testmap->map, sourcemap->map, 2*(sourcemap->size+1)); + testmap->firstitem = sourcemap->firstitem; + testmap->lastitem = sourcemap->lastitem; + testmap->size = sourcemap->size; + testmap->count = sourcemap->count; + } + else { + int j; + + createLink(newsize, &testmap, NULL); + for(j = firstActiveLink(sourcemap); (j != 0) && (j <= newsize); j = nextActiveLink(sourcemap, j)) + appendLink(testmap, j); + } + if(freesource) + freeLink(&sourcemap); + + return(testmap); +} + +STATIC int compareLink(LLrec *linkmap1, LLrec *linkmap2) +{ + int test; + + test = memcmp(&linkmap1->size, &linkmap2->size, sizeof(int)); + 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)); + + return( test ); +} + +STATIC MYBOOL verifyLink(LLrec *linkmap, int itemnr, MYBOOL doappend) +{ + LLrec *testmap; + + testmap = cloneLink(linkmap, -1, FALSE); + if(doappend) { + appendLink(testmap, itemnr); + removeLink(testmap, itemnr); + } + else { + int previtem = prevActiveLink(testmap, itemnr); + removeLink(testmap, itemnr); + insertLink(testmap, previtem, itemnr); + } + itemnr = compareLink(linkmap, testmap); + freeLink(&testmap); + return((MYBOOL) (itemnr == 0)); +} + +/* Packed vector routines */ +STATIC PVrec *createPackedVector(int size, LPSREAL *values, int *workvector) +{ + int i, k; + REGISTER LPSREAL ref; + PVrec *newPV = NULL; + MYBOOL localWV = (MYBOOL) (workvector == NULL); + + if(localWV) + workvector = (int *) malloc((size+1)*sizeof(*workvector)); + + /* Tally equal-valued vector entries - also check if it is worth compressing */ + k = 0; + workvector[k] = 1; + ref = values[1]; + for(i = 2; i <= size; i++) { + if(fabs(ref - values[i]) > DEF_EPSMACHINE) { + k++; + workvector[k] = i; + ref = values[i]; + } + } + if(k > size / 2) { + if(localWV) + FREE(workvector); + return( newPV ); + } + + /* Create the packing object, adjust the position vector and allocate value vector */ + newPV = (PVrec *) malloc(sizeof(*newPV)); + k++; /* Adjust from index to to count */ + newPV->count = k; + if(localWV) + newPV->startpos = (int *) realloc(workvector, (k + 1)*sizeof(*(newPV->startpos))); + else { + newPV->startpos = (int *) malloc((k + 1)*sizeof(*(newPV->startpos))); + 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))); + + /* Fill the values vector before returning */ + for(i = 0; i < k; i++) + newPV->value[i] = values[newPV->startpos[i]]; + + return( newPV ); +} + +STATIC MYBOOL unpackPackedVector(PVrec *PV, LPSREAL **target) +{ + int i, ii, k; + REGISTER LPSREAL ref; + + /* Test for validity of the target and create it if necessary */ + if(target == NULL) + return( FALSE ); + if(*target == NULL) + allocREAL(NULL, target, PV->startpos[PV->count], FALSE); + + /* Expand the packed vector into the target */ + i = PV->startpos[0]; + for(k = 0; k < PV->count; k++) { + ii = PV->startpos[k+1]; + ref = PV->value[k]; + while (i < ii) { + (*target)[i] = ref; + i++; + } + } + return( TRUE ); +} + +STATIC LPSREAL getvaluePackedVector(PVrec *PV, int index) +{ + index = searchFor(index, PV->startpos, PV->count, 0, FALSE); + index = abs(index)-1; + if(index >= 0) + return( PV->value[index] ); + else + return( 0 ); +} + +STATIC MYBOOL freePackedVector(PVrec **PV) +{ + if((PV == NULL) || (*PV == NULL)) + return( FALSE ); + + FREE((*PV)->value); + FREE((*PV)->startpos); + FREE(*PV); + return( TRUE ); +} + +STATIC void pushPackedVector(PVrec *PV, PVrec *parent) +{ + PV->parent = parent; +} + +STATIC PVrec *popPackedVector(PVrec *PV) +{ + PVrec *parent = PV->parent; + freePackedVector(&PV); + return( parent ); +} + diff --git a/src/external/lpsolve/build/lp_solve/lp_wlp.c b/src/external/lpsolve/build/lp_solve/lp_wlp.c new file mode 100644 index 00000000..30e2a161 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lp_wlp.c @@ -0,0 +1,370 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#include +#include +#include + +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_scale.h" +#include "lp_utils.h" +#include "lp_report.h" +#include "lp_wlp.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +/* Define buffer-size controled function mapping */ +# if defined _MSC_VER +# define vsnprintf _vsnprintf +# endif + +/* ------------------------------------------------------------------------- */ +/* Input and output of lp format model files for lp_solve */ +/* ------------------------------------------------------------------------- */ + +static int 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); + 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) +{ + 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) +{ + 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)) + 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; + } + } + } + return(elements); +} + +#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; + MYBOOL ok; + LPSREAL a, *val; + char *ptr; + + if(!mat_validate(lp->matA)) { + report(lp, IMPORTANT, "LP_writefile: Could not validate the data matrix.\n"); + return(FALSE); + } + + /* Write name of model */ + ptr = get_lp_name(lp); + if(ptr != NULL) { + if(*ptr) + write_lpcomment(userhandle, write_modeldata, ptr, FALSE); + else + ptr = NULL; + } + + /* Write the objective function */ + write_lpcomment(userhandle, write_modeldata, "Objective function", (MYBOOL) (ptr != NULL)); + if(is_maxim(lp)) + write_data(userhandle, write_modeldata, "max: "); + 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); + a = get_rh(lp, 0); + if(a != 0) + write_data(userhandle, write_modeldata, " %+.12g", a); + write_data(userhandle, write_modeldata, ";\n"); + + /* Write constraints */ + if(nrows > 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)) + ptr = get_row_name(lp, j); + else + ptr = NULL; + if((ptr != NULL) && (*ptr)) + write_data(userhandle, write_modeldata, "%s: ", ptr); + +#ifndef SingleBoundedRowInLP + /* Write the ranged part of the constraint, if specified */ + if ((lp->orig_upbo[j]) && (lp->orig_upbo[j] < lp->infinite)) { + if(my_chsign(is_chsign(lp, j), lp->orig_rhs[j]) == -lp->infinite) + write_data(userhandle, write_modeldata, "-Inf %s ", (is_chsign(lp, j)) ? ">=" : "<="); + else if(my_chsign(is_chsign(lp, j), lp->orig_rhs[j]) == lp->infinite) + write_data(userhandle, write_modeldata, "+Inf %s ", (is_chsign(lp, j)) ? ">=" : "<="); + else + write_data(userhandle, write_modeldata, "%+.12g %s ", + (lp->orig_upbo[j]-lp->orig_rhs[j]) * (is_chsign(lp, j) ? 1.0 : -1.0) / (lp->scaling_used ? lp->scalars[j] : 1.0), + (is_chsign(lp, j)) ? ">=" : "<="); + } +#endif + + if((!write_lprow(lp, j, userhandle, write_modeldata, maxlen, idx, val)) && (ncols >= 1)) + write_data(userhandle, write_modeldata, "0 %s", get_col_name(lp, 1)); + + if(lp->orig_upbo[j] == 0) + write_data(userhandle, write_modeldata, " ="); + else if(is_chsign(lp, j)) + write_data(userhandle, write_modeldata, " >="); + else + write_data(userhandle, write_modeldata, " <="); + if(fabs(get_rh(lp, j) + lp->infinite) < 1) + write_data(userhandle, write_modeldata, " -Inf;\n"); + else if(fabs(get_rh(lp, j) - lp->infinite) < 1) + write_data(userhandle, write_modeldata, " +Inf;\n"); + else + write_data(userhandle, write_modeldata, " %.12g;\n", get_rh(lp, j)); + +#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)) + 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)) + write_data(userhandle, write_modeldata, "0 %s", get_col_name(lp, 1)); + write_data(userhandle, write_modeldata, " %s %g;\n", + (is_chsign(lp, j)) ? "<=" : ">=", + (lp->orig_upbo[j]-lp->orig_rhs[j]) * (is_chsign(lp, j) ? 1.0 : -1.0) / (lp->scaling_used ? lp->scalars[j] : 1.0)); + } +#endif + } + + /* Write bounds on variables */ + ok = FALSE; + for(i = nrows + 1; i <= lp->sum; i++) + if(!is_splitvar(lp, i - nrows)) { + 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)); + } + 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; + } + 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)); + 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, ";\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_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) { + 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 optional integer section */ + if(lp->int_vars > 0) { + write_lpcomment(userhandle, write_modeldata, "Integer definitions", TRUE); + i = 1; + while((i <= ncols) && !is_int(lp, i)) + i++; + if(i <= ncols) { + nchars = 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; + } + write_data(userhandle, write_modeldata, ",%s", get_col_name(lp, i)); + } + write_data(userhandle, write_modeldata, ";\n"); + } + } + + /* Write optional SEC section */ + if(lp->sc_vars > 0) { + write_lpcomment(userhandle, write_modeldata, "Semi-continuous variables", TRUE); + i = 1; + while((i <= ncols) && !is_semicont(lp, i)) + i++; + if(i <= ncols) { + nchars = 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)); + } + write_data(userhandle, write_modeldata, ";\n"); + } + } + + /* Write optional SOS section */ + 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: ", + (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; + } + if(SOS->sos_list[i]->weights[j] == ++a) + nchars += 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", + (j > 1) ? "," : "", + get_col_name(lp, SOS->sos_list[i]->members[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); + else + nchars += 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); +} + +static int __WINAPI write_lpdata(void *userhandle, char *buf) +{ + return(fprintf((FILE *) userhandle, "%s", buf)); +} + +MYBOOL LP_writefile(lprec *lp, char *filename) +{ + FILE *output = stdout; + MYBOOL ok; + + if (filename != NULL) { + ok = (MYBOOL) ((output = fopen(filename, "w")) != NULL); + if(!ok) + return(ok); + } + else + output = lp->outstream; + + ok = write_lpex(lp, (void *) output, write_lpdata); + + if (filename != NULL) + fclose(output); + + return(ok); +} + +MYBOOL LP_writehandle(lprec *lp, FILE *output) +{ + MYBOOL ok; + + if (output != NULL) + set_outputstream(lp, output); + + output = lp->outstream; + + ok = write_lpex(lp, (void *) output, write_lpdata); + + return(ok); +} diff --git a/src/external/lpsolve/build/lp_solve/lusol.c b/src/external/lpsolve/build/lp_solve/lusol.c new file mode 100644 index 00000000..5573fe82 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lusol.c @@ -0,0 +1,808 @@ + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + LUSOL routines from the Stanford Optimization Laboratory + The parts included are: + lusol1 Factor a given matrix A from scratch (lu1fac). + lusol2 Heap-management routines for lu1fac. + lusol6 Solve with the current LU factors. + lusol7 Utilities for all update routines. + lusol8 Replace a column (Bartels-Golub update). + ------------------------------------------------------------------ + 26 Apr 2002: TCP implemented using heap data structure. + 01 May 2002: lu1DCP implemented. + 07 May 2002: lu1mxc must put 0.0 at top of empty columns. + 09 May 2002: lu1mCP implements Markowitz with cols searched + in heap order. + Often faster (searching 20 or 40 cols) but more dense. + 11 Jun 2002: TRP implemented. + lu1mRP implements Markowitz with Threshold Rook + Pivoting. + lu1mxc maintains max col elements (was lu1max.) + lu1mxr maintains max row elements. + 12 Jun 2002: lu1mCP seems too slow on big problems (e.g. memplus). + Disabled it for the moment. (Use lu1mar + TCP.) + 14 Dec 2002: TSP implemented. + lu1mSP implements Markowitz with TSP. + 07 Mar 2003: character*1, character*2 changed to f90 form. + Comments changed from * in column to ! in column 1. + Comments kept within column 72 to avoid compiler + warning. + 06 Mar 2004: Translation to C by Kjell Eikland with the addition + of data wrappers, parametric constants, various + helper routines, and dynamic memory reallocation. + 26 May 2004: Added LUSOL_IP_UPDATELIMIT parameter and provided + for dynamic memory expansion based on possible + forward requirements. + 08 Jul 2004: Revised logic in lu6chk based on new code from + Michael Saunders. + 01 Dec 2005: Add support for CMEX interface (disable by undef MATLAB) + Also include various bug fixes (disable by undef YZHANG) + Yin Zhang + 01 Jan 2006: Added storage of singular indeces, not only the last. + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ + +#include +#include +#include +#include +#include +#include +#include "lusol.h" +#include "myblas.h" +#ifdef MATLAB + #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; + oldptr = LUSOL_REALLOC(oldptr, newsize); + if(newsize > oldsize) +/* MEMCLEAR(oldptr+oldsize, newsize-oldsize); */ + memset((char *)oldptr+oldsize, '\0', newsize-oldsize); + return(oldptr); +} + +MYBOOL LUSOL_realloc_a(LUSOLrec *LUSOL, int newsize) +{ + int oldsize; + + if(newsize < 0) + newsize = LUSOL->lena + MAX(abs(newsize), LUSOL_MINDELTA_a); + + oldsize = LUSOL->lena; + LUSOL->lena = newsize; + if(newsize > 0) + newsize++; + if(oldsize > 0) + oldsize++; + + LUSOL->a = (LPSREAL *) clean_realloc(LUSOL->a, sizeof(*(LUSOL->a)), + newsize, oldsize); + LUSOL->indc = (int *) clean_realloc(LUSOL->indc, sizeof(*(LUSOL->indc)), + newsize, oldsize); + LUSOL->indr = (int *) clean_realloc(LUSOL->indr, sizeof(*(LUSOL->indr)), + newsize, oldsize); + if((newsize == 0) || + ((LUSOL->a != NULL) && (LUSOL->indc != NULL) && (LUSOL->indr != NULL))) + return( TRUE ); + else + return( FALSE ); +} + +MYBOOL LUSOL_expand_a(LUSOLrec *LUSOL, int *delta_lena, int *right_shift) +{ +#ifdef StaticMemAlloc + return( FALSE ); +#else + int LENA, NFREE, LFREE; + + /* Add expansion factor to avoid having to resize too often/too much; + (exponential formula suggested by Michael A. Saunders) */ + LENA = LUSOL->lena; + *delta_lena = DELTA_SIZE(*delta_lena, LENA); + + /* Expand it! */ + if((*delta_lena <= 0) || !LUSOL_realloc_a(LUSOL, LENA+(*delta_lena))) + return( FALSE ); + + /* Make sure we return the actual memory increase of a */ + *delta_lena = LUSOL->lena-LENA; + + /* Shift the used memory area to the right */ + LFREE = *right_shift; + NFREE = LFREE+*delta_lena; + LENA -= LFREE-1; + MEMMOVE(LUSOL->a+NFREE, LUSOL->a+LFREE, LENA); + MEMMOVE(LUSOL->indr+NFREE, LUSOL->indr+LFREE, LENA); + MEMMOVE(LUSOL->indc+NFREE, LUSOL->indc+LFREE, LENA); + + /* Also return the new starting position for the used memory area of a */ + *right_shift = NFREE; + + LUSOL->expanded_a++; + return( TRUE ); +#endif +} + +MYBOOL LUSOL_realloc_r(LUSOLrec *LUSOL, int newsize) +{ + int oldsize; + + if(newsize < 0) + newsize = LUSOL->maxm + MAX(abs(newsize), LUSOL_MINDELTA_rc); + + oldsize = LUSOL->maxm; + LUSOL->maxm = newsize; + if(newsize > 0) + newsize++; + if(oldsize > 0) + oldsize++; + + LUSOL->lenr = (int *) clean_realloc(LUSOL->lenr, sizeof(*(LUSOL->lenr)), + newsize, oldsize); + LUSOL->ip = (int *) clean_realloc(LUSOL->ip, sizeof(*(LUSOL->ip)), + newsize, oldsize); + LUSOL->iqloc = (int *) clean_realloc(LUSOL->iqloc, sizeof(*(LUSOL->iqloc)), + newsize, oldsize); + LUSOL->ipinv = (int *) clean_realloc(LUSOL->ipinv, sizeof(*(LUSOL->ipinv)), + newsize, oldsize); + LUSOL->locr = (int *) clean_realloc(LUSOL->locr, sizeof(*(LUSOL->locr)), + newsize, oldsize); + + if((newsize == 0) || + ((LUSOL->lenr != NULL) && + (LUSOL->ip != NULL) && (LUSOL->iqloc != NULL) && + (LUSOL->ipinv != NULL) && (LUSOL->locr != NULL))) { + +#ifndef ClassicHamaxR +#ifdef AlwaysSeparateHamaxR + if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TRP) +#endif + { + LUSOL->amaxr = (LPSREAL *) clean_realloc(LUSOL->amaxr, sizeof(*(LUSOL->amaxr)), + newsize, oldsize); + if((newsize > 0) && (LUSOL->amaxr == NULL)) + return( FALSE ); + } +#endif + return( TRUE ); + } + else + return( FALSE ); +} + +MYBOOL LUSOL_realloc_c(LUSOLrec *LUSOL, int newsize) +{ + int oldsize; + + if(newsize < 0) + newsize = LUSOL->maxn + MAX(abs(newsize), LUSOL_MINDELTA_rc); + + oldsize = LUSOL->maxn; + LUSOL->maxn = newsize; + if(newsize > 0) + newsize++; + if(oldsize > 0) + oldsize++; + + LUSOL->lenc = (int *) clean_realloc(LUSOL->lenc, sizeof(*(LUSOL->lenc)), + newsize, oldsize); + LUSOL->iq = (int *) clean_realloc(LUSOL->iq, sizeof(*(LUSOL->iq)), + newsize, oldsize); + LUSOL->iploc = (int *) clean_realloc(LUSOL->iploc, sizeof(*(LUSOL->iploc)), + newsize, oldsize); + LUSOL->iqinv = (int *) clean_realloc(LUSOL->iqinv, sizeof(*(LUSOL->iqinv)), + newsize, oldsize); + LUSOL->locc = (int *) clean_realloc(LUSOL->locc, sizeof(*(LUSOL->locc)), + newsize, oldsize); + LUSOL->w = (LPSREAL *) clean_realloc(LUSOL->w, sizeof(*(LUSOL->w)), + newsize, oldsize); +#ifdef LUSOLSafeFastUpdate + LUSOL->vLU6L = (LPSREAL *) clean_realloc(LUSOL->vLU6L, sizeof(*(LUSOL->vLU6L)), + newsize, oldsize); +#else + LUSOL->vLU6L = LUSOL->w; +#endif + + if((newsize == 0) || + ((LUSOL->w != NULL) && (LUSOL->lenc != NULL) && + (LUSOL->iq != NULL) && (LUSOL->iploc != NULL) && + (LUSOL->iqinv != NULL) && (LUSOL->locc != NULL))) { + +#ifndef ClassicHamaxR + if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TCP) { + LUSOL->Ha = (LPSREAL *) clean_realloc(LUSOL->Ha, sizeof(*(LUSOL->Ha)), + newsize, oldsize); + LUSOL->Hj = (int *) clean_realloc(LUSOL->Hj, sizeof(*(LUSOL->Hj)), + newsize, oldsize); + LUSOL->Hk = (int *) clean_realloc(LUSOL->Hk, sizeof(*(LUSOL->Hk)), + newsize, oldsize); + if((newsize > 0) && + ((LUSOL->Ha == NULL) || (LUSOL->Hj == NULL) || (LUSOL->Hk == NULL))) + return( FALSE ); + } +#endif +#ifndef ClassicdiagU + if(LUSOL->luparm[LUSOL_IP_KEEPLU] == FALSE) { + LUSOL->diagU = (LPSREAL *) clean_realloc(LUSOL->diagU, sizeof(*(LUSOL->diagU)), + newsize, oldsize); + if((newsize > 0) && (LUSOL->diagU == NULL)) + return( FALSE ); + } +#endif + + return( TRUE ); + } + else + return( FALSE ); +} + +LUSOLrec *LUSOL_create(FILE *outstream, int msgfil, int pivotmodel, int updatelimit) +{ + LUSOLrec *newLU; + + newLU = (LUSOLrec *) LUSOL_CALLOC(1, sizeof(*newLU)); + if(newLU == NULL) + return( newLU ); + + newLU->luparm[LUSOL_IP_SCALAR_NZA] = LUSOL_MULT_nz_a; + newLU->outstream = outstream; + newLU->luparm[LUSOL_IP_PRINTUNIT] = msgfil; + newLU->luparm[LUSOL_IP_PRINTLEVEL] = LUSOL_MSG_SINGULARITY; + + LUSOL_setpivotmodel(newLU, pivotmodel, LUSOL_PIVTOL_DEFAULT); + + newLU->parmlu[LUSOL_RP_GAMMA] = LUSOL_DEFAULT_GAMMA; + + newLU->parmlu[LUSOL_RP_ZEROTOLERANCE] = 3.0e-13; + + newLU->parmlu[LUSOL_RP_SMALLDIAG_U] = /*3.7e-11;*/ + newLU->parmlu[LUSOL_RP_EPSDIAG_U] = 3.7e-11; + + newLU->parmlu[LUSOL_RP_COMPSPACE_U] = 3.0e+0; + + newLU->luparm[LUSOL_IP_MARKOWITZ_MAXCOL] = 5; + newLU->parmlu[LUSOL_RP_MARKOWITZ_CONLY] = 0.3e+0; + newLU->parmlu[LUSOL_RP_MARKOWITZ_DENSE] = 0.5e+0; + + newLU->parmlu[LUSOL_RP_SMARTRATIO] = LUSOL_DEFAULT_SMARTRATIO; +#ifdef ForceRowBasedL0 + newLU->luparm[LUSOL_IP_ACCELERATION] = LUSOL_BASEORDER; +#endif + newLU->luparm[LUSOL_IP_KEEPLU] = TRUE; + newLU->luparm[LUSOL_IP_UPDATELIMIT] = updatelimit; + + init_BLAS(); + + return( newLU ); +} + +MYBOOL LUSOL_sizeto(LUSOLrec *LUSOL, int init_r, int init_c, int init_a) +{ + if(init_c == 0) + LUSOL_FREE(LUSOL->isingular); + if(LUSOL_realloc_a(LUSOL, init_a) && + LUSOL_realloc_r(LUSOL, init_r) && + LUSOL_realloc_c(LUSOL, init_c)) + return( TRUE ); + else + return( FALSE ); +} + +char *LUSOL_pivotLabel(LUSOLrec *LUSOL) +{ + static /*const*/ char *pivotText[LUSOL_PIVMOD_MAX+1] = + {"TPP", "TRP", "TCP", "TSP"}; + return(pivotText[LUSOL->luparm[LUSOL_IP_PIVOTTYPE]]); +} + +void LUSOL_setpivotmodel(LUSOLrec *LUSOL, int pivotmodel, int initlevel) +{ + LPSREAL newFM, newUM; + + /* Set pivotmodel if specified */ + if(pivotmodel > LUSOL_PIVMOD_NOCHANGE) { + if((pivotmodel <= LUSOL_PIVMOD_DEFAULT) || (pivotmodel > LUSOL_PIVMOD_MAX)) + pivotmodel = LUSOL_PIVMOD_TPP; + LUSOL->luparm[LUSOL_IP_PIVOTTYPE] = pivotmodel; + } + + /* Check if we need bother about changing tolerances */ + if((initlevel <= LUSOL_PIVTOL_NOCHANGE) || (initlevel > LUSOL_PIVTOL_MAX)) + return; + + /* Set default pivot tolerances + (note that UPDATEMAX should always be <= FACTORMAX) */ + if(initlevel == LUSOL_PIVTOL_BAGGY) { /* Extra-loose pivot thresholds */ + newFM = 500.0; + newUM = newFM / 20; + } + else if(initlevel == LUSOL_PIVTOL_LOOSE) { /* Moderately tight pivot tolerances */ + newFM = 100.0; + newUM = newFM / 10; + } + else if(initlevel == LUSOL_PIVTOL_NORMAL) { /* Standard pivot tolerances */ + newFM = 28.0; + newUM = newFM / 4; + } + else if(initlevel == LUSOL_PIVTOL_SLIM) { /* Better accuracy pivot tolerances */ + newFM = 10.0; + newUM = newFM / 2; + } + else if(initlevel == LUSOL_PIVTOL_TIGHT) { /* Enhanced accuracy pivot tolerances */ + newFM = 5.0; + newUM = newFM / 2; + } + else if(initlevel == LUSOL_PIVTOL_SUPER) { /* Very tight pivot tolerances for extra accuracy */ + newFM = 2.5; + newUM = 1.99; + } + else { /* Extremely tight pivot tolerances for extra accuracy */ + newFM = 1.99; + newUM = newFM / 1.49; + } + + /* Set the tolerances */ + LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij] = newFM; + LUSOL->parmlu[LUSOL_RP_UPDATEMAX_Lij] = newUM; +} + +MYBOOL LUSOL_tightenpivot(LUSOLrec *LUSOL) +{ +#if 0 + LPSREAL newvalue; +#endif + + /* Give up tightening if we are already less than limit and we cannot change strategy */ + if(MIN(LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij], + LUSOL->parmlu[LUSOL_RP_UPDATEMAX_Lij]) < 1.1) { + if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] >= LUSOL_PIVMOD_TRP) + return( FALSE ); + LUSOL_setpivotmodel(LUSOL, LUSOL->luparm[LUSOL_IP_PIVOTTYPE]+1, LUSOL_PIVTOL_DEFAULT+1); + return( 2 ); + } + + /* Otherwise tighten according to defined schedule */ +#if 0 /* This is Michael Saunder's proposed tightening procedure */ + newvalue = sqrt(LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij]); + LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij] = newvalue; + SETMIN(LUSOL->parmlu[LUSOL_RP_UPDATEMAX_Lij], newvalue); +#elif 0 /* This is Kjell Eikland's schedule #1 */ + newvalue = sqrt(LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij]); + LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij] = newvalue; + LUSOL->parmlu[LUSOL_RP_UPDATEMAX_Lij] = 1.0 + (newvalue - 1.0) / 2; +#else /* This was Kjell Eikland's schedule #2 */ + LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij] = 1.0 + LUSOL->parmlu[LUSOL_RP_FACTORMAX_Lij]/3.0; + LUSOL->parmlu[LUSOL_RP_UPDATEMAX_Lij] = 1.0 + LUSOL->parmlu[LUSOL_RP_UPDATEMAX_Lij]/3.0; +#endif + return( TRUE ); +} + +MYBOOL LUSOL_addSingularity(LUSOLrec *LUSOL, int singcol, int *inform) +{ + int NSING = LUSOL->luparm[LUSOL_IP_SINGULARITIES], + ASING = LUSOL->luparm[LUSOL_IP_SINGULARLISTSIZE]; + + /* Check if we need to allocated list memory to store multiple singularities */ + if((NSING > 0) && (NSING >= ASING)) { + + /* Increase list in "reasonable" steps */ + ASING += (int) (10.0 * (log10((LPSREAL) 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; + *inform = LUSOL_INFORM_NOMEMLEFT; + return( FALSE ); + } + LUSOL->luparm[LUSOL_IP_SINGULARLISTSIZE] = ASING; + + /* Transfer the first singularity if the list was just created */ + if(NSING == 1) + LUSOL->isingular[NSING] = LUSOL->luparm[LUSOL_IP_SINGULARINDEX]; + } + + /* Update singularity count and store its index */ + NSING++; + if(NSING > 1) { + LUSOL->isingular[0] = NSING; + LUSOL->isingular[NSING] = singcol; + } + LUSOL->luparm[LUSOL_IP_SINGULARITIES] = NSING; + + /* Mimic old logic by keeping the last singularity stored */ + LUSOL->luparm[LUSOL_IP_SINGULARINDEX] = singcol; + + return( TRUE ); +} + +int LUSOL_getSingularity(LUSOLrec *LUSOL, int singitem) +{ + if((singitem > LUSOL->luparm[LUSOL_IP_SINGULARITIES]) || (singitem < 0)) + singitem = -1; + else if(singitem == 0) + singitem = LUSOL->luparm[LUSOL_IP_SINGULARITIES]; + else if(singitem > 1) + singitem = LUSOL->isingular[singitem]; + else + singitem = LUSOL->luparm[LUSOL_IP_SINGULARINDEX]; + return( singitem ); +} + +int LUSOL_findSingularityPosition(LUSOLrec *LUSOL, int singcol) +/* The purpose of this routine is to find the slack row/column in + user-index that was singular in the last unsuccessful column + update; zero is returned if the search was unsuccessful. + By adding a slack at this position this particular singularity + should disappear. + (Source: Michael A. Saunders; private communication to KE) */ +{ +#if 0 /* Michael Saunders version */ + int j; + for(j = LUSOL->m; j > 0; j--) + if(LUSOL->iq[j] == singcol) + break; + singcol = j; +#else /* Kjell Eikland version (note that iqinv could be invalid in early versions of LUSOL) */ + singcol = LUSOL->iqinv[singcol]; +#endif + return( LUSOL->ip[singcol] ); +} + +char *LUSOL_informstr(LUSOLrec *LUSOL, int inform) +{ + static char *informText[LUSOL_INFORM_MAX-LUSOL_INFORM_MIN+1] = + {"LUSOL_RANKLOSS: Lost rank", + "LUSOL_LUSUCCESS: Success", + "LUSOL_LUSINGULAR: Singular A", + "LUSOL_LUUNSTABLE: Unstable factorization", + "LUSOL_ADIMERR: Row or column count exceeded", + "LUSOL_ADUPLICATE: Duplicate A matrix entry found", + "(Undefined message)", + "(Undefined message)", + "LUSOL_ANEEDMEM: Insufficient memory for factorization", + "LUSOL_FATALERR: Fatal internal error", + "LUSOL_NOPIVOT: Found no suitable pivot", + "LUSOL_NOMEMLEFT: Could not obtain more memory"}; + if((inform < LUSOL_INFORM_MIN) || (inform > LUSOL_INFORM_MAX)) + inform = LUSOL->luparm[LUSOL_IP_INFORM]; + return(informText[inform-LUSOL_INFORM_MIN]); +} + +void LUSOL_clear(LUSOLrec *LUSOL, MYBOOL nzonly) +{ + int len; + + LUSOL->nelem = 0; + if(!nzonly) { + + /* lena arrays */ + len = LUSOL->lena + LUSOL_ARRAYOFFSET; + MEMCLEAR(LUSOL->a, len); + MEMCLEAR(LUSOL->indc, len); + MEMCLEAR(LUSOL->indr, len); + + /* maxm arrays */ + len = LUSOL->maxm + LUSOL_ARRAYOFFSET; + MEMCLEAR(LUSOL->lenr, len); + MEMCLEAR(LUSOL->ip, len); + MEMCLEAR(LUSOL->iqloc, len); + MEMCLEAR(LUSOL->ipinv, len); + MEMCLEAR(LUSOL->locr, len); + +#ifndef ClassicHamaxR + if((LUSOL->amaxr != NULL) +#ifdef AlwaysSeparateHamaxR + && (LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TRP) +#endif + ) + MEMCLEAR(LUSOL->amaxr, len); +#endif + + /* maxn arrays */ + len = LUSOL->maxn + LUSOL_ARRAYOFFSET; + MEMCLEAR(LUSOL->lenc, len); + MEMCLEAR(LUSOL->iq, len); + MEMCLEAR(LUSOL->iploc, len); + MEMCLEAR(LUSOL->iqinv, len); + MEMCLEAR(LUSOL->locc, len); + MEMCLEAR(LUSOL->w, len); + + if(LUSOL->luparm[LUSOL_IP_PIVOTTYPE] == LUSOL_PIVMOD_TCP) { + MEMCLEAR(LUSOL->Ha, len); + MEMCLEAR(LUSOL->Hj, len); + MEMCLEAR(LUSOL->Hk, len); + } +#ifndef ClassicdiagU + if(LUSOL->luparm[LUSOL_IP_KEEPLU] == FALSE) { + MEMCLEAR(LUSOL->diagU, len); + } +#endif + + } +} + +MYBOOL LUSOL_assign(LUSOLrec *LUSOL, int iA[], int jA[], LPSREAL Aij[], int nzcount, MYBOOL istriplet) +{ + int k, m, n, ij, kol; + + /* Adjust the size of the a structure */ + if(nzcount > (LUSOL->lena/LUSOL->luparm[LUSOL_IP_SCALAR_NZA]) && + !LUSOL_realloc_a(LUSOL, nzcount*LUSOL->luparm[LUSOL_IP_SCALAR_NZA])) + return( FALSE ); + + m = 0; + n = 0; + kol = 1; + for(k = 1; k <= nzcount; k++) { + /* First the row indicator */ + ij = iA[k]; + if(ij > m) { + m = ij; + if(m > LUSOL->maxm && + !LUSOL_realloc_r(LUSOL, -(m / LUSOL_MINDELTA_FACTOR + 1))) + return( FALSE ); + } + LUSOL->indc[k] = ij; + + /* Then the column indicator; + Handle both triplet and column count formats */ + if(istriplet) + ij = jA[k]; + else { + if(k >= jA[kol]) + kol++; + ij = kol; + } + if(ij > n) { + n = ij; + if(n > LUSOL->maxn && + !LUSOL_realloc_c(LUSOL, -(n / LUSOL_MINDELTA_FACTOR + 1))) + return( FALSE ); + } + LUSOL->indr[k] = ij; + + /* Lastly the matrix value itself */ + LUSOL->a[k] = Aij[k]; + } + LUSOL->m = m; + LUSOL->n = n; + LUSOL->nelem = nzcount; + return( TRUE ); +} + +int LUSOL_loadColumn(LUSOLrec *LUSOL, int iA[], int jA, LPSREAL Aij[], int nzcount, int offset1) +{ + int i, ii, nz, k; + + nz = LUSOL->nelem; + i = nz + nzcount; + if(i > (LUSOL->lena/LUSOL->luparm[LUSOL_IP_SCALAR_NZA]) && + !LUSOL_realloc_a(LUSOL, i*LUSOL->luparm[LUSOL_IP_SCALAR_NZA])) + return( -1 ); + + k = 0; + for(ii = 1; ii <= nzcount; ii++) { + i = ii + offset1; + if(Aij[i] == 0) + continue; + if(iA[i] <= 0 || iA[i] > LUSOL->m || + jA <= 0 || jA > LUSOL->n) { + LUSOL_report(LUSOL, 0, "Variable index outside of set bounds (r:%d/%d, c:%d/%d)\n", + iA[i], LUSOL->m, jA, LUSOL->n); + continue; + } + k++; + nz++; + LUSOL->a[nz] = Aij[i]; + LUSOL->indc[nz] = iA[i]; + LUSOL->indr[nz] = jA; + } + LUSOL->nelem = nz; + return( k ); +} + +void LUSOL_free(LUSOLrec *LUSOL) +{ + LUSOL_realloc_a(LUSOL, 0); + LUSOL_realloc_r(LUSOL, 0); + LUSOL_realloc_c(LUSOL, 0); + if(LUSOL->L0 != NULL) + LUSOL_matfree(&(LUSOL->L0)); + if(LUSOL->U != NULL) + LUSOL_matfree(&(LUSOL->U)); + if(!is_nativeBLAS()) + unload_BLAS(); + LUSOL_FREE(LUSOL); +} + +void LUSOL_report(LUSOLrec *LUSOL, int msglevel, char *format, ...) +{ + va_list ap; + + if(LUSOL == NULL) { + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(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); + 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); + } + } +} + +void LUSOL_timer(LUSOLrec *LUSOL, int timerid, char *text) +{ + LUSOL_report(LUSOL, -1, "TimerID %d at %s - %s\n", + timerid, "", text); +} + +int LUSOL_factorize(LUSOLrec *LUSOL) +{ + int inform; + + LU1FAC( LUSOL, &inform ); + return( inform ); +} + +int LUSOL_ftran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[], MYBOOL prepareupdate) +{ + int inform; + LPSREAL *vector; + + if(prepareupdate) + vector = LUSOL->vLU6L; + else + vector = LUSOL->w; + + /* Copy RHS vector, but make adjustment for offset since this + 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; + + LU6SOL(LUSOL, LUSOL_SOLVE_Aw_v, vector, b, NZidx, &inform); + LUSOL->luparm[LUSOL_IP_FTRANCOUNT]++; + + return(inform); +} + + +int LUSOL_btran(LUSOLrec *LUSOL, LPSREAL b[], int NZidx[]) +{ + int inform; + + /* Copy RHS vector, but make adjustment for offset since this + 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; + + LU6SOL(LUSOL, LUSOL_SOLVE_Atv_w, b, LUSOL->w, NZidx, &inform); + LUSOL->luparm[LUSOL_IP_BTRANCOUNT]++; + + return(inform); +} + + +int LUSOL_replaceColumn(LUSOLrec *LUSOL, int jcol, LPSREAL v[]) +{ + int inform; + LPSREAL DIAG, VNORM; + + LU8RPC(LUSOL, LUSOL_UPDATE_OLDNONEMPTY, LUSOL_UPDATE_NEWNONEMPTY, + jcol, v, NULL, + &inform, &DIAG, &VNORM); + + LUSOL->replaced_c++; + return( inform ); +} + +LPSREAL LUSOL_vecdensity(LUSOLrec *LUSOL, LPSREAL V[]) +{ + int I, N = 0; + + for(I = 1; I <= LUSOL->m; I++) + if(fabs(V[I]) > 0) + N++; + return( (LPSREAL) N / (LPSREAL) LUSOL->m ); +} + +char relationChar(LPSREAL left, LPSREAL right) +{ + if(left > right) + return('>'); + else if(left == right) + return('='); + else + return('<'); +} + +/* 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 */ + + +void LUSOL_dump(FILE *output, LUSOLrec *LUSOL) +{ + MYBOOL userfile = (MYBOOL) (output != NULL); + + if(!userfile) + output = fopen("LUSOL.dbg", "w"); + + blockWriteREAL(output, "a", LUSOL->a, 1, LUSOL->lena); + blockWriteINT(output, "indc", LUSOL->indc, 1, LUSOL->lena); + blockWriteINT(output, "indr", LUSOL->indr, 1, LUSOL->lena); + + blockWriteINT(output, "ip", LUSOL->ip, 1, LUSOL->m); + blockWriteINT(output, "iq", LUSOL->iq, 1, LUSOL->n); + blockWriteINT(output, "lenc", LUSOL->lenc, 1, LUSOL->n); + blockWriteINT(output, "lenr", LUSOL->lenr, 1, LUSOL->m); + + blockWriteINT(output, "locc", LUSOL->locc, 1, LUSOL->n); + blockWriteINT(output, "locr", LUSOL->locr, 1, LUSOL->m); + blockWriteINT(output, "iploc", LUSOL->iploc, 1, LUSOL->n); + blockWriteINT(output, "iqloc", LUSOL->iqloc, 1, LUSOL->m); + + blockWriteINT(output, "ipinv", LUSOL->ipinv, 1, LUSOL->m); + blockWriteINT(output, "iqinv", LUSOL->iqinv, 1, LUSOL->n); + + if(!userfile) + fclose(output); +} + +LUSOLmat *LUSOL_matcreate(int dim, int nz) +{ + LUSOLmat *newm; + + newm = (LUSOLmat *) LUSOL_CALLOC(1, sizeof(*newm)); + if(newm != NULL) { + newm->a = (LPSREAL *) LUSOL_MALLOC((nz+1)*sizeof(LPSREAL)); + 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)); + newm->indc = (int *) LUSOL_MALLOC((nz+1)*sizeof(int)); + if((newm->a == NULL) || + (newm->lenx == NULL) || (newm->indx == NULL) || + (newm->indr == NULL) || (newm->indc == NULL)) + LUSOL_matfree(&newm); + } + return(newm); +} +void LUSOL_matfree(LUSOLmat **mat) +{ + if((mat == NULL) || (*mat == NULL)) + return; + LUSOL_FREE((*mat)->a); + LUSOL_FREE((*mat)->indc); + LUSOL_FREE((*mat)->indr); + LUSOL_FREE((*mat)->lenx); + LUSOL_FREE((*mat)->indx); + LUSOL_FREE(*mat); +} + diff --git a/src/external/lpsolve/build/lp_solve/lusol1.c b/src/external/lpsolve/build/lp_solve/lusol1.c new file mode 100644 index 00000000..90ef643e --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lusol1.c @@ -0,0 +1,3725 @@ + +/* ================================================================== + 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, TCP, TPP, TRP, TSP; + 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); + TRP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TRP); + TCP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TCP); + TSP = (MYBOOL) (LPIV==LUSOL_PIVMOD_TSP); +/* 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 + 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/external/lpsolve/build/lp_solve/lusol2.c b/src/external/lpsolve/build/lp_solve/lusol2.c new file mode 100644 index 00000000..49fb8a21 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lusol2.c @@ -0,0 +1,204 @@ + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 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/external/lpsolve/build/lp_solve/lusol6l0.c b/src/external/lpsolve/build/lp_solve/lusol6l0.c new file mode 100644 index 00000000..c7555e27 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lusol6l0.c @@ -0,0 +1,171 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +/* 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/external/lpsolve/build/lp_solve/lusol6u.c b/src/external/lpsolve/build/lp_solve/lusol6u.c new file mode 100644 index 00000000..5e1e3bf4 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lusol6u.c @@ -0,0 +1,184 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +/* 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/external/lpsolve/build/lp_solve/lusol7a.c b/src/external/lpsolve/build/lp_solve/lusol7a.c new file mode 100644 index 00000000..128362f8 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lusol7a.c @@ -0,0 +1,704 @@ + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 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/external/lpsolve/build/lp_solve/lusol8a.c b/src/external/lpsolve/build/lp_solve/lusol8a.c new file mode 100644 index 00000000..2bb8005c --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/lusol8a.c @@ -0,0 +1,279 @@ + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 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/external/lpsolve/build/lp_solve/mmio.c b/src/external/lpsolve/build/lp_solve/mmio.c new file mode 100644 index 00000000..185ef51b --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/mmio.c @@ -0,0 +1,496 @@ +/* +* Matrix Market I/O library for ANSI C +* +* See http://math.nist.gov/MatrixMarket for details. +* +* (Version 1.01, 5/2003) +*/ + + +#include +#include +#include +#include + +#include "mmio.h" + +int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, + double **val_, int **I_, int **J_) +{ + FILE *f; + MM_typecode matcode; + int M, N, 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); + 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", + 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"); + 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); + if (num_items_read == EOF) return MM_PREMATURE_EOF; + } while (num_items_read < 2); + + return 0; +} + + +int mm_read_mtx_array_size(FILE *f, int *M, int *N) +{ + char line[MM_MAX_LINE_LENGTH]; + 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 + { + 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); + if (num_items_read == EOF) return MM_PREMATURE_EOF; + } + while (num_items_read != 2); + + return 0; +} + +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 + return 0; +} + + + +/*-------------------------------------------------------------------------*/ + +/******************************************************************/ +/* use when I[], J[], and val[]J, and val[] are already allocated */ +/******************************************************************/ + +int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], + double val[], MM_typecode matcode) +{ + int i; + if (mm_is_complex(matcode)) + { + for (i=0; i +#include +/*#include */ +#include +#include +#include "myblas.h" +#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/external/lpsolve/build/lp_solve/yacc_read.c b/src/external/lpsolve/build/lp_solve/yacc_read.c new file mode 100644 index 00000000..e762edd3 --- /dev/null +++ b/src/external/lpsolve/build/lp_solve/yacc_read.c @@ -0,0 +1,1284 @@ +/* + ============================================================================ + 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; + + MALLOCCPY(orig_upbo, lp->orig_upbo, 1 + pp->Rows, LPSREAL); + 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/external/lpsolve/headers/include/colamd.h b/src/external/lpsolve/headers/include/colamd.h new file mode 100644 index 00000000..e8539922 --- /dev/null +++ b/src/external/lpsolve/headers/include/colamd.h @@ -0,0 +1,286 @@ +/* ========================================================================== */ +/* === 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/external/lpsolve/headers/include/commonlib.h b/src/external/lpsolve/headers/include/commonlib.h new file mode 100644 index 00000000..d5fe84be --- /dev/null +++ b/src/external/lpsolve/headers/include/commonlib.h @@ -0,0 +1,344 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + + +#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/external/lpsolve/headers/include/ini.h b/src/external/lpsolve/headers/include/ini.h new file mode 100644 index 00000000..e707ee01 --- /dev/null +++ b/src/external/lpsolve/headers/include/ini.h @@ -0,0 +1,26 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_BFP.h b/src/external/lpsolve/headers/include/lp_BFP.h new file mode 100644 index 00000000..0f36ec28 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_BFP.h @@ -0,0 +1,90 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +/* ---------------------------------------------------------------------------------- */ +/* 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/external/lpsolve/headers/include/lp_Hash.h b/src/external/lpsolve/headers/include/lp_Hash.h new file mode 100644 index 00000000..13befcb4 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_Hash.h @@ -0,0 +1,52 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_LUSOL.h b/src/external/lpsolve/headers/include/lp_LUSOL.h new file mode 100644 index 00000000..2babd203 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_LUSOL.h @@ -0,0 +1,72 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_MDO.h b/src/external/lpsolve/headers/include/lp_MDO.h new file mode 100644 index 00000000..60eb330a --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_MDO.h @@ -0,0 +1,27 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_MPS.h b/src/external/lpsolve/headers/include/lp_MPS.h new file mode 100644 index 00000000..9b7dab88 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_MPS.h @@ -0,0 +1,42 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_SOS.h b/src/external/lpsolve/headers/include/lp_SOS.h new file mode 100644 index 00000000..98bd4b02 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_SOS.h @@ -0,0 +1,117 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_bit.h b/src/external/lpsolve/headers/include/lp_bit.h new file mode 100644 index 00000000..ffcd6661 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_bit.h @@ -0,0 +1,31 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#include "lp_types.h" + +#if defined INLINE +# define MYINLINE INLINE +#else +# define MYINLINE static +#endif + +MYINLINE void set_biton(MYBOOL *bitarray, int item) +{ + bitarray[item / 8] |= (1 << (item % 8)); +} + +MYINLINE void set_bitoff(MYBOOL *bitarray, int item) +{ + bitarray[item / 8] &= ~(1 << (item % 8)); +} + +MYINLINE MYBOOL is_biton(MYBOOL *bitarray, int item) +{ + return( (MYBOOL) ((bitarray[item / 8] & (1 << (item % 8))) != 0) ); +} diff --git a/src/external/lpsolve/headers/include/lp_crash.h b/src/external/lpsolve/headers/include/lp_crash.h new file mode 100644 index 00000000..a8b1137b --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_crash.h @@ -0,0 +1,35 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_lib.h b/src/external/lpsolve/headers/include/lp_lib.h new file mode 100644 index 00000000..336525b3 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_lib.h @@ -0,0 +1,2302 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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 + +#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/external/lpsolve/headers/include/lp_matrix.h b/src/external/lpsolve/headers/include/lp_matrix.h new file mode 100644 index 00000000..6bae033b --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_matrix.h @@ -0,0 +1,266 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_mipbb.h b/src/external/lpsolve/headers/include/lp_mipbb.h new file mode 100644 index 00000000..4235a119 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_mipbb.h @@ -0,0 +1,73 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_presolve.h b/src/external/lpsolve/headers/include/lp_presolve.h new file mode 100644 index 00000000..348ad896 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_presolve.h @@ -0,0 +1,135 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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); + +INLINE int presolve_nextrow(presolverec *psdata, int colnr, int *previtem); +INLINE 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); + +INLINE int presolve_rowlength(presolverec *psdata, int rownr) +{ + int *items = psdata->rows->next[rownr]; + + if(items == NULL) + return( 0 ); + else + return( items[0] ); +} +INLINE int presolve_collength(presolverec *psdata, int colnr) +{ + int *items = psdata->cols->next[colnr]; + if(items == NULL) + return( 0 ); + else + return( items[0] ); +} + +STATIC int presolve(lprec *lp); +STATIC MYBOOL postsolve(lprec *lp, int status); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_presolve */ diff --git a/src/external/lpsolve/headers/include/lp_price.h b/src/external/lpsolve/headers/include/lp_price.h new file mode 100644 index 00000000..fddf2a1f --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_price.h @@ -0,0 +1,108 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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); +INLINE 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/external/lpsolve/headers/include/lp_pricePSE.h b/src/external/lpsolve/headers/include/lp_pricePSE.h new file mode 100644 index 00000000..422e1666 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_pricePSE.h @@ -0,0 +1,37 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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); +INLINE 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/external/lpsolve/headers/include/lp_report.h b/src/external/lpsolve/headers/include/lp_report.h new file mode 100644 index 00000000..82b7becd --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_report.h @@ -0,0 +1,51 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_rlp.h b/src/external/lpsolve/headers/include/lp_rlp.h new file mode 100644 index 00000000..058b4523 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_rlp.h @@ -0,0 +1,2460 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + + +#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 inline int lp_yyinput (lp_yyscan_t lp_yyscanner) +#else + static inline 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/external/lpsolve/headers/include/lp_scale.h b/src/external/lpsolve/headers/include/lp_scale.h new file mode 100644 index 00000000..7f9699de --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_scale.h @@ -0,0 +1,40 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_simplex.h b/src/external/lpsolve/headers/include/lp_simplex.h new file mode 100644 index 00000000..71faebbb --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_simplex.h @@ -0,0 +1,43 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_types.h b/src/external/lpsolve/headers/include/lp_types.h new file mode 100644 index 00000000..1f166a62 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_types.h @@ -0,0 +1,339 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_utils.h b/src/external/lpsolve/headers/include/lp_utils.h new file mode 100644 index 00000000..07ca11ac --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_utils.h @@ -0,0 +1,155 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/lp_wlp.h b/src/external/lpsolve/headers/include/lp_wlp.h new file mode 100644 index 00000000..7cfeecf4 --- /dev/null +++ b/src/external/lpsolve/headers/include/lp_wlp.h @@ -0,0 +1,30 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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 */ + diff --git a/src/external/lpsolve/headers/include/lpkit.h b/src/external/lpsolve/headers/include/lpkit.h new file mode 100644 index 00000000..728d2d01 --- /dev/null +++ b/src/external/lpsolve/headers/include/lpkit.h @@ -0,0 +1,41 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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 + +#define MALLOCCPY(nptr, optr, nr, type)\ + (MALLOC(nptr, nr, type), (nptr != NULL) ? memcpy(nptr, optr, (size_t)((nr) * sizeof(*optr))) : 0) diff --git a/src/external/lpsolve/headers/include/lusol.h b/src/external/lpsolve/headers/include/lusol.h new file mode 100644 index 00000000..56e9b9e9 --- /dev/null +++ b/src/external/lpsolve/headers/include/lusol.h @@ -0,0 +1,366 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/include/mmio.h b/src/external/lpsolve/headers/include/mmio.h new file mode 100644 index 00000000..ea19c8b6 --- /dev/null +++ b/src/external/lpsolve/headers/include/mmio.h @@ -0,0 +1,142 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + + /* +* 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); + +int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, + double **val_, int **I_, int **J_); + + + +#endif diff --git a/src/external/lpsolve/headers/include/myblas.h b/src/external/lpsolve/headers/include/myblas.h new file mode 100644 index 00000000..4408e7be --- /dev/null +++ b/src/external/lpsolve/headers/include/myblas.h @@ -0,0 +1,50 @@ +#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/external/lpsolve/headers/include/yacc_read.h b/src/external/lpsolve/headers/include/yacc_read.h new file mode 100644 index 00000000..d4f0b411 --- /dev/null +++ b/src/external/lpsolve/headers/include/yacc_read.h @@ -0,0 +1,66 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + + /* 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/external/lpsolve/headers/run_headers/lp_Hash.h b/src/external/lpsolve/headers/run_headers/lp_Hash.h new file mode 100644 index 00000000..13befcb4 --- /dev/null +++ b/src/external/lpsolve/headers/run_headers/lp_Hash.h @@ -0,0 +1,52 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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/external/lpsolve/headers/run_headers/lp_SOS.h b/src/external/lpsolve/headers/run_headers/lp_SOS.h new file mode 100644 index 00000000..e1d0c96d --- /dev/null +++ b/src/external/lpsolve/headers/run_headers/lp_SOS.h @@ -0,0 +1,117 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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; + REAL *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, REAL *weights); +STATIC MYBOOL delete_SOSrec(SOSgroup *group, int sosindex); +STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, REAL *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, REAL *upbound, REAL *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, REAL *bound, REAL value, + MYBOOL isupper, int *diffcount, DeltaVrec *changelog); +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); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_SOS */ diff --git a/src/external/lpsolve/headers/run_headers/lp_lib.h b/src/external/lpsolve/headers/run_headers/lp_lib.h new file mode 100644 index 00000000..2ef654fe --- /dev/null +++ b/src/external/lpsolve/headers/run_headers/lp_lib.h @@ -0,0 +1,2294 @@ + +#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 + +#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 */ + REAL *fixed_rhs; /* rows_alloc+1 : Storage of values of presolved fixed colums */ + REAL *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; + REAL 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, REAL *column); +typedef MYBOOL (__WINAPI add_columnex_func)(lprec *lp, int count, REAL *column, int *rowno); +typedef MYBOOL (__WINAPI add_constraint_func)(lprec *lp, REAL *row, int constr_type, REAL rh); +typedef MYBOOL (__WINAPI add_constraintex_func)(lprec *lp, int count, REAL *row, int *colno, int constr_type, REAL rh); +typedef MYBOOL (__WINAPI add_lag_con_func)(lprec *lp, REAL *row, int con_type, REAL rhs); +typedef int (__WINAPI add_SOS_func)(lprec *lp, char *name, int sostype, int priority, int count, int *sosvars, REAL *weights); +typedef int (__WINAPI column_in_lp_func)(lprec *lp, REAL *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 REAL (__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, REAL *column); +typedef int (__WINAPI get_columnex_func)(lprec *lp, int colnr, REAL *column, int *nzrow); +typedef int (__WINAPI get_constr_type_func)(lprec *lp, int rownr); +typedef REAL (__WINAPI get_constr_value_func)(lprec *lp, int rownr, int count, REAL *primsolution, int *nzindex); +typedef MYBOOL (__WINAPI get_constraints_func)(lprec *lp, REAL *constr); +typedef MYBOOL (__WINAPI get_dual_solution_func)(lprec *lp, REAL *rc); +typedef REAL (__WINAPI get_epsb_func)(lprec *lp); +typedef REAL (__WINAPI get_epsd_func)(lprec *lp); +typedef REAL (__WINAPI get_epsel_func)(lprec *lp); +typedef REAL (__WINAPI get_epsint_func)(lprec *lp); +typedef REAL (__WINAPI get_epsperturb_func)(lprec *lp); +typedef REAL (__WINAPI get_epspivot_func)(lprec *lp); +typedef int (__WINAPI get_improve_func)(lprec *lp); +typedef REAL (__WINAPI get_infinite_func)(lprec *lp); +typedef MYBOOL (__WINAPI get_lambda_func)(lprec *lp, REAL *lambda); +typedef REAL (__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 REAL (__WINAPI get_mat_func)(lprec *lp, int rownr, int colnr); +typedef REAL (__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 REAL (__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 REAL (__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 REAL (__WINAPI get_obj_bound_func)(lprec *lp); +typedef REAL (__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, REAL *pv); +typedef int (__WINAPI get_print_sol_func)(lprec *lp); +typedef MYBOOL (__WINAPI get_pseudocosts_func)(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit); +typedef MYBOOL (__WINAPI get_ptr_constraints_func)(lprec *lp, REAL **constr); +typedef MYBOOL (__WINAPI get_ptr_dual_solution_func)(lprec *lp, REAL **rc); +typedef MYBOOL (__WINAPI get_ptr_lambda_func)(lprec *lp, REAL **lambda); +typedef MYBOOL (__WINAPI get_ptr_primal_solution_func)(lprec *lp, REAL **pv); +typedef MYBOOL (__WINAPI get_ptr_sensitivity_obj_func)(lprec *lp, REAL **objfrom, REAL **objtill); +typedef MYBOOL (__WINAPI get_ptr_sensitivity_objex_func)(lprec *lp, REAL **objfrom, REAL **objtill, REAL **objfromvalue, REAL **objtillvalue); +typedef MYBOOL (__WINAPI get_ptr_sensitivity_rhs_func)(lprec *lp, REAL **duals, REAL **dualsfrom, REAL **dualstill); +typedef MYBOOL (__WINAPI get_ptr_variables_func)(lprec *lp, REAL **var); +typedef REAL (__WINAPI get_rh_func)(lprec *lp, int rownr); +typedef REAL (__WINAPI get_rh_range_func)(lprec *lp, int rownr); +typedef int (__WINAPI get_rowex_func)(lprec *lp, int rownr, REAL *row, int *colno); +typedef MYBOOL (__WINAPI get_row_func)(lprec *lp, int rownr, REAL *row); +typedef char * (__WINAPI get_row_name_func)(lprec *lp, int rownr); +typedef REAL (__WINAPI get_scalelimit_func)(lprec *lp); +typedef int (__WINAPI get_scaling_func)(lprec *lp); +typedef MYBOOL (__WINAPI get_sensitivity_obj_func)(lprec *lp, REAL *objfrom, REAL *objtill); +typedef MYBOOL (__WINAPI get_sensitivity_objex_func)(lprec *lp, REAL *objfrom, REAL *objtill, REAL *objfromvalue, REAL *objtillvalue); +typedef MYBOOL (__WINAPI get_sensitivity_rhs_func)(lprec *lp, REAL *duals, REAL *dualsfrom, REAL *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 REAL (__WINAPI get_upbo_func)(lprec *lp, int colnr); +typedef int (__WINAPI get_var_branch_func)(lprec *lp, int colnr); +typedef REAL (__WINAPI get_var_dualresult_func)(lprec *lp, int index); +typedef REAL (__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, REAL *var); +typedef int (__WINAPI get_verbose_func)(lprec *lp); +typedef MYBOOL (__WINAPI guess_basis_func)(lprec *lp, REAL *guessvector, int *basisvector); +typedef REAL (__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, REAL *values, REAL threshold); +typedef MYBOOL (__WINAPI is_unbounded_func)(lprec *lp, int colnr); +typedef MYBOOL (__WINAPI is_infinite_func)(lprec *lp, REAL 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, REAL lower, REAL 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, REAL break_at_value); +typedef MYBOOL (__WINAPI set_column_func)(lprec *lp, int colnr, REAL *column); +typedef MYBOOL (__WINAPI set_columnex_func)(lprec *lp, int colnr, int count, REAL *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, REAL epsb); +typedef void (__WINAPI set_epsd_func)(lprec *lp, REAL epsd); +typedef void (__WINAPI set_epsel_func)(lprec *lp, REAL epsel); +typedef void (__WINAPI set_epsint_func)(lprec *lp, REAL epsint); +typedef MYBOOL (__WINAPI set_epslevel_func)(lprec *lp, int epslevel); +typedef void (__WINAPI set_epsperturb_func)(lprec *lp, REAL epsperturb); +typedef void (__WINAPI set_epspivot_func)(lprec *lp, REAL 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, REAL 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, REAL value); +typedef MYBOOL (__WINAPI set_lp_name_func)(lprec *lp, char *lpname); +typedef MYBOOL (__WINAPI set_mat_func)(lprec *lp, int row, int column, REAL 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, REAL mip_gap); +typedef MYBOOL (__WINAPI set_multiprice_func)(lprec *lp, int multiblockdiv); +typedef void (__WINAPI set_negrange_func)(lprec *lp, REAL negrange); +typedef MYBOOL (__WINAPI set_obj_func)(lprec *lp, int colnr, REAL value); +typedef void (__WINAPI set_obj_bound_func)(lprec *lp, REAL obj_bound); +typedef MYBOOL (__WINAPI set_obj_fn_func)(lprec *lp, REAL *row); +typedef MYBOOL (__WINAPI set_obj_fnex_func)(lprec *lp, int count, REAL *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, REAL *clower, REAL *cupper, int *updatelimit); +typedef MYBOOL (__WINAPI set_rh_func)(lprec *lp, int rownr, REAL value); +typedef MYBOOL (__WINAPI set_rh_range_func)(lprec *lp, int rownr, REAL deltavalue); +typedef void (__WINAPI set_rh_vec_func)(lprec *lp, REAL *rh); +typedef MYBOOL (__WINAPI set_row_func)(lprec *lp, int rownr, REAL *row); +typedef MYBOOL (__WINAPI set_rowex_func)(lprec *lp, int rownr, int count, REAL *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, REAL 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, REAL value); +typedef MYBOOL (__WINAPI set_var_branch_func)(lprec *lp, int colnr, int branch_mode); +typedef MYBOOL (__WINAPI set_var_weights_func)(lprec *lp, REAL *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, REAL rh); +typedef MYBOOL (__WINAPI str_add_lag_con_func)(lprec *lp, char *row_string, int con_type, REAL 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 REAL (__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, REAL *pcol, int *nzlist, int *maxabs); +typedef int (__WINAPI getpackedfunc)(lprec *lp, int j, int rn[], double bj[]); +typedef REAL (__WINAPI get_OF_activefunc)(lprec *lp, int varnr, REAL 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 REAL (BFP_CALLMODEL BFPreal_lp)(lprec *lp); +typedef REAL *(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, REAL *pcol, int *nzidx); +typedef void (BFP_CALLMODEL BFP_lprealintrealint)(lprec *lp, REAL *prow, int *pnzidx, REAL *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, REAL *pcol); +typedef REAL (BFP_CALLMODEL BFPreal_lplrealreal)(lprec *lp, LREAL theta, REAL *pcol); + +typedef int (BFP_CALLMODEL getcolumnex_func)(lprec *lp, int colnr, REAL *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 */ + + REAL real_solution; /* Optimal non-MIP solution base */ + REAL *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 */ + REAL *best_solution; /* sum_alloc+1 : Solution array of optimal 'Integer' LP, + structured as the solution array above */ + REAL *full_solution; /* sum_alloc+1 : Final solution array expanded for deleted variables */ + REAL *edgeVector; /* Array of reduced cost scaling norms (DEVEX and Steepest Edge) */ + + REAL *drow; /* sum+1: Reduced costs of the last simplex */ + int *nzdrow; /* sum+1: Indeces of non-zero reduced costs of the last simplex */ + REAL *duals; /* rows_alloc+1 : The dual variables of the last LP */ + REAL *full_duals; /* sum_alloc+1: Final duals array expanded for deleted variables */ + REAL *dualsfrom; /* sum_alloc+1 :The sensitivity on dual variables/reduced costs + of the last LP */ + REAL *dualstill; /* sum_alloc+1 :The sensitivity on dual variables/reduced costs + of the last LP */ + REAL *objfrom; /* columns_alloc+1 :The sensitivity on objective function + of the last LP */ + REAL *objtill; /* columns_alloc+1 :The sensitivity on objective function + of the last LP */ + REAL *objfromvalue; /* columns_alloc+1 :The value of the variables when objective value + is at its from value of the last LP */ + REAL *orig_obj; /* Unused pointer - Placeholder for OF not part of B */ + REAL *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; + + REAL 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 */ + REAL *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 */ + REAL *bsolveVal; /* rows+1: bsolved solution vector for reduced costs */ + int *bsolveIdx; /* rows+1: Non-zero indeces of bsolveVal */ + + /* RHS storage */ + REAL *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 */ + REAL *orig_upbo; /* sum_alloc+1 : Bound before transformations */ + REAL *upbo; /* " " : Upper bound after transformation and B&B work */ + REAL *orig_lowbo; /* " " */ + REAL *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 */ + REAL *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 */ + REAL *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 */ + REAL rhsmax; /* The maximum |value| of the rhs vector at any iteration */ + REAL suminfeas; /* The working sum of primal and dual infeasibilities */ + REAL bigM; /* Original objective weighting in primal phase 1 */ + REAL 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; + REAL *lag_rhs; /* Array of Lagrangean rhs vector */ + int *lag_con_type; /* Array of GT, LT or EQ */ + REAL *lambda; /* Lambda values (Lagrangean multipliers) */ + REAL lag_bound; /* The Lagrangian lower OF bound */ + REAL lag_accept; /* The Lagrangian convergence criterion */ + + /* Solver thresholds */ + REAL infinite; /* Limit for dynamic range */ + REAL negrange; /* Limit for negative variable range */ + REAL epsmachine; /* Default machine accuracy */ + REAL epsvalue; /* Input data precision / rounding of data values to 0 */ + REAL epsprimal; /* For rounding RHS values to 0/infeasibility */ + REAL epsdual; /* For rounding reduced costs to zero */ + REAL epspivot; /* Pivot reject tolerance */ + REAL epsperturb; /* Perturbation scalar */ + REAL 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 */ + + REAL bb_deltaOF; /* Minimum OF step value; computed at beginning of solve() */ + + REAL bb_breakOF; /* User-settable value for the objective function deemed + to be sufficiently good in an integer problem */ + REAL bb_limitOF; /* "Dual" bound / limit to final optimal MIP solution */ + REAL bb_heuristicOF; /* Set initial "at least better than" guess for objective function + (can significantly speed up B&B iterations) */ + REAL bb_parentOF; /* The OF value of the previous BB simplex */ + REAL bb_workOF; /* The unadjusted OF value for the current best solution */ + + /* Internal work arrays allocated as required */ + presolveundorec *presolve_undo; + workarraysrec *workarrays; + + /* MIP parameters */ + REAL epsint; /* Margin of error in determining if a float value is integer */ + REAL mip_absgap; /* Absolute MIP gap */ + REAL 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, REAL value); +MYBOOL __EXPORT_TYPE __WINAPI set_obj_fn(lprec *lp, REAL *row); +MYBOOL __EXPORT_TYPE __WINAPI set_obj_fnex(lprec *lp, int count, REAL *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, REAL *row, int constr_type, REAL rh); +MYBOOL __EXPORT_TYPE __WINAPI add_constraintex(lprec *lp, int count, REAL *row, int *colno, int constr_type, REAL 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, REAL rh); +/* The same, but with string input */ + +MYBOOL __EXPORT_TYPE __WINAPI set_row(lprec *lp, int rownr, REAL *row); +MYBOOL __EXPORT_TYPE __WINAPI set_rowex(lprec *lp, int rownr, int count, REAL *row, int *colno); +MYBOOL __EXPORT_TYPE __WINAPI get_row(lprec *lp, int rownr, REAL *row); +int __EXPORT_TYPE __WINAPI get_rowex(lprec *lp, int rownr, REAL *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, REAL *row, int con_type, REAL 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, REAL 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); +REAL __EXPORT_TYPE __WINAPI get_constr_value(lprec *lp, int rownr, int count, REAL *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, REAL value); +REAL __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, REAL deltavalue); +REAL __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, REAL *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, REAL *column); +MYBOOL __EXPORT_TYPE __WINAPI add_columnex(lprec *lp, int count, REAL *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, REAL *column); +MYBOOL __EXPORT_TYPE __WINAPI set_columnex(lprec *lp, int colnr, int count, REAL *column, int *rowno); +/* Overwrite existing column data */ + +int __EXPORT_TYPE __WINAPI column_in_lp(lprec *lp, REAL *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, REAL *column, int *nzrow); +MYBOOL __EXPORT_TYPE __WINAPI get_column(lprec *lp, int colnr, REAL *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, REAL value); +/* Fill in element (Row,Column) of the matrix + Row in [0..Rows] and Column in [1..Columns] */ +REAL __EXPORT_TYPE __WINAPI get_mat(lprec *lp, int rownr, int colnr); +REAL __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, REAL *lower, REAL *upper); +MYBOOL __EXPORT_TYPE __WINAPI get_bounds_tighter(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI set_upbo(lprec *lp, int colnr, REAL value); +REAL __EXPORT_TYPE __WINAPI get_upbo(lprec *lp, int colnr); +MYBOOL __EXPORT_TYPE __WINAPI set_lowbo(lprec *lp, int colnr, REAL value); +REAL __EXPORT_TYPE __WINAPI get_lowbo(lprec *lp, int colnr); +MYBOOL __EXPORT_TYPE __WINAPI set_bounds(lprec *lp, int colnr, REAL lower, REAL 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, REAL *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, REAL *clower, REAL *cupper, int *updatelimit); +MYBOOL __EXPORT_TYPE __WINAPI get_pseudocosts(lprec *lp, REAL *clower, REAL *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, REAL *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, REAL *guessvector, int *basisvector); + +MYBOOL __EXPORT_TYPE __WINAPI is_feasible(lprec *lp, REAL *values, REAL 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 */ + +REAL __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, REAL *pv); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_primal_solution(lprec *lp, REAL **pv); +MYBOOL __EXPORT_TYPE __WINAPI get_dual_solution(lprec *lp, REAL *rc); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_dual_solution(lprec *lp, REAL **rc); +MYBOOL __EXPORT_TYPE __WINAPI get_lambda(lprec *lp, REAL *lambda); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_lambda(lprec *lp, REAL **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, REAL obj_bound); +REAL __EXPORT_TYPE __WINAPI get_obj_bound(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_mip_gap(lprec *lp, MYBOOL absolute, REAL mip_gap); +REAL __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, REAL value); +void __EXPORT_TYPE __WINAPI set_infinite(lprec *lp, REAL infinite); +REAL __EXPORT_TYPE __WINAPI get_infinite(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsint(lprec *lp, REAL epsint); +REAL __EXPORT_TYPE __WINAPI get_epsint(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsb(lprec *lp, REAL epsb); +REAL __EXPORT_TYPE __WINAPI get_epsb(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsd(lprec *lp, REAL epsd); +REAL __EXPORT_TYPE __WINAPI get_epsd(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsel(lprec *lp, REAL epsel); +REAL __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, REAL scalelimit); +REAL __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, REAL break_at_value); +REAL __EXPORT_TYPE __WINAPI get_break_at_value(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_negrange(lprec *lp, REAL negrange); +REAL __EXPORT_TYPE __WINAPI get_negrange(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsperturb(lprec *lp, REAL epsperturb); +REAL __EXPORT_TYPE __WINAPI get_epsperturb(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epspivot(lprec *lp, REAL epspivot); +REAL __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); + +REAL __EXPORT_TYPE __WINAPI get_objective(lprec *lp); +REAL __EXPORT_TYPE __WINAPI get_working_objective(lprec *lp); + +REAL __EXPORT_TYPE __WINAPI get_var_primalresult(lprec *lp, int index); +REAL __EXPORT_TYPE __WINAPI get_var_dualresult(lprec *lp, int index); + +MYBOOL __EXPORT_TYPE __WINAPI get_variables(lprec *lp, REAL *var); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_variables(lprec *lp, REAL **var); + +MYBOOL __EXPORT_TYPE __WINAPI get_constraints(lprec *lp, REAL *constr); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_constraints(lprec *lp, REAL **constr); + +MYBOOL __EXPORT_TYPE __WINAPI get_sensitivity_rhs(lprec *lp, REAL *duals, REAL *dualsfrom, REAL *dualstill); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_sensitivity_rhs(lprec *lp, REAL **duals, REAL **dualsfrom, REAL **dualstill); + +MYBOOL __EXPORT_TYPE __WINAPI get_sensitivity_obj(lprec *lp, REAL *objfrom, REAL *objtill); +MYBOOL __EXPORT_TYPE __WINAPI get_sensitivity_objex(lprec *lp, REAL *objfrom, REAL *objtill, REAL *objfromvalue, REAL *objtillvalue); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_sensitivity_obj(lprec *lp, REAL **objfrom, REAL **objtill); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_sensitivity_objex(lprec *lp, REAL **objfrom, REAL **objtill, REAL **objfromvalue, REAL **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); + +REAL get_rh_upper(lprec *lp, int rownr); +REAL get_rh_lower(lprec *lp, int rownr); +MYBOOL set_rh_upper(lprec *lp, int rownr, REAL value); +MYBOOL set_rh_lower(lprec *lp, int rownr, REAL 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, REAL x, REAL y, int variable); +STATIC MYBOOL feasiblePhase1(lprec *lp, REAL 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, REAL *solution, + REAL *upbo, REAL *lowbo, REAL 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 REAL compute_dualslacks(lprec *lp, int target, REAL **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, REAL *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, REAL *upbo, REAL *lowbo); +STATIC MYBOOL impose_bounds(lprec *lp, REAL * upbo, REAL *lowbo); +STATIC int unload_BB(lprec *lp); + +STATIC REAL feasibilityOffset(lprec *lp, MYBOOL isdual); +STATIC MYBOOL isP1extra(lprec *lp); +STATIC REAL get_refactfrequency(lprec *lp, MYBOOL final); +STATIC int findBasicFixedvar(lprec *lp, int afternr, MYBOOL slacksonly); +STATIC MYBOOL isBasisVarFeasible(lprec *lp, REAL tol, int basis_row); +STATIC MYBOOL isPrimalFeasible(lprec *lp, REAL tol, int infeasibles[], REAL *feasibilitygap); +STATIC MYBOOL isDualFeasible(lprec *lp, REAL tol, int *boundflips, int infeasibles[], REAL *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, REAL *prow, int *nzprow, REAL *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 REAL scaled_floor(lprec *lp, int colnr, REAL value, REAL epsscale); +STATIC REAL scaled_ceil(lprec *lp, int colnr, REAL value, REAL 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 REAL get_pseudorange(BBPSrec *pc, int mipvar, int varcode); +STATIC void update_pseudocost(BBPSrec *pc, int mipvar, int varcode, MYBOOL capupper, REAL varsol); +STATIC REAL get_pseudobranchcost(BBPSrec *pc, int mipvar, MYBOOL dofloor); +STATIC REAL get_pseudonodecost(BBPSrec *pc, int mipvar, int vartype, REAL varsol); + +/* Matrix access and equation solving routines */ +STATIC void set_OF_override(lprec *lp, REAL *ofVector); +STATIC void set_OF_p1extra(lprec *lp, REAL p1extra); +STATIC void unset_OF_p1extra(lprec *lp); +MYBOOL modifyOF1(lprec *lp, int index, REAL *ofValue, REAL mult); +REAL __WINAPI get_OF_active(lprec *lp, int varnr, REAL mult); +STATIC MYBOOL is_OF_nz(lprec *lp, int colnr); + +STATIC int get_basisOF(lprec *lp, int coltarget[], REAL crow[], int colno[]); +int __WINAPI get_basiscolumn(lprec *lp, int j, int rn[], double bj[]); +int __WINAPI obtain_column(lprec *lp, int varin, REAL *pcol, int *nzlist, int *maxabs); +STATIC int compute_theta(lprec *lp, int rownr, LREAL *theta, int isupbound, REAL HarrisScalar, MYBOOL primal); + +/* Pivot utility routines */ +STATIC int findBasisPos(lprec *lp, int notint, int *var_basic); +STATIC MYBOOL check_degeneracy(lprec *lp, REAL *pcol, int *degencount); + +#endif /* HEADER_lp_lib */ diff --git a/src/external/lpsolve/headers/run_headers/lp_matrix.h b/src/external/lpsolve/headers/run_headers/lp_matrix.h new file mode 100644 index 00000000..5fb1e35a --- /dev/null +++ b/src/external/lpsolve/headers/run_headers/lp_matrix.h @@ -0,0 +1,266 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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; + REAL 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(REAL)) + +#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; + REAL *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; + REAL *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 */ + + REAL *colmax; /* Array of maximum values of each column */ + REAL *rowmax; /* Array of maximum values of each row */ + + REAL epsvalue; /* Zero element rejection threshold */ + REAL infnorm; /* The largest absolute value in the matrix */ + REAL 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, REAL 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, REAL *row, int *colno, REAL mult, MYBOOL checkrowmode); +STATIC int mat_appendcol(MATrec *mat, int count, REAL *column, int *rowno, REAL mult, MYBOOL checkrowmode); +MYBOOL mat_get_data(lprec *lp, int matindex, MYBOOL isrow, int **rownr, int **colnr, REAL **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, REAL mult, MYBOOL DoObj); +STATIC REAL mat_getitem(MATrec *mat, int row, int column); +STATIC MYBOOL mat_setitem(MATrec *mat, int row, int column, REAL value); +STATIC MYBOOL mat_additem(MATrec *mat, int row, int column, REAL delta); +STATIC MYBOOL mat_setvalue(MATrec *mat, int Row, int Column, REAL 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, REAL mult); +STATIC void mat_multadd(MATrec *mat, REAL *lhsvector, int varnr, REAL mult); +STATIC MYBOOL mat_setrow(MATrec *mat, int rowno, int count, REAL *row, int *colno, MYBOOL doscale, MYBOOL checkrowmode); +STATIC MYBOOL mat_setcol(MATrec *mat, int colno, int count, REAL *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, REAL *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(REAL *densevector, int startpos, int endpos, REAL epsilon, REAL *nzvector, int *nzindex); +STATIC MYBOOL vec_expand(REAL *nzvector, int *nzindex, REAL *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, REAL *input, int *nzinput, REAL roundzero, REAL ofscalar, REAL *output, int *nzoutput, int roundmode); +STATIC int prod_xA(lprec *lp, int *coltarget, REAL *input, int *nzinput, REAL roundzero, REAL ofscalar, REAL *output, int *nzoutput, int roundmode); +STATIC MYBOOL prod_xA2(lprec *lp, int *coltarget, REAL *prow, REAL proundzero, int *pnzprow, + REAL *drow, REAL droundzero, int *dnzdrow, REAL ofscalar, int roundmode); + +/* Equation solution */ +STATIC MYBOOL fimprove(lprec *lp, REAL *pcol, int *nzidx, REAL roundzero); +STATIC void ftran(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero); +STATIC MYBOOL bimprove(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero); +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, + int row_nr1, REAL *vector1, REAL roundzero1, int *nzvector1, + int row_nr2, REAL *vector2, REAL 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, REAL target[], REAL newvalue); +STATIC int countsUndoLadder(DeltaVrec *DV); +STATIC int restoreUndoLadder(DeltaVrec *DV, REAL target[]); +STATIC int decrementUndoLadder(DeltaVrec *DV); +STATIC MYBOOL freeUndoLadder(DeltaVrec **DV); + +/* Specialized presolve undo functions */ +STATIC MYBOOL appendUndoPresolve(lprec *lp, MYBOOL isprimal, REAL beta, int colnrDep); +STATIC MYBOOL addUndoPresolve(lprec *lp, MYBOOL isprimal, int colnrElim, REAL alpha, REAL beta, int colnrDep); + + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_lp_matrix */ + diff --git a/src/external/lpsolve/headers/run_headers/lp_mipbb.h b/src/external/lpsolve/headers/run_headers/lp_mipbb.h new file mode 100644 index 00000000..1d1ca4d4 --- /dev/null +++ b/src/external/lpsolve/headers/run_headers/lp_mipbb.h @@ -0,0 +1,73 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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; + REAL noderesult; + REAL lastsolution; /* Optimal solution of the previous branch */ + REAL sc_bound; + REAL *upbo, *lowbo; + REAL 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, REAL *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/external/lpsolve/headers/run_headers/lp_types.h b/src/external/lpsolve/headers/run_headers/lp_types.h new file mode 100644 index 00000000..e98fbe37 --- /dev/null +++ b/src/external/lpsolve/headers/run_headers/lp_types.h @@ -0,0 +1,339 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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 REAL + #define REAL double +#endif + +#ifndef REALXP + #if 1 + #define REALXP long double /* Set local accumulation variable as long double */ + #else + #define REALXP REAL /* 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 REAL /* 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((REAL) (x)) == 0 ? 0 : -(x) ) +#define my_roundzero(val, eps) if (fabs((REAL) (val)) < eps) val = 0 +#define my_avoidtiny(val, eps) (fabs((REAL) (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((REAL) (val))) < (eps) ? 0 : (val)) +#else + #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 +#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 */ + REAL *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; + REAL thisobj, prevobj, + objstep[OBJ_STEPS], + thisinfeas, previnfeas, + epsvalue; + char spxfunc[10]; + MYBOOL pivdynamic; + MYBOOL isdual; + MYBOOL active; +} OBJmonrec; + +typedef struct _edgerec +{ + REAL *edgeVector; +} edgerec; + +typedef struct _pricerec +{ + REAL theta; + REAL pivot; + REAL 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 */ + REAL *stepList; /* Working array (values in sortedList order) */ + REAL *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; + REAL step_base; + REAL step_last; + REAL obj_base; + REAL obj_last; + REAL epszero; + REAL maxpivot; + REAL maxbound; + MYBOOL sorted; + MYBOOL truncinf; + MYBOOL objcheck; + MYBOOL dirty; +} multirec; + +#endif /* HEADER_lp_types */ diff --git a/src/external/lpsolve/headers/run_headers/lp_utils.h b/src/external/lpsolve/headers/run_headers/lp_utils.h new file mode 100644 index 00000000..f5137c6f --- /dev/null +++ b/src/external/lpsolve/headers/run_headers/lp_utils.h @@ -0,0 +1,155 @@ +// Copyright(c) 2016-2018 Kjell Konis . +// Version: 5.5.2.0-17 +// Description: The lpSolveAPI package provides an R interface to 'lp_solve', +// a Mixed Integer Linear Programming (MILP) solver with support for pure +// linear, (mixed) integer/binary, semi-continuous and special ordered sets +// (SOS) models. +// License: LGPL-2 +// Repository: CRAN + +#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 */ + REAL *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, REAL **ptr, int size, MYBOOL clear); +STATIC MYBOOL allocLREAL(lprec *lp, LREAL **ptr, int size, MYBOOL clear); +STATIC MYBOOL allocFREE(lprec *lp, void **ptr); +REAL *cloneREAL(lprec *lp, REAL *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 REAL normalizeVector(REAL *myvector, int endpos); + +STATIC void swapINT(int *item1, int *item2); +STATIC void swapREAL(REAL *item1, REAL *item2); +STATIC void swapPTR(void **item1, void **item2); +STATIC REAL restoreINT(REAL valREAL, REAL epsilon); +STATIC REAL roundToPrecision(REAL value, REAL precision); + +STATIC int searchFor(int target, int *attributes, int size, int offset, MYBOOL absolute); + +STATIC MYBOOL isINT(lprec *lp, REAL value); +STATIC MYBOOL isOrigFixed(lprec *lp, int varno); +STATIC void chsign_bounds(REAL *lobound, REAL *upbound); +STATIC REAL rand_uniform(lprec *lp, REAL 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, REAL *values, int *workvector); +STATIC void pushPackedVector(PVrec *PV, PVrec *parent); +STATIC MYBOOL unpackPackedVector(PVrec *PV, REAL **target); +STATIC REAL 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/external/minimum_ellipsoid/bnmin_main.h b/src/external/minimum_ellipsoid/bnmin_main.h new file mode 100644 index 00000000..bc109dab --- /dev/null +++ b/src/external/minimum_ellipsoid/bnmin_main.h @@ -0,0 +1,87 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-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..56904cab --- /dev/null +++ b/src/external/minimum_ellipsoid/khach.h @@ -0,0 +1,233 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-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 +#include +#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 { + + namespace ublas=boost::numeric::ublas; + + struct KhachiyanEllipsoid + { + ublas::matrix Q; + ublas::vector c; + }; + + template + bool InvertMatrix(const ublas::matrix &input, + ublas::matrix &inverse) + { + using namespace boost::numeric::ublas; + typedef permutation_matrix pmatrix; + matrix A(input); + pmatrix pm(A.size1()); + int res = lu_factorize(A,pm); + if( res != 0 ) return false; + inverse.assign(ublas::identity_matrix(A.size1())); + lu_substitute(A, pm, inverse); + return true; + } + + + inline void InvertLP(const ublas::matrix &Lambdap, + ublas::matrix &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 ublas::matrix &A, + ublas::matrix &Ap) + { + Ap.resize(A.size1()+1, + A.size2()); + ublas::matrix_range > + sub(Ap, + ublas::range(0, A.size1()), + ublas::range(0, A.size2())); + sub.assign(A); + ublas::row(Ap, Ap.size1()-1)=ublas::scalar_vector(A.size2(),1.0); + + } + + inline void genDiag(const ublas::vector &p, + ublas::matrix &res) + { + res.assign(ublas::zero_matrix(p.size(), + p.size())); + for(size_t i=0; i &Ap, + const ublas::vector &p, + ublas::matrix &Lambdap) + { + + ublas::matrix dp(p.size(), p.size()); + genDiag(p, dp); + + dp=ublas::prod(dp, ublas::trans(Ap)); + Lambdap=ublas::prod(Ap, + dp); + } + + inline double KhachiyanIter(const ublas::matrix &Ap, + ublas::vector &p) + { + /// Dimensionality of the problem + const size_t d=Ap.size1()-1; + + ublas::matrix Lp; + ublas::matrix M; + KaLambda(Ap, p, Lp); + ublas::matrix ILp(Lp.size1(), Lp.size2()); + InvertLP(Lp, ILp); + M=ublas::prod(ILp, Ap); + M=ublas::prod(ublas::trans(Ap), 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)); + ublas::vector newp=p*(1-step_size); + newp(maxi) += step_size; + + const double err= ublas::norm_2(newp-p); + p=newp; + return err; + + } + + inline void KaInvertDual(const ublas::matrix &A, + const ublas::vector &p, + ublas::matrix &Q, + ublas::vector &c + ) + { + const size_t d=A.size1(); + ublas::matrix dp(p.size(), p.size()); + genDiag(p, dp); + + ublas::matrix PN=ublas::prod(dp, ublas::trans(A)); + PN=ublas::prod(A, PN); + + ublas::vector M2=ublas::prod(A, p); + ublas::matrix M3=ublas::outer_prod(M2, M2); + + ublas::matrix invert(PN.size1(), PN.size2()); + InvertLP(PN- M3, invert); + + Q.assign( 1.0/d *invert); + c=ublas::prod(A, p); + + + } + + inline double KhachiyanAlgo(const ublas::matrix &A, + double eps, + size_t maxiter, + ublas::matrix &Q, + ublas::vector &c) + { + ublas::vector p=ublas::scalar_vector(A.size2(), 1.0)*(1.0/A.size2()); + + ublas::matrix 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(); + ublas::matrix 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; + } + + ublas::matrix Q(d,d); + ublas::vector 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..5e332695 --- /dev/null +++ b/src/external/minimum_ellipsoid/mcpoint.h @@ -0,0 +1,477 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-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/extractMatPoly.h b/src/extractMatPoly.h new file mode 100644 index 00000000..5adb9d56 --- /dev/null +++ b/src/extractMatPoly.h @@ -0,0 +1,37 @@ +// 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 EXTRACTMATPOLY_H +#define EXTRACTMATPOLY_H + +// Take a H or a V-polytope and return a numerical matrix in ine or ext format respectively +template +Rcpp::NumericMatrix extractMatPoly(Polytope P) { + + typedef typename Polytope::MT MT; + + MT Mat(P.get_mat().rows(), P.dimension()+1); + Mat << P.get_vec(), P.get_mat(); + + return Rcpp::wrap(Mat); +} + +#endif diff --git a/src/frustum_of_simplex.cpp b/src/frustum_of_simplex.cpp new file mode 100644 index 00000000..7841627b --- /dev/null +++ b/src/frustum_of_simplex.cpp @@ -0,0 +1,43 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + + +#include +#include +#include "volume/exact_vols.h" + +//' Compute the percentage of the volume of the simplex that is contained in the intersection of a half-space and the simplex. +//' +//' A half-space \eqn{H} is given as a pair of a vector \eqn{a\in R^d} and a scalar \eqn{z0\in R} s.t.: \eqn{a^Tx\leq z0}. This function calls the Ali's version of the Varsi formula to compute a frustum of the simplex. +//' +//' @param a A \eqn{d}-dimensional vector that defines the direction of the hyperplane. +//' @param z0 The scalar that defines the half-space. +//' +//' @references \cite{Varsi, Giulio, +//' \dQuote{The multidimensional content of the frustum of the simplex,} \emph{Pacific J. Math. 46, no. 1, 303--314,} 1973.} +//' +//' @references \cite{Ali, Mir M., +//' \dQuote{Content of the frustum of a simplex,} \emph{ Pacific J. Math. 48, no. 2, 313--322,} 1973.} +//' +//' @return The percentage of the volume of the simplex that is contained in the intersection of a given half-space and the simplex. +//' +//' @examples +//' # compute the frustum of H: -x1+x2<=0 +//' a=c(-1,1) +//' z0=0 +//' frustum = frustum_of_simplex(a, z0) +//' @export +// [[Rcpp::export]] +double frustum_of_simplex(Rcpp::NumericVector a, double z0){ + + unsigned int dim = a.size(); + if (dim < 2) { + throw Rcpp::exception("Dimension has to be greater than 2"); + } + std::vector hyp = Rcpp::as >(a); + + return vol_Ali(hyp, -z0, dim); + +} diff --git a/src/include b/src/include new file mode 160000 index 00000000..d9d1e901 --- /dev/null +++ b/src/include @@ -0,0 +1 @@ +Subproject commit d9d1e901fbeef277d30165d4770b56cd9c174863 diff --git a/src/inner_ball.cpp b/src/inner_ball.cpp new file mode 100644 index 00000000..ca0e9ef1 --- /dev/null +++ b/src/inner_ball.cpp @@ -0,0 +1,112 @@ + +// Copyright (c) 20012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + + +#include +#include +#include +#include "cartesian_geom/cartesian_kernel.h" +#include +#include +#include +#include +#include "volume/volume_sequence_of_balls.hpp" + +//' Compute an inscribed ball of a convex polytope +//' +//' For a H-polytope described by a \eqn{m\times d} matrix \eqn{A} and a \eqn{m}-dimensional vector \eqn{b}, s.t.: \eqn{P=\{x\ |\ Ax\leq b\} }, this function computes the largest inscribed ball (Chebychev ball) by solving the corresponding linear program. +//' For both zonotopes and V-polytopes the function computes the minimum \eqn{r} s.t.: \eqn{ r e_i \in P} for all \eqn{i=1, \dots ,d}. Then the ball centered at the origin with radius \eqn{r/ \sqrt{d}} is an inscribed ball. +//' +//' @param P A convex polytope. It is an object from class (a) Hpolytope or (b) Vpolytope or (c) Zonotope or (d) VpolytopeIntersection. +//' +//' @return A \eqn{(d+1)}-dimensional vector that describes the inscribed ball. The first \eqn{d} coordinates corresponds to the center of the ball and the last one to the radius. +//' +//' @examples +//' # compute the Chebychev ball of the 2d unit simplex +//' P = gen_simplex(2,'H') +//' ball_vec = inner_ball(P) +//' +//' # compute an inscribed ball of the 3-dimensional unit cube in V-representation +//' P = gen_cube(3, 'V') +//' ball_vec = inner_ball(P) +//' @export +// [[Rcpp::export]] +Rcpp::NumericVector inner_ball(Rcpp::Reference P) { + + typedef double NT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef BoostRandomNumberGenerator RNGType; + typedef HPolytope Hpolytope; + typedef VPolytope Vpolytope; + typedef Zonotope zonotope; + typedef IntersectionOfVpoly InterVP; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + + unsigned int n, type_num; + std::string type = Rcpp::as(P.slot("type")); + + if (type.compare(std::string("Hpolytope")) == 0) { + n = Rcpp::as(P.slot("A")).cols(); + type_num = 1; + } else if (type.compare(std::string("Vpolytope")) == 0) { + n = Rcpp::as(P.slot("V")).cols(); + type_num = 2; + } else if (type.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(P.slot("G")).cols(); + type_num = 3; + } else if (type.compare(std::string("VpolytopeIntersection")) == 0) { + n = Rcpp::as(P.slot("V1")).cols(); + type_num = 4; + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } + + std::pair InnerBall; + switch (type_num) { + case 1: { + // Hpolytope + Hpolytope HP; + HP.init(n, Rcpp::as(P.slot("A")), Rcpp::as(P.slot("b"))); + InnerBall = HP.ComputeInnerBall(); + break; + } + case 2: { + // Vpolytope + Vpolytope VP; + VP.init(n, Rcpp::as(P.slot("V")), VT::Ones(Rcpp::as(P.slot("V")).rows())); + InnerBall = VP.ComputeInnerBall(); + break; + } + case 3: { + // Zonotope + zonotope ZP; + InnerBall = ZP.ComputeInnerBall(); + ZP.init(n, Rcpp::as(P.slot("G")), VT::Ones(Rcpp::as(P.slot("G")).rows())); + InnerBall = ZP.ComputeInnerBall(); + break; + } + case 4: { + // Intersection of two V-polytopes + Vpolytope VP1; + Vpolytope VP2; + InterVP VPcVP; + VP1.init(n, Rcpp::as(P.slot("V1")), VT::Ones(Rcpp::as(P.slot("V1")).rows())); + VP2.init(n, Rcpp::as(P.slot("V2")), VT::Ones(Rcpp::as(P.slot("V2")).rows())); + VPcVP.init(VP1, VP2); + if (!VPcVP.is_feasible()) throw Rcpp::exception("Empty set!"); + InnerBall = VPcVP.ComputeInnerBall(); + break; + } + } + + Rcpp::NumericVector vec(n + 1); + for (unsigned int k = 0; k < n; ++k){ + vec[k] = InnerBall.first[k]; + } + + vec[n] = InnerBall.second; + return vec; +} diff --git a/src/load_sdpa_format_file.cpp b/src/load_sdpa_format_file.cpp new file mode 100644 index 00000000..2cd77c0c --- /dev/null +++ b/src/load_sdpa_format_file.cpp @@ -0,0 +1,63 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-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); +} + diff --git a/src/poly_gen.cpp b/src/poly_gen.cpp new file mode 100644 index 00000000..05b7b5ba --- /dev/null +++ b/src/poly_gen.cpp @@ -0,0 +1,111 @@ +// [[Rcpp::depends(BH)]] + +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +#include +#include +#include +#include "cartesian_geom/cartesian_kernel.h" +#include +#include +#include +#include +#include "convex_bodies/hpolytope.h" +#include "convex_bodies/vpolytope.h" +#include "convex_bodies/zpolytope.h" +#include "generators/known_polytope_generators.h" +#include "generators/h_polytopes_generator.h" +#include "generators/v_polytopes_generators.h" +#include "generators/z_polytopes_generators.h" +#include "extractMatPoly.h" + +//' An internal Rccp function as a polytope generator +//' +//' @param kind_gen An integer to declare the type of the polytope. +//' @param Vpoly_gen A boolean parameter to declare if the requested polytope has to be in V-representation. +//' @param Zono_gen A boolean parameter to declare if the requested polytope has to be a zonotope. +//' @param dim_gen An integer to declare the dimension of the requested polytope. +//' @param m_gen An integer to declare the number of generators for the requested random zonotope or the number of vertices for a V-polytope. +//' @param seed Optional. A fixed seed for the random polytope generator. +//' +//' @keywords internal +//' +//' @return A numerical matrix describing the requested polytope +// [[Rcpp::export]] +Rcpp::NumericMatrix poly_gen (int kind_gen, bool Vpoly_gen, bool Zono_gen, int dim_gen, int m_gen, + Rcpp::Nullable seed = R_NilValue) { + + typedef double NT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef boost::mt19937 RNGType; + typedef HPolytope Hpolytope; + typedef VPolytope Vpolytope; + typedef Zonotope zonotope; + + double seed2 = (!seed.isNotNull()) ? std::numeric_limits::signaling_NaN() : Rcpp::as(seed); + + if (Zono_gen) { + switch (kind_gen) { + + case 1: + return extractMatPoly(gen_zonotope_uniform(dim_gen, m_gen, seed2)); + case 2: + return extractMatPoly(gen_zonotope_gaussian(dim_gen, m_gen, seed2)); + case 3: + return extractMatPoly(gen_zonotope_exponential(dim_gen, m_gen, seed2)); + + } + + } else if (Vpoly_gen) { + switch (kind_gen) { + + case 1: + return extractMatPoly(gen_cube(dim_gen, true)); + + case 2: + return extractMatPoly(gen_cross(dim_gen, true)); + + case 3: + return extractMatPoly(gen_simplex(dim_gen, true)); + + case 4: + return extractMatPoly(random_vpoly(dim_gen, m_gen, seed2)); + + case 5: + return extractMatPoly(random_vpoly_incube(dim_gen, m_gen, seed2)); + + } + } else { + switch (kind_gen) { + + case 1: + return extractMatPoly(gen_cube(dim_gen, false)); + + case 2: + return extractMatPoly(gen_cross(dim_gen, false)); + + case 3: + return extractMatPoly(gen_simplex(dim_gen, false)); + + case 4: + return extractMatPoly(gen_prod_simplex(dim_gen)); + + case 5: + return extractMatPoly(gen_skinny_cube(dim_gen)); + + case 6: + return extractMatPoly(random_hpoly(dim_gen, m_gen, seed2)); + + case 7: + return extractMatPoly(random_hpoly_ball(dim_gen, m_gen, seed2)); + } + } + + throw Rcpp::exception("Wrong inputs!"); +} diff --git a/src/rotating.cpp b/src/rotating.cpp new file mode 100644 index 00000000..fb1bf7cf --- /dev/null +++ b/src/rotating.cpp @@ -0,0 +1,121 @@ +// [[Rcpp::depends(BH)]] + +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +#include +#include +#include +#include "cartesian_geom/cartesian_kernel.h" +#include +#include +#include +#include +#include "volume/volume_sequence_of_balls.hpp" +#include "volume/rotating.hpp" +#include "extractMatPoly.h" + +//' An internal Rccp function for the random rotation of a convex polytope +//' +//' @param P A convex polytope (H-, V-polytope or a zonotope). +//' @param T Optional. A rotation matrix. +//' @param seed Optional. A fixed seed for the random linear map generator. +//' +//' @keywords internal +//' +//' @return A matrix that describes the rotated polytope +// [[Rcpp::export]] +Rcpp::NumericMatrix rotating (Rcpp::Reference P, Rcpp::Nullable T = R_NilValue, + Rcpp::Nullable seed = R_NilValue){ + + typedef double NT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef BoostRandomNumberGenerator RNGType; + typedef HPolytope Hpolytope; + typedef VPolytope Vpolytope; + typedef Zonotope zonotope; + typedef IntersectionOfVpoly InterVP; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + + MT TransorfMat; + Rcpp::NumericMatrix Mat; + unsigned int n, type_num; + + std::string type = Rcpp::as(P.slot("type")); + + if (type.compare(std::string("Hpolytope")) == 0) { + n = Rcpp::as(P.slot("A")).cols(); + type_num = 1; + } else if (type.compare(std::string("Vpolytope")) == 0) { + n = Rcpp::as(P.slot("V")).cols(); + type_num = 2; + } else if (type.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(P.slot("G")).cols(); + type_num = 3; + } else if (type.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 seed2 = (!seed.isNotNull()) ? std::chrono::system_clock::now() + .time_since_epoch().count() + : Rcpp::as(seed); + + switch (type_num) { + case 1: { + // Hpolytope + Hpolytope HP; + HP.init(n, Rcpp::as(P.slot("A")), Rcpp::as(P.slot("b"))); + if (T.isNotNull()) { + TransorfMat = Rcpp::as(T); + HP.linear_transformIt(TransorfMat.inverse()); + } else { + TransorfMat = rotating < MT > (HP, seed2); + } + Mat = extractMatPoly(HP); + break; + } + case 2: { + // Vpolytope + Vpolytope VP; + VP.init(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()); + } else { + TransorfMat = rotating < MT > (VP, seed2); + } + Mat = extractMatPoly(VP); + break; + } + case 3: { + // Zonotope + zonotope ZP; + ZP.init(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()); + } else { + TransorfMat = rotating < MT > (ZP, seed2); + } + Mat = extractMatPoly(ZP); + break; + } + case 4: { + throw Rcpp::exception("volesti does not support rotation for this representation currently."); + } + } + + TransorfMat.conservativeResize(n+1, n); + TransorfMat.row(n) = VT::Ones(n); + MT res(TransorfMat.rows(), Rcpp::as(Mat).rows()+n); + res << Rcpp::as(Mat).transpose(), TransorfMat; + return Rcpp::wrap(res); +} diff --git a/src/rounding.cpp b/src/rounding.cpp new file mode 100644 index 00000000..538e4b7c --- /dev/null +++ b/src/rounding.cpp @@ -0,0 +1,171 @@ +// [[Rcpp::depends(BH)]] + +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +//Contributed and/or modified by Apostolos Chalkis, as part of Google Summer of Code 2018 program. + +#include +#include +#include +#include "cartesian_geom/cartesian_kernel.h" +#include +#include +#include +#include +#include "random_walks/random_walks.hpp" +#include "volume/volume_sequence_of_balls.hpp" +#include "volume/volume_cooling_gaussians.hpp" +#include "extractMatPoly.h" + +//' Internal rcpp function for the rounding of a convex polytope +//' +//' @param P A convex polytope (H- or V-representation or zonotope). +//' @param settings A list to set the random walk and its walk length +//' @param seed Optional. A fixed seed for the number generator. +//' +//' @keywords internal +//' +//' @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::Nullable settings = R_NilValue, + Rcpp::Nullable seed = R_NilValue){ + + typedef double NT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef BoostRandomNumberGenerator RNGType; + typedef HPolytope Hpolytope; + typedef VPolytope Vpolytope; + typedef Zonotope zonotope; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + + bool cdhr = false, rdhr = false; + unsigned int n, walkL, type_num; + + std::string type = Rcpp::as(P.slot("type")); + + if (type.compare(std::string("Hpolytope")) == 0) { + n = Rcpp::as(P.slot("A")).cols(); + type_num = 1; + } else if (type.compare(std::string("Vpolytope")) == 0) { + n = Rcpp::as(P.slot("V")).cols(); + type_num = 2; + } else if (type.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(P.slot("G")).cols(); + type_num = 3; + } else if (type.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!"); + } + + RNGType rng(n); + if (seed.isNotNull()) { + unsigned seed2 = Rcpp::as(seed); + rng.set_seed(seed2); + } + + Hpolytope HP; + Vpolytope VP; + zonotope ZP; + + std::pair InnerBall; + Rcpp::NumericMatrix Mat; + + if (!settings.isNotNull() || !Rcpp::as(settings).containsElementNamed("random_walk")) { + if (type_num == 1) { + walkL = 10 + 10 * n; + cdhr = true; + } else { + walkL = 2; + } + } else if (Rcpp::as(Rcpp::as(settings)["random_walk"]).compare(std::string("CDHR")) == 0) { + walkL = 10 + 10 * n; + cdhr = true; + } else if (Rcpp::as(Rcpp::as(settings)["random_walk"]).compare(std::string("BiW")) == 0) { + walkL = 2; + } else if (Rcpp::as(Rcpp::as(settings)["random_walk"]).compare(std::string("RDHR")) == 0) { + walkL = 10 + 10 * n; + rdhr = true; + } else { + throw Rcpp::exception("Unknown walk type!"); + } + + if (Rcpp::as(settings).containsElementNamed("walk_length")) { + walkL = Rcpp::as(Rcpp::as(settings)["walk_length"]); + if (walkL <= 0) { + throw Rcpp::exception("The walk length has to be a positive integer!"); + } + } + + switch (type_num) { + case 1: { + // Hpolytope + HP.init(n, Rcpp::as(P.slot("A")), Rcpp::as(P.slot("b"))); + HP.normalize(); + InnerBall = HP.ComputeInnerBall(); + break; + } + case 2: { + // Vpolytope + VP.init(n, Rcpp::as(P.slot("V")), VT::Ones(Rcpp::as(P.slot("V")).rows())); + InnerBall = VP.ComputeInnerBall(); + break; + } + case 3: { + // Zonotope + ZP.init(n, Rcpp::as(P.slot("G")), VT::Ones(Rcpp::as(P.slot("G")).rows())); + InnerBall = ZP.ComputeInnerBall(); + break; + } + case 4: { + throw Rcpp::exception("volesti does not support rounding for this representation currently."); + } + } + + std::pair< std::pair, NT > round_res; + switch (type_num) { + case 1: { + if (cdhr) { + round_res = round_polytope(HP, InnerBall, walkL, rng); + } else if(rdhr) { + round_res = round_polytope(HP, InnerBall, walkL, rng); + } else { + round_res = round_polytope(HP, InnerBall, walkL, rng); + } + Mat = extractMatPoly(HP); + break; + } + case 2: { + if (cdhr) { + round_res = round_polytope(VP, InnerBall, walkL, rng); + } else if(rdhr) { + round_res = round_polytope(VP, InnerBall, walkL, rng); + } else { + round_res = round_polytope(VP, InnerBall, walkL, rng); + } + Mat = extractMatPoly(VP); + break; + } + case 3: { + if (cdhr) { + round_res = round_polytope(ZP, InnerBall, walkL, rng); + } else if (rdhr) { + round_res = round_polytope(ZP, InnerBall, walkL, rng); + } else { + round_res = round_polytope(ZP, InnerBall, walkL, rng); + } + Mat = extractMatPoly(ZP); + break; + } + } + + return Rcpp::List::create(Rcpp::Named("Mat") = Mat, Rcpp::Named("T") = Rcpp::wrap(round_res.first.first), + Rcpp::Named("shift") = Rcpp::wrap(round_res.first.second), + Rcpp::Named("round_value") = round_res.second); +} diff --git a/src/sample_points.cpp b/src/sample_points.cpp new file mode 100644 index 00000000..f7c48a8d --- /dev/null +++ b/src/sample_points.cpp @@ -0,0 +1,405 @@ +// [[Rcpp::depends(BH)]] + +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2020 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. + +#include +#include +#include +#include +#include +#include +#include +#include "random_walks/random_walks.hpp" +#include "volume/volume_sequence_of_balls.hpp" +#include "volume/volume_cooling_gaussians.hpp" +#include "sampling/sampling.hpp" + +template +void sample_from_polytope(Polytope &P, RNGType &rng, PointList &randPoints, unsigned int const& walkL, unsigned int const& numpoints, + bool const& gaussian, NT const& a, NT const& L, bool const& boundary, Point const& StartingPoint, unsigned int const& nburns, + bool const& set_L, bool const& cdhr, bool const& rdhr, bool const& billiard, bool const& ball_walk) +{ + if (boundary) { + if (cdhr) { + uniform_sampling_boundary (randPoints, P, rng, walkL, numpoints, + StartingPoint, nburns); + } else { + uniform_sampling_boundary (randPoints, P, rng, walkL, numpoints, + StartingPoint, nburns); + } + } else if (cdhr) { + if (gaussian) { + gaussian_sampling(randPoints, P, rng, walkL, numpoints, + a, StartingPoint, nburns); + } else { + uniform_sampling(randPoints, P, rng, walkL, numpoints, + StartingPoint, nburns); + } + } else if (rdhr){ + if (gaussian) { + gaussian_sampling(randPoints, P, rng, walkL, numpoints, + a, StartingPoint, nburns); + } else { + uniform_sampling(randPoints, P, rng, walkL, numpoints, + StartingPoint, nburns); + } + } else if (billiard) { + if (set_L) { + BilliardWalk WalkType(L); + uniform_sampling(randPoints, P, rng, WalkType, walkL, numpoints, StartingPoint, nburns); + } else { + uniform_sampling(randPoints, P, rng, walkL, numpoints, + StartingPoint, nburns); + } + } else { + if (set_L) { + if (gaussian) { + GaussianBallWalk WalkType(L); + gaussian_sampling(randPoints, P, rng, WalkType, walkL, numpoints, a, + StartingPoint, nburns); + } else { + BallWalk WalkType(L); + uniform_sampling(randPoints, P, rng, WalkType, walkL, numpoints, + StartingPoint, nburns); + } + } else { + if (gaussian) { + gaussian_sampling(randPoints, P, rng, walkL, numpoints, + a, StartingPoint, nburns); + } else { + uniform_sampling(randPoints, P, rng, walkL, numpoints, + StartingPoint, nburns); + } + } + } +} + + +//' Sample uniformly or normally distributed points from a convex Polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes). +//' +//' Sample n points with uniform or multidimensional spherical gaussian -with a mode at any point- as the target distribution. +//' +//' @param P A convex polytope. It is an object from class (a) Hpolytope or (b) Vpolytope or (c) Zonotope or (d) VpolytopeIntersection. +//' @param n The number of points that the function is going to sample from the convex polytope. +//' @param 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{'BCDHR'} boundary sampling by keeping the extreme points of CDHR or vi) \code{'BRDHR'} boundary sampling by keeping the extreme points of RDHR. The default walk is \code{'BiW'} for the uniform distribution or \code{'CDHR'} for the Gaussian distribution.} +//' \item{\code{walk_length} }{ The number of the steps per generated point for the random walk. The default value is 1.} +//' \item{\code{nburns} }{ The number of points to burn before start sampling.} +//' \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.} +//' \item{\code{seed} }{ A fixed seed for the number generator.} +//' } +//' @param 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.} +//' \item{\code{variance} }{ The variance of the multidimensional spherical gaussian. 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()}.} +//' } +//' +//' @return A \eqn{d\times n} matrix that contains, column-wise, the sampled points from the convex polytope P. +//' @examples +//' # uniform distribution from the 3d unit cube in H-representation using ball walk +//' P = gen_cube(3, 'H') +//' points = sample_points(P, n = 100, random_walk = list("walk" = "BaW", "walk_length" = 5)) +//' +//' # 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(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 +//' P = gen_rand_hpoly(2,20) +//' points = sample_points(P, n = 100, random_walk = list("walk" = "BRDHR")) +//' +//' @export +// [[Rcpp::export]] +Rcpp::NumericMatrix sample_points(Rcpp::Reference P, + int n, + Rcpp::Nullable random_walk = R_NilValue, + Rcpp::Nullable distribution = R_NilValue){ + + typedef double NT; + typedef Cartesian Kernel; + typedef BoostRandomNumberGenerator RNGType; + typedef typename Kernel::Point Point; + typedef HPolytope Hpolytope; + typedef VPolytope Vpolytope; + typedef Zonotope zonotope; + typedef IntersectionOfVpoly InterVP; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + + unsigned int dim, walkL = 1, type_num; + + std::string type = Rcpp::as(P.slot("type")); + + if (type.compare(std::string("Hpolytope")) == 0) { + dim = Rcpp::as(P.slot("A")).cols(); + type_num = 1; + } else if (type.compare(std::string("Vpolytope")) == 0) { + dim = Rcpp::as(P.slot("V")).cols(); + type_num = 2; + } else if (type.compare(std::string("Zonotope")) == 0) { + dim = Rcpp::as(P.slot("G")).cols(); + type_num = 3; + } else if (type.compare(std::string("VpolytopeIntersection")) == 0) { + dim = Rcpp::as(P.slot("V1")).cols(); + type_num = 4; + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } + + RNGType rng(dim); + if (Rcpp::as(random_walk).containsElementNamed("seed")) { + unsigned seed2 = Rcpp::as(Rcpp::as(random_walk)["seed"]); + rng.set_seed(seed2); + } + + Hpolytope HP; + Vpolytope VP; + zonotope ZP; + InterVP VPcVP; + + unsigned int numpoints, nburns = 0; + NT radius = 1.0, L; + bool set_mode = false, cdhr = false, rdhr = false, ball_walk = false, gaussian = false, + billiard = false, boundary = false, set_starting_point = false, set_L = false; + std::list randPoints; + std::pair InnerBall; + Point mode(dim); + + numpoints = n; + if (numpoints <= 0) throw Rcpp::exception("The number of samples has to be a positice integer!"); + + if (!distribution.isNotNull() || !Rcpp::as(distribution).containsElementNamed("density")) { + billiard = true; + } else if ( + Rcpp::as(Rcpp::as(distribution)["density"]).compare(std::string("uniform")) == 0) { + billiard = true; + } else if ( + Rcpp::as(Rcpp::as(distribution)["density"]).compare(std::string("gaussian")) == 0) { + gaussian = true; + } else { + throw Rcpp::exception("Wrong distribution!"); + } + + if (Rcpp::as(distribution).containsElementNamed("mode")) { + if (!gaussian) throw Rcpp::exception("Mode is given only for Gaussian sampling!"); + if (Rcpp::as(Rcpp::as(distribution)["mode"]).size() != dim) { + throw Rcpp::exception("Mode has to be a point in the same dimension as the polytope P"); + } else { + set_mode = true; + VT temp = Rcpp::as(Rcpp::as(distribution)["mode"]); + mode = Point(dim, std::vector(&temp[0], temp.data() + temp.cols() * temp.rows())); + } + } + + NT a = 0.5; + if (Rcpp::as(distribution).containsElementNamed("variance")) { + a = 1.0 / (2.0 * Rcpp::as(Rcpp::as(distribution)["variance"])); + if (!gaussian) { + Rcpp::warning("The variance can be set only for Gaussian sampling!"); + } else if (a <= 0.0) { + throw Rcpp::exception("The variance has to be positive!"); + } + } + + if (!random_walk.isNotNull() || !Rcpp::as(random_walk).containsElementNamed("walk")) { + if (gaussian) { + if (type_num == 1) { + cdhr = true; + } else { + rdhr = true; + } + } else { + billiard = true; + } + } else if (Rcpp::as(Rcpp::as(random_walk)["walk"]).compare(std::string("CDHR")) == 0) { + cdhr = true; + billiard = false; + } else if (Rcpp::as(Rcpp::as(random_walk)["walk"]).compare(std::string("RDHR")) == 0) { + rdhr = true; + billiard = false; + } else if (Rcpp::as(Rcpp::as(random_walk)["walk"]).compare(std::string("BaW")) == 0) { + if (Rcpp::as(random_walk).containsElementNamed("BaW_rad")) { + L = Rcpp::as(Rcpp::as(random_walk)["BaW_rad"]); + set_L = true; + if (L<=0.0) throw Rcpp::exception("BaW diameter must be a postitive number!"); + } + ball_walk = true; + billiard = false; + } else if (Rcpp::as(Rcpp::as(random_walk)["walk"]).compare(std::string("BiW")) == 0) { + if (gaussian) throw Rcpp::exception("Billiard walk can be used only for uniform sampling!"); + billiard = true; + if (Rcpp::as(random_walk).containsElementNamed("L")) { + L = Rcpp::as(Rcpp::as(random_walk)["L"]); + set_L = true; + if (L<=0.0) throw Rcpp::exception("L must be a postitive number!"); + } + } else if (Rcpp::as(Rcpp::as(random_walk)["walk"]).compare(std::string("BRDHR")) == 0) { + if (gaussian) throw Rcpp::exception("Gaussian sampling from the boundary is not supported!"); + rdhr = true; + boundary = true; + } else if (Rcpp::as(Rcpp::as(random_walk)["walk"]).compare(std::string("BCDHR")) == 0) { + if (gaussian) throw Rcpp::exception("Gaussian sampling from the boundary is not supported!"); + cdhr = true; + boundary = true; + } else { + throw Rcpp::exception("Unknown walk type!"); + } + + Point StartingPoint; + if (Rcpp::as(random_walk).containsElementNamed("starting_point")) { + if (Rcpp::as(Rcpp::as(random_walk)["starting_point"]).size() != dim) { + throw Rcpp::exception("Starting Point has to lie in the same dimension as the polytope P"); + } else { + set_starting_point = true; + VT temp = Rcpp::as(Rcpp::as(random_walk)["starting_point"]); + StartingPoint = Point(dim, std::vector(&temp[0], temp.data() + temp.cols() * temp.rows())); + } + } + + if (Rcpp::as(random_walk).containsElementNamed("walk_length")) { + walkL = Rcpp::as(Rcpp::as(random_walk)["walk_length"]); + if (walkL <= 0) { + throw Rcpp::exception("The walk length has to be a positive integer!"); + } + } + + + if (Rcpp::as(random_walk).containsElementNamed("nburns")) { + nburns = Rcpp::as(Rcpp::as(random_walk)["nburns"]); + if (nburns < 0) { + throw Rcpp::exception("The number of points to burn before sampling has to be a positive integer!"); + } + } + + switch(type_num) { + case 1: { + // Hpolytope + HP.init(dim, Rcpp::as(P.slot("A")), + Rcpp::as(P.slot("b"))); + + if (!set_starting_point || (!set_mode && gaussian)) { + InnerBall = HP.ComputeInnerBall(); + if (!set_starting_point) StartingPoint = InnerBall.first; + if (!set_mode && gaussian) mode = InnerBall.first; + } + if (HP.is_in(StartingPoint) == 0) { + throw Rcpp::exception("The given point is not in the interior of the polytope!"); + } + HP.normalize(); + if (gaussian) { + StartingPoint = StartingPoint - mode; + HP.shift(mode.getCoefficients()); + } + break; + } + case 2: { + // Vpolytope + VP.init(dim, Rcpp::as(P.slot("V")), + VT::Ones(Rcpp::as(P.slot("V")).rows())); + + if (!set_starting_point || (!set_mode && gaussian)) { + InnerBall = VP.ComputeInnerBall(); + if (!set_starting_point) StartingPoint = InnerBall.first; + if (!set_mode && gaussian) mode = InnerBall.first; + } + if (VP.is_in(StartingPoint) == 0) + throw Rcpp::exception("The given point is not in the interior of the polytope!"); + if (gaussian) { + StartingPoint = StartingPoint - mode; + VP.shift(mode.getCoefficients()); + } + break; + } + case 3: { + // Zonotope + ZP.init(dim, Rcpp::as(P.slot("G")), + VT::Ones(Rcpp::as(P.slot("G")).rows())); + + if (!set_starting_point || (!set_mode && gaussian)) { + InnerBall = ZP.ComputeInnerBall(); + if (!set_starting_point) StartingPoint = InnerBall.first; + if (!set_mode && gaussian) mode = InnerBall.first; + } + if (ZP.is_in(StartingPoint) == 0) + throw Rcpp::exception("The given point is not in the interior of the polytope!"); + if (gaussian) { + StartingPoint = StartingPoint - mode; + ZP.shift(mode.getCoefficients()); + } + break; + } + case 4: { + // Intersection of two V-polytopes + Vpolytope VP1; + Vpolytope VP2; + VP1.init(dim, Rcpp::as(P.slot("V1")), + VT::Ones(Rcpp::as(P.slot("V1")).rows())); + VP2.init(dim, Rcpp::as(P.slot("V2")), + VT::Ones(Rcpp::as(P.slot("V2")).rows())); + VPcVP.init(VP1, VP2); + + if (!VPcVP.is_feasible()) throw Rcpp::exception("Empty set!"); + InnerBall = VPcVP.ComputeInnerBall(); + if (!set_starting_point) StartingPoint = InnerBall.first; + if (!set_mode && gaussian) mode = InnerBall.first; + if (VPcVP.is_in(StartingPoint) == 0) + throw Rcpp::exception("The given point is not in the interior of the polytope!"); + if (gaussian) { + StartingPoint = StartingPoint - mode; + VPcVP.shift(mode.getCoefficients()); + } + break; + } + } + + switch (type_num) { + case 1: { + sample_from_polytope(HP, rng, randPoints, walkL, numpoints, gaussian, a, L, boundary, StartingPoint, nburns, + set_L, cdhr, rdhr, billiard, ball_walk); + break; + } + case 2: { + sample_from_polytope(VP, rng, randPoints, walkL, numpoints, gaussian, a, L, boundary, StartingPoint, nburns, + set_L, cdhr, rdhr, billiard, ball_walk); + break; + } + case 3: { + sample_from_polytope(ZP, rng, randPoints, walkL, numpoints, gaussian, a, L, boundary, StartingPoint, nburns, + set_L, cdhr, rdhr, billiard, ball_walk); + break; + } + case 4: { + sample_from_polytope(VPcVP, rng, randPoints, walkL, numpoints, gaussian, a, L, boundary, StartingPoint, nburns, + set_L, cdhr, rdhr, billiard, ball_walk); + break; + } + } + + if (numpoints % 2 == 1 && boundary) numpoints--; + MT RetMat(dim, numpoints); + unsigned int jj = 0; + + for (typename std::list::iterator rpit = randPoints.begin(); rpit!=randPoints.end(); rpit++, jj++) { + if (gaussian) { + + RetMat.col(jj) = rpit->getCoefficients() + mode.getCoefficients(); + + } else { + RetMat.col(jj) = (*rpit).getCoefficients(); + } + } + + return Rcpp::wrap(RetMat); + +} diff --git a/src/volume.cpp b/src/volume.cpp new file mode 100644 index 00000000..b89837b2 --- /dev/null +++ b/src/volume.cpp @@ -0,0 +1,319 @@ +// [[Rcpp::depends(BH)]] + +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2020 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. + +#include +#include +#include +#include +#include +#include +#include "random_walks/random_walks.hpp" +#include "volume/volume_cooling_gaussians.hpp" +#include "volume/volume_sequence_of_balls.hpp" +#include "volume/volume_cooling_gaussians.hpp" +#include "volume/volume_cooling_balls.hpp" +#include "volume/volume_cooling_hpoly.hpp" + + +template +double generic_volume(Polytope& P, RNGType &rng, unsigned int walk_length, NT e, + bool CG, bool CB, unsigned int win_len, + bool rounding, bool cdhr, bool rdhr, bool ball_walk, + bool billiard, int type) +{ + typedef typename Polytope::MT MT; + typedef typename Polytope::VT VT; + typedef typename Polytope::PointType Point; + + typedef HPolytope Hpolytope; + + NT round_val = 1.0; + unsigned int n = P.dimension(); + + if (rounding) { + std::pair InnerBall = P.ComputeInnerBall(); + + if (type == 1) { + round_val = round_polytope(P, InnerBall, 10 + 10 * n, rng).second; + } else { + round_val = round_polytope(P, InnerBall, 2, rng).second; + } + } + + NT vol; + if (CG) { + if (cdhr) { + vol = volume_cooling_gaussians(P, rng, e, walk_length); + } else if (rdhr) { + vol = volume_cooling_gaussians(P, rng, e, walk_length); + } else { + vol = volume_cooling_gaussians(P, rng, e, walk_length); + } + } else if (CB) { + if (cdhr) { + vol = volume_cooling_balls(P, rng, e, walk_length, win_len); + } else if (rdhr) { + vol = volume_cooling_balls(P, rng, e, walk_length, win_len); + } else if (ball_walk) { + vol = volume_cooling_balls(P, rng, e, walk_length, win_len); + } else { + vol = volume_cooling_balls(P, rng, e, walk_length, win_len); + } + } else { + if (cdhr) { + vol = volume_sequence_of_balls(P, rng, e, walk_length); + } else if (rdhr) { + vol = volume_sequence_of_balls(P, rng, e, walk_length); + } else if (ball_walk) { + vol = volume_sequence_of_balls(P, rng, e, walk_length); + } else { + vol = volume_sequence_of_balls(P, rng, e, walk_length); + } + } + vol *= round_val; + return vol; +} + +//' The main function for volume approximation of a convex Polytope (H-polytope, V-polytope, zonotope or intersection of two V-polytopes) +//' +//' For the volume approximation can be used three algorithms. Either CoolingBodies (CB) or SequenceOfBalls (SOB) or CoolingGaussian (CG). An H-polytope with \eqn{m} facets is described by a \eqn{m\times d} matrix \eqn{A} and a \eqn{m}-dimensional vector \eqn{b}, s.t.: \eqn{P=\{x\ |\ Ax\leq b\} }. A V-polytope is defined as the convex hull of \eqn{m} \eqn{d}-dimensional points which correspond to the vertices of P. A zonotope is desrcibed by the Minkowski sum of \eqn{m} \eqn{d}-dimensional segments. +//' +//' @param P A convex polytope. It is an object from class a) Hpolytope or b) Vpolytope or c) Zonotope or d) VpolytopeIntersection. +//' @param settings Optional. A list that declares which algorithm, random walk and values of parameters to use, as follows: +//' \itemize{ +//' \item{\code{algorithm} }{ A string to set the algorithm to use: a) \code{'CB'} for CB algorithm, b) \code{'SoB'} for SOB algorithm or b) \code{'CG'} for CG algorithm. The defalut algorithm is \code{'CB'}.} +//' \item{\code{error} }{ A numeric value to set the upper bound for the approximation error. The default value is \eqn{1} for SOB algorithm and \eqn{0.1} otherwise.} +//' \item{\code{random_walk} }{ A string that declares the random walk method: a) \code{'CDHR'} for Coordinate Directions Hit-and-Run, b) \code{'RDHR'} for Random Directions Hit-and-Run, c) \code{'BaW'} for Ball Walk, or \code{'BiW'} for Billiard walk. For CB and SOB algorithms the default walk is \code{'CDHR'} for H-polytopes and \code{'BiW'} for the other representations. For CG algorithm the default walk is \code{'CDHR'} for H-polytopes and \code{'RDHR'} for the other representations.} +//' \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{400+3d^2} for CB or \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 A boolean parameter for rounding. The default value is \code{FALSE}. +//' +//' @references \cite{I.Z.Emiris and V. Fisikopoulos, +//' \dQuote{Practical polytope volume approximation,} \emph{ACM Trans. Math. Soft.,} 2018.}, +//' @references \cite{A. Chalkis and I.Z.Emiris and V. Fisikopoulos, +//' \dQuote{Practical Volume Estimation by a New Annealing Schedule for Cooling Convex Bodies,} \emph{CoRR, abs/1905.05494,} 2019.}, +//' @references \cite{B. Cousins and S. Vempala, \dQuote{A practical volume algorithm,} \emph{Springer-Verlag Berlin Heidelberg and The Mathematical Programming Society,} 2015.} +//' +//' +//' @return The approximation of the volume of a convex polytope. +//' @examples +//' +//' # calling SOB algorithm for a H-polytope (3d unit simplex) +//' HP = gen_cube(3,'H') +//' vol = volume(HP) +//' +//' # calling CG algorithm for a V-polytope (2d simplex) +//' VP = gen_simplex(2,'V') +//' vol = volume(VP, settings = list("algorithm" = "CG")) +//' +//' # calling CG algorithm for a 2-dimensional zonotope defined as the Minkowski sum of 4 segments +//' Z = gen_rand_zonotope(2, 4) +//' vol = volume(Z, settings = list("random_walk" = "RDHR", "walk_length" = 2)) +//' +//' @export +// [[Rcpp::export]] +double volume (Rcpp::Reference P, + Rcpp::Nullable settings = R_NilValue, + bool rounding = false) { + + typedef double NT; + typedef Cartesian Kernel; + typedef typename Kernel::Point Point; + typedef BoostRandomNumberGenerator RNGType; + typedef HPolytope Hpolytope; + typedef VPolytope Vpolytope; + typedef Zonotope zonotope; + typedef IntersectionOfVpoly InterVP; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + + unsigned int n, walkL, type_num; + std::string type = Rcpp::as(P.slot("type")); + + if (type.compare(std::string("Hpolytope")) == 0) { + n = Rcpp::as(P.slot("A")).cols(); + type_num = 1; + } else if (type.compare(std::string("Vpolytope")) == 0) { + n = Rcpp::as(P.slot("V")).cols(); + type_num = 2; + } else if (type.compare(std::string("Zonotope")) == 0) { + n = Rcpp::as(P.slot("G")).cols(); + type_num = 3; + } else if (type.compare(std::string("VpolytopeIntersection")) == 0) { + n = Rcpp::as(P.slot("V1")).cols(); + type_num = 4; + } else { + throw Rcpp::exception("Unknown polytope representation!"); + } + + RNGType rng(n); + if (Rcpp::as(settings).containsElementNamed("seed")) { + unsigned seed2 = Rcpp::as(Rcpp::as(settings)["seed"]); + rng.set_seed(seed2); + } + + bool CG = false, CB = false, cdhr = false, rdhr = false, ball_walk = false, + hpoly = false, billiard = false; + unsigned int win_len = 4*n*n+500; + + NT e; + + if (!Rcpp::as(settings).containsElementNamed("algorithm")) { + if (type_num == 2 || type_num == 3 || type_num == 4) { + CB = true; + } else if (n <= 200) { + CB = true; + } else { + CG = true; + } + walkL = (!Rcpp::as(settings).containsElementNamed("walk_length")) ? 1 : Rcpp::as( + Rcpp::as(settings)["walk_length"]); + e = (!Rcpp::as(settings).containsElementNamed("error")) ? 0.1 : Rcpp::as( + Rcpp::as(settings)["error"]); + } else if (Rcpp::as(Rcpp::as(settings)["algorithm"]).compare(std::string("SOB")) == 0) { + + walkL = (!Rcpp::as(settings).containsElementNamed("walk_length")) ? 10 + n / 10 : Rcpp::as( + Rcpp::as(settings)["walk_length"]); + e = (!Rcpp::as(settings).containsElementNamed("error")) ? 1.0 : Rcpp::as( + Rcpp::as(settings)["error"]); + + } else if (Rcpp::as(Rcpp::as(settings)["algorithm"]).compare(std::string("CG")) == 0) { + + CG = true; + walkL = (!Rcpp::as(settings).containsElementNamed("walk_length")) ? 1 : Rcpp::as( + Rcpp::as(settings)["walk_length"]); + e = (!Rcpp::as(settings).containsElementNamed("error")) ? 0.1 : Rcpp::as( + Rcpp::as(settings)["error"]); + + } else if (Rcpp::as(Rcpp::as(settings)["algorithm"]).compare(std::string("CB")) == 0) { + + CB = true; + walkL = (!Rcpp::as(settings).containsElementNamed("walk_length")) ? 1 : Rcpp::as( + Rcpp::as(settings)["walk_length"]); + e = (!Rcpp::as(settings).containsElementNamed("error")) ? 0.1 : Rcpp::as( + Rcpp::as(settings)["error"]); + + } else { + throw Rcpp::exception("Unknown method!"); + } + + + if (!Rcpp::as(settings).containsElementNamed("random_walk")) { + if ( type_num == 1 ){ + cdhr = true; + if (CB) win_len = 3*n*n+400; + } else { + if (CB) { + billiard = true; + win_len = 250; + } else { + rdhr = true; + } + } + }else if (Rcpp::as(Rcpp::as(settings)["random_walk"]).compare(std::string("CDHR")) == 0) { + cdhr = true; + if (CB) win_len = 3*n*n+400; + } else if (Rcpp::as(Rcpp::as(settings)["random_walk"]).compare(std::string("RDHR")) == 0) { + rdhr = true; + if (CB) win_len = 3*n*n+400; + } else if (Rcpp::as(Rcpp::as(settings)["random_walk"]).compare(std::string("BaW")) == 0) { + ball_walk = true; + } else if (Rcpp::as(Rcpp::as(settings)["random_walk"]).compare(std::string("BiW")) == 0) { + if (CG) { + if (type_num !=1){ + Rcpp::Rcout << "Billiard walk is not supported for CG algorithm. RDHR is used."<(settings).containsElementNamed("win_len")) { + win_len = Rcpp::as(Rcpp::as(settings)["win_len"]); + if (!CB && !CG) Rf_warning("flag 'win_len' can be used only for CG or CB algorithms."); + } + + switch(type_num) { + case 1: { + // Hpolytope + Hpolytope HP; + HP.init(n, Rcpp::as(P.slot("A")), Rcpp::as(P.slot("b"))); + return generic_volume(HP, rng, walkL, e, CG, CB, win_len, rounding, + cdhr, rdhr, ball_walk, billiard, type_num); + } + case 2: { + // Vpolytope + Vpolytope VP; + VP.init(n, Rcpp::as(P.slot("V")), VT::Ones(Rcpp::as(P.slot("V")).rows())); + return generic_volume(VP, rng, walkL, e, CG, CB, win_len, rounding, + cdhr, rdhr, ball_walk, billiard, type_num); + } + case 3: { + // Zonotope + zonotope ZP; + ZP.init(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 && !CB) + Rf_warning("flag 'hpoly' can be used to only in MMC of CB algorithm for zonotopes."); + } else if (ZP.num_of_generators() / ZP.dimension() < 5) { + hpoly = true; + } else { + hpoly = false; + } + if (hpoly && CB) { + if (cdhr) { + return volume_cooling_hpoly(ZP, rng, e, walkL, win_len); + } else if (rdhr) { + return volume_cooling_hpoly(ZP, rng, e, walkL, win_len); + } else if (ball_walk) { + return volume_cooling_hpoly(ZP, rng, e, walkL, win_len); + } else { + return volume_cooling_hpoly(ZP, rng, e, walkL, win_len); + } + } + return generic_volume(ZP, rng, walkL, e, CG, CB, win_len, rounding, + cdhr, rdhr, ball_walk, billiard, type_num); + } + case 4: { + // Intersection of two V-polytopes + Vpolytope VP1; + Vpolytope VP2; + InterVP VPcVP; + VP1.init(n, Rcpp::as(P.slot("V1")), VT::Ones(Rcpp::as(P.slot("V1")).rows())); + VP2.init(n, Rcpp::as(P.slot("V2")), VT::Ones(Rcpp::as(P.slot("V2")).rows())); + if (Rcpp::as(settings).containsElementNamed("seed")) { + unsigned seed3 = Rcpp::as(Rcpp::as(settings)["seed"]); + rng.set_seed(seed3); + VPcVP.init(VP1, VP2, seed3); + } else { + VPcVP.init(VP1, VP2); + } + if (!VPcVP.is_feasible()) throw Rcpp::exception("Empty set!"); + return generic_volume(VPcVP, rng, walkL, e, CG, CB, win_len, rounding, + cdhr, rdhr, ball_walk, billiard, type_num); + } + } + + return 0; +} diff --git a/src/write_sdpa_format_file.cpp b/src/write_sdpa_format_file.cpp new file mode 100644 index 00000000..fc6faede --- /dev/null +++ b/src/write_sdpa_format_file.cpp @@ -0,0 +1,70 @@ +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-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; +} + diff --git a/src/zonotope_approximation.cpp b/src/zonotope_approximation.cpp new file mode 100644 index 00000000..bc1161a3 --- /dev/null +++ b/src/zonotope_approximation.cpp @@ -0,0 +1,111 @@ +// [[Rcpp::depends(BH)]] + +// VolEsti (volume computation and sampling library) + +// Copyright (c) 20012-2018 Vissarion Fisikopoulos +// Copyright (c) 2018 Apostolos Chalkis + +#include +#include +#include +#include +#include +#include +#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 "volume/volume_cooling_hpoly.hpp" + +//' An internal Rccp function for the over-approximation of a zonotope +//' +//' @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. +//' @param seed Optional. A fixed seed for the number generator. +//' +//' @keywords internal +//' +//' @return A List that contains a numerical matrix that describes the PCA approximation as a H-polytope and the ratio of fitness. +// [[Rcpp::export]] +Rcpp::List zono_approx (Rcpp::Reference Z, + Rcpp::Nullable fit_ratio = R_NilValue, + Rcpp::Nullable settings = R_NilValue, + Rcpp::Nullable seed = R_NilValue) { + + typedef double NT; + typedef Cartesian Kernel; + typedef BoostRandomNumberGenerator RNGType; + typedef typename Kernel::Point Point; + typedef HPolytope Hpolytope; + typedef Zonotope zonotope; + typedef Eigen::Matrix VT; + typedef Eigen::Matrix MT; + int n, k = Rcpp::as(Z.slot("G")).rows(), win_len = 250, walkL = 1; + + std::string type = Rcpp::as(Z.slot("type")); + + 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()) { + unsigned seed2 = Rcpp::as(seed); + rng.set_seed(seed2); + } + + NT e = 0.1, ratio = std::numeric_limits::signaling_NaN();; + bool hpoly = false; + + MT X(n, 2 * k); + 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.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); + MT Mat(2 * n, n + 1); + Mat << Gred_ii, A.transpose() * svd.matrixU().transpose(); + + Hpolytope HP; + HP.init(n, A.transpose() * svd.matrixU().transpose(), Gred_ii); + + if (fit_ratio.isNotNull() && Rcpp::as(fit_ratio)) { + NT vol_red = std::abs(svd.matrixU().determinant()); + for (int i = 0; i < n; ++i) { + vol_red *= 2.0 * Gred_ii(i); + } + + walkL = (!Rcpp::as(settings).containsElementNamed("walk_length")) ? 1 : Rcpp::as( + Rcpp::as(settings)["walk_length"]); + e = (!Rcpp::as(settings).containsElementNamed("error")) ? 0.1 : Rcpp::as( + Rcpp::as(settings)["error"]); + win_len = (!Rcpp::as(settings).containsElementNamed("win_len")) ? 200 : Rcpp::as( + Rcpp::as(settings)["win_len"]); + + zonotope ZP; + ZP.init(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"]); + } else if (ZP.num_of_generators() / ZP.dimension() < 5 ) { + hpoly = true; + } else { + hpoly = false; + } + + NT vol; + if (!hpoly) { + vol = volume_cooling_balls(ZP, rng, e, walkL, win_len); + } else { + vol = volume_cooling_hpoly(ZP, rng, e, walkL, win_len); + } + ratio = std::pow(vol_red / vol, 1.0 / NT(n)); + } + + return Rcpp::List::create(Rcpp::Named("Mat") = Rcpp::wrap(Mat), Rcpp::Named("fit_ratio") = ratio); +} diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 00000000..051a041f --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,3 @@ +library(testthat) + +test_check("volesti") diff --git a/tests/testthat/test_Hvol.R b/tests/testthat/test_Hvol.R new file mode 100644 index 00000000..af8ca908 --- /dev/null +++ b/tests/testthat/test_Hvol.R @@ -0,0 +1,67 @@ +context("H-polytopes' volume test") + +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, settings = list("algorithm" = "CB", "seed" = seed)) + } else if (alg == "SOB") { + vol = vol + volume(P, settings = list("algorithm" = "SOB", "seed" = seed)) + } else { + vol = vol + volume(P, settings = list("algorithm" = "CG", "seed" = seed)) + } + } + vol = vol / num_of_exps + error = abs(vol - exactvol) / exactvol + if (error >= tol){ + res = 0 + } else { + res = 1 + } + return(res) +} + +cran_only = TRUE +path = system.file('extdata', package = 'volesti') + +for (i in 1:2) { + + if (i==1) { + algo = 'CG' + num_of_exps = 10 + } else { + algo = 'CB' + 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_InnerBall.R b/tests/testthat/test_InnerBall.R new file mode 100644 index 00000000..a62a04f9 --- /dev/null +++ b/tests/testthat/test_InnerBall.R @@ -0,0 +1,111 @@ +context("Chebychev ball test") + +library(volesti) + +runCheTest <- function(P, name_string, radius, tol) { + + vec_ball = inner_ball(P) + rad = vec_ball[length(vec_ball)] + + error = abs(radius - rad) / radius + if (error >= tol){ + res = 0 + } else { + res = 1 + } + return(res) +} + + +path = system.file('extdata', package = 'volesti') +tol = 0.00001 + +test_that("Chebychev test", { + P = gen_cube(10, 'H') + res = runCheTest(P, 'H-cube10', 1.0, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_cube(20, 'H') + res = runCheTest(P, 'H-cube20', 1.0, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_cube(30, 'H') + res = runCheTest(P, 'H-cube30', 1.0, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_cross(10, 'H') + res = runCheTest(P, 'H-cross10', 0.316228, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_prod_simplex(5) + res = runCheTest(P, 'H-prod_simplex_5_5', 0.138197, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_prod_simplex(10) + res = runCheTest(P, 'H-prod_simplex_10_10', 0.0759747, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_prod_simplex(15) + res = runCheTest(P, 'H-prod_simplex_15_15', 0.0529858, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_prod_simplex(20) + res = runCheTest(P, 'H-prod_simplex_20_20', 0.0408628, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_simplex(10, 'H') + res = runCheTest(P, 'H-simplex10', 0.0759747, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_simplex(20, 'H') + res = runCheTest(P, 'H-simplex20', 0.0408628, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_simplex(30, 'H') + res = runCheTest(P, 'H-simplex30', 0.0281871, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_simplex(40, 'H') + res = runCheTest(P, 'H-simplex40', 0.0215868, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_simplex(50, 'H') + res = runCheTest(P, 'H-simplex50', 0.017522, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_skinny_cube(10) + res = runCheTest(P, 'H-skinny_cube10', 1.0, tol) + expect_equal(res, 1) +}) + +test_that("Chebychev test", { + P = gen_skinny_cube(20) + res = runCheTest(P, 'H-skinny_cube20', 1.0, tol) + expect_equal(res, 1) +}) diff --git a/tests/testthat/test_Vvol.R b/tests/testthat/test_Vvol.R new file mode 100644 index 00000000..31d5fec3 --- /dev/null +++ b/tests/testthat/test_Vvol.R @@ -0,0 +1,44 @@ +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 = FALSE, settings = list("seed" = seed)) + } else { + vol = vol + volume(P, settings = list("algorithm" = "CG", "error" = 0.1, "seed" = seed), rounding = FALSE) + } + } + vol = vol / num_of_exps + error = abs(vol - exactvol) / exactvol + if (error >= tol){ + res = 0 + } else { + res = 1 + } + return(res) +} + +cran_only = TRUE +num_of_exps = 2 + +for (i in 1:2) { + seed = 5 + if (i==1) { + algo = 'CG' + tol = 0.2 + } else { + algo = 'CB' + tol = 0.2 + } + + 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) + expect_equal(res, 1) + }) + +} diff --git a/tests/testthat/test_Zvol.R b/tests/testthat/test_Zvol.R new file mode 100644 index 00000000..ceb92170 --- /dev/null +++ b/tests/testthat/test_Zvol.R @@ -0,0 +1,44 @@ +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, "seed" = seed), rounding = FALSE) + } else { + vol = vol + volume(P, settings = list("algorithm" = "CG", "error" = 0.1, "seed" = seed), rounding = FALSE) + } + } + vol = vol / num_of_exps + error = abs(vol - exactvol) / exactvol + if (error >= tol){ + res = 0 + } else { + res = 1 + } + return(res) +} + +cran_only = TRUE +num_of_exps = 2 + +for (i in 1:2) { + if (i==1) { + algo = 'CG' + tol = 0.2 + } else { + algo = 'CB' + tol = 0.2 + } + + test_that("Volume Zonotope_2_4", { + Z = gen_rand_zonotope(2, 4, generator = list("seed" = 5)) + res = Zruntest(Z, 'Zonotope_2_4', tol, num_of_exps, algo, 5) + expect_equal(res, 1) + }) + +} diff --git a/tests/testthat/test_copulas.R b/tests/testthat/test_copulas.R new file mode 100644 index 00000000..e0ee5137 --- /dev/null +++ b/tests/testthat/test_copulas.R @@ -0,0 +1,23 @@ +context("Copulas' test") + +library(volesti) + +test_that("10-dimensional 2-hyp_fam copula", { + h1 = h1 = runif(n = 10, min = 1, max = 1000) + h1 = h1 / 1000 + h2 = h2 = runif(n = 10, min = 1, max = 1000) + h2 = h1 / 1000 + cop = copula(r1 = h1 , r2 = h2 , m = 10, n = 100000) + res = sum(cop) + expect_equal(res, 1) +}) + +test_that("20-dimensional 1_hyp_1_ell fam copula", { + h = h = runif(n = 20, min = 1, max = 1000) + h = h / 1000 + E = replicate(20, rnorm(30)) + E = cov(E) + cop = copula(r1 = h , sigma = E , m = 100, n = 100000) + res = sum(cop) + expect_equal(res, 1) +}) \ No newline at end of file diff --git a/tests/testthat/test_rounding.R b/tests/testthat/test_rounding.R new file mode 100644 index 00000000..254d0d46 --- /dev/null +++ b/tests/testthat/test_rounding.R @@ -0,0 +1,49 @@ +context("Rounding test") + +library(volesti) + +testRound <- function(P, exactvol, tol, name_string, num_of_exps, algo, rotation,seed){ + + if (rotation) { + P = rotate_polytope(P)$P + listHpoly = round_polytope(P, settings = list("seed" = seed)) + } else { + 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, settings=list("algorithm"="CB", "error"=0.1, "seed" = seed)) + } else { + vol = vol + listHpoly$round_value * volume(listHpoly$P, settings=list("algorithm"="CG", "error"=0.1, "seed" = seed)) + } + } + vol = vol / num_of_exps + error = abs(vol - exactvol) / exactvol + if (error >= tol){ + res = 0 + } else { + 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) + }) + + +} diff --git a/tests/testthat/test_sampling.R b/tests/testthat/test_sampling.R new file mode 100644 index 00000000..911cf50c --- /dev/null +++ b/tests/testthat/test_sampling.R @@ -0,0 +1,78 @@ +context("Sampling test") + +library(volesti) + +runsample <- function(P, name_string, dist){ + if (dist == "uniform") { + p = sample_points(P, n = 100) + } else { + p = sample_points(P, n = 100, distribution = list("density" = "gaussian")) + } + if (length(p[is.nan(p)])>0 | length(p[is.infinite(p)])>0) { + res = 0 + } else { + res = 1 + } + return(res) + +} + +path = system.file('extdata', package = 'volesti') + +for (i in 1:2) { + + if (i==1) { + distribution = 'gaussian' + } else { + distribution = 'uniform' + } + + test_that("Sampling test", { + P= gen_cube(10, 'H') + res = runsample(P, 'H-cube10', distribution) + expect_equal(res, 1) + }) + + test_that("Sampling test", { + P = gen_cross(10, 'H') + res = runsample(P, 'H-cross10', distribution) + expect_equal(res, 1) + }) + + test_that("Sampling test", { + P = gen_prod_simplex(5) + res = runsample(P, 'H-prod_simplex_5_5', distribution) + expect_equal(res, 1) + }) + + test_that("Sampling test", { + P = gen_prod_simplex(10) + res = runsample(P, 'H-prod_simplex_10_10', distribution) + expect_equal(res, 1) + }) + + test_that("Sampling test", { + P = gen_simplex(10, 'H') + res = runsample(P, 'H-prod_simplex10', distribution) + expect_equal(res, 1) + }) + + test_that("Sampling test", { + P = gen_skinny_cube(10) + res = runsample(P, 'H-skinny_cube10', distribution) + expect_equal(res, 1) + }) + + test_that("Sampling test", { + P = gen_skinny_cube(20) + res = runsample(P, 'H-skinny_cube20', distribution) + expect_equal(res, 1) + }) + + test_that("Sampling test", { + Z = gen_rand_zonotope(4, 8) + res = runsample(Z, 'zonotope_4_8', distribution) + expect_equal(res, 1) + }) + +}