Gurobi and Python: how to query a dictionary using optimal variable solutions - python-2.7

I'm using Python and Gurobi and I'm having difficulty on how to optimal variable solutions to query a dictionary.
my_dict = {(i, j) : func(Z) for i in I for j in J}
my_dict results to be like this:
{(15687, 'B'): [[7, 0, 0, 0], [0, 7, 0, 0], [0, 0, 7, 0], [0, 0, 0, 7]],
...
(18906, 'C'): [[4, 0, 0, 3], [3, 0, 0, 3], [4, 0, 0, 0], [3, 0, 0, 0]}
Moreover I have a binary variable x[i, j, z] and an assignment constraint:
assignment = m.addConstrs((quicksum(x[i, j, z]
for z in range(len(my_dict[i, j]))) == 1
for i in I for j in J), "assignment")
Supposing I obtain as optimal solution variables
x[15687,'B',0] 1.000000
x[18906,'C',2] 1.000000
Is there a way to retrieve the sublist of my_dict corresponding to the "z" index?
(For instance, if my solution is x[18906,'C',2] 1.000000 then z = 2 and I want to obtain the sublist [4, 0, 0, 0])

Your code is not really a nice minimal example to work with, so it's hard to post valid code.
The general problem does not look that tough.
If your original dict looks like:
{(15687, 'B'): [[7, 0, 0, 0], [0, 7, 0, 0], [0, 0, 7, 0], [0, 0, 0, 7]],
...
(18906, 'C'): [[4, 0, 0, 3], [3, 0, 0, 3], [4, 0, 0, 0], [3, 0, 0, 0]}
and your solution is my_dict_opt, probably something like this should do (python3):
import numpy as np # easy fp-math comparison
sublists = []
for key, val in my_dict.items():
n_vars = len(val) # my assumption
for i in range(n_vars):
if np.isclose(my_dict_opt[key + tuple([i])].X, 1.0): # depends on your dict if .X is needed
sublists.append(my_dict[key][i])
Because of the dicts, the order of elements in sublists is not defined and this should only be a prototype as it's not really clear to me how those dicts are in use for you.

Related

Python: How to make values of a list in a list of lists zeros

I would like to make all the values in the first list inside the list of lists named "child_Before" below zero. The piece of code I wrote to accomplish this task is also shown below after the list:
child_Before = [[9, 12, 7, 3, 13, 14, 10, 5, 4, 11, 8, 6, 2],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1],
[[1, 0], [1, 1]]]
for elem in range(len(child_Before[0])):
child_Before[0][elem] = 0
Below is the expected result:
child_After = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1],
[[1, 0], [1, 1]]]
However, I think there should be a more nibble way to accomplish this exercise. Hence, I welcome your help. Thank you in advance.
just to add a creative answer
import numpy as np
child_Before[0] = (np.array(child_Before[0])&0).tolist()
this is bad practice though since i'm using bitwise operasions in a senario where it is not intuitive, and i think there is a slight chance i'm making 2 loops xD on the bright site the & which is making all the zeros is O(1) time complexity
Just create a list of [0] with the same length as the original list.
# Answer to this question - make first list in the list to be all 0
child_Before[0] = [0] * len(child_Before[0])
As for you answer, I can correct it to make all the elements in the lists of this list to be zero.
# Make all elements 0
for child in range(len(child_Before)):
child_Before[child] = [0] * len(child_Before[child])
Use list comprehension:
child_after = [[i if n != 0 else 0 for i in j] for n, j in enumerate(child_Before)]

How to average sub arrays in Numpy using broadcasting?

If there is a numpy array that is a list of 2d arrays, is there more efficient way than calling the mean function twice?
z = np.array([[[0, 0, 0],
[10, 10, 10]],
[[0, 0, 0],
[5, 5, 5]],
[[0, 0, 0],
[2, 2, 2]]])
print(z.mean(axis=2).mean(axis=1))
>[ 5. 2.5 1. ]

Permute rows and columns of a matrix

Assuming that I have the following matrix/array:
array([[0, 0, 1, 1, 1],
[0, 0, 1, 0, 1],
[1, 1, 0, 1, 1],
[1, 0, 1, 0, 0],
[1, 1, 1, 0, 0]])
and I want to apply the following permutation:
1 -> 5
2 -> 4
the result should be in the end:
array([[1, 1, 1, 0, 0],
[1, 0, 1, 0, 0],
[1, 1, 0, 1, 1],
[0, 0, 1, 0, 1],
[0, 0, 1, 1, 1]])
Now, an incredibly naive (and memory costly) way of doing so might be:
a2 = deepcopy(a1)
a2[0,:] = a1[4,:]
a2[4,:] = a1[0,:]
a = deepcopy(a2)
a2[:,0] = a[:,4]
a2[:,4] = a[:,0]
a3 = deepcopy(a2)
a2[1,:] = a3[3,:]
a2[3,:] = a3[1,:]
a = deepcopy(a2)
a2[:,1] = a[:,3]
a2[:,3] = a[:,1]
But, I would like to know if there is something more efficient that does this. numpy.shuffle and numpy.permutation seem to permute only the rows of the matrix (not the columns at the same time). That doesn't work for me because the matrices are adjacency matrices (representing graphs), and I need to do the permutations which will give me a graph which is isomorphic with the original graph. Furthermore, I need to do an arbitrary number of permutations (more than one).
Thanks!
You can perform the swap in a one-liner using integer array indexing:
a = np.array([[0, 0, 1, 1, 1],
[0, 0, 1, 0, 1],
[1, 1, 0, 1, 1],
[1, 0, 1, 0, 0],
[1, 1, 1, 0, 0]])
b = a.copy()
# map 0 -> 4 and 1 -> 3 (N.B. Python indexing starts at 0 rather than 1)
a[[4, 3, 0, 1]] = a[[0, 1, 4, 3]]
print(repr(a))
# array([[1, 1, 1, 0, 0],
# [1, 0, 1, 0, 0],
# [1, 1, 0, 1, 1],
# [0, 0, 1, 0, 1],
# [0, 0, 1, 1, 1]])
Note that array indexing always returns a copy rather than a view - there's no way to swap arbitrary rows/columns of an array without generating a copy.
In this particular case you could avoid the copy by using slice indexing, which returns a view rather than a copy:
b = b[::-1] # invert the row order
print(repr(b))
# array([[1, 1, 1, 0, 0],
# [1, 0, 1, 0, 0],
# [1, 1, 0, 1, 1],
# [0, 0, 1, 0, 1],
# [0, 0, 1, 1, 1]])
Update:
You can use the same indexing approach to swap columns.
c = np.arange(25).reshape(5, 5)
print(repr(c))
# array([[ 0, 1, 2, 3, 4],
# [ 5, 6, 7, 8, 9],
# [10, 11, 12, 13, 14],
# [15, 16, 17, 18, 19],
# [20, 21, 22, 23, 24]])
c[[0, 4], :] = c[[4, 0], :] # swap row 0 with row 4...
c[:, [0, 4]] = c[:, [4, 0]] # ...and column 0 with column 4
print(repr(c))
# array([[24, 21, 22, 23, 20],
# [ 9, 6, 7, 8, 5],
# [14, 11, 12, 13, 10],
# [19, 16, 17, 18, 15],
# [ 4, 1, 2, 3, 0]])
I've used a different example array in this case - your version will yield an identical output after performing the row/column swaps which makes it difficult to understand what's going on.
I found a solution to do what I want (though it is expensive):
a2 = deepcopy(a1)
first = randint(0, 5, 10)
second = randint(0, 5, 10)
for i in range(len(first)):
a = deepcopy(a2)
a2[first[i],:] = a[second[i],:]
a2[second[i],:] = a[first[i],:]
for i in range(len(first)):
a = deepcopy(a2)
a2[:,first[i]] = a[:,second[i]]
a2[:,second[i]] = a[:,first[i]]
Basically, I am doing 10 random switches. However, I need to copy the matrix many times. Anyway, a2 now represents a graph which is isomorphic with a1.

Transforming a mapping matrix

I have a 2-dimensional array of ones and zeros called M where the g rows represent groups and the a columns represent articles. M maps groups and articles. If a given article "art" belongs to group "gr" then we have M[gr,art]=1; if not we have M[gr,art]=0.
Now, I would like to convert M into a square a x a matrix of ones and zeros (call it N) where if an article "art1" is in the same group as article "art2", we have N(art1,art2)=1 and N(art1,art2)=0 otherwise. N is clearly symmetric with 1's in the diagonal.
How do I construct N based on M?
Many thanks for your suggestions - and sorry if this is trivial (still new to python...)!
So you have a boolean matrix M like the following:
>>> M
array([[1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 1],
[0, 0, 1, 0, 0, 0],
[1, 0, 1, 0, 0, 0]])
>>> ngroups, narticles = M.shape
and what you want is a matrix of shape (narticles, narticles) that represents co-occurrence. That's simply the square of the matrix:
>>> np.dot(M, M.T)
array([[1, 0, 0, 1],
[0, 2, 0, 0],
[0, 0, 1, 1],
[1, 0, 1, 2]])
... except that you don't want counts, so set entries > 0 to 1.
>>> N = np.dot(M, M.T)
>>> N[N > 0] = 1
>>> N
array([[1, 0, 0, 1],
[0, 1, 0, 0],
[0, 0, 1, 1],
[1, 0, 1, 1]])

Creating lists of lists in a pythonic way

I'm using a list of lists to store a matrix in python. I tried to initialise a 2x3 Zero matrix as follows.
mat=[[0]*2]*3
However, when I change the value of one of the items in the matrix, it changes the value of that entry in every row, since the id of each row in mat is the same. For example, after assigning
mat[0][0]=1
mat is [[1, 0], [1, 0], [1, 0]].
I know I can create the Zero matrix using a loop as follows,
mat=[[0]*2]
for i in range(1,3):
mat.append([0]*2)
but can anyone show me a more pythonic way?
Use a list comprehension:
>>> mat = [[0]*2 for x in xrange(3)]
>>> mat[0][0] = 1
>>> mat
[[1, 0], [0, 0], [0, 0]]
Or, as a function:
def matrix(rows, cols):
return [[0]*cols for x in xrange(rows)]
Try this:
>>> cols = 6
>>> rows = 3
>>> a = [[0]*cols for _ in [0]*rows]
>>> a
[[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]
>>> a[0][3] = 2
>>> a
[[0, 0, 0, 2, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]
This is also discussed in this answer:
>>> lst_2d = [[0] * 3 for i in xrange(3)]
>>> lst_2d
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> lst_2d[0][0] = 5
>>> lst_2d
[[5, 0, 0], [0, 0, 0], [0, 0, 0]]
This one is faster than the accepted answer!
Using xrange(rows) instead of [0]*rows makes no difference.
>>> from itertools import repeat
>>> rows,cols = 3,6
>>> a=[x[:] for x in repeat([0]*cols,rows)]
A variation that doesn't use itertools and runs around the same speed
>>> a=[x[:] for x in [[0]*cols]*rows]
From ipython:
In [1]: from itertools import repeat
In [2]: rows=cols=10
In [3]: timeit a = [[0]*cols for _ in [0]*rows]
10000 loops, best of 3: 17.8 us per loop
In [4]: timeit a=[x[:] for x in repeat([0]*cols,rows)]
100000 loops, best of 3: 12.7 us per loop
In [5]: rows=cols=100
In [6]: timeit a = [[0]*cols for _ in [0]*rows]
1000 loops, best of 3: 368 us per loop
In [7]: timeit a=[x[:] for x in repeat([0]*cols,rows)]
1000 loops, best of 3: 311 us per loop
I use
mat = [[0 for col in range(3)] for row in range(2)]
although depending on what you do with the matrix after you create it, you might take a look at using a NumPy array.
This will work
col = 2
row = 3
[[0] * col for row in xrange(row)]
What about:
m, n = 2, 3
>>> A = [[0]*m for _ in range(n)]
>>> A
[[0, 0], [0, 0], [0, 0]]
>>> A[0][0] = 1
[[1, 0], [0, 0], [0, 0]]
Aka List comprehension; from the docs:
List comprehensions provide a concise way to create lists
without resorting to use of
map(), filter() and/or lambda.
The resulting list definition tends often to be clearer
than lists built using those constructs.
If the sizes involved are really only 2 and 3,
mat = [[0, 0], [0, 0], [0, 0]]
is easily best and hasn't been mentioned yet.
Is there anything itertools can't do? :)
>>> from itertools import repeat,izip
>>> rows=3
>>> cols=6
>>> A=map(list,izip(*[repeat(0,rows*cols)]*cols))
>>> A
[[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]
>>> A[0][3] = 2
>>> A
[[0, 0, 0, 2, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]