Example #1
0
def test_consensus():
    n = 100
    k = 4
    np.random.seed(0)

    proxes = []
    for _ in range(k):
        A = np.random.randn(n, n)
        b = np.random.randn(n)
        x = cvx.Variable(n)

        obj = cvx.Minimize(cvx.norm(A * x - b))
        prob = cvx.Problem(obj)

        prox = Prox(prob, {'x': x})
        proxes += [prox]

    admm = ADMM(proxes, rho=1)
    admm.step(100)

    # check that the residuals are relatively small
    assert 1e-7 <= admm.infos[-1]['r'] <= 1e-4
    assert 1e-7 <= admm.infos[-1]['s'] <= 1e-4

    # a few more admm steps should make the residuals very small
    admm.step(100)

    assert admm.infos[-1]['r'] <= 1e-8
    assert admm.infos[-1]['s'] <= 1e-8
Example #2
0
def test_defaults():
    prob, xvars = example()
    prox = Prox(prob, xvars)

    assert prox.settings == dict(eps=1e-3, max_iters=100, verbose=False)

    # make sure the CySCS settings are set to the correct defaults
    assert prox._work.settings['eps'] == 1e-3
    assert prox._work.settings['max_iters'] == 100
    assert not prox._work.settings['verbose']
Example #3
0
def test1():
    prob, xvars = example()
    prox = Prox(prob, xvars)

    x = prox()
    # assert that info has correct keys
    for key in 'status', 'iter', 'time':
        assert key in prox.info

    assert 'x' in x

    # test that SCS settings get passed down
    prox.update_settings(eps=4.5, max_iters=123)
    assert prox.settings['eps'] == 4.5
    assert prox.settings['max_iters'] == 123

    # note: settings don't actually get passed to CySCS until we call the CySCS solve internally, i.e., compute the prox
    assert prox._work.settings['eps'] != 4.5
    assert prox._work.settings['max_iters'] != 123
    prox()
    assert prox._work.settings['eps'] == 4.5
    assert prox._work.settings['max_iters'] == 123
Example #4
0
def test1():
    prob, xvars = example()
    prox = Prox(prob, xvars)

    x = prox()
    # assert that info has correct keys
    for key in 'status', 'iter', 'time':
        assert key in prox.info

    assert 'x' in x

    # test that SCS settings get passed down
    prox.update_settings(eps=4.5, max_iters=123)
    assert prox.settings['eps'] == 4.5
    assert prox.settings['max_iters'] == 123

    # note: settings don't actually get passed to CySCS until we call the CySCS solve internally, i.e., compute the prox
    assert prox._work.settings['eps'] != 4.5
    assert prox._work.settings['max_iters'] != 123
    prox()
    assert prox._work.settings['eps'] == 4.5
    assert prox._work.settings['max_iters'] == 123
Example #5
0
def test1():
    prob, xvars = example()
    prox = Prox(prob, xvars)

    assert prox.settings == dict(eps=1e-3, max_iters=100, verbose=False)

    prox.update_settings(eps=1e-7)
    assert prox.settings['eps'] == 1e-7

    with pytest.raises(ValueError):
        prox.update_settings(cows=17)

    # won't catch the bad key here
    prox.settings['cows'] = 17
    # but will catch it when prox is run
    with pytest.raises(ValueError):
        prox()
Example #6
0
def test1():
    prob, xvars = example()
    prox = Prox(prob, xvars)

    assert prox.settings == dict(eps=1e-3, max_iters=100, verbose=False)

    prox.update_settings(eps=1e-7)
    assert prox.settings['eps'] == 1e-7

    with pytest.raises(ValueError):
        prox.update_settings(cows=17)

    # won't catch the bad key here
    prox.settings['cows'] = 17
    # but will catch it when prox is run
    with pytest.raises(ValueError):
        prox()
Example #7
0
    def test_memory():
        prob, x_vars, _ = example_rand(100, 50)
        prox = Prox(prob, x_vars, verbose=False, max_iters=20, eps=1e-7)
        x0 = prox()

        num_checks = 100
        check_iters = 100

        mem = np.zeros(num_checks)

        # a few iterations allows the memory variation to settle down
        for _ in range(check_iters * 3):
            x0 = prox(x0)

        # see if memory grows with iterations
        for i in range(num_checks * check_iters):
            x0 = prox(x0)

            if (i + 1) % check_iters == 0:
                m = get_mem_MB()
                mem[i // check_iters] = m

        assert np.var(mem) / np.mean(mem) <= 1e-6
Example #8
0
def admm_inner_iter(data):
    (idx, orig_prob, prox, rho_val, gamma_merit, max_iter,
     random_z, polish_best, seed, sigma, show_progress, neighbor_func, polish_func,
     prox_polished, polish_depth, lower_bound, alpha, args, kwargs) = data
    noncvx_vars = get_noncvx_vars(orig_prob)

    np.random.seed(idx + seed)
    random.seed(idx + seed)
    # Augmented objective.
    # gamma = cvx.Parameter(sign="positive")
    merit_func = orig_prob.objective.args[0]
    for constr in orig_prob.constraints:
        merit_func += gamma_merit*get_constr_error(constr)

    # Form ADMM problem.
    # obj = orig_prob.objective.args[0]
    # for var in noncvx_vars:
    #     obj += (rho_val/2)*cvx.sum_squares(var - var.z + var.u)
    # prob = cvx.Problem(cvx.Minimize(obj), orig_prob.constraints)
    if prox is None:
        xvars = {var.id: var for var in orig_prob.variables()}
        prox = Prox(orig_prob, xvars)

    for var in noncvx_vars:
        # var.init_z(random=random_z)
        # var.init_u()
        if idx == 0 or not random_z:
            var.z.value = np.zeros(var.size)
        elif var.z.value is not None:
            var.z.value = np.random.normal(0, sigma, var.size)
        var.u.value = np.zeros(var.size)

    # x^k prev.
    old_vars = {var.id:np.zeros(var.size) for var in orig_prob.variables()}

    best_so_far = [np.inf, {v.id:np.zeros(v.size) for v in orig_prob.variables()}]
    cur_merit = best_so_far[0]
    # ADMM loop
    for k in range(max_iter):
        prev_merit = cur_merit
        try:
            # prob.solve(*args, **kwargs)
            x0 = {}
            for var in orig_prob.variables():
                x0[var.id] = var.value
            for var in noncvx_vars:
                x0[var.id] = var.z.value.A1 - var.u.value.A1
            x1 = prox(x0, rho_val)
            for var in orig_prob.variables():
                var.value = np.reshape(x1[var.id], var.size, order='F')
            # print "post solve cost", idx, k, orig_prob.objective.value
        except cvx.SolverError as e:
            pass
        if prox.info['status'] in ['Solved', 'Solved/Inaccurate']:
            for var in noncvx_vars:
                var.z.value = var.project(alpha*var.value + (1-alpha)*old_vars[var.id] + var.u.value)
                # var.z.value = var.project(np.random.randn(*var.size))
                # var.z.value = var.project(np.random.uniform(0, 1, size=var.size))
                var.u.value += alpha*var.value + (1-alpha)*old_vars[var.id] - var.z.value
            # Update previous iterate.
            old_vars = {var.id: var.value for var in orig_prob.variables()}

            if only_discrete(orig_prob):
                if polish_depth == 0:
                    noncvx_vars[0].value = noncvx_vars[0].z.value
                    cur_merit = orig_prob.objective.value
                    sltn = {var.id: var.value for var in orig_prob.variables()}
                elif neighbor_func is None:
                    cur_merit, sltn = neighbor_search(merit_func, old_vars, best_so_far,
                                                      idx, polish_depth)
                else:
                    sltn = noncvx_vars[0].z.value.A.copy()
                    noncvx_vars[0].value = sltn
                    cur_merit = orig_prob.objective.value

                    for i in range(polish_depth):
                        prev_merit = cur_merit
                        cur_merit, sltn = neighbor_func(sltn, cur_merit)
                        if (prev_merit - cur_merit)/(prev_merit + 1) < 1e-3:
                            break
                    sltn = {noncvx_vars[0].id: sltn}
            else:
                if polish_func is None:
                    # Try to polish.
                    try:
                        polish_opt_val, status = polish(orig_prob, polish_depth, *args, **kwargs)
                        # print "post polish cost", idx, k, orig_prob.objective.value
                    except cvx.SolverError as e:
                        polish_opt_val = None
                        status = cvx.SOLVER_ERROR

                    # print "polish_opt_val", polish_opt_val
                    if status not in [cvx.OPTIMAL, cvx.OPTIMAL_INACCURATE]:
                        # Undo change in var.value.
                        for var in orig_prob.variables():
                            if isinstance(var, NonCvxVariable):
                                var.value = var.z.value
                            else:
                                var.value = old_vars[var.id]

                    cur_merit = merit_func.value
                    sltn = {v.id:v.value for v in orig_prob.variables()}
                else:
                    sltn = {}
                    for var in orig_prob.variables():
                        sltn[var.id] = var.value
                    for var in noncvx_vars:
                        sltn[var.id] = var.z.value
                    prev_merit = np.inf
                    for i in range(polish_depth):
                        cur_merit, sltn = polish_func(sltn)
                        if (prev_merit - cur_merit)/(prev_merit + 1) < 1e-5:
                            break
                        prev_merit = cur_merit

            if show_progress and idx == 0:
                print("objective", idx, k, cur_merit, best_so_far[0])
            if cur_merit < best_so_far[0]:
                best_so_far[0] = cur_merit
                best_so_far[1] = sltn

            # # Restore variable values.
            # for var in noncvx_vars:
            #     var.value = var.z.value

            # Termination conditions.
            if best_so_far[0] - lower_bound <= 1e-4:# or \
               # abs(cur_merit - prev_merit) + abs(cur_merit - prev_merit)/(prev_merit + 1) < 1e-4:
                return best_so_far

        else:
            print(prox.info['status'])
            break

    return best_so_far
Example #9
0
def admm(self, rho=None, max_iter=50, restarts=5, alpha=1.8,
         random=False, sigma=1.0, gamma=1e6, polish_best=True,
         num_procs=None, parallel=True, seed=1, show_progress=False,
         prox_polished=False, polish_depth=5,
         neighbor_func=None, polish_func=None,
         *args, **kwargs):
    # rho is a list of values, one for each restart.
    if rho is None:
        rho = [np.random.uniform() for i in range(restarts)]
    else:
        assert len(rho) == restarts
    # num_procs is the number of processors to launch.
    if num_procs is None:
        num_procs = multiprocessing.cpu_count()

    # Construct the relaxation.
    if type(self.objective) == cvx.Minimize:
        rel_obj = self.objective
    else:
        rel_obj = -self.objective
    rel_constr = self.constraints
    for var in get_noncvx_vars(self):
        rel_constr += var.relax()
    rel_prob = cvx.Problem(rel_obj, rel_constr)

    # HACK skip this.
    # lower_bound = rel_prob.solve(*args, **kwargs)
    lower_bound = -np.inf
    if show_progress:
        print("lower bound =", lower_bound)

    # Algorithm.
    if parallel:
        pool = multiprocessing.Pool(num_procs)
        tmp_prob = cvx.Problem(rel_prob.objective, rel_prob.constraints)
        best_per_rho = pool.map(admm_inner_iter,
            [(idx, tmp_prob, None, rho_val, gamma, max_iter,
              random, polish_best, seed, sigma, show_progress, neighbor_func, polish_func,
              prox_polished, polish_depth, lower_bound, alpha, args, kwargs) for idx, rho_val in enumerate(rho)])
        pool.close()
        pool.join()
    else:
        xvars = {var.id: var for var in rel_prob.variables()}
        prox = Prox(rel_prob, xvars)
        best_per_rho = list(map(admm_inner_iter,
            [(idx, rel_prob, prox, rho_val, gamma, max_iter,
              random, polish_best, seed, sigma, show_progress, neighbor_func, polish_func,
              prox_polished, polish_depth, lower_bound, alpha, args, kwargs) for idx, rho_val in enumerate(rho)]))
    # Merge best so far.
    argmin = min([(val[0], idx) for idx, val in enumerate(best_per_rho)])[1]
    best_so_far = best_per_rho[argmin]
    #print "best found", best_so_far[0]
    # Unpack result.
    for var in self.variables():
        var.value = best_so_far[1][var.id]

    residual = cvx.Constant(0)
    for constr in self.constraints:
        residual += get_constr_error(constr)

    return self.objective.value, residual.value
Example #10
0
def admm_inner_iter(data):
    (
        idx,
        orig_prob,
        prox,
        rho_val,
        gamma_merit,
        max_iter,
        random_z,
        polish_best,
        seed,
        sigma,
        show_progress,
        neighbor_func,
        polish_func,
        prox_polished,
        polish_depth,
        lower_bound,
        alpha,
        args,
        kwargs,
    ) = data
    noncvx_vars = get_noncvx_vars(orig_prob)

    np.random.seed(idx + seed)
    random.seed(idx + seed)
    # Augmented objective.
    # gamma = cvx.Parameter(nonneg=True)
    merit_func = orig_prob.objective.args[0]
    for constr in orig_prob.constraints:
        merit_func += gamma_merit * get_constr_error(constr)

    # Form ADMM problem.
    # obj = orig_prob.objective.args[0]
    # for var in noncvx_vars:
    #     obj += (rho_val/2)*cvx.sum_squares(var - var.z + var.u)
    # prob = cvx.Problem(cvx.Minimize(obj), orig_prob.constraints)
    if prox is None:
        xvars = {var.id: var for var in orig_prob.variables()}
        prox = Prox(orig_prob, xvars)

    for var in noncvx_vars:
        # var.init_z(random=random_z)
        # var.init_u()
        if idx == 0 or not random_z:
            var.z.value = np.zeros(var.shape)
        elif var.z.value is not None:
            var.z.value = np.random.normal(0, sigma, var.shape)
        var.u.value = np.zeros(var.shape)

    # x^k prev.
    old_vars = {var.id: np.zeros(var.shape) for var in orig_prob.variables()}

    best_so_far = [np.inf, {v.id: np.zeros(v.shape) for v in orig_prob.variables()}]
    cur_merit = best_so_far[0]
    # ADMM loop
    for k in range(max_iter):
        prev_merit = cur_merit
        try:
            # prob.solve(*args, **kwargs)
            x0 = {}
            for var in orig_prob.variables():
                x0[var.id] = var.value
            for var in noncvx_vars:
                x0[var.id] = (
                    np.asarray(var.z.value, order="F").ravel()
                    - np.asarray(var.u.value, order="F").ravel()
                )
            x1 = prox(x0, rho_val)
            for var in orig_prob.variables():
                # Bypass the projection step when going through the setter, and instead
                # set the private value directly. Should probably revisit to see if some
                # longer-term approach we want to take; it's obviously a bit uncomfortable
                # to bypass the setter.
                var._value = np.reshape(x1[var.id], var.shape, order="F")
        except cp.SolverError:
            pass
        if prox.info["status"] in ["Solved", "Solved/Inaccurate"]:
            for var in noncvx_vars:
                var.z.value = var.project(
                    alpha * var.value + (1 - alpha) * old_vars[var.id] + var.u.value
                )
                var.u.value += (
                    alpha * var.value + (1 - alpha) * old_vars[var.id] - var.z.value
                )
            # Update previous iterate.
            old_vars = {var.id: var.value for var in orig_prob.variables()}

            if only_discrete(orig_prob):
                if polish_depth == 0:
                    noncvx_vars[0].value = noncvx_vars[0].z.value
                    cur_merit = orig_prob.objective.value
                    sltn = {var.id: var.value for var in orig_prob.variables()}
                elif neighbor_func is None:
                    cur_merit, sltn = neighbor_search(
                        merit_func, old_vars, best_so_far, idx, polish_depth
                    )
                else:
                    sltn = noncvx_vars[0].z.value.copy()
                    noncvx_vars[0].value = sltn
                    cur_merit = orig_prob.objective.value

                    for i in range(polish_depth):
                        prev_merit = cur_merit
                        cur_merit, sltn = neighbor_func(sltn, cur_merit)
                        if (prev_merit - cur_merit) / (prev_merit + 1) < 1e-3:
                            break
                    sltn = {noncvx_vars[0].id: sltn}
            else:
                if polish_func is None:
                    # Try to polish.
                    try:
                        polish_opt_val, status = polish(
                            orig_prob, polish_depth, *args, **kwargs
                        )
                        # print "post polish cost", idx, k, orig_prob.objective.value
                    except cp.SolverError as e:
                        polish_opt_val = None
                        status = cp.SOLVER_ERROR

                    # print "polish_opt_val", polish_opt_val
                    if status not in [cp.OPTIMAL, cp.OPTIMAL_INACCURATE]:
                        # Undo change in var.value.
                        for var in orig_prob.variables():
                            if isinstance(var, NonCvxVariable):
                                var.value = var.z.value
                            else:
                                var.value = old_vars[var.id]

                    cur_merit = merit_func.value
                    sltn = {v.id: v.value for v in orig_prob.variables()}
                else:
                    sltn = {}
                    for var in orig_prob.variables():
                        sltn[var.id] = var.value
                    for var in noncvx_vars:
                        sltn[var.id] = var.z.value
                    prev_merit = np.inf
                    for i in range(polish_depth):
                        cur_merit, sltn = polish_func(sltn)
                        if (prev_merit - cur_merit) / (prev_merit + 1) < 1e-5:
                            break
                        prev_merit = cur_merit

            if show_progress and idx == 0:
                print("objective", idx, k, cur_merit, best_so_far[0])
            if cur_merit < best_so_far[0]:
                best_so_far[0] = cur_merit
                best_so_far[1] = sltn

            # # Restore variable values.
            # for var in noncvx_vars:
            #     var.value = var.z.value

            # Termination conditions.
            if best_so_far[0] - lower_bound <= 1e-4:  # or \
                # abs(cur_merit - prev_merit) + abs(cur_merit - prev_merit)/(prev_merit + 1) < 1e-4:
                return best_so_far

        else:
            print(prox.info["status"])
            break

    return best_so_far