Beispiel #1
0
 def test_paper_example_eye_minus_inv(self):
     X = cvxpy.Variable((2, 2), pos=True)
     obj = cvxpy.Minimize(cvxpy.trace(cvxpy.eye_minus_inv(X)))
     constr = [cvxpy.geo_mean(cvxpy.diag(X)) == 0.1,
               cvxpy.geo_mean(cvxpy.hstack([X[0, 1], X[1, 0]])) == 0.1]
     problem = cvxpy.Problem(obj, constr)
     problem.solve(gp=True, solver="ECOS")
     np.testing.assert_almost_equal(X.value, 0.1*np.ones((2, 2)), decimal=3)
     self.assertAlmostEqual(problem.value, 2.25)
Beispiel #2
0
 def test_paper_example_eye_minus_inv(self):
     X = cvxpy.Variable((2, 2), pos=True)
     obj = cvxpy.Minimize(cvxpy.trace(cvxpy.eye_minus_inv(X)))
     constr = [cvxpy.geo_mean(cvxpy.diag(X)) == 0.1]
     problem = cvxpy.Problem(obj, constr)
     # smoke test.
     problem.solve(gp=True, solver="SCS")
Beispiel #3
0
    def layout(self):
        constraints = []
        for box in self.boxes:
            # Enforce that boxes lie in bounding box.
            constraints += [
                box.bottom >= FloorPlan.MARGIN,
                box.top + FloorPlan.MARGIN <= self.height
            ]
            constraints += [
                box.left >= FloorPlan.MARGIN,
                box.right + FloorPlan.MARGIN <= self.width
            ]
            # Enforce aspect ratios.
            constraints += [(1 / box.ASPECT_RATIO) * box.height <= box.width,
                            box.width <= box.ASPECT_RATIO * box.height]
            # Enforce minimum area
            constraints += [
                geo_mean(vstack([box.width, box.height])) >= math.sqrt(
                    box.min_area)
            ]

        # Enforce the relative ordering of the boxes.
        for ordering in self.horizontal_orderings:
            constraints += self._order(ordering, True)
        for ordering in self.vertical_orderings:
            constraints += self._order(ordering, False)
        p = Problem(Minimize(2 * (self.height + self.width)), constraints)
        return p.solve()
Beispiel #4
0
 def test_matrix_constraint(self) -> None:
     X = cp.Variable((2, 2), pos=True)
     a = cp.Parameter(pos=True, value=0.1)
     obj = cp.Minimize(cp.geo_mean(cp.vec(X)))
     constr = [cp.diag(X) == a, cp.hstack([X[0, 1], X[1, 0]]) == 2 * a]
     problem = cp.Problem(obj, constr)
     gradcheck(problem, gp=True)
     perturbcheck(problem, gp=True)
    def test_geo_mean(self):
        """Test gradient for geo_mean
        """
        expr = cp.geo_mean(self.x)
        self.x.value = [1, 2]
        self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), [np.sqrt(2)/2, 1.0/2/np.sqrt(2)])

        self.x.value = [0, 2]
        self.assertAlmostEqual(expr.grad[self.x], None)

        expr = cp.geo_mean(self.x, [1, 0])
        self.x.value = [1, 2]
        self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), [1, 0])

        # No exception for single weight.
        self.x.value = [-1, 2]
        self.assertAlmostEqual(expr.grad[self.x], None)
Beispiel #6
0
 def test_geo_mean(self):
     x = cvxpy.Variable(3, pos=True)
     p = [1, 2, 0.5]
     geo_mean = cvxpy.geo_mean(x, p)
     self.assertTrue(geo_mean.is_dgp())
     self.assertTrue(geo_mean.is_log_log_affine())
     self.assertTrue(geo_mean.is_log_log_convex())
     self.assertTrue(geo_mean.is_log_log_concave())
Beispiel #7
0
    def test_geo_mean(self) -> None:
        """Test domain for geo_mean
        """
        dom = cp.geo_mean(self.x).domain
        prob = Problem(Minimize(sum(self.x)), dom)
        prob.solve(solver=cp.SCS, eps=1e-5)
        self.assertAlmostEqual(prob.value, 0)

        # No special case for only one weight.
        dom = cp.geo_mean(self.x, [0, 2]).domain
        dom.append(self.x >= -1)
        prob = Problem(Minimize(sum(self.x)), dom)
        prob.solve(solver=cp.SCS, eps=1e-5)
        self.assertItemsAlmostEqual(self.x.value, [-1, 0])

        dom = cp.geo_mean(self.z, [0, 1, 1]).domain
        dom.append(self.z >= -1)
        prob = Problem(Minimize(sum(self.z)), dom)
        prob.solve(solver=cp.SCS, eps=1e-5)
        self.assertItemsAlmostEqual(self.z.value, [-1, 0, 0])
Beispiel #8
0
 def test_geo_mean(self):
     x = cvxpy.Variable(3, pos=True)
     p = [1, 2, 0.5]
     geo_mean = cvxpy.geo_mean(x, p)
     dgp = cvxpy.Problem(cvxpy.Minimize(geo_mean), [])
     dgp2dcp = cvxpy.reductions.Dgp2Dcp(dgp)
     dcp = dgp2dcp.reduce()
     dcp.solve(SOLVER)
     self.assertEqual(dcp.value, -float("inf"))
     dgp.unpack(dgp2dcp.retrieve(dcp.solution))
     self.assertEqual(dgp.value, 0.0)
     self.assertEqual(dgp.status, "unbounded")
     dgp._clear_solution()
     dgp.solve(SOLVER, gp=True)
     self.assertEqual(dgp.value, 0.0)
     self.assertEqual(dgp.status, "unbounded")
Beispiel #9
0
 def test_geo_mean(self):
     atom = cp.geo_mean(self.x)
     self.assertEqual(atom.shape, tuple())
     self.assertEqual(atom.curvature, s.CONCAVE)
     self.assertEqual(atom.sign, s.NONNEG)
     # Test copy with args=None
     copy = atom.copy()
     self.assertTrue(type(copy) is type(atom))
     # A new object is constructed, so copy.args == atom.args but copy.args
     # is not atom.args.
     self.assertEqual(copy.args, atom.args)
     self.assertFalse(copy.args is atom.args)
     self.assertEqual(copy.get_data(), atom.get_data())
     # Test copy with new args
     copy = atom.copy(args=[self.y])
     self.assertTrue(type(copy) is type(atom))
     self.assertTrue(copy.args[0] is self.y)
     self.assertEqual(copy.get_data(), atom.get_data())
Beispiel #10
0
    def test_multi_step_dyad_completion(self) -> None:
        """
        Consider four market equilibrium problems.

        The budgets "b" in these problems are chosen so that canonicalization
        of geo_mean(u, b) hits a recursive code-path in power_tools.dyad_completion(...).

        The reference solution is computed by taking the log of the geo_mean objective,
        which has the effect of making the problem ExpCone representable.
        """
        if 'MOSEK' in cp.installed_solvers():
            log_solve_args = {'solver': 'MOSEK'}
        else:
            log_solve_args = {'solver': 'ECOS'}
        n_buyer = 5
        n_items = 7
        np.random.seed(0)
        V = 0.5 * (1 + np.random.rand(n_buyer, n_items))
        X = cp.Variable(shape=(n_buyer, n_items), nonneg=True)
        cons = [cp.sum(X, axis=0) <= 1]
        u = cp.sum(cp.multiply(V, X), axis=1)
        bs = np.array([[110, 14, 6, 77, 108], [15., 4., 8., 0., 9.],
                       [14., 21., 217., 57., 6.], [3., 36., 77., 8., 8.]])
        for i, b in enumerate(bs):
            log_objective = cp.Maximize(b @ cp.log(u))
            log_prob = cp.Problem(log_objective, cons)
            log_prob.solve(**log_solve_args)
            expect_X = X.value

            geo_objective = cp.Maximize(cp.geo_mean(u, b))
            geo_prob = cp.Problem(geo_objective, cons)
            geo_prob.solve()
            actual_X = X.value
            try:
                self.assertItemsAlmostEqual(actual_X, expect_X, places=3)
            except AssertionError as e:
                print(f'Failure at index {i} (when b={str(b)}).')
                log_prob.solve(**log_solve_args, verbose=True)
                print(X.value)
                geo_prob.solve(verbose=True)
                print(X.value)
                print('The valuation matrix was')
                print(V)
                raise e
            def f_Gamma(c):
                # For fixed c solve the semidefinite program for Algorithm 4
                
                # Variable Gamma_plus (for \Gamma_{i+1})
                d_plus = variable(2*n, 1, name='d_plus')
                
                # Constraints
                constr = []
                for j in range(2*n):
                    ctt =  less_equals(d_plus[j,0], 0)
                    constr.append( less_equals(-d_plus[j,0], 0))
                    constr.append( less_equals( d_plus[j,0], d[j,0]))
                    constr.append( less_equals( J[j,j]*d_plus[j,0] + sum([abs(J[i,j])*d_plus[i,0] for i in range(2*n)]) - abs(J[j,j])*d_plus[j,0], c* d_plus[j,0]))
                
                # Objective function
                obj = geo_mean(d_plus)
                # Find solution
                p = program(maximize(obj), constr)

                return d_plus.value
Beispiel #12
0
    def test_3d_power_cone_approx(self):
        """
        Use
            geo_mean((x,y), (alpha, 1-alpha)) >= |z|
        as a reformulation of
            PowCone3D(x, y, z, alpha).

        Check validity of the reformulation by solving
        orthogonal projection problems.
        """
        if 'MOSEK' in cp.installed_solvers():
            proj_solve_args = {'solver': 'MOSEK'}
        else:
            proj_solve_args = {'solver': 'SCS', 'eps': 1e-10}
        min_numerator = 2
        denominator = 25
        x = cp.Variable(3)
        np.random.seed(0)
        y = 10 * np.random.rand(3)  # the third value doesn't matter
        for i, numerator in enumerate(range(min_numerator, denominator, 3)):
            alpha_float = numerator / denominator
            y[2] = (y[0]**alpha_float) * (y[1]**(1 - alpha_float)) + 0.05
            objective = cp.Minimize(cp.norm(y - x, 2))

            actual_constraints = [
                cp.constraints.PowCone3D(x[0], x[1], x[2], [alpha_float])
            ]
            actual_prob = cp.Problem(objective, actual_constraints)
            actual_prob.solve(**proj_solve_args)
            actual_x = x.value.copy()

            weights = np.array([alpha_float, 1 - alpha_float])
            approx_constraints = [cp.geo_mean(x[:2], weights) >= cp.abs(x[2])]
            approx_prob = cp.Problem(objective, approx_constraints)
            approx_prob.solve()
            approx_x = x.value.copy()
            try:
                self.assertItemsAlmostEqual(actual_x, approx_x, places=4)
            except AssertionError as e:
                print(f'Failure at index {i} (when alpha={alpha_float}).')
                raise e
Beispiel #13
0
def h():
    x = cp.Variable()
    y = cp.Variable()
    z = cp.Variable()
    obj = cp.Minimize(0)
    constraints = [
            x + z <= 1 + cp.sqrt(x * y - z**2),
            x >= 0,
            y >= 0,
            ]
    problem = cp.Problem(obj, constraints)
    print(f"h before: {problem.is_dcp()}")
   
    t = cp.Variable()
    constraints = [
            x + z - 1 <= t,
            cp.norm(cp.vstack([x, t])) <= cp.geo_mean(cp.vstack([x, y])),
            x >= 0,
            y >= 0,
            ]
    problem = cp.Problem(obj, constraints)
    print(f"h after: {problem.is_dcp()}")
    problem.solve()
import numpy as np
import cvxpy as cp

a = np.array([0.2, 0.02, 0.04, 0.1])
A = np.diag(a)
b = np.array([0.5, 0.1, 0.0, 0.2])
b = np.asmatrix(b).T
p = cp.Variable(4)
obj = cp.Maximize(cp.geo_mean(p) - (0.5 * cp.quad_form(p, A) + b.T * p))
cons = [p > 0]
prob = cp.Problem(obj, cons)
prob.solve()

p_opt = p.value
print "Market clearing prices:"
print p_opt

print "Demand:"
print 0.25 * pow(p_opt[1, 0] * p_opt[2, 0] * p_opt[3, 0], 0.25) * pow(p_opt[0, 0], -3.0/4.0)
print 0.25 * pow(p_opt[0, 0] * p_opt[2, 0] * p_opt[3, 0], 0.25) * pow(p_opt[1, 0], -3.0/4.0)
print 0.25 * pow(p_opt[1, 0] * p_opt[0, 0] * p_opt[3, 0], 0.25) * pow(p_opt[2, 0], -3.0/4.0)
print 0.25 * pow(p_opt[1, 0] * p_opt[2, 0] * p_opt[0, 0], 0.25) * pow(p_opt[3, 0], -3.0/4.0)

print "Supply:"
print 0.2 * p_opt[0, 0] + 0.5
print 0.02 * p_opt[1, 0] + 0.1
print 0.04 * p_opt[2, 0]
print 0.1 * p_opt[3, 0] + 0.2
Beispiel #15
0
import numpy as np
import cvxpy as cp

A_file = "~/development/cvxopt/hwk7/max_vol_box/A.csv"
b_file = "~/development/cvxopt/hwk7/max_vol_box/b.csv"
A = np.loadtxt(open(A_file, "rb"), delimiter=",")
b = np.loadtxt(open(b_file, "rb"), delimiter=",")

L = np.zeros_like(A)
U = np.zeros_like(A)
for i, j in it.product(range(A.shape[0]), range(A.shape[1])):
    L[i,j] = max(-A[i,j], 0)
    U[i,j] = max(A[i,j], 0)

l = cp.Variable(A.shape[1], nonneg=True)
u = cp.Variable(A.shape[1], nonneg=True)
obj = cp.geo_mean(u - l)
con = [L @ l + U @ u <= b]
p = cp.Problem(cp.Maximize(obj), con)
p.solve()

Beispiel #16
0
 def area(self):
     return cvx.geo_mean(cvx.vstack([self.width, self.height]))
import numpy as np
import cvxpy as cp
from data.correlation_bounds_data import m, n, A, sigma
np.set_printoptions(precision=4, suppress=True)

Sigma = cp.Variable((n, n), PSD=True)
constraints = []
for i in range(m):
    a = A[:,i]
    constraints.append(cp.quad_form(a, Sigma) == sigma[i] ** 2)

rhos = []
for i in range(n):
    for j in range(i):
        denom = cp.geo_mean(cp.hstack([Sigma[i, i], Sigma[j, j]]))
        rho_ij = cp.quad_over_lin(Sigma[i, j], denom)
        rhos.append(rho_ij)

rho_max = cp.max(cp.hstack(rhos))
obj = cp.Minimize(rho_max)
problem = cp.Problem(obj, constraints)
problem.solve()
print(problem.status)
print(Sigma.value)
print(rho_max.value)
Beispiel #18
0
 def mean_area(cls, w, h, area):
     return cvx.geo_mean(cvx.hstack([w, h])) >= np.sqrt(area)
    (lambda x: x[2:0:-1], (2, ), [[3, 4, 5]], Constant([5, 4])),
    (lambda x: x[2::-1], (3, ), [[3, 4, 5]], Constant([5, 4, 3])),
    (lambda x: x[3:0:-1], (2, ), [[3, 4, 5]], Constant([5, 4])),
    (lambda x: x[3::-1], (3, ), [[3, 4, 5]], Constant([5, 4, 3])),
]

atoms_maximize = [
    (cp.entr, (2, 2), [[[1, math.e], [math.e**2, 1.0 / math.e]]],
     Constant([[0, -math.e], [-2 * math.e**2, 1.0 / math.e]])),
    (cp.log_det, tuple(), [[[20, 8, 5, 2], [8, 16, 2, 4], [5, 2, 5, 2],
                            [2, 4, 2, 4]]], Constant([7.7424020218157814])),
    (cp.geo_mean, tuple(), [[4, 1]], Constant([2])),
    (cp.geo_mean, tuple(), [[0.01, 7]], Constant([0.2645751311064591])),
    (cp.geo_mean, tuple(), [[63, 7]], Constant([21])),
    (cp.geo_mean, tuple(), [[1, 10]], Constant([math.sqrt(10)])),
    (lambda x: cp.geo_mean(x, [1, 1]), tuple(), [[1, 10]],
     Constant([math.sqrt(10)])),
    (lambda x: cp.geo_mean(x, [.4, .8, 4.9]), tuple(), [[.5, 1.8, 17]],
     Constant([10.04921378316062])),
    (cp.harmonic_mean, tuple(), [[1, 2, 3]], Constant([1.6363636363636365])),
    (cp.harmonic_mean, tuple(), [[2.5, 2.5, 2.5, 2.5]], Constant([2.5])),
    (cp.harmonic_mean, tuple(), [[0, 1, 2]], Constant([0])),
    (lambda x: cp.diff(x, 0), (3, ), [[1, 2, 3]], Constant([1, 2, 3])),
    (cp.diff, (2, ), [[1, 2, 3]], Constant([1, 1])),
    (cp.diff, tuple(), [[1.1, 2.3]], Constant([1.2])),
    (lambda x: cp.diff(x, 2), tuple(), [[1, 2, 3]], Constant([0])),
    (cp.diff, (3, ), [[2.1, 1, 4.5, -.1]], Constant([-1.1, 3.5, -4.6])),
    (lambda x: cp.diff(x, 2), (2, ), [[2.1, 1, 4.5,
                                       -.1]], Constant([4.6, -8.1])),
    (lambda x: cp.diff(x, 1, axis=0), (1, 2), [np.array([[-5, -3], [2, 1]])],
     Constant([[7], [4]])),
Beispiel #20
0
import cvxpy as cp
from scipy.stats import bernoulli
from progressbar import progressbar

# Retrieve data
path = "../storage/max-volume-rectangle.csv"
A = np.genfromtxt(path, skip_footer=1, delimiter=" ")
b = np.genfromtxt(path, skip_header=len(A), delimiter=" ")

# Problem definition
n = A.shape[1]
lower = cp.Variable(n)
upper = cp.Variable(n)
sides = upper - lower
volume = cp.prod(sides)
objective = cp.Maximize(cp.geo_mean(sides))
A_u = np.maximum(A, 0)
A_l = np.maximum(-A, 0)
constraints = [
    sides >= 0,
    A_u @ upper - A_l @ lower <= b,
]
problem = cp.Problem(objective, constraints)
problem.solve()
volume.value

# constraints checker
checks = 1000000
print(f"Starting {checks} random corner checks...")
for _ in progressbar(range(checks)):
    mask = bernoulli.rvs(0.5, size=n)
import numpy as np
import cvxpy as cp

a = np.array([0.2, 0.02, 0.04, 0.1])
A = np.diag(a)
b = np.array([0.5, 0.1, 0.0, 0.2])
b = np.asmatrix(b).T
p = cp.Variable(4)
obj = cp.Maximize(cp.geo_mean(p) - (0.5 * cp.quad_form(p, A) + b.T * p))
cons = [p > 0]
prob = cp.Problem(obj, cons)
prob.solve()

p_opt = p.value
print "Market clearing prices:"
print p_opt

print "Demand:"
print 0.25 * pow(p_opt[1, 0] * p_opt[2, 0] * p_opt[3, 0], 0.25) * pow(
    p_opt[0, 0], -3.0 / 4.0)
print 0.25 * pow(p_opt[0, 0] * p_opt[2, 0] * p_opt[3, 0], 0.25) * pow(
    p_opt[1, 0], -3.0 / 4.0)
print 0.25 * pow(p_opt[1, 0] * p_opt[0, 0] * p_opt[3, 0], 0.25) * pow(
    p_opt[2, 0], -3.0 / 4.0)
print 0.25 * pow(p_opt[1, 0] * p_opt[2, 0] * p_opt[0, 0], 0.25) * pow(
    p_opt[3, 0], -3.0 / 4.0)

print "Supply:"
print 0.2 * p_opt[0, 0] + 0.5
print 0.02 * p_opt[1, 0] + 0.1
print 0.04 * p_opt[2, 0]
Beispiel #22
0
]


atoms_maximize = [
    (cp.entr, (2, 2), [[[1, math.e], [math.e**2, 1.0 / math.e]]],
     Constant([[0, -math.e], [-2 * math.e**2, 1.0 / math.e]])),
    (cp.log_det, tuple(),
     [[[20, 8, 5, 2],
       [8, 16, 2, 4],
       [5, 2, 5, 2],
       [2, 4, 2, 4]]], Constant([7.7424020218157814])),
    (cp.geo_mean, tuple(), [[4, 1]], Constant([2])),
    (cp.geo_mean, tuple(), [[0.01, 7]], Constant([0.2645751311064591])),
    (cp.geo_mean, tuple(), [[63, 7]], Constant([21])),
    (cp.geo_mean, tuple(), [[1, 10]], Constant([math.sqrt(10)])),
    (lambda x: cp.geo_mean(x, [1, 1]), tuple(), [[1, 10]], Constant([math.sqrt(10)])),
    (lambda x: cp.geo_mean(x, [.4, .8, 4.9]), tuple(),
     [[.5, 1.8, 17]], Constant([10.04921378316062])),
    (cp.harmonic_mean, tuple(), [[1, 2, 3]], Constant([1.6363636363636365])),
    (cp.harmonic_mean, tuple(), [[2.5, 2.5, 2.5, 2.5]], Constant([2.5])),
    (cp.harmonic_mean, tuple(), [[1e-8, 1, 2]], Constant([0])),

    (lambda x: cp.diff(x, 0), (3,), [[1, 2, 3]], Constant([1, 2, 3])),
    (cp.diff, (2,), [[1, 2, 3]], Constant([1, 1])),
    (cp.diff, tuple(), [[1.1, 2.3]], Constant([1.2])),
    (lambda x: cp.diff(x, 2), tuple(), [[1, 2, 3]], Constant([0])),
    (cp.diff, (3,), [[2.1, 1, 4.5, -.1]], Constant([-1.1, 3.5, -4.6])),
    (lambda x: cp.diff(x, 2), (2,), [[2.1, 1, 4.5, -.1]], Constant([4.6, -8.1])),
    (lambda x: cp.diff(x, 1, axis=0), (1, 2), [np.array([[-5, -3], [2, 1]])],
     Constant([[7], [4]])),
    (lambda x: cp.diff(x, 1, axis=1), (2, 1), [np.array([[-5, -3], [2, 1]])],
    def get_allocation(self,
                       unflattened_throughputs,
                       scale_factors,
                       unflattened_priority_weights,
                       cluster_spec,
                       recurse_deeper=True):
        throughputs, index = super().flatten(unflattened_throughputs,
                                             cluster_spec)
        if throughputs is None: return None
        (m, n) = throughputs.shape
        (job_ids, worker_types) = index

        if recurse_deeper:
            all_throughputs_minus_job = []
            for job_id in job_ids:
                unflattened_throughputs_minus_job = copy.copy(
                    unflattened_throughputs)
                del unflattened_throughputs_minus_job[job_id]
                throughputs_minus_job = self.get_allocation(
                    unflattened_throughputs_minus_job,
                    scale_factors,
                    unflattened_priority_weights,
                    cluster_spec,
                    recurse_deeper=False)
                all_throughputs_minus_job.append(throughputs_minus_job)

        # Row i of scale_factors_array is the scale_factor of job i
        # repeated len(worker_types) times.
        scale_factors_array = self.scale_factors_array(scale_factors, job_ids,
                                                       m, n)

        priority_weights = np.array(
            [1. / unflattened_priority_weights[job_id] for job_id in job_ids])

        proportional_throughputs = self._proportional_policy.get_throughputs(
            throughputs, index, cluster_spec)
        priority_weights = np.multiply(
            priority_weights.reshape((m, 1)),
            1.0 / proportional_throughputs.reshape((m, 1)))

        x = cp.Variable(throughputs.shape)
        # Multiply throughputs by scale_factors to ensure that scale_factor
        # is taken into account while allocating times to different jobs.
        # A job run on 1 GPU should receive `scale_factor` more time than
        # a job run on `scale_factor` GPUs if throughputs are equal.
        objective = cp.Maximize(
            cp.geo_mean(
                cp.sum(cp.multiply(
                    np.multiply(throughputs * priority_weights.reshape((m, 1)),
                                scale_factors_array), x),
                       axis=1)))
        # Make sure that the allocation can fit in the cluster.
        constraints = self.get_base_constraints(x, scale_factors_array)
        cvxprob = cp.Problem(objective, constraints)
        result = cvxprob.solve(solver=self._solver)

        if cvxprob.status != "optimal":
            print('WARNING: Allocation returned by policy not optimal!')

        throughputs = np.sum(np.multiply(throughputs, x.value), axis=1)
        throughputs_dict = {
            job_ids[i]: throughputs[i]
            for i in range(len(job_ids))
        }
        if not recurse_deeper:
            return throughputs_dict

        discount_factors = np.zeros(len(job_ids))
        for i, job_id in enumerate(job_ids):
            discount_factor = 1.0
            for other_job_id in all_throughputs_minus_job[i]:
                discount_factor *= (throughputs_dict[other_job_id] /
                                    all_throughputs_minus_job[i][other_job_id])
            discount_factors[i] = discount_factor

        discounted_allocation = np.multiply(x.value.T, discount_factors).T

        return super().unflatten(discounted_allocation.clip(min=0.0).clip(max=1.0), index), \
            discount_factors