Тайм-аут для Z3 Optimize
Как установить тайм-аут для оптимизатора z3, чтобы он давал вам наиболее известное решение, когда время истекает?
from z3 import *
s = Optimize()
# Hard Problem
print(s.check())
print(s.model())
Последующий вопрос: можете ли вы настроить z3 на случайное восхождение на холм или он всегда выполняет полный поиск?
2 ответа
Длинный ответ, короткий, нельзя. Оптимизатор работает не так. То есть он не находит решения, а затем пытается его улучшить. Если вы прервете его или установите тайм-аут, когда таймер истечет, у него может даже не быть удовлетворительного решения, не говоря уже об "улучшенном" каким-либо образом. Подробности см. В документе по оптимизации: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/nbjorner-nuz.pdf
Однако верно, что z3 действительно отслеживает границы переменных для числовых величин. Возможно, вам удастся извлечь их, хотя в целом у вас не будет возможности узнать, какие значения из этих интервалов вам нужно выбрать, чтобы получить удовлетворительное решение для общей проблемы. См. Этот ответ для обсуждения: возможно ли получить достоверную информацию о диапазоне при использовании ограничения SMT с Z3
На этом форуме часто возникают такие "восхождения в гору". Ответ прост: оптимизатор z3 работает не так. Некоторые предыдущие вопросы таким образом:
Есть несколько других вопросов по этим строкам в stack-overflow. Ищите "оптимизировать" и "тайм-аут".
Ваш лучший выбор
Это теоретическая сторона дела. На практике я считаю, что лучший подход к решению проблемы такого рода - вообще не использовать оптимизатор. Вместо этого сделайте следующее:
- Укажите свою проблему
- Спросите модель. Если модели нет, ответьте
unsat
. Уволиться. - Сохраняйте текущую модель как "лучшую на данный момент"
- Вне времени? Верните имеющуюся у вас модель как "лучшую на данный момент". Вы сделали.
Еще есть время?
5а. Вычислите "стоимость" этой модели. т. е. показатель, который вы пытаетесь минимизировать или максимизировать. Если вы храните стоимость в качестве переменной в своей модели, вы можете просто запросить ее значение из модели.
5б. Утвердите новое ограничение, согласно которому стоимость должна быть ниже, чем стоимость текущей модели. (Или выше, если вы максимизируете.) В зависимости от того, насколько хорошо вы хотите получить, вы можете захотеть "удвоить" функцию стоимости или реализовать какой-то бинарный поиск, чтобы быстрее найти значение. Но все это действительно зависит от точных деталей проблемы.
5c. Спросите новую модель. Если
unsat
, верните последнюю полученную модель как "оптимальную". В противном случае повторите с шага 3.
Я считаю, что это наиболее практичный подход для оптимизации временных ограничений в z3. Это дает вам полный контроль над тем, сколько раз выполнять итерацию, и направлять поиск любым удобным для вас способом. (Например, вы можете запросить различные переменные для каждой модели и направить поиск, сказав "найди мне более крупнуюx
, или меньший y
и т. д., вместо того, чтобы смотреть только на одну метрику.) Надеюсь, что это имеет смысл.
Резюме
Обратите внимание, что решающая программа SMT может работать так, как вы описываете, т. Е. Давать вам оптимальное на данный момент решение, когда истекает время ожидания. Просто оптимизатор z3 так не работает. Для z3 я обнаружил, что итерационный цикл, описанный выше, является наиболее практичным решением такого рода оптимизации на основе тайм-аута.
Вы также можете посмотреть OptiMathSAT ( http://optimathsat.disi.unitn.it/), который может предложить лучшие возможности в этом отношении. @ Патрик Трентен, который часто читает этот форум, является экспертом в этом вопросе и может высказать отдельное мнение относительно его использования.
В общем, Levent Erkok прав, когда заявляет, что решатель OMT не дает никаких гарантий того, что решение будет доступно в конце поиска оптимизации, когда он прерываетсяtimeout
сигнал.
Решатель OMT может искать оптимальное решение одним из двух способов:
начиная с исходной модели формулы и пытаясь улучшить значение целевой функции; Это случай стандартного подхода OMT, который перечисляет ряд частично оптимизированных решений, пока не будет найдено оптимальное.
начиная с более чем оптимального, неудовлетворительного назначения и постепенно ослабляя такое назначение, пока оно не приведет к оптимальному решению; AFAIK, это только случай механизма максимального разрешения для решения проблем MaxSMT.
Когда решающая программа OMT использует технику оптимизации, которая попадает в первую категорию, тогда возможно получить наиболее известное решение, когда для него не хватит времени, при условии, что решающая программа OMT сохранит его в безопасном месте во время поиска оптимизации. Это не относится ко второму механизму MaxRes (см. Этот вопрос / ответ).
Возможный обходной путь. (CAVEAT: я не тестировал это)
z3
отслеживает lower
а также upper
граница целевой функции по поиску оптимизации. При сворачиванииupper
граница соответствует значению целевой функции в последнем частичном решении, найденном решателем OMT (двойное для максимизации). После появления сигнала тайм-аута при минимизации (или максимизации)obj
экземпляр получен из minimize()
(соотв. maximize()
), должно быть возможно получить последнее приближение v
оптимального значения obj
позвонив obj.upper()
(соотв. obj.lower()
). Предполагая, что такое значениеv
отличается от +oo
(соотв. -oo
), можно постепенно изучать ограничение вида cost = v
и выполнить инкрементную SMT-проверку выполнимости, чтобы восстановить модель, соответствующую субоптимальному решению, для которого z3
.
OptiMathSAT
- это одна из решателей OMT, которая хранит в надежном месте последнее решение, с которым сталкивается во время поиска по оптимизации. Это позволяет легко достичь того, что вы хотите делать.
Есть два типа timeout
сигналы в OptiMathSAT
:
жесткий тайм-аут: как только
timeout
срабатывает, поиск оптимизации немедленно останавливается; если решатель OMT нашел какое-либо решение, результат поиска оптимизации (доступен черезmsat_objective_result(env, obj)
) являетсяMSAT_OPT_SAT_PARTIAL
и модель, соответствующая последнему субоптимальному решению, может быть извлечена и распечатана; если вместо этого решающая программа OMT не нашла никакого решения, результатом поиска оптимизации будетMSAT_UNKNOWN
и модель недоступна.мягкий тайм-аут: если
timeout
срабатывает после того, как решатель OMT нашел какое-либо решение, затем поиск немедленно останавливается, как в случае жесткого тайм-аута. В противном случаеtimeout
не игнорируется, пока ОМТ решатель находит одно решение.
Тип timeout
сигнал можно установить с помощью опции opt.soft_timeout=[true|false]
.
Пример: Следующий пример - это модульный тест timeout.py, содержащийся в моем репозитории github omt_python_examples, который содержит ряд примеров использования интерфейса API Python дляOptiMathSAT
.
"""
timeout unit-test.
"""
###
### SETUP PATHS
###
import os
import sys
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
INCLUDE_DIR = os.path.join(BASE_DIR, '..', 'include')
LIB_DIR = os.path.join(BASE_DIR, '..', 'lib')
sys.path.append(INCLUDE_DIR)
sys.path.append(LIB_DIR)
from wrapper import * # pylint: disable=unused-wildcard-import,wildcard-import
###
### DATA
###
OPTIONS = {
"model_generation" : "true", # !IMPORTANT!
"opt.soft_timeout" : "false",
"opt.verbose" : "true",
}
###
### TIMEOUT UNIT-TEST
###
with create_config(OPTIONS) as cfg:
with create_env(cfg) as env:
# Load Hard Problem from file
with open(os.path.join(BASE_DIR, 'smt2', 'bacp-19.smt2'), 'r') as f:
TERM = msat_from_smtlib2(env, f.read())
assert not MSAT_ERROR_TERM(TERM)
msat_assert_formula(env, TERM)
# Impose a timeout of 3.0 seconds
CALLBACK = Timer(3.0)
msat_set_termination_test(env, CALLBACK)
with create_minimize(env, "objective", lower="23", upper="100") as obj:
assert_objective(env, obj)
solve(env) # optimization search until timeout
get_objectives_pretty(env) # print latest range of optimization search
load_model(env, obj) # retrieve sub-optimal model
dump_model(env) # print sub-optimal model
Это подробный результат поиска по оптимизации:
# obj(.cost_0) := objective
# obj(.cost_0) - search start: [ 23, 100 ]
# obj(.cost_0) - linear step: 1
# obj(.cost_0) - new: 46
# obj(.cost_0) - update upper: [ 23, 46 ]
# obj(.cost_0) - linear step: 2
# obj(.cost_0) - new: 130/3
# obj(.cost_0) - update upper: [ 23, 130/3 ]
# obj(.cost_0) - linear step: 3
# obj(.cost_0) - new: 40
# obj(.cost_0) - update upper: [ 23, 40 ]
# obj(.cost_0) - linear step: 4
# obj(.cost_0) - new: 119/3
# obj(.cost_0) - update upper: [ 23, 119/3 ]
# obj(.cost_0) - linear step: 5
# obj(.cost_0) - new: 112/3
# obj(.cost_0) - update upper: [ 23, 112/3 ]
# obj(.cost_0) - linear step: 6
# obj(.cost_0) - new: 104/3
# obj(.cost_0) - update upper: [ 23, 104/3 ]
# obj(.cost_0) - linear step: 7
# obj(.cost_0) - new: 34
# obj(.cost_0) - update upper: [ 23, 34 ]
# obj(.cost_0) - linear step: 8
# obj(.cost_0) - new: 133/4
# obj(.cost_0) - update upper: [ 23, 133/4 ]
# obj(.cost_0) - linear step: 9
# obj(.cost_0) - new: 161/5
# obj(.cost_0) - update upper: [ 23, 161/5 ]
# obj(.cost_0) - linear step: 10
# obj(.cost_0) - new: 32
# obj(.cost_0) - update upper: [ 23, 32 ]
# obj(.cost_0) - linear step: 11
# obj(.cost_0) - new: 158/5
# obj(.cost_0) - update upper: [ 23, 158/5 ]
# obj(.cost_0) - linear step: 12
# obj(.cost_0) - new: 247/8
# obj(.cost_0) - update upper: [ 23, 247/8 ]
# obj(.cost_0) - linear step: 13
# obj(.cost_0) - new: 123/4
# obj(.cost_0) - update upper: [ 23, 123/4 ]
# obj(.cost_0) - linear step: 14
# obj(.cost_0) - new: 61/2
# obj(.cost_0) - update upper: [ 23, 61/2 ]
# obj(.cost_0) - linear step: 15
unknown ;; <== Timeout!
(objectives
(objective 61/2), partial search, range: [ 23, 61/2 ]
) ;; sub-optimal value, latest search interval
course_load__ARRAY__1 : 9 ;; and the corresponding sub-optimal model
course_load__ARRAY__2 : 1
course_load__ARRAY__3 : 2
course_load__ARRAY__4 : 10
course_load__ARRAY__5 : 3
course_load__ARRAY__6 : 4
course_load__ARRAY__7 : 1
course_load__ARRAY__8 : 10
course_load__ARRAY__9 : 4
course_load__ARRAY__10 : 1
course_load__ARRAY__11 : 1
course_load__ARRAY__12 : 5
course_load__ARRAY__13 : 10
course_load__ARRAY__14 : 9
course_load__ARRAY__15 : 1
...
;; the sub-optimal model is pretty long, it has been cut to fit this answer!
...