forked from google-deepmind/dm_control
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbase.py
More file actions
279 lines (227 loc) · 9.75 KB
/
base.py
File metadata and controls
279 lines (227 loc) · 9.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# Copyright 2018 The dm_control Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Base class for all MJCF elements in the object model."""
import abc
from dm_control.mjcf import constants
class Element(metaclass=abc.ABCMeta):
"""Abstract base class for an MJCF element.
This class is provided so that `isinstance(foo, Element)` is `True` for all
Element-like objects. We do not implement the actual element here because
the actual object returned from traversing the object hierarchy is a
weakproxy-like proxy to an actual element. This is because we do not allow
orphaned non-root elements, so when a particular element is removed from the
tree, all references held automatically become invalid.
"""
__slots__ = []
@abc.abstractmethod
def get_init_stack(self):
"""Gets the stack trace where this element was first initialized."""
@abc.abstractmethod
def get_last_modified_stacks_for_all_attributes(self):
"""Gets a dict of stack traces where each attribute was last modified."""
@abc.abstractmethod
def is_same_as(self, other):
"""Checks whether another element is semantically equivalent to this one.
Two elements are considered equivalent if they have the same
specification (i.e. same tag appearing in the same context), the same
attribute values, and all of their children are equivalent. The ordering
of non-repeated children is not important for this comparison, while
the ordering of repeated children are important only amongst the same
type* of children. In other words, for two bodies to be considered
equivalent, their child sites must appear in the same order, and their
child geoms must appear in the same order, but permutations between sites
and geoms are disregarded. (The only exception is in tendon definition,
where strict ordering of all children is necessary for equivalence.)
*Note that the notion of "same type" in this function is very loose:
for example different actuator element subtypes are treated as separate
types when children ordering is considered. Therefore, two <actuator>
elements might be considered equivalent even though they result in different
orderings of `mjData.ctrl` when compiled. As it stands, this function
is designed primarily as a testing aid and should not be used to guarantee
that models are actually identical.
Args:
other: An `mjcf.Element`
Returns:
`True` if `other` element is semantically equivalent to this one.
"""
@property
@abc.abstractmethod
def tag(self):
pass
@property
@abc.abstractmethod
def spec(self):
pass
@property
@abc.abstractmethod
def parent(self):
pass
@property
@abc.abstractmethod
def namescope(self):
pass
@property
@abc.abstractmethod
def root(self):
pass
@abc.abstractmethod
def prefixed_identifier(self, prefix_root):
pass
@property
@abc.abstractmethod
def full_identifier(self):
"""Fully-qualified identifier used for this element in the generated XML."""
@abc.abstractmethod
def find(self, namespace, identifier):
"""Finds an element with a particular identifier.
This function allows the direct access to an arbitrarily deeply nested
child element by name, without the need to manually traverse through the
object tree. The `namespace` argument specifies the kind of element to
find. In most cases, this corresponds to the element's XML tag name.
However, if an element has multiple specialized tags, then the namespace
corresponds to the tag name of the most general element of that kind.
For example, `namespace='joint'` would search for `<joint>` and
`<freejoint>`, while `namespace='actuator'` would search for `<general>`,
`<motor>`, `<position>`, `<velocity>`, and `<cylinder>`.
Args:
namespace: A string specifying the namespace being searched. See the
docstring above for explanation.
identifier: The identifier string of the desired element.
Returns:
An `mjcf.Element` object, or `None` if an element with the specified
identifier is not found.
Raises:
ValueError: if either `namespace` or `identifier` is not a string, or if
`namespace` is not a valid namespace.
"""
@abc.abstractmethod
def find_all(self, namespace,
immediate_children_only=False, exclude_attachments=False):
"""Finds all elements of a particular kind.
The `namespace` argument specifies the kind of element to
find. In most cases, this corresponds to the element's XML tag name.
However, if an element has multiple specialized tags, then the namespace
corresponds to the tag name of the most general element of that kind.
For example, `namespace='joint'` would search for `<joint>` and
`<freejoint>`, while `namespace='actuator'` would search for `<general>`,
`<motor>`, `<position>`, `<velocity>`, and `<cylinder>`.
Args:
namespace: A string specifying the namespace being searched. See the
docstring above for explanation.
immediate_children_only: (optional) A boolean, if `True` then only
the immediate children of this element are returned.
exclude_attachments: (optional) A boolean, if `True` then elements
belonging to attached models are excluded.
Returns:
A list of `mjcf.Element`.
Raises:
ValueError: if `namespace` is not a valid namespace.
"""
@abc.abstractmethod
def enter_scope(self, scope_identifier):
"""Finds the root element of the given scope and returns it.
This function allows the access to a nested scope that is a child of this
element. The `scope_identifier` argument specifies the path to the child
scope element.
Args:
scope_identifier: The path of the desired scope element.
Returns:
An `mjcf.Element` object, or `None` if a scope element with the
specified path is not found.
"""
@abc.abstractmethod
def get_attribute_xml_string(self, attribute_name, prefix_root=None):
pass
@abc.abstractmethod
def get_attributes(self):
pass
@abc.abstractmethod
def set_attributes(self, **kwargs):
pass
@abc.abstractmethod
def get_children(self, element_name):
pass
@abc.abstractmethod
def add(self, element_name, **kwargs):
"""Add a new child element to this element.
Args:
element_name: The tag of the element to add.
**kwargs: Attributes of the new element being created.
Raises:
ValueError: If the 'element_name' is not a valid child, or if an invalid
attribute is specified in `kwargs`.
Returns:
An `mjcf.Element` corresponding to the newly created child element.
"""
@abc.abstractmethod
def remove(self, affect_attachments=False):
"""Removes this element from the model."""
@property
@abc.abstractmethod
def is_removed(self):
pass
@abc.abstractmethod
def all_children(self):
pass
@abc.abstractmethod
def to_xml(self, prefix_root=None, debug_context=None,
*,
precision=constants.XML_DEFAULT_PRECISION,
zero_threshold=0):
"""Generates an etree._Element corresponding to this MJCF element.
Args:
prefix_root: (optional) A `NameScope` object to be treated as root
for the purpose of calculating the prefix.
If `None` then no prefix is included.
debug_context: (optional) A `debugging.DebugContext` object to which
the debugging information associated with the generated XML is written.
This is intended for internal use within PyMJCF; users should never need
manually pass this argument.
precision: (optional) Number of digits to output for floating point
quantities.
zero_threshold: (optional) When outputting XML, floating point quantities
whose absolute value falls below this threshold will be treated as zero.
Returns:
An etree._Element object.
"""
@abc.abstractmethod
def to_xml_string(self, prefix_root=None,
self_only=False, pretty_print=True, debug_context=None,
*,
precision=constants.XML_DEFAULT_PRECISION,
zero_threshold=0):
"""Generates an XML string corresponding to this MJCF element.
Args:
prefix_root: (optional) A `NameScope` object to be treated as root
for the purpose of calculating the prefix.
If `None` then no prefix is included.
self_only: (optional) A boolean, whether to generate an XML corresponding
only to this element without any children.
pretty_print: (optional) A boolean, whether to the XML string should be
properly indented.
debug_context: (optional) A `debugging.DebugContext` object to which
the debugging information associated with the generated XML is written.
This is intended for internal use within PyMJCF; users should never need
manually pass this argument.
precision: (optional) Number of digits to output for floating point
quantities.
zero_threshold: (optional) When outputting XML, floating point quantities
whose absolute value falls below this threshold will be treated as zero.
Returns:
A string.
"""
@abc.abstractmethod
def resolve_references(self):
pass