Skip to content

Commit 5d0f405

Browse files
committed
improve code
1 parent cc48045 commit 5d0f405

1 file changed

Lines changed: 62 additions & 66 deletions

File tree

tesseract_core/runtime/experimental.py

Lines changed: 62 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,19 @@ def vjp_from_jacobian(
7373
dict mapping input paths to gradient arrays.
7474
"""
7575
jac = jacobian_fn(inputs=inputs, jac_inputs=vjp_inputs, jac_outputs=vjp_outputs)
76-
out = {}
77-
for dx in vjp_inputs:
78-
grad = None
79-
for dy in vjp_outputs:
80-
J = np.asarray(jac[dy][dx]) # shape: (*dy_shape, *dx_shape)
81-
v = np.asarray(cotangent_vector[dy]) # shape: (*dy_shape)
82-
if diagonal:
83-
diag = np.diag(J.reshape(v.size, v.size))
84-
term = diag.reshape(v.shape) * v
85-
else:
86-
term = np.tensordot(v, J, axes=v.ndim)
87-
grad = term if grad is None else grad + term
88-
out[dx] = grad
89-
return out
76+
77+
def contract(J: np.ndarray, v: np.ndarray) -> np.ndarray:
78+
if diagonal:
79+
return np.diag(J.reshape(v.size, v.size)).reshape(v.shape) * v
80+
return np.tensordot(v, J, axes=v.ndim)
81+
82+
return {
83+
dx: sum(
84+
contract(np.asarray(jac[dy][dx]), np.asarray(cotangent_vector[dy]))
85+
for dy in vjp_outputs
86+
)
87+
for dx in vjp_inputs
88+
}
9089

9190

9291
def jvp_from_jacobian(
@@ -118,20 +117,19 @@ def jvp_from_jacobian(
118117
dict mapping output paths to JVP result arrays.
119118
"""
120119
jac = jacobian_fn(inputs=inputs, jac_inputs=jvp_inputs, jac_outputs=jvp_outputs)
121-
out = {}
122-
for dy in jvp_outputs:
123-
result = None
124-
for dx in jvp_inputs:
125-
J = np.asarray(jac[dy][dx]) # shape: (*dy_shape, *dx_shape)
126-
t = np.asarray(tangent_vector[dx]) # shape: (*dx_shape)
127-
if diagonal:
128-
diag = np.diag(J.reshape(t.size, t.size))
129-
term = diag.reshape(t.shape) * t
130-
else:
131-
term = np.tensordot(J, t, axes=t.ndim)
132-
result = term if result is None else result + term
133-
out[dy] = result
134-
return out
120+
121+
def contract(J: np.ndarray, t: np.ndarray) -> np.ndarray:
122+
if diagonal:
123+
return np.diag(J.reshape(t.size, t.size)).reshape(t.shape) * t
124+
return np.tensordot(J, t, axes=t.ndim)
125+
126+
return {
127+
dy: sum(
128+
contract(np.asarray(jac[dy][dx]), np.asarray(tangent_vector[dx]))
129+
for dx in jvp_inputs
130+
)
131+
for dy in jvp_outputs
132+
}
135133

136134

137135
def jacobian_from_vjp(
@@ -161,33 +159,31 @@ def jacobian_from_vjp(
161159
raw_outputs.model_dump() if hasattr(raw_outputs, "model_dump") else raw_outputs
162160
)
163161

164-
jac = {}
165-
output_vals = {}
166-
for dy in jac_outputs:
167-
dy_val = np.asarray(get_at_path(outputs_dict, dy))
168-
output_vals[dy] = dy_val
169-
jac[dy] = {}
170-
for dx in jac_inputs:
171-
dx_val = np.asarray(get_at_path(inputs, dx))
172-
jac[dy][dx] = np.zeros((*dy_val.shape, *dx_val.shape), dtype=dy_val.dtype)
173-
174-
for dy in jac_outputs:
175-
dy_val = output_vals[dy]
176-
dy_shape = dy_val.shape
177-
for nd_idx in np.ndindex(*dy_shape) if dy_shape else [()]:
178-
cotangent = {dy: np.zeros_like(dy_val)}
179-
if dy_shape:
180-
cotangent[dy][nd_idx] = 1.0
162+
out_vals = {dy: np.asarray(get_at_path(outputs_dict, dy)) for dy in jac_outputs}
163+
in_vals = {dx: np.asarray(get_at_path(inputs, dx)) for dx in jac_inputs}
164+
jac = {
165+
dy: {
166+
dx: np.zeros((*v.shape, *in_vals[dx].shape), dtype=v.dtype)
167+
for dx in jac_inputs
168+
}
169+
for dy, v in out_vals.items()
170+
}
171+
172+
for dy, dy_val in out_vals.items():
173+
for nd_idx in np.ndindex(*dy_val.shape) if dy_val.shape else [()]:
174+
e = np.zeros_like(dy_val)
175+
if dy_val.shape:
176+
e[nd_idx] = 1.0
181177
else:
182-
cotangent[dy] = np.array(1.0, dtype=dy_val.dtype)
178+
e = np.ones((), dtype=dy_val.dtype)
183179
grad = vjp_fn(
184180
inputs=inputs,
185181
vjp_inputs=jac_inputs,
186182
vjp_outputs={dy},
187-
cotangent_vector=cotangent,
183+
cotangent_vector={dy: e},
188184
)
189185
for dx in jac_inputs:
190-
if dy_shape:
186+
if dy_val.shape:
191187
jac[dy][dx][nd_idx] = np.asarray(grad[dx])
192188
else:
193189
jac[dy][dx] = np.asarray(grad[dx])
@@ -221,32 +217,32 @@ def jacobian_from_jvp(
221217
raw_outputs.model_dump() if hasattr(raw_outputs, "model_dump") else raw_outputs
222218
)
223219

224-
jac = {}
225-
for dy in jac_outputs:
226-
dy_val = np.asarray(get_at_path(outputs_dict, dy))
227-
jac[dy] = {}
228-
for dx in jac_inputs:
229-
dx_val = np.asarray(get_at_path(inputs, dx))
230-
jac[dy][dx] = np.zeros((*dy_val.shape, *dx_val.shape), dtype=dy_val.dtype)
231-
232-
for dx in jac_inputs:
233-
dx_val = np.asarray(get_at_path(inputs, dx))
234-
dx_shape = dx_val.shape
235-
for nd_idx in np.ndindex(*dx_shape) if dx_shape else [()]:
236-
tangent = {dx: np.zeros_like(dx_val)}
237-
if dx_shape:
238-
tangent[dx][nd_idx] = 1.0
220+
out_vals = {dy: np.asarray(get_at_path(outputs_dict, dy)) for dy in jac_outputs}
221+
in_vals = {dx: np.asarray(get_at_path(inputs, dx)) for dx in jac_inputs}
222+
jac = {
223+
dy: {
224+
dx: np.zeros((*v.shape, *in_vals[dx].shape), dtype=v.dtype)
225+
for dx in jac_inputs
226+
}
227+
for dy, v in out_vals.items()
228+
}
229+
230+
for dx, dx_val in in_vals.items():
231+
for nd_idx in np.ndindex(*dx_val.shape) if dx_val.shape else [()]:
232+
e = np.zeros_like(dx_val)
233+
if dx_val.shape:
234+
e[nd_idx] = 1.0
239235
else:
240-
tangent[dx] = np.array(1.0, dtype=dx_val.dtype)
236+
e = np.ones((), dtype=dx_val.dtype)
241237
result = jvp_fn(
242238
inputs=inputs,
243239
jvp_inputs={dx},
244240
jvp_outputs=jac_outputs,
245-
tangent_vector=tangent,
241+
tangent_vector={dx: e},
246242
)
247243
for dy in jac_outputs:
248244
dy_result = np.asarray(result[dy])
249-
if dx_shape:
245+
if dx_val.shape:
250246
jac[dy][dx][(..., *nd_idx)] = dy_result
251247
else:
252248
jac[dy][dx] = dy_result

0 commit comments

Comments
 (0)