Updating constraint set rhs in docplex - optimization

I am trying to update the rhs of a constraint iteratively under a while loop in docplex, however, it does not work properly. As I analyze the output text file, while some constraints are updated as I want, others are not.
z_bar is a list consists of y+1 elements and is updated at each iteration. Constraint set I want to change the RHS of consists of (x+1)*(y+1) constraints. z_bar is related to the j indice, however since each constraint involves i and j indices, I have to update all. What do you think I am doing wrong?
Original constraint set:
for i in range(1, x + 1):
for j in range(1, y + 1):
sub_cbd.add_constraint(x_cbd[i, j] <= z_bar[j], ctname='constraint_name{0}{1}'.format(i, j))
Updating constraint set rhs attempt:
for i in range(1, x + 1):
for j in range(1, y + 1):
sub_cbd.get_constraint_by_name('constraint_name{0}{1}'.format(i, j)).rhs = z_bar[j]
For the updated z_bar: [0, 0, 0, 0, 1, 1, 0...0], 15 elements in total, two 1s and 13 0s.
How it looks now:
constraint11: x_1_1 <= 0
constraint12: x_1_2 <= 0
constraint13: x_1_3 <= 0
constraint14: x_1_4 <= 0
constraint15: x_1_5 <= 1
constraint16: x_1_6 <= 1
constraint17: x_1_7 <= 0
constraint18: x_1_8 <= 0
constraint19: x_1_9 <= 0
constraint110: x_1_10 <= 0
constraint111: x_1_11 <= 1
constraint112: x_1_12 <= 1
constraint113: x_1_13 <= 1
constraint114: x_1_14 <= 1
constraint115: x_1_15 <= 1
constraint21: x_2_1 <= 0
constraint22: x_2_2 <= 0
constraint23: x_2_3 <= 0
constraint24: x_2_4 <= 0
constraint25: x_2_5 <= 1
constraint26: x_2_6 <= 1
constraint27: x_2_7 <= 0
constraint28: x_2_8 <= 0
constraint29: x_2_9 <= 0
constraint210: x_2_10 <= 0
constraint211: x_2_11 <= 1
constraint212: x_2_12 <= 1
constraint213: x_2_13 <= 1
constraint214: x_2_14 <= 1
constraint215: x_2_15 <= 1
How it should look:
constraint11: x_1_1 <= 0
constraint12: x_1_2 <= 0
constraint13: x_1_3 <= 0
constraint14: x_1_4 <= 0
constraint15: x_1_5 <= 1
constraint16: x_1_6 <= 1
constraint17: x_1_7 <= 0
constraint18: x_1_8 <= 0
constraint19: x_1_9 <= 0
constraint110: x_1_10 <= 0
constraint111: x_1_11 <= 0
constraint112: x_1_12 <= 0
constraint113: x_1_13 <= 0
constraint114: x_1_14 <= 0
constraint115: x_1_15 <= 0
constraint21: x_2_1 <= 0
constraint22: x_2_2 <= 0
constraint23: x_2_3 <= 0
constraint24: x_2_4 <= 0
constraint25: x_2_5 <= 1
constraint26: x_2_6 <= 1
constraint27: x_2_7 <= 0
constraint28: x_2_8 <= 0
constraint29: x_2_9 <= 0
constraint210: x_2_10 <= 0
constraint211: x_2_11 <= 0
constraint212: x_2_12 <= 0
constraint213: x_2_13 <= 0
constraint214: x_2_14 <= 0
constraint215: x_2_15 <= 0

Full working example in https://github.com/AlexFleischerParis/zoodocplex/blob/master/zooincremental.py
from docplex.mp.model import Model
# original model
mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
mdl.add_constraint(nbbus40*40 + nbbus30*30 >= 300, 'kids')
mdl.minimize(nbbus40*500 + nbbus30*400)
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
#now 350 kids instead of 300
print()
print("now 350 kids instead of 300")
mdl.get_constraint_by_name("kids").rhs=350;
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
# no more than 4 buses 40 seats
print()
print("no more than 4 buses 40 seats")
mdl.get_var_by_name("nbBus40").ub=4
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
#change the objective so that cost for 40 seats is 450
#and remove the limit on the number of buses 40 seats
print()
print("change the objective so that cost for 40 seats is 450")
print("and remove the limit on the number of buses 40 seats ")
mdl.get_var_by_name("nbBus40").ub=1000
mdl.set_objective("min",nbbus40*450 + nbbus30*400);
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
Now let me change this into a loop:
from docplex.mp.model import Model
# original model
mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
for i in range(0,10):
mdl.add_constraint(nbbus40*40 + nbbus30*30*i >= 300+i,'kids'+str(i))
mdl.minimize(nbbus40*500 + nbbus30*400)
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
#now 350 kids instead of 300
for i in range(0,10):
mdl.get_constraint_by_name("kids"+str(i)).rhs=350+i;
mdl.export("zoo.lp")
mdl.solve()
and then in zoo.lp I get
Minimize
obj: 500 nbBus40 + 400 nbBus30
Subject To
kids0: 40 nbBus40 >= 350
kids1: 40 nbBus40 + 30 nbBus30 >= 351
kids2: 40 nbBus40 + 60 nbBus30 >= 352
kids3: 40 nbBus40 + 90 nbBus30 >= 353
kids4: 40 nbBus40 + 120 nbBus30 >= 354
kids5: 40 nbBus40 + 150 nbBus30 >= 355
kids6: 40 nbBus40 + 180 nbBus30 >= 356
kids7: 40 nbBus40 + 210 nbBus30 >= 357
kids8: 40 nbBus40 + 240 nbBus30 >= 358
kids9: 40 nbBus40 + 270 nbBus30 >= 359
Bounds
Generals
nbBus40 nbBus30
End

Related

Rounding Non-LinearExpr with google or-tools SAT solver

Using CP-SAT of google or-tools I'm trying to write this constraint:
q >= (50x + 100y + 150z + 200k + 250p + 300v) / (x + y + z + k + p + v)
Where q is a simple integer.
The thing is I need to round the right side of the equation (let's call it expression) as follows:
if(expression < 75) {
expression = 50;
} else if(expression < 125) {
expression = 100;
} else if(expression < 175) {
expression = 150;
} else if(expression < 225) {
expression = 200;
} else if(expression < 275) {
expression = 250;
} else {
expression = 300;
}
So I need to round the expression
(50x + 100y + 150z + 200k + 250p + 300v) / (x + y + z + k + p + v)
So that it gets one of the following values:
{50, 100, 150, 200, 250, 300}
Let's review 2 cases:
Case 1
q = 180 and expression = 176.
Although the condition 180 >= 176 is true, after rounding up 176 to 200 the tested condition should be 180 >= 200 which is false.
So for q = 180 and expression = 176 I would like the condition to return false.
Case 2
q = 210 and expression = 218.
Although the condition 210 >= 218 is false, after rounding down 218 to 200 the tested condition should be 210 >= 200 which is true.
So for q = 210 and expression = 218 I would like the condition to return true.
I got a great answer here for resolving this challenge over a linear expression but now I need to resolve it for a non-linear expression.
Any suggestions?
Let's rephrase
you have an integer variable e with a value between 0 and 300.
You want to round it to the nearest multiple of 50
if you do:
(e div 50) * 50
you will get the max multiple of 50 less or equal to e
(70 / 50) * 50 -> 50
(99 / 50) * 50 -> 50
(102 / 50) * 50 -> 100
To get a round to nearest, you need to add 25 to e before the division
((e + 25) div 50) * 50
Which will do the correct rounding
((70 + 25) / 50) * 50 -> 50
((99 + 25) / 50) * 50 -> 100
((102 + 25) / 50) * 50 -> 100
with the correct or-tools CP-SAT python code:
numerator = model.NewIntVar(...)
model.Add(numerator == 50x + 100y + 150z + 200k + 250p + 300v)
denom = model.NewIntVar(...)
model.Add(denom == 50x + 100y + 150z + 200k + 250p + 300v)
e = model.NewIntVar(0, 300, 'e')
model.AddDivisionEquality(e, numerator, denom)
shifted_e = model.NewIntVar(25, 325, 'shifted_e')
model.Add(shifted_e == e + 25)
multiple_of_fifty = model.NewIntVar(0, 6, 'multiple_of_fifty')
model.AddDivisionEquality(multiple_of_fifty, shifted_e, 50)
result = model.NewIntVar(0, 300, 'result')
model.Add(result = multiple_of_fifty * 50)
if a and b are positive then
a div b >= q
is equivalent to
a >= q * b
now, your example does not specify how to round (nearest or down)
if you want to round down
q * (x + y + z + k + p + v) <= (50x + 100y + 150z + 200k + 250p + 300v)
If you want to round to nearest, you need to add q / 2 in the right place
q * (x + y + z + k + p + v) <= (50x + 100y + 150z + 200k + 250p + 300v + q / 2)
Now, if you want the other direction
a div b <= q
is equivalent to
a <= q * b + q - 1
The rest of the transformation is the same.

Recursive scalar function

I'm attempting to rebuild some VBA functions in SQL server 2016 and I'm having difficult making a recursive function work properly.
My attempt below is returning NULL and when I step through the function in debug #returnValue never seems to hold a value even though it appears to recurse correctly.
Can someone please advise how to fix this.
Example expected results:
select [dbo].TickDiff(1.01, 1.05) -- Expect result 4
select [dbo].TickDiff(2.02, 16) -- Expect result 121
Functions:
CREATE FUNCTION [dbo].TickDiff (#odds1 float, #odds2 float)
RETURNS float
AS
BEGIN
DECLARE #returnValue as float
IF (#odds1 > #odds2)
BEGIN
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksDown(#odds1), #odds2) + 1
END
ELSE
IF (#odds1 < #odds2)
BEGIN
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksUp(#odds1), #odds2) + 1
END
RETURN #returnValue;
END
GO
CREATE FUNCTION [dbo].TicksUp(
#odds float
)
RETURNS float
AS
BEGIN
RETURN
case
when #odds < 1 then 1.02
when #odds >= 1 and #odds <= 1.99 then #odds + 0.01
when #odds >= 2 and #odds <= 2.98 then #odds + 0.02
when #odds >= 3 and #odds <= 3.95 then #odds + 0.05
when #odds >= 4 and #odds <= 5.9 then #odds + 0.1
when #odds >= 6 and #odds <= 9.8 then #odds + 0.2
when #odds >= 10 and #odds <= 19.5 then #odds + 0.5
when #odds >= 20 and #odds <= 29 then #odds + 1.0
when #odds >= 30 and #odds <= 48 then #odds + 2.0
when #odds >= 50 and #odds <= 95 then #odds + 5.0
when #odds >= 100 and #odds < 1000 then #odds + 10
when #odds >= 1000 then 1000
end
END
GO
CREATE FUNCTION [dbo].TicksDown(
#odds float
)
RETURNS float
AS
BEGIN
RETURN
case
when #odds <= 1.01 then 1.01
when #odds >= 1.01 and #odds <= 2 then #odds - 0.01
when #odds >= 2.02 and #odds <= 3 then #odds - 0.02
when #odds >= 3.05 and #odds <= 4 then #odds - 0.05
when #odds >= 4.1 and #odds <= 6 then #odds - 0.1
when #odds >= 6.2 and #odds <= 10 then #odds - 0.2
when #odds >= 10.5 and #odds <= 20 then #odds - 0.5
when #odds >= 21 and #odds <= 30 then #odds - 1.0
when #odds >= 32 and #odds <= 50 then #odds - 2.0
when #odds >= 55 and #odds <= 100 then #odds - 5.0
when #odds >= 110 and #odds < 1000 then #odds - 10
when #odds >= 1000 then 990
end
END
GO
The VBA functions I'm trying to copy are below:
Function TicksDown(ByVal odds As Currency) As Currency
Dim IncrementOdds As Currency
Select Case odds
Case 1.01 To 2
IncrementOdds = 0.01
Case 2.02 To 3
IncrementOdds = 0.02
Case 3.05 To 4
IncrementOdds = 0.05
Case 4.1 To 6
IncrementOdds = 0.1
Case 6.2 To 10
IncrementOdds = 0.2
Case 10.5 To 20
IncrementOdds = 0.5
Case 21 To 30
IncrementOdds = 1
Case 32 To 50
IncrementOdds = 2
Case 55 To 100
IncrementOdds = 5
Case 110 To 1000
IncrementOdds = 10
End Select
If Math.Round(odds - IncrementOdds, 2) >= 1.01 Then
TicksDown = Math.Round(odds - IncrementOdds, 2)
Else
TicksDown = 1.01
End If
End Function
Function TicksUp(ByVal odds As Currency) As Currency
Dim IncrementOdds As Currency
Select Case odds
Case 1 To 1.99
IncrementOdds = 0.01
Case 2 To 2.98
IncrementOdds = 0.02
Case 3 To 3.95
IncrementOdds = 0.05
Case 4 To 5.9
IncrementOdds = 0.1
Case 6 To 9.8
IncrementOdds = 0.2
Case 10 To 19.5
IncrementOdds = 0.5
Case 20 To 29
IncrementOdds = 1
Case 30 To 48
IncrementOdds = 2
Case 50 To 95
IncrementOdds = 5
Case 100 To 1000
IncrementOdds = 10
End Select
If Math.Round(odds + IncrementOdds, 2) <= 1000 Then
TicksUp = Math.Round(odds + IncrementOdds, 2)
Else
TicksUp = 1000
End If
End Function
Function TickDiff(odds1 As Currency, odds2 As Currency) As Long
If odds1 > odds2 Then
odds1 = TicksDown(odds1)
TickDiff = TickDiff(odds1, odds2) + 1
ElseIf odds1 < odds2 Then
odds1 = TicksUp(odds1)
TickDiff = TickDiff(odds1, odds2) + 1
Else
'Found, Exit Recursive Function
End If
End Function
You need to set your #ReturnValue to zero:
DECLARE #returnValue as float = 0.0
That being said, your second example exceeds SQLs max recursion of 32. You may want to rewrite without recursion. Here's an example: http://www.sql-server-helper.com/error-messages/msg-217.aspx
Your #returnValue is not initialized , so it is null. And
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksDown(#odds1), #odds2) + 1
evaluates to null.
Try
CREATE FUNCTION [dbo].TickDiff (#odds1 float, #odds2 float)
RETURNS float
AS
BEGIN
DECLARE #returnValue as float = 0.;
IF (#odds1 > #odds2)
BEGIN
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksDown(#odds1), #odds2) + 1
END
ELSE
IF (#odds1 < #odds2)
BEGIN
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksUp(#odds1), #odds2) + 1
END
RETURN #returnValue;
END
Also case expressions can be simplified because sql-server evaluates when conditions in the order specified.
CREATE FUNCTION [dbo].TicksUp(
#odds float
)
RETURNS float
AS
BEGIN
RETURN
case
when #odds < 1 then 1.02
when #odds <= 1.99 then #odds + 0.01
when #odds <= 2.98 then #odds + 0.02
when #odds <= 3.95 then #odds + 0.05
when #odds <= 5.9 then #odds + 0.1
when #odds <= 9.8 then #odds + 0.2
when #odds <= 19.5 then #odds + 0.5
when #odds <= 29 then #odds + 1.0
when #odds <= 48 then #odds + 2.0
when #odds <= 95 then #odds + 5.0
when #odds < 1000 then #odds + 10
when #odds >= 1000 then 1000
end
END

Conditional if sum

I want to sum over a range (Areas) except if j is not equal to k.
Can anybody help me?
I tried:
forall( k in Areas )
sum ( j in Areas: j!=k ) X[k][j] == 1;
Also tried:
forall( k in Areas )
sum ( j in Areas) (j!=k)*X[k][j] == 1;
int NbAreas = 5;
range Areas = 1..NbAreas;
float P[Areas] = [0, 0.3, 0.65, 0.2, 0.1];
float D[Areas] = [0, 7, 5, 3, 9];
float FROMTO[Areas][Areas] = [
[0, 2, 5, 1, 3],
[2, 0, 4, 3, 8],
[5, 4, 0, 6, 2],
[1, 3, 6, 0, 7],
[3, 8, 2, 7, 0]];
dvar int Y[Areas];
dvar int T[Areas];
dvar int X[Areas][Areas] in 0..1;
maximize sum( i in Areas ) P[i] * Y[i];
subject to {
forall( k in Areas )
sum ( j in Areas: j!=k) X[k][j] == 1;
forall( k in Areas)
sum ( i in Areas: i!=k) X[i][k] == 1;
forall( i in Areas) forall (j in Areas) T[i] + FROMTO[i][j] - T[j] - 100*(1-X[i][j]) <= 0;
T[1] == 0;
forall( i in Areas: i!=1) T[i] - D[i] - 1000*(1-Y[i]) <= 0;
}
I take it you meant
I want to sum over a range (Areas) except if j is equal (instead
of "not equal") to k.
I also assume that your issue is that the model you posted is infeasible. You should label your constraints so that the conflict refiner can run and then look at the results of the conflict refiner. If I label your constraints like so:
maximize sum( i in Areas ) P[i] * Y[i];
subject to {
forall( k in Areas )
sum1: sum ( j in Areas: j!=k) X[k][j] == 1;
forall( k in Areas)
sum2: sum ( i in Areas: i!=k) X[i][k] == 1;
forall( i in Areas) forall (j in Areas)
fromto: T[i] + FROMTO[i][j] - T[j] - 100*(1-X[i][j]) <= 0;
T[1] == 0;
forall( i in Areas: i!=1)
limit: T[i] - D[i] - 1000*(1-Y[i]) <= 0;
Then I get this conflict:
sum1(1): X(1)(2) + X(1)(3) + X(1)(4) + X(1)(5) = 1
sum1(2): X(2)(1) + X(2)(3) + X(2)(4) + X(2)(5) = 1
sum1(3): X(3)(1) + X(3)(2) + X(3)(4) + X(3)(5) = 1
sum1(4): X(4)(1) + X(4)(2) + X(4)(3) + X(4)(5) = 1
sum1(5): X(5)(1) + X(5)(2) + X(5)(3) + X(5)(4) = 1
fromto(1)(2): 100 X(1)(2) + T(1) - T(2) <= 98
fromto(1)(3): 100 X(1)(3) + T(1) - T(3) <= 95
fromto(1)(4): 100 X(1)(4) + T(1) - T(4) <= 99
fromto(1)(5): 100 X(1)(5) + T(1) - T(5) <= 97
fromto(2)(1): 100 X(2)(1) - T(1) + T(2) <= 98
fromto(2)(3): 100 X(2)(3) + T(2) - T(3) <= 96
fromto(2)(4): 100 X(2)(4) + T(2) - T(4) <= 97
fromto(2)(5): 100 X(2)(5) + T(2) - T(5) <= 92
fromto(3)(1): 100 X(3)(1) - T(1) + T(3) <= 95
fromto(3)(2): 100 X(3)(2) - T(2) + T(3) <= 96
fromto(3)(4): 100 X(3)(4) + T(3) - T(4) <= 94
fromto(3)(5): 100 X(3)(5) + T(3) - T(5) <= 98
fromto(4)(1): 100 X(4)(1) - T(1) + T(4) <= 99
fromto(4)(2): 100 X(4)(2) - T(2) + T(4) <= 97
fromto(4)(3): 100 X(4)(3) - T(3) + T(4) <= 94
fromto(4)(5): 100 X(4)(5) + T(4) - T(5) <= 93
fromto(5)(1): 100 X(5)(1) - T(1) + T(5) <= 97
fromto(5)(2): 100 X(5)(2) - T(2) + T(5) <= 92
fromto(5)(3): 100 X(5)(3) - T(3) + T(5) <= 98
fromto(5)(4): 100 X(5)(4) - T(4) + T(5) <= 93
So it seems your data in FROMTO and T does not allow a feasible solution.
You wrote "except if j is not equal to k".
So instead of
forall( k in Areas)
sum ( i in Areas: i!=k) X[i][k] == 1;
I would write
forall( k in Areas)
sum ( i in Areas: i==k) X[i][k] == 1;

Display commend in ampl

I have a 2 dimension variable in ampl and I want to display it. I want to change the order of the indices but I do not know how to do that! I put my code , data and out put I described what kind of out put I want to have.
Here is my code:
param n;
param t;
param w;
param p;
set Var, default{1..n};
set Ind, default{1..t};
set mode, default{1..w};
var E{mode, Ind};
var B{mode,Var};
var C{mode,Ind};
param X{mode,Var,Ind};
var H{Ind};
minimize obj: sum{m in mode,i in Ind}E[m,i];
s.t. a1{m in mode, i in Ind}: sum{j in Var} X[m,j,i]*B[m,j] -C[m,i] <=E[m,i];
solve;
display C;
data;
param w:=4;
param n:=9;
param t:=2;
param X:=
[*,*,1]: 1 2 3 4 5 6 7 8 9 :=
1 69 59 100 70 35 1 1 0 0
2 34 31 372 71 35 1 0 1 0
3 35 25 417 70 35 1 0 0 1
4 0 10 180 30 35 1 0 0 0
[*,*,2]: 1 2 3 4 5 6 7 8 9 :=
1 64 58 68 68 30 2 1 0 0
2 44 31 354 84 30 2 0 1 0
3 53 25 399 85 30 2 0 0 1
4 0 11 255 50 30 2 0 0 0
The output of this code using glpksol is like tis:
C[1,1].val = -1.11111111111111
C[1,2].val = -1.11111111111111
C[2,1].val = -0.858585858585859
C[2,2].val = -1.11111111111111
C[3,1].val = -0.915032679738562
C[3,2].val = -1.11111111111111
C[4,1].val = 0.141414141414141
C[4,2].val = 0.2003367003367
but I want the result to be like this:
C[1,1].val = -1.11111111111111
C[2,1].val = -0.858585858585859
C[3,1].val = -0.915032679738562
C[4,1].val = 0.141414141414141
C[1,2].val = -1.11111111111111
C[2,2].val = -1.11111111111111
C[3,2].val = -1.11111111111111
C[4,2].val = 0.2003367003367
any idea?
You can use for loops and printf commands in your .run file:
for {i in Ind}
for {m in mode}
printf "C[%d,%d] = %.4f\n", m, i, C[m,i];
or even:
printf {i in Ind, m in mode} "C[%d,%d] = %.4f\n", m, i, C[m,i];
I don't get the same numerical results as you, but anyway the output works:
C[1,1] = 0.0000
C[2,1] = 0.0000
C[3,1] = 0.0000
C[4,1] = 0.0000
C[1,2] = 0.0000
C[2,2] = 0.0000
C[3,2] = 0.0000
C[4,2] = 0.0000

VBA if else doesn't return correct values

Trying to return a L, M, H value based on the values of two inputs. Here is what I have:
If (80 <= x <= 120 And y > 120) Or (x > 120 And y > 120) Or (x > 120 And 80 <= y <= 120) Then
CDI = "H"
ElseIf (x < 80 And y <= 120) Or (x < 120 And y < 80) Then
CDI = "L"
ElseIf (x < 80 And y > 120) Or (80 <= x <= 120 And 80 <= y <= 120) Or (x > 120 And y < 80) Then
CDI = "M"
End If
The values being returned aren't what I need. For example, (0,291) returns H when it should clearly be M. Why?
The conditions with the variable in the middle (even if they are acceptable to the VBA interpreter) are almost certainly not going to produce the result you expect. Change all such conditions to the equivalent of the form:
variable condition constant
Example: Change
80 <= x <= 120
to
x >= 80 and x <= 120
Try this:
If (x >= 80 And x <= 120 And y > 120) Or (x > 120 And y > 120) Or (x > 120 And y >= 80 And y <= 120) Then
CDI = "H"
ElseIf (x < 80 And y <= 120) Or (x < 120 And y < 80) Then
CDI = "L"
ElseIf (x < 80 And y > 120) Or (x >= 80 And x <= 120 And y >= 80 And y <= 120) Or (x > 120 And y < 80) Then
CDI = "M"
End If