Example #1
0
 def test_norm_inf(self):
     exp = self.x + self.y
     atom = cp.norm_inf(exp)
     # self.assertEqual(atom.name(), "norm_inf(x + y)")
     self.assertEqual(atom.shape, tuple())
     self.assertEqual(atom.curvature, s.CONVEX)
     assert atom.is_convex()
     assert (-atom).is_concave()
     self.assertEqual(cp.norm_inf(atom).curvature, s.CONVEX)
     self.assertEqual(cp.norm_inf(-atom).curvature, s.CONVEX)
Example #2
0
def compute_min_norm_solution(x, y, norm_type):
  """Compute the min-norm solution using a convex-program solver."""
  w = cp.Variable((x.shape[0], 1))
  if norm_type == 'linf':
    # compute minimal L_infinity solution
    constraints = [cp.multiply(y, (w.T @ x)) >= 1]
    prob = cp.Problem(cp.Minimize(cp.norm_inf(w)), constraints)
  elif norm_type == 'l2':
    # compute minimal L_2 solution
    constraints = [cp.multiply(y, (w.T @ x)) >= 1]
    prob = cp.Problem(cp.Minimize(cp.norm2(w)), constraints)
  elif norm_type == 'l1':
    # compute minimal L_1 solution
    constraints = [cp.multiply(y, (w.T @ x)) >= 1]
    prob = cp.Problem(cp.Minimize(cp.norm1(w)), constraints)
  elif norm_type[0] == 'l':
    # compute minimal Lp solution
    p = float(norm_type[1:])
    constraints = [cp.multiply(y, (w.T @ x)) >= 1]
    prob = cp.Problem(cp.Minimize(cp.pnorm(w, p)), constraints)
  elif norm_type == 'dft1':
    w = cp.Variable((x.shape[0], 1), complex=True)
    # compute minimal Fourier L1 norm (||F(w)||_1) solution
    dft = np.matrix(scipy.linalg.dft(x.shape[0], scale='sqrtn'))
    constraints = [cp.multiply(y, (cp.real(w).T @ x)) >= 1]
    prob = cp.Problem(cp.Minimize(cp.norm1(dft @ w)), constraints)
  prob.solve()
  logging.info('Min %s-norm solution found (norm=%.4f)', norm_type,
               float(norm_f(w.value, norm_type)))
  return cp.real(w).value
Example #3
0
def bound_fit(A, b, norm=2):
    r""" solve a norm constrained problem

	.. math:: 

		\min_{x} \| \mathbf{A}\mathbf{x} - \mathbf{b}\|_p
		\text{such that} \mathbf{A}	\mathbf{x} -\mathbf{b} \ge 0
	"""
    with warnings.catch_warnings():
        warnings.simplefilter('ignore', PendingDeprecationWarning)
        x = cp.Variable(A.shape[1])
        residual = x.__rmatmul__(A) - b
        if norm == 1:
            obj = cp.norm1(residual)
        elif norm == 2:
            obj = cp.norm(residual)
        elif norm == np.inf:
            obj = cp.norm_inf(residual)
        constraint = [residual >= 0]
        #constraint = [x.__rmatmul__(A) - b >= 0]
        problem = cp.Problem(cp.Minimize(obj), constraint)
        problem.solve(feastol=1e-10, solver=cp.ECOS)
        #problem.solve(eps = 1e-10, solver = cp.SCS)
        #problem.solve(feastol = 1e-10, solver = cp.CVXOPT)
        # TODO: The solution doesn't obey the constraints for 1 and inf norm, but does for 2-norm.
        return x.value
Example #4
0
def linear_fit(A, b, norm=2, bound=None):
    r""" solve the linear optimization problem subject to constraints
	"""
    assert norm in [1, 2, np.inf], "Invalid norm specified"
    assert bound in [None, 'lower', 'upper'], "invalid bound specified"

    if norm == 2 and bound == None:
        return scipy.linalg.lstsq(A, b)[0]
    else:
        x = cp.Variable(A.shape[1])
        residual = x.__rmatmul__(A) - b
        if norm == 1: obj = cp.norm1(residual)
        elif norm == 2: obj = cp.norm(residual)
        elif norm == np.inf: obj = cp.norm_inf(residual)

        if bound == 'lower':
            constraint = [residual <= 0]
        elif bound == 'upper':
            constraint = [residual >= 0]
        else:
            constraint = []

        # Now actually solve the problem
        problem = cp.Problem(cp.Minimize(obj), constraint)
        problem.solve(feastol=1e-10, reltol=1e-8, abstol=1e-8, solver=cp.ECOS)
        return x.value
Example #5
0
    def _to_cvxpy(self):
        import cvxpy as cvx
        if self.order == 2 or self.order == None:
            return cvx.norm(self.fn._to_cvxpy())
        
        if self.order == 1:
            return cvx.norm1(self.fn._to_cvxpy())

        if self.order == np.inf:
            return cvx.norm_inf(self.fn._to_cvxpy())
Example #6
0
def inf_norm_fit(A, b):
    r""" Solve inf-norm linear optimization problem

	.. math::

		\min_{x} \| \mathbf{A} \mathbf{x} - \mathbf{b}\|_\infty

	"""
    with warnings.catch_warnings():
        warnings.simplefilter('ignore', PendingDeprecationWarning)
        x = cp.Variable(A.shape[1])
        obj = cp.norm_inf(A @ x - b.flatten())
        problem = cp.Problem(cp.Minimize(obj))
        problem.solve(solver='ECOS')
        return x.value
Example #7
0
 def construct_constraints(
     self,
     x: np.ndarray,
     y: np.ndarray,
     beta: Optional[cp.Variable] = None
 ) -> Optional[List[Expression]]:  # type: ignore
     """
     Dantzig selector constraints
     Args:
         x (np.ndarray): MxN input data array
         y (np.ndarray): M output targets
         beta (cp.Variable): dimension N vector for optimization
     Returns: List of constraints
     """
     return [cp.norm_inf(x.T @ (y - x @ beta)) <= self.lambd * self.sigma]
Example #8
0
def Example_4():
    print("\n=======================================================")
    print("Example 4 minimize the infinite-norm of Ax-b in CVXPY:")

    # Problem data.
    m = 10
    n = 5
    np.random.seed(1)
    A = np.random.randn(m, n)
    b = np.random.randn(m)

    # Construct the problem.
    x = cp.Variable(n)
    # Notice that in numpy you use * for elementwise multiplication
    # However, in cvxpy * is matrix muliplication when two sizes are matricies or vectors
    # To do elementwise multiplication use cvxpy.multiply
    # You can still use the numpy @ operator for matmul as well, just substitute * with @ in next line
    objective = cp.Minimize(cp.norm_inf(A * x - b))
    prob = cp.Problem(objective)

    print("Optimal value", prob.solve())
    print("Optimal var")
    print(x.value)  # A numpy ndarray.
 def apply_atom_with_unity_weight(self, variable, weight, var_dim=None):
     return cvx.sum(cvx.norm_inf(variable.var_mat_N_tilde, axis=0))
Example #10
0
 def optim_stela(self, mu, threshold=None):
     self.val, self.st_x, self.error = st.stela_lasso(
         self.pathDic, self.signals,
         mu * cp.norm_inf(self.signals @ self.pathDic).value, 500)
Example #11
0
def main(datafile=default_data_file,
         namesfile=default_volunteers_file,
         min_staff=5,
         alpha=1.0,
         beta=0.7,
         gamma=0.28,
         verbose=1,
         solver_options=None):
    '''Notação:
	M = min_staff: número mínimo de voluntários por turno.
	i = número (código) da pessoa. vai de 1 a N (talvez 28?)
	j = número (código) do turno.  vai de 1 a T (provavelmente 14)

	A_ij = available[i, j] == True quando pessoa i está disponível no turno j
	P_ij = preference[i, j] == True quando pessoa i tem preferência pelo turno j
	w_i  = prev_week[i] == número de turnos (almoços OU jantas) alocados à pessoa i na semana anterior

	Variável a ser otimizada: Z[i, j] == True quando pessoa i é alocada para colaborar no turno j.

	Variável auxiliar: L_i = load[i] == sum_j Z[i, j] == (soma da linha i de Z) == número de turnos alocados pra pessoa i.

	OBJETIVO: minimizar o máximo dos load[i], conforme disponibilidade das pessoas, orientando-se também pelas preferências.

	* Por enquanto, não estamos usando `preference` e `prev_week`, já que poucos usaram esses recursos na planilha.
	'''
    table_string = read_data(datafile)
    names_string = read_data(namesfile)
    available, preference, prev_week, managers = data_to_array(
        table_string, names_string)
    N, T = available.shape

    Z = cp.Variable((N, T), name="Z", boolean=True)

    if verbose >= 1:
        print(
            "# Programa de otimização linear com variáveis inteiras (MIP)\n\nDados de Entrada:"
        )
        print("\n[Array: AVAILABLE]", 0 + available, sep="\n", end="\n\n")
        print("\n[Array: PREFERENCE]", 0 + preference, sep="\n", end="\n\n")

    # load[i] == 'número de turnos alocados para a pessoa i' == sum(Z[i,j] para cada j)
    load = cp.sum(Z, axis=1)

    # Variável para induzir a preferência de soluções sem muita gente contribuindo em um só turno do dia:
    same_day = cp.Variable((N, T // 2))

    constraints = [
        # Sempre que A_ij==0, impõe-se Z_ij==0:
        Z <= available,

        # Somatório em i (somatório de cada coluna) deve ser, no mínimo, min_staff:
        cp.sum(Z, axis=0) == min_staff,

        # Restrição para garantir a presença de no mínimo um dos `responsáveis` (managers) a cada turno:
        cp.sum(Z[managers, :], axis=0) >= 1,

        # Forma matricial das desigualdades same_day[:, j//2] <= min(Z[:, j], Z[:, j+1]), com j em (0, 2, 4, ..., T-2):
        same_day <= Z[:, 0::2],
        same_day <= Z[:, 1::2]
    ]

    # Expressão para a carga média por pessoa, usando a igualdade cp.sum(load) == cp.sum(Z):
    mean_load = cp.sum(Z) / N

    # Função Objetivo (função a ser MINIMIZADA):
    load_cost = cp.norm_inf(load)  # máximo das entradas do vetor `load`

    # PENALTY_1 DESATIVADO (substituído por zero), vide restrição `Z <= available` acima.
    penalty_1 = cp.Constant(
        0
    )  # alpha * cp.sum(cp.pos(Z - available))  # penalizava-se alpha para cada entrada Z[i,j] > available[i,j].
    penalty_2 = beta * cp.sum(
        cp.scalene(load - mean_load, 1.8, 1.0)
    )  # penalizar cada load[i] longe da média (especialmente acima da média)
    same_day_bonus = gamma * cp.sum(same_day)

    objective = cp.Minimize(
        load_cost + penalty_1 + penalty_2 - same_day_bonus
    )  # minimize ('máximo das entradas' do vetor load) + penalidades
    problem = cp.Problem(
        objective,
        constraints)  # <= problema de otimização sujeito às restrições acima.

    if verbose >= 1:
        print("# Solucionando problema de otimização.")

    if solver_options is None:
        solver_options = {"verbose": (verbose >= 2)}
    elif "TimeLimit" in solver_options:
        # GAMBIARRA: "cvxpy throws error when Gurobi solver encounters time limit"
        cp.settings.ERROR = [cp.settings.USER_LIMIT]
        cp.settings.SOLUTION_PRESENT = [
            cp.settings.OPTIMAL, cp.settings.OPTIMAL_INACCURATE,
            cp.settings.SOLVER_ERROR
        ]
        # CONFERIR: https://github.com/cvxgrp/cvxpy/issues/735

    if verbose >= 2: print("\n", "# # # # # " * 9, "\n", sep="")
    problem.solve(solver=cp.GUROBI, **solver_options)
    if verbose >= 2: print("\n", "# # # # # " * 9, "\n", sep="")

    results_dict = {
        "alpha": alpha,
        "beta": beta,
        "gamma": gamma,
        "Z": Z,
        "Z_array": None,
        "objective": objective,
        "constraints": constraints,
        "problem": problem,
        "load": load,
        "load_cost": load_cost,
        "penalty_1": penalty_1,
        "penalty_2": penalty_2,
        "same_day_bonus": same_day_bonus
    }

    if results_dict["Z"] is not None:
        results_dict["Z_array"] = np.asarray(Z.value + 0.1, dtype=int)
        if problem.status != cp.OPTIMAL:
            print(
                "# WARNING. Talvez tenha atingido o tempo limite sem otimalidade?"
            )
            print("Status do problema: {}".format(problem.status))
        if verbose: show_results(results_dict)

    else:
        print("# ERRO: o solver não encontrou uma solução!")
        print("Status do problema: {}".format(problem.status))
        print("# Para as disponibilidades dadas, talvez não seja")
        print("# possível obter `min_staff` pessoas por turno?")

    return results_dict
def get_perturbation(source_vector, target_vector, convert_matrix, obj):
    n = source_vector.size

    # Declare variable named 'perturb' to optimize. It is a 1d array/vector of size same as source_vector
    # perturb is basically delta1 in 1dimension
    # This method is where all the magic happens so read the details below to understand
    # 1. perturb(or delta1) is an array/vector that can assume a lot of possible elements while being of size n
    #    Further multiple sets(arrays) of n size can satisfy perturb
    # 2. This method computes all the possible elements and sets that perturb can have or be while maintaining some rules
    # 3. Rule a: Each element of (perturb when added with source_vector) should remain between 0 and 255(allowed pixel)
    #    Rule b: delta2 should be bounded by(less than) 0.01*255
    #            This kinda denotes that the difference between target image and output image should not exceed this value
    #            delta2 = CL(perturb + source_vector) - target_vector; since attack_vector = CL*(perturb + source_vector)
    #    Rule c: As we know that there could be multiple sets of perturb that could satisfy Rule a and b, therefore we
    #            find the Lsquare norm(which is basically squareroot of(sum of squares of all elements of perturb).
    #            In a way it gives the magnitude of an array/vector, our job is to find the set that has the min/max
    #            Lsquare Norm off all the possible sets
    perturb = cp.Variable(n)

    # Create the function to be Maximised/Minimised (||delta1||2 --> L^2 norm of delta1)
    function = cp.norm(perturb)

    # Declare objective function based on value of obj
    if obj == 'max':
        objective = cp.Maximize(function)  # Rule c
    else:
        objective = cp.Minimize(function)  # Rule c

    constraints = []
    # set up constraints
    # Rule a:
    constraints += [source_vector + perturb >= 0]
    constraints += [source_vector + perturb <= 255]

    # Rule b:
    # if the number of columns of convert_matrix coincide with the number of rows of source_vector(when its column vector)
    if convert_matrix[0].size == source_vector.size:
        constraints += [
            cp.norm_inf((convert_matrix *
                         (source_vector + perturb)) - target_vector) <=
            (0.01 * 255)
        ]
    else:  # else Transpose convert_matrix to make them coincide (usually happens when source_vector is a row vector)
        constraints += [
            cp.norm_inf((convert_matrix.T *
                         (source_vector + perturb)) - target_vector) <=
            (0.01 * 255)
        ]

    # Create problem with objective and constraints
    prob = cp.Problem(objective, constraints)

    # Launch the solver
    prob.solve()

    # Testing:
    print(prob.status)
    print(perturb.value.shape)

    # Solution array/vector is stored in the value field of perturb
    return perturb.value
Example #13
0
def cvx_inequality_time_graphical_lasso(S, K_init, max_iter, loss, C, theta,
                                        psi, gamma, tol):
    """Inequality constrained time-varying graphical LASSO solver.

    Solves the following problem via ADMM:
        min sum_{i=1}^T ||K_i||_{od,1} + beta sum_{i=2}^T Psi(K_i - K_{i-1})
        s.t. objective =< c_i for i = 1, ..., T

    where S_i = (1/n_i) X_i^T X_i is the empirical covariance of data
    matrix X (training observations by features).

    Parameters
    ----------
    emp_cov : ndarray, shape (n_features, n_features)
        Empirical covariance of data.
    alpha, beta : float, optional
        Regularisation parameter.
    rho : float, optional
        Augmented Lagrangian parameter.
    max_iter : int, optional
        Maximum number of iterations.
    n_samples : ndarray
        Number of samples available for each time point.
    gamma: float, optional
        Kernel parameter when psi is chosen to be 'kernel'.
    tol : float, optional
        Absolute tolerance for convergence.
    rtol : float, optional
        Relative tolerance for convergence.
    return_history : bool, optional
        Return the history of computed values.
    return_n_iter : bool, optional
        Return the number of iteration before convergence.
    verbose : bool, default False
        Print info at each iteration.
    update_rho_options : dict, optional
        Arguments for the rho update.
        See regain.update_rules.update_rho function for more information.
    compute_objective : bool, default True
        Choose to compute the objective value.
    init : {'empirical', 'zero', ndarray}
        Choose how to initialize the precision matrix, with the inverse
        empirical covariance, zero matrix or precomputed.

    Returns
    -------
    K : numpy.array, 3-dimensional (T x d x d)
        Solution to the problem for each time t=1...T .
    history : list
        If return_history, then also a structure that contains the
        objective value, the primal and dual residual norms, and tolerances
        for the primal and dual residual norms at each iteration.

    """

    if loss == 'LL':
        loss_function = neg_logl
    else:
        loss_function = dtrace

    T, p, _ = S.shape
    K = [cp.Variable(shape=(p, p), PSD=True) for t in range(T)]
    # Z_1 = [cp.Variable(shape=(p, p), PSD=True) for t in range(T-1)]
    # Z_2 = [cp.Variable(shape=(p, p), PSD=True) for t in range(T-1)]

    if psi == 'laplacian':
        objective = cp.Minimize(
            theta * cp.sum(
                [cp.norm(K[t] - cp.diag(cp.diag(K[t])), 1)
                 for t in range(T)]) + (1 - theta) *
            cp.sum([cp.norm(K[t] - K[t - 1], 'fro') for t in range(1, T)]))
    elif psi == 'l1':
        objective = cp.Minimize(theta * cp.sum([
            cp.norm(K[t] - cp.diag(cp.diag(K[t])), 1) for t in range(T)
        ]) + (1 - theta) * cp.sum(
            [cp.sum(cp.norm1(K[t] - K[t - 1], axis=1)) for t in range(1, T)]))
    elif psi == 'l2':
        objective = cp.Minimize(theta * cp.sum(
            [cp.norm(K[t] - cp.diag(cp.diag(K[t])), 1)
             for t in range(T)]) + (1 - theta) * cp.sum([
                 cp.sum(cp.norm(K[t] - K[t - 1], p=2, axis=1))
                 for t in range(1, T)
             ]))
    elif psi == 'linf':
        objective = cp.Minimize(theta * cp.sum(
            [cp.norm(K[t] - cp.diag(cp.diag(K[t])), 1)
             for t in range(T)]) + (1 - theta) * cp.sum([
                 cp.sum(cp.norm_inf(K[t] - K[t - 1], axis=1))
                 for t in range(1, T)
             ]))

    # if loss_function == neg_logl:
    constraints = [(cp.sum(cp.multiply(K[t], S[t])) - cp.log_det(K[t]) <= C[t])
                   for t in range(T)]
    # [(cp.trace(K[t] @ S[t]) - cp.log_det(K[t]) <= C[t]) for t in range(T)] # + \
    # [(Z_1[t] == K[t]) for t in range(T-1)] + \
    # [(Z_2[t] == K[t+1]) for t in range(T-1)]
    # else:
    #     constraints = [(cp.trace(K[t] @ K[t] @ S[t]) - cp.trace(K[t]) <= C[t]) for t in range(T)] # + \
    #                     # [(Z_1[t] == K[t]) for t in range(T-1)] + \
    #                     # [(Z_2[t] == K[t+1]) for t in range(T-1)]

    prob = cp.Problem(objective, constraints)
    # prob.solve(solver=cp.SCS, max_iters=np.int(max_iter), eps=tol, verbose=True)
    prob.solve(solver=cp.MOSEK, verbose=True)

    print(prob.status)
    print(prob.value)

    K = np.array([k.value for k in K])
    covariance_ = np.array([linalg.pinvh(k) for k in K])
    return_list = [K, covariance_]
    return return_list
Example #14
0
    def test_is_dcp(self):
        self.assertEqual(cp.Minimize(cp.norm_inf(self.x)).is_dcp(), True)
        self.assertEqual(cp.Minimize(-cp.norm_inf(self.x)).is_dcp(), False)

        self.assertEqual(cp.Maximize(cp.norm_inf(self.x)).is_dcp(), False)
        self.assertEqual(cp.Maximize(-cp.norm_inf(self.x)).is_dcp(), True)
Example #15
0
def sequential_lp(f, x0, jac, search_constraints = None,
	norm = 2, trajectory = trajectory_linear, obj_lb = None, obj_ub = None,
	constraints = None, constraint_grads = None, constraints_lb = None, constraints_ub = None,
	maxiter = 100, bt_maxiter = 50, domain = None,
	tol_dx = 1e-10, tol_obj = 1e-10,  verbose = False, **kwargs):
	r""" Solves a nonlinear optimization problem by a sequence of linear programs


	Given the optimization problem

	.. math::

		\min{\mathbf{x} \in \mathbb{R}^m} &\ \| \mathbf{f}(\mathbf{x}) \|_p \\
		\text{such that} & \  \text{lb} \le \mathbf{f} \le \text{ub} \\
			& \ \text{constraint_lb} \le \mathbf{g} \le \text{constraint_ub}

	this function solves this problem by linearizing both the objective and constraints
	and solving a sequence of disciplined convex problems.


	Parameters
	----------
	norm: [1,2, np.inf, None, 'hinge']
		If hinge, sum of values of the objective exceeding 0.

	
	References
	----------
	.. FS89


	"""
	assert norm in [1,2,np.inf, None, 'hinge'], "Invalid norm specified."

	if search_constraints is None:
		search_constraints = lambda x, p: []
	
	if domain is None:
		domain = UnboundedDomain(len(x0))

	if constraints is None:
		constraints = []
	if constraint_grads is None:
		constraint_grads = []

	assert len(constraints) == len(constraint_grads), "Must provide same number of constraints as constraint gradients"

	if constraints_lb is None:
		constraints_lb = -np.inf*np.ones(len(constraints))
	
	if constraints_ub is None:
		constraints_ub = np.inf*np.ones(len(constraints))


	# The default solver for 1/inf-norm doesn't converge sharp enough, but ECOS does.	
	if 'solver' not in kwargs:
		kwargs['solver'] = 'ECOS'


	if norm in [1,2, np.inf]:
		objfun = lambda fx: np.linalg.norm(fx, ord = norm)
	elif norm == 'hinge':
		objfun = lambda fx: np.sum(np.maximum(fx, 0))
	else:
		objfun = lambda fx: float(fx)

	# evalutate KKT norm
	def kkt_norm(fx, jacx):
		kkt_norm = np.nan
		if norm == np.inf:
			# TODO: allow other constraints into the solution
			t = objfun(fx)
			obj_grad = np.zeros(len(x)+1)
			obj_grad[-1] = 1.
			con = np.hstack([fx - t, -fx -t])
			con_grad = np.zeros((2*len(fx),len(x)+1))
			con_grad[:len(fx),:-1] = jacx
			con_grad[:len(fx),-1] = -1.
			con_grad[len(fx):,:-1] = -jacx
			con_grad[len(fx):,-1] = -1.

			# Find the active constraints (which have non-zero Lagrange multipliers)
			I = np.abs(con) < 1e-10
			lam, kkt_norm = scipy.optimize.nnls(con_grad[I,:].T, -obj_grad)
		elif norm == 1:
			t = np.abs(fx)
			obj_grad = np.zeros(len(x) + len(fx))
			obj_grad[len(x):] = 1.
			con = np.hstack([fx - t, -fx-t])
			con_grad = np.zeros((2*len(fx), len(x)+len(fx)))
			con_grad[:len(fx),:len(x)] = jacx
			con_grad[:len(fx),len(x):] = -1.
			con_grad[len(fx):,:len(x)] = -jacx
			con_grad[len(fx):,len(x):] = -1.
			I = np.abs(con) == 0.
			lam, kkt_norm = scipy.optimize.nnls(con_grad[I,:].T, -obj_grad)

		elif norm == 2:
			kkt_norm = np.linalg.norm(jacx.T.dot(fx))

		# TODO: Should really orthogonalize against unallowed search directions
		#err = con_grad[I,:].T.dot(lam) + obj_grad
		#print err
	
		return kkt_norm

	# Start optimizaiton loop
	x = np.copy(x0)
	try:
		fx = np.array(f(x))
	except TypeError:
		fx = np.array([fi(x) for fi in f]).reshape(-1,)

	objval = objfun(fx)

	try:
		jacx = jac(x)
	except TypeError:
		jacx = np.array([jaci(x) for jaci in jac]).reshape(len(fx), len(x))

	if verbose:
		print('iter |     objective     |  norm px | TR radius | KKT norm | violation |')
		print('-----|-------------------|----------|-----------|----------|-----------|')
		print('%4d | %+14.10e |          |           | %8.2e |           |' % (0, objval, kkt_norm(fx, jacx))) 

	Delta = 1.

	for it in range(maxiter):
	
		# Search direction
		p = cp.Variable(len(x))

		# Linearization of the objective function
		f_lin = fx + p.__rmatmul__(jacx)

		if norm == 1: obj = cp.norm1(f_lin)
		elif norm == 2: obj = cp.norm(f_lin)
		elif norm == np.inf: obj = cp.norm_inf(f_lin)
		elif norm == 'hinge': obj = cp.sum(cp.pos(f_lin))
		elif norm == None: obj = f_lin
		else: raise NotImplementedError
		# Now setup constraints
		nonlinear_constraints = []

		# First, constraints on "f"
		if obj_lb is not None:
			nonlinear_constraints.append(obj_lb <= f_lin)
		if obj_ub is not None:
			nonlinear_constraints.append(f_lin <= obj_ub)  
		
		# Next, we add other nonlinear constraints
		for con, congrad, con_lb, con_ub in zip(constraints, constraint_grads, constraints_lb, constraints_ub):
			conx = con(x)
			congradx = congrad(x)
			#print "conx", conx, congradx
			if np.isfinite(con_lb):
				nonlinear_constraints.append(con_lb <= conx + p.__rmatmul__(congradx) )
			if np.isfinite(con_ub):
				nonlinear_constraints.append(conx + p.__rmatmul__(congradx) <= con_ub )

		# Constraints on the search direction specified by user
		search_step_constraints = search_constraints(x, p)

		# Append constraints from the domain of x
		domain_constraints = domain._build_constraints(x + p)

		stop = False
		for it2 in range(bt_maxiter):
			active_constraints = nonlinear_constraints + domain_constraints + search_step_constraints

			if it2 > 0:
				trust_region_constraints = [cp.norm(p) <= Delta]
				active_constraints += trust_region_constraints

			# Solve for the search direction

			with warnings.catch_warnings():
				warnings.simplefilter('ignore', PendingDeprecationWarning)
				try:
					problem = cp.Problem(cp.Minimize(obj), active_constraints)
					problem.solve(**kwargs)
					status = problem.status
				except cp.SolverError:
					if it2 == 0:
						status = 'unbounded'
					else:
						status = cp.SolverError

			if (status == 'unbounded' or status == 'unbounded_inaccurate') and it2 == 0:
				# On the first step, the trust region is off, allowing a potentially unbounded domain
				pass
			elif status in ['optimal', 'optimal_inaccurate']:
				# Otherwise, we've found a feasible step
				px = p.value
				# Evaluate new point along the trajectory
				x_new = trajectory(x, px, 1.)

				# Check for movement of the point
				if np.all(np.isclose(x, x_new, rtol = tol_dx, atol = 0)):
					stop = True
					break

				# Evaluate value at new point
				try:
					fx_new = np.array(f(x_new))
				except TypeError:
					fx_new = np.array([fi(x_new) for fi in f]).reshape(-1,)
				objval_new = objfun(fx_new)

				constraint_violation = 0.
				if obj_lb is not None:
					I = ~(obj_lb <= fx_new)
					constraint_violation += np.linalg.norm((fx_new - obj_lb)[I], 1)
				if obj_ub is not None:
					I = ~(fx_new <= obj_ub)
					constraint_violation += np.linalg.norm((fx_new - obj_ub)[I], 1)

				if objval_new < objval and np.isclose(constraint_violation, 0., rtol = 1e-10, atol = 1e-10):
					x = x_new
					fx = fx_new

					if np.abs(objval_new - objval) < tol_obj:
						stop = True
					objval = objval_new
					Delta = max(1., np.linalg.norm(px))
					break

				Delta *=0.5
		
			else:
				warnings.warn("Could not find acceptible step; stopping prematurely; %s" % (status,) )
				stop = True
				px = np.zeros(x.shape)
				
			#elif status in ['unbounded', 'unbounded_inaccurate']:
			#	raise UnboundedException
			#elirf status in ['infeasible']:
			#	raaise InfeasibleException 
			#else:
			#	raise Exception(status)
	
		if it2 == bt_maxiter-1:
			stop = True

		# Update the jacobian information
		try:
			jacx = jac(x)
		except TypeError:
			jacx = np.array([jaci(x) for jaci in jac]).reshape(len(fx), len(x))

		if verbose:
			print('%4d | %+14.10e | %8.2e |  %8.2e | %8.2e |  %8.2e |' 
				% (it+1, objval, np.linalg.norm(px), Delta, kkt_norm(fx, jacx), constraint_violation))
		if stop:
			break	

	return x