Debugging using States

In DIDPPy, the values of expressions used in transitions, base cases, state constraints, and dual bounds depend on the values of state variables, which are manipulated inside the solver. However, we sometimes want to know the values of the expressions given a specific state. DIDPPy allows us to do this kind of debugging using State. You can even develop your own solver in Python using the interfaces introduced here.

Getting and Changing a State

We can get the target state of the model by didppy.Model.target_state.

>>> import didpppy as dp
>>> model = dp.Model()
>>> var = model.add_int_var(target=0)
>>> state = model.target_state
>>> state[var]
0

We can get and change the value of a state variable using state[var].

>>> state[var] = 1
>>> state[var]
1

Note that the value of the target state does not change with the above code because state is the copy of the target state. If we want to change the target state, we can use set_target() or do like the following.

>>> model.target_state = state

However, model.target_state[var] = 1 does not work.

In the case of SetVar, we need to use SetConst to change its value.

>>> import didppy as dp
>>> model = dp.Model()
>>> obj = model.add_object_type(number=4)
>>> var = model.add_set_var(object_type=obj, target=[0, 1])
>>> state = model.target_state
>>> state[var]
{0, 1}
>>> const = model.create_set_const(object_type=obj, value=[1, 2])
>>> state[var] = const
>>> state[var]
{1, 2}

Evaluating an Expression

We can evaluate an expression given a state with eval(). Expressions other than IntExpr can be evaluated in the same way.

>>> import didppy as dp
>>> model = dp.Model()
>>> var = model.add_int_var(target=0)
>>> state = model.target_state
>>> (var + 1).eval(state, model)
1
>>> (var > 0).eval(state, model)
False

Applying a Transition to a State

For a transition, we can check if it is applicable, the values of state variables after application, and the cost given a state.

>>> import didppy as dp
>>> model = dp.Model()
>>> var = model.add_int_var(target=0)
>>> state = model.target_state
>>> transition = dp.Transition(
...     name="increment",
...     cost=2 + dp.IntExpr.state_cost(),
...     preconditions=[var <= 2],
...     effects=[(var, var + 1)],
... )
>>> transition.is_applicable(state, model)
True
>>> next_state = transition.apply(state, model)
>>> next_state[var]
1
>>> transition.eval_cost(1, state, model)
3

We can also get components of transitions by using cost, preconditions, and transition[var] for an effect.

>>> cost = transition.cost
>>> cost.eval_cost(1, state, model)
3
>>> preconditions = transition.preconditions
>>> preconditions[0].eval(state, model)
True
>>> effect = transition[var]
>>> effect.eval(state, model)
1

Note that the order of the preconditions might be changed due to internal implementation.

We can update cost and the effects.

>>> transition.cost = 1 + dp.IntExpr.state_cost()
>>> transition.eval_cost(1, state, model)
2
>>> transition[var] = var + 2
>>> next_state = transition.apply(state, model)
>>> next_state[var]
2

Checking Base Cases

We can check if a state satisfies the base cases with eval_base_cost(). If base cases are satisfied, the value of the state is returned. Otherwise, None is returned.

>>> import didppy as dp
>>> model = dp.Model()
>>> var = model.add_int_var(target=2)
>>> model.add_base_case([var >= 2], cost=2)
>>> state = model.target_state
>>> model.eval_base_cost(state)
2

We can get the base cases with base_cases. It returns a list of tuples of conditions (a list of conditions) and the cost.

>>> base_cases = model.base_cases
>>> base_cases[0][0][0].eval(state, model)
True
>>> base_cases[0][1].eval(state, model)
2

The order of base cases is preserved.

Checking State Constraints

We can check if a state satisfies the state constraints with check_state_constr().

>>> import didppy as dp
>>> model = dp.Model()
>>> var = model.add_int_var(target=0)
>>> model.add_state_constr(var >= 0)
>>> state = model.target_state
>>> model.check_state_constr(state)
True

We can get the state constraints with state_constrs.

>>> constraints = model.state_constrs
>>> constraints[0].eval(state, model)
True

The order of state constraints is preserved.

Evaluating Dual Bound

We can evaluate the value of the dual bound for a state with eval_dual_bound().

>>> import didppy as dp
>>> model = dp.Model()
>>> var = model.add_int_var(target=0)
>>> model.add_dual_bound(var)
>>> state = model.target_state
>>> model.eval_dual_bound(state)
0

We can get the dual bounds with dual_bounds.

>>> dual_bounds = model.dual_bounds
>>> dual_bounds[0].eval(state, model)
0

The order of dual bounds is preserved.