adamconkey
adamconkey

Reputation: 4745

Retrieve symbolic function from cost or constraint in pydrake

I apparently have a complicated enough constraint function that it takes over 1 minute just to call prog.AddConstraint(), presumably because it's spending a long time constructing the symbolic function (although if you have other insights why it takes so long I would appreciate it). What I would like to do is pull out the symbolic constraint function and cache it so that once it's created, I don't need to wait 1 minute and it can just load it from disk.

My issue is I don't know how to access that function. I can clearly see the expression when I print it, for example in this simplified example:

from pydrake.all import MathematicalProgram

prog = MathematicalProgram()
x = prog.NewContinuousVariables(2, "x")
c = prog.AddConstraint(x[0] * x[1] == 1)
print(c)

I get the output:

ExpressionConstraint
0 <= (-1 + (x(0) * x(1))) <= 0

I realize I could parse that string and pull out the part that represents the function, but it seems like there should be a better way to do it? I'm thinking I could pull out the expression function and upper/lower bounds and use that subsequently in my AddConstraint calls.

In my real use-case, I need to apply the same constraint to multiple timesteps in a trajectory, so I think it would be helpful to create the symbolic function once when I call AddConstraint for the first timestep and then all subsequent timesteps shouldn't have to re-create the symbolic function, I can just use the cached version and apply it to the relevant variables. The expression involves a Cholesky decomposition of a covariance matrix so there are a lot of constraints being applied.

Any help is greatly appreciated.

Upvotes: 4

Views: 149

Answers (1)

Hongkai Dai
Hongkai Dai

Reputation: 2766

In my real use-case, I need to apply the same constraint to multiple timesteps in a trajectory, so I think it would be helpful to create the symbolic function once when I call AddConstraint for the first timestep and then all subsequent timesteps shouldn't have to re-create the symbolic function, I can just use the cached version and apply it to the relevant variables. The expression involves a Cholesky decomposition of a covariance matrix so there are a lot of constraints being applied.

I would strongly encourage to write this constraint using a function evaluation, instead of a symbolic expression. One example is that if you want to impose the constraint lb <= my_evaluator(x) <= ub, then you can call it this way

def my_evaluator(x):
    # Do Cholesky decomposition and other things to evaluate it. Return the evaluation result.
    return result

prog.AddConstraint(my_evaluator, lb, ub, x)

Adding constraint using symbolic expression is convenient when your constraint is linear in the decision variables, otherwise it is better to avoid using symbolic expression to add constraint. (Evaluating a symbolic expression, especially one involving Cholesky decomposition, is really time consuming).

For more details on adding generic nonlinear constraint, you could refer to our tutorial

Upvotes: 1

Related Questions