Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nonant name validation #339

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
297 changes: 297 additions & 0 deletions examples/farmer/bad_farmer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
# special for ph debugging DLW Dec 2018
# unlimited crops
# ALL INDEXES ARE ZERO-BASED
# ___________________________________________________________________________
#
# Pyomo: Python Optimization Modeling Objects
# Copyright 2018 National Technology and Engineering Solutions of Sandia, LLC
# Under the terms of Contract DE-NA0003525 with National Technology and
# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
# rights in this software.
# This software is distributed under the 3-clause BSD License.
# ___________________________________________________________________________
#
# special scalable farmer for stress-testing

import pyomo.environ as pyo
import numpy as np
import mpisppy.scenario_tree as scenario_tree
import mpisppy.utils.sputils as sputils
from mpisppy.utils import config

# Use this random stream:
farmerstream = np.random.RandomState()

def scenario_creator(
scenario_name, use_integer=False, sense=pyo.minimize, crops_multiplier=1,
num_scens=None, seedoffset=0
):
""" Create a scenario for the (scalable) farmer example.

Args:
scenario_name (str):
Name of the scenario to construct.
use_integer (bool, optional):
If True, restricts variables to be integer. Default is False.
sense (int, optional):
Model sense (minimization or maximization). Must be either
pyo.minimize or pyo.maximize. Default is pyo.minimize.
crops_multiplier (int, optional):
Factor to control scaling. There will be three times this many
crops. Default is 1.
num_scens (int, optional):
Number of scenarios. We use it to compute _mpisppy_probability.
Default is None.
seedoffset (int): used by confidence interval code
"""
# scenario_name has the form <str><int> e.g. scen12, foobar7
# The digits are scraped off the right of scenario_name using regex then
# converted mod 3 into one of the below avg./avg./above avg. scenarios
scennum = sputils.extract_num(scenario_name)
basenames = ['BelowAverageScenario', 'AverageScenario', 'AboveAverageScenario']
basenum = scennum % 3
groupnum = scennum // 3
scenname = basenames[basenum]+str(groupnum)

# The RNG is seeded with the scenario number so that it is
# reproducible when used with multiple threads.
# NOTE: if you want to do replicates, you will need to pass a seed
# as a kwarg to scenario_creator then use seed+scennum as the seed argument.
farmerstream.seed(scennum+seedoffset)

# Check for minimization vs. maximization
if sense not in [pyo.minimize, pyo.maximize]:
raise ValueError("Model sense Not recognized")

# Create the concrete model object
model = pysp_instance_creation_callback(
scenname,
use_integer=use_integer,
sense=sense,
crops_multiplier=crops_multiplier,
)

# Create the list of nodes associated with the scenario (for two stage,
# there is only one node associated with the scenario--leaf nodes are
# ignored).
varlist = [var for var in model.DevotedAcreage.values()]
if scennum == 2:
varlist.reverse()
sputils.attach_root_node(model, model.FirstStageCost, varlist)

#Add the probability of the scenario
if num_scens is not None :
model._mpisppy_probability = 1/num_scens
return model

def pysp_instance_creation_callback(
scenario_name, use_integer=False, sense=pyo.minimize, crops_multiplier=1
):
# long function to create the entire model
# scenario_name is a string (e.g. AboveAverageScenario0)
#
# Returns a concrete model for the specified scenario

# scenarios come in groups of three
scengroupnum = sputils.extract_num(scenario_name)
scenario_base_name = scenario_name.rstrip("0123456789")

model = pyo.ConcreteModel(scenario_name)

def crops_init(m):
retval = []
for i in range(crops_multiplier):
retval.append("WHEAT"+str(i))
retval.append("CORN"+str(i))
retval.append("SUGAR_BEETS"+str(i))
return retval

model.CROPS = pyo.Set(initialize=crops_init)

#
# Parameters
#

model.TOTAL_ACREAGE = 500.0 * crops_multiplier

def _scale_up_data(indict):
outdict = {}
for i in range(crops_multiplier):
for crop in ['WHEAT', 'CORN', 'SUGAR_BEETS']:
outdict[crop+str(i)] = indict[crop]
return outdict

model.PriceQuota = _scale_up_data(
{'WHEAT':100000.0,'CORN':100000.0,'SUGAR_BEETS':6000.0})

model.SubQuotaSellingPrice = _scale_up_data(
{'WHEAT':170.0,'CORN':150.0,'SUGAR_BEETS':36.0})

model.SuperQuotaSellingPrice = _scale_up_data(
{'WHEAT':0.0,'CORN':0.0,'SUGAR_BEETS':10.0})

model.CattleFeedRequirement = _scale_up_data(
{'WHEAT':200.0,'CORN':240.0,'SUGAR_BEETS':0.0})

model.PurchasePrice = _scale_up_data(
{'WHEAT':238.0,'CORN':210.0,'SUGAR_BEETS':100000.0})

model.PlantingCostPerAcre = _scale_up_data(
{'WHEAT':150.0,'CORN':230.0,'SUGAR_BEETS':260.0})

#
# Stochastic Data
#
Yield = {}
Yield['BelowAverageScenario'] = \
{'WHEAT':2.0,'CORN':2.4,'SUGAR_BEETS':16.0}
Yield['AverageScenario'] = \
{'WHEAT':2.5,'CORN':3.0,'SUGAR_BEETS':20.0}
Yield['AboveAverageScenario'] = \
{'WHEAT':3.0,'CORN':3.6,'SUGAR_BEETS':24.0}

def Yield_init(m, cropname):
# yield as in "crop yield"
crop_base_name = cropname.rstrip("0123456789")
if scengroupnum != 0:
return Yield[scenario_base_name][crop_base_name]+farmerstream.rand()
else:
return Yield[scenario_base_name][crop_base_name]

model.Yield = pyo.Param(model.CROPS,
within=pyo.NonNegativeReals,
initialize=Yield_init,
mutable=True)

#
# Variables
#

if (use_integer):
model.DevotedAcreage = pyo.Var(model.CROPS,
within=pyo.NonNegativeIntegers,
bounds=(0.0, model.TOTAL_ACREAGE))
else:
model.DevotedAcreage = pyo.Var(model.CROPS,
bounds=(0.0, model.TOTAL_ACREAGE))

model.QuantitySubQuotaSold = pyo.Var(model.CROPS, bounds=(0.0, None))
model.QuantitySuperQuotaSold = pyo.Var(model.CROPS, bounds=(0.0, None))
model.QuantityPurchased = pyo.Var(model.CROPS, bounds=(0.0, None))

#
# Constraints
#

def ConstrainTotalAcreage_rule(model):
return pyo.sum_product(model.DevotedAcreage) <= model.TOTAL_ACREAGE

model.ConstrainTotalAcreage = pyo.Constraint(rule=ConstrainTotalAcreage_rule)

def EnforceCattleFeedRequirement_rule(model, i):
return model.CattleFeedRequirement[i] <= (model.Yield[i] * model.DevotedAcreage[i]) + model.QuantityPurchased[i] - model.QuantitySubQuotaSold[i] - model.QuantitySuperQuotaSold[i]

model.EnforceCattleFeedRequirement = pyo.Constraint(model.CROPS, rule=EnforceCattleFeedRequirement_rule)

def LimitAmountSold_rule(model, i):
return model.QuantitySubQuotaSold[i] + model.QuantitySuperQuotaSold[i] - (model.Yield[i] * model.DevotedAcreage[i]) <= 0.0

model.LimitAmountSold = pyo.Constraint(model.CROPS, rule=LimitAmountSold_rule)

def EnforceQuotas_rule(model, i):
return (0.0, model.QuantitySubQuotaSold[i], model.PriceQuota[i])

model.EnforceQuotas = pyo.Constraint(model.CROPS, rule=EnforceQuotas_rule)

# Stage-specific cost computations;

def ComputeFirstStageCost_rule(model):
return pyo.sum_product(model.PlantingCostPerAcre, model.DevotedAcreage)
model.FirstStageCost = pyo.Expression(rule=ComputeFirstStageCost_rule)

def ComputeSecondStageCost_rule(model):
expr = pyo.sum_product(model.PurchasePrice, model.QuantityPurchased)
expr -= pyo.sum_product(model.SubQuotaSellingPrice, model.QuantitySubQuotaSold)
expr -= pyo.sum_product(model.SuperQuotaSellingPrice, model.QuantitySuperQuotaSold)
return expr
model.SecondStageCost = pyo.Expression(rule=ComputeSecondStageCost_rule)

def total_cost_rule(model):
if (sense == pyo.minimize):
return model.FirstStageCost + model.SecondStageCost
return -model.FirstStageCost - model.SecondStageCost
model.Total_Cost_Objective = pyo.Objective(rule=total_cost_rule,
sense=sense)

return model

# begin functions not needed by farmer_cylinders
# (but needed by special codes such as confidence intervals)
#=========
def scenario_names_creator(num_scens,start=None):
# (only for Amalgamator): return the full list of num_scens scenario names
# if start!=None, the list starts with the 'start' labeled scenario
if (start is None) :
start=0
return [f"scen{i}" for i in range(start,start+num_scens)]



#=========
def inparser_adder(cfg):
# add options unique to farmer
cfg.num_scens_required()
cfg.add_to_config("crops_multiplier",
description="number of crops will be three times this (default 1)",
domain=int,
default=1)

cfg.add_to_config("farmer_with_integers",
description="make the version that has integers (default False)",
domain=bool,
default=False)


#=========
def kw_creator(cfg):
# (for Amalgamator): linked to the scenario_creator and inparser_adder
kwargs = {"use_integer": cfg.get('farmer_with_integers', False),
"crops_multiplier": cfg.get('crops_multiplier', 1),
"num_scens" : cfg.get('num_scens', None),
}
return kwargs

def sample_tree_scen_creator(sname, stage, sample_branching_factors, seed,
given_scenario=None, **scenario_creator_kwargs):
""" Create a scenario within a sample tree. Mainly for multi-stage and simple for two-stage.
(this function supports zhat and confidence interval code)
Args:
sname (string): scenario name to be created
stage (int >=1 ): for stages > 1, fix data based on sname in earlier stages
sample_branching_factors (list of ints): branching factors for the sample tree
seed (int): To allow random sampling (for some problems, it might be scenario offset)
given_scenario (Pyomo concrete model): if not None, use this to get data for ealier stages
scenario_creator_kwargs (dict): keyword args for the standard scenario creator funcion
Returns:
scenario (Pyomo concrete model): A scenario for sname with data in stages < stage determined
by the arguments
"""
# Since this is a two-stage problem, we don't have to do much.
sca = scenario_creator_kwargs.copy()
sca["seedoffset"] = seed
sca["num_scens"] = sample_branching_factors[0] # two-stage problem
return scenario_creator(sname, **sca)


# end functions not needed by farmer_cylinders


#============================
def scenario_denouement(rank, scenario_name, scenario):
sname = scenario_name
s = scenario
if sname == 'scen0':
print("Arbitrary sanity checks:")
print ("SUGAR_BEETS0 for scenario",sname,"is",
pyo.value(s.DevotedAcreage["SUGAR_BEETS0"]))
print ("FirstStageCost for scenario",sname,"is", pyo.value(s.FirstStageCost))
Loading
Loading