Example #1
0
    def is_converged(self):
        ## might as well get a bound, in this case
        if self.opt._PHIter == 1:
            self.BestOuterBound = self.OuterBoundUpdate(self.opt.trivial_bound)

        if not self.has_innerbound_spokes:
            if self.opt._PHIter == 1:
                logger.warning("PHHub cannot compute convergence without "
                               "inner bound spokes.")

            ## you still want to output status, even without inner bounders configured
            if self.rank_global == 0:
                self.screen_trace()

            return False

        if not self.has_outerbound_spokes:
            if self.opt._PHIter == 1 and self.rank_global == 0:
                tt_timer.toc(
                    "Without outer bound spokes, no progress "
                    "will be made on the Best Bound",
                    delta=False)

        ## log some output
        if self.rank_global == 0:
            self.screen_trace()

        return self.determine_termination()
Example #2
0
    def hub_finalize(self):
        if self.has_outerbound_spokes:
            self.receive_outerbounds()
        if self.has_innerbound_spokes:
            self.receive_innerbounds()

        if self.rank_global == 0:
            self.print_init = True
            tt_timer.toc(f" ", delta=False)
            tt_timer.toc(f"Statistics at termination", delta=False)
            self.screen_trace()
Example #3
0
 def screen_trace(self):
     current_iteration = self.current_iteration()
     rel_gap = self.compute_gap(compute_relative=True)
     abs_gap = self.compute_gap(compute_relative=False)
     best_solution = self.BestInnerBound
     best_bound = self.BestOuterBound
     update_source = self.get_update_string()
     if self.print_init:
         row = f'{"Iter.":>5s}  {"   "}  {"Best Bound":>14s}  {"Best Incumbent":>14s}  {"Rel. Gap":>12s}  {"Abs. Gap":>14s}'
         tt_timer.toc(row, delta=False)
         self.print_init = False
     row = f"{current_iteration:5d}  {update_source}  {best_bound:14.4f}  {best_solution:14.4f}  {rel_gap*100:12.4f}  {abs_gap:14.4f}"
     tt_timer.toc(row, delta=False)
     self.clear_latest_chars()
Example #4
0
 def determine_termination(self):
     abs_gap_satisfied = False
     rel_gap_satisfied = False
     if hasattr(self,"options") and self.options is not None:
         if "rel_gap" in self.options:
             rel_gap = self.compute_gap(compute_relative=True)
             rel_gap_satisfied = rel_gap <= self.options["rel_gap"]
         if "abs_gap" in self.options:
             abs_gap = self.compute_gap(compute_relative=False)
             abs_gap_satisfied = abs_gap <= self.options["abs_gap"]
     if abs_gap_satisfied and self.rank_global == 0:
         tt_timer.toc(f"Terminating based on inter-cylinder absolute gap {abs_gap:12.4f}", delta=False)
     if rel_gap_satisfied and self.rank_global == 0:
         tt_timer.toc(f"Terminating based on inter-cylinder relative gap {rel_gap*100:12.3f}%", delta=False)
     return abs_gap_satisfied or rel_gap_satisfied
Example #5
0
    def miditer(self):
        self.iter_since_last_check += 1

        ib = self.opt.spcomm.BestInnerBound
        if ib != self.cur_ib:
            self.cur_ib = ib
            self.iter_at_cur_ib = 1
        elif self.cur_ib is not None and math.isfinite(self.cur_ib):
            self.iter_at_cur_ib += 1

        ob = self.opt.spcomm.BestOuterBound
        if self.cur_ob is not None and math.isclose(ob, self.cur_ob):
            ob_new = False
        else:
            self.cur_ob = ob
            ob_new = True

        if not self.any_cuts:
            if self.opt.spcomm.new_cuts:
                self.any_cuts = True

        ## if its the second time or more with this IB, we'll only check
        ## if the last improved the OB, or if the OB is new itself (from somewhere else)
        check = (self.check_bound_iterations is not None) and self.any_cuts and ( \
                (self.iter_at_cur_ib == self.check_bound_iterations) or \
                (self.iter_at_cur_ib > self.check_bound_iterations and ob_new) or \
                ((self.iter_since_last_check%self.check_bound_iterations == 0) and self.opt.spcomm.new_cuts))
        # if there hasn't been OB movement, check every so often if we have new cuts
        if check:
            if self.opt.spcomm.rank_global == 0:
                tt_timer.toc(
                    f"Attempting to update Best Bound with CrossScenarioExtension",
                    delta=False)
            self._check_bound()
            self.opt.spcomm.new_cuts = False
            self.iter_since_last_check = 0
Example #6
0
    def is_converged(self):
        if not (self.has_innerbound_spokes and self.has_outerbound_spokes):
            if self.opt._PHIter == 1:
                logger.warning("PHHub cannot compute convergence without "
                               "both inner and outer bound spokes.")
            return False
        if self.opt._PHIter == 1:
            self.BestOuterBound = self.OuterBoundUpdate(self.opt.trivial_bound)

        ## log some output
        if self.rank_global == 0:
            rel_gap = self.compute_gap(compute_relative=True)
            abs_gap = self.compute_gap(compute_relative=False)
            best_solution = self.BestInnerBound
            best_bound = self.BestOuterBound
            elapsed = time.time() - self.inst_time
            if self.opt._PHIter == 1:
                row = f'{"Best Bound":>14s} {"Best Incumbent":>14s} {"Rel. Gap (%)":>12s} {"Abs. Gap":>14s}'
                tt_timer.toc(row, delta=False)
            row = f"{best_bound:14.4f} {best_solution:14.4f} {rel_gap*100:12.4f} {abs_gap:14.4f}"
            tt_timer.toc(row, delta=False)

        abs_gap_satisfied = False
        rel_gap_satisfied = False
        if hasattr(self, "options") and self.options is not None:
            if "rel_gap" in self.options:
                rel_gap = self.compute_gap(compute_relative=True)
                rel_gap_satisfied = rel_gap <= self.options["rel_gap"]
            if "abs_gap" in self.options:
                abs_gap = self.compute_gap(compute_relative=False)
                abs_gap_satisfied = abs_gap <= self.options["abs_gap"]
        if abs_gap_satisfied and self.rank_global == 0:
            tt_timer.toc(
                f"Terminating based on cylinder absolute gap {abs_gap:14.4f}",
                delta=False)
        if rel_gap_satisfied and self.rank_global == 0:
            tt_timer.toc(
                f"Terminating based on cylinder relative gap {rel_gap*100:12.4f}",
                delta=False)
        return abs_gap_satisfied or rel_gap_satisfied
Example #7
0
    def __init__(
        self,
        options,
        all_scenario_names,
        scenario_creator,
        scenario_denouement=None,
        all_nodenames=None,
        mpicomm=None,
        rank0=0,
        cb_data=None,
    ):
        self.startdt = dt.datetime.now()
        self.start_time = time.time()
        self.options = options
        self.all_scenario_names = all_scenario_names
        self.scenario_creator = scenario_creator
        self.scenario_denouement = scenario_denouement
        self.comms = dict()
        self.local_scenarios = dict()
        self.local_scenario_names = list()
        self.E1_tolerance = 1e-5  # probs must sum to almost 1
        self.names_in_bundles = None
        self.scenarios_constructed = False
        if all_nodenames is None:
            self.all_nodenames = ["ROOT"]
        elif "ROOT" in all_nodenames:
            self.all_nodenames = all_nodenames
        else:
            raise RuntimeError("'ROOT' must be in the list of node names")

        # Set up MPI communicator and rank
        if mpicomm is not None:
            self.mpicomm = mpicomm
        else:
            self.mpicomm = MPI.COMM_WORLD
        self.rank = self.mpicomm.Get_rank()
        self.n_proc = self.mpicomm.Get_size()
        self.rank0 = rank0
        self.rank_global = MPI.COMM_WORLD.Get_rank()

        if self.rank_global == 0:
            tt_timer.toc("Start SPBase.__init__", delta=False)

        # This doesn't seemed to be checked anywhere else
        if self.n_proc > len(self.all_scenario_names):
            raise RuntimeError("More ranks than scenarios")

        # Call various initialization methods
        if "branching_factors" in self.options:
            self.branching_factors = self.options["branching_factors"]
        else:
            self.branching_factors = [len(self.all_scenario_names)]
        self.calculate_scenario_ranks()
        self.attach_scenario_rank_maps()
        if "bundles_per_rank" in self.options and self.options[
                "bundles_per_rank"] > 0:
            self.assign_bundles()
            self.bundling = True
        else:
            self.bundling = False
        self.create_scenarios(cb_data)
        self.look_before_leap_all()
        self.attach_nlens()
        self.attach_nonant_indexes()
        self.create_communicators()
        self.set_sense()
        self.set_multistage()

        ## SPCommunicator object
        self._spcomm = None
Example #8
0
def spin_the_wheel(hub_dict, list_of_spoke_dict, comm_world=None):
    """ top level for the hub and spoke system
    Args:
        hub_dict(dict): controls hub creation
        list_of_spoke_dict(list dict): controls creation of spokes
        comm_world (MPI comm): the world for this hub-spoke system

    Returns:
        spcomm (Hub or Spoke object): the object that did the work (windowless)
        opt_dict (dict): the dictionary that controlled creation for this rank

    NOTE: the return is after termination; the objects are provided for query.

    """
    if not haveMPI:
        raise RuntimeError("spin_the_wheel called, but cannot import mpi4py")
    # Confirm that the provided dictionaries specifying
    # the hubs and spokes contain the appropriate keys
    if "hub_class" not in hub_dict:
        raise RuntimeError(
            "The hub_dict must contain a 'hub_class' key specifying "
            "the hub class to use")
    if "opt_class" not in hub_dict:
        raise RuntimeError(
            "The hub_dict must contain an 'opt_class' key specifying "
            "the SPBase class to use (e.g. PHBase, etc.)")
    if "hub_kwargs" not in hub_dict:
        hub_dict["hub_kwargs"] = dict()
    if "opt_kwargs" not in hub_dict:
        hub_dict["opt_kwargs"] = dict()
    for spoke_dict in list_of_spoke_dict:
        if "spoke_class" not in spoke_dict:
            raise RuntimeError(
                "Each spoke_dict must contain a 'spoke_class' key "
                "specifying the spoke class to use")
        if "opt_class" not in spoke_dict:
            raise RuntimeError(
                "Each spoke_dict must contain an 'opt_class' key "
                "specifying the SPBase class to use (e.g. PHBase, etc.)")
        if "spoke_kwargs" not in spoke_dict:
            spoke_dict["spoke_kwargs"] = dict()
        if "opt_kwargs" not in spoke_dict:
            spoke_dict["opt_kwargs"] = dict()

    if comm_world is None:
        comm_world = MPI.COMM_WORLD
    n_spokes = len(list_of_spoke_dict)

    # Create the necessary communicators
    fullcomm = comm_world
    intercomm, intracomm = make_comms(n_spokes, fullcomm=fullcomm)
    rank_inter = intercomm.Get_rank()
    rank_intra = intracomm.Get_rank()
    rank_global = fullcomm.Get_rank()

    # Assign hub/spokes to individual ranks
    if rank_inter == 0:  # This rank is a hub
        sp_class = hub_dict["hub_class"]
        sp_kwargs = hub_dict["hub_kwargs"]
        opt_class = hub_dict["opt_class"]
        opt_kwargs = hub_dict["opt_kwargs"]
        opt_dict = hub_dict
    else:  # This rank is a spoke
        spoke_dict = list_of_spoke_dict[rank_inter - 1]
        sp_class = spoke_dict["spoke_class"]
        sp_kwargs = spoke_dict["spoke_kwargs"]
        opt_class = spoke_dict["opt_class"]
        opt_kwargs = spoke_dict["opt_kwargs"]
        opt_dict = spoke_dict

    # Create the appropriate opt object locally
    opt_kwargs["mpicomm"] = intracomm
    opt = opt_class(**opt_kwargs)

    # Create the SPCommunicator object (hub/spoke) with
    # the appropriate SPBase object attached
    if rank_inter == 0:  # Hub
        spcomm = sp_class(opt, fullcomm, intercomm, intracomm,
                          list_of_spoke_dict, **sp_kwargs)
    else:  # Spokes
        spcomm = sp_class(opt, fullcomm, intercomm, intracomm, **sp_kwargs)

    # Create the windows, run main(), destroy the windows
    spcomm.make_windows()
    if rank_inter == 0:
        spcomm.setup_hub()
    if rank_global == 0:
        tt_timer.toc("Starting spcomm.main()", delta=False)
    spcomm.main()
    if rank_inter == 0:  # If this is the hub
        spcomm.send_terminate()

    # Anything that's left to do
    spcomm.finalize()

    if rank_global == 0:
        tt_timer.toc("Hub algorithm complete, waiting for termination barrier",
                     delta=False)
    fullcomm.Barrier()

    ## give the hub the chance to catch new values
    spcomm.hub_finalize()

    spcomm.free_windows()
    if rank_global == 0:
        tt_timer.toc("Windows freed", delta=False)

    return spcomm, opt_dict