Pyomo - MindtPy : Why is it that status=ok and termination_condition=feasible for an infeasible problem? - status

I ran a simple problem: Objective is Max exp(x[0]+x[1]) Constraints are x[0]+x[1]>=5, 0<=x[0]<=2, 0 <=x[1]<=2. x[0] and x[1] are integers. It is supposed to be infeasible because x[0]+x[1] <=4. But the solver.status =ok and solver.termination_condition = feasible. variables x[0], x[1] and objective contain None. Attached below is Python code
import pandas as pd
import numpy as np
import pyomo.environ as pe
from pyomo.opt import SolverStatus, TerminationCondition
model = pe.ConcreteModel(name="Pyomo Test Optimization")
model.x = pe.Var( [0, 1], within=pe.NonNegativeIntegers, bounds= (0, 2) )
model.objective = pe.Objective(expr=pe.exp(model.x[0] +model.x[1]), sense= pe.maximize)
model.Constraint_Budget_lb= pe.Constraint(expr= model.x[0]+model.x[1] >= 5 )
model.pprint()
results=pe.SolverFactory('mindtpy').solve(model)
print()
print("Results=", results)
status= results.solver.termination_condition
print("############################## Status ############################")
print()
print( "results.solver.status =", results.solver.status)
print("results.solver.termination_condition =", results.solver.termination_condition )
print("SolverStatus.warning =", SolverStatus.warning)
print()
print("Solution:")
print()
print( "pe.value(model.objective)=", pe.value(model.objective) )
print( "Decision variable x =", [model.x[0].value, model.x[1].value ] )
print("################################################################")
Output is shown below
1 Set Declarations
x_index : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 2 : {0, 1}
1 Var Declarations
x : Size=2, Index=x_index
Key : Lower : Value : Upper : Fixed : Stale : Domain
0 : 0 : None : 2 : False : True : NonNegativeIntegers
1 : 0 : None : 2 : False : True : NonNegativeIntegers
1 Objective Declarations
objective : Size=1, Index=None, Active=True
Key : Active : Sense : Expression
None : True : maximize : exp(x[0] + x[1])
1 Constraint Declarations
Constraint_Budget_lb : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 5.0 : x[0] + x[1] : +Inf : True
4 Declarations: x_index x objective Constraint_Budget_lb
---------------------------------------------------------------------------------------------
Mixed-Integer Nonlinear Decomposition Toolbox in Pyomo (MindtPy)
---------------------------------------------------------------------------------------------
For more information, please visit https://pyomo.readthedocs.io/en/stable/contributed_packages/mindtpy.html
Original model has 1 constraints (0 nonlinear) and 0 disjunctions, with 2 variables, of which 0 are binary, 2 are integer, and 0 are continuous.
Objective is nonlinear. Moving it to constraint set.
rNLP is the initial strategy being used.
===============================================================================================
Iteration | Subproblem Type | Objective Value | Primal Bound | Dual Bound | Gap | Time(s)
Initial relaxed NLP problem is infeasible. Problem may be infeasible.
MILP main problem is infeasible. Problem may have no more feasible binary configurations.
MindtPy initialization may have generated poor quality cuts.
MindtPy exiting due to MILP main problem infeasibility.
===============================================================================================
Primal integral : nan
Dual integral : nan
Primal-dual gap integral : nan
Results=
Problem:
- Name: Pyomo Test Optimization
Lower bound: -inf
Upper bound: inf
Number of objectives: 1
Number of constraints: 1
Number of variables: 2
Number of binary variables: 0
Number of integer variables: 2
Number of continuous variables: 0
Number of nonzeros: None
Sense: maximize
Number of disjunctions: 0
Solver:
- Name: MindtPyOA
Status: ok
Message: None
User time: None
System time: None
Wallclock time: None
Termination condition: feasible
Termination message: None
Timing: initialization: 0.050159021047875285
main loop: 0.024332273053005338
main: 0.02267576800659299
main_timer_start_time: 771443.781539982
total: 0.08562357001937926
Iterations: 1
Num infeasible nlp subproblem: 0
Best solution found time: None
Primal integral: nan
Dual integral: nan
Primal dual gap integral: nan
############################## Status ############################
results.solver.status = ok
results.solver.termination_condition = feasible
SolverStatus.warning = warning
Solution:
ERROR: evaluating object as numeric value: x[0]
(object: <class 'pyomo.core.base.var._GeneralVarData'>)
No value for uninitialized NumericValue object x[0]
ERROR: evaluating object as numeric value: objective
(object: <class 'pyomo.core.base.objective.ScalarObjective'>)
No value for uninitialized NumericValue object x[0]
Can you please help me why results.solver.termination_condition = feasible while the solver identified "MindtPy exiting due to MILP main problem infeasibility"?
I tried to change model.x[0]+model.x[1] >= 5 to model.x[0]+model.x[1] >= 4. It was able to find optimal solution. So the question is the wrong flags for results.solver.status
and results.solver.termination_condition.

Related

Linear programming/MIP setting up sum of conditional constraints?

I'm trying to set up MIP problem using LpSolve and I'm at a loss on how to set it up.
We are trying to allocate resources to various investments, to maximize revenue, subject to a budget constraint. Each business is located in a particular state, and We also have to spread our allocations across 5 states (this is the one that's causing me issues). My current linear equations are this:
Objective function: maximize following where Xi represents the number of dollars allocated to
ith business, and Ri represents revenue generated by the ith business
X1 * R1 +....+ X35 * R35
Subject to the constraint:
X1 * C1 +....+ X35 * C35 <= 1,000,000
Where Ci represents the costs of the ith business. Setting this objective function and constraint matrix is very easy. However each business is located in some state, and what I need to do is setup a constraint that we must allocate to business in at least 5 states.
What I initially considered was a set of constraints where each state was represented as a row: so if this row represents the state of California for instance:
X1 * 1 +....+ X2 * 1 + X3 *0 + X35 * 1 <= Some Constraint
Then each business in California is assigned a value of 1, then this would give me the sum of $s (from the Xi assigned to each business) allocated to California. I could do this for all states, but that doesn't quite get me what I want as I thought about it mroe. I could constrain each state using this methodology, but what I want is something that lets me assign Unique_States >5
But this doesn't quite get me what I want. What I really want is something that gives me a sum of total number of states "used".
Is this even possible?
I've spent a while trying to rack my brain on how to do this but I can't quite figure it out.
There's one other questions like this on SO, but I couldn't follow any of the answers:
LpSolve R conditional constraint
This question is awfully similar albeit a totally different context, but I couldn't follow it and I don't have enough reputation to ask any questions.
If anyone could give any sort of guidance
Here is a skeleton of your model in pyomo. This is basically a "Knapsack Problem" with a twist (the min number of states reqt.).
Code
import pyomo.environ as pyo
from collections import defaultdict
# some data... this could be from dictionary (as below) or file read or ....
# name state C R
investments = { 'Burger Joint': ('CA', 1.2, 3.1),
'car wash': ('CA', 1.1, 2.6),
'gem store': ('NV', 1.0, 2.5),
'warehouse': ('UT', 1.2, 3.6)}
budget = 10
min_investment = 1
min_states_to_use = 2
states = {v[0] for v in investments.values()}
investments_by_state = defaultdict(list)
for k, v in investments.items():
investments_by_state[v[0]].append(k)
# set up the model
m = pyo.ConcreteModel()
# SETS
m.I = pyo.Set(initialize=investments.keys())
m.S = pyo.Set(initialize=list(states))
# the below is an "indexed set" which is indexed by STATE and contains the
# subset of I within that state
m.IS = pyo.Set(m.S, within=m.I, initialize=investments_by_state)
# PARAMETERS
m.cost = pyo.Param(m.I, initialize={k:v[1] for k, v in investments.items()})
m.rev = pyo.Param(m.I, initialize={k:v[2] for k, v in investments.items()})
# VARIABLES
m.X = pyo.Var(m.I, domain=pyo.NonNegativeReals) # amount invested in i
m.Y = pyo.Var(m.S, domain=pyo.Binary) # 1 if state y has an investment
# OBJECTIVE
m.obj = pyo.Objective(expr=sum(m.X[i]*m.rev[i] for i in m.I), sense=pyo.maximize)
# CONSTRAINTS
# stay in budget...
m.C1 = pyo.Constraint(expr=sum(m.X[i]*m.cost[i] for i in m.I) <= budget)
# connect indicator Y to X...
def state_invest(m, s):
return m.Y[s] * min_investment <= sum(m.X[i] for i in m.IS[s])
m.C2 = pyo.Constraint(m.S, rule=state_invest)
# use needed number of states
m.C3 = pyo.Constraint(expr=sum(m.Y[s] for s in m.S) >= min_states_to_use)
m.pprint() # <-- show the whole model
# instantiate a solver and solve...
solver = pyo.SolverFactory('cbc')
result = solver.solve(m)
print(result)
m.X.display() # <-- "display" substitutes in the variable values
m.Y.display()
Output
3 Set Declarations
I : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 4 : {'Burger Joint', 'car wash', 'gem store', 'warehouse'}
IS : Size=3, Index=S, Ordered=Insertion
Key : Dimen : Domain : Size : Members
CA : 1 : I : 2 : {'Burger Joint', 'car wash'}
NV : 1 : I : 1 : {'gem store',}
UT : 1 : I : 1 : {'warehouse',}
S : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 3 : {'CA', 'NV', 'UT'}
2 Param Declarations
cost : Size=4, Index=I, Domain=Any, Default=None, Mutable=False
Key : Value
Burger Joint : 1.2
car wash : 1.1
gem store : 1.0
warehouse : 1.2
rev : Size=4, Index=I, Domain=Any, Default=None, Mutable=False
Key : Value
Burger Joint : 3.1
car wash : 2.6
gem store : 2.5
warehouse : 3.6
2 Var Declarations
X : Size=4, Index=I
Key : Lower : Value : Upper : Fixed : Stale : Domain
Burger Joint : 0 : None : None : False : True : NonNegativeReals
car wash : 0 : None : None : False : True : NonNegativeReals
gem store : 0 : None : None : False : True : NonNegativeReals
warehouse : 0 : None : None : False : True : NonNegativeReals
Y : Size=3, Index=S
Key : Lower : Value : Upper : Fixed : Stale : Domain
CA : 0 : None : 1 : False : True : Binary
NV : 0 : None : 1 : False : True : Binary
UT : 0 : None : 1 : False : True : Binary
1 Objective Declarations
obj : Size=1, Index=None, Active=True
Key : Active : Sense : Expression
None : True : maximize : 3.1*X[Burger Joint] + 2.6*X[car wash] + 2.5*X[gem store] + 3.6*X[warehouse]
3 Constraint Declarations
C1 : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : -Inf : 1.2*X[Burger Joint] + 1.1*X[car wash] + X[gem store] + 1.2*X[warehouse] : 10.0 : True
C2 : Size=3, Index=S, Active=True
Key : Lower : Body : Upper : Active
CA : -Inf : Y[CA] - (X[Burger Joint] + X[car wash]) : 0.0 : True
NV : -Inf : Y[NV] - X[gem store] : 0.0 : True
UT : -Inf : Y[UT] - X[warehouse] : 0.0 : True
C3 : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 2.0 : Y[CA] + Y[NV] + Y[UT] : +Inf : True
11 Declarations: I S IS cost rev X Y obj C1 C2 C3
Problem:
- Name: unknown
Lower bound: -29.5
Upper bound: -29.5
Number of objectives: 1
Number of constraints: 5
Number of variables: 7
Number of binary variables: 3
Number of integer variables: 3
Number of nonzeros: 4
Sense: maximize
Solver:
- Status: ok
User time: -1.0
System time: 0.0
Wallclock time: 0.0
Termination condition: optimal
Termination message: Model was solved to optimality (subject to tolerances), and an optimal solution is available.
Statistics:
Branch and bound:
Number of bounded subproblems: 0
Number of created subproblems: 0
Black box:
Number of iterations: 0
Error rc: 0
Time: 0.014362096786499023
Solution:
- number of solutions: 0
number of solutions displayed: 0
X : Size=4, Index=I
Key : Lower : Value : Upper : Fixed : Stale : Domain
Burger Joint : 0 : 1.0 : None : False : False : NonNegativeReals
car wash : 0 : 0.0 : None : False : False : NonNegativeReals
gem store : 0 : 0.0 : None : False : False : NonNegativeReals
warehouse : 0 : 7.3333333 : None : False : False : NonNegativeReals
Y : Size=3, Index=S
Key : Lower : Value : Upper : Fixed : Stale : Domain
CA : 0 : 1.0 : 1 : False : False : Binary
NV : 0 : 0.0 : 1 : False : False : Binary
UT : 0 : 1.0 : 1 : False : False : Binary
[Finished in 696ms]
There are 2 general approaches here that might help. Caveat: I don't know LpSolve syntax and I don't know how you would (or could) express these in r/LpSolve syntax. Aside: Are you open to a different framework? There are several LP frameworks in python that would make this very easy to set up... Anyhow... with some pseudocode...
In both of the approaches below, you will need to implement a binary "indicator variable" that indicates whether a state has been used. This also assumes there is some logical "min investment" value to prevent spreading something like 0.00001 in a particular investment
S = {CA, NV, UT, ...} set of states
Y[s ∈ S] ∈ {0, 1} binary indicator
Approach 1: Make subsets of your investment options by state and index those by state. Then you could sum over the whole set as needed
I = {1, 2, ..., 35} set of investment options
X[i ∈ I] ∈ {non-negative reals} the amount invested in i
IS = {CA: {1, 5, 10,...}, NV: {2, 4, 15,...} , ...} subsets by state
for each s ∈ S:
Y[s] * min_investment <= ∑ X[IS[s]] # constrain Y to be less than state sum
∑Y[s] >= 5
Approach 2: double-index your investments by [state, i] and then you can sum over either the states or investments (I).
X[s, i] <= 0 constrain for all applicable combos that are illegal
for s ∈ S:
Y[s] * min_investment <= ∑ (X[s, i] for all i ∈ I)
∑Y[s] >= 5
It appears that LpSolve wants a big matrix for constraints, so either of these might be difficult in that framework, but they are straightforward in other frameworks. Perhaps somebody w/ deeper experience in LpSolve can suggest syntax.

Matix Exponential Layer (Custom: Keras in R)

I'm trying to make a layer in Keras (R) which (matrix) exponential a layer of shape (d,d).
ie.: Input to layer is a dxd matrix and the output is a dxd matrix which is the (matrix) exponential of the input matrix.
What I've Implemented to Date:
Here's what I've done (its a degree 4 approximation because I'm also not sure how to get the tensorflow matrix exponential command working in Keras):
# Matrix Exponential
Matrix_Exp<- R6::R6Class("KerasLayer",
inherit = KerasLayer,
public = list(
call = function(x, mask = NULL) {
# Initialize Tenor-like Object -> Tensor Objects
ord0 = k_eye((k_shape(x)[1]))
ord1 = x
ord2 = (1/2)*k_dot(x,x) # note x is square so this works
ord3 = (1/6)*k_dot(x,ord2)
ord4 = (1/24)*k_dot(x,ord3)
ord0+ord1+ord2 +ord3+ord4
},
compute_output_shape = function(input_shape) {
c(d,d)
}
)
)
# Create layer wrapper function
layer_Matrix_Exp <- function(object) {
create_layer(Matrix_Exp, object)
}
I'm plugging a model with this summary into the custom layer:
Model: "sequential_32"
_________________________________________________________________________________________________________________________________________________________________
Layer (type) Output Shape Param #
=================================================================================================================================================================
dense_63 (Dense) (None, 100) 400
_________________________________________________________________________________________________________________________________________________________________
dense_64 (Dense) (None, 4) 404
_________________________________________________________________________________________________________________________________________________________________
reshape_10 (Reshape) (None, 2, 2) 0
=================================================================================================================================================================
Total params: 804
Trainable params: 804
Non-trainable params: 0
_________________________________________________________________________________________________________________________________________________________________
Problem/Error:
But I run into this error when passing layers_NE %>% layer_Matrix_Exp
WARNING:tensorflow:Entity <function wrap_fn.<locals>.fn at 0x7fbdd0cf2b90> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Evaluation error: object 'size' not found.
Error in py_call_impl(callable, dots$args, dots$keywords) :
RuntimeError: in converted code:
/scratch/users/BIM/R/x86_64-redhat-linux-gnu-library/3.6/keras/python/kerastools/layer.py:30 call *
return self.r_call(inputs, mask)
<string>:4 fn
/scratch/users/BIM/R/x86_64-redhat-linux-gnu-library/3.6/reticulate/python/rpytools/call.py:21 python_function
raise RuntimeError(res[kErrorKey])
RuntimeError: Evaluation error: object 'size' not found.
Note:
The problem is coming from the identity part but I don't know how to fix this.
Question:
How to fix error.
How to replace the order 4 (manual) approximation to the matrix exponential with the keras equivalent to the tensorflow matrix exponential command.
Thanks in advance.

R: error when trying to write an equivalent function n choose k

I'm taking the class of introduction for R programming.
we were asked to write a function that will be the same as n choose k:
choose(n, k)
we were asked to check if the function works by running n = 200, k = 50.
I wrote the following code:
select_k <- function(n, k){
sr <- c(log10(log10((factorial(n-1)/factorial(k-1)*factorial(n-k-2)))*(n/k)))
return(sr)
}
as select_k is supposed to be the " n choose k".
my function works with values such as: 100 choose 25, but it doesn't work with greater values, like n = 200, k = = 50.
select_k( n = 200, k = 50)
[1] NaN
Warning message:
In factorial(n) : value out of range in 'gammafn'
I have no idea what else can be done to fix that.
This doesn't work for larger n because factorial(n) is too big:
> factorial(199)
[1] Inf
Warning message:
In factorial(199) : value out of range in 'gammafn'
This should return 200, but the computer only sees that you are trying to divide Inf by Inf:
> factorial(200)/factorial(199)
[1] NaN
Warning messages:
1: In factorial(200) : value out of range in 'gammafn'
2: In factorial(199) : value out of range in 'gammafn'
Obviously a lot of the multiplications in "n choose k" cancel out, so you'll need to avoid using regular factorial and only multiply the numbers that don't cancel out (?prod might be useful for you). Or (probably better) use the log version lfactorial to avoid running into numbers your computer can't store.
Edit: Added lfactorial recommendation from #MrFlick's comment
Have a look at this {
a <- function(n, k) {
exp(lgamma(n+1) - lgamma(n - k + 1) - lgamma(k + 1) )
}

Eigenvalues of a large sparse matrix in R

I have some large (symmetric real) sparse matrix, and I would like to calculate a few of the largest and smallest (by magnitude) eigenvalues. I looked into Armadillo, but that links to ARPACK library, which is written in Fortran. Being a Widnows user, I don't have a Fortran compiler handy and so I decided to use ARPACK R package which comes precompiled for Windows.
My matrix is stored in MatrixMarket format, and in R, I read it as follows:
library(Matrix)
f <- file("file://./lambda.mtx", "r")
str(m <- readMM(f))
close(f)
# read sparse matrix
The matrix is symmetric, and only the lower half is actually stored in the mtx file. I have tried using a symmetric matrix, but I got nowhere. So I wrote the following (rather gruesome to me) code to form both halves of the sparse matrix:
str(A <- sparseMatrix(c(m#i, m#j), c(m#j, m#i), index1 = FALSE, x = c(m#x, m#x), giveCsparse = TRUE, use.last.ij = TRUE))
# do not treat it as symmetric, this doubles the memory but avoids
# conversion to a full matrix in eigs() so it is a good deal ...
str(nnzM <- length(m#x))
str(nnzA <- A#p[A#Dim[2] + 1])
if(nnzM * 2 - m#Dim[2] != nnzA) {
stop("failed to form full matrix A")
}
if(A#x[1] != m#x[1]) {
if(A#x[1] == 2 * m#x[1])
warning("failed to form full matrix A: diagonal elements added")
else
warning("failed to form full matrix A: bad diagonal value")
}
# make sure the repeated values of the diagonal were eliminated instead of added (use.last.ij = TRUE)
Finally, I calculate Eigenvalues using rARPACK:
library(rARPACK)
str(le <- eigs(A, k = 5, which = "LM", opts = list(retvec = FALSE))) # or dsyMatrix
str(se <- eigs(A, k = 5, sigma = 0, opts = list(retvec = FALSE))) # or dsyMatrix
le$values[1]
se$values[se$nconv]
And this works on small matrices. But on large matrices, I get bad_alloc errors in eigs(). Note that I'm running 64-bit version of R and there is 16 GB of RAM in the box (the sparse matrix in question is 174515 x 174515, with 9363966 nonzeroes and has just over 300 MB in the mtx (text format)).
I had memory issues before when I was using the symmetric sparse matrix. My guess is that the matrix is not in a good format and still gets converted to a dense matrix somewhere. Otherwise I don't see how could it be needing so much memory. Any help would be appreciated, this is the first and only thing I've written in R so far.
Upon request, I can upload the matrix somewhere and share the link. I can calculate the eigenvalues of the same matrix in Matlab, but that's mostly a manual process and I have to transfer the matrix to another machine (also 16 GB of RAM, but the Matlab is 32-bit so in theory it has much more limited working space), and the machine happens to be in Europe while i'm in Australia so it takes forever. I'd much more prefer using R on my local machine, if at all possible.
EDIT
All the data and the script, required to replicate the problem is at https://sourceforge.net/projects/slamfrontend/files/data/eigenvalues/. Also, the process actually does allocate about 12 GB of memory before it dies.
EDIT 2
This is the output of the script. It actually crashes when calculating the smallest eigenvalue, the largest calculates quickly and without a crash:
E:\my-projects\Robotics\MonaschGit\wdir>"C:\Program Files\R\R-3.2.2\bin\x64\R" --no-save -q --slave 0<get_lambda_eigenvalues.R
Formal class 'dsTMatrix' [package "Matrix"] with 7 slots
..# i : int [1:9363966] 0 1 2 3 4 5 6 1 2 3 ...
..# j : int [1:9363966] 0 0 0 0 0 0 0 1 1 1 ...
..# Dim : int [1:2] 174515 174515
..# Dimnames:List of 2
.. ..$ : NULL
.. ..$ : NULL
..# x : num [1:9363966] 1.62e+08 -2.49e+01 -2.49e+06 2.19e+07 1.79e+09 ...
..# uplo : chr "L"
..# factors : list()
Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
..# i : int [1:18553417] 0 1 2 3 4 5 6 7 8 9 ...
..# p : int [1:174516] 0 14362 28724 43086 57448 71810 86172 100534 115525 130516 ...
..# Dim : int [1:2] 174515 174515
..# Dimnames:List of 2
.. ..$ : NULL
.. ..$ : NULL
..# x : num [1:18553417] 1.62e+08 -2.49e+01 -2.49e+06 2.19e+07 1.79e+09 ...
..# factors : list()
int 9363966
int 18553417
List of 4
$ values : num [1:5] 1.38e+11 1.31e+11 1.30e+11 1.22e+11 1.16e+11
$ vectors: NULL
$ nconv : int 5
$ niter : int 60
Error: std::bad_alloc
Execution halted
This might also mean that the problem is related to the shift and invert mode used for the smallest eigenvalue. Does it actually invert the matrix in the process? If so, I can see why it runs out of memory, inverse of such matrix would be completely dense.
After taking the discussion with the developer of the rARPACK package, it became clear that the problem is not in the matrix being converted to dense, but rather in the LU factorization having to significantly change the ordering of the sparse matrix to avoid numerical problems, and hence filling the matrix in considerably.
The new version of the package will use the LDLT factorization, which does not seem to suffer from this problem, and calculates the eigenvalues quickly.
Until the new version is released, it is possible to grab a copy of the C++ sources and use them with:
class CSymmetricSparseMatrix_InvProduct {
protected:
typedef Eigen::SparseMatrix<double, Eigen::ColMajor> SpMat;
typedef Eigen::SimplicialLDLT<SpMat> SpLDLTSolver;
const size_t n;
SpMat m_mat;
SpLDLTSolver solver;
public:
CSymmetricSparseMatrix_InvProduct(const cs *p_matrix)
:m_mat(int(p_matrix->m), int(p_matrix->n)), n(p_matrix->n)
{
// could maybe use Eigen::internal::viewAsEigen(p_matrix);
_ASSERTE(p_matrix->m == p_matrix->n);
// must be square
std::vector<Eigen::Triplet<double> > triplets;
triplets.reserve(p_matrix->p[p_matrix->n]);
for(size_t i = 0; i < n; ++ i) {
for(size_t p = p_matrix->p[i], e = p_matrix->p[i + 1]; p < e; ++ p) {
double f_val = p_matrix->x[p];
size_t n_row = p_matrix->i[p], n_col = i;
triplets.push_back(Eigen::Triplet<double>(int(n_row), int(n_col), f_val));
}
}
// expand the CSparse matrix to triplets
m_mat.setFromTriplets(triplets.begin(), triplets.end());
// fill the Eigen matrix
m_mat.makeCompressed();
// compress the Eigen matrix
}
size_t rows() const
{
return n;
}
size_t cols() const
{
return n;
}
void set_shift(double sigma)
{
SpMat I((int)n, (int)n);
I.setIdentity();
solver.compute(m_mat - I * sigma); // also better use solver.setShift(-sigma, 1.0);
/*size_t n_fac_nnz = ((SpMat)solver.matrixL()).nonZeros(); // type cast required, otherwise it loops forever
fprintf(stderr, "debug: the LDLT factorization has " PRIsize " nonzeros\n", n_fac_nnz);*/
}
// y_out = inv(A - sigma * I) * x_in
void perform_op(const double *x_in, double *y_out) const
{
Eigen::Map<const Eigen::VectorXd, Eigen::DontAlign> x(x_in, n);
Eigen::Map<Eigen::VectorXd, Eigen::DontAlign> y(y_out, n);
y.noalias() = solver.solve(x);
}
private:
CSymmetricSparseMatrix_InvProduct(const CSymmetricSparseMatrix_InvProduct &r_other); // no-copy
CSymmetricSparseMatrix_InvProduct &operator =(const CSymmetricSparseMatrix_InvProduct &r_other); // no-copy
};

How do you use matrices in Nimrod?

I found this project on GitHub; it was the only search term returned for "nimrod matrix". I took the bare bones of it and changed it a little bit so that it compiled without errors, and then I added the last two lines to build a simple matrix, and then output a value, but the "getter" function isn't working for some reason. I adapted the instructions for adding properties found here, but something isn't right.
Here is my code so far. I'd like to use the GNU Scientific Library from within Nimrod, and I figured that this was the first logical step.
type
TMatrix*[T] = object
transposed: bool
dataRows: int
dataCols: int
data: seq[T]
proc index[T](x: TMatrix[T], r,c: int): int {.inline.} =
if r<0 or r>(x.rows()-1):
raise newException(EInvalidIndex, "matrix index out of range")
if c<0 or c>(x.cols()-1):
raise newException(EInvalidIndex, "matrix index out of range")
result = if x.transposed: c*x.dataCols+r else: r*x.dataCols+c
proc rows*[T](x: TMatrix[T]): int {.inline.} =
## Returns the number of rows in the matrix `x`.
result = if x.transposed: x.dataCols else: x.dataRows
proc cols*[T](x: TMatrix[T]): int {.inline.} =
## Returns the number of columns in the matrix `x`.
result = if x.transposed: x.dataRows else: x.dataCols
proc matrix*[T](rows, cols: int, d: openarray[T]): TMatrix[T] =
## Constructor. Initializes the matrix by allocating memory
## for the data and setting the number of rows and columns
## and sets the data to the values specified in `d`.
result.dataRows = rows
result.dataCols = cols
newSeq(result.data, rows*cols)
if len(d)>0:
if len(d)<(rows*cols):
raise newException(EInvalidIndex, "insufficient data supplied in matrix constructor")
for i in countup(0,rows*cols-1):
result.data[i] = d[i]
proc `[][]`*[T](x: TMatrix[T], r,c: int): T =
## Element access. Returns the element at row `r` column `c`.
result = x.data[x.index(r,c)]
proc `[][]=`*[T](x: var TMatrix[T], r,c: int, a: T) =
## Sets the value of the element at row `r` column `c` to
## the value supplied in `a`.
x.data[x.index(r,c)] = a
var m = matrix( 2, 2, [1,2,3,4] )
echo( $m[0][0] )
This is the error I get:
c:\program files (x86)\nimrod\config\nimrod.cfg(36, 11) Hint: added path: 'C:\Users\H127\.babel\libs\' [Path]
Hint: used config file 'C:\Program Files (x86)\Nimrod\config\nimrod.cfg' [Conf]
Hint: system [Processing]
Hint: mat [Processing]
mat.nim(48, 9) Error: type mismatch: got (TMatrix[int], int literal(0))
but expected one of:
system.[](a: array[Idx, T], x: TSlice[Idx]): seq[T]
system.[](a: array[Idx, T], x: TSlice[int]): seq[T]
system.[](s: string, x: TSlice[int]): string
system.[](s: seq[T], x: TSlice[int]): seq[T]
Thanks you guys!
I'd like to first point out that the matrix library you refer to is three years old. For a programming language in development that's a lot of time due to changes, and it doesn't compile any more with the current Nimrod git version:
$ nimrod c matrix
...
private/tmp/n/matrix/matrix.nim(97, 8) Error: ']' expected
It fails on the double array accessor, which seems to have changed syntax. I guess your attempt to create a double [][] accessor is problematic, it could be ambiguous: are you accessing the double array accessor of the object or are you accessing the nested array returned by the first brackets? I had to change the proc to the following:
proc `[]`*[T](x: TMatrix[T], r,c: int): T =
After that change you also need to change the way to access the matrix. Here's what I got:
for x in 0 .. <2:
for y in 0 .. <2:
echo "x: ", x, " y: ", y, " = ", m[x,y]
Basically, instead of specifying two bracket accesses you pass all the parameters inside a single bracket. That code generates:
x: 0 y: 0 = 1
x: 0 y: 1 = 2
x: 1 y: 0 = 3
x: 1 y: 1 = 4
With regards to finding software for Nimrod, I would like to recommend you using Nimble, Nimrod's package manager. Once you have it installed you can search available and maintained packages. The command nimble search math shows two potential packages: linagl and extmath. Not sure if they are what you are looking for, but at least they seem more fresh.

Resources