At Chapter 16 of the documentation the following is said:
Exporting information to a solver or algorithm to aid in solving a mathematical program (e.g., warm-starting information, variable branching priorities).
Yet I cannot find an example of how variable priorities could be set (for compatable solvers). I cannot find something about this in the source code.
As setting variable priorities is solver-specific: with which solvers does this work? More specifically: how to do this with CPLEX or Gurobi? And may it also work for open-source solvers?
This is now possible with the CPLEX LP solver using a fork of pyomo.
m = ConcreteModel()
m.x = Var(domain=Integers)
m.s = RangeSet(10)
m.y = Var(m.s, domain=Integers)
m.o = Objective(expr=m.x + sum(m.y), sense=minimize)
m.c = Constraint(expr=m.x >= 1)
m.c2 = Constraint(expr=quicksum(m.y[i] for i in m.s) >= 10)
m.priority = Suffix(direction=Suffix.EXPORT, datatype=Suffix.INT)
m.direction = Suffix(direction=Suffix.EXPORT, datatype=Suffix.INT) # this is optional
m.priority.set_value(m.x, 1)
m.priority.set_value(m.y, 2)
m.direction.set_value(m.y, BranchDirection.down)
m.direction.set_value(m.y[10], 1)
with SolverFactory('cplex', solver_io='lp') as opt:
opt.solve(model, priorities=True)
You should now see something similar to this screenshot in the logs of your CPLEX run:
To run this currently you'll need to install a specific dev branch of pyomo as follows:
pip install git+https://github.com/Pyomo/pyomo.git#refs/pull/1300/merge
This should be made available to the master dev version of pyomo and eventually a pyomo release in due time.
Pyomo has a system for passing options to the solvers. For the most part, these are passed exactly as specified, so you would look at the Gurobi or CPLEX documentation for what the accepted keywords and values are.
Related
I am trying to solve a MIP with CBC using pyomo's solver factory, however running into some infeasibility issues. I wanted to first try configuring the tolerance level and see if that works before diving deep into the data points that could cause the infeasibility.
However when I use this command, the cbc solver outputs an error:
options = {
'tol': 0.0001
}
solver = SolverFactory(solver_type)
solver.options.update(options)
Can anyone help me with the understanding how to define tolerance level in cbc? Thanks!
So, it depends a bit. Assuming you are looking for the tolerance for a mixed integer program, the keyword for CBC is 'ratio'.
Here is a setup that runs 6 threads, max 20 seconds, ratio of 0.02 (2% gap)
### SOLVE
solver = pyo.SolverFactory('cbc')
solver.options = {'sec': 20, 'threads': 6, 'ratio': 0.02}
results = solver.solve(mdl)
print(results)
There are several different types of syntax that are acceptable here... You can pass the options in to the SolverFactory, etc... but this works fine for me.
As another trick, I always get hung up on the correct keywords for these solvers.... If you have CBC installed properly, you can just go to the terminal, open CBC with the command cbc, which should give you the 'coin' prompt and type "?" to see the commands. Then you can use the command and double '??' to get details. This also works for glpk which is super helpful.
For instance:
% cbc
Welcome to the CBC MILP Solver
Version: 2.10.5
Build Date: Dec 5 2021
CoinSolver takes input from arguments ( - switches to stdin)
Enter ? for list of commands or help
Coin:?
In argument list keywords have leading - , -stdin or just - switches to stdin
One command per line (and no -)
abcd? gives list of possibilities, if only one + explanation
abcd?? adds explanation, if only one fuller help
abcd without value (where expected) gives current value
abcd value sets value
Commands are:
Double parameters:
dualB(ound) dualT(olerance) primalT(olerance) primalW(eight) psi
zeroT(olerance)
Branch and Cut double parameters:
allow(ableGap) cuto(ff) inc(rement) integerT(olerance) preT(olerance)
pumpC(utoff) ratio(Gap) sec(onds)
Integer parameters:
force(Solution) idiot(Crash) maxF(actor) maxIt(erations) output(Format)
randomS(eed) slog(Level) sprint(Crash)
Branch and Cut integer parameters:
cutD(epth) cutL(ength) depth(MiniBab) hot(StartMaxIts) log(Level) maxN(odes)
maxSaved(Solutions) maxSo(lutions) passC(uts) passF(easibilityPump)
passT(reeCuts) pumpT(une) randomC(bcSeed) slow(cutpasses) strat(egy)
strong(Branching) trust(PseudoCosts)
Keyword parameters:
allC(ommands) chol(esky) crash cross(over) direction error(sAllowed)
fact(orization) keepN(ames) mess(ages) perturb(ation) presolve
printi(ngOptions) scal(ing) timeM(ode)
I am new to Python, coming from MATLAB, and long ago from C. I have written a script in MATLAB which simulates sediment transport in rivers as a Markov Process. The code randomly places circles of a random diameter within a rectangular area of a specified dimension. The circles are non-uniform is size, drawn randomly from a specified range of sizes. I do not know how many times I will step through the circle placement operation so I use a while loop to complete the process. In an attempt to be more community oriented, I am translating the MATLAB script to Python. I used the online tool OMPC to get started, and have been working through it manually from the auto-translated version (was not that helpful, which is not surprising). To debug the code as I go, I use the
MATLAB generated results to generally compare and contrast against results in Python. It seems clear to me that I have declared variables in a way that introduces problems as calculations proceed in the script. Here are two examples of consistent problems between different instances of code execution. First, the code generated what I think are arrays within arrays because the script is returning results which look like:
array([[ True]
[False]], dtype=bool)
This result was generated for the following code snippet at the overlap_logix operation:
CenterCoord_Array = np.asarray(CenterCoordinates)
Diameter_Array = np.asarray(Diameter)
dist_check = ((CenterCoord_Array[:,0] - x_Center) ** 2 + (CenterCoord_Array[:,1] - y_Center) ** 2) ** 0.5
radius_check = (Diameter_Array / 2) + radius
radius_check_update = np.reshape(radius_check,(len(radius_check),1))
radius_overlap = (radius_check_update >= dist_check)
# Now actually check the overalp condition.
if np.sum([radius_overlap]) == 0:
# The new circle does not overlap so proceed.
newCircle_Found = 1
debug_value = 2
elif np.sum([radius_overlap]) == 1:
# The new circle overlaps with one other circle
overlap = np.arange(0,len(radius_overlap), dtype=int)
overlap_update = np.reshape(overlap,(len(overlap),1))
overlap_logix = (radius_overlap == 1)
idx_true = overlap_update[overlap_logix]
radius = dist_check(idx_true,1) - (Diameter(idx_true,1) / 2)
A similar result for the same run was produced for variables:
radius_check_update
radius_overlap
overlap_update
Here is the same code snippet for the working MATLAB version (as requested):
distcheck = ((Circles.CenterCoordinates(1,:)-x_Center).^2 + (Circles.CenterCoordinates(2,:)-y_Center).^2).^0.5;
radius_check = (Circles.Diameter ./ 2) + radius;
radius_overlap = (radius_check >= distcheck);
% Now actually check the overalp condition.
if sum(radius_overlap) == 0
% The new circle does not overlap so proceed.
newCircle_Found = 1;
debug_value = 2;
elseif sum(radius_overlap) == 1
% The new circle overlaps with one other circle
temp = 1:size(radius_overlap,2);
idx_true = temp(radius_overlap == 1);
radius = distcheck(1,idx_true) - (Circles.Diameter(1,idx_true)/2);
In the Python version I have created arrays from lists to more easily operate on the contents (the first two lines of the code snippet). The array within array result and creating arrays to access data suggests to me that I have incorrectly declared variable types, but I am not sure. Furthermore, some variables have a size, for example, (2L,) (the numerical dimension will change as circles are placed) where there is no second dimension. This produces obvious problems when I try to use the array in an operation with another array with a size (2L,1L). Because of these problems I started reshaping arrays, and then I stopped because I decided these were hacks because I had declared one, or more than one variable incorrectly. Second, for the same run I encountered the following error:
TypeError: 'numpy.ndarray' object is not callable
for the operation:
radius = dist_check(idx_true,1) - (Diameter(idx_true,1) / 2)
which occurs at the bottom of the above code snippet. I have posted the entire script at the following link because it is probably more useful to execute the script for oneself:
https://github.com/smchartrand/MarkovProcess_Bedload
I have set-up the code to run with some initial parameter values so decisions do not need to be made; these parameter values produce the expected results in the MATLAB-based script, which look something like this when plotted:
So, I seem to specifically be having issues with operations on lines 151-165, depending on the test value np.sum([radius_overlap]) and I think it is because I incorrectly declared variable types, but I am really not sure. I can say with confidence that the Python version and the MATLAB version are consistent in output through the first step of the while loop, and code line 127 which is entering the second step of the while loop. Below this point in the code the above documented issues eventually cause the script to crash. Sometimes the script executes to 15% complete, and sometimes it does not make it to 5% - this is due to the random nature of circle placement. I am preparing the code in the Spyder (Python 2.7) IDE and will share the working code publicly as a part of my research. I would greatly appreciate any help that can be offered to identify my mistakes and misapplications of python coding practice.
I believe I have answered my own question, and maybe it will be of use for someone down the road. The main sources of instruction for me can be found at the following three web pages:
Stackoverflow Question 176011
SciPy FAQ
SciPy NumPy for Matlab users
The third web page was very helpful for me coming from MATLAB. Here is the modified and working python code snippet which relates to the original snippet provided above:
dist_check = ((CenterCoordinates[0,:] - x_Center) ** 2 + (CenterCoordinates[1,:] - y_Center) ** 2) ** 0.5
radius_check = (Diameter / 2) + radius
radius_overlap = (radius_check >= dist_check)
# Now actually check the overalp condition.
if np.sum([radius_overlap]) == 0:
# The new circle does not overlap so proceed.
newCircle_Found = 1
debug_value = 2
elif np.sum([radius_overlap]) == 1:
# The new circle overlaps with one other circle
overlap = np.arange(0,len(radius_overlap[0]), dtype=int).reshape(1, len(radius_overlap[0]))
overlap_logix = (radius_overlap == 1)
idx_true = overlap[overlap_logix]
radius = dist_check[idx_true] - (Diameter[0,idx_true] / 2)
In the end it was clear to me that it was more straightforward for this example to use numpy arrays vs. lists to store results for each iteration of filling the rectangular area. For the corrected code snippet this means I initialized the variables:
CenterCoordinates, and
Diameter
as numpy arrays whereas I initialized them as lists in the posted question. This made a few mathematical operations more straightforward. I was also incorrectly indexing into variables with parentheses () as opposed to the correct method using brackets []. Here is an example of a correction I made which helped the code execute as envisioned:
Incorrect: radius = dist_check(idx_true,1) - (Diameter(idx_true,1) / 2)
Correct: radius = dist_check[idx_true] - (Diameter[0,idx_true] / 2)
This example also shows that I had issues with array dimensions which I corrected variable by variable. I am still not sure if my working code is the most pythonic or most efficient way to fill a rectangular area in a random fashion, but I have tested it about 100 times with success. The revised and working code can be downloaded here:
Working Python Script to Randomly Fill Rectangular Area with Circles
Here is an image of a final results for a successful run of the working code:
The main lessons for me were (1) numpy arrays are more efficient for repetitive numerical calculations, and (2) dimensionality of arrays which I created were not always what I expected them to be and care must be practiced when establishing arrays. Thanks to those who looked at my question and asked for clarification.
I am interested in this course and also new to python. I try the first NN program but it is quite slow (mostly in the following loop).
# loop over all test rows
for i in xrange(num_test):
distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
min_index = np.argmin(distances)
Ypred[i] = self.ytr[min_index]
Is there way to accelerate it?
Thanks.
Answer myself: The parallel approach introduced in this link (Parallelise python loop with numpy arrays and shared-memory) seems to work, basically cython, prange, gil, openmp and other tweaks.
I would like to obtain .rc or .urc suffixes for my variables from the cplex solver, using Pyomo with the NL / ASL interface. This interface is generally faster than the default cplex interface for my models. However I can't seem to get the NL interface to return these suffixes. If I use the cplex solver with default options, I get values for the rc suffix. However, if I use solver_io='nl' or set the solver to 'cplexamp' (which I think does the same thing), then I get no rc values. (I am able to get duals, but not rc's.)
Here's some example code:
from pyomo.environ import *
from pyomo.opt import SolverFactory
def show_rc(m, *args, **kwargs):
opt = SolverFactory(*args, **kwargs)
results = opt.solve(m, suffixes=['rc'])
m.solutions.load_from(results)
m.rc.pprint()
m = ConcreteModel()
m.X = Var(bounds=(0, 1))
m.obj = Objective(rule=lambda m: 3.14 * m.X, sense=maximize)
m.rc = Suffix(direction=Suffix.IMPORT, datatype=Suffix.FLOAT)
show_rc(m, "cplex") # has value 3.14
show_rc(m, "cplex", solver_io="nl") # no value returned
show_rc(m, "cplexamp") # no value returned
The documentation specifically mentions getting reduced costs via a suffix, and the .rc suffix seems to be the standard place for this in AMPL, but I'm having no luck reading this via Pyomo's NL interface. Can anyone point me in the right direction?
Unfortunately, the cplexamp executable does not return reduced costs in the solution file (I just checked). I suppose AMPL must be computing these using the dual solution that is returned. I would open up a ticket on GitHub. Perhaps we can add that functionality to our ASL interface.
In terms of speed, you should try out Pyomo's Python-based interface to Cplex (solver_io="python"). This is usually much faster as it does not require any file I/O. You will need to install the Cplex-Python bindings before you can use that interface through Pyomo. If you can "import cplex", then it should be good to go.
Edit: I forgot to mention that solver_io="python" does return reduced costs for Cplex.
i am currently trying to tune the svm function in the e1071 package for R. my input is genomic data (that is each attribute takes a value in the set {-1, 0, 1}) and none of the four kernels currently offered in the package is really good for this kind of data --- i would like to use Hamming distance as my kernel instead.
the svm function, it seems, is written in C++. i have downloaded the source via
download.packages(pkgs = "e1071",
destdir = ".",
type = "source")
found the svm.cpp file containing code for the function and the corresponding kernel portion, where i can potentially add my own custom kernel. has anyone tried doing this? is it possible to do this? once i've finished modifying svm.cpp (provided i figure out how..), how do i make the package "see" the modified file?
You can modify the existing kernel.
I changed the return statement of radial kernel to make the changes..
You can try with that