Dear Andres,
One cannot perform an expand operation on a graph that contains a Callback; the Callback stands for an otherworthly chunk of code. From CasADi's point of view its a blackbox thing, expanding into scalars is impossible for this.
You still had two issues in your code:
- get_jacobian is being called with more argument (check api docs)
- The function that get_jacobian returns must have just one output (the jacobian). The (deprecated) F.jacobian syntax gives an object with 2 outputs.
It is advised to work with expressions instead.
from casadi import *
class MyCallback(Callback):
def __init__(self, name, opts={}):
Callback.__init__(self)
# My plan is to implement this function externally.
# For the sake of the example, lets use a simple MX
X = MX.sym('X',1,1)
SQ = (X-5)**2
F = Function('Square',[X],[SQ],['x'],['y'])
self.F = F
self.X = X
self.SQ = SQ
self.construct(name, opts)
# Number of inputs and outputs
def get_n_in(self): return 1
def get_n_out(self): return 1
# Initialize the object
def init(self):
print('Initializing object')
def finalize(self):
print('Finalizing object construction')
def has_jacobian(self): return True
def get_jacobian(self,name,options):
# Will also provide first order jacobians from external code
return Function('name',
[self.X],
[jacobian(self.SQ,self.X)],options)
# Evaluate numerically
def eval(self, arg):
x = arg[0]
f = self.F(x)
print [x]
return [f]
f = MyCallback('f')
x = MX.sym('x',1,1)
y = f(x)
print y
# NLP
nlp = {'x':x, 'f':y}
# NLP solver options
opts = {}
opts["ipopt.max_iter"] = 1000
opts['ipopt.hessian_approximation'] = 'limited-memory'
# Allocate an NLP solver
solver = nlpsol("solver", "ipopt", nlp, opts)
arg = {}
# Initial condition
arg["x0"] = 10
# Bounds on x
arg["lbx"] = -100
arg["ubx"] = +100
# Solve the problem
res = solver(**arg)
# Print the optimal cost
print("optimal cost: ", float(res["f"]))
Best regards,
Joris