Closed MaxvdKolk closed 3 years ago
Imo g, dg, ddg is self-explanatory. However, it might be confusing as this is also used for the "sub"-problem optimizer. I would still op for g, dg, and ddg. Also, because you could also use the "sub"problem solvers to directly solve a simple Problem (if the problem is convex separable).
Wrt your note: myproblem(Problem) sounds perfect.
Note. we might want to store g in the Problem class for certain problems that require x (or any other property) to calculate the sensitivities. E.g. store displacements u in the problem class. Maybe make a child of Problem that has this functionality?
Agreed! Lets keep the following format then:
class Problem
def __init__(self)
def g(self, x)
def dg(self, x)
def ddg(self, x)
class Li2015Fig4(Problem)
class Rosenbrock(Problem)
I'll pull the new dev
branch, make a new branch dev-PROBLEM_STRUCTURE
, edit these things, push it, and make a pull request for that so we can close the issue after merging it into dev
.
@artofscience why do we need a separate child class for the problems that need certain attributes (e.g. prob.u
) to compute their sensitivities? Since this occurs only in certain problems, can we not simply add prob.u
to the problems that need it?
Maybe @artofscience means that if there is a specific pattern for certain problems, for example to store/cache the solution of the simulation, we could provide a class that does something like that. Such that a user can implement only one function, that automatically deals with storing the solution/sensitivity as attribute and then returns whatever is needed when called through g, dg, ddg
class CachedProblem(Problem):
def __init__(self, ...):
# cached response/sensitivity
self.u, self.du = (None, None)
def simulation(self, x):
# perform simulation
# this could also include a flag to indicate to simulation if it should
# or should not evaluate the sensitivity/backward solutions
self.u, self.du = solve_external_simulation(x)
def g(self, x):
# renew the simulation for `g`?
self.simulation(x)
return self.u
def dg(self, x):
# only renew simulation when sensitivity not present
# otherwise just return whatever is present in the cache
if not self.du:
self.simulation(x)
return self.du
Not sure if @artofscience hinted at this, but in this case we could make only simulation
an abstract interface where the user needs to provide its call to solve the response and/or sensitivity based on a set of design variables
For an external user it would be
from sao.problems import CachedProblem
class MyCalculixProblem(CachedProblem):
def simulation(self, x):
# wrapper code goes here
What is the exact purpose of the ddg
method, if no explicit second derivatives are calculated?
Does the abstract base-class provide default options for ddg
? BFGS-kind of approximations?
The option @MaxvdKolk (CachedProblem
) might be nice as a simple extension indeed, wrapping function-oriented code.
For the multiple-responses code, we might use something like
class MultiProblem(Problem):
def __init__(self):
self.problem[0] = Objective()
self.problem[1] = Constraint()
...
def g(self, x):
u[:,0] = self.problem[0].g(x)
u[:,1] = self.problem[1].g(x)
...
return u
@aatmdelissen There is no reason for ddg
if it is not implemented, that's why it is not decorated with @abstractmethod
in the class Problem(ABC)
. At a later stage however, we could indeed opt to place some BFGS-like scheme as a default option, if a 2nd-order Taylor expansion is chosen.
This issue continues as an enhancement at #28
The discussions seems to converge towards the solution where we approach a single
Problem
class for this moment. This class then could hold various functions for the apiThis is similar to the other
Response
class that was discussed, and as @aatmdelissen mentioned, we could get away with using subclasses of eitherProblem
orResponse
rather then defining them both.Personally still doubt on function names:
response
,sensitivity
. These are a bit verbose, might be nice for reading the code, but it could be thatis sufficient in the context. The actual naming might require some further discussion.
Note: the current code in
dev
mostly defines each problem simply as a class, e.g.class Li2015Fig4
as a base object. I would propose to still define the abstract interface for aProblem
(with the above API) and then make this subclassing more explicity, e.g.This helps documentation purposes, but also makes it clear for any user how to define problems using a Problem class.