# Model Formulation A combinatorial optimization problem comprises decision variables, objective functions, and constraints. In "2. [](variables.md)," "3. [](objective.md)," and "4. [](constraint.md)," we explained that you can create decision variables using {py:class}`~amplify.VariableGenerator`. At the same time, you can construct objective functions and constraints using {py:class}`~amplify.Poly` and {py:class}`~amplify.Constraint`. This page describes how these can be combined and used together to express combinatorial optimization problems in program code. ```{seealso} In addition to the {py:class}`~amplify.Poly` class, you can use instances of the coefficient matrix {py:class}`~amplify.Matrix` class as an objective function. When you construct the objective function as {py:class}`~amplify.Matrix`, you can use it in the same way described on this page. The model converts the matrix to a {py:class}`~amplify.Poly`. For details on the format of the coefficient matrix, see "[](matrix.md)". ``` ## Model construction The Amplify SDK represents combinatorial optimization problems as instances of the {py:class}`~amplify.Model` class. For a combinatorial optimization problem with an objective function and constraints, a simple way is to add two things. Add a {py:class}`~amplify.Poly` that represents the objective function and a {py:class}`~amplify.Constraint` or {py:class}`~amplify.ConstraintList` that defines the constraints. ```{testcode} from amplify import VariableGenerator, Model, equal_to, one_hot gen = VariableGenerator() q = gen.array("Binary", shape=(2, 3)) objective = q[0, 0] * q[0, 1] - q[0, 2] constraint1 = equal_to(q[0, 0] + q[0, 1] - q[0, 2], 0) constraint2 = one_hot(q[1, :]) constraint_list = constraint1 + constraint2 ``` * Constructing the model by adding the objective function and constraints: ```{doctest} >>> model = objective + constraint1 >>> print(model) minimize: q_{0,0} q_{0,1} - q_{0,2} subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1) ``` * Constructing the model by adding the objective function and the constraint list: ```{doctest} >>> model = objective + constraint_list >>> print(model) minimize: q_{0,0} q_{0,1} - q_{0,2} subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1), q_{1,0} + q_{1,1} + q_{1,2} == 1 (weight: 1) ``` For a combinatorial optimization problem with either an objective function or constraints, you can pass a single instance to the constructor of the {py:class}`~amplify.Model` class. Pass a {py:class}`~amplify.Poly` (or {py:class}`~amplify.Matrix`) instance to represent the objective function. Pass a {py:class}`~amplify.Constraint` or {py:class}`~amplify.ConstraintList` instance to represent the constraints. * Constructing a model from the objective function ({py:class}`~amplify.Poly`): ```{doctest} >>> model = Model(objective) >>> print(model) minimize: q_{0,0} q_{0,1} - q_{0,2} ``` * Constructing a model from a single constraint object ({py:class}`~amplify.Constraint`): ```{doctest} >>> model = Model(constraint1) >>> print(model) minimize: 0 subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1) ``` * Constructing a model from multiple constraint objects ({py:class}`~amplify.ConstraintList`): ```{doctest} >>> model = Model(constraint_list) >>> print(model) minimize: 0 subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1), q_{1,0} + q_{1,1} + q_{1,2} == 1 (weight: 1) ``` You can also add constraints ({py:class}`~amplify.Constraint` or {py:class}`~amplify.ConstraintList`) after the model is constructed. ```{doctest} >>> model = Model(objective) >>> print(model) minimize: q_{0,0} q_{0,1} - q_{0,2} >>> model += constraint1 >>> print(model) minimize: q_{0,0} q_{0,1} - q_{0,2} subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1) ``` Addition and subtraction of the objective function are also possible. ```{doctest} >>> model += q[0, 0] * q[0, 1] >>> print(model) minimize: 2 q_{0,0} q_{0,1} - q_{0,2} subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1) ``` ```{doctest} >>> model -= q[0, 0] >>> print(model) minimize: 2 q_{0,0} q_{0,1} - q_{0,0} - q_{0,2} subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1) ``` ## Model attributes The {py:attr}`~amplify.Model.objective` property allows you to retrieve the objective function of the {py:class}`~amplify.Model` class. ```{testcode} from amplify import VariableGenerator, equal_to gen = VariableGenerator() q = gen.array("Binary", 2, 3) objective = q[0,0] * q[0,1] - q[0,2] constraint = equal_to(q[0,0] + q[0,1] - q[0,2], 0) model = objective + constraint ``` ```{doctest} >>> print(model.objective) q_{0,0} q_{0,1} - q_{0,2} ``` The {py:attr}`~amplify.Model.constraints` property allows the user to retrieve the constraints the {py:class}`~amplify.Model` class holds. This property returns an instance of the {py:class}`~amplify.ConstraintList` class. ```{doctest} >>> print(model.constraints) [q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1)] ``` ````{note} The Model stores the objective function and constraints without copying them. Therefore, changing the objective function or constraints in the model will also change the variables before model construction. A {py:class}`~amplify.Matrix` is the exception, because the model converts it to a {py:class}`~amplify.Poly`. ```{doctest} model = Model(objective) model += q[0, 0] assert model.objective == objective ``` ```` ## Changing the constraint weight in the model After the model is constructed, you may want to change the weight of the constraint included in the model. ```{note} See "[](#penalty-weight)" for information on adjusting the weights of constraints. ``` In the following example, the weight of a constraint is doubled. ```{doctest} >>> print(model) minimize: q_{0,0} q_{0,1} - q_{0,2} subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 1) >>> model.constraints[0] *= 2 >>> print(model) minimize: q_{0,0} q_{0,1} - q_{0,2} subject to: q_{0,0} + q_{0,1} - q_{0,2} == 0 (weight: 2) ```