예제 #1
0
def step_fw(objective_function, feasible_region, point_x, step_size_param):
    """
    Makes a Vanilla Frank-Wolfe step.

    Note that the VANILLA FW algorithm only uses the cartesian coordinates and does not
    use the active set or the barycentric coordinates for anything.
    """

    grad = objective_function.evaluate_grad(point_x.cartesian_coordinates)
    v = feasible_region.lp_oracle(grad)
    wolfe_gap = grad.dot(point_x.cartesian_coordinates - v)
    d = v - point_x.cartesian_coordinates
    alpha_max = 1.0
    alpha = step_size(
        objective_function,
        point_x.cartesian_coordinates,
        d,
        grad,
        alpha_max,
        step_size_param,
    )
    if alpha != alpha_max:
        flag, point_v = point_x.is_vertex_in_support(v)
        if flag == False:
            new_barycentric_coordinates = list(point_x.barycentric_coordinates)
            new_barycentric_coordinates.append(0.0)
            point_x = Point(
                point_x.cartesian_coordinates,
                tuple(new_barycentric_coordinates),
                point_v.support,
            )
        return point_x + alpha * (point_v - point_x), wolfe_gap, 0.0
    else:
        return Point(v, (1.0,), (v,)), wolfe_gap, 0.0
예제 #2
0
def away_step_fw(objective_function, feasible_region, point_x, step_size_param):
    """Makes a away-step Frank-Wolfe step."""

    grad = objective_function.evaluate_grad(point_x.cartesian_coordinates)
    v = feasible_region.lp_oracle(grad)
    point_a, index_max = feasible_region.away_oracle(grad, point_x)
    wolfe_gap = grad.dot(point_x.cartesian_coordinates - v)
    strong_wolfe_gap = grad.dot(point_a.cartesian_coordinates - v)
    if wolfe_gap > grad.dot(
        point_a.cartesian_coordinates - point_x.cartesian_coordinates
    ):
        alpha_max = 1.0
        alpha = step_size(
            objective_function,
            point_x.cartesian_coordinates,
            v - point_x.cartesian_coordinates,
            grad,
            alpha_max,
            step_size_param,
        )
        if alpha != alpha_max:
            flag, point_v = point_x.is_vertex_in_support(v)
            if flag == False:
                new_barycentric_coordinates = list(point_x.barycentric_coordinates)
                new_barycentric_coordinates.append(0.0)
                point_x = Point(
                    point_x.cartesian_coordinates,
                    tuple(new_barycentric_coordinates),
                    point_v.support,
                )
            return point_x + alpha * (point_v - point_x), wolfe_gap, strong_wolfe_gap
        else:
            return (
                Point(v, (1.0,), (v,)),
                wolfe_gap,
                strong_wolfe_gap,
            )
    else:
        alpha_max = point_x.barycentric_coordinates[index_max] / (
            1.0 - point_x.barycentric_coordinates[index_max]
        )
        alpha = step_size(
            objective_function,
            point_x.cartesian_coordinates,
            point_x.cartesian_coordinates - point_a.cartesian_coordinates,
            grad,
            alpha_max,
            step_size_param,
        )
        point_x = point_x + alpha * (point_x - point_a)
        if alpha == alpha_max:
            point_x = point_x.delete_vertex_in_support(index_max)
        return point_x, wolfe_gap, strong_wolfe_gap
예제 #3
0
def max_vertex(d, vertices):
    """
    Iterate over current active set and return vertex with greatest inner product.

    Parameters
    ----------
    d: np.ndarray
        Direction.
    vertices: tuple(np.ndarray) or list(np.ndarray)
        Tuple or list of vertices.

    Returns
    -------
    Point
    """

    max_prod = d.dot(vertices[0])
    max_ind = 0
    for i in range(1, len(vertices)):
        if d.dot(vertices[i]) > max_prod:
            max_prod = d.dot(vertices[i])
            max_ind = i
    barycentric = np.zeros(len(vertices))
    barycentric[max_ind] = 1.0
    return Point(vertices[max_ind], barycentric, vertices), max_ind
예제 #4
0
def dummy_call_argmin_quadratic_over_active_set():
    LOGGER.info("Compiling argmin_quadratic_over_active_set with numba jit.")
    try:
        active_set = (
            np.array([1.0, 0.0, 0.0]),
            np.array([0.0, 1.0, 0.0]),
        )
        point_reference = Point(
            np.array([0.5, 0.5, 0.0]),
            np.array([0.5, 0.5]),
            active_set,
        )

        _ = argmin_quadratic_over_active_set(
            1.0,
            np.array([1.0, 1.0, 1.0]),
            active_set,
            point_reference,
            "dual gap",
            10e-3,
        )
    except:
        LOGGER.info(
            "Compiling argmin_quadratic_over_active_set with numba jit failed."
        )
        return False

    LOGGER.info(
        "Compiling argmin_quadratic_over_active_set with numba jit done.")
    return True
예제 #5
0
def dipfw(objective_function, feasible_region, point_x, step_size_param):
    """Makes a decompositional invariant Frank-Wolfe step."""

    grad = objective_function.evaluate_grad(point_x.cartesian_coordinates)
    v = feasible_region.lp_oracle(grad)
    grad_aux = grad.copy()
    for i in range(len(grad_aux)):
        if point_x.cartesian_coordinates[i] == 0.0:
            grad_aux[i] = -1.0e15
    a = feasible_region.lp_oracle(-grad_aux)
    d = v - a
    alpha_max = calculate_stepsize(point_x.cartesian_coordinates, d)
    assert (
        step_size_param["type_step"] == "line_search"
    ), "DIPFW only accepts exact linesearch."
    alpha = step_size(
        objective_function,
        point_x.cartesian_coordinates,
        d,
        grad,
        alpha_max,
        step_size_param,
    )
    new_cartesian = point_x.cartesian_coordinates + alpha * d
    return (
        Point(new_cartesian, (1.0,), (new_cartesian,)),
        grad.dot(point_x.cartesian_coordinates - v),
        0.0,
    )
예제 #6
0
def pairwise_step_fw(objective_function, feasible_region, point_x, step_size_param):
    """Makes a pairwise-step Frank-Wolfe step."""

    grad = objective_function.evaluate_grad(point_x.cartesian_coordinates)
    v = feasible_region.lp_oracle(grad)
    wolfe_gap = grad.dot(point_x.cartesian_coordinates - v)
    point_a, index_max = feasible_region.away_oracle(grad, point_x)
    strong_wolfe_gap = grad.dot(point_a.cartesian_coordinates - v)
    # Find the weight of the extreme point a in the decomposition.
    alpha_max = point_x.barycentric_coordinates[index_max]
    alpha = step_size(
        objective_function,
        point_x.cartesian_coordinates,
        v - point_a.cartesian_coordinates,
        grad,
        alpha_max,
        step_size_param,
    )
    flag, point_v = point_x.is_vertex_in_support(v)
    if flag == False:
        new_barycentric_coordinates = list(point_x.barycentric_coordinates)
        new_barycentric_coordinates.append(0.0)
        point_x = Point(
            point_x.cartesian_coordinates,
            tuple(new_barycentric_coordinates),
            point_v.support,
        )
        new_barycentric_coordinates = list(point_a.barycentric_coordinates)
        new_barycentric_coordinates.append(0.0)
        point_a = Point(
            point_a.cartesian_coordinates,
            tuple(new_barycentric_coordinates),
            point_v.support,
        )
    point_x = point_x + alpha * (point_v - point_a)
    if alpha == alpha_max:
        point_x = point_x.delete_vertex_in_support(index_max)
    return point_x, wolfe_gap, strong_wolfe_gap
예제 #7
0
    def run(
        self,
        objective_function,
        feasible_region,
        point_initial,
        epsilon=0.0,
        initial_eta=None,
        initial_sigma=None,
        shared_buffers_dict=None,
        last_restart_iter=0,
        epsilon_f=1e-12,
    ):
        """
        Run PF-ACC given an initial point and an active set/feasible region.

        Parameters
        ----------
        objective_function: implemented _AbstractObjectiveFunction
            Objective function over which this algorithm optimizes.
        feasible_region: implemented _AbstractFeasibleRegion
            Feasible region over which this algorithm optimizes.
        point_initial: Point
            Initial point object.
        epsilon: float
            Accuracy w.r.t. to norm of gradient mapping.
        initial_eta: float
            Initial estimate of the smoothness parameter eta.
        initial_sigma: float
            Initial estimate of the strong convexity parameter sigma.
        shared_buffers_dict: dict
            If provided, updates the shared memory buffers.
        last_restart_iter: int
            The iteration since the last restart in PFLaCG.
        epsilon_f: float
            Accuaracy w.r.t. to primal gap to early halt algorithm.

        Returns
        -------
        list(run_status)
        """

        LOGGER.info(
            f"ACC process started at last_restart_iter = {last_restart_iter}")
        if len(feasible_region.vertices) <= 1:
            return point_initial, initial_eta, initial_sigma, 0

        # Precomputations
        matrix = np.vstack(feasible_region.vertices)
        base_quadratic = matrix.dot(matrix.T)

        # Initial shared buffers based on shared_buffers_dict
        if shared_buffers_dict:
            global_eta = shared_buffers_dict["global_eta"]
            global_sigma = shared_buffers_dict["global_sigma"]

            ret_x_cartesian_coordinates_shm = shared_memory.SharedMemory(
                name=shared_buffers_dict["ret_x_cartesian_coordinates"])
            ret_x_cartesian_coordinates = np.ndarray(
                shape=objective_function.dim,
                dtype=np.float64,
                buffer=ret_x_cartesian_coordinates_shm.buf,
            )

            ret_x_barycentric_coordinates_shm = shared_memory.SharedMemory(
                name=shared_buffers_dict["ret_x_barycentric_coordinates"])
            ret_x_barycentric_coordinates = np.ndarray(
                shape=len(feasible_region.vertices),
                dtype=np.float64,
                buffer=ret_x_barycentric_coordinates_shm.buf,
            )

            global_iter = shared_buffers_dict["global_iter"]
            ACC_paused_flag = shared_buffers_dict["ACC_paused_flag"]
            buffer_lock = shared_buffers_dict["buffer_lock"]
        else:
            global_eta, global_sigma = None, None

        # Initializtion
        point_x = point_initial
        if np.allclose(point_x.cartesian_coordinates, point_x.support[0]):
            point_y = Point(
                point_x.support[1],
                [1.0 if i == 1 else 0.0 for i in range(len(point_x.support))],
                point_x.support,
            )
        else:
            point_y = Point(
                point_x.support[0],
                [1.0 if i == 0 else 0.0 for i in range(len(point_x.support))],
                point_x.support,
            )

        # Guess a eta if initial_sigma is None
        if initial_sigma is None or initial_sigma == 0.0:
            x = point_x.cartesian_coordinates
            y = point_y.cartesian_coordinates
            initial_sigma = (
                2.0 * (objective_function.evaluate(y) -
                       objective_function.evaluate(x) -
                       np.dot(objective_function.evaluate_grad(x), y - x)) /
                (np.linalg.norm(y - x)**2))

        # Set initial_eta to initial_sigma if initial_eta is None
        if initial_eta is None or initial_eta == 0.0:
            initial_eta = initial_sigma
        eta = initial_eta
        sigma = initial_sigma
        LOGGER.info(f"initial_sigma = {initial_sigma}")
        LOGGER.info(f"initial_eta = {initial_eta}")
        iteration = 0

        # Early return if primal gap is small
        strong_wolfe_gap = compute_strong_wolfe_gap(point_x,
                                                    objective_function,
                                                    feasible_region)
        if strong_wolfe_gap <= epsilon_f:
            LOGGER.info("Early halting ACC with wolfe_gap <= epsilon_f")
            if shared_buffers_dict:
                buffer_lock.acquire()
                global_eta.value = eta
                global_sigma.value = sigma
                buffer_lock.release()
            return point_x, eta, sigma, iteration

        point_x_plus = argmin_quadratic_over_active_set(
            quadratic_coefficient=eta / 2.0,
            linear_vector=(objective_function.evaluate_grad(
                point_x.cartesian_coordinates) -
                           eta * point_x.cartesian_coordinates),
            active_set=feasible_region.vertices,
            point_reference=point_x,
            tolerance_type="gradient mapping",
            tolerance=eta / 32,
            base_quadratic=base_quadratic,
        )
        grad_mapping = (point_x.cartesian_coordinates -
                        point_x_plus.cartesian_coordinates)

        while np.linalg.norm(
                grad_mapping) > epsilon and strong_wolfe_gap > epsilon_f:
            (
                point_x,
                grad_mapping,
                strong_wolfe_gap,
                eta,
                sigma,
                _iteration,
            ) = self.ACC_iter(
                objective_function,
                feasible_region,
                point_initial=point_x,
                eta=eta,
                sigma=sigma,
                global_eta=global_eta,
                global_sigma=global_sigma,
                base_quadratic=base_quadratic,
                epsilon_f=epsilon_f,
            )
            iteration += _iteration

            LOGGER.info("ACC about to update buffer.")
            if shared_buffers_dict:
                buffer_lock.acquire()
                global_eta.value = eta
                global_sigma.value = sigma
                buffer_lock.release()
            while shared_buffers_dict:
                # if global_iter is None, then assume no iteration sync required.
                if global_iter:
                    with global_iter.get_lock():
                        _global_iter = global_iter.value
                else:
                    _global_iter = np.infty

                if _global_iter >= last_restart_iter + iteration or not self.iter_sync:
                    # Update shared buffers
                    buffer_lock.acquire()
                    ret_x_cartesian_coordinates[:] = point_x.cartesian_coordinates[:]
                    ret_x_barycentric_coordinates[:] = point_x.barycentric_coordinates[:]
                    buffer_lock.release()

                    # Continue with the next ACC
                    with ACC_paused_flag.get_lock():
                        ACC_paused_flag.value = 0
                    break
                else:
                    # Pausing ACC's execution and sleep for some time.
                    with ACC_paused_flag.get_lock():
                        ACC_paused_flag.value = 1
                    time.sleep(WAIT_TIME_FOR_LOCK)

        if shared_buffers_dict:
            buffer_lock.acquire()
            global_eta.value = eta
            global_sigma.value = sigma
            buffer_lock.release()
        return point_x, eta, sigma, iteration
예제 #8
0
    def run(
        self,
        objective_function,
        feasible_region,
        exit_criterion,
        point_initial=None,
    ):
        """
        Minimizing objective function over feasible region using PF-LaCG.

        Parameters
        ----------
        objective_function: implemented _AbstractObjectiveFunction
            Objective function over which this algorithm optimizes.
        feasible_region: implemented _AbstractFeasibleRegion
            Feasible region over which this algorithm optimizes.
        exit_criterion: ExitCriterion
            Conditions required for it to halt the execution.
        point_initial: Point
            Initial point object.

        Returns
        -------
        list(run_status)
        """

        if point_initial is None:
            vertex = feasible_region.initial_point.copy()
            point_initial = Point(vertex, (1.0, ), (vertex, ))
        else:
            point_initial = point_initial

        # Initialization
        strong_wolfe_gap_out = compute_strong_wolfe_gap(
            point_initial, objective_function, feasible_region)
        iteration = 0
        start_time = time.time()
        duration = 0.0
        num_halvings = 0
        f_val = objective_function.evaluate(
            point_initial.cartesian_coordinates)
        run_status = (
            iteration,
            duration,
            f_val,
            0.0,  # Dummy dual gap since we are concerned about SWG here
            strong_wolfe_gap_out,
        )
        run_history = [run_status]
        LOGGER.info(
            "Running PFLaCG: "
            "iteration = {1}, duration = {2:.{0}f}, "
            "f_val = {3:.{0}f}, dual_gap = {4:.{0}f}, SWG = {5:.{0}f}".format(
                DISPLAY_DECIMALS, *run_status))

        point_x_FAFW = point_initial
        point_x_ACC = point_initial
        active_set_ACC = point_initial.support
        strong_wolfe_gap_FAFW = strong_wolfe_gap_out
        strong_wolfe_gap_ACC = strong_wolfe_gap_out
        eta = None
        sigma = None

        # Create new shared memory buffers and buffer lock
        ret_x_cartesian_coordinates_shm = shared_memory.SharedMemory(
            create=True,
            size=np.zeros(shape=objective_function.dim,
                          dtype=np.float64).nbytes,
        )
        ret_x_cartesian_coordinates = np.ndarray(
            shape=objective_function.dim,
            dtype=np.float64,
            buffer=ret_x_cartesian_coordinates_shm.buf,
        )
        ret_x_cartesian_coordinates[:] = point_x_ACC.cartesian_coordinates[:]
        global_eta = Value("d", 0)
        global_sigma = Value("d", 0)
        ACC_paused_flag = Value("i", 0)
        buffer_lock = Lock()
        global_iter = Value("i", 0)

        ACC_restart_flag = True
        ACC_process_started = False
        while not exit_criterion.has_met_exit_criterion(run_status):
            # Set halving strong Wolfe gap
            target_accuracy = strong_wolfe_gap_FAFW * self.ratio
            LOGGER.info(f"SWG target accuracy = {target_accuracy}")
            num_halvings += 1

            if ACC_restart_flag:
                LOGGER.info("Restarting ACC")
                ret_x_barycentric_coordinates_shm = shared_memory.SharedMemory(
                    create=True,
                    size=np.zeros(shape=len(active_set_ACC),
                                  dtype=np.float64).nbytes,
                )
                ret_x_barycentric_coordinates = np.ndarray(
                    shape=len(active_set_ACC),
                    dtype=np.float64,
                    buffer=ret_x_barycentric_coordinates_shm.buf,
                )
                ret_x_barycentric_coordinates[:] = point_x_ACC.barycentric_coordinates[:]
                ret_x_cartesian_coordinates[:] = point_x_ACC.cartesian_coordinates[:]

                shared_buffers_dict = {
                    "buffer_lock":
                    buffer_lock,
                    "global_iter":
                    global_iter,
                    "ACC_paused_flag":
                    ACC_paused_flag,
                    "global_eta":
                    global_eta,
                    "global_sigma":
                    global_sigma,
                    "ret_x_cartesian_coordinates":
                    ret_x_cartesian_coordinates_shm.name,
                    "ret_x_barycentric_coordinates":
                    ret_x_barycentric_coordinates_shm.name,
                }

                LOGGER.info(
                    f"Creating ACC process with set size {len(active_set_ACC)}"
                )
                convex_hull_ACC = ConvexHull(active_set_ACC)
                ACC_process = Process(
                    target=self.ACC.run,
                    args=(
                        objective_function,
                        convex_hull_ACC,
                        point_x_ACC,
                        0.0,
                        eta,
                        sigma,
                        shared_buffers_dict,
                        iteration,
                        exit_criterion.criterion_value,
                    ),
                )
                if len(active_set_ACC) > 1:
                    LOGGER.info("Starting ACC process")
                    ACC_process.start()
                    ACC_process_started = True

            LOGGER.info("Running FAFW")
            # Run FAFW and wait for the output
            point_x_FAFW, _, strong_wolfe_gap_FAFW = self.FAFW.run(
                objective_function,
                feasible_region,
                point_x_FAFW,
                target_accuracy=target_accuracy,
                global_iter=global_iter,
            )
            with global_iter.get_lock():
                _global_iter = global_iter.value
            LOGGER.info(f"FAFW returned at global_iter = {_global_iter}")

            # if iteration sync, need to wait for ACC to complete same #iterations
            num_wait_interval = 0
            while self.iter_sync and ACC_process_started and ACC_process.is_alive(
            ):
                with ACC_paused_flag.get_lock():
                    _ACC_paused_flag = ACC_paused_flag.value
                if _ACC_paused_flag == 1:
                    # ACC has paused (the buffer is been updated since last ACC restart)
                    break
                else:
                    LOGGER.info("Waiting for ACC")
                    time.sleep(WAIT_TIME_FOR_LOCK)
                    num_wait_interval += 1
                    if num_wait_interval > MAX_NUM_WAIT_INTERVALS:
                        LOGGER.info("ACC timed out. Continuing with PFLaCG.")
                        break

            LOGGER.info("Acquiring buffer")
            # retrieve the most recent output
            buffer_lock.acquire()
            point_x_ACC = Point(
                np.copy(ret_x_cartesian_coordinates),
                np.copy(ret_x_barycentric_coordinates),
                active_set_ACC,
            )
            if ACC_process_started:
                sigma = global_sigma.value
                eta = global_eta.value
            buffer_lock.release()

            # Compute Strong Wolfe gap (or dual gap)
            strong_wolfe_gap_ACC_prev = strong_wolfe_gap_ACC
            strong_wolfe_gap_ACC = compute_strong_wolfe_gap(
                point_x_ACC, objective_function, feasible_region)

            if strong_wolfe_gap_FAFW <= min(strong_wolfe_gap_ACC,
                                            strong_wolfe_gap_ACC_prev / 2):
                # Terminate ACC process and set restart flag
                LOGGER.info("FAFW did better")
                if ACC_process_started and ACC_process.is_alive():
                    LOGGER.info("Terminating ACC")
                    ACC_process.terminate()
                    ACC_process.join()
                ACC_process_started = False
                with ACC_paused_flag.get_lock():
                    ACC_paused_flag.value = 0
                ret_x_barycentric_coordinates_shm.close()
                ret_x_barycentric_coordinates_shm.unlink()

                ACC_restart_flag = True
                point_x_ACC = point_x_FAFW
                active_set_ACC = point_x_FAFW.support

                # Set output points
                point_x_out = point_x_FAFW
                strong_wolfe_gap_out = strong_wolfe_gap_FAFW
            else:
                LOGGER.info("ACC did better")
                LOGGER.info("Not terminating ACC")
                # Allow ACC to continue its execution
                ACC_restart_flag = False

                # Couple FAFW by using the better point from ACC if condition satisfies
                if len(point_x_ACC.support) <= len(point_x_FAFW.support):
                    LOGGER.info("FAFW <- ACC")
                    point_x_FAFW = point_x_ACC
                    strong_wolfe_gap_FAFW = strong_wolfe_gap_ACC

                # Set output points
                point_x_out = point_x_ACC
                strong_wolfe_gap_out = strong_wolfe_gap_ACC

            # Append output points
            LOGGER.info("Outputting")
            with global_iter.get_lock():
                iteration = global_iter.value
            duration = time.time() - start_time
            f_val = objective_function.evaluate(
                point_x_out.cartesian_coordinates)
            run_status = (
                iteration,
                duration,
                f_val,
                0.0,  # Dummy dual gap since we are concerned about SWG here
                strong_wolfe_gap_out,
            )
            LOGGER.info(
                "Running PFLaCG: "
                "iteration = {1}, duration = {2:.{0}f},"
                " f_val = {3:.{0}f}, dual_gap = {4:.{0}f}, SWG = {5:.{0}f}".
                format(DISPLAY_DECIMALS, *run_status))
            run_history.append(run_status)

        # Cleaning up buffers and process
        ret_x_cartesian_coordinates_shm.close()
        ret_x_cartesian_coordinates_shm.unlink()
        if ACC_process_started and ACC_process.is_alive():
            ACC_process.terminate()
            ACC_process.join()

        return run_history
예제 #9
0
    def run(
        self,
        objective_function,
        feasible_region,
        exit_criterion,
        point_initial=None,
        save_and_output_results=True,
        global_iter=None,
    ):

        if point_initial is None:
            vertex = feasible_region.initial_point.copy()
            point_x = Point(vertex, (1.0,), (vertex,))
        else:
            point_x = point_initial

        start_time = time.time()
        grad = objective_function.evaluate_grad(point_x.cartesian_coordinates)

        iteration = 0
        duration = 0.0
        f_val = objective_function.evaluate(point_x.cartesian_coordinates)
        v = feasible_region.lp_oracle(grad)
        if self.fw_variant == "FW" or self.fw_variant == "DIPFW":
            strong_wolfe_gap = 0.0
        else:
            a, index_max = feasible_region.away_oracle(grad, point_x)
            strong_wolfe_gap = grad.dot(a.cartesian_coordinates - v)

        dual_gap = grad.dot(point_x.cartesian_coordinates - v)

        if self.fw_variant == "lazy" or self.fw_variant == "lazy quick exit":
            phi_val = [dual_gap]

        run_status = (iteration, duration, f_val, dual_gap, strong_wolfe_gap)
        if save_and_output_results:
            LOGGER.info(
                "Running " + str(self.fw_variant) + "({5}): "
                "iteration = {1:.{0}f}, duration = {2:.{0}f},"
                "f_val = {3:.{0}f}, dual_gap = {4:.{0}f}, strong_wolfe_gap = {5:.{0}f}".format(
                    DISPLAY_DECIMALS, *run_status, self.fw_variant
                )
            )
            run_history = [run_status]

        while True:
            point_x_prev = point_x
            if self.fw_variant == "AFW":
                point_x, dual_gap_prev, strong_wolfe_gap_prev = away_step_fw(
                    objective_function,
                    feasible_region,
                    point_x,
                    self.step_size_param,
                )
            if self.fw_variant == "PFW":
                point_x, dual_gap_prev, strong_wolfe_gap_prev = pairwise_step_fw(
                    objective_function,
                    feasible_region,
                    point_x,
                    self.step_size_param,
                )
            if self.fw_variant == "FW":
                point_x, dual_gap_prev, strong_wolfe_gap_prev = step_fw(
                    objective_function,
                    feasible_region,
                    point_x,
                    self.step_size_param,
                )
                if (
                    iteration % self.sampling_frequency == 0
                    and strong_wolfe_gap_prev is None
                ):
                    grad = objective_function.evaluate_grad(
                        point_x_prev.cartesian_coordinates
                    )
                    v = feasible_region.lp_oracle(grad)
                    point_a, indexMax = feasible_region.away_oracle(grad, point_x_prev)
                    dual_gap_prev = grad.dot(point_x_prev.cartesian_coordinates - v)
                    strong_wolfe_gap_prev = grad.dot(point_a.cartesian_coordinates - v)
            if self.fw_variant == "lazy":
                point_x, dual_gap_prev, strong_wolfe_gap_prev = fw_away_lazy(
                    objective_function,
                    feasible_region,
                    point_x,
                    self.step_size_param,
                    phi_val,
                )
                if iteration % self.sampling_frequency == 0 and (
                    strong_wolfe_gap_prev is None or dual_gap_prev is None
                ):
                    grad = objective_function.evaluate_grad(
                        point_x_prev.cartesian_coordinates
                    )
                    v = feasible_region.lp_oracle(grad)
                    point_a, indexMax = feasible_region.away_oracle(grad, point_x_prev)
                    dual_gap_prev = grad.dot(point_x_prev.cartesian_coordinates - v)
                    strong_wolfe_gap_prev = grad.dot(point_a.cartesian_coordinates - v)
            if self.fw_variant == "DIPFW":
                point_x, dual_gap_prev, strong_wolfe_gap_prev = dipfw(
                    objective_function, feasible_region, point_x, self.step_size_param
                )
            iteration += 1
            duration = time.time() - start_time
            f_val = objective_function.evaluate(point_x.cartesian_coordinates)
            run_status = (
                iteration,
                duration,
                f_val,
                dual_gap_prev,
                strong_wolfe_gap_prev,
            )
            if exit_criterion.has_met_exit_criterion(run_status):
                break
            if global_iter:
                # Increment global iteration count
                with global_iter.get_lock():
                    global_iter.value += 1
            if save_and_output_results:

                if dual_gap_prev is None or strong_wolfe_gap_prev is None:
                    LOGGER.info(
                        "Running " + str(self.fw_variant) + ": "
                        "iteration = {1}, duration = {2:.{0}f}, "
                        "f_val = {3:.{0}f}, dual_gap = None, strong_wolfe_gap = None".format(
                            DISPLAY_DECIMALS, *run_status
                        )
                    )
                else:
                    LOGGER.info(
                        "Running " + str(self.fw_variant) + ": "
                        "iteration = {1}, duration = {2:.{0}f}, "
                        "f_val = {3:.{0}f}, dual_gap = {4:.{0}f}, strong_wolfe_gap = {5:.{0}f}".format(
                            DISPLAY_DECIMALS, *run_status
                        )
                    )
                run_history.append(run_status)
        if save_and_output_results:
            return run_history
        else:
            return point_x_prev, dual_gap_prev, strong_wolfe_gap_prev
예제 #10
0
def fw_away_lazy(
    objective_function, feasible_region, point_x, step_size_param, phi_val, K=2.0
):
    """Makes a lazy Frank-Wolfe step."""

    grad = objective_function.evaluate_grad(point_x.cartesian_coordinates)
    point_a, index_max, point_v, index_min = point_x.max_min_vertices(grad)

    # Use old FW vertex.
    if (
        np.dot(grad, point_x.cartesian_coordinates - point_v.cartesian_coordinates)
        >= np.dot(grad, point_a.cartesian_coordinates - point_x.cartesian_coordinates)
        and np.dot(grad, point_x.cartesian_coordinates - point_v.cartesian_coordinates)
        >= phi_val[0] / K
    ):
        alpha_max = 1.0
        alpha = step_size(
            objective_function,
            point_x.cartesian_coordinates,
            point_v.cartesian_coordinates - point_x.cartesian_coordinates,
            grad,
            alpha_max,
            step_size_param,
        )
        if alpha != alpha_max:
            return (
                point_x + alpha * (point_v - point_x),
                None,
                None,
            )
        else:
            return (
                point_v,
                None,
                None,
            )
    else:
        if (
            np.dot(grad, point_a.cartesian_coordinates - point_x.cartesian_coordinates)
            > np.dot(
                grad, point_x.cartesian_coordinates - point_v.cartesian_coordinates
            )
            and np.dot(
                grad, point_a.cartesian_coordinates - point_x.cartesian_coordinates
            )
            >= phi_val[0] / K
        ):
            alpha_max = point_x.barycentric_coordinates[index_max] / (
                1.0 - point_x.barycentric_coordinates[index_max]
            )
            alpha = step_size(
                objective_function,
                point_x.cartesian_coordinates,
                point_x.cartesian_coordinates - point_a.cartesian_coordinates,
                grad,
                alpha_max,
                step_size_param,
            )
            point_x = point_x + alpha * (point_x - point_a)
            if alpha == alpha_max:
                point_x = point_x.delete_vertex_in_support(index_max)
            return (
                point_x,
                None,
                None,
            )
        else:
            v = feasible_region.lp_oracle(grad)
            strong_wolfe_gap = grad.dot(point_a.cartesian_coordinates - v)
            wolfe_gap = grad.dot(point_x.cartesian_coordinates - v)
            if np.dot(grad, point_x.cartesian_coordinates - v) >= phi_val[0] / K:
                flag, point_v = point_x.is_vertex_in_support(v)
                alpha_max = 1.0
                alpha = step_size(
                    objective_function,
                    point_x.cartesian_coordinates,
                    point_v.cartesian_coordinates - point_x.cartesian_coordinates,
                    grad,
                    alpha_max,
                    step_size_param,
                )
                if flag == False:
                    new_barycentric_coordinates = list(point_x.barycentric_coordinates)
                    new_barycentric_coordinates.append(0.0)
                    point_x = Point(
                        point_x.cartesian_coordinates,
                        tuple(new_barycentric_coordinates),
                        point_v.support,
                    )
                if alpha != alpha_max:
                    return (
                        point_x + alpha * (point_v - point_x),
                        wolfe_gap,
                        strong_wolfe_gap,
                    )
                else:
                    return (
                        Point(v, (1.0,), (v,)),
                        wolfe_gap,
                        strong_wolfe_gap,
                    )
            else:
                phi_val[0] = min(
                    grad.dot(point_x.cartesian_coordinates - v), phi_val[0] / 2.0
                )
                return point_x, wolfe_gap, strong_wolfe_gap