cvxpy not allowing item assignment for MulExpression - python

I have a sparse matrix operation as part of the optimization constraint. I can implement the program in Matlab cvx, now I am trying to implement a cvxpy version. The problem is following constraint:
N - M << 0
M is a sparse matrix, with only a few entries are declared variables. I don't know a decent way to construct this constraint.
For example,
N = cp.Variable((800, 800), PSD=True)
a = cp.Variable((10, 1), nonneg=True)
M is a 800* 800 matrix, with M[i, i]= a[i] for 0<=i<10, and the rest of M are all 0's.
What I have done now is to declare M as M = cp.Variable((800,800), symmetric=True), and then add constraint like
constraints.append(M[i,i]==a[i]) for 0<=i<10; and constraints.append(M[i,j]==0) for the rest of M. But this way, it take lots of time and also the constraint list is large. I am wondering what is the best way to do so.
I also tried to do things like N[i,i] -= a[i] for 0<=i<10, but the item assignment is not allowed.

You can use this function to create a variable with a given sparsity pattern:
from typing import List, Tuple
import cvxpy as cp
import scipy.sparse as sp
import numpy as np
def sparse_variable(shape: Tuple[int, int], sparsity: List[Tuple[int, int]]):
"""Create a variable with given sparsity pattern."""
nnz = len(sparsity)
flat_var = cp.Variable(nnz)
# Column major order.
V = np.ones(nnz)
I = []
J = []
# Column-major order.
for idx, (row, col) in enumerate(sparsity):
I.append(row + col * shape[0])
J.append(idx)
reshape_mat = sp.coo_matrix((V, (I, J)), shape=(np.prod(shape), nnz))
return cp.reshape(reshape_mat # flat_var, shape)

Related

How to declare params in pyomo?

I am having a difficult with pyomo's params declaration. I have a np array like I declared bellow, but it appear this error: KeyError: "Index '0' is not valid for indexed component 'c'.
model = pyo.ConcreteModel()
V = range(20)
model.V = pyo.Set(initialize = V, doc = 'Set: clients and depots', within = pyo.NonNegativeIntegers)
c = np.zeros((len(V), len(V)))
model.c = pyo.Param(V, V, initialize = c, doc = 'Param: distances', within = pyo.NonNegativeReals)
ChatGPT said that I could make this:
c_dict = {(i, j): c[i][j] for i in V for j in V}
model.c = pyo.Param(V, V, initialize=c_dict, doc='Param: distances', within=pyo.NonNegativeReals)
But I don't undestand very well why this. I already readed the documentation, but I didn't undestand why I can't declare in "initialize" key.
The core issue is that multi-indexed things in pyomo are tuple-indexed like:
x[i, j, k, ...]
and if you have an n-dimensional array in basic python (list of lists) or n-dimensional numpy array, they are "layered" indexed (not sure if that is right term) like:
x[i][j][k]
So in your case, if you have a distance matrix in a matrix format of some kind, which is very natural, you have 2 choices: You can either convert it to a dictionary (which is tuple-indexed) or just use a helper function...something like:
import pyomo.environ as pyo
import numpy as np
model = pyo.ConcreteModel()
V = range(20)
model.V = pyo.Set(initialize = V, doc = 'Set: clients and depots', within = pyo.NonNegativeIntegers)
c = np.zeros((len(V), len(V)))
def helper(model, i, j):
# convert from layered inex to tuple-indexed...
return c[i][j]
model.c = pyo.Param(model.V, model.V, initialize = helper, doc = 'Param: distances', within = pyo.NonNegativeReals)
A couple notes...
If your distances are all zero (or if you have a sparse matrix), you can/should just use a default value in the construct to fill in the missing data or the whole thing (if all zero)
Also, you should use model.V for the indexing sets instead of just V. (See the changes I made in that part.)

Vectorization of matrix creation by difference of vectors (e.g. for numpy)

i often need to calculate a matrix A[i,j] based on a given vector v[i] by:
A[i, j] = v[j] - v[i]
This is straightforward in a nested loop, but I'd like to vectorize it. So far I've only come up with the rather ugly solution of creating two matrizes additional, where v is repeated in each row/column and I therefore can use simple element-wise matrix addition.
Here a numpy example:
import numpy as np
length = 10
v = np.random.random(length)
vjMatrix = np.broadcast_to(v, (length, length))
viMatrix = np.transpose(vjMatrix)
A = vjMatrix - viMatrix
print(A)
However, I hope there is a more elegant solution, that I just fail to see. I was looking through a lot of threads, but haven't found anything particularly suitable.
Thanks!
If I understand you question correctly, you currently fill array A like:
import numpy as np
length = 100
np.random.seed(123)
v = np.random.rand(length)
vjMatrix = np.broadcast_to(v, (length, length))
viMatrix = np.transpose(vjMatrix)
A = vjMatrix - viMatrix
If this is what you want, you can replace the loop and the explicit creation of the v-matrices by broadcasting the vector v:
A_new = v - v[:, None]
print(np.all(A == A_new))
# Out: True

Python - Create sparse matrix representation from 10000 random values

I'm having a homework assignment about airport flights, where at first i have to create the representation of a sparse matrix(i, j and values) for a 1000x1000 array from 10000 random numbers with the following criteria:
i and j must be between 0-999 since are the rows and columns of array
values must be between 1.0-5.0
i must not be equal to j
i and j must be present only once
The i is the departure airport, the j is the arrival airport and the values are the hours for the trip from i to j.
Then i have to find the roundtrips for an airport A with 2 to 8 maximum stops based on the criteria above. For example:
A, D, F, G, A is a legal roundtrip with 4 stops
A, D, F, D, A is not a legal roundtrip since the D is visited twice
NOTE: the problem must be solved purely with python built-in libraries. No external libraries are accepted like scipy and numpy.
I have tried to run a loop for 10000 numbers and assign to row, column and value a random number based on the above criteria but i guess this is not what the assignment asks me to do since the loop doesn't stop.
I guess the i and j are not the actual iloc and j representations of the sparse matrix but rather the values of those? i don't know.
I currently don't have a working code other than the example for the roundtrip implementation. Although will raise an error if the list is empty:
dNext = {
0: [],
1: [4, 2, 0],
2: [1, 4],
3: [0],
4: [3, 1]
}
def findRoundTrips(trip, n, trips):
if (trip[0] == trip[-1]) and (1 < len(trip) <= n + 1):
trips.append(trip.copy())
return
for x in dNext[trip[-1]]:
if ((x not in trip[1:]) and (len(trip) < n)) or (x == trip[0]):
trip.append(x)
findRoundTrips(trip, n, trips)
trip.pop()
Here's how I would build a sparse matrix:
from collections import defaultdict
import random
max_location = 1000
min_value = 1.0
max_value = 5.0
sparse_matrix = defaultdict(list)
num_entries = 10000
for _ in range(num_entries):
source = random.randint(0, max_location)
dest = random.randint(0, max_location)
value = random.uniform(min_value, max_value)
sparse_matrix[source].append((dest, value))
What this does is define a sparse matrix as a dictionary where the key of the dictionary is the starting point of a trip. The values of a key define everywhere you can fly to and how long it takes to fly there as a list of tuples.
Note, I didn't check that I'm using randint and uniform perfectly correctly, if you use this, you should look at the documentation of those functions to find out if there are any off-by-one errors in this solution.

What exactly are indexed objects in sympy?

I don't understand what indexed objects in sympy are for. The documentation didn't help me understand the concept much.
For instance :
>>> from sympy import symbols, IndexedBase, Idx
>>> M = IndexedBase('M')
>>> i, j = symbols('i j', cls=Idx)
>>> M[i, j]
M[i, j]
What does this code do? What is M[i,j]?
All I understand is that an indexed object gives indices to individual sympy symbols .
I'm looking for a better explanation of indexed objects , along with it's relation to IndexedBase and idx
One-line explanation: they represent a symbolic array of undetermined, possibly infinite, size.
Suppose you want to work with n symbols, how would you do that? Easy enough if n is a given number, like 10. But it's just n, an unspecified integer number. Formulas like that appear in mathematics all the time: "add or multiply (something) over the indices i=1, ..., n".
For example, suppose I have a function in n-dimensional space Rn, such as f(x) = 1/distance(x, 0). The distance is, of course, the square root of the sum of squares of coordinates. And maybe I want to find some partial derivative of f. How to express all of this in SymPy? Like this:
from sympy import *
x = IndexedBase('x')
j, k, n = symbols('j k n', cls=Idx)
f = 1/sqrt(Sum(x[k]**2, (k, 1, n)))
print(f.diff(x[j]))
This computes the derivative of f with respect to the coordinate x[j]. The answer is
-Sum(2*KroneckerDelta(j, k)*x[k], (k, 1, n))/(2*Sum(x[k]**2, (k, 1, n))**(3/2))
which is correct (although perhaps the numerator could be simplified if we assume that j is in the range 1..n).
In the above example, x[j] is the coordinate with index j. In your example, M[i, j] could be the entry of some matrix at position i, j.
M is the name of symbolic array, its class is IndexedBase
i and j are indices of that array, their class is Idx
The above are the classes that you would instantiate yourself. The class of M[i, j] is Indexed but you don't create those objects by using class name, M[i, j] simply does that.
Two recent questions with examples of working with indexed objects:
Create an unknown number of programmatically defined variables
Sum of partial derivatives of a product over a symbolic number of variables
Indexed is primarily used for two use-cases:
Formulas with symbolic subscripts. For example, \sum_{i=1}^n a_i. You could just use Symbol('a_i'), but them the i is not symbolic and in any way related to Symbol('i'). So for instance, Sum(a_i, (i, 1, n)) will just give you n*a_i. Instead IndexedBase('a')[i] represents a different symbol for every value of i, and Sum(IndexedBase('a')[i], (i, 1, n)) effectively represents the above summation.
N-d arrays. This is especially useful for code generation, because the SymPy code printers for languages like C or Fortran will print Indexed objects as array lookups automatically, for instance
>>> a = IndexedBase('a')
>>> i = Idx('i', (1, n))
>>> ccode(a[i])
'a[i]'
>>> fcode(a[i])
' a(i)'
This makes it very easy to write array-based code symbolically using SymPy and generate fast code that computes it.

Matrix of variable size [i x j] (Python, Numpy)

I am attempting to build a simple genetic algorithm that will optimize to an input string, but am having trouble building the [individual x genome] matrix (row n is individual n's genome.) I want to be able to change the population size, mutation rate, and other parameters to study how that affects convergence rate and program efficiency.
This is what I have so far:
import random
import itertools
import numpy as np
def evolve():
goal = 'Hello, World!' #string to optimize towards
ideal = list(goal)
#converting the string into a list of integers
for i in range (0,len(ideal)):
ideal [i] = ord(ideal[i])
print(ideal)
popSize = 10 #population size
genome = len(ideal) #determineing the length of the genome to be the length of the target string
mut = 0.03 #mutation rate
S = 4 #tournament size
best = float("inf") #initial best is very large
maxVal = max(ideal)
minVal = min(ideal)
print (maxVal)
i = 0 #counting variables assigned to solve UnboundLocalError
j = 0
print(maxVal, minVal)
#constructing initial population array (individual x genome)
pop = np.empty([popSize, len(ideal)])
for i, j in itertools.product(range(i), range(j)):
pop[i, j] = [i, random.randint(minVal,maxVal)]
print(pop)
This produces a matrix of the population size with the correct genome length, but the genomes are something like:
[ 6.91364167e-310 6.91364167e-310 1.80613009e-316 1.80613009e-316
5.07224590e-317 0.00000000e+000 6.04100487e+151 3.13149876e-120
1.11787892e+253 1.47872844e-028 7.34486815e+223 1.26594941e-118
7.63858409e+228]
I need them to be random integers corresponding to random ASCII characters .
What am I doing wrong with this method?
Is there a way to make this faster?
I found my current method here:
building an nxn matrix in python numpy, for any n
I found another method that I do not understand, but seems faster and simper, if I can use it here I would like to.
Initialise numpy array of unknown length
Thank you for any assistance you can provide.
Your loop isn't executing because i and j are both 0, so range(i) and range(j) are empty. Also you can't assign a list [i,random] to an array value (np.empty defaults to np.float64). I've simply changed it to only store the random number, but if you really want to store a list, you can change the creation of pop to be pop = np.empty([popSize, len(ideal)],dtype=list)
Otherwise use this for the last lines:
for i, j in itertools.product(range(popSize), range(len(ideal))):
pop[i, j] = random.randint(minVal,maxVal)

Categories