Constraints allow you to specify relationships between the parameters you want to optimize, or just constrain the values that a parameter can take.
For example, you may want an optional parameter to always be enabled if another parameter is set to True
, or you may want an IntParameter to be between 0 and 10, but never 3.
You can use all the familiar numeric and comparison operators.
Constraints are automatically converted to string expressions in a format that can be parsed by OPTaaS.
from mindfoundry.optaas.client.parameter import IntParameter
from mindfoundry.optaas.client.constraint import Constraint
x = IntParameter("x", id="id_x", minimum=0, maximum=20)
y = IntParameter("y", id="id_y", minimum=0, maximum=20)
Constraint(x<y)
#id_x < #id_y
Constraint(x+y == 25)
( #id_x + #id_y ) == 25
Constraint(x**2 < y%2)
( #id_x ** 2 ) < ( #id_y % 2 )
If you don't specify an id
for a parameter, the object id will be used instead - but it's not very readable!
no_id = IntParameter("no_id", minimum=0, maximum=20)
Constraint(x // y <= no_id)
( #id_x // #id_y ) <= #1476456425960
Use the binary operators |
and &
instead of or
and and
(this is because or
and and
cannot be overridden).
Enclose the operands in brackets to avoid any errors due to the precedence of the binary operators.
Constraint((x<3) | (x>5) & (y>=7))
( #id_x < 3 ) || ( ( #id_x > 5 ) && ( #id_y >= 7 ) )
Use is_present
and is_absent
to create constraints based on whether a parameter is present in a configuration.
from mindfoundry.optaas.client.parameter import FloatParameter
z = FloatParameter("z", id="id_z", minimum=0, maximum=1, optional=True)
Note that you can create conditional constraints that only apply when a condition is true (in this case, if z
is present).
Constraint(when=z.is_present(), then=z >= x/y)
if #id_z is_present then #id_z >= ( #id_x / #id_y )
Constraint(when=(x==0) & (y==0), then=z.is_absent())
if ( #id_x == 0 ) && ( #id_y == 0 ) then #id_z is_absent
You can use is_present
and is_absent
with parameters that are part of a choice.
from mindfoundry.optaas.client.parameter import ChoiceParameter
choice = ChoiceParameter("xy", [x, y], id="id_choice", default=y)
Constraint(when=x.is_present(), then=z<0.5)
if #id_x is_present then #id_z < 0.5
You can also use ==
and !=
with choices:
Constraint(when=choice==x, then=z<0.5)
if #id_choice == #id_x then #id_z < 0.5
Constraint(when=z==1, then=choice!=x)
if #id_z == 1 then #id_choice != #id_x
You can use ==
, !=
, is_present
and is_absent
with categoricals as well.
from mindfoundry.optaas.client.parameter import CategoricalParameter
abc = CategoricalParameter('abc', ['a', 'b', 'c'], id='id_abc')
Constraint(when=abc == "a", then=x == 0)
if #id_abc == 'a' then #id_x == 0
Constraint(when=x > 1, then=abc != "c")
if #id_x > 1 then #id_abc != 'c'
from mindfoundry.optaas.client.client import OPTaaSClient
client = OPTaaSClient('https://optaas.mindfoundry.ai', '<Your OPTaaS API key>')
task = client.create_task(
title='My Task With Constraints',
parameters=[abc, choice, z],
constraints=[
Constraint(when=(x==0) & (y==0), then=z.is_absent()),
Constraint(when=choice==x, then=z < 0.5),
Constraint(when=x==1, then=abc != "c")
]
)
All generated configurations will obey the constraints:
task.generate_configurations(10)
[{'type': 'default', 'values': {'abc': 'a', 'xy': {'y': 10}, 'z': 0.5}}, {'type': 'exploration', 'values': {'abc': 'a', 'xy': {'y': 10}}}, {'type': 'exploration', 'values': {'abc': 'a', 'xy': {'y': 16}}}, {'type': 'exploration', 'values': {'abc': 'a', 'xy': {'y': 20}}}, { 'type': 'exploration', 'values': {'abc': 'c', 'xy': {'x': 0}, 'z': 0.41254703404025717}}, { 'type': 'exploration', 'values': {'abc': 'a', 'xy': {'y': 11}, 'z': 0.8632684114311011}}, { 'type': 'exploration', 'values': {'abc': 'a', 'xy': {'x': 13}, 'z': 0.2981315596478492}}, { 'type': 'exploration', 'values': {'abc': 'a', 'xy': {'y': 6}, 'z': 0.09724688592587882}}, { 'type': 'exploration', 'values': {'abc': 'a', 'xy': {'y': 6}, 'z': 0.8982091428469245}}, { 'type': 'exploration', 'values': {'abc': 'b', 'xy': {'x': 2}, 'z': 0.3196519301900007}}]
If a configuration violates a constraint, it will not be accepted:
task.add_user_defined_configuration({'xy': {'x': 1}, 'z': 0.5, 'abc': 'c'})
OPTaaSError: 400 Configuration violates constraint: if #id_choice == #id_x then #id_z < 0.5