Two Loop Network on Gams - gams-math

Regarding the Two Loop Network formulation on GAMS, I'm struggling with one of the equations.
I can solve the problem without the energy conservation loop constraint but, once i add it, the problem becomes infeasible. Also, I'm not sure if the two loops are well defined
I would apprecite if someone spots my error.
Thank you.
Set
n 'nodes' / 1, 2, 3, 4, 5, 6, 7 /
a(n,n) 'arcs/pipes arbitrarly directed'
/1.2, 4.(2,5,6), 3.(2,5), 7.(5,6)/
rn(n) 'reservoir' / 1 /
dn(n) 'demand nodes' /2, 3, 4, 5, 6, 7/
m 'number of loops' /c1, c2/
c1(n,n) 'loop 1'
/2.(3,4), 5.(3,4)/
c2(n,n) 'loop 2'
/4.(5,6), 7.(5,6)/
k 'Options available for the diameters'
/ k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14 /;
dn(n) = yes;
dn(rn) = no;
display a;
display dn;
display rn;
display m;
display c1;
display c2;
Alias(n,np);
Table node(n,*) 'node data'
demand elevation minhead
* m^3/sec m m
1 210 30
2 0.0278 150 30
3 0.0278 160 30
4 0.0333 155 30
5 0.0750 150 30
6 0.0917 165 30
7 0.0444 200 30 ;
display node;
Table Diam(k,*) 'Diameter and cost information'
Diameter Cost
* m $/m
k1 0.0254 2
k2 0.0508 5
k3 0.0762 8
k4 0.1016 11
k5 0.1524 16
k6 0.2032 23
k7 0.2540 32
k8 0.3048 50
k9 0.3556 60
k10 0.4064 90
k11 0.4572 130
k12 0.5080 170
k13 0.5588 300
k14 0.6096 550;
Scalar
length 'pipes diameter' /1000/
roughcoef 'roughness coefficient for every pipe' /130/
Vmin 'Minimum velocity (m/s)' /0.3/
Vmax 'Maximum velocity (m/s)' /3.0/
dmin 'minimum diameter of pipe' /0.0254/
dmax 'maximum diameter of pipe' /0.6096/
davg 'Diamter Average for starting point';
davg = sqrt(dmin*dmax);
Variable
x(n,n) 'absolute flow through each arc'
y(n,n,k) 'takes value 1 when a pipe in arc(n,n) has diameter e(k) and 0 otherwise'
t(n,n) 'auxiliary variable for modeling the flow going in the forward direction'
r(n,n) 'auxiliary variable for modeling the flow going in the reverse direction'
u(n) 'variable representing the head of node n'
d(n,n) 'representing the diameter of pipe in link (n,n), takes the same value as some e(n)'
v(n,n) 'Water velocity'
q(n,n)'real variable representing the flow direction by being the flow sign, being 1 if flow goes forward or −1 if in reverse direction for a link (n,n)'
Custo 'total cost';
Binary Variable y, t, r;
NonNegative Variable x, d;
Equation
UniPipeDiam(n,n) 'Unique Pipe Diameter'
PipeDiam(n,n) 'Pipe Diameter Real Value'
FlowDirection(n,n) 'Flow Direction'
FlowSign(n,n) 'Flow Sign'
FlowConservation(n) 'Flow Conservation at Each Node'
HeadLoss(n,n) 'Head Loss'
EnerConserLoop1(n,np) 'Energy Conservation in Loop 1'
EnerConserLoop2(n,np) 'Energy Conservation in Loop 2'
Objective 'Objective Function: Capital Cost'
Velocity(n,n) 'Velocity calculation'
VelocUp(n,np) 'Upper bound velocity'
VelocDown(n,np) 'Lower bound velocity';
UniPipeDiam(a).. sum(k, y(a,k)) =e= 1;
PipeDiam(a(n,np)).. d(n,np) =e= sum(k, Diam(k,'Diameter')*y(n,np,k));
FlowDirection(a(n,np)).. t(a) + r(a) =e= 1;
FlowSign(a(n,np)).. q(a) =e= t(a) - r(a);
FlowConservation(dn(n)).. sum(a(np,n), x(a)*q(a)) - sum(a(n,np), x(a)*q(a)) =e= node(n,"demand");
HeadLoss(a(n,np)).. u(n) - u(np) =e= [10.667]*[roughcoef**(-1.852)]*length*[d(a)**(-4.8704)]*[x(a)**(2)]*q(a);
Velocity(a(n,np)).. v(a) =e= (4.0*x(a))/(Pi*d(a)**2.0);
VelocUp(a).. v(a) =l= Vmax;
VelocDown(a).. v(a) =g= Vmin;
EnerConserLoop1(n,np).. sum(a(n,np)$c1(n,np), q(a) * (u(n) - u(np))) =e= 0;
EnerConserLoop2(n,np).. sum(a(n,np)$c2(n,np),q(a) * (u(n) - u(np))) =e= 0;
Objective.. Custo =e= sum(a(n,np), sum(k, length*Diam(k,'Cost')*y(n,np,k)));
*bounds
d.lo(n,np)$a(n,np) = dmin;
d.up(n,np)$a(n,np) = dmax;
u.lo(rn) = node(rn,"elevation");
u.lo(dn) = node(dn,"elevation") + 5.0 + 5.0*node(dn,"demand");
u.up(dn) = 300.0;
* initial values
d.l(n,np)$a(n,np) = davg;
u.l(n) = u.lo(n) + 5.0;
Model network / all /;
network.domLim = 1000;
Option Iterlim = 50000;
option MINLP = baron;
solve network using minlp minimizing Custo;

Related

Is this an issue with pre-solve or how AMPL uses arrays?

I am modeling a production problem in AMPL with start and end inventories to be zero.
Simple 3 products being produced over 4 day span, on a single machine.
however my model keeps having problems with the bigM constraint and no inventory is allocated.Model is infeasible per AMPL presolve.
I would really appreciate if someone could point whats wrong or what i should try. Thanks in advance!
HERE IS THE CODE -
param n;
param m;
param M;
param T;
set I := {1..n};
set J := {1..m};
param r {I} integer; #productionrate
param stp {I} integer; #setuptime
param rev {I} integer; #sellingprice
param h {I} integer; #inventorycost
param d {I,J} integer; #demand
var t {I,J} integer >= 0, <=480 default 50; #production time allocated for product i on day j
var s {I,J} integer >= 0 default 50; #inventory of product i on day j
var z {I,J} binary default 1; #setup for product i done on day j or not
#sum of revenue - sum of inventory cost
maximize K: sum {i in I,j in J}(rev[i] * t[i,j] * r[i] * z[i,j]) - sum {i in I,j in J}(h[i] * s[i,j]);
#inventory equation
s.t. c2 {i in I,j in 2..4}: s[i,j] = s[i,j-1] + t[i,j]*r[i] - d[i,j];
s.t. c14 {i in I}: s[i,1] = t[i,1]*r[i] - d[i,1];
#initial and end inventories
s.t. c3 {i in I}: s[i,1] = 0;
s.t. c4 {i in I}: s[i,4] = 0;
#ensuring time allocation only when setup
s.t. c5 {i in I,j in J}: t[i,j] <= M*z[i,j];
#ensuring demand is satisfied
s.t. c6 {i in I,j in 2..4}: s[i,j-1] + t[i,j]*r[i] = d[i,j];
s.t. c11 {i in I}: t[i,1]*r[i] = d[i,1];
#production time constraint
s.t. c7 {j in J}: sum{i in I}(z[i,j]*stp[i]) + sum{i in I}(t[i,j]) <= T;
#other non-negativity constraints
s.t. c12 {i in I,j in J}: s[i,j] >= 0;
#s.t. c13 {i in I,j in J}: t[i,j] >= 0;
end;
HERE IS THE DATA -
param n :=3;
param m :=4;
param T :=480;
param M :=480;
#param o :=2;
param d: 1 2 3 4 :=
1 400 600 200 800
2 240 440 100 660
3 80 120 40 100;
param r:=
1 5
2 4
3 2;
param stp:=
1 45
2 60
3 100;
param rev:=
1 50
2 70
3 120;
param h:=
1 2
2 1
3 3;
end;
RUN FILE -
#RESET THE AMPL ENVIROMENT
reset;
#LOAD THE MODEL
model 'EDX 15.053 Production problem.mod';
#LOAD THE DATA
data 'EDX 15.053 Production problem.dat';
#DISPLAY THE PROBLEM FORMULATION
expand K;
expand c2;
#expand c3;
expand c4;
expand c5;
expand c6;
expand c7;
expand c11;
#let s[3,3] := 20;
#CHANGE THE SOLVER (optional)
#option solver CBC;
option solver cplex, cplex_options ’presolve 0’;
option show_stats 1;
option send_statuses 0;
#option presolve 0;
#SOLVE
solve;
print {j in 1.._nvars:_var[j].status = "pre"}: _varname[j];
print {i in 1.._ncons:_con[i].status = "pre"}: _conname[i];
display solve_message;
display solve_result_num, solve_result;
#SHOW RESULTS
display t,s,z,K;
end;

Hard Big O complexity for 3 loops

I try to calculate Big O complexity for this code but I always fail....
I tried to nest SUM's or to get the number of steps for each case like:
i=1 j=1 k=1 (1 step)
i=2 j=1,2 k=1,2,3,4 (4 steps)
. . . . . . . . . . . . . . .
i=n (i said n = 2^(log n) j = 1,2,4,8,16,.....,n k=1,2,3,4,.....n^2 (n^2 steps)
then sum all the steps together, I need help.
for (int i=1; i<=n; i*=2)
for (int j=1; j<=i; j*=2)
for(int k=1; k<=j*j; k++)
//code line with complexity code O(1)
Let's take a look at the number of times the inner loop runs: j2. But j steps along in powers of 2 up to i. i in turn steps in powers of 2 up to n. So let's "draw" a little graphic of the terms of the sum that would give us the total number of iterations:
---- 1
^ 1 4
| 1 4 16
log2(n) ...
| 1 4 16 ... n2/16
v 1 4 16 ... n2/16 n2/4
---- 1 4 16 ... n2/16 n2/4 n2
|<------log2(n)------>|
The graphic can be interpreted as follows: each value of i corresponds to a row. Each value of j is a column within that row. The number itself is the number of iterations k goes through. The values of j are the square roots of the numbers. The values of i are the square roots of the last element in each row. The sum of all the numbers is the total number of iterations.
Looking at the bottom row, the terms of the sum are (2z)2 = 22z for z = 1 ... log2(n). The number of times that the terms appear in the sum is modulated by the height of the column. The height for a given term is log2(n) + 1 - z (basically a count down from log2(n)).
So the final sum is
log2(n)
Σ 22z(log2(n) + 1 - z)
z = 1
Here is what Wolfram Alpha has to say about evaluating the sum: http://m.wolframalpha.com/input/?i=sum+%28%28log%5B2%2C+n%5D%29+%2B+1+-+z%29%282%5E%282z%29%29%2C+z%3D1+to+log%5B2%2C+n%5D:
C1n2 - C2log(n) - C3
Cutting out all the less significant terms and constants, the result is
O(n2)
For the outermost loop:
sum_{i in {1, 2, 4, 8, 16, ...}} 1, i <= n (+)
<=>
sum_{i in {2^0, 2^1, 2^2, ... }} 1, i <= n
Let 2^I = i:
2^I = i <=> e^{I log 2} = i <=> I log 2 = log i <=> I = (log i)/(log 2)
Thus, (+) is equivalent to
sum_{I in {0, 1, ... }} 1, I <= floor((log n)/(log 2)) ~= log n (*)
Second outermost loop:
sum_{j in {1, 2, 4, 8, 16, ...}} 1, j <= i (++)
As above, 2^I = i, and let 2^J = j. Similarly to above,
(++) is equivalent to:
sum_{J in {0, 1, ... }} 1, J <= floor((log (2^I))/(log 2)) = floor(I/(log 2)) ~= I (**)
To touch base, only the outermost and second outermost
have now been reduced to
sum_{I in {0, 1, ... }}^{log n} sum_{J in {0, 1, ...}}^{I} ...
Which is (if there would be no innermost loop) O((log n)^2)
Innermost loop is a trivial one if we can express the largest bound in terms of `n`.
sum_{k in {1, 2, 3, 4, ...}} 1, k <= j^2 (+)
As above, let 2^J = j and note that j^2 = 2^(2J)
sum_{k in {1, 2, 3, 4, ...}} 1, k <= 2^(2J)
Thus, k is bounded by 2^(2 max(J)) = 2^(2 max(I)) = 2^(2 log(n) ) = 2n^2 (***)
Combining (*), (**) and (***), the asymptotic complexity of the three nested loops is:
O(n^2 log^2 n) (or, O((n log n)^2)).

Time complexity Recursive program

I can't figure out the time complexety of the following snippet of code:
void f( int N ){
sum ++;
if ( N > 1){
f( N /2);
f( N /2);
}
It's the double iteration that gives me problems.
I know (or think) that the time complexity of
void f( int N ){
sum ++;
if ( N > 1){
f( N /2);
}
is ~log2(N) but don't know what to do with the other code.
You are calling the recursion twice on (N/2). Lets write the formula:
T(n) = 2*(N/2) + 1
Using master theorem, we fall on the first case where:
a=2
f = O(1)
b=2
T(n) = Θ(n)
We also find T(n) = 2*T(n/2) + 1 here, which shows it is bounded by O(n)
A good way to solve this problem would be:
1. Finding the recurrence relation
For each call to f we have time complexity T(N). Each call contains:
A constant amount of work C: sum++, comparison N > 1, recursive calling overhead, etc.
2 recursive calls to f, each with time complexity T(N / 2).
Thus the recurrence relation is given by T(N) = 2T(N/2) + C.
2. Finding a solution by inspection
If we repeatedly substitute T(N) into itself, we can see an emerging pattern:
What is the upper limit of the summation, m? Since the stopping condition is N > 1, after repeated substitutions the requirement would be
Thus the summation equals (dropping the round-down, and the constant C because it is simply multiplicative):
3. Proof by induction
Base step: test if the summation result is correct for the lowest possible value of N, i.e. 2:
The result is consistent.
Recurrence step: confirm that if the summation is correct for N / 2, it is also correct for N:
Which is exactly our original recurrence relation.
Hence by induction, the summation result is correct, and T(N) is indeed Ө(N).
4. Numerical testing:
We can write code to confirm our result if needed:
function T(n) {
return n > 1 ? 2 * T(floor(n/2)) + 1 : 1;
}
Results:
N T(N)
-------------------------
2 3
4 7
8 15
16 31
32 63
64 127
128 255
256 511
512 1023
1024 2047
2048 4095
4096 8191
8192 16383
16384 32767
32768 65535
65536 131071
131072 262143
262144 524287
524288 1048575
1048576 2097151
2097152 4194303
4194304 8388607
8388608 16777215
16777216 33554431
33554432 67108863
67108864 134217727
134217728 268435455
268435456 536870911
536870912 1073741823
1073741824 2147483647
Graph:
Let's try tracing through it:
Let N be 8.
1 + f(4) + f(4)
=> 1 + 2 + f(2) + f(2) + f(2) + f(2)
=> 1 + 6 + f(1) + f(1) + f(1) + f(1) + f(1) + f(1) + f(1) + f(1)
When n = 8; work = 15
Let N be 4.
1 + f(2) + f(2)
=> 1 + 2 + f(1) + f(1) + f(1) + f(1)
When n = 4; work = 7
Let N be 2
1 + f(1) + f(1)
When n = 2; work = 3;
Let N be 1
1
When n = 1; work = 1
So at a glance the work pattern seems to be 2n - 1
We still need to prove it!
From the algorithm the recursive relation is:
W(1) = 1
W(N) = 1 + 2 * W(N / 2)
Proof by induction
Base Case:
W(1) = 2(1) - 1 = 1 as required.
Recursive case:
Assume W(N / 2) = 2(n/2) - 1 = n - 1
W(N) = 1 + 2 * (N / 2)
Applying induction...
W(N) = 1 + 2(n - 1) = 1 + 2n - 2 = 2n - 1 as required
Therefore the complexity is O(2n - 1) because O is reflexive
=> O( max { 2n, -1 } ) because of the rule of sums
=> O(2n)
=> O(n) because of the rule of scaling
This code is much similar to binary tree traversal,
void f( int N ){
sum ++;
if ( N > 1){
f( N /2); //like Traverse left subtree
f( N /2); //like Traverse right subtree
}
which basically traverses through each node once, with O(N) time complexity.
n/8
n/4 ---------
n/8
n/2 ------------------
n/8
n/4 ---------
n/8
n -------------------------------
n/8
n/4 ---------
n/8
n/2 ----------------
n/8
n/4 ---------
n/8
this goes on till the passed value becomes 1 or 0.

How to find the time complexity of the following code snippet

What is the time complexity of the following code snippet and how to calculate it.
function(int n){
for(int i = 1 ; i<=n ; i++){
for(int j=1 ; j<=n; j+=i){
System.out.println("*");
}
}
Let's think about the total work that's done. As you noted in your comment, the inner loop runs n times when i = 1, then n / 2 times when i = 2, then n / 3 times when i = 3, etc. (ignoring rounding errors). This means that the total work done is
n + n/2 + n/3 + n/4 + ... + n/n
= n(1 + 1/2 + 1/3 + 1/4 + ... + 1/n)
The term in parentheses is the nth harmonic number, denoted Hn, so the work done overall is roughly nHn. It's known that Hn = Θ(log n), so the total work is Θ(n log n).

Fastest way to create a sparse matrix of the form A.T * diag(b) * A + C?

I'm trying to optimize a piece of code that solves a large sparse nonlinear system using an interior point method. During the update step, this involves computing the Hessian matrix H, the gradient g, then solving for d in H * d = -g to get the new search direction.
The Hessian matrix has a symmetric tridiagonal structure of the form:
A.T * diag(b) * A + C
I've run line_profiler on the particular function in question:
Line # Hits Time Per Hit % Time Line Contents
==================================================
386 def _direction(n, res, M, Hsig, scale_var, grad_lnprior, z, fac):
387
388 # gradient
389 44 1241715 28220.8 3.7 g = 2 * scale_var * res - grad_lnprior + z * np.dot(M.T, 1. / n)
390
391 # hessian
392 44 3103117 70525.4 9.3 N = sparse.diags(1. / n ** 2, 0, format=FMT, dtype=DTYPE)
393 44 18814307 427597.9 56.2 H = - Hsig - z * np.dot(M.T, np.dot(N, M)) # slow!
394
395 # update direction
396 44 10329556 234762.6 30.8 d, fac = my_solver(H, -g, fac)
397
398 44 111 2.5 0.0 return d, fac
Looking at the output it's clear that constructing H is by far the most costly step - it takes considerably longer than actually solving for the new direction.
Hsig and M are both CSC sparse matrices, n is a dense vector and z is a scalar. The solver I'm using requires H to be either a CSC or CSR sparse matrix.
Here's a function that produces some toy data with the same formats, dimensions and sparseness as my real matrices:
import numpy as np
from scipy import sparse
def make_toy_data(nt=200000, nc=10):
d0 = np.random.randn(nc * (nt - 1))
d1 = np.random.randn(nc * (nt - 1))
M = sparse.diags((d0, d1), (0, nc), shape=(nc * (nt - 1), nc * nt),
format='csc', dtype=np.float64)
d0 = np.random.randn(nc * nt)
Hsig = sparse.diags(d0, 0, shape=(nc * nt, nc * nt), format='csc',
dtype=np.float64)
n = np.random.randn(nc * (nt - 1))
z = np.random.randn()
return Hsig, M, n, z
And here's my original approach for constructing H:
def original(Hsig, M, n, z):
N = sparse.diags(1. / n ** 2, 0, format='csc')
H = - Hsig - z * np.dot(M.T, np.dot(N, M)) # slow!
return H
Timing:
%timeit original(Hsig, M, n, z)
# 1 loops, best of 3: 483 ms per loop
Is there a faster way to construct this matrix?
I get close to a 4x speed-up in computing the product M.T * D * M out of the three diagonal arrays. If d0 and d1 are the main and upper diagonal of M, and d is the main diagonal of D, then the following code creates M.T * D * M directly:
def make_tridi_bis(d0, d1, d, nc=10):
d00 = d0*d0*d
d11 = d1*d1*d
d01 = d0*d1*d
len_ = d0.size
data = np.empty((3*len_ + nc,))
indices = np.empty((3*len_ + nc,), dtype=np.int)
# Fill main diagonal
data[:2*nc:2] = d00[:nc]
indices[:2*nc:2] = np.arange(nc)
data[2*nc+1:-2*nc:3] = d00[nc:] + d11[:-nc]
indices[2*nc+1:-2*nc:3] = np.arange(nc, len_)
data[-2*nc+1::2] = d11[-nc:]
indices[-2*nc+1::2] = np.arange(len_, len_ + nc)
# Fill top diagonal
data[1:2*nc:2] = d01[:nc]
indices[1:2*nc:2] = np.arange(nc, 2*nc)
data[2*nc+2:-2*nc:3] = d01[nc:]
indices[2*nc+2:-2*nc:3] = np.arange(2*nc, len_+nc)
# Fill bottom diagonal
data[2*nc:-2*nc:3] = d01[:-nc]
indices[2*nc:-2*nc:3] = np.arange(len_ - nc)
data[-2*nc::2] = d01[-nc:]
indices[-2*nc::2] = np.arange(len_ - nc ,len_)
indptr = np.empty((len_ + nc + 1,), dtype=np.int)
indptr[0] = 0
indptr[1:nc+1] = 2
indptr[nc+1:len_+1] = 3
indptr[-nc:] = 2
np.cumsum(indptr, out=indptr)
return sparse.csr_matrix((data, indices, indptr), shape=(len_+nc, len_+nc))
If your matrix M were in CSR format, you can extract d0 and d1 as d0 = M.data[::2] and d1 = M.data[1::2], I modified you toy data making routine to return those arrays as well, and here's what I get:
In [90]: np.allclose((M.T * sparse.diags(d, 0) * M).A, make_tridi_bis(d0, d1, d).A)
Out[90]: True
In [92]: %timeit make_tridi_bis(d0, d1, d)
10 loops, best of 3: 124 ms per loop
In [93]: %timeit M.T * sparse.diags(d, 0) * M
1 loops, best of 3: 501 ms per loop
The whole purpose of the above code is to take advantage of the structure of the non-zero entries. If you draw a diagram of the matrices you are multiplying together, it is relatively easy to convince yourself that the main (d_0) and top and bottom (d_1) diagonals of the resulting tridiagonal matrix are simply:
d_0 = np.zeros((len_ + nc,))
d_0[:len_] = d00
d_0[-len_:] += d11
d_1 = d01
The rest of the code in that function is simply building the tridiagonal matrix directly, as calling sparse.diags with the above data is several times slower.
I tried running your test case and had problems with the np.dot(N, M). I didn't dig into it, but I think my numpy/sparse combo (both pretty new) had problems using np.dot on sparse arrays.
But H = -Hsig - z*M.T.dot(N.dot(M)) runs just fine. This uses the sparse dot.
I haven't run a profile, but here are Ipython timings for several parts. It takes longer to generate the data than to do that double dot.
In [37]: timeit Hsig,M,n,z=make_toy_data()
1 loops, best of 3: 2 s per loop
In [38]: timeit N = sparse.diags(1. / n ** 2, 0, format='csc')
1 loops, best of 3: 377 ms per loop
In [39]: timeit H = -Hsig - z*M.T.dot(N.dot(M))
1 loops, best of 3: 1.55 s per loop
H is a
<2000000x2000000 sparse matrix of type '<type 'numpy.float64'>'
with 5999980 stored elements in Compressed Sparse Column format>