RobotLocomotion / drake

Model-based design and verification for robotics.
https://drake.mit.edu
Other
3.2k stars 1.25k forks source link

Revamp Context values API #15986

Open sherm1 opened 2 years ago

sherm1 commented 2 years ago

Background

The current API for setting & getting values from the Context has several problems in practice as discussed in issue #11346. Among those:

Proposal summary

Main features:

Proposal

Pseudocode for brevity: brackets for optional parameter; left off template declaration for V and Overlay † means method already exists (for plain VectorX).

Setter Getter
SetContinuousState(_vectorspec) VectorX GetContinuousState() const // copies
SetContinuousStateAt(int index,_vectorspec) const T& GetContinousStateAt(int index) const
SetContinuousStateAs<Overlay>(const Overlay&) const Overlay& GetContinuousStateAs<Overlay>() const
---
SetDiscreteState([which,]_vectorspec) const VectorX& GetDiscreteState([which]) const
SetDiscreteStateAt([which,] int index,_vectorspec) const T& GetDiscreteStateAt([which,] int index) const
SetDiscreteStateAs<Overlay>([which,] const Overlay&) const Overlay& GetDiscreteStateAs<Overlay>([which]) const
---
SetAbstractState<V>([which,] const V&) const V& GetAbstractState<V>([which]) const
---
SetNumericParameter([which,]_vectorspec) const VectorX& GetNumericParameter([which]) const
SetAbstractParameter<V>([which,] const V&) const V& GetAbstractParameter<V>([which]) const
---
SetTime(const T&) const T& time() const
SetAccuracy(optional<double>) const optional<double>& accuracy() const
Accessor (const) Return Type
time() const T&
accuracy() const optional<double>&
state() const State<T>&
parameters() const Parameters<T>&

There are more details but I want to stop there to solicit initial thoughts. cc @RussTedrake

sherm1 commented 2 years ago

Here is an alternative that would allow GetContinuousState() to return a reference rather than a copy. But is it too weird to live?

const VectorX<T>& GetContinuousState() const;

Nice signature, but here's how it would have to work:

In many cases this would silently produce the optimal behavior but the weird thing is that the referenced state would be live in some cases and a dead copy in others. OTOH the proposal in the issue description above is similar in that the continuous state comes back as a copy while the rest return live references.

Some weirdness is unavoidable because of our attempt to hide that a Diagram's continuous state comprises multiple independent VectorX's. For discrete state we didn't attempt that illusion and consequently we can always return references to the live state as a VectorX. (The proposed reengineering of state in #9171 treats continuous and discrete state similarly as collections of VectorX's.)

The current approach of returning a drake::VectorBase<T>& rather than an Eigen::VectorX<T>& avoids some of this weirdness in exchange for API awkwardness that @RussTedrake says is a pedagogical impediment.

Note: for advanced users we will still have access to the actual underlying types (State, ContinousState, VectorBase, etc.). The question here is how should the beginner's SetContinuousState()/GetContinuousState() pair behave?

sherm1 commented 2 years ago

(I'm working on a PR that will show how the call sites are changed by the new APIs.)

RussTedrake commented 2 years ago

The proposed API above looks great. I guess SetTime() and time() violate your principle of providing matching case setters and getters? Same for SetAccuracy() and accuracy?

RussTedrake commented 2 years ago

Re: VectorX<T> or const VectorX<T>&, I really do think there is value in having the simple version look simple. I don't have a strong preference between these, too. Keep in mind that the python versions are going to do the copy in either case, (if I understand correctly), since it will convert to the numpy array, and honestly it's the python API that will be used the most by the non-experts.