Exemplo n.º 1
0
 def __init__(self,
              options,
              scenario_names,
              scenario_creator,
              kw_creator,
              scenario_denouement=None,
              verbose=True):
     self.options = options
     self.scenario_names = scenario_names
     self.scenario_creator = scenario_creator
     self.scenario_denouement = scenario_denouement
     self.kw_creator = kw_creator
     self.kwargs = self.kw_creator(self.options)
     self.verbose = verbose
     self.is_EF = _bool_option(options, "EF-2stage") or _bool_option(
         options, "EF-mstage")
     if self.is_EF:
         self.solvername = options['EF_solver_name'] if (
             'EF_solver_name' in options) else 'gurobi'
         self.solver_options = options['EF_solver_options'] \
             if ('EF_solver_options' in options) else {}
     self.is_multi = _bool_option(options, "EF-mstage") or _bool_option(
         options, "mstage")
     if self.is_multi and not "all_nodenames" in options:
         if "branching_factors" in options:
             self.options[
                 "all_nodenames"] = sputils.create_nodenames_from_BFs(
                     options["branching_factors"])
         else:
             raise RuntimeError(
                 "For a multistage problem, please provide branching factors or all_nodenames"
             )
Exemplo n.º 2
0
def add_multistage_options(cylinder_dict, all_nodenames, branching_factors):
    cylinder_dict = copy.deepcopy(cylinder_dict)
    if branching_factors is not None:
        if hasattr(cylinder_dict["opt_kwargs"], "options"):
            cylinder_dict["opt_kwargs"]["options"][
                "branching_factors"] = branching_factors
        if all_nodenames is None:
            all_nodenames = sputils.create_nodenames_from_BFs(
                branching_factors)
    if all_nodenames is not None:
        print("Hello, surprise !!")
        cylinder_dict["opt_kwargs"]["all_nodenames"] = all_nodenames
    print("Hello,", cylinder_dict)
    return cylinder_dict
Exemplo n.º 3
0
    options["xhat_looper_options"] =  {"xhat_solver_options":\
                                         None,
                                         "scen_limit": 3,
                                         "dump_prefix": "delme",
                                         "csvname": "looper.csv"}

    # branching factor (3 stages is hard-wired)
    BFs = options["branching_factors"]
    ScenCount = BFs[0] * BFs[1]
    all_scenario_names = list()
    for sn in range(ScenCount):
        all_scenario_names.append("Scen" + str(sn + 1))
    # end hardwire

    # This is multi-stage, so we need to supply node names
    all_nodenames = sputils.create_nodenames_from_BFs(BFs)

    # **** ef ****
    solver = pyo.SolverFactory(options["solvername"])

    ef = sputils.create_EF(
        all_scenario_names,
        scenario_creator,
        scenario_creator_kwargs={"branching_factors": BFs},
    )
    results = solver.solve(ef, tee=options["verbose"])
    print('EF objective value:', pyo.value(ef.EF_Obj))
    sputils.ef_nonants_csv(ef, "vardump.csv")

    # **** ph ****
    options["xhat_specific_options"] = {"xhat_solver_options":
Exemplo n.º 4
0
def gap_estimators(xhat_one,
                   mname, 
                   solving_type="EF-2stage",
                   scenario_names=None,
                   sample_options=None,
                   ArRP=1,
                   scenario_creator_kwargs={}, 
                   scenario_denouement=None,
                   solvername='gurobi', 
                   solver_options=None,
                   verbose=True,
                   objective_gap=False
                   ):
    ''' Given a xhat, scenario names, a scenario creator and options, 
    gap_estimators creates a scenario tree and the associatd estimators 
    G and s from §2 of [bm2011].
    Returns G and s evaluated at xhat.
    If ArRP>1, G and s are pooled, from a number ArRP of estimators,
        computed with different scenario trees.
    

    Parameters
    ----------
    xhat_one : dict
        A candidate first stage solution
    mname: str
        Name of the reference model, e.g. 'mpisppy.tests.examples.farmer'.
    solving_type: str, optional
        The way we solve the approximate problem. Can be "EF-2stage" (default)
        or "EF-mstage".
    scenario_names: list, optional
        List of scenario names used to compute G_n and s_n. Default is None
        Must be specified for 2 stage, but can be missing for multistage
    sample_options: dict, optional
        Only for multistage. Must contain a 'seed' and a 'BFs' attribute,
        specifying the starting seed and the branching factors 
        of the scenario tree
    ArRP:int,optional
        Number of batches (we create a ArRP model). Default is 1 (one batch).
    scenario_creator_kwargs: dict, optional
        Additional arguments for scenario_creator. Default is {}
    scenario_denouement: function, optional
        Function to run after scenario creation. Default is None.
    solvername : str, optional
        Solver. Default is 'gurobi'
    solver_options: dict, optional
        Solving options. Default is None
    verbose: bool, optional
        Should it print the gap estimator ? Default is True
    objective_gap: bool, optional
        Returns a gap estimate around approximate objective value

    BFs: list, optional
        Only for multistage. List of branching factors of the sample scenario tree.

    Returns
    -------
    G_k and s_k, gap estimator and associated standard deviation estimator.

    '''
    if solving_type not in ["EF-2stage","EF-mstage"]:
        raise RuntimeError("Only EF solve for the approximate problem is supported yet.")
    else:
        is_multi = (solving_type=="EF-mstage")
    
    
        
    if is_multi:
        try:
            BFs = sample_options['BFs']
            start = sample_options['seed']
        except (TypeError,KeyError,RuntimeError):
            raise RuntimeError('For multistage problems, sample_options must be a dict with BFs and seed attributes.')
    else:
        start = sputils.extract_num(scenario_names[0])
    if ArRP>1: #Special case : ArRP, G and s are pooled from r>1 estimators.
        if is_multi:
            raise RuntimeError("Pooled estimators are not supported for multistage problems yet.")
        n = len(scenario_names)
        if(n%ArRP != 0):
            raise RuntimeWarning("You put as an input a number of scenarios"+\
                                 f" which is not a mutliple of {ArRP}.")
            n = n- n%ArRP
        G =[]
        s = []

        for k in range(ArRP):
            scennames = scenario_names[k*(n//ArRP):(k+1)*(n//ArRP)]
            tmp = gap_estimators(xhat_one, mname,
                                   solvername=solvername,
                                   scenario_names=scennames, ArRP=1,
                                   scenario_creator_kwargs=scenario_creator_kwargs,
                                   scenario_denouement=scenario_denouement,
                                   solver_options=solver_options,
                                   solving_type=solving_type
                                   )
            G.append(tmp['G'])
            s.append(tmp['s']) 

        #Pooling
        G = np.mean(G)
        s = np.linalg.norm(s)/np.sqrt(n//ArRP)
        return {"G": G, "s": s, "seed": start}
    

    #A1RP
    
    #We start by computing the optimal solution to the approximate problem induced by our scenarios
    if is_multi:
        #Sample a scenario tree: this is a subtree, but starting from stage 1
        samp_tree = sample_tree.SampleSubtree(mname,
                                              xhats =[],
                                              root_scen=None,
                                              starting_stage=1, 
                                              BFs=BFs,
                                              seed=start, 
                                              options=scenario_creator_kwargs,
                                              solvername=solvername,
                                              solver_options=solver_options)
        samp_tree.run()
        start += sputils.number_of_nodes(BFs)
        ama_object = samp_tree.ama
    else:
        #We use amalgomator to do it

        ama_options = dict(scenario_creator_kwargs)
        ama_options['start'] = start
        ama_options['num_scens'] = len(scenario_names)
        ama_options['EF_solver_name'] = solvername
        ama_options['EF_solver_options'] = solver_options
        ama_options[solving_type] = True
        ama_object = ama.from_module(mname, ama_options,use_command_line=False)
        ama_object.scenario_names = scenario_names
        ama_object.verbose = False
        ama_object.run()
        start += len(scenario_names)
        
    #Optimal solution of the approximate problem
    zstar = ama_object.best_outer_bound
    #Associated policies
    xstars = sputils.nonant_cache_from_ef(ama_object.ef)
    
    #Then, we evaluate the fonction value induced by the scenario at xstar.
    
    if is_multi:
        # Find feasible policies (i.e. xhats) for every non-leaf nodes
        if len(samp_tree.ef._ef_scenario_names)>1:
            local_scenarios = {sname:getattr(samp_tree.ef,sname) for sname in samp_tree.ef._ef_scenario_names}
        else:
            local_scenarios = {samp_tree.ef._ef_scenario_names[0]:samp_tree.ef}
        xhats,start = sample_tree.walking_tree_xhats(mname,
                                                    local_scenarios,
                                                    xhat_one['ROOT'],
                                                    BFs,
                                                    start,
                                                    scenario_creator_kwargs,
                                                    solvername=solvername,
                                                    solver_options=solver_options)
        
        #Compute then the average function value with this policy
        scenario_creator_kwargs = samp_tree.ama.kwargs
        all_nodenames = sputils.create_nodenames_from_BFs(BFs)
    else:
        #In a 2 stage problem, the only non-leaf is the ROOT node
        xhats = xhat_one
        all_nodenames = None
    
    xhat_eval_options = {"iter0_solver_options": None,
                         "iterk_solver_options": None,
                         "display_timing": False,
                         "solvername": solvername,
                         "verbose": False,
                         "solver_options":solver_options}
    ev = xhat_eval.Xhat_Eval(xhat_eval_options,
                            scenario_names,
                            ama_object.scenario_creator,
                            scenario_denouement,
                            scenario_creator_kwargs=scenario_creator_kwargs,
                            all_nodenames = all_nodenames)
    #Evaluating xhat and xstar and getting the value of the objective function 
    #for every (local) scenario
    ev.evaluate(xhats)
    objs_at_xhat = ev.objs_dict
    ev.evaluate(xstars)
    objs_at_xstar = ev.objs_dict
    
    eval_scen_at_xhat = []
    eval_scen_at_xstar = []
    scen_probs = []
    for k,s in ev.local_scenarios.items():
        eval_scen_at_xhat.append(objs_at_xhat[k])
        eval_scen_at_xstar.append(objs_at_xstar[k])
        scen_probs.append(s._mpisppy_probability)

    
    scen_gaps = np.array(eval_scen_at_xhat)-np.array(eval_scen_at_xstar)
    local_gap = np.dot(scen_gaps,scen_probs)
    local_ssq = np.dot(scen_gaps**2,scen_probs)
    local_prob_sqnorm = np.linalg.norm(scen_probs)**2
    local_obj_at_xhat = np.dot(eval_scen_at_xhat,scen_probs)
    local_estim = np.array([local_gap,local_ssq,local_prob_sqnorm,local_obj_at_xhat])
    global_estim = np.zeros(4)
    ev.mpicomm.Allreduce(local_estim, global_estim, op=mpi.SUM) 
    G,ssq, prob_sqnorm,obj_at_xhat = global_estim
    if global_rank==0 and verbose:
        print(f"G = {G}")
    sample_var = (ssq - G**2)/(1-prob_sqnorm) #Unbiased sample variance
    s = np.sqrt(sample_var)
    
    use_relative_error = (np.abs(zstar)>1)
    G = correcting_numeric(G,objfct=obj_at_xhat,
                           relative_error=use_relative_error)
    if objective_gap:
        if is_multi:
            return {"G":G,"s":s,"zhats": [obj_at_xhat], "seed":start} 
        else:
            return {"G":G,"s":s,"zhats": eval_scen_at_xhat, "seed":start} 
    else:
        return {"G":G,"s":s,"seed":start}