Search code examples
pyomo

Not initializing variables in Pyomo


By default, variables seem to be initialized in Pyomo. Then the solver uses this initial point. Is there a way to not initialize variables in Pyomo and let the solver compute the initial point? I tried to initialized to None but an initial point is still passed to the solver

Here is a simple example:

import pyomo.environ as pyo

model = pyo.ConcreteModel()

model.x = pyo.Var(within=pyo.Reals)

model.objective = pyo.Objective(
        expr=(model.x + 10)**2,
        sense=pyo.minimize)

solver = pyo.SolverFactory('knitroampl')
results = solver.solve(model, tee=True)

Knitro would indicate "No initial point provided" if it was the case:

Knitro presolve eliminated 0 variables and 0 constraints.

concurrent_evals         0
datacheck                0
hessian_no_f             1
hessopt                  1
The problem is identified as unconstrained.

Problem Characteristics                                 (   Presolved)
-----------------------
Objective goal:  Minimize
Objective type:  quadratic
Number of variables:                                  1 (           1)
    bounded below only:                               0 (           0)
    bounded above only:                               0 (           0)
    bounded below and above:                          0 (           0)
    fixed:                                            0 (           0)
    free:                                             1 (           1)
Number of constraints:                                0 (           0)
    linear equalities:                                0 (           0)
    quadratic equalities:                             0 (           0)
    gen. nonlinear equalities:                        0 (           0)
    linear one-sided inequalities:                    0 (           0)
    quadratic one-sided inequalities:                 0 (           0)
    gen. nonlinear one-sided inequalities:            0 (           0)
    linear two-sided inequalities:                    0 (           0)
    quadratic two-sided inequalities:                 0 (           0)
    gen. nonlinear two-sided inequalities:            0 (           0)
Number of nonzeros in Jacobian:                       0 (           0)
Number of nonzeros in Hessian:                        1 (           1)

Knitro using the Interior-Point/Barrier Direct algorithm.

  Iter      Objective      FeasError   OptError    ||Step||    CGits
--------  --------------  ----------  ----------  ----------  -------
       0    1.000000e+02   0.000e+00
       1    0.000000e+00   0.000e+00   0.000e+00   1.000e+01        0

EXIT: Locally optimal solution found.

Final Statistics
----------------
Final objective value               =   0.00000000000000e+00
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   0.00e+00 / 0.00e+00
# of iterations                     =          1
# of CG iterations                  =          0
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00318 (     0.016 CPU time)
Time spent in evaluations (secs)    =       0.00000

===============================================================================

Solution

  • TL;DR: this is an issue with / artifact of the Knitro / ASL (AMPL Solver Library) interface and not Pyomo.


    Pyomo only passes initial variable values to ASL solvers for variables whose values are not None. You can see this by looking at the NL file that Pyomo produces. For your model:

    from pyomo.opt import WriterFactory
    with open('test.nl', 'w') as NL_FILE:
        WriterFactory('nl').write(model, NL_FILE)
    

    gives the following NL file:

    g3 1 1 0    # problem unknown
     1 0 1 0 0  # vars, constraints, objectives, ranges, eqns
     0 1 0 0 0 0    # nonlinear constrs, objs; ccons: lin, nonlin, nd, nzlb
     0 0    # network constraints: nonlinear, linear
     0 1 0  # nonlinear vars in constraints, objectives, both
     0 0 0 1    # linear network variables; functions; arith, flags
     0 0 0 0 0  # discrete variables: binary, integer, nonlinear (b,c,o)
     0 1    # nonzeros in Jacobian, obj. gradient
     0 0    # max name lengths: constraints, variables
     0 0 0 0 0  # common exprs: b,c,o,c1,o1
    O0 0
    o5
    o0
    v0
    n10
    n2
    x0
    r
    b
    3
    k0
    G0 1
    0 0
    

    Reading NL files is a bit involved (see https://ampl.github.io/nlwrite.pdf), but the relevant line is x0, which tells the ASL that we are not providing any initial values.

    You can see the difference by setting an initial value for the variable:

    model.x = 42
    with open('test_initialized.nl', 'w') as NL_FILE:
        WriterFactory('nl').write(model, NL_FILE)
    

    Which gives a new NL file with the lines x1 and 0 42 (we are providing a single initial value, and the initial value of variable #0 is 42):

    g3 1 1 0        # problem unknown
     1 0 1 0 0      # vars, constraints, objectives, ranges, eqns
     0 1 0 0 0 0    # nonlinear constrs, objs; ccons: lin, nonlin, nd, nzlb
     0 0    # network constraints: nonlinear, linear
     0 1 0  # nonlinear vars in constraints, objectives, both
     0 0 0 1        # linear network variables; functions; arith, flags
     0 0 0 0 0      # discrete variables: binary, integer, nonlinear (b,c,o)
     0 1    # nonzeros in Jacobian, obj. gradient
     0 0    # max name lengths: constraints, variables
     0 0 0 0 0      # common exprs: b,c,o,c1,o1
    O0 0
    o5
    o0
    v0
    n10
    n2
    x1
    0 42
    r
    b
    3
    k0
    G0 1
    0 0
    

    The fact that Knitro is not emitting No initial point provided is an artifact of how Knitro was built to work with the ASL.