convexengineering / gpkit

Geometric programming for engineers
http://gpkit.readthedocs.org
MIT License
206 stars 40 forks source link

.localsolve ignoring x0 and use_pccp #1520

Closed dkhall closed 3 years ago

dkhall commented 4 years ago

I have a fairly complex model, with a few SP constraints, that is sensitive to the initial guess, i.e., won't converge (or won't converge without slack) unless I give it a good x0. When I set x0= a pickle of a previous solution, it solves fine in 2 or 3 GPs. However, if I set x0= a dictionary with the same information as in the pickle, the solver appears to ignore x0 and has the same convergence history as if no x0 was given.

If I go back to 1f5f8bf, the behavior is as expected, with both the pickle-load and user-defined dictionary yielding the same convergence history. Beyond b6fd017, only the pickle-loaded x0 works, and .localsolve also appears to ignore use_pccp=False.

bqpd commented 4 years ago

Oops! Do you have a working example? I test some small x0s but clearly didn't catch this.

use_pccp=False doesn't fall back on the previous algorithm, but rather tries to do a sequential solve without any slack variables or feasibility solves, so it's likely to only work with a full x0 and no SignomialEqualities

dkhall commented 4 years ago

The case I'm dealing with is probably too unwieldy to share, spread across multiple modules. I'll work to come up with a MWE.

nikilrselvam commented 3 years ago

Hi, I would also like to report similar issues.

1. use_pccp=False is ignored.

The nature of my problem requires that the optimization problem be solved without any slack (it is always feasible). However, the program seems to ignore use_pccp=False. I have tried using high values of pccp_penalty, but it does not seem to be making a difference as the value of my objective function is often pretty large. Additionally, it is worth noting that the program has no signomial equality constraints.

My signomial program is a little complex, but you can find a snippet at the end of this comment for your reference.

2. x0 ignored if supplied user-defined dictionary.

I was able to find a small example for this (inspired by the documentation).

x = Variable('x')
y = Variable('y')

with SignomialsEnabled():
    constraints = [x >= 1-y, y <= 0.2]
    constraints+= [x>=0.000001]

d={x:0.8, y:0.2} # this is the optimal solution

m = Model(x, constraints)
print(m.localsolve(verbosity=0, solver='mosek_cli', x0=d).summary())

The program above ignores x0 and takes 4 iterations to arrive at the optimal value.

3. Other potentially related issues

Sample program which ignores use_pccp=False

from gpkit import Model, Variable, SignomialsEnabled
from gpkit.constraints.bounded import Bounded

# ... variables are th1 through th40.

# prev_res obtained from a previous solve
prev_res={th1: 0.1271235284874968, th2: 0.8728764994398744, th3: 0.7035231472386804, th4: 0.29647689185193177, th5: 0.4899583458305375, th6: 0.5100416448647169, th7: 0.6034266420601294, th8: 0.3965733346611888, th9: 0.6684074074242299, th10: 0.33159265703838525, th11: 0.22930254274891962, th12: 0.7706973689874355, th13: 1.0, th14: 0.8630073250876732, th15: 0.13699265057923807, th16: 1.0, th17: 1.0, th18: 1.0, th19: 1.0, th20: 0.9365216681429392, th21: 0.06347831039588891, th22: 0.7518573253206168, th23: 0.248142525077401, th24: 0.4436889611463308, th25: 0.5563109936540922, th26: 0.46457080482419366, th27: 0.5354292057400054, th28: 0.7516171452759821, th29: 0.24838284333694824, th30: 0.17493339106490582, th31: 0.8250666566267977, th32: 1.0, th33: 0.9183586668841595, th34: 0.08164138307826121, th35: 1.0, th36: 1.0, th37: 0.999999991511505, th38: 0.999999991511505, th39: 0.8758025613814215, th40: 0.12419740542485974, SGP.PCCPslack: 2.236259267395232}

objective_function= th1^-5412·th2^-37332·th3^-30064·th4^-12680·th5^-20955·th6^-21789·th7^-25856·th8^-16888·th9^-28464·th10^-14280·th11^-9924·th12^-32820·th13^-42744·th14^-36751·th15^-5993·th16^-42744·th17^-42744·th18^-42744·th19^-42744·th20^-5705·th21^-385·th22^-4585·th23^-1505·th24^-2691·th25^-3399·th26^-2744·th27^-3346·th28^-4697·th29^-1393·th30^-934·th31^-5156·th32^-6090·th33^-5739·th34^-351·th35^-6090·th36^-6090·th37^-6090·th38^-6090·th39^-42744·th40^-6090

# The last 2 constraints are signomial constraints
constraints = [th1 <= 1, th2 <= 1, th3 <= 1, th4 <= 1, th5 <= 1, th6 <= 1, th7 <= 1, th8 <= 1, th9 <= 1, th10 <= 1, th11 <= 1, th12 <= 1, th13 <= 1, th14 <= 1, th15 <= 1, th16 <= 1, th17 <= 1, th18 <= 1, th19 <= 1, th20 <= 1, th21 <= 1, th22 <= 1, th23 <= 1, th24 <= 1, th25 <= 1, th26 <= 1, th27 <= 1, th28 <= 1, th29 <= 1, th30 <= 1, th31 <= 1, th32 <= 1, th33 <= 1, th34 <= 1, th35 <= 1, th36 <= 1, th37 <= 1, th38 <= 1, th39 <= 1, th40 <= 1, th1 + th2 <= 1, th3 + th4 <= 1, th5 + th6 <= 1, th7 + th8 <= 1, th9 + th10 <= 1, th11 + th12 <= 1, th13 <= 1, th14 + th15 <= 1, th16 <= 1, th17 <= 1, th18 <= 1, th19 <= 1, th20 + th21 <= 1, th22 + th23 <= 1, th24 + th25 <= 1, th26 + th27 <= 1, th28 + th29 <= 1, th30 + th31 <= 1, th32 <= 1, th33 + th34 <= 1, th35 <= 1, th36 <= 1, th37 <= 1, th38 <= 1, th39 + th40 <= 1, th39·th19·th1·th18·th6·th3·th17·th7·th16·th13·th10·th11·th15·(th39·th19·th1·th18·th6·th3 + th40·th38·th20·th37·th25·th22) + 1 - th39·th19·th1·th18·th6·th3·(th39·th19·th1·th18·th6·th3·th17·th7·th16·th13·th10·th11·th15 + th40·th38·th20·th37·th25·th22·th36·th26·th35·th32·th29·th30·th34) - 0.1·(th39·th19·th1·th18·th6·th3·th17·th7·th16·th13·th10·th11·th15 + th40·th38·th20·th37·th25·th22·th36·th26·th35·th32·th29·th30·th34)·(th39·th19·th1·th18·th6·th3 + th40·th38·th20·th37·th25·th22) <= 1, th39·th19·th1·th18·th6·th3·(th39·th19·th1·th18·th6·th3·th17·th7·th16·th13·th10·th11·th15 + th40·th38·th20·th37·th25·th22·th36·th26·th35·th32·th29·th30·th34) + 1 - th39·th19·th1·th18·th6·th3·th17·th7·th16·th13·th10·th11·th15·(th39·th19·th1·th18·th6·th3 + th40·th38·th20·th37·th25·th22) - 0.1·(th39·th19·th1·th18·th6·th3·th17·th7·th16·th13·th10·th11·th15 + th40·th38·th20·th37·th25·th22·th36·th26·th35·th32·th29·th30·th34)·(th39·th19·th1·th18·th6·th3 + th40·th38·th20·th37·th25·th22) <= 1]

m = Model(objective_function, Bounded(constraints))
print(m.localsolve(verbosity=0, solver='mosek_cli', use_pccp=False, x0=prev_res).summary())

Thanks for your support!

bqpd commented 3 years ago

This model does not seem to be bounded (and requires slackening by 0.00056% to solve, but that may just be numerical noise). I'll try reproducing the error another way!

With regards to pccp_solve, it's worth noting that enabling it generally makes the solve faster even if it's already feasible.

bqpd commented 3 years ago

Figured out why it wasn't disabling PCCP - there's a different syntax for that which I forgot to document! Use m.sp(use_pccp=False).localsolve(...)

bqpd commented 3 years ago

@dkhall also figured out the x0 issue: it wasn't properly "cleaning" the x0, so if you were including strings or variable objects as keys -- instead of VarKey objects as the previous solution would use -- it wouldn't convert them to VarKeys. I'll fix & test this.

bqpd commented 3 years ago

@nikilrselvam re the points in 3: the x0 syntax and documentation definitely needs to be cleared up, I'll try to do that soon. As for the arbitrary keyword arguments, that's to make room for arbitrary arguments that a particular solver might use, as localsolve kwargs are passed along to the underlying solver! But because different solvers accept different arguments, none of them error on getting arguments they didn't want, leading to this silent acceptance.

bqpd commented 3 years ago

To summarize the above:

1. use_pccp=False is ignored.

It needs to be specified when creating the program, not when solving. This is a bit confusing, because calling localsolve/solve on a Model will automatically create the relevant program, but you can also do so manually with, e.g.: m.sp(use_pccp=False).localsolve(...)

2. x0 ignored if supplied user-defined dictionary.

This is because it's no longer "cleaned" before being passed to the GP, and so any strings that are variables or strings will get ignored. Quick fix: use varkeys directly, e.g. d={x.key:0.8, y.key:0.2}. I'll be changing the code to fix this, however, and use @nikilrselvam's example as a test.

@dkhall, @nikilrselvam hope this fixes your issues, and sorry for the wait!

dkhall commented 3 years ago

@bqpd Thanks!

Disabling PCCP works as expected now that I'm using the correct syntax.

I expect the x0 cleaning fix will improve my particular situation, as I am using the strings of the variable names as the keys in my x0 dictionary (which lives in a separate, static module, so I haven't tested the varkey quick fix).

nikilrselvam commented 3 years ago

@bqpd Thank you for your prompt response!

I'm unsure which model you were referring to in your first comment (where you reported a slack of 0.00056%). In case you were referring to the snippet of the non-trivial program in my original comment, I use Bounded(constraints) to introduce some trivial bounds. When I tried solving it with pccp, my model reported a slack of around 2.23.

However, more importantly, I can confirm that disabling PCCP now seems to work as expected for me too! Thank you for clarifying this.

bqpd commented 3 years ago

x0 fix is up, along with a fix to the error message that is presumably how y'all learned about use_pccp to clarify its proper syntax

bqpd commented 3 years ago

@nikilrselvam also, welcome to GPkit! How'd you hear about this project?

nikilrselvam commented 3 years ago

@bqpd Thank you! I'm sorry I missed this message. I'm currently working at the UCLA StarAI Lab, and a graduate student who had used GPkit for a prior project introduced me to it! I'm finding it extremely useful in my current research :)

bqpd commented 3 years ago

ah fabulous! glad to hear that.

bqpd commented 3 years ago

@dkhall @nikilrselvam I realized that the syntax above couldn't handle changing the pccp_penalty and sweeping, so I changed it in https://github.com/convexengineering/gpkit/pull/1548 such that the pccp_penalty can be an argument to localsolve after all; the syntax you were using before (and that I erroneously suggested in the error message) should now work!