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.