9using namespace std::literals;
30 auto& world = f->world();
31 auto F = f->type()->as<
Pi>();
32 auto G = g->type()->as<
Pi>();
37 auto A = G->
dom(2, 0);
39 auto C = F->ret_dom();
42 world.log().d(
"compose f: {}: {}, g: {}: {}", f, F, g, G);
43 world.log().d(
" A = {}, B = {}, C = {}", A, B, C);
45 auto name =
"comp_"s + f->sym().str() +
"_" + g->sym().str();
46 auto h = world.mut_fun(A, C)->set(name);
47 auto hcont = world.mut_con(B)->set(name +
"_cont");
49 h->app(
true, g, {h->var(2, 0), hcont});
51 auto hcont_var = hcont->var();
52 hcont->app(
true, f, {hcont_var, h->var(2, 1) });
59 if (
auto res = fe::lookup(augmented, def))
return res;
60 return augmented[def] =
augment_(def, f, f_diff);
64 if (
auto res = fe::lookup(derived, def))
return res;
65 return derived[def] =
derive_(def);
74 auto arg =
rewrite(ad_app->arg());
80 assert(0 &&
"not implemented");
95 auto deriv = world().mut_lam(deriv_ty)->set(lam->sym().str() +
"_deriv");
101 derived[lam] = deriv;
103 auto [arg_ty, ret_pi] = lam->type()->doms<2>();
104 auto deriv_all_args = deriv->var();
105 const Def* deriv_arg = deriv->
var(2, 0)->
set(
"arg");
113 partial_pullback[deriv_arg] = arg_id_pb;
115 auto ret_var = deriv->var(2, 1);
117 partial_pullback[ret_var] = ret_pb;
119 shadow_pullback[deriv_all_args] = world().tuple({arg_id_pb, ret_pb});
124 augmented[def] = deriv;
125 augmented[lam->var()] = deriv->var();
138 auto new_body =
augment(lam->body(), lam, deriv);
139 deriv->set(
true, new_body);
150 partial_pullback[lit] = pb;
157 return partial_pullback[aug];
161 assert(augmented.count(var));
162 assert(partial_pullback.count(augmented[var]));
170 if (augmented.count(lam)) {
175 return augmented[lam];
188 auto cont_dom = lam->
type()->
dom();
191 auto aug_lam = world().mut_con({aug_dom, pb_ty})->set(
"aug_"s + lam->
sym().str());
192 auto aug_var = aug_lam->var(2, 0);
193 augmented[lam->
var()] = aug_var;
194 augmented[lam] = aug_lam;
195 derived[lam] = aug_lam;
196 auto pb = aug_lam->var(2, 1);
197 partial_pullback[aug_var] = pb;
201 aug_lam->set(lam->
filter(), new_body);
204 partial_pullback[aug_lam] = lam_pb;
211 auto aug_lam =
derive(lam);
218 auto index = ext->
index();
221 auto aug_index =
augment(index, f, f_diff);
224 if (shadow_pullback.count(aug_tuple)) {
225 auto shadow_tuple_pb = shadow_pullback[aug_tuple];
226 pb = world().extract(shadow_tuple_pb, aug_index);
233 auto tuple_pb =
pullback(aug_tuple, f);
236 auto pb_fun = world().mut_lam(pb_ty)->set(
"extract_pb");
237 auto pb_tangent = pb_fun->var(2, 0)->set(
"s");
238 auto tuple_tan = world().insert(world().call<zero>(aug_tuple->type()), aug_index, pb_tangent)->set(
"tup_s");
239 pb_fun->app(
true, tuple_pb, {tuple_tan, pb_fun->var(2, 1) });
243 auto aug_ext = world().extract(aug_tuple, aug_index);
244 partial_pullback[aug_ext] = pb;
251 auto aug_ops = tup->
projs([&](
const Def* op) ->
const Def* {
return augment(op, f, f_diff); });
252 auto aug_tup = world().tuple(aug_ops);
256 auto shadow_pb = world().tuple(pbs);
257 shadow_pullback[aug_tup] = shadow_pb;
265 auto pb = world().mut_lam(pb_ty)->set(
"tup_pb");
267 auto pb_tangent = pb->var(2, 0)->set(
"tup_s");
270 pbs.size(), [&](
nat_t i) { return world().app(cps::op_cps2ds_dep(pbs[i]), world().extract(pb_tangent, i)); });
271 pb->app(
true, pb->var(2, 1),
274 partial_pullback[aug_tup] = pb;
280 auto arity = pack->
arity();
281 auto body = pack->
body();
283 auto aug_arity =
augment_(arity, f, f_diff);
284 auto aug_body =
augment(body, f, f_diff);
286 auto aug_pack = world().pack(aug_arity, aug_body);
289 auto body_pb =
pullback(aug_body, f);
290 assert(body_pb &&
"pack pullback should exists");
291 auto pb_pack = world().pack(aug_arity, body_pb);
292 shadow_pullback[aug_pack] = pb_pack;
295 auto pb = world().mut_lam(pb_type)->set(
"pack_pb");
298 auto app_pb = world().mut_pack(world().arr(aug_arity, f_arg_ty_diff))->set_shape(aug_arity);
303 app_pb->set_body(world().app(
cps::op_cps2ds_dep(body_pb), world().extract(pb->var(2, 0), app_pb->var())));
305 auto sumup = world().app(world().
annex<sum>(), {aug_arity, f_arg_ty_diff});
307 pb->app(
true, pb->var(2, 1), world().app(sumup, app_pb));
309 partial_pullback[aug_pack] = pb;
315 auto callee = app->
callee();
316 auto arg = app->
arg();
318 auto aug_arg =
augment(arg, f, f_diff);
319 auto aug_callee =
augment(callee, f, f_diff);
325 if (app->
type()->isa<
Pi>()) {
326 auto aug_app = world().app(aug_callee, aug_arg);
341 auto aug_app = world().app(aug_callee, {aug_arg, arg_pb});
347 auto aug_app = world().app(aug_callee, aug_arg);
350 auto [aug_res, fun_pb] = aug_app->projs<2>();
358 partial_pullback[aug_res] = res_pb;
371 auto g_deriv = aug_callee;
373 auto [real_aug_args, aug_cont] = aug_arg->projs<2>();
374 auto e_pb =
pullback(real_aug_args, f);
377 auto ret_g_deriv_ty = g_deriv->type()->as<
Pi>()->dom(2, 1);
378 auto c1_ty = ret_g_deriv_ty->as<
Pi>();
379 auto c1 = world().mut_lam(c1_ty)->
set(
"c1");
380 auto res = c1->
var(2, 0);
381 auto r_pb = c1->
var(2, 1);
382 c1->app(
true, aug_cont, {res,
compose_cn(e_pb, r_pb)});
384 auto aug_app = world().app(aug_callee, {real_aug_args, c1});
393 if (
auto app = def->isa<
App>()) {
395 }
else if (
auto ext = def->isa<
Extract>()) {
397 }
else if (
auto var = def->isa<
Var>()) {
401 }
else if (
auto lam = def->isa<
Lam>()) {
402 log().e(
"cannot augment an immutable Lam: {}", lam);
403 assert(
false &&
"can not handle non-mutable lambdas");
404 }
else if (
auto lit = def->isa<
Lit>()) {
406 }
else if (
auto tup = def->isa<
Tuple>()) {
408 }
else if (
auto pack = def->isa<
Pack>()) {
411 }
else if (
auto ax = def->isa<
Axm>()) {
412 auto diff_name = ax->sym().str();
413 fe::find_and_replace(diff_name,
".",
"_");
414 diff_name =
"autodiff.diff." + diff_name;
420 log().e(
"no derivative `{}`; expected type: {}", diff_name, expected_type);
421 assert(
false &&
"unhandled axm");
430 log().e(
"cannot augment {} node {}: {}", def->
node_name(), def, def->
type());
431 assert(
false &&
"augment not implemented on this def");
const Def * callee() const
static auto isa(const Def *def)
Def * set(size_t i, const Def *)
Successively set from left to right.
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
std::string_view node_name() const
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
const Def * var(nat_t a, nat_t i) noexcept
auto projs(Projector auto f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
const Def * arity() const
Number of elements available to Extract / Insert (may be dynamic).
const Def * filter() const
static const Lam * isa_basicblock(const Def *d)
A (possibly paramterized) Tuple.
const fe::Log & log() const
A dependent function type.
const Def * ret_dom() const
Pi::domain of Pi::ret_pi.
static const Pi * isa_cn(const Def *d)
static const Pi * isa_basicblock(const Def *d)
Pi * set(const Def *dom, const Def *codom)
static const Pi * isa_returning(const Def *d)
World & old_world()
Get old Defs from here.
const Def * rewrite_imm_App(const App *) override
virtual const Def * rewrite(const Def *)
Data constructor for a Sigma.
A variable introduced by a binder (mutable).
const Def * annex(Sym sym)
Lookup annex by Sym.
const Def * augment_app(const App *, Lam *, Lam *)
const Def * augment_extract(const Extract *, Lam *, Lam *)
const Def * augment_lit(const Lit *, Lam *, Lam *)
const Def * rewrite_imm_App(const App *) final
Detect autodiff calls.
const Def * derive(const Def *)
Acts on toplevel autodiff on closed terms:
const Def * augment_lam(Lam *, Lam *, Lam *)
const Def * augment_tuple(const Tuple *, Lam *, Lam *)
const Def * augment_var(const Var *, Lam *, Lam *)
helper functions for augment
const Def * augment(const Def *, Lam *, Lam *)
Applies to (open) expressions in a functional context.
const Def * augment_pack(const Pack *pack, Lam *f, Lam *f_diff)
const Def * pullback(const Def *aug, Lam *f)
The partial pullback of aug in the closed function f.
const Def * derive_(const Def *)
Additionally to the derivation, the pullback is registered and the maps are initialized.
const Def * augment_(const Def *, Lam *, Lam *)
Rewrites the given definition in a lambda environment.
static const Def * compose_cn(const Def *f, const Def *g)
The high level view is:
const Pi * autodiff_type_fun_pi(const Pi *)
const Def * op_sum(const Def *T, Defs)
const Def * autodiff_type_fun(const Def *)
const Def * tangent_type_fun(const Def *)
const Def * zero_pullback(const Def *E, const Def *A)
const Def * id_pullback(const Def *)
const Pi * pullback_type(const Def *E, const Def *A)
computes pb type E* -> A* E - type of the expression (return type for a function) A - type of the arg...
const Def * op_cps2ds_dep(const Def *k)
fe::View< const Def * > Defs
fe::Vector< const Def * > DefVec