Import google-deepmind/mujoco_warp from GitHub.

PiperOrigin-RevId: 929061622
Change-Id: I53892a819c3727b1f04db943839606ec1f477df1
This commit is contained in:
Taylor Howell
2026-06-09 02:27:41 -07:00
committed by Copybara-Service
parent 58a8c6ee56
commit 763e713d1b
8 changed files with 425 additions and 605 deletions
-2
View File
@@ -182,8 +182,6 @@ class ModelIOTest(parameterized.TestCase):
self.assertFalse(hasattr(mx, 'bvh_aabb')) self.assertFalse(hasattr(mx, 'bvh_aabb'))
elif impl == 'warp': elif impl == 'warp':
# Options specific to Warp are populated.
self.assertTrue(hasattr(mx.opt._impl, 'ls_parallel'))
# Fields private to Warp backend impl are populated. # Fields private to Warp backend impl are populated.
self.assertTrue(hasattr(mx._impl, 'nxn_geom_pair')) self.assertTrue(hasattr(mx._impl, 'nxn_geom_pair'))
elif impl == 'cpp': elif impl == 'cpp':
+31 -5
View File
@@ -257,9 +257,6 @@ def put_model(mjm: mujoco.MjModel) -> types.Model:
opt.tolerance = max(opt.tolerance, 1e-6) opt.tolerance = max(opt.tolerance, 1e-6)
# warp only fields # warp only fields
ls_parallel_id = mujoco.mj_name2id(mjm, mujoco.mjtObj.mjOBJ_NUMERIC, "ls_parallel")
opt.ls_parallel = (ls_parallel_id > -1) and (mjm.numeric_data[mjm.numeric_adr[ls_parallel_id]] == 1)
opt.ls_parallel_min_step = 1.0e-6 # TODO(team): determine good default setting
opt.broadphase = types.BroadphaseType.NXN opt.broadphase = types.BroadphaseType.NXN
opt.broadphase_filter = types.BroadphaseFilter.PLANE | types.BroadphaseFilter.SPHERE | types.BroadphaseFilter.OBB opt.broadphase_filter = types.BroadphaseFilter.PLANE | types.BroadphaseFilter.SPHERE | types.BroadphaseFilter.OBB
opt.graph_conditional = True opt.graph_conditional = True
@@ -301,6 +298,12 @@ def put_model(mjm: mujoco.MjModel) -> types.Model:
m.nmaxpyramid = np.maximum(1, 2 * (m.nmaxcondim - 1)) m.nmaxpyramid = np.maximum(1, 2 * (m.nmaxcondim - 1))
m.has_sdf_geom = (mjm.geom_type == mujoco.mjtGeom.mjGEOM_SDF).any() m.has_sdf_geom = (mjm.geom_type == mujoco.mjtGeom.mjGEOM_SDF).any()
m.block_dim = types.BlockDim() m.block_dim = types.BlockDim()
# Derive CG solver block_dim from nv: clamp(round_up_to_32(nv), 32, 256)
_nv_block = max(32, min(256, ((mjm.nv + 31) // 32) * 32))
m.block_dim.update_gradient_grad = _nv_block
m.block_dim.solve_beta_accumulate = _nv_block
m.block_dim.solve_search_update_cg = _nv_block
m.block_dim.solve_init_search_cg = _nv_block
if mjm.nv > 500: if mjm.nv > 500:
m.block_dim.linesearch_iterative = 512 m.block_dim.linesearch_iterative = 512
m.is_sparse = is_sparse(mjm) m.is_sparse = is_sparse(mjm)
@@ -308,6 +311,26 @@ def put_model(mjm: mujoco.MjModel) -> types.Model:
m.max_ten_J_rownnz = int(mjm.ten_J_rownnz.max()) if mjm.ntendon else 0 m.max_ten_J_rownnz = int(mjm.ten_J_rownnz.max()) if mjm.ntendon else 0
# Upper bound on a contact's Jacobian support, to size the elliptic-cone JTCJ launch (one
# thread per (contact, support-pair)). A contact's row spans the dof chains of weld(b1) and
# weld(b2) (see _efc_contact_jac_sparse in constraint.py); take the largest union over all
# geom-carrying bodies -- a safe superset, since over-estimating only adds skipped threads.
# A body's dof chain is exactly the sparsity of its deepest dof's row in the (ancestor-
# structured) mass matrix, so reuse MuJoCo's precomputed M_colind rather than re-walking.
def _dof_chain(body):
if mjm.body_dofnum[body] == 0:
return frozenset()
dof = int(mjm.body_dofadr[body] + mjm.body_dofnum[body] - 1)
adr = int(mjm.M_rowadr[dof])
return frozenset(int(mjm.M_colind[adr + k]) for k in range(int(mjm.M_rownnz[dof])))
chains = list({_dof_chain(int(mjm.body_weldid[b])) for b in mjm.geom_bodyid})
max_rownnz = 0
for i, chain_i in enumerate(chains):
for chain_j in chains[i:]:
max_rownnz = max(max_rownnz, len(chain_i | chain_j))
m.jtcj_max_pairs = max(max_rownnz * (max_rownnz + 1) // 2, 1)
# body ids grouped by tree level (depth-based traversal) # body ids grouped by tree level (depth-based traversal)
bodies, body_depth = {}, np.zeros(mjm.nbody, dtype=int) - 1 bodies, body_depth = {}, np.zeros(mjm.nbody, dtype=int) - 1
for i in range(mjm.nbody): for i in range(mjm.nbody):
@@ -2841,7 +2864,6 @@ def override_model(model: types.Model | mujoco.MjModel, overrides: dict[str, Any
Overrides are of the format: Overrides are of the format:
opt.iterations = 1 opt.iterations = 1
opt.ls_parallel = True
opt.cone = pyramidal opt.cone = pyramidal
opt.disableflags = contact | spring opt.disableflags = contact | spring
""" """
@@ -2865,7 +2887,6 @@ def override_model(model: types.Model | mujoco.MjModel, overrides: dict[str, Any
mjw_only_fields = { mjw_only_fields = {
"opt.broadphase", "opt.broadphase",
"opt.broadphase_filter", "opt.broadphase_filter",
"opt.ls_parallel",
"opt.graph_conditional", "opt.graph_conditional",
"opt.contact_sensor_maxmatch", "opt.contact_sensor_maxmatch",
} }
@@ -2881,6 +2902,11 @@ def override_model(model: types.Model | mujoco.MjModel, overrides: dict[str, Any
overrides = overrides_dict overrides = overrides_dict
for key, val in overrides.items(): for key, val in overrides.items():
if key == "opt.ls_parallel":
raise ValueError("ls_parallel was removed in MuJoCo Warp 3.9.1.")
if key == "opt.ls_parallel_min_step":
raise ValueError("ls_parallel_min_step was removed in MuJoCo Warp 3.9.1.")
# skip overrides on MjModel for properties that are only on mjw.Model # skip overrides on MjModel for properties that are only on mjw.Model
if key in mjw_only_fields and isinstance(model, mujoco.MjModel): if key in mjw_only_fields and isinstance(model, mujoco.MjModel):
continue continue
+14 -6
View File
@@ -630,6 +630,12 @@ def _flex_elasticity(
f = i f = i
break break
stiffness_adr_base = flex_stiffnessadr[f]
if stiffness_adr_base < 0:
return
if flex_stiffness[stiffness_adr_base] == 0.0:
return
local_elemid = elemid - flex_elemadr[f] local_elemid = elemid - flex_elemadr[f]
dim = flex_dim[f] dim = flex_dim[f]
nvert = dim + 1 nvert = dim + 1
@@ -671,7 +677,7 @@ def _flex_elasticity(
metric = wp.matrix(0.0, shape=(6, 6)) metric = wp.matrix(0.0, shape=(6, 6))
stiffness_size = nedge * (nedge + 1) / 2 stiffness_size = nedge * (nedge + 1) / 2
stiffness_adr = flex_stiffnessadr[f] + local_elemid * stiffness_size stiffness_adr = stiffness_adr_base + local_elemid * stiffness_size
id = int(0) id = int(0)
for ed1 in range(nedge): for ed1 in range(nedge):
for ed2 in range(ed1, nedge): for ed2 in range(ed1, nedge):
@@ -721,6 +727,10 @@ def _flex_bending(
f = i f = i
break break
bendingadr = flex_bendingadr[f]
if bendingadr < 0:
return
if flex_dim[f] != 2: if flex_dim[f] != 2:
return return
@@ -734,10 +744,8 @@ def _flex_bending(
flex_vertadr[f] + flex_edgeflap[edgeid][1], flex_vertadr[f] + flex_edgeflap[edgeid][1],
) )
adr = flex_bendingadr[f]
frc = wp.matrix(0.0, shape=(4, 3)) frc = wp.matrix(0.0, shape=(4, 3))
if flex_bending[adr + 16]: if flex_bending[bendingadr + 16]:
v0 = flexvert_xpos_in[worldid, v[0]] v0 = flexvert_xpos_in[worldid, v[0]]
v1 = flexvert_xpos_in[worldid, v[1]] v1 = flexvert_xpos_in[worldid, v[1]]
v2 = flexvert_xpos_in[worldid, v[2]] v2 = flexvert_xpos_in[worldid, v[2]]
@@ -752,8 +760,8 @@ def _flex_bending(
for x in range(3): for x in range(3):
acc = float(0.0) acc = float(0.0)
for j in range(nvert): for j in range(nvert):
acc += flex_bending[adr + 4 * i + j] * flexvert_xpos_in[worldid, v[j]][x] acc += flex_bending[bendingadr + 4 * i + j] * flexvert_xpos_in[worldid, v[j]][x]
force[i, x] = -(acc + flex_bending[adr + 16] * frc[i, x]) force[i, x] = -(acc + flex_bending[bendingadr + 16] * frc[i, x])
for i in range(nvert): for i in range(nvert):
bodyid = flex_vertbodyid[v[i]] bodyid = flex_vertbodyid[v[i]]
+279 -498
View File
@@ -130,7 +130,6 @@ def _create_solver_context(m: types.Model, d: types.Data) -> SolverContext:
mv=wp.empty((nworld, nv), dtype=float), mv=wp.empty((nworld, nv), dtype=float),
jv=wp.empty((nworld, njmax), dtype=float), jv=wp.empty((nworld, njmax), dtype=float),
quad=wp.empty((nworld, njmax), dtype=wp.vec3), quad=wp.empty((nworld, njmax), dtype=wp.vec3),
quad_gauss=wp.empty((nworld,), dtype=wp.vec3),
alpha=wp.empty((nworld,), dtype=float), alpha=wp.empty((nworld,), dtype=float),
improvement=wp.empty((nworld,), dtype=float), improvement=wp.empty((nworld,), dtype=float),
prev_grad=wp.empty((nworld, nv), dtype=float), prev_grad=wp.empty((nworld, nv), dtype=float),
@@ -459,227 +458,6 @@ def _eval_constraint(
return wp.vec3(-D * jaref, float(types.ConstraintState.QUADRATIC.value), 0.5 * D * jaref * jaref) return wp.vec3(-D * jaref, float(types.ConstraintState.QUADRATIC.value), 0.5 * D * jaref * jaref)
@wp.func
def _log_scale(min_value: float, max_value: float, num_values: int, i: int) -> float:
step = (wp.log(max_value) - wp.log(min_value)) / wp.max(1.0, float(num_values - 1))
return wp.exp(wp.log(min_value) + float(i) * step)
@wp.kernel
def _linesearch_parallel_fused(
# Model:
opt_ls_iterations: int,
opt_impratio_invsqrt: wp.array[float],
opt_ls_parallel_min_step: float,
# Data in:
ne_in: wp.array[int],
nf_in: wp.array[int],
nefc_in: wp.array[int],
contact_friction_in: wp.array[types.vec5],
contact_efc_address_in: wp.array2d[int],
efc_type_in: wp.array2d[int],
efc_id_in: wp.array2d[int],
efc_D_in: wp.array2d[float],
efc_frictionloss_in: wp.array2d[float],
njmax_in: int,
nacon_in: wp.array[int],
# In:
ctx_Jaref_in: wp.array2d[float],
ctx_jv_in: wp.array2d[float],
ctx_quad_in: wp.array2d[wp.vec3],
ctx_quad_gauss_in: wp.array[wp.vec3],
ctx_done_in: wp.array[bool],
# Out:
cost_out: wp.array2d[float],
):
worldid, alphaid = wp.tid()
if ctx_done_in[worldid]:
return
alpha = _log_scale(opt_ls_parallel_min_step, 1.0, opt_ls_iterations, alphaid)
quad_gauss = ctx_quad_gauss_in[worldid]
out = alpha * alpha * quad_gauss[2] + alpha * quad_gauss[1]
ne = ne_in[worldid]
nf = nf_in[worldid]
for efcid in range(min(njmax_in, nefc_in[worldid])):
# equality
if efcid < ne:
quad = ctx_quad_in[worldid, efcid]
out += alpha * alpha * quad[2] + alpha * quad[1]
# friction
elif efcid < ne + nf:
# search point, friction loss, bound (rf)
start = ctx_Jaref_in[worldid, efcid]
dir = ctx_jv_in[worldid, efcid]
x = start + alpha * dir
f = efc_frictionloss_in[worldid, efcid]
efc_D = efc_D_in[worldid, efcid]
rf = math.safe_div(f, efc_D)
out += _eval_frictionloss_cost(x, f, rf, efc_D) - _eval_frictionloss_cost(start, f, rf, efc_D)
# limit and contact
elif efc_type_in[worldid, efcid] == types.ConstraintType.CONTACT_ELLIPTIC:
# extract contact info
conid = efc_id_in[worldid, efcid]
if conid >= nacon_in[0]:
continue
efcid0 = contact_efc_address_in[conid, 0]
if efcid != efcid0:
continue
# unpack quad
efcid1 = contact_efc_address_in[conid, 1]
efcid2 = contact_efc_address_in[conid, 2]
impratio_invsqrt = opt_impratio_invsqrt[worldid % opt_impratio_invsqrt.shape[0]]
friction = contact_friction_in[conid]
quad = ctx_quad_in[worldid, efcid]
quad1 = ctx_quad_in[worldid, efcid1]
quad2 = ctx_quad_in[worldid, efcid2]
mu = friction[0] * impratio_invsqrt
out += _eval_elliptic_cost(mu, quad, quad1, quad2, alpha)
out -= _eval_elliptic_cost(mu, quad, quad1, quad2, 0.0)
else:
# search point
start = ctx_Jaref_in[worldid, efcid]
x = start + alpha * ctx_jv_in[worldid, efcid]
cost0 = wp.where(start < 0.0, ctx_quad_in[worldid, efcid][0], 0.0)
# active
if x < 0.0:
out += _eval_cost(ctx_quad_in[worldid, efcid], alpha) - cost0
else:
out -= cost0
cost_out[worldid, alphaid] = out
@wp.kernel
def _linesearch_parallel_best_alpha(
# Model:
opt_ls_iterations: int,
opt_ls_parallel_min_step: float,
# In:
ctx_done_in: wp.array[bool],
cost_in: wp.array2d[float],
# Out:
ctx_alpha_out: wp.array[float],
ctx_improvement_out: wp.array[float],
):
worldid = wp.tid()
if ctx_done_in[worldid]:
return
bestid = int(0)
best_cost = float(0.0)
improved = bool(False)
for i in range(opt_ls_iterations):
cost = cost_in[worldid, i]
if cost < best_cost:
best_cost = cost
bestid = i
improved = True
ctx_alpha_out[worldid] = wp.where(improved, _log_scale(opt_ls_parallel_min_step, 1.0, opt_ls_iterations, bestid), 0.0)
ctx_improvement_out[worldid] = wp.where(improved, -best_cost, 0.0)
def _linesearch_parallel(m: types.Model, d: types.Data, ctx: SolverContext, cost: wp.array2d[float]):
"""Parallel linesearch with setup and teardown kernels."""
dofs_per_thread = 20 if m.nv > 50 else 50
threads_per_efc = ceil(m.nv / dofs_per_thread)
# quad_gauss = [0, search.T @ Ma - search.T @ qfrc_smooth, 0.5 * search.T @ mv]
if threads_per_efc > 1:
ctx.quad_gauss.zero_()
wp.launch(
_linesearch_prepare_gauss(m.nv, dofs_per_thread),
dim=(d.nworld, threads_per_efc),
inputs=[d.qfrc_smooth, d.efc.Ma, ctx.search, ctx.mv, ctx.done],
outputs=[ctx.quad_gauss],
)
# quad = [0.5 * Jaref * Jaref * efc_D, jv * Jaref * efc_D, 0.5 * jv * jv * efc_D]
wp.launch(
_linesearch_prepare_quad,
dim=(d.nworld, d.njmax),
inputs=[
m.opt.impratio_invsqrt,
d.nefc,
d.contact.friction,
d.contact.dim,
d.contact.efc_address,
d.efc.type,
d.efc.id,
d.efc.D,
d.nacon,
ctx.Jaref,
ctx.jv,
ctx.done,
],
outputs=[ctx.quad],
)
wp.launch(
_linesearch_parallel_fused,
dim=(d.nworld, m.opt.ls_iterations),
inputs=[
m.opt.ls_iterations,
m.opt.impratio_invsqrt,
m.opt.ls_parallel_min_step,
d.ne,
d.nf,
d.nefc,
d.contact.friction,
d.contact.efc_address,
d.efc.type,
d.efc.id,
d.efc.D,
d.efc.frictionloss,
d.njmax,
d.nacon,
ctx.Jaref,
ctx.jv,
ctx.quad,
ctx.quad_gauss,
ctx.done,
],
outputs=[cost],
)
wp.launch(
_linesearch_parallel_best_alpha,
dim=d.nworld,
inputs=[m.opt.ls_iterations, m.opt.ls_parallel_min_step, ctx.done, cost],
outputs=[ctx.alpha, ctx.improvement],
)
# Teardown: update qacc, Ma, Jaref
wp.launch(
_linesearch_qacc_ma,
dim=(d.nworld, m.nv),
inputs=[ctx.search, ctx.mv, ctx.alpha, ctx.done],
outputs=[d.qacc, d.efc.Ma],
)
wp.launch(
_linesearch_jaref,
dim=(d.nworld, d.njmax),
inputs=[d.nefc, ctx.jv, ctx.alpha, ctx.done],
outputs=[ctx.Jaref],
)
# kernel_analyzer: off # kernel_analyzer: off
@wp.func @wp.func
def _compute_efc_eval_pt_pyramidal( def _compute_efc_eval_pt_pyramidal(
@@ -1628,205 +1406,21 @@ def _linesearch_jv_fused_kernel(is_sparse: bool, nv: int, dofs_per_thread: int):
return kernel return kernel
@cache_kernel
def _linesearch_prepare_gauss(nv: int, dofs_per_thread: int):
@wp.kernel(module="unique", enable_backward=False)
def kernel(
# Data in:
qfrc_smooth_in: wp.array2d[float],
efc_Ma_in: wp.array2d[float],
# In:
ctx_search_in: wp.array2d[float],
ctx_mv_in: wp.array2d[float],
ctx_done_in: wp.array[bool],
# Out:
ctx_quad_gauss_out: wp.array[wp.vec3],
):
worldid, dofstart = wp.tid()
if ctx_done_in[worldid]:
return
quad_gauss_1 = float(0.0)
quad_gauss_2 = float(0.0)
if wp.static(dofs_per_thread >= nv):
for i in range(wp.static(nv)):
search = ctx_search_in[worldid, i]
quad_gauss_1 += search * (efc_Ma_in[worldid, i] - qfrc_smooth_in[worldid, i])
quad_gauss_2 += 0.5 * search * ctx_mv_in[worldid, i]
ctx_quad_gauss_out[worldid] = wp.vec3(0.0, quad_gauss_1, quad_gauss_2)
else:
for i in range(wp.static(dofs_per_thread)):
ii = dofstart * wp.static(dofs_per_thread) + i
if ii < nv:
search = ctx_search_in[worldid, ii]
quad_gauss_1 += search * (efc_Ma_in[worldid, ii] - qfrc_smooth_in[worldid, ii])
quad_gauss_2 += 0.5 * search * ctx_mv_in[worldid, ii]
wp.atomic_add(ctx_quad_gauss_out, worldid, wp.vec3(0.0, quad_gauss_1, quad_gauss_2))
return kernel
@wp.kernel
def _linesearch_prepare_quad(
# Model:
opt_impratio_invsqrt: wp.array[float],
# Data in:
nefc_in: wp.array[int],
contact_friction_in: wp.array[types.vec5],
contact_dim_in: wp.array[int],
contact_efc_address_in: wp.array2d[int],
efc_type_in: wp.array2d[int],
efc_id_in: wp.array2d[int],
efc_D_in: wp.array2d[float],
nacon_in: wp.array[int],
# In:
ctx_Jaref_in: wp.array2d[float],
ctx_jv_in: wp.array2d[float],
ctx_done_in: wp.array[bool],
# Out:
ctx_quad_out: wp.array2d[wp.vec3],
):
worldid, efcid = wp.tid()
if efcid >= nefc_in[worldid]:
return
if ctx_done_in[worldid]:
return
Jaref = ctx_Jaref_in[worldid, efcid]
jv = ctx_jv_in[worldid, efcid]
efc_D = efc_D_in[worldid, efcid]
# init with scalar quadratic
quad = wp.vec3(0.5 * Jaref * Jaref * efc_D, jv * Jaref * efc_D, 0.5 * jv * jv * efc_D)
# elliptic cone: extra processing
if efc_type_in[worldid, efcid] == types.ConstraintType.CONTACT_ELLIPTIC:
# extract contact info
conid = efc_id_in[worldid, efcid]
if conid >= nacon_in[0]:
return
efcid0 = contact_efc_address_in[conid, 0]
if efcid != efcid0:
return
dim = contact_dim_in[conid]
friction = contact_friction_in[conid]
mu = friction[0] * opt_impratio_invsqrt[worldid % opt_impratio_invsqrt.shape[0]]
u0 = Jaref * mu
v0 = jv * mu
uu = float(0.0)
uv = float(0.0)
vv = float(0.0)
for j in range(1, dim):
# complete vector quadratic (for bottom zone)
efcidj = contact_efc_address_in[conid, j]
if efcidj < 0:
return
jvj = ctx_jv_in[worldid, efcidj]
jarefj = ctx_Jaref_in[worldid, efcidj]
dj = efc_D_in[worldid, efcidj]
DJj = dj * jarefj
quad += wp.vec3(
0.5 * jarefj * DJj,
jvj * DJj,
0.5 * jvj * dj * jvj,
)
# rescale to make primal cone circular
frictionj = friction[j - 1]
uj = jarefj * frictionj
vj = jvj * frictionj
# accumulate sums of squares
uu += uj * uj
uv += uj * vj
vv += vj * vj
quad1 = wp.vec3(u0, v0, uu)
efcid1 = contact_efc_address_in[conid, 1]
ctx_quad_out[worldid, efcid1] = quad1
mu2 = mu * mu
quad2 = wp.vec3(uv, vv, efc_D / (mu2 * (1.0 + mu2)))
efcid2 = contact_efc_address_in[conid, 2]
ctx_quad_out[worldid, efcid2] = quad2
ctx_quad_out[worldid, efcid] = quad
@wp.kernel
def _linesearch_qacc_ma(
# In:
ctx_search_in: wp.array2d[float],
ctx_mv_in: wp.array2d[float],
ctx_alpha_in: wp.array[float],
ctx_done_in: wp.array[bool],
# Data out:
qacc_out: wp.array2d[float],
efc_Ma_out: wp.array2d[float],
):
worldid, dofid = wp.tid()
if ctx_done_in[worldid]:
return
alpha = ctx_alpha_in[worldid]
qacc_out[worldid, dofid] += alpha * ctx_search_in[worldid, dofid]
efc_Ma_out[worldid, dofid] += alpha * ctx_mv_in[worldid, dofid]
@wp.kernel
def _linesearch_jaref(
# Data in:
nefc_in: wp.array[int],
# In:
ctx_jv_in: wp.array2d[float],
ctx_alpha_in: wp.array[float],
ctx_done_in: wp.array[bool],
# Out:
ctx_Jaref_out: wp.array2d[float],
):
worldid, efcid = wp.tid()
if efcid >= nefc_in[worldid]:
return
if ctx_done_in[worldid]:
return
ctx_Jaref_out[worldid, efcid] += ctx_alpha_in[worldid] * ctx_jv_in[worldid, efcid]
@event_scope @event_scope
def _linesearch(m: types.Model, d: types.Data, ctx: SolverContext, cost: wp.array2d[float]): def _linesearch(m: types.Model, d: types.Data, ctx: SolverContext):
"""Linesearch for constraint solver. """Linesearch for constraint solver.
Args: Args:
m: Model m: Model
d: Data d: Data
ctx: SolverContext ctx: SolverContext
cost: Scratch array for storing costs per (world, alpha) - used for parallel mode
""" """
# mv = M @ search (common to both parallel and iterative) # mv = M @ search (common to both parallel and iterative)
support.mul_m(m, d, ctx.mv, ctx.search, skip=ctx.done) support.mul_m(m, d, ctx.mv, ctx.search, skip=ctx.done)
# Fuse jv computation in-kernel for small nv (iterative only, dense only) # Fuse jv computation in-kernel for small nv (iterative only, dense only)
# Parallel linesearch always requires jv pre-computed
# Sparse mode requires pre-computed jv since in-kernel uses dense indexing # Sparse mode requires pre-computed jv since in-kernel uses dense indexing
fuse_jv = m.nv <= 50 and not m.opt.ls_parallel and not m.is_sparse fuse_jv = m.nv <= 50 and not m.is_sparse
# jv = J @ search (when not fused into iterative kernel) # jv = J @ search (when not fused into iterative kernel)
if not fuse_jv: if not fuse_jv:
@@ -1848,9 +1442,6 @@ def _linesearch(m: types.Model, d: types.Data, ctx: SolverContext, cost: wp.arra
outputs=[ctx.jv], outputs=[ctx.jv],
) )
if m.opt.ls_parallel:
_linesearch_parallel(m, d, ctx, cost)
else:
_linesearch_iterative(m, d, ctx, fuse_jv) _linesearch_iterative(m, d, ctx, fuse_jv)
@@ -1931,6 +1522,40 @@ def _solve_init_search(
wp.atomic_add(ctx_search_dot_out, worldid, search * search) wp.atomic_add(ctx_search_dot_out, worldid, search * search)
@wp.kernel
def _solve_init_search_cg_tiled(
# Model:
nv: int,
# In:
ctx_grad_in: wp.array2d[float],
ctx_Mgrad_in: wp.array2d[float],
# Out:
ctx_search_out: wp.array2d[float],
ctx_search_dot_out: wp.array[float],
ctx_prev_grad_out: wp.array2d[float],
ctx_prev_Mgrad_out: wp.array2d[float],
):
worldid, tid = wp.tid()
local_search_dot = float(0.0)
BLOCK_DIM = wp.block_dim()
for dofid in range(tid, nv, BLOCK_DIM):
mgrad = ctx_Mgrad_in[worldid, dofid]
search = -1.0 * mgrad
ctx_search_out[worldid, dofid] = search
local_search_dot += search * search
ctx_prev_grad_out[worldid, dofid] = ctx_grad_in[worldid, dofid]
ctx_prev_Mgrad_out[worldid, dofid] = mgrad
search_dot_tile = wp.tile(local_search_dot, preserve_type=True)
search_dot_sum = wp.tile_reduce(wp.add, search_dot_tile)
if tid == 0:
ctx_search_dot_out[worldid] = search_dot_sum[0]
@cache_kernel @cache_kernel
def _update_constraint_efc(track_changes: bool): def _update_constraint_efc(track_changes: bool):
TRACK_CHANGES = track_changes TRACK_CHANGES = track_changes
@@ -2285,6 +1910,40 @@ def _update_gradient_grad(
wp.atomic_add(ctx_grad_dot_out, worldid, grad * grad) wp.atomic_add(ctx_grad_dot_out, worldid, grad * grad)
@wp.kernel
def _update_gradient_grad_tiled(
# Model:
nv: int,
# Data in:
qfrc_smooth_in: wp.array2d[float],
qfrc_constraint_in: wp.array2d[float],
efc_Ma_in: wp.array2d[float],
# In:
ctx_done_in: wp.array[bool],
# Out:
ctx_grad_out: wp.array2d[float],
ctx_grad_dot_out: wp.array[float],
):
worldid, tid = wp.tid()
if ctx_done_in[worldid]:
return
local_grad_dot = float(0.0)
BLOCK_DIM = wp.block_dim()
for dofid in range(tid, nv, BLOCK_DIM):
grad = efc_Ma_in[worldid, dofid] - qfrc_smooth_in[worldid, dofid] - qfrc_constraint_in[worldid, dofid]
ctx_grad_out[worldid, dofid] = grad
local_grad_dot += grad * grad
grad_dot_tile = wp.tile(local_grad_dot, preserve_type=True)
grad_dot_sum = wp.tile_reduce(wp.add, grad_dot_tile)
if tid == 0:
ctx_grad_dot_out[worldid] = grad_dot_sum[0]
@wp.kernel @wp.kernel
def _update_gradient_init_h_sparse( def _update_gradient_init_h_sparse(
# Model: # Model:
@@ -2400,8 +2059,6 @@ def _update_gradient_JTDAJ_dense_tiled(nv_pad: int, tile_size: int, njmax: int):
def _update_gradient_JTCJ_sparse( def _update_gradient_JTCJ_sparse(
# Model: # Model:
opt_impratio_invsqrt: wp.array[float], opt_impratio_invsqrt: wp.array[float],
dof_tri_row: wp.array[int],
dof_tri_col: wp.array[int],
# Data in: # Data in:
contact_dist_in: wp.array[float], contact_dist_in: wp.array[float],
contact_includemargin_in: wp.array[float], contact_includemargin_in: wp.array[float],
@@ -2425,10 +2082,7 @@ def _update_gradient_JTCJ_sparse(
# Out: # Out:
ctx_h_out: wp.array3d[float], ctx_h_out: wp.array3d[float],
): ):
conid_start, elementid = wp.tid() conid_start, pairid = wp.tid()
dof1id = dof_tri_row[elementid]
dof2id = dof_tri_col[elementid]
for i in range(nblocks_perblock): for i in range(nblocks_perblock):
conid = conid_start + i * dim_block conid = conid_start + i * dim_block
@@ -2453,22 +2107,24 @@ def _update_gradient_JTCJ_sparse(
if efc_state_in[worldid, efcid0] != types.ConstraintState.CONE: if efc_state_in[worldid, efcid0] != types.ConstraintState.CONE:
continue continue
# All dims share the same sparsity pattern. Scan colind once to find # One thread per (contact, support-pair): the support dofs are exactly the colind entries,
# the sparse positions of dof1id and dof2id. Skip if either is absent. # so decode pairid -> (pos1, pos2) with pos1 <= pos2 directly. No colind scan, and no
# membership skip (which the all-dof-pairs version wasted on ~99% absent dofs).
rownnz = efc_J_rownnz_in[worldid, efcid0] rownnz = efc_J_rownnz_in[worldid, efcid0]
rowadr0 = efc_J_rowadr_in[worldid, efcid0] npairs = rownnz * (rownnz + 1) // 2
pos1 = int(-1) if pairid >= npairs:
pos2 = int(-1)
for k in range(rownnz):
col = efc_J_colind_in[worldid, 0, rowadr0 + k]
if col == dof1id:
pos1 = k
if col == dof2id:
pos2 = k
if pos1 >= 0 and pos2 >= 0:
break
if pos1 < 0 or pos2 < 0:
continue continue
rowadr0 = efc_J_rowadr_in[worldid, efcid0]
pos1 = int(0)
rem = pairid
while rem >= rownnz - pos1:
rem -= rownnz - pos1
pos1 += 1
pos2 = pos1 + rem
dofa = efc_J_colind_in[worldid, 0, rowadr0 + pos1]
dofb = efc_J_colind_in[worldid, 0, rowadr0 + pos2]
dof1id = wp.min(dofa, dofb)
dof2id = wp.max(dofa, dofb)
fri = contact_friction_in[conid] fri = contact_friction_in[conid]
mu = fri[0] * opt_impratio_invsqrt[worldid % opt_impratio_invsqrt.shape[0]] mu = fri[0] * opt_impratio_invsqrt[worldid % opt_impratio_invsqrt.shape[0]]
@@ -2555,7 +2211,8 @@ def _update_gradient_JTCJ_sparse(
if dim1id != dim2id: if dim1id != dim2id:
h += hcone * efc_J12 * efc_J21 h += hcone * efc_J12 * efc_J21
ctx_h_out[worldid, dof1id, dof2id] += h # multiple contacts can contribute to the same (dof1id, dof2id); atomic_add is exact
wp.atomic_add(ctx_h_out[worldid, dof1id], dof2id, h)
@wp.kernel @wp.kernel
@@ -2893,8 +2550,16 @@ def _JTDAJ_sparse(
def _update_gradient(m: types.Model, d: types.Data, ctx: SolverContext): def _update_gradient(m: types.Model, d: types.Data, ctx: SolverContext):
# grad = Ma - qfrc_smooth - qfrc_constraint # grad = Ma - qfrc_smooth - qfrc_constraint
if m.opt.solver == types.SolverType.CG:
wp.launch_tiled(
_update_gradient_grad_tiled,
dim=d.nworld,
inputs=[m.nv, d.qfrc_smooth, d.qfrc_constraint, d.efc.Ma, ctx.done],
outputs=[ctx.grad, ctx.grad_dot],
block_dim=m.block_dim.update_gradient_grad,
)
else:
wp.launch(_update_gradient_zero_grad_dot, dim=d.nworld, inputs=[ctx.done], outputs=[ctx.grad_dot]) wp.launch(_update_gradient_zero_grad_dot, dim=d.nworld, inputs=[ctx.done], outputs=[ctx.grad_dot])
wp.launch( wp.launch(
_update_gradient_grad, _update_gradient_grad,
dim=(d.nworld, m.nv), dim=(d.nworld, m.nv),
@@ -2944,8 +2609,11 @@ def _update_gradient(m: types.Model, d: types.Data, ctx: SolverContext):
# of SMs on the GPU. We can now query the SM count: # of SMs on the GPU. We can now query the SM count:
# https://github.com/NVIDIA/warp/commit/f3814e7e5459e5fd13032cf0fddb3daddd510f30 # https://github.com/NVIDIA/warp/commit/f3814e7e5459e5fd13032cf0fddb3daddd510f30
# make dim_block and nblocks_perblock static for update_gradient_JTCJ to allow # Block-limit the launch: cap the grid near SM-filling width and stride over contacts, so
# loop unrolling # we don't over-launch naconmax (capacity) threads when active contacts are far fewer. The
# sparse kernel uses one thread per (contact, support-pair) (jtcj_max_pairs), the dense one
# per (contact, dof-pair) (dof_tri_row.size).
jtcj_second_dim = m.jtcj_max_pairs if m.is_sparse else m.dof_tri_row.size
if wp.get_device().is_cuda: if wp.get_device().is_cuda:
sm_count = wp.get_device().sm_count sm_count = wp.get_device().sm_count
@@ -2953,7 +2621,7 @@ def _update_gradient(m: types.Model, d: types.Data, ctx: SolverContext):
# can be changed in the future to fine-tune the perf. The optimal factor will # can be changed in the future to fine-tune the perf. The optimal factor will
# depend on the kernel's occupancy, which determines how many blocks can # depend on the kernel's occupancy, which determines how many blocks can
# simultaneously run on the SM. TODO: This factor can be tuned further. # simultaneously run on the SM. TODO: This factor can be tuned further.
dim_block = ceil((sm_count * 6 * 256) / m.dof_tri_row.size) dim_block = ceil((sm_count * 6 * 256) / jtcj_second_dim)
else: else:
# fall back for CPU # fall back for CPU
dim_block = d.naconmax dim_block = d.naconmax
@@ -2963,11 +2631,9 @@ def _update_gradient(m: types.Model, d: types.Data, ctx: SolverContext):
if m.is_sparse: if m.is_sparse:
wp.launch( wp.launch(
_update_gradient_JTCJ_sparse, _update_gradient_JTCJ_sparse,
dim=(dim_block, m.dof_tri_row.size), dim=(dim_block, m.jtcj_max_pairs),
inputs=[ inputs=[
m.opt.impratio_invsqrt, m.opt.impratio_invsqrt,
m.dof_tri_row,
m.dof_tri_col,
d.contact.dist, d.contact.dist,
d.contact.includemargin, d.contact.includemargin,
d.contact.friction, d.contact.friction,
@@ -3071,25 +2737,6 @@ def _update_gradient_incremental(m: types.Model, d: types.Data, ctx: SolverConte
_cholesky_factorize_solve(m, d, ctx, skip_unchanged=True) _cholesky_factorize_solve(m, d, ctx, skip_unchanged=True)
@wp.kernel
def _solve_prev_grad_Mgrad(
# In:
ctx_grad_in: wp.array2d[float],
ctx_Mgrad_in: wp.array2d[float],
ctx_done_in: wp.array[bool],
# Out:
ctx_prev_grad_out: wp.array2d[float],
ctx_prev_Mgrad_out: wp.array2d[float],
):
worldid, dofid = wp.tid()
if ctx_done_in[worldid]:
return
ctx_prev_grad_out[worldid, dofid] = ctx_grad_in[worldid, dofid]
ctx_prev_Mgrad_out[worldid, dofid] = ctx_Mgrad_in[worldid, dofid]
@wp.kernel @wp.kernel
def _solve_beta_zero( def _solve_beta_zero(
# Out: # Out:
@@ -3101,6 +2748,47 @@ def _solve_beta_zero(
ctx_beta_den_out[worldid] = 0.0 ctx_beta_den_out[worldid] = 0.0
@wp.kernel
def _solve_beta_accumulate_tiled(
# Model:
nv: int,
# In:
ctx_grad_in: wp.array2d[float],
ctx_Mgrad_in: wp.array2d[float],
ctx_prev_grad_in: wp.array2d[float],
ctx_prev_Mgrad_in: wp.array2d[float],
ctx_done_in: wp.array[bool],
# Out:
ctx_beta_num_out: wp.array[float],
ctx_beta_den_out: wp.array[float],
):
worldid, tid = wp.tid()
if ctx_done_in[worldid]:
return
local_num = float(0.0)
local_den = float(0.0)
BLOCK_DIM = wp.block_dim()
for dofid in range(tid, nv, BLOCK_DIM):
prev_Mgrad = ctx_prev_Mgrad_in[worldid, dofid]
num = ctx_grad_in[worldid, dofid] * (ctx_Mgrad_in[worldid, dofid] - prev_Mgrad)
den = ctx_prev_grad_in[worldid, dofid] * prev_Mgrad
local_num += num
local_den += den
num_tile = wp.tile(local_num, preserve_type=True)
num_sum = wp.tile_reduce(wp.add, num_tile)
den_tile = wp.tile(local_den, preserve_type=True)
den_sum = wp.tile_reduce(wp.add, den_tile)
if tid == 0:
ctx_beta_num_out[worldid] = num_sum[0]
ctx_beta_den_out[worldid] = den_sum[0]
@wp.kernel @wp.kernel
def _solve_beta_accumulate( def _solve_beta_accumulate(
# In: # In:
@@ -3125,23 +2813,6 @@ def _solve_beta_accumulate(
wp.atomic_add(ctx_beta_den_out, worldid, den) wp.atomic_add(ctx_beta_den_out, worldid, den)
@wp.kernel
def _solve_beta_finalize(
# In:
ctx_beta_num_in: wp.array[float],
ctx_beta_den_in: wp.array[float],
ctx_done_in: wp.array[bool],
# Out:
ctx_beta_out: wp.array[float],
):
worldid = wp.tid()
if ctx_done_in[worldid]:
return
ctx_beta_out[worldid] = wp.max(0.0, ctx_beta_num_in[worldid] / wp.max(types.MJ_MINVAL, ctx_beta_den_in[worldid]))
@wp.kernel @wp.kernel
def _solve_zero_search_dot( def _solve_zero_search_dot(
# In: # In:
@@ -3184,6 +2855,91 @@ def _solve_search_update(
wp.atomic_add(ctx_search_dot_out, worldid, search * search) wp.atomic_add(ctx_search_dot_out, worldid, search * search)
@wp.kernel
def _solve_search_update_cg_tiled(
# Model:
nv: int,
# In:
ctx_grad_in: wp.array2d[float],
ctx_Mgrad_in: wp.array2d[float],
ctx_search_in: wp.array2d[float],
ctx_beta_in: wp.array[float],
ctx_done_in: wp.array[bool],
# Out:
ctx_search_out: wp.array2d[float],
ctx_search_dot_out: wp.array[float],
ctx_prev_grad_out: wp.array2d[float],
ctx_prev_Mgrad_out: wp.array2d[float],
):
worldid, tid = wp.tid()
if ctx_done_in[worldid]:
return
local_search_dot = float(0.0)
BLOCK_DIM = wp.block_dim()
beta = ctx_beta_in[worldid]
for dofid in range(tid, nv, BLOCK_DIM):
mgrad = ctx_Mgrad_in[worldid, dofid]
search = -1.0 * mgrad + beta * ctx_search_in[worldid, dofid]
ctx_search_out[worldid, dofid] = search
local_search_dot += search * search
ctx_prev_grad_out[worldid, dofid] = ctx_grad_in[worldid, dofid]
ctx_prev_Mgrad_out[worldid, dofid] = mgrad
search_dot_tile = wp.tile(local_search_dot, preserve_type=True)
search_dot_sum = wp.tile_reduce(wp.add, search_dot_tile)
if tid == 0:
ctx_search_dot_out[worldid] = search_dot_sum[0]
@wp.kernel
def _solve_cg_finalize(
# Model:
nv: int,
opt_tolerance: wp.array[float],
opt_iterations: int,
stat_meaninertia: wp.array[float],
# In:
ctx_beta_num_in: wp.array[float],
ctx_beta_den_in: wp.array[float],
ctx_improvement_in: wp.array[float],
ctx_done_in: wp.array[bool],
ctx_grad_dot_in: wp.array[float],
# Data out:
solver_niter_out: wp.array[int],
# Out:
ctx_beta_out: wp.array[float],
nsolving_out: wp.array[int],
ctx_done_out: wp.array[bool],
):
worldid = wp.tid()
if ctx_done_in[worldid]:
return
# 1. solve_beta_finalize
ctx_beta_out[worldid] = wp.max(0.0, ctx_beta_num_in[worldid] / wp.max(types.MJ_MINVAL, ctx_beta_den_in[worldid]))
# 2. solve_done
solver_niter_out[worldid] += 1
tolerance = opt_tolerance[worldid % opt_tolerance.shape[0]]
meaninertia = stat_meaninertia[worldid % stat_meaninertia.shape[0]]
grad_dot = ctx_grad_dot_in[worldid]
improvement = _rescale(nv, meaninertia, ctx_improvement_in[worldid])
gradient = _rescale(nv, meaninertia, wp.sqrt(grad_dot))
done = (improvement < tolerance) or (gradient < tolerance)
if done or solver_niter_out[worldid] == opt_iterations:
ctx_done_out[worldid] = True
wp.atomic_add(nsolving_out, 0, -1)
@wp.kernel @wp.kernel
def _solve_done( def _solve_done(
# Model: # Model:
@@ -3225,18 +2981,9 @@ def _solver_iteration(
m: types.Model, m: types.Model,
d: types.Data, d: types.Data,
ctx: SolverContext, ctx: SolverContext,
step_size_cost: wp.array2d[float],
nsolving: wp.array[int], nsolving: wp.array[int],
): ):
_linesearch(m, d, ctx, step_size_cost) _linesearch(m, d, ctx)
if m.opt.solver == types.SolverType.CG:
wp.launch(
_solve_prev_grad_Mgrad,
dim=(d.nworld, m.nv),
inputs=[ctx.grad, ctx.Mgrad, ctx.done],
outputs=[ctx.prev_grad, ctx.prev_Mgrad],
)
# Incremental H is only valid for non-elliptic cones. The elliptic cone # Incremental H is only valid for non-elliptic cones. The elliptic cone
# path in _update_constraint_efc has early returns that skip state change # path in _update_constraint_efc has early returns that skip state change
@@ -3262,19 +3009,43 @@ def _solver_iteration(
dim=d.nworld, dim=d.nworld,
outputs=[ctx.beta, ctx.beta_den], outputs=[ctx.beta, ctx.beta_den],
) )
wp.launch( wp.launch_tiled(
_solve_beta_accumulate, _solve_beta_accumulate_tiled,
dim=(d.nworld, m.nv), dim=d.nworld,
inputs=[ctx.grad, ctx.Mgrad, ctx.prev_grad, ctx.prev_Mgrad, ctx.done], inputs=[m.nv, ctx.grad, ctx.Mgrad, ctx.prev_grad, ctx.prev_Mgrad, ctx.done],
outputs=[ctx.beta, ctx.beta_den], outputs=[ctx.beta, ctx.beta_den],
block_dim=m.block_dim.solve_beta_accumulate,
) )
wp.launch( wp.launch(
_solve_beta_finalize, _solve_cg_finalize,
dim=d.nworld, dim=d.nworld,
inputs=[ctx.beta, ctx.beta_den, ctx.done], inputs=[
outputs=[ctx.beta], m.nv,
m.opt.tolerance,
m.opt.iterations,
m.stat.meaninertia,
ctx.beta,
ctx.beta_den,
ctx.improvement,
ctx.done,
ctx.grad_dot,
],
outputs=[
d.solver_niter,
ctx.beta,
nsolving,
ctx.done,
],
)
wp.launch_tiled(
_solve_search_update_cg_tiled,
dim=d.nworld,
inputs=[m.nv, ctx.grad, ctx.Mgrad, ctx.search, ctx.beta, ctx.done],
outputs=[ctx.search, ctx.search_dot, ctx.prev_grad, ctx.prev_Mgrad],
block_dim=m.block_dim.solve_search_update_cg,
) )
else:
wp.launch(_solve_zero_search_dot, dim=d.nworld, inputs=[ctx.done], outputs=[ctx.search_dot]) wp.launch(_solve_zero_search_dot, dim=d.nworld, inputs=[ctx.done], outputs=[ctx.search_dot])
wp.launch( wp.launch(
@@ -3369,6 +3140,16 @@ def _solve(m: types.Model, d: types.Data, ctx: SolverContext):
init_context(m, d, ctx, grad=True) init_context(m, d, ctx, grad=True)
# search = -Mgrad # search = -Mgrad
if m.opt.solver == types.SolverType.CG:
wp.launch_tiled(
_solve_init_search_cg_tiled,
dim=d.nworld,
inputs=[m.nv, ctx.grad, ctx.Mgrad],
outputs=[ctx.search, ctx.search_dot, ctx.prev_grad, ctx.prev_Mgrad],
block_dim=m.block_dim.solve_init_search_cg,
)
else:
wp.launch( wp.launch(
_solve_init_search, _solve_init_search,
dim=(d.nworld, m.nv), dim=(d.nworld, m.nv),
@@ -3376,8 +3157,6 @@ def _solve(m: types.Model, d: types.Data, ctx: SolverContext):
outputs=[ctx.search, ctx.search_dot], outputs=[ctx.search, ctx.search_dot],
) )
step_size_cost = wp.empty((d.nworld, m.opt.ls_iterations if m.opt.ls_parallel else 0), dtype=float)
nsolving = wp.full(shape=(1,), value=d.nworld, dtype=int) nsolving = wp.full(shape=(1,), value=d.nworld, dtype=int)
if m.opt.iterations != 0 and m.opt.graph_conditional: if m.opt.iterations != 0 and m.opt.graph_conditional:
# Note: the iteration kernel (indicated by while_body) is repeatedly launched # Note: the iteration kernel (indicated by while_body) is repeatedly launched
@@ -3387,15 +3166,13 @@ def _solve(m: types.Model, d: types.Data, ctx: SolverContext):
# When the number of iterations reaches m.opt.iterations, solver_niter # When the number of iterations reaches m.opt.iterations, solver_niter
# becomes zero and all worlds are marked as converged to avoid an infinite loop. # becomes zero and all worlds are marked as converged to avoid an infinite loop.
# note: we only launch the iteration kernel if everything is not done # note: we only launch the iteration kernel if everything is not done
wp.capture_while( wp.capture_while(nsolving, while_body=_solver_iteration, m=m, d=d, ctx=ctx, nsolving=nsolving)
nsolving, while_body=_solver_iteration, m=m, d=d, ctx=ctx, step_size_cost=step_size_cost, nsolving=nsolving
)
else: else:
# This branch is mostly for when JAX is used as it is currently not compatible # This branch is mostly for when JAX is used as it is currently not compatible
# with CUDA graph conditional. # with CUDA graph conditional.
# It should be removed when JAX becomes compatible. # It should be removed when JAX becomes compatible.
for _ in range(m.opt.iterations): for _ in range(m.opt.iterations):
_solver_iteration(m, d, ctx, step_size_cost, nsolving) _solver_iteration(m, d, ctx, nsolving)
# TODO(team): Consolidate monolithic and island solver code where possible # TODO(team): Consolidate monolithic and island solver code where possible
@@ -4510,6 +4287,10 @@ def _solve_beta_island_finalize(
if islandid >= nisland_in[worldid]: if islandid >= nisland_in[worldid]:
return return
if island_done_in[worldid, islandid]:
island_beta_out[worldid, islandid] = 0.0
return
island_beta_out[worldid, islandid] = wp.max( island_beta_out[worldid, islandid] = wp.max(
0.0, island_beta_num_in[worldid, islandid] / wp.max(types.MJ_MINVAL, island_beta_den_in[worldid, islandid]) 0.0, island_beta_num_in[worldid, islandid] / wp.max(types.MJ_MINVAL, island_beta_den_in[worldid, islandid])
) )
+27 -5
View File
@@ -66,6 +66,10 @@ class BlockDim:
update_gradient_JTDAJ_sparse: update gradient JTDAJ sparse block dimension (solver) update_gradient_JTDAJ_sparse: update gradient JTDAJ sparse block dimension (solver)
update_gradient_JTDAJ_dense: update gradient JTDAJ dense block dimension (solver) update_gradient_JTDAJ_dense: update gradient JTDAJ dense block dimension (solver)
linesearch_iterative: linesearch iterative block dimension (solver) linesearch_iterative: linesearch iterative block dimension (solver)
update_gradient_grad: update gradient grad block dimension (solver)
solve_beta_accumulate: solve beta accumulate block dimension (solver)
solve_search_update_cg: solve search update CG block dimension (solver)
solve_init_search_cg: solve init search CG block dimension (solver)
contact_jac_tiled: contact Jacobian tiled block dimension (solver) contact_jac_tiled: contact Jacobian tiled block dimension (solver)
qderiv_actuator_dense: qderiv actuator dense block dimension (derivative) qderiv_actuator_dense: qderiv actuator dense block dimension (derivative)
render: render block dimension (render) render: render block dimension (render)
@@ -92,6 +96,10 @@ class BlockDim:
update_gradient_JTDAJ_sparse: int = 64 update_gradient_JTDAJ_sparse: int = 64
update_gradient_JTDAJ_dense: int = 128 update_gradient_JTDAJ_dense: int = 128
linesearch_iterative: int = 32 linesearch_iterative: int = 32
update_gradient_grad: int = 256
solve_beta_accumulate: int = 256
solve_search_update_cg: int = 256
solve_init_search_cg: int = 256
contact_jac_tiled: int = 32 contact_jac_tiled: int = 32
# derivative # derivative
qderiv_actuator_dense: int = 32 qderiv_actuator_dense: int = 32
@@ -800,8 +808,6 @@ class Option:
warp only fields: warp only fields:
impratio_invsqrt: ratio of friction-to-normal contact impedance (stored as inverse square root) impratio_invsqrt: ratio of friction-to-normal contact impedance (stored as inverse square root)
ls_parallel: evaluate engine solver step sizes in parallel
ls_parallel_min_step: minimum step size for solver linesearch
broadphase: broadphase type (BroadphaseType) broadphase: broadphase type (BroadphaseType)
broadphase_filter: broadphase filter bitflag (BroadphaseFilter) broadphase_filter: broadphase filter bitflag (BroadphaseFilter)
graph_conditional: flag to use cuda graph conditional graph_conditional: flag to use cuda graph conditional
@@ -834,14 +840,29 @@ class Option:
sdf_iterations: int sdf_iterations: int
# warp only fields: # warp only fields:
impratio_invsqrt: array("*", float) impratio_invsqrt: array("*", float)
ls_parallel: bool
ls_parallel_min_step: float
broadphase: BroadphaseType broadphase: BroadphaseType
broadphase_filter: BroadphaseFilter broadphase_filter: BroadphaseFilter
graph_conditional: bool graph_conditional: bool
run_collision_detection: bool run_collision_detection: bool
contact_sensor_maxmatch: int contact_sensor_maxmatch: int
# TODO(team): remove in future version
@property
def ls_parallel(self) -> bool:
raise AttributeError("ls_parallel was removed in MuJoCo Warp 3.9.1.")
@ls_parallel.setter
def ls_parallel(self, value: bool):
raise AttributeError("ls_parallel was removed in MuJoCo Warp 3.9.1.")
@property
def ls_parallel_min_step(self) -> float:
raise AttributeError("ls_parallel_min_step was removed in MuJoCo Warp 3.9.1.")
@ls_parallel_min_step.setter
def ls_parallel_min_step(self, value: float):
raise AttributeError("ls_parallel_min_step was removed in MuJoCo Warp 3.9.1.")
@dataclasses.dataclass @dataclasses.dataclass
class Statistic: class Statistic:
@@ -1290,6 +1311,7 @@ class Model:
tendon_geom_adr: geom tendon address tendon_geom_adr: geom tendon address
tendon_limited_adr: addresses for limited tendons tendon_limited_adr: addresses for limited tendons
max_ten_J_rownnz: maximum number of non-zeros in a tendon row max_ten_J_rownnz: maximum number of non-zeros in a tendon row
jtcj_max_pairs: bound on a contact's support-pair count, sizes the elliptic-cone JTCJ launch
ten_wrapadr_site: wrap object starting address for sites ten_wrapadr_site: wrap object starting address for sites
ten_wrapnum_site: number of site wrap objects per tendon ten_wrapnum_site: number of site wrap objects per tendon
wrap_jnt_adr: addresses for joint tendon wrap object wrap_jnt_adr: addresses for joint tendon wrap object
@@ -1719,6 +1741,7 @@ class Model:
tendon_geom_adr: wp.array[int] tendon_geom_adr: wp.array[int]
tendon_limited_adr: wp.array[int] tendon_limited_adr: wp.array[int]
max_ten_J_rownnz: int max_ten_J_rownnz: int
jtcj_max_pairs: int
ten_wrapadr_site: wp.array[int] ten_wrapadr_site: wp.array[int]
ten_wrapnum_site: wp.array[int] ten_wrapnum_site: wp.array[int]
wrap_jnt_adr: wp.array[int] wrap_jnt_adr: wp.array[int]
@@ -2205,7 +2228,6 @@ class SolverContext:
mv: wp.array2d[float] mv: wp.array2d[float]
jv: wp.array2d[float] jv: wp.array2d[float]
quad: wp.array2d[wp.vec3] quad: wp.array2d[wp.vec3]
quad_gauss: wp.array[wp.vec3]
alpha: wp.array[float] alpha: wp.array[float]
improvement: wp.array[float] improvement: wp.array[float]
prev_grad: wp.array2d[float] prev_grad: wp.array2d[float]
+1 -1
View File
@@ -169,7 +169,7 @@ def _main(argv: Sequence[str]) -> None:
solver, cone = mjw.SolverType(m.opt.solver).name, mjw.ConeType(m.opt.cone).name solver, cone = mjw.SolverType(m.opt.solver).name, mjw.ConeType(m.opt.cone).name
integrator = mjw.IntegratorType(m.opt.integrator).name integrator = mjw.IntegratorType(m.opt.integrator).name
iterations, ls_iterations = m.opt.iterations, m.opt.ls_iterations iterations, ls_iterations = m.opt.iterations, m.opt.ls_iterations
ls_str = f"{'parallel' if m.opt.ls_parallel else 'iterative'} linesearch iterations: {ls_iterations}" ls_str = f"linesearch iterations: {ls_iterations}"
print( print(
f" nbody: {m.nbody} nv: {m.nv} ngeom: {m.ngeom} nu: {m.nu} is_sparse: {m.is_sparse}\n" f" nbody: {m.nbody} nv: {m.nv} ngeom: {m.ngeom} nu: {m.nu} is_sparse: {m.is_sparse}\n"
f" broadphase: {broadphase} broadphase_filter: {filter}\n" f" broadphase: {broadphase} broadphase_filter: {filter}\n"
+8 -17
View File
@@ -14,17 +14,14 @@
# ============================================================================== # ==============================================================================
"""DO NOT EDIT. This file is auto-generated.""" """DO NOT EDIT. This file is auto-generated."""
import dataclasses import dataclasses
import functools import functools
import jax import jax
import warp as wp
from mujoco.mjx._src import types from mujoco.mjx._src import types
from mujoco.mjx.warp import ffi
import mujoco.mjx.third_party.mujoco_warp as mjwarp import mujoco.mjx.third_party.mujoco_warp as mjwarp
from mujoco.mjx.third_party.mujoco_warp._src import types as mjwp_types from mujoco.mjx.third_party.mujoco_warp._src import types as mjwp_types
from mujoco.mjx.warp import ffi import warp as wp
_m = mjwarp.Model( _m = mjwarp.Model(
**{f.name: None for f in dataclasses.fields(mjwarp.Model) if f.init} **{f.name: None for f in dataclasses.fields(mjwarp.Model) if f.init}
@@ -241,6 +238,7 @@ def _forward_shim(
jnt_stiffness: wp.array2d[float], jnt_stiffness: wp.array2d[float],
jnt_stiffnesspoly: wp.array2d[wp.vec2], jnt_stiffnesspoly: wp.array2d[wp.vec2],
jnt_type: wp.array[int], jnt_type: wp.array[int],
jtcj_max_pairs: int,
light_bodyid: wp.array[int], light_bodyid: wp.array[int],
light_dir: wp.array2d[wp.vec3], light_dir: wp.array2d[wp.vec3],
light_dir0: wp.array2d[wp.vec3], light_dir0: wp.array2d[wp.vec3],
@@ -415,8 +413,6 @@ def _forward_shim(
opt__integrator: int, opt__integrator: int,
opt__iterations: int, opt__iterations: int,
opt__ls_iterations: int, opt__ls_iterations: int,
opt__ls_parallel: bool,
opt__ls_parallel_min_step: float,
opt__ls_tolerance: wp.array[float], opt__ls_tolerance: wp.array[float],
opt__magnetic: wp.array[wp.vec3], opt__magnetic: wp.array[wp.vec3],
opt__run_collision_detection: bool, opt__run_collision_detection: bool,
@@ -780,6 +776,7 @@ def _forward_shim(
_m.jnt_stiffness = jnt_stiffness _m.jnt_stiffness = jnt_stiffness
_m.jnt_stiffnesspoly = jnt_stiffnesspoly _m.jnt_stiffnesspoly = jnt_stiffnesspoly
_m.jnt_type = jnt_type _m.jnt_type = jnt_type
_m.jtcj_max_pairs = jtcj_max_pairs
_m.light_bodyid = light_bodyid _m.light_bodyid = light_bodyid
_m.light_dir = light_dir _m.light_dir = light_dir
_m.light_dir0 = light_dir0 _m.light_dir0 = light_dir0
@@ -865,8 +862,6 @@ def _forward_shim(
_m.opt.integrator = opt__integrator _m.opt.integrator = opt__integrator
_m.opt.iterations = opt__iterations _m.opt.iterations = opt__iterations
_m.opt.ls_iterations = opt__ls_iterations _m.opt.ls_iterations = opt__ls_iterations
_m.opt.ls_parallel = opt__ls_parallel
_m.opt.ls_parallel_min_step = opt__ls_parallel_min_step
_m.opt.ls_tolerance = opt__ls_tolerance _m.opt.ls_tolerance = opt__ls_tolerance
_m.opt.magnetic = opt__magnetic _m.opt.magnetic = opt__magnetic
_m.opt.run_collision_detection = opt__run_collision_detection _m.opt.run_collision_detection = opt__run_collision_detection
@@ -1796,6 +1791,7 @@ def _forward_jax_impl(m: types.Model, d: types.Data):
m.jnt_stiffness, m.jnt_stiffness,
m.jnt_stiffnesspoly, m.jnt_stiffnesspoly,
m.jnt_type, m.jnt_type,
m._impl.jtcj_max_pairs,
m._impl.light_bodyid, m._impl.light_bodyid,
m.light_dir, m.light_dir,
m.light_dir0, m.light_dir0,
@@ -1970,8 +1966,6 @@ def _forward_jax_impl(m: types.Model, d: types.Data):
m.opt.integrator, m.opt.integrator,
m.opt.iterations, m.opt.iterations,
m.opt.ls_iterations, m.opt.ls_iterations,
m.opt._impl.ls_parallel,
m.opt._impl.ls_parallel_min_step,
m.opt.ls_tolerance, m.opt.ls_tolerance,
m.opt.magnetic, m.opt.magnetic,
m.opt._impl.run_collision_detection, m.opt._impl.run_collision_detection,
@@ -2498,6 +2492,7 @@ def _step_shim(
jnt_stiffness: wp.array2d[float], jnt_stiffness: wp.array2d[float],
jnt_stiffnesspoly: wp.array2d[wp.vec2], jnt_stiffnesspoly: wp.array2d[wp.vec2],
jnt_type: wp.array[int], jnt_type: wp.array[int],
jtcj_max_pairs: int,
light_bodyid: wp.array[int], light_bodyid: wp.array[int],
light_dir: wp.array2d[wp.vec3], light_dir: wp.array2d[wp.vec3],
light_dir0: wp.array2d[wp.vec3], light_dir0: wp.array2d[wp.vec3],
@@ -2677,8 +2672,6 @@ def _step_shim(
opt__integrator: int, opt__integrator: int,
opt__iterations: int, opt__iterations: int,
opt__ls_iterations: int, opt__ls_iterations: int,
opt__ls_parallel: bool,
opt__ls_parallel_min_step: float,
opt__ls_tolerance: wp.array[float], opt__ls_tolerance: wp.array[float],
opt__magnetic: wp.array[wp.vec3], opt__magnetic: wp.array[wp.vec3],
opt__run_collision_detection: bool, opt__run_collision_detection: bool,
@@ -3048,6 +3041,7 @@ def _step_shim(
_m.jnt_stiffness = jnt_stiffness _m.jnt_stiffness = jnt_stiffness
_m.jnt_stiffnesspoly = jnt_stiffnesspoly _m.jnt_stiffnesspoly = jnt_stiffnesspoly
_m.jnt_type = jnt_type _m.jnt_type = jnt_type
_m.jtcj_max_pairs = jtcj_max_pairs
_m.light_bodyid = light_bodyid _m.light_bodyid = light_bodyid
_m.light_dir = light_dir _m.light_dir = light_dir
_m.light_dir0 = light_dir0 _m.light_dir0 = light_dir0
@@ -3136,8 +3130,6 @@ def _step_shim(
_m.opt.integrator = opt__integrator _m.opt.integrator = opt__integrator
_m.opt.iterations = opt__iterations _m.opt.iterations = opt__iterations
_m.opt.ls_iterations = opt__ls_iterations _m.opt.ls_iterations = opt__ls_iterations
_m.opt.ls_parallel = opt__ls_parallel
_m.opt.ls_parallel_min_step = opt__ls_parallel_min_step
_m.opt.ls_tolerance = opt__ls_tolerance _m.opt.ls_tolerance = opt__ls_tolerance
_m.opt.magnetic = opt__magnetic _m.opt.magnetic = opt__magnetic
_m.opt.run_collision_detection = opt__run_collision_detection _m.opt.run_collision_detection = opt__run_collision_detection
@@ -4089,6 +4081,7 @@ def _step_jax_impl(m: types.Model, d: types.Data):
m.jnt_stiffness, m.jnt_stiffness,
m.jnt_stiffnesspoly, m.jnt_stiffnesspoly,
m.jnt_type, m.jnt_type,
m._impl.jtcj_max_pairs,
m._impl.light_bodyid, m._impl.light_bodyid,
m.light_dir, m.light_dir,
m.light_dir0, m.light_dir0,
@@ -4268,8 +4261,6 @@ def _step_jax_impl(m: types.Model, d: types.Data):
m.opt.integrator, m.opt.integrator,
m.opt.iterations, m.opt.iterations,
m.opt.ls_iterations, m.opt.ls_iterations,
m.opt._impl.ls_parallel,
m.opt._impl.ls_parallel_min_step,
m.opt.ls_tolerance, m.opt.ls_tolerance,
m.opt.magnetic, m.opt.magnetic,
m.opt._impl.run_collision_detection, m.opt._impl.run_collision_detection,
+28 -34
View File
@@ -15,17 +15,14 @@
"""MJX Warp types. """MJX Warp types.
DO NOT EDIT. This file is auto-generated. DO NOT EDIT. This file is auto-generated.
""" """
import dataclasses import dataclasses
import typing import typing
from typing import Tuple from typing import Tuple
import jax import jax
from jax import tree_util from jax import tree_util
from jax.interpreters import batching from jax.interpreters import batching
import numpy as np
from mujoco.mjx._src import dataclasses as mjx_dataclasses from mujoco.mjx._src import dataclasses as mjx_dataclasses
import numpy as np
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
GraphMode = int GraphMode = int
@@ -37,7 +34,6 @@ if typing.TYPE_CHECKING:
else: else:
try: try:
from warp._src.jax_experimental.ffi import GraphMode from warp._src.jax_experimental.ffi import GraphMode
from mujoco.mjx.third_party.mujoco_warp._src import types as mjwp_types from mujoco.mjx.third_party.mujoco_warp._src import types as mjwp_types
Callback = mjwp_types.Callback Callback = mjwp_types.Callback
@@ -46,7 +42,6 @@ else:
Callback = None Callback = None
PyTreeNode = mjx_dataclasses.PyTreeNode PyTreeNode = mjx_dataclasses.PyTreeNode
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
@tree_util.register_pytree_node_class @tree_util.register_pytree_node_class
class TileSet: class TileSet:
@@ -58,12 +53,9 @@ class TileSet:
adr: address of each tile in the set adr: address of each tile in the set
size: size of all the tiles in this set size: size of all the tiles in this set
""" """
adr: np.ndarray adr: np.ndarray
size: int size: int
# Manually kept in this generated shim until TileSet method generation is
# needed more broadly. Keep this in sync with mujoco_warp._src.types.TileSet.
def __eq__(self, other) -> bool: def __eq__(self, other) -> bool:
if self.__class__ is not other.__class__: if self.__class__ is not other.__class__:
return NotImplemented return NotImplemented
@@ -101,18 +93,25 @@ class BlockDim:
energy_vel_kinetic: energy velocity kinetic block dimension (sensor) energy_vel_kinetic: energy velocity kinetic block dimension (sensor)
cholesky_factorize: Cholesky factorize block dimension (smooth) cholesky_factorize: Cholesky factorize block dimension (smooth)
cholesky_solve: Cholesky solve block dimension (smooth) cholesky_solve: Cholesky solve block dimension (smooth)
cholesky_factorize_solve: Cholesky factorize and solve block dimension (smooth) cholesky_factorize_solve: Cholesky factorize and solve block dimension
(smooth)
solve_LD_sparse_fused: solve LD sparse fused block dimension (smooth) solve_LD_sparse_fused: solve LD sparse fused block dimension (smooth)
update_gradient_cholesky: update gradient Cholesky block dimension (solver) update_gradient_cholesky: update gradient Cholesky block dimension (solver)
update_gradient_cholesky_blocked: update gradient Cholesky blocked block dimension (solver) update_gradient_cholesky_blocked: update gradient Cholesky blocked block
update_gradient_JTDAJ_sparse: update gradient JTDAJ sparse block dimension (solver) dimension (solver)
update_gradient_JTDAJ_dense: update gradient JTDAJ dense block dimension (solver) update_gradient_JTDAJ_sparse: update gradient JTDAJ sparse block dimension
(solver)
update_gradient_JTDAJ_dense: update gradient JTDAJ dense block dimension
(solver)
linesearch_iterative: linesearch iterative block dimension (solver) linesearch_iterative: linesearch iterative block dimension (solver)
update_gradient_grad: update gradient grad block dimension (solver)
solve_beta_accumulate: solve beta accumulate block dimension (solver)
solve_search_update_cg: solve search update CG block dimension (solver)
solve_init_search_cg: solve init search CG block dimension (solver)
contact_jac_tiled: contact Jacobian tiled block dimension (solver) contact_jac_tiled: contact Jacobian tiled block dimension (solver)
qderiv_actuator_dense: qderiv actuator dense block dimension (derivative) qderiv_actuator_dense: qderiv actuator dense block dimension (derivative)
render: render block dimension (render) render: render block dimension (render)
""" """
actuator_velocity: int actuator_velocity: int
cholesky_factorize: int cholesky_factorize: int
cholesky_factorize_solve: int cholesky_factorize_solve: int
@@ -127,10 +126,14 @@ class BlockDim:
render: int render: int
segmented_sort: int segmented_sort: int
solve_LD_sparse_fused: int solve_LD_sparse_fused: int
solve_beta_accumulate: int
solve_init_search_cg: int
solve_search_update_cg: int
update_gradient_JTDAJ_dense: int update_gradient_JTDAJ_dense: int
update_gradient_JTDAJ_sparse: int update_gradient_JTDAJ_sparse: int
update_gradient_cholesky: int update_gradient_cholesky: int
update_gradient_cholesky_blocked: int update_gradient_cholesky_blocked: int
update_gradient_grad: int
def tree_flatten(self): def tree_flatten(self):
children = list((getattr(self, k) for k in self.__dataclass_fields__)) children = list((getattr(self, k) for k in self.__dataclass_fields__))
@@ -144,13 +147,10 @@ class BlockDim:
class StatisticWarp(PyTreeNode): class StatisticWarp(PyTreeNode):
"""Derived fields from Statistic.""" """Derived fields from Statistic."""
meaninertia: jax.Array meaninertia: jax.Array
class OptionWarp(PyTreeNode): class OptionWarp(PyTreeNode):
"""Derived fields from Option.""" """Derived fields from Option."""
broadphase: int broadphase: int
broadphase_filter: int broadphase_filter: int
ccd_iterations: int ccd_iterations: int
@@ -159,17 +159,13 @@ class OptionWarp(PyTreeNode):
graph_conditional: bool graph_conditional: bool
graph_mode: GraphMode graph_mode: GraphMode
impratio_invsqrt: jax.Array impratio_invsqrt: jax.Array
ls_parallel: bool
ls_parallel_min_step: float
run_collision_detection: bool run_collision_detection: bool
sdf_initpoints: int sdf_initpoints: int
sdf_iterations: int sdf_iterations: int
sleep_tolerance: jax.Array sleep_tolerance: jax.Array
class ModelWarp(PyTreeNode): class ModelWarp(PyTreeNode):
"""Derived fields from Model.""" """Derived fields from Model."""
D_colind: np.ndarray D_colind: np.ndarray
D_diag: np.ndarray D_diag: np.ndarray
D_rowadr: np.ndarray D_rowadr: np.ndarray
@@ -254,6 +250,7 @@ class ModelWarp(PyTreeNode):
is_sparse: bool is_sparse: bool
jnt_limited_ball_adr: np.ndarray jnt_limited_ball_adr: np.ndarray
jnt_limited_slide_hinge_adr: np.ndarray jnt_limited_slide_hinge_adr: np.ndarray
jtcj_max_pairs: int
light_bodyid: np.ndarray light_bodyid: np.ndarray
light_targetbodyid: np.ndarray light_targetbodyid: np.ndarray
mapD2M: np.ndarray mapD2M: np.ndarray
@@ -351,10 +348,8 @@ class ModelWarp(PyTreeNode):
wrap_site_adr: np.ndarray wrap_site_adr: np.ndarray
wrap_site_pair_adr: np.ndarray wrap_site_pair_adr: np.ndarray
class DataWarp(PyTreeNode): class DataWarp(PyTreeNode):
"""Derived fields from Data.""" """Derived fields from Data."""
M: jax.Array M: jax.Array
actuator_moment: jax.Array actuator_moment: jax.Array
actuator_velocity: jax.Array actuator_velocity: jax.Array
@@ -473,8 +468,6 @@ class DataWarp(PyTreeNode):
wrap_obj: jax.Array wrap_obj: jax.Array
wrap_xpos: jax.Array wrap_xpos: jax.Array
shape = property(lambda self: self.cacc.shape) shape = property(lambda self: self.cacc.shape)
DATA_NON_VMAP = { DATA_NON_VMAP = {
'contact__dim', 'contact__dim',
'contact__dist', 'contact__dist',
@@ -503,7 +496,6 @@ DATA_NON_VMAP = {
'nworld', 'nworld',
} }
def _to_elt(cont, _, d, axis): def _to_elt(cont, _, d, axis):
return DataWarp(**{ return DataWarp(**{
f.name: ( f.name: (
@@ -749,10 +741,14 @@ _NDIM = {
'block_dim__render': 0, 'block_dim__render': 0,
'block_dim__segmented_sort': 0, 'block_dim__segmented_sort': 0,
'block_dim__solve_LD_sparse_fused': 0, 'block_dim__solve_LD_sparse_fused': 0,
'block_dim__solve_beta_accumulate': 0,
'block_dim__solve_init_search_cg': 0,
'block_dim__solve_search_update_cg': 0,
'block_dim__update_gradient_JTDAJ_dense': 0, 'block_dim__update_gradient_JTDAJ_dense': 0,
'block_dim__update_gradient_JTDAJ_sparse': 0, 'block_dim__update_gradient_JTDAJ_sparse': 0,
'block_dim__update_gradient_cholesky': 0, 'block_dim__update_gradient_cholesky': 0,
'block_dim__update_gradient_cholesky_blocked': 0, 'block_dim__update_gradient_cholesky_blocked': 0,
'block_dim__update_gradient_grad': 0,
'body_branch_start': 1, 'body_branch_start': 1,
'body_branches': 1, 'body_branches': 1,
'body_conaffinity': 1, 'body_conaffinity': 1,
@@ -914,6 +910,7 @@ _NDIM = {
'jnt_stiffness': 2, 'jnt_stiffness': 2,
'jnt_stiffnesspoly': 3, 'jnt_stiffnesspoly': 3,
'jnt_type': 1, 'jnt_type': 1,
'jtcj_max_pairs': 0,
'light_active': 2, 'light_active': 2,
'light_ambient': 3, 'light_ambient': 3,
'light_attenuation': 3, 'light_attenuation': 3,
@@ -1043,8 +1040,6 @@ _NDIM = {
'opt__integrator': 0, 'opt__integrator': 0,
'opt__iterations': 0, 'opt__iterations': 0,
'opt__ls_iterations': 0, 'opt__ls_iterations': 0,
'opt__ls_parallel': 0,
'opt__ls_parallel_min_step': 0,
'opt__ls_tolerance': 1, 'opt__ls_tolerance': 1,
'opt__magnetic': 2, 'opt__magnetic': 2,
'opt__run_collision_detection': 0, 'opt__run_collision_detection': 0,
@@ -1170,8 +1165,6 @@ _NDIM = {
'integrator': 0, 'integrator': 0,
'iterations': 0, 'iterations': 0,
'ls_iterations': 0, 'ls_iterations': 0,
'ls_parallel': 0,
'ls_parallel_min_step': 0,
'ls_tolerance': 1, 'ls_tolerance': 1,
'magnetic': 2, 'magnetic': 2,
'run_collision_detection': 0, 'run_collision_detection': 0,
@@ -1407,10 +1400,14 @@ _BATCH_DIM = {
'block_dim__render': False, 'block_dim__render': False,
'block_dim__segmented_sort': False, 'block_dim__segmented_sort': False,
'block_dim__solve_LD_sparse_fused': False, 'block_dim__solve_LD_sparse_fused': False,
'block_dim__solve_beta_accumulate': False,
'block_dim__solve_init_search_cg': False,
'block_dim__solve_search_update_cg': False,
'block_dim__update_gradient_JTDAJ_dense': False, 'block_dim__update_gradient_JTDAJ_dense': False,
'block_dim__update_gradient_JTDAJ_sparse': False, 'block_dim__update_gradient_JTDAJ_sparse': False,
'block_dim__update_gradient_cholesky': False, 'block_dim__update_gradient_cholesky': False,
'block_dim__update_gradient_cholesky_blocked': False, 'block_dim__update_gradient_cholesky_blocked': False,
'block_dim__update_gradient_grad': False,
'body_branch_start': False, 'body_branch_start': False,
'body_branches': False, 'body_branches': False,
'body_conaffinity': False, 'body_conaffinity': False,
@@ -1572,6 +1569,7 @@ _BATCH_DIM = {
'jnt_stiffness': True, 'jnt_stiffness': True,
'jnt_stiffnesspoly': True, 'jnt_stiffnesspoly': True,
'jnt_type': False, 'jnt_type': False,
'jtcj_max_pairs': False,
'light_active': True, 'light_active': True,
'light_ambient': True, 'light_ambient': True,
'light_attenuation': True, 'light_attenuation': True,
@@ -1701,8 +1699,6 @@ _BATCH_DIM = {
'opt__integrator': False, 'opt__integrator': False,
'opt__iterations': False, 'opt__iterations': False,
'opt__ls_iterations': False, 'opt__ls_iterations': False,
'opt__ls_parallel': False,
'opt__ls_parallel_min_step': False,
'opt__ls_tolerance': True, 'opt__ls_tolerance': True,
'opt__magnetic': True, 'opt__magnetic': True,
'opt__run_collision_detection': False, 'opt__run_collision_detection': False,
@@ -1828,8 +1824,6 @@ _BATCH_DIM = {
'integrator': False, 'integrator': False,
'iterations': False, 'iterations': False,
'ls_iterations': False, 'ls_iterations': False,
'ls_parallel': False,
'ls_parallel_min_step': False,
'ls_tolerance': True, 'ls_tolerance': True,
'magnetic': True, 'magnetic': True,
'run_collision_detection': False, 'run_collision_detection': False,