largest and others constraints in portfolio optimization (MILP problem) CVXPY - python

Here is python code in cvxpy:
import numpy as np
import time
import cvxpy as cp
n = 10
a = np.random.randint(1, 10, size=n)
b = np.random.randint(1, 10, size=n)
c = np.random.randint(1, 10, size=n)
d = np.random.randint(1, 10, size=n)
x = cp.Variable(shape=n, boolean=True)
# objective function
objective = cp.Maximize(cp.sum(cp.multiply(x,a)))
# constraints
constraints = []
constraints.append(cp.sum(cp.multiply(x, b) <= 50) # constraint 1
constraints.append(cp.sum_largest(cp.hstack([
cp.sum(cp.multiply(x, b)),
cp.sum(cp.multiply(x, c)),
cp.sum(cp.multiply(x, d))]), 2) <= 100) # constraint 2
prob = cp.Problem(objective, constraints)
# solve model
prob.solve(solver=cp.CBC, verbose=False, maximumSeconds=100)
print("status:", prob.status)
a, b, c, d and x are all binary. The objective is max(sum(x*a)) and the constraints are:
sum(x*b) <= 50
sum of the largest 2 values in [sum(x*b), sum(x*c), sum(x*d)] <= 100, this is implemented via sum_largest sum_largest([x*a, x*b, x*c], 2) <= 100
define others=[b, c, d] - b - (largest 2 value in [b, c, d])
For example:
case1: [sum(x*b), sum(x*c), sum(x*d)] = [1,2,3], so (largest 2 value in [b, c, d]) = [c, d] and others=[b, c, d] - b - [c, d] = None
case2: [sum(x*b), sum(x*c), sum(x*d)] = [3,2,1], so (largest 2 value in [b, c, d]) = [b, c] and others=[b, c, d] - b - [b, c] = d
constriants:
for i in others:
constraints.append(cp.sum(cp.multiply(x, i) <= 10)
Constraints 1, 2 have already been implemented. How can I implement constraint 3? Is it even possible in cvxpy?

Note: the question has changed, so this is no longer a valid answer.
The original problem was:
(1) sum(x*b) <= 5
(2) max([sum(x*b), sum(x*c), sum(x*d)]) <= 10
(3) define others=[b, c, d] - b - maxBCD([b, c, d]) (others is a set of symbols, maxBCD returns a symbol)
for i in others:
constraints.append(cp.sum(cp.multiply(x, i) <= 1)
This is an answer to that original problem statement. I have not deleted the answer as it is a good starting point (also because the answer states the problem a bit more precisely: in mathematical terms).
(1) and (2) can be written as:
xb = sum(x*b)
xc = sum(x*c)
xd = sum(x*d)
xb <= 5
xc <= 10
xd <= 10
(3) needs to know which one is the maximum. I assume two or all three can be maximum.
bIsMax,cIsMax,dIsMax: binary variables
# bisMax=1 => xb is largest
xb >= xc - (1-bisMax)*10
xb >= xd - (1-bisMax)*10
# cisMax=1 => xc is largest
xc >= xb - (1-cisMax)*5
xc >= xd - (1-cisMax)*10
# disMax=1 => xd is largest
xd >= xb - (1-disMax)*5
xd >= xc - (1-disMax)*10
# at least one of them is largest
bIsMax + cIsMax + dIsMax >= 1
Note that others=[b, c, d] - b- maxBCD can be restated as: others=[c, d] - maxBCD.
# if c is not max then xc<=1
xc <= 1 + 9*cIsMax
# if d is not max then xd<=1
xd <= 1 + 9*dIsMax
Left to the reader:
check the math and implement in CVXPY.
update the answer to the revised question.

Related

How to understand the behavior of sklearn.preprocessing.PolynomialFeatures?

In sklearn doc, it was introduced that the two dimensional feature of the form [a, b] would get its degree-2 polynomial features as [1, a, b, a^2, ab, b^2].
However, it's not clear how this happens. I cannot understand if the degree changes to 3, for example, what would the feature be like.
I tried to interpret it by [1, a, b, a^2, ab, b^2] = the upper triangle of ([a, b] # [a, b].T ) because it seems like a quadratic form. But this would not be correct for degree 3.
[1, a, b, a², ab, b², a³, a²b, ab², b³] probably. These are the powers in the expansion of (1 + a + b)³.
In the case of three variables, [1, a, a², a³, ab, ab², abc, ac, ac², b, b², b³, ba², bc, bc², c, c², c³, ca², cb²].
This pseudo code explains how the features are generated for a given max degree n
polynomial_features = []
for degree in range(n + 1):
for k in range(degree):
polynomial_features.append(a**k * b**(degree - k))
So, for degrees up to 3 for instance, you would get:
# for degree = 0:
# k = 0
polynomial_features.append(a**0 * b**0) # = 1
# for degree = 1:
# k = 0
polynomial_features.append(a**0 * b**1) # = b
# k = 1
polynomial_features.append(a**1 * b**0) # = a
# for degree = 2:
# k = 0
polynomial_features.append(a**0 * b**2) # = b**2
# k = 1
polynomial_features.append(a**1 * b**1) # = a*b
# k = 2
polynomial_features.append(a**2 * b**0) # = a**2
# for degree = 3:
# k = 0
polynomial_features.append(a**0 * b**3) # = b**3
# k = 1
polynomial_features.append(a**1 * b**2) # = a*(b**2)
# k = 2
polynomial_features.append(a**2 * b**1) # = (a**2) * b
# k = 3
polynomial_features.append(a**3 * b**0) # = a**3

vectorized quadratic formula, why is the runtime warning invalid value in numpy.sqrt() still raised?

numpy version 1.20.1
Bob lawblaw's Law Blog, I need more details, this post has tooooo much code.
def quadratic_formula(a, b, c):
"""just like the song
Args:
a (numpy.ndarray): shape(N,1)
b (numpy.ndarray): shape(N,1)
c (numpy.ndarray): shape(N,1)
Returns:
numpy.ndarray: shape(N,2)
* [soln_a, soln_b]
* [np.NaN, np.NaN] when discriminant is negative
* [soln_a, soln_a] single soln when discriminate == 0]
Notes:
.. math::
ax^2 + bx + c = 0
solns = \\frac{-b \\pm \\sqrt{b^2 -4ac}}{2a}
"""
# TODO: raise value error if any a == 0
a = np.array(a)
b = np.array(b)
c = np.array(c)
det = b ** 2 - 4 * a * c
res_a = np.where(
det >= 0,
(-b + np.sqrt(det)) / (2 * a),
np.NaN,
)
res_b = np.where(
det >= 0,
(-b - np.sqrt(det)) / (2 * a),
np.NaN,
)
res = np.array([res_a, res_b]).T
return res
a = [1,2]
b = [1,0]
c = [0,1]
res = quadratic_formula(a,b,c)
print(res)
>>> [[0, -1],
[NaN, NaN]]
works, but raise RuntimeWarning: invalid value encountered in sqrt.
Why is the square root even evaluated for a negative discriminant?
Any suggestions for implementation?
Note that you are still computing np.sqrt(det) for all values of det hence the warning. The where filters the x and y arrays after they have been computed.
The implementation can be fixed by simply casting the a,b and c arrays to complex.
a = np.array(a).astype(complex)
b = np.array(b).astype(complex)
c = np.array(c).astype(complex)
That way numpy knows to use the complex version of sqrt. Once you are there you can completely omit the np.where and check after the fact if your solutions are real, if that is what you are interested on only.

Fastest Integer Search for Any Arbitrary X and Y

With the application of Discrete Math, what's the fastest algorithm in python to solve this problem:
With the equation ax + by = d, where a, b and d are user inputs, search for the integer value pairs for x and y in the range |L, R| (including L and R) that satisfies the equation.
L and R are user inputs as well.
Output all possible values for x and y in increasing order. Print none if there are no possible pairs.
Case 1:
a = 1
b = 5
d = 40
L = 0
R = 10
Result:
0 8
5 7
10 6
Case 2:
a = 14
b = 91
d = 53
L = 1
R = 100
Result:
none
Here's my code, but I believe there is a much faster way in searching. This is too inefficient.
a = int(input())
b = int(input())
d = int(input())
L = int(input())
R = int(input())
isNone = True
for x in range(L, R+1):
for y in range(L, R+1):
if (a*x) + (b*y) == d:
print(x, y)
isNone = False
if isNone: print("none")
Can there be an algorithm with O(1)? What's the fastest way?
The identity I imagine you want to apply is as follows (straight from wikipedia, though a similar phrasing should be found in most discrete math texts or could be proven on your own):
The simplest linear Diophantine equation takes the form ax + by = c, where a, b and c are given integers. The solutions are described by the following theorem:
This Diophantine equation has a solution (where x and y are integers) if and only if c is a multiple of the greatest common divisor of a and b. Moreover, if (x, y) is a solution, then the other solutions have the form (x + kv, y − ku), where k is an arbitrary integer, and u and v are the quotients of a and b (respectively) by the greatest common divisor of a and b.
This nearly immediately answers the question, especially since the standard proof of it uses something called the euclidean algorithm. In the interest of simplicity we're going to do the following:
Use the euclidean algorithm in the forward direction to find g = gcd(a, b).
Backsolve through the euclidean algorithm to find _x, _y such that _x*a + _y*b == g.
If d isn't a multiple of g then there can't possibly be any solutions, so exit early.
Otherwise, x, y = _x*(d//g), _y*(d//g) is a possible solution. Use that to find all solutions in the desired range.
def gcd(a, b):
# forward euclidean algorithm
q, r, x, qs = None, b, a, []
while x%r:
(q, r), x = divmod(x, r), r
qs.append(q)
# save the gcd for later
g = r
# backsolve euclidean algorithm
if not qs:
return 1, 1-a//b, g
theta, omega = 1, -qs[-1]
for q in reversed(qs[:-1]):
theta, omega = omega, theta - q * omega
# theta * a + omega * b == g
# g might be negative, but we don't care about a canonical solution
return theta, omega, g
def idivide_zero(a, b):
# integer division a/b, round toward 0 instead of round down
q = a // b
if q < 0 and b*q != a:
q += 1
return q
def bounded_solutions(a, b, d, L, R):
_x, _y, g = gcd(a, b)
if d%g:
return
# a*x + b*y == d
x, y = _x*(d//g), _y*(d//g)
# solutions are of the form (x+k*v, y-k*u)
u, v = a//g, b//g
# The next trick is to find all solutions in [L, R].
# Basically, we need L <= x+k*v <= R and L <= y-k*u <= R.
# Note that valid choices of k exist in a contiguous interval, so
# we only have to find the lower and upper bounds to be able to
# quickly enumerate all options.
xb = sorted(idivide_zero(b-x, v) for b in (L, R))
yb = sorted(idivide_zero(y-b, u) for b in (L, R))
m, M = min(xb[0], yb[0]), max(xb[1], yb[1])
for k in range(m, M+1):
yield x+k*v, y-k*u
a = int(input())
b = int(input())
d = int(input())
L = int(input())
R = int(input())
empty = True
for x, y in bounded_solutions(a, b, d, L, R):
print(x, y)
empty = False
if empty:
print('none')
Code is untested. It's right in spirit, but there may be some debugging left.

Advanced broadcasting in TensorFlow (or Numpy)

In TensorFlow I have a rank-2 tensor M (a matrix) of shape [D, D] and a rank-3 tensor T of shape [D, D, D].
I need to combine them to form a new matrix R as follows: the element R[a, b+c-a] is given by the sum of all the elements T[a, b, c]*M[b, c] where b+c-a is constant (where b+c-a has to be between 0 and D-1).
An inefficient way to create R is with nested for loops over the indices and a check that b+c-a does not exceed D-1 (e.g. in numpy):
R = np.zeros([D,D])
for a in range(D):
for b in range(D):
for c in range(D):
if 0 <= b+c-a < D:
R[a, b+c-a] += T[a, b, c]*M[b, c]
but I would like to use broadcasting and/or other more efficient methods.
How can I achieve this?
You can vectorize that calculation as follows:
import numpy as np
np.random.seed(0)
D = 10
M = np.random.rand(D, D)
T = np.random.rand(D, D, D)
# Original calculation
R = np.zeros([D, D])
for a in range(D):
for b in range(D):
for c in range(D):
if 0 <= b + c - a < D:
R[a, b + c - a] += T[a, b, c] * M[b, c]
# Vectorized calculation
tm = T * M
a = np.arange(D)[:, np.newaxis, np.newaxis]
b, c = np.ogrid[:D, :D]
col_idx = b + c - a
m = (col_idx >= 0) & (col_idx < D)
row_idx = np.tile(a, [1, D, D])
R2 = np.zeros([D, D])
np.add.at(R2, (row_idx[m], col_idx[m]), tm[m])
# Check result
print(np.allclose(R, R2))
# True
Alternatively, you could consider using Numba to accelerate the loops:
import numpy as np
import numba as nb
#nb.njit
def calculation_nb(T, M, D):
tm = T * M
R = np.zeros((D, D), dtype=tm.dtype)
for a in nb.prange(D):
for b in range(D):
for c in range(max(a - b, 0), min(D + a - b, D)):
R[a, b + c - a] += tm[a, b, c]
return R
print(np.allclose(R, calculation_nb(T, M, D)))
# True
In a couple of quick tests, even without parallelization, this is quite faster than NumPy.

Apply 3-argument function to 3D numpy array

I have a 3D numpy array A of shape (2133, 3, 3). Basically this is a list of 2133 lists with three 3D points. Furthermore I have a function which takes three 3D points and returns one 3D point, x = f(a, b, c), with a, b, c, x numpy arrays of length 3. Now I want to apply f to A, so that the output is an array of shape (2133, 3). So something like numpy.array([f(*A[0]),...,f(*A[2132])).
I tried numpy.apply_along_axis and numpy.vectorize without success.
To be more precise the function f I consider is given by:
def f(a, b, c, r1, r2=None, r3=None):
a = np.asarray(a)
b = np.asarray(b)
c = np.asarray(c)
if np.linalg.matrix_rank(np.matrix([a, b, c])) != 3:
# raise ValueError('The points are not collinear.')
return None
a, b, c, = sort_triple(a, b, c)
if any(r is None for r in (r2, r3)):
r2, r3 = (r1, r1)
ex = (b - a) / (np.linalg.norm(b - a))
i = np.dot(ex, c - a)
ey = (c - a - i*ex) / (np.linalg.norm(c - a - i*ex))
ez = np.cross(ex, ey)
d = np.linalg.norm(b - a)
j = np.dot(ey, c - a)
x = (pow(r1, 2) - pow(r2, 2) + pow(d, 2)) / (2 * d)
y = ((pow(r1, 2) - pow(r3, 2) + pow(i, 2) + pow(j, 2)) / (2*j)) - ((i/j)*x)
z_square = pow(r1, 2) - pow(x, 2) - pow(y, 2)
if z_square >= 0:
z = np.sqrt(z_square)
intersection = a + x * ex + y*ey + z*ez
return intersection
A = np.array([[[131.83, 25.2, 0.52], [131.51, 22.54, 0.52],[133.65, 23.65, 0.52]], [[13.02, 86.98, 0.52], [61.02, 87.12, 0.52],[129.05, 87.32, 0.52]]])
r1 = 1.7115
Thanks to the great help of #jdehesa I was able to produce an alternative solution to the one given by #hpaulj. I am not sure if this solution is the most elegant one but it worked so far. Comments are appreciated.
def sort_triple(a, b, c):
pts = np.stack((a, b, c), axis=1)
xSorted = pts[np.arange(pts.shape[0])[:, None], np.argsort(pts[:, :, 0])]
orientation = np.cross(xSorted[:, 1] - xSorted[:, 0], xSorted[:, 2] -
xSorted[:, 0])[:, 2] >= 0
xSorted_flipped = np.stack((xSorted[:, 0], xSorted[:, 2], xSorted[:, 1]),
axis=1)
xSorted = np.where(orientation[:, np.newaxis, np.newaxis], xSorted,
xSorted_flipped)
return map(np.squeeze, np.split(xSorted, 3, axis=1))
def f(A, r1, r2=None, r3=None):
a, b, c = map(np.squeeze, np.split(A, 3, axis=1))
a, b, c = sort_triple(a, b, c)
if any(r is None for r in (r2, r3)):
r2, r3 = (r1, r1)
ex = (b - a) / (np.linalg.norm(b - a, axis=1))[:, np.newaxis]
i = inner1d(ex, (c - a))
ey = ((c - a - i[:, np.newaxis]*ex) /
(np.linalg.norm(c - a - i[:, np.newaxis]*ex, axis=1))[:, np.newaxis])
ez = np.cross(ex, ey)
d = np.linalg.norm(b - a, axis=1)
j = inner1d(ey, c - a)
x = (np.square(r1) - np.square(r2) + np.square(d)) / (2 * d)
y = ((np.square(r1) - np.square(r3) + np.square(i) + np.square(j)) / (2*j) -
i/j*x)
z_square = np.square(r1) - np.square(x) - np.square(y)
mask = z_square < 0
z_square[mask] *= 0
z = np.sqrt(z_square)
z[mask] = np.nan
intersection = (a + x[:, np.newaxis] * ex + y[:, np.newaxis] * ey +
z[:, np.newaxis] * ez)
return intersection
Probably the map parts in each function could be done better. Maybe also the excessive use of np.newaxis.
This works fine (after commenting out sort_triple):
res = [f(*row,r1) for row in A]
print(res)
producing:
[array([ 132.21182324, 23.80481826, 1.43482849]), None]
That looks like one row produced a (3,) array, the other had some sort of problem and produced None. I don't know if that None was due to removing the sort or not. But in any case, turning a mix of arrays and None back into an array would be a problem. If all items of res were matching arrays, we could stack them back into a 2d array.
There are ways of getting modest speed improvements (compared to this list comprehension). But with a complex function like this, the time spent in the function (called 2000 times) dominates the time spent by the iteration mechanism.
And since you are iterating on the 1st dimension, and passing the other 2 (as 3 arrays), this explicit loop is a lot easier to use than vectorize, frompyfunc or apply_along/over...
To get significant time savings you have to write f() to work with the 3d array directly.

Categories

Resources