I have a working model with its associated dzn files. I have tried different solvers (Gurobi, ORTools & Chuffed). All solvers can find a solution (for now, I'm only using solve satisfy;.
However, if I increase the problem size, Gurobi & ORTools cannot find a solution in a reasonable amount of time (> 25 min) whereas Chuffed find a solution within 1 to 2 min.
For ORTools I have these stats, but cannot find a solution within 25 min.
Flattening ...
CompilePass: Flatten with /Applications/MiniZincIDE.app/Contents/Resources/share/minizinc//' library ...
MIP domains ... done (298.16 s)
Optimizing ... done (314.31 s)
Converting to old FlatZinc ... done (352.18 s)
done (352.18 s)
done (352.38 s), max stack depth 32
I am using default settings for solvers. What would you recommend ? What could explain such big differences between Chuffed & Gurobi/ORTools ?
Thanks for your hints.
PS: here is the model
include "globals.mzn";
%-----------------------------------------------------------------------------%
% MODEL PARAMETERS
% Tasks
int: n_tasks; % The number of tasks
set of int: Tasks = 1..n_tasks; % The set of all tasks
array[Tasks] of int : duration ; % The task durations
array[Tasks] of set of int: predecessor; % The task successors
array [Tasks, 1..2] of int: early_late_start; % early late start bouding information
% Resources
int: n_resources;
set of int: Resources = 1..n_resources;
array[Tasks] of set of int: resource_needed; % The resource requirements
array[Tasks] of set of int: resource_forbidden; % The resource requirements
array[Tasks] of int: number_resource_needed; % The resource requirements
% Maximum duration
int: t_max;
%-----------------------------------------------------------------------------%
%~~~~~~~~~~~~~~~~~
% MODEL VARIABLES.
%~~~~~~~~~~~~~~~~~
array [Tasks] of var 1..t_max: start; % The start times
array[Tasks, Resources] of var 0..1: resource_allocation; %Selection of resources per task.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%~~~~~~~~~~~~~~~~~
% CONSTRAINTS
%~~~~~~~~~~~~~~~~~
%~~~~~~~~~~~~~~~~~
% Resource Allocation Constraints
%~~~~~~~~~~~~~~~~~
% Mandatory & Forbidden constraint allocation
constraint forall(t in Tasks, r in resource_needed[t])(resource_allocation[t,r]=1);
constraint forall(t in Tasks, r in resource_forbidden[t])(resource_allocation[t,r]=0);
% Number of Resources per Task constraint
constraint forall(t in Tasks) (
sum(r in Resources) (resource_allocation[t, r]) = number_resource_needed[t]
);
% Constraint allocated to only one task at a time
constraint forall(r in Resources)(
cumulative(start, duration, [resource_allocation[t, r] | t in Tasks], 1)
);
%constraint cumulative(start, duration, [ sum(r in Resources)(resource_allocation[t,r]) | t in Tasks ],
% 1 );
%~~~~~~~~~~~~~~~~~
% Task Scheduling
%~~~~~~~~~~~~~~~~~
% Early Start / Late Start
constraint forall(i in Tasks)(
early_late_start[i, 1]<= start[i] /\ start[i] <= early_late_start[i, 2]
);
% Precedence constraint
constraint
forall(i in Tasks, j in predecessor[i])(
start[j]+duration[j]<= start[i]
);
% Max Time constraint
constraint forall(i in Tasks)(
start[i] + duration[i] <= t_max
);
solve satisfy;
Related
I want to solve a minimization problem between two datasets A and B defined here:
% sample data
nvars = 1000;
A = rand(69,1);
B = rand(69,nvars);
% Objective function
fun_Obj = #(Alpha,A,B) norm(A- sum(Alpha.*B(:,:),2),2);
I would like to introduce two constraints (confuneq1 and confuneq2) during the data minimization of the objective function fun_Obj (between A and B), with Alpha as a variable parameter. Fmincon is defined as follow:
[~]= fmincon(#(Alpha)fun_Obj(Alpha,A,B),x0,[],[],[],[],lb,ub,{confuneq1;confuneq2},options);
with:
- confuneq1 = #(Alpha)deal(-1, sum(Alpha)-1); % 1st constraint
- x0 = ones(1,nvars)/nvars; % starting values
- lb = zeros(1,nvars); ub = ones(1,nvars); % lower and upper bounds
The first constraint is working well. Regarding the second one, I want to force the module of Alpha to decrease when its index is increasing following a defined distribution (linear, normal...).
I tried to define several expression such as :
- confuneq2 = #(Alpha)deal(-1,Alpha(i+1)<Alpha(i));
- confuneq2 = #(Alpha)deal(-1,normpdf(Alpha,lb,sigma)-1) % Normal distribution decrease
but I can't manage to implement it correctly for fmincon.
Thanks a lot for your precious help.
Is it possible to find the nearest solution to optimal for a mixed-integer problem? For example I would want the simplified problem below:
f = [1;1;1];
intcon = 1:3;
Aeq = [0.99,0.97,0.15];
beq = 0.16;
lb = zeros(3,1);
ub = [1;1;1];
x = intlinprog(f,intcon,[],[],Aeq,beq,lb,ub)
to return x=[0;0;1] as this is the nearest integer solution to the objective value of 0.16. Instead currently it returns
Intlinprog stopped because no point satisfies the constraints.
Does not necessarily have to run intlinprog. Would ideally need to also work if beq is low, for example 0.14.
You can introduce some slack variables to allow some constraint violation when needed as follows:
largeValue = 100; % choose some large value to penalise the constraint violation
f_ = [f; largeValue; largeValue]; % penalise both slack variables
Aeq_ = [Aeq, 1, -1]; % add a positive and negative slack variable to the constraint
beq_ = beq;
lb_ = [lb; 0; 0]; % limit the constraint to a positive number
ub_ = [ub; inf; inf];
x_ = intlinprog(f_,intcon,[],[],Aeq_,beq_,lb_,ub_); % solve the adapted problem
x = x_(1:3) % extract the solution of the original problem
Remarks
I added two (positive) slack variables, one for a positive constraint violation and another one for a negative constraint violation.
You should penalise the slack variables with a large value, otherwise it is beneficial to violate your constraints more than strictly necessary. A more general approach would be to determine a good penalisation value based on the values in f and Aeq, for example
largeValue = 2*max(abs(f))/min(abs(Aeq(Aeq ~= 0)))
An example : Consider the unimodal logistic map : x[n+1] = 4*x[n](1-x[n]). The map can be used to generate +1/-1 symbols using the technique
I want to extend the above concept using the map f(x) for 3 levels, each level corresponds to a symbol but I am unsure how I can do that.
To map a continuous range (obtained for example as the output of a pseudo-random number generator, or alternatively the logistic map) to a small set of discrete values, you would need to split the continuous range into regions, and assign an output value to each of those regions. The limits of those regions would determine the corresponding threshold values to use.
For example, in the binary case you start off with a continuous range of values in [0,1] which you split into two regions: [0,0.5] and (0.5,1]. Each of those region begin assigned an output symbol, namely -1 and +1. As you have noted, the boundary of the regions being set to the midpoint of your [0,1] input range gives you a threshold of 0.5. This could be implemented as:
if (x > 0.5)
symbol = +1;
else
symbol = -1;
end
As a more compact implementation, the formula 2*(x>0.5)-1 takes advantage of the fact that in Matlab a true condition (from the x>0.5 expression) has a value of 1, whereas false has a value of 0.
For 3 discrete output values, you'd similarly split your [0,1] input range into 3 regions: [0,1/3], (1/3,2/3] and (2/3,1]. The corresponding thresholds thus being 1/3 and 2/3.
Finally for 8 discrete output values, you would similarly split your [0,1] input range into 8 regions: [0,1/8], (1/8,2/8], (2/8,3/8], (3/8,4/8], (4/8,5/8], (5/8,6/8], (6/8,7/8] and (7/8,1]. The corresponding thresholds thus being 1/8, 2/8, 3/8, 4/8, 5/8, 6/8 and 7/8, as illustrated in the following diagram:
thresholding function input: |-----|-----|-----|-----|-----|-----|-----|-----|
0 | | | | | | | 1
thresholds: 1/8 2/8 3/8 4/8 5/8 6/8 7/8
| | | | | | | |
v v v v v v v v
generated symbol: -7 -5 -3 -1 +1 +3 +5 +7
This then gives the following symbol mapping implementation:
if (x < 1/8)
symbol = -7;
elseif (x < 2/8)
symbol = -5;
elseif (x < 3/8)
symbol = -3;
elseif (x < 4/8)
symbol = -1;
elseif (x < 5/8)
symbol = +1;
elseif (x < 6/8)
symbol = +3;
elseif (x < 7/8)
symbol = +5;
else
symbol = +7;
end
As a more compact implementation, you could similarly use the floor function to obtain discrete levels:
% x : some value in the [0,1] range
% s : a symbol in the {-7,-5,-3,-1,+1,+3,+5,+7} set
function s = threshold(x)
% Note on implementation:
% 8*x turns the input range from [0,1] to [0,8]
% floor(8*x) then turns that into values {0,1,2,3,4,5,6,7}
% then a linear transform (2*() - 7) is applied to map
% 0 -> -7, 1 -> -5, 2 -> -3, ..., 7 -> 7
% min/max finally applied just as a safety to make sure we don't overflow due
% to roundoff errors (if any).
s = min(7, max(-7, 2*floor(8*x) - 7));
end
Now if you want to generate complex symbols with 8 levels for the real part and 8 levels for the imaginary part, you'd simply combine them just like in the binary case. Mainly you'd generate a first value which gives you the real part, then a second value for the imaginary part:
x_real = rand(); % random input 0 <= x_real <= 1
x_imag = rand(); % another one
s = threshold(x_real) + sqrt(-1)*threshold(x_imag);
Addressing some points raised by a previous revision of the question:
One thing to note is that x[n+1] = 4*x[n](1-x[n]) maps values in [0,1] to the same range of values. This makes it possible to iteratively apply the mapping to obtain additional values, and correspondingly generate a binary sequence with the threshold application (x > 0.5). The function f(x) you provided (in an earlier edit of the question) on the other hand, maps values within a range with discontinuities (roughly covering [-7.5,7.5] depending on p) to [0,1]. In other words you would need to either modify f(x) or otherwise map its output back to the input domain of f(x). It would probably be easier to consider a general uniform pseudo-random number generator over the [-8,+8] range as input to the threshold function:
% x : some value in the [-8,8] range
% s : a symbol in the {-7,-5,-3,-1,+1,+3,+5,+7} set
function s = threshold_8PAM(x)
s = min(7, max(-7, 2*round(x/2 + 0.5) - 1));
end
To get the final 64-QAM symbols you would combine two 8-PAM symbols in quadrature (i.e. x64qam = xQ + sqrt(-1)*xI, where xQ and xI have both been generated with the above procedure).
That said, if the goal is to implement a digital communication system using 64-QAM symbols with additional chaotic modulation, you'd ultimately want to take into account the source of input data to transmit rather than randomly generating both the chaotic modulation and the source data in one shot. That is even if for performance evaluation you wind up generating the source data randomly, it is still a good idea to be generating it independently of the chaotic modulation.
Addressing those concerns, the paper An Enhanced Spectral Efficiency Chaos-Based Symbolic Dynamics Transceiver Design suggests a different approach based on the inverse map you provided, which can be implemented as:
function x = inverse_mapping(x,SymbIndex,p)
if (SymbIndex==0)
x = ((1-p)*x-14)/2;
elseif (SymbIndex==1)
x = ((1-p)*x-10)/2;
elseif (SymbIndex==2)
x = ((1-p)*x-6)/2;
elseif (SymbIndex==3)
x = ((1-p)*x-2)/2;
elseif (SymbIndex==4)
x = ((1-p)*x+2)/2;
elseif (SymbIndex==5)
x = ((1-p)*x+6)/2;
elseif (SymbIndex==6)
x = ((1-p)*x+10)/2;
elseif (SymbIndex==7)
x = ((1-p)*x+14)/2;
end
end
As you may notice, the function takes a symbol index (3 bits, which you'd get from the input source data) and the current state of the modulated output (which you may seed with any value within the convergence range of inverse_mapping) as two independent input streams. Note that you can compute the bounds of the convergence range of inverse_mapping by finding the limits of repeated application of the mapping using input symbol index s=0, and s=7 (using for example a seed of x=0). This should converge to [-14/(1+p), 14/(1+p)].
The chaotic modulation described in the above referenced paper can then be achieved with (setting the control parameter p=0.8 as an example):
% Simulation parameters
Nsymb = 10000;
p = 0.8;
M = 64;
% Source data generation
SymbolIndexQ = randi([0 sqrt(M)-1],Nsymb,1);
SymbolIndexI = randi([0 sqrt(M)-1],Nsymb,1);
% Modulation
xmax = 14/(1+p); % found by iterative application of inverse_mapping
xQ = xmax*(2*rand(1)-1); % seed initial state
xI = xmax*(2*rand(1)-1); % seed initial state
x = zeros(Nsymb,1);
for i=1:Nsymb
xQ = inverse_mapping(xQ, SymbolIndexQ(i), p);
xI = inverse_mapping(xI, SymbolIndexI(i), p);
x(i) = xQ + sqrt(-1)*xI;
end
% x holds the modulated symbols
plot(real(x), imag(x), '.');
% if you also need the unmodulated symbols you can get them from
% SymbolIndexQ and SymbolIndexI
s = (2*SymbolIndexQ-7) + sqrt(-1)*(2*SymbolIndexI-7);
with should produce the corresponding constellation diagram:
or with p=1 (which is essentially unmodulated):
I am receiving the following error message:
Attempted to access sym(67); index out of bounds because numel(sym)=2.
I have been working on this for three days. I looked for similar error, but it didn't help. My code is below:
filename='DriveCyclesCP.xlsx';
V=xlsread('DriveCyclesCP.xlsx',2,'C9:C774'); % Get the velocity values, they are in an array V.
N=length(V); % Find out how many readings
mass = 1700 ; % Vehicle mass+ two 70 kg passengers.
area_Cd = 0.75; % Frontal area in square metres
Crr=0.009; %rolling resistance
g=9.8; % gravity acceleration
T=774; %UDDS cycle time duration
V_ave = 21.5; % UDDS avearage speed im m/s
rd=0.3; % Effective tire radius
Qhv =12.22; % E85 low Heating value in kWh/kg
Vd = 2.189; % engine size in L
md=0.801; % mass density of Ethanol
mf =Vd*md; % mf is the fuel mass consumed per cycle
Per = zeros(1,N); % engine power for each point of the drive cycle
a = zeros(1,N); % acceleration
SFC = zeros(1,N); % specific fuel consumption
Wc = zeros (1,N); % mass flow rate
nf = zeros (1,N); %fuel efficiency
Pm = zeros (1,N); % motor power
Pt = zeros (1,N);
Te =zeros (1,N); % Engine Troque
Tt = zeros (1,N);
Tm =zeros (1,N);
we =zeros (1,N); % Engine rot speed
wt = zeros (1,N);
wm =zeros (1,N);
S =zeros (1,8);
int (sym ('C'));
for C=1:N
a(C)=V(C+1)-V(C);
Pt(C)= V(C)*(mass*g*Crr + (0.5*area_Cd*1.202*(V(C))^2) + mass*a(C))/1000;
Per(C)=(mass*g*Crr +0.5*area_Cd*1.202*(V(C))^2 +mass*g*0.03)/1000*0.85;% e
syms Te(C) Tt(C) Tm(C) wt(C) we(C) wm(C) k1 k2
S = solve( Pm(C)==Pt(C) - Per(C), Tt(C)*wt(C)== Pt(C), Tt(C)*wt(C)== Te(C)*we(C) + Tm(C)*wm(C), wt(C)==we(C)/k1, wt(C)==wm(C)/k2, Pm(C)==wm(C) *Tm(C), Per(C)==we(C) *Te(C), Tt == k1*Te + k2*Tm );
end
The problem is on the line
int (sym ('C'));
You have defined sym to be a matrix with 2 entries somewhere (either earlier in the code or in a previous mfile), thus it treats sym as a matrix instead of a function. Thus when Matlab gets to the statement sym('C') it first converts the character 'C' to its ASCII integer representation (this just happens to be the number 67), then it tries to calculate sym(67) which is impossible as sym only has 2 elements.
Thus you have to stop sym from being a matrix (variable) and let it be a function again. There are two ways to solve this, either you can start you file with the statement clear;, this will remove all variables in memory, which might not be what you want; or you can use a function instead of script, as this hides all variables that have been defined previously and prevents this sort of error.
Note the line numel(X) is a way to measure how many elements are in X. Thus numel(sym)=2 means that sym has 2 elements.
P.S. There is an error in the lines (notice that I only taken some of the lines of you code)
N=length(V); % Find out how many readings
for C=1:N
a(C)=V(C+1)-V(C);
end
When C becomes equal to N, then V(C+1) will generate an error.
Suppose I have a vector J of jump sizes and an initial starting point X_0. Also I have boundaries 0, B (assume 0 < X_0 < B). I want to do a random walk where X_i = [min(X_{i-1} + J_i,B)]^+. (positive part). Basically if it goes over a boundary, it is made equal to the boundary. Anyone know a vectorized way to do this? The current way I am doing it consists of doing cumsums and then finding places where it violates a condition, and then starting from there and repeating the cumsum calculation, etc until I find that I stop violating the boundaries. It works when the boundaries are rarely hit, but if they are hit all the time, it basically becomes a for loop.
In the code below, I am doing this across many samples. To 'fix' the ones that go out of the boundary, I have to loop through the samples to check...(don't think there is a vectorized 'find')
% X_init is a row vector describing initial resource values to use for
% each sample
% J is matrix where each col is a sequence of Jumps (columns = sample #)
% In this code the jumps are subtracted, but same thing
X_intvl = repmat(X_init,NumJumps,1) - cumsum(J);
X = [X_init; X_intvl];
for sample = 1:NumSamples
k = find(or(X_intvl(:,sample) > B, X_intvl(:,sample) < 0),1);
while(~isempty(k))
change = X_intvl(k-1,sample) - X_intvl(k,sample);
X_intvl(k:end,sample) = X_intvl(k:end,sample)+change;
k = find(or(X_intvl(:,sample) > B, X_intvl(:,sample) < 0),1);
end
end
Interesting question (+1).
I faced a similar problem a while back, although slightly more complex as my lower and upper bound depended on t. I never did work out a fully-vectorized solution. In the end, the fastest solution I found was a single loop which incorporates the constraints at each step. Adapting the code to your situation yields the following:
%# Set the parameters
LB = 0; %# Lower bound
UB = 5; %# Upper bound
T = 100; %# Number of observations
N = 3; %# Number of samples
X0 = (1/2) * (LB + UB); %# Arbitrary start point halfway between LB and UB
%# Generate the jumps
Jump = randn(N, T-1);
%# Build the constrained random walk
X = X0 * ones(N, T);
for t = 2:T
X(:, t) = max(min(X(:, t-1) + Jump(:, t-1), UB), 0);
end
X = X';
I would be interested in hearing if this method proves faster than what you are currently doing. I suspect it will be for cases where the constraint is binding in more than one or two places. I can't test it myself as the code you provided is not a "working" example, ie I can't just copy and paste it into Matlab and run it, as it depends on several variables for which example (or simulated) values are not provided. I tried adapting it myself, but couldn't get it to work properly?
UPDATE: I just switched the code around so that observations are indexed on columns and samples are indexed on rows, and then I transpose X in the last step. This will make the routine more efficient, since Matlab allocates memory for numeric arrays column-wise - hence it is faster when performing operations down the columns of an array (as opposed to across the rows). Note, you will only notice the speed-up for large N.
FINAL THOUGHT: These days, the JIT accelerator is very good at making single loops in Matlab efficient (double loops are still pretty slow). Therefore personally I'm of the opinion that every time you try and obtain a fully-vectorized solution in Matlab, ie no loops, you should weigh up whether the effort involved in finding a clever solution is worth the slight gains in efficiency to be made over an easier-to-obtain method that utilizes a single loop. And it is important to remember that fully-vectorized solutions are sometimes slower than solutions involving single loops when T and N are small!
I'd like to propose another vectorized solution.
So, first we should set the parameters and generate random Jumpls. I used the same set of parameters as Colin T Bowers:
% Set the parameters
LB = 0; % Lower bound
UB = 20; % Upper bound
T = 1000; % Number of observations
N = 3; % Number of samples
X0 = (1/2) * (UB + LB); % Arbitrary start point halfway between LB and UB
% Generate the jumps
Jump = randn(N, T-1);
But I changed generation code:
% Generate initial data without bounds
X = cumsum(Jump, 2);
% Apply bounds
Amplitude = UB - LB;
nsteps = ceil( max(abs(X(:))) / Amplitude - 0.5 );
for ii = 1:nsteps
ind = abs(X) > (1/2) * Amplitude;
X(ind) = Amplitude * sign(X(ind)) - X(ind);
end
% Shifting X
X = X0 + X;
So, instead of for loop I'm using cumsum function with smart post-processing.
N.B. This solution works significantly slower than Colin T Bowers's one for tight bounds (Amplitude < 5), but for loose bounds (Amplitude > 20) it works much faster.