예제 #1
0
 def make_nested_block_model(self):
     """For the next two tests: Has BooleanVar on model, but
     LogicalConstraints on a Block and a Block nested on that Block."""
     m = ConcreteModel()
     m.b = Block()
     m.Y = BooleanVar([1, 2])
     m.b.logical = LogicalConstraint(expr=~m.Y[1])
     m.b.b = Block()
     m.b.b.logical = LogicalConstraint(expr=m.Y[1].xor(m.Y[2]))
     return m
예제 #2
0
 def test_constant_True(self):
     m = ConcreteModel()
     with self.assertRaisesRegex(ValueError,
                                 "LogicalConstraint 'p' is always True."):
         m.p = LogicalConstraint(expr=True)
         TransformationFactory('core.logical_to_linear').apply_to(m)
     self.assertIsNone(m.component('logic_to_linear'))
예제 #3
0
 def test_literal(self):
     m = ConcreteModel()
     m.Y = BooleanVar()
     m.p = LogicalConstraint(expr=m.Y)
     TransformationFactory('core.logical_to_linear').apply_to(m)
     _constrs_contained_within(self, [(1, m.Y.get_associated_binary(), 1)],
                               m.logic_to_linear.transformed_constraints)
예제 #4
0
    def test_transformed_components_on_parent_block(self):
        m = ConcreteModel()
        m.b = Block()
        m.b.s = RangeSet(3)
        m.b.Y = BooleanVar(m.b.s)
        m.b.p = LogicalConstraint(
            expr=m.b.Y[1].implies(lor(m.b.Y[2], m.b.Y[3])))
        TransformationFactory('core.logical_to_linear').apply_to(m)

        boolean_var = m.b.component("Y_asbinary")
        self.assertIsInstance(boolean_var, Var)
        notAVar = m.component("Y_asbinary")
        self.assertIsNone(notAVar)

        transBlock = m.b.component("logic_to_linear")
        self.assertIsInstance(transBlock, Block)
        notAThing = m.component("logic_to_linear")
        self.assertIsNone(notAThing)

        # check the constraints on the transBlock
        _constrs_contained_within(
            self, [
                (1,
                 m.b.Y[2].get_associated_binary() + \
                 m.b.Y[3].get_associated_binary()
                 + (1 - m.b.Y[1].get_associated_binary()),
                 None)
            ], m.b.logic_to_linear.transformed_constraints)
예제 #5
0
def _generate_boolean_model(nvars):
    m = ConcreteModel()
    m.s = RangeSet(nvars)
    m.Y = BooleanVar(m.s)
    # make sure all the variables are used in at least one logical constraint
    m.constraint = LogicalConstraint(expr=exactly(2, m.Y))
    return m
예제 #6
0
 def test_xfrm_atleast_nested(self):
     m = _generate_boolean_model(4)
     m.p = LogicalConstraint(expr=atleast(1, atleast(2, m.Y[1], m.Y[1].lor(m.Y[2]), m.Y[2]).lor(m.Y[3]), m.Y[4]))
     TransformationFactory('core.logical_to_linear').apply_to(m)
     Y_aug = m.logic_to_linear.augmented_vars
     self.assertEqual(len(Y_aug), 3)
     _constrs_contained_within(
         self, [
             (1, Y_aug[1].get_associated_binary() + m.Y[4].get_associated_binary(), None),
             (1, 1 - Y_aug[2].get_associated_binary() + Y_aug[1].get_associated_binary(), None),
             (1, 1 - m.Y[3].get_associated_binary() + Y_aug[1].get_associated_binary(), None),
             (1,
              Y_aug[2].get_associated_binary() + m.Y[3].get_associated_binary()
              + 1 - Y_aug[1].get_associated_binary(),
              None),
             (1, 1 - m.Y[1].get_associated_binary() + Y_aug[3].get_associated_binary(), None),
             (1, 1 - m.Y[2].get_associated_binary() + Y_aug[3].get_associated_binary(), None),
             (1,
              m.Y[1].get_associated_binary() + m.Y[2].get_associated_binary() + 1 - Y_aug[3].get_associated_binary(),
              None),
             (None,
              2 - 2 * (1 - Y_aug[2].get_associated_binary())
              - (m.Y[1].get_associated_binary() + Y_aug[3].get_associated_binary() + m.Y[2].get_associated_binary()),
              0),
             (None,
              m.Y[1].get_associated_binary() + Y_aug[3].get_associated_binary() + m.Y[2].get_associated_binary()
              - (1 + 2 * Y_aug[2].get_associated_binary()),
              0)
         ], m.logic_to_linear.transformed_constraints)
예제 #7
0
    def test_construct(self):
        model = self.create_model()

        def rule(model):
            return model.x

        model.p = LogicalConstraint(rule=rule)

        self.assertIs(model.p.body, model.x)
예제 #8
0
 def test_implies(self):
     m = ConcreteModel()
     m.x = BooleanVar()
     m.y = BooleanVar()
     m.p = LogicalConstraint(expr=m.x.implies(m.y))
     TransformationFactory('core.logical_to_linear').apply_to(m)
     _constrs_contained_within(
         self, [(1, (1 - m.x.get_associated_binary()) + m.y.get_associated_binary(), None)],
         m.logic_to_linear.transformed_constraints)
예제 #9
0
 def test_RIC_strip_pack_maxBinary_logical_constraints(self):
     """Test RIC with strip packing using max_binary initialization and 
     including logical constraints."""
     exfile = import_file(
         join(exdir, 'strip_packing', 'strip_packing_concrete.py'))
     strip_pack = exfile.build_rect_strip_packing_model()
     # add logical constraints 
     strip_pack.Rec3AboveOrBelowRec1 = LogicalConstraint(
         expr=strip_pack.no_overlap[1,3].disjuncts[2].indicator_var.lor(
             strip_pack.no_overlap[1,3].disjuncts[3].indicator_var))
     strip_pack.Rec3RightOrLeftOfRec2 = LogicalConstraint(
         expr=strip_pack.no_overlap[2,3].disjuncts[0].indicator_var.lor(
             strip_pack.no_overlap[2,3].disjuncts[1].indicator_var))
     SolverFactory('gdpopt').solve(
         strip_pack, strategy='RIC', init_strategy='max_binary',
         mip_solver=mip_solver,
         nlp_solver=nlp_solver)
     self.assertTrue(
         fabs(value(strip_pack.total_length.expr) - 13) <= 1E-2)
예제 #10
0
 def test_GLOA_strip_pack_default_init_logical_constraints(self):
     """Test logic-based outer approximation with strip packing."""
     exfile = import_file(
         join(exdir, 'strip_packing', 'strip_packing_concrete.py'))
     strip_pack = exfile.build_rect_strip_packing_model()
     # add logical constraints 
     strip_pack.Rec3AboveOrBelowRec1 = LogicalConstraint(
         expr=strip_pack.no_overlap[1,3].disjuncts[2].indicator_var.lor(
             strip_pack.no_overlap[1,3].disjuncts[3].indicator_var))
     strip_pack.Rec3RightOrLeftOfRec2 = LogicalConstraint(
         expr=strip_pack.no_overlap[2,3].disjuncts[0].indicator_var.lor(
             strip_pack.no_overlap[2,3].disjuncts[1].indicator_var))
     SolverFactory('gdpopt').solve(
         strip_pack, strategy='GLOA',
         mip_solver=mip_solver,
         nlp_solver=global_nlp_solver,
         nlp_solver_args=global_nlp_solver_args)
     self.assertTrue(
         fabs(value(strip_pack.total_length.expr) - 13) <= 1E-2)
예제 #11
0
 def test_xfrm_exactly_statement(self):
     m = ConcreteModel()
     m.s = RangeSet(3)
     m.Y = BooleanVar(m.s)
     m.p = LogicalConstraint(expr=exactly(2, m.Y[1], m.Y[2], m.Y[3]))
     TransformationFactory('core.logical_to_linear').apply_to(m)
     _constrs_contained_within(self, [(2, m.Y[1].get_associated_binary() +
                                       m.Y[2].get_associated_binary() +
                                       m.Y[3].get_associated_binary(), 2)],
                               m.logic_to_linear.transformed_constraints)
예제 #12
0
 def test_longer_statement(self):
     m = ConcreteModel()
     m.s = RangeSet(3)
     m.Y = BooleanVar(m.s)
     m.p = LogicalConstraint(expr=m.Y[1].implies(lor(m.Y[2], m.Y[3])))
     TransformationFactory('core.logical_to_linear').apply_to(m)
     _constrs_contained_within(
         self,
         [(1,
           m.Y[2].get_associated_binary() + m.Y[3].get_associated_binary() +
           (1 - m.Y[1].get_associated_binary()), None)],
         m.logic_to_linear.transformed_constraints)
예제 #13
0
    def test_logical_constraints_transformed(self):
        """It is expected that the result of this transformation is a MI(N)LP,
        so check that LogicalConstraints are handeled correctly"""
        m = ConcreteModel()
        m.x = Var(bounds=(0, 10))
        m.d1 = Disjunct()
        m.d2 = Disjunct()
        m.d2.c = Constraint()
        m.d = Disjunction(expr=[m.d1, m.d2])
        m.another = Disjunction(expr=[[m.x == 3], [m.x == 0]])
        m.Y = BooleanVar()
        m.global_logical = LogicalConstraint(expr=m.Y.xor(m.d1.indicator_var))
        m.d1.logical = LogicalConstraint(
            expr=implies(~m.Y, m.another.disjuncts[0].indicator_var))
        m.obj = Objective(expr=m.x)

        m.d1.indicator_var.set_value(True)
        m.d2.indicator_var.set_value(False)
        m.another.disjuncts[0].indicator_var.set_value(True)
        m.another.disjuncts[1].indicator_var.set_value(False)

        TransformationFactory('gdp.fix_disjuncts').apply_to(m)

        # Make sure there are no active LogicalConstraints
        self.assertEqual(
            len(
                list(
                    m.component_data_objects(LogicalConstraint,
                                             active=True,
                                             descend_into=(Block, Disjunct)))),
            0)
        # See that it solves as expected
        SolverFactory('gurobi').solve(m)
        self.assertTrue(value(m.d1.indicator_var))
        self.assertFalse(value(m.d2.indicator_var))
        self.assertTrue(value(m.another.disjuncts[0].indicator_var))
        self.assertFalse(value(m.another.disjuncts[1].indicator_var))
        self.assertEqual(value(m.Y.get_associated_binary()), 0)
        self.assertEqual(value(m.x), 3)
예제 #14
0
 def test_link_with_gdp_indicators(self):
     m = _generate_boolean_model(4)
     m.d1 = Disjunct()
     m.d2 = Disjunct()
     m.x = Var()
     m.dd = Disjunct([1, 2])
     m.d1.c = Constraint(expr=m.x >= 2)
     m.d2.c = Constraint(expr=m.x <= 10)
     m.dd[1].c = Constraint(expr=m.x >= 5)
     m.dd[2].c = Constraint(expr=m.x <= 6)
     m.Y[1].associate_binary_var(m.d1.indicator_var)
     m.Y[2].associate_binary_var(m.d2.indicator_var)
     m.Y[3].associate_binary_var(m.dd[1].indicator_var)
     m.Y[4].associate_binary_var(m.dd[2].indicator_var)
     m.p = LogicalConstraint(expr=m.Y[1].implies(lor(m.Y[3], m.Y[4])))
     m.p2 = LogicalConstraint(expr=atmost(2, *m.Y[:]))
     TransformationFactory('core.logical_to_linear').apply_to(m)
     _constrs_contained_within(
         self, [
             (1, m.dd[1].indicator_var + m.dd[2].indicator_var + 1 - m.d1.indicator_var, None),
             (None, m.d1.indicator_var + m.d2.indicator_var + m.dd[1].indicator_var + m.dd[2].indicator_var, 2)
         ], m.logic_to_linear.transformed_constraints)
예제 #15
0
 def test_backmap_hierarchical_model(self):
     m = _generate_boolean_model(3)
     m.b = Block()
     m.b.Y = BooleanVar()
     m.b.lc = LogicalConstraint(expr=m.Y[1].lor(m.b.Y))
     TransformationFactory('core.logical_to_linear').apply_to(m)
     m.Y_asbinary[1].value = 1
     m.Y_asbinary[2].value = 0
     m.b.Y.get_associated_binary().value = 1
     update_boolean_vars_from_binary(m)
     self.assertTrue(m.Y[1].value)
     self.assertFalse(m.Y[2].value)
     self.assertIsNone(m.Y[3].value)
     self.assertTrue(m.b.Y.value)
예제 #16
0
def build_eight_process_flowsheet():
    """Build flowsheet for the 8 process problem."""
    m = ConcreteModel(name='DuranEx3 Disjunctive')
    """Set declarations"""
    m.streams = RangeSet(2, 25, doc="process streams")
    m.units = RangeSet(1, 8, doc="process units")

    no_unit_zero_flows = {
        1: (2, 3),
        2: (4, 5),
        3: (9, ),
        4: (12, 14),
        5: (15, ),
        6: (19, 20),
        7: (21, 22),
        8: (10, 17, 18),
    }
    """Parameter and initial point declarations"""
    # FIXED COST INVESTMENT COEFF FOR PROCESS UNITS
    # Format: process #: cost
    fixed_cost = {1: 5, 2: 8, 3: 6, 4: 10, 5: 6, 6: 7, 7: 4, 8: 5}
    m.CF = Param(m.units, initialize=fixed_cost)

    def fixed_cost_bounds(m, unit):
        return (0, m.CF[unit])

    m.yCF = Var(m.units, initialize=0, bounds=fixed_cost_bounds)

    # VARIABLE COST COEFF FOR PROCESS UNITS - STREAMS
    # Format: stream #: cost
    variable_cost = {
        3: -10,
        5: -15,
        9: -40,
        19: 25,
        21: 35,
        25: -35,
        17: 80,
        14: 15,
        10: 15,
        2: 1,
        4: 1,
        18: -65,
        20: -60,
        22: -80
    }
    CV = m.CV = Param(m.streams, initialize=variable_cost, default=0)

    # initial point information for stream flows
    initX = {
        2: 2,
        3: 1.5,
        6: 0.75,
        7: 0.5,
        8: 0.5,
        9: 0.75,
        11: 1.5,
        12: 1.34,
        13: 2,
        14: 2.5,
        17: 2,
        18: 0.75,
        19: 2,
        20: 1.5,
        23: 1.7,
        24: 1.5,
        25: 0.5
    }
    """Variable declarations"""
    # FLOWRATES OF PROCESS STREAMS
    m.flow = Var(m.streams,
                 domain=NonNegativeReals,
                 initialize=initX,
                 bounds=(0, 10))
    # OBJECTIVE FUNCTION CONSTANT TERM
    CONSTANT = m.constant = Param(initialize=122.0)
    """Constraint definitions"""
    # INPUT-OUTPUT RELATIONS FOR process units 1 through 8
    @m.Disjunct(m.units)
    def use_unit(disj, unit):
        disj.impose_fixed_cost = Constraint(expr=m.yCF[unit] == m.CF[unit])
        disj.io_relation = ConstraintList(doc="Input-Output relationship")

    @m.Disjunct(m.units)
    def no_unit(disj, unit):
        disj.no_flow = ConstraintList()
        for stream in no_unit_zero_flows[unit]:
            disj.no_flow.add(expr=m.flow[stream] == 0)

    @m.Disjunction(m.units)
    def use_unit_or_not(m, unit):
        return [m.use_unit[unit], m.no_unit[unit]]

    # Note: this could be done in an automated manner by indexed construction.
    # Below is just for illustration
    m.use_unit[1].io_relation.add(expr=exp(m.flow[3]) - 1 == m.flow[2])
    m.use_unit[2].io_relation.add(expr=exp(m.flow[5] / 1.2) - 1 == m.flow[4])
    m.use_unit[3].io_relation.add(expr=1.5 * m.flow[9] +
                                  m.flow[10] == m.flow[8])
    m.use_unit[4].io_relation.add(expr=1.25 *
                                  (m.flow[12] + m.flow[14]) == m.flow[13])
    m.use_unit[5].io_relation.add(expr=m.flow[15] == 2 * m.flow[16])
    m.use_unit[6].io_relation.add(expr=exp(m.flow[20] / 1.5) - 1 == m.flow[19])
    m.use_unit[7].io_relation.add(expr=exp(m.flow[22]) - 1 == m.flow[21])
    m.use_unit[8].io_relation.add(expr=exp(m.flow[18]) - 1 == m.flow[10] +
                                  m.flow[17])

    m.no_unit[3].bypass = Constraint(expr=m.flow[10] == m.flow[8])

    # Mass balance equations
    m.massbal1 = Constraint(expr=m.flow[13] == m.flow[19] + m.flow[21])
    m.massbal2 = Constraint(expr=m.flow[17] == m.flow[9] + m.flow[16] +
                            m.flow[25])
    m.massbal3 = Constraint(expr=m.flow[11] == m.flow[12] + m.flow[15])
    m.massbal4 = Constraint(expr=m.flow[3] + m.flow[5] == m.flow[6] +
                            m.flow[11])
    m.massbal5 = Constraint(expr=m.flow[6] == m.flow[7] + m.flow[8])
    m.massbal6 = Constraint(expr=m.flow[23] == m.flow[20] + m.flow[22])
    m.massbal7 = Constraint(expr=m.flow[23] == m.flow[14] + m.flow[24])

    # process specifications
    m.specs1 = Constraint(expr=m.flow[10] <= 0.8 * m.flow[17])
    m.specs2 = Constraint(expr=m.flow[10] >= 0.4 * m.flow[17])
    m.specs3 = Constraint(expr=m.flow[12] <= 5 * m.flow[14])
    m.specs4 = Constraint(expr=m.flow[12] >= 2 * m.flow[14])

    m.Y = Reference(m.use_unit[:].indicator_bool)

    # logical propositions
    m.use1or2 = LogicalConstraint(expr=m.Y[1].xor(m.Y[2]))
    m.use1or2implies345 = LogicalConstraint(
        expr=lor(m.Y[1], m.Y[2]).implies(lor(m.Y[3], m.Y[4], m.Y[5])))
    m.use4implies6or7 = LogicalConstraint(
        expr=m.Y[4].implies(lor(m.Y[6], m.Y[7])))
    m.use3implies8 = LogicalConstraint(expr=m.Y[3].implies(m.Y[8]))
    m.use6or7implies4 = LogicalConstraint(
        expr=lor(m.Y[6], m.Y[7]).implies(m.Y[4]))
    m.use6or7 = LogicalConstraint(expr=m.Y[6].xor(m.Y[7]))
    """Profit (objective) function definition"""
    m.profit = Objective(expr=sum(m.yCF[unit] for unit in m.units) +
                         sum(m.flow[stream] * CV[stream]
                             for stream in m.streams) + CONSTANT,
                         sense=minimize)
    """Bound definitions"""
    # x (flow) upper bounds
    x_ubs = {3: 2, 5: 2, 9: 2, 10: 1, 14: 1, 17: 2, 19: 2, 21: 2, 25: 3}
    for i, x_ub in x_ubs.item():
        m.flow[i].setub(x_ub)

    # Optimal solution uses units 2, 4, 6, 8 with objective value 68.

    return m
예제 #17
0
 def make_indexed_logical_constraint_model(self):
     m = _generate_boolean_model(3)
     m.cons = LogicalConstraint([1, 2])
     m.cons[1] = exactly(2, m.Y)
     m.cons[2] = m.Y[1].implies(lor(m.Y[2], m.Y[3]))
     return m
예제 #18
0
    def test_xfrm_special_atoms_nonroot(self):
        m = ConcreteModel()
        m.s = RangeSet(3)
        m.Y = BooleanVar(m.s)
        m.p = LogicalConstraint(
            expr=m.Y[1].implies(atleast(2, m.Y[1], m.Y[2], m.Y[3])))
        TransformationFactory('core.logical_to_linear').apply_to(m)
        Y_aug = m.logic_to_linear.augmented_vars
        self.assertEqual(len(Y_aug), 1)
        self.assertEqual(Y_aug[1].domain, BooleanSet)
        _constrs_contained_within(
            self, [(None, sum(m.Y[:].get_associated_binary()) -
                    (1 + 2 * Y_aug[1].get_associated_binary()), 0),
                   (1, (1 - m.Y[1].get_associated_binary()) +
                    Y_aug[1].get_associated_binary(), None),
                   (None, 2 - 2 * (1 - Y_aug[1].get_associated_binary()) -
                    sum(m.Y[:].get_associated_binary()), 0)],
            m.logic_to_linear.transformed_constraints)

        m = ConcreteModel()
        m.s = RangeSet(3)
        m.Y = BooleanVar(m.s)
        m.p = LogicalConstraint(
            expr=m.Y[1].implies(atmost(2, m.Y[1], m.Y[2], m.Y[3])))
        TransformationFactory('core.logical_to_linear').apply_to(m)
        Y_aug = m.logic_to_linear.augmented_vars
        self.assertEqual(len(Y_aug), 1)
        self.assertEqual(Y_aug[1].domain, BooleanSet)
        _constrs_contained_within(
            self, [(None, sum(m.Y[:].get_associated_binary()) -
                    (1 - Y_aug[1].get_associated_binary() + 2), 0),
                   (1, (1 - m.Y[1].get_associated_binary()) +
                    Y_aug[1].get_associated_binary(), None),
                   (None, 3 - 3 * Y_aug[1].get_associated_binary() -
                    sum(m.Y[:].get_associated_binary()), 0)],
            m.logic_to_linear.transformed_constraints)

        m = ConcreteModel()
        m.s = RangeSet(3)
        m.Y = BooleanVar(m.s)
        m.p = LogicalConstraint(
            expr=m.Y[1].implies(exactly(2, m.Y[1], m.Y[2], m.Y[3])))
        TransformationFactory('core.logical_to_linear').apply_to(m)
        Y_aug = m.logic_to_linear.augmented_vars
        self.assertEqual(len(Y_aug), 3)
        self.assertEqual(Y_aug[1].domain, BooleanSet)
        _constrs_contained_within(self, [
            (1, (1 - m.Y[1].get_associated_binary()) +
             Y_aug[1].get_associated_binary(), None),
            (None, sum(m.Y[:].get_associated_binary()) -
             (1 - Y_aug[1].get_associated_binary() + 2), 0),
            (None, 2 - 2 * (1 - Y_aug[1].get_associated_binary()) -
             sum(m.Y[:].get_associated_binary()), 0),
            (1, sum(Y_aug[:].get_associated_binary()), None),
            (None, sum(m.Y[:].get_associated_binary()) -
             (1 + 2 * (1 - Y_aug[2].get_associated_binary())), 0),
            (None, 3 - 3 * (1 - Y_aug[3].get_associated_binary()) -
             sum(m.Y[:].get_associated_binary()), 0),
        ], m.logic_to_linear.transformed_constraints)

        # Note: x is now a variable
        m = ConcreteModel()
        m.s = RangeSet(3)
        m.Y = BooleanVar(m.s)
        m.x = Var(bounds=(1, 3))
        m.p = LogicalConstraint(
            expr=m.Y[1].implies(exactly(m.x, m.Y[1], m.Y[2], m.Y[3])))
        TransformationFactory('core.logical_to_linear').apply_to(m)
        Y_aug = m.logic_to_linear.augmented_vars
        self.assertEqual(len(Y_aug), 3)
        self.assertEqual(Y_aug[1].domain, BooleanSet)
        _constrs_contained_within(self, [
            (1, (1 - m.Y[1].get_associated_binary()) +
             Y_aug[1].get_associated_binary(), None),
            (None, sum(m.Y[:].get_associated_binary()) -
             (m.x + 2 * (1 - Y_aug[1].get_associated_binary())), 0),
            (None, m.x - 3 * (1 - Y_aug[1].get_associated_binary()) -
             sum(m.Y[:].get_associated_binary()), 0),
            (1, sum(Y_aug[:].get_associated_binary()), None),
            (None, sum(m.Y[:].get_associated_binary()) -
             (m.x - 1 + 3 * (1 - Y_aug[2].get_associated_binary())), 0),
            (None, m.x + 1 - 4 * (1 - Y_aug[3].get_associated_binary()) -
             sum(m.Y[:].get_associated_binary()), 0),
        ], m.logic_to_linear.transformed_constraints)
예제 #19
0
 def test_nothing_to_do(self):
     m = ConcreteModel()
     m.p = LogicalConstraint()
     TransformationFactory('core.logical_to_linear').apply_to(m)
     self.assertIsNone(m.component('logic_to_linear'))
예제 #20
0
def build_model():
    m = ConcreteModel()

    seed(1)  # Fix seed to generate same parameters and solution every time
    m.T_max = randint(10, 10)
    m.T = RangeSet(m.T_max)

    # Variables
    m.s = Var(m.T, domain=NonNegativeReals, bounds=(0, 10000), doc='stock')
    m.x = Var(m.T, domain=NonNegativeReals, bounds=(0, 10000), doc='purchased')
    m.c = Var(m.T, domain=NonNegativeReals, bounds=(0, 10000), doc='cost')
    m.f = Var(m.T, domain=NonNegativeReals, bounds=(0, 10000), doc='feed')

    m.max_q_idx = RangeSet(m.T_max)

    # Randomly generated parameters
    m.D = Param(m.T, doc='demand',
                initialize=dict((t, randint(50, 100)) for t in m.T))
    m.alpha = Param(m.T, doc='storage cost',
                    initialize=dict((t, randint(5, 20)) for t in m.T))
    m.gamma = Param(m.T, doc='base buying cost',
                    initialize=dict((t, randint(10, 30)) for t in m.T))
    m.beta_B = Param(m.T, doc='bulk discount',
                     initialize=dict((t, randint(50, 500)/1000) for t in m.T))

    m.F_B_lo = Param(m.T, doc='bulk minimum purchase amount',
                     initialize=dict((t, randint(50, 100)) for t in m.T))

    m.beta_L = Param(m.T, m.max_q_idx,
                     initialize=dict(((t, q), randint(10, 999)/1000)
                                     for t in m.T for q in m.max_q_idx),
                     doc='long-term discount')
    m.F_L_lo = Param(m.T, m.max_q_idx,
                     initialize=dict(((t, q), randint(50, 100))
                                     for t in m.T for q in m.max_q_idx),
                     doc='long-term minimum purchase amount')

    # Contract choices 'standard', 'bulk' and long term contracts '0','1',...
    time_time_choices = [(t1, str(t2)) for t1, t2 in m.T * m.T if t2 <= m.T_max - t1]
    time_special_choices = [(t, s) for t in m.T for s in {'S', 'B', '0'}]
    m.contract_time_choices = Set(initialize=time_time_choices + time_special_choices)
    m.disjunct_choices = Set(
        initialize=['S', 'B', *[str(t) for t in range(m.T_max)]])
    m.disjuncts = Disjunct(m.contract_time_choices)
    m.Y = BooleanVar(m.contract_time_choices)
    for t, c in m.contract_time_choices:
        m.Y[t, c].associate_binary_var(m.disjuncts[t, c].indicator_var)

    # Create disjuncts for contracts in each timeset
    for t in m.T:
        m.disjuncts[t, 'S'].cost = Constraint(expr=m.c[t] == m.gamma[t]*m.x[t])

        m.disjuncts[t, 'B'].cost = Constraint(
            expr=m.c[t] == (1-m.beta_B[t])*m.gamma[t]*m.x[t])
        m.disjuncts[t, 'B'].amount = Constraint(
            expr=m.x[t] >= m.F_B_lo[t])

        m.disjuncts[t, '0'].c = Constraint(expr=0 <= m.c[t])

        for q in range(1, m.T_max-t+1):
            m.disjuncts[t, str(q)].t_idx = RangeSet(t, t+q)
            m.disjuncts[t, str(q)].cost = Constraint(m.disjuncts[t, str(q)].t_idx)
            m.disjuncts[t, str(q)].amount = Constraint(m.disjuncts[t, str(q)].t_idx)
            for t_ in m.disjuncts[t, str(q)].t_idx:
                m.disjuncts[t, str(q)].cost[t_] =\
                    m.c[t_] == (1-m.beta_L[t, q])*m.gamma[t]*m.x[t_]
                m.disjuncts[t, str(q)].amount[t_] =\
                    m.x[t_] >= m.F_L_lo[t, q]

    # Create disjunctions
    @m.Disjunction(m.T, xor=True)
    def disjunctions(m, t):
        return [m.disjuncts[t, 'S'], m.disjuncts[t, 'B'], m.disjuncts[t, '0'],
                *[m.disjuncts[t, str(q)] for q in range(1, m.T_max-t+1)]]

    # Connect the disjuncts indicator variables using logical expressions
    m.logical_blocks = Block(range(1, m.T_max+1))

    # Enforce absence of existing long-term contract
    m.logical_blocks[1].not_y_1_0 = LogicalConstraint(expr=~m.Y[1, '0'], doc="no pre-existing long-term contract")

    # Long-term contract implies '0'-disjunct in following timesteps
    for t in range(2, m.T_max+1):
        m.logical_blocks[t].equiv = LogicalConstraint(
            expr=m.Y[t, '0'].equivalent_to(lor(m.Y[t_, str(q)] for t_ in range(1, t) for q in range(t - t_, m.T_max - t_ + 1)))
        )

    # Objective function
    m.objective = Objective(expr=sum(m.alpha[t]*m.s[t]+m.c[t] for t in m.T))

    # Global constraints
    m.demand_satisfaction = Constraint(m.T)
    for t in m.T:
        m.demand_satisfaction[t] = m.f[t] >= m.D[t]

    m.material_balance = Constraint(m.T)
    for t in m.T:
        m.material_balance[t]=m.s[t] == (m.s[t-1] if t>1 else 0) + m.x[t] - m.f[t]

    return m