MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
autodiff.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4
5#include <mim/config.h>
6#include <mim/phase.h>
7
8#include <mim/plug/mem/mem.h>
9
11
12using namespace std::literals;
13using namespace mim;
14using namespace mim::plug;
15
16static void reg_phases(Flags2Phases& phases) {
18
20 if (auto zero = Axm::isa<autodiff::zero>(def); zero) {
21 if (auto z = autodiff::zero_def(zero->arg())) return z;
22 }
23 return {};
24 });
25}
26
28 plugin.register_normalizers = autodiff::register_normalizers;
29 plugin.register_phases = reg_phases;
30}
31
32namespace mim::plug::autodiff {
33const Def* id_pullback(const Def* A) {
34 auto& world = A->world();
35 auto arg_pb_ty = pullback_type(A, A);
36 auto id_pb = world.mut_lam(arg_pb_ty)->set("id_pb");
37 auto id_pb_scalar = id_pb->var(2, 0)->set("s");
38 id_pb->app(true, id_pb->var(2, 1), // can not use ret_var as the result might be higher order
39 id_pb_scalar);
40
41 return id_pb;
42}
43
44const Def* zero_pullback(const Def* E, const Def* A) {
45 auto& world = A->world();
46 auto A_tangent = tangent_type_fun(A);
47 auto pb_ty = pullback_type(E, A);
48 auto pb = world.mut_lam(pb_ty)->set("zero_pb");
49 pb->app(true, pb->var(2, 1), world.call<zero>(A_tangent));
50 return pb;
51}
52
53// `P` => `P*`
54// TODO: nothing? function => R? Mem => R?
55// TODO: rename to op_tangent_type
56const Def* tangent_type_fun(const Def* ty) { return ty; }
57
58/// computes pb type `E* -> A*`
59/// `E` - type of the expression (return type for a function)
60/// `A` - type of the argument (point of orientation resp. derivative - argument type for partial pullbacks)
61const Pi* pullback_type(const Def* E, const Def* A) {
62 auto& world = E->world();
63 auto tang_arg = tangent_type_fun(A);
64 auto tang_ret = tangent_type_fun(E);
65 auto pb_ty = world.cn({tang_ret, world.cn(tang_arg)});
66 return pb_ty;
67}
68
69namespace {
70// `A,R` => `(A->R)' = A' -> R' * (R* -> A*)`
71const Pi* autodiff_type_fun(const Def* arg, const Def* ret) {
72 auto& world = arg->world();
75 if (!aug_arg || !aug_ret) return nullptr;
76 // `Q* -> P*`
77 auto pb_ty = pullback_type(ret, arg);
78 // `P' -> Q' * (Q* -> P*)`
79
80 auto deriv_ty = world.cn({aug_arg, world.cn({aug_ret, pb_ty})});
81 return deriv_ty;
82}
83} // namespace
84
85const Pi* autodiff_type_fun_pi(const Pi* pi) {
86 auto& world = pi->world();
87 if (!Pi::isa_cn(pi)) {
88 // TODO: dependency
89 auto arg = pi->dom();
90 auto ret = pi->codom();
91 if (ret->isa<Pi>()) {
92 auto aug_arg = autodiff_type_fun(arg);
93 if (!aug_arg) return nullptr;
94 auto aug_ret = autodiff_type_fun(pi->codom());
95 if (!aug_ret) return nullptr;
96 return world.pi(aug_arg, aug_ret);
97 }
98 return autodiff_type_fun(arg, ret);
99 }
100 auto [arg, ret_pi] = pi->doms<2>();
101 auto ret = ret_pi->as<Pi>()->dom();
102 return autodiff_type_fun(arg, ret);
103}
104
105// In general transforms `A` => `A'`.
106// Especially `P->Q` => `P'->Q' * (Q* -> P*)`.
107const Def* autodiff_type_fun(const Def* ty) {
108 auto& world = ty->world();
109 // TODO: handle DS (operators)
110 if (auto pi = ty->isa<Pi>()) return autodiff_type_fun_pi(pi);
111 // Also handles autodiff call from axm declaration => abstract => leave it.
112 if (Idx::isa(ty)) return ty;
113 if (ty == world.type_nat()) return ty;
114 if (auto arr = ty->isa<Arr>()) {
115 auto shape = arr->shape();
116 auto body = arr->body();
117 auto body_ad = autodiff_type_fun(body);
118 if (!body_ad) return nullptr;
119 return world.arr(shape, body_ad);
120 }
121 if (auto sig = ty->isa<Sigma>()) {
122 // TODO: mut sigma
123 auto ops = DefVec(sig->ops(), [&](const Def* op) { return autodiff_type_fun(op); });
124 if (std::ranges::contains(ops, nullptr)) return nullptr;
125 return world.sigma(ops);
126 }
127 // mem
128 if (Axm::isa<mem::M>(ty)) return ty;
129 world.log().w("not differentiable: {}", ty);
130 return nullptr;
131}
132
133const Def* zero_def(const Def* T) {
134 // TODO: we want: zero mem -> zero mem or bot
135 // zero [A,B,C] -> [zero A, zero B, zero C]
136 auto& world = T->world();
137 if (auto arr = T->isa<Arr>()) {
138 auto shape = arr->shape();
139 auto body = arr->body();
140 auto inner_zero = world.app(world.annex<zero>(), body);
141 auto zero_arr = world.pack(shape, inner_zero);
142 return zero_arr;
143 } else if (Idx::isa(T)) {
144 // TODO: real
145 auto zero = world.lit(T, 0)->set("zero");
146 return zero;
147 } else if (auto sig = T->isa<Sigma>()) {
148 auto ops = DefVec(sig->ops(), [&](const Def* op) { return world.app(world.annex<zero>(), op); });
149 return world.tuple(ops);
150 }
151 return nullptr;
152}
153
154const Def* op_sum(const Def* T, Defs defs) {
155 // TODO: assert all are of type T
156 auto& world = T->world();
157 return world.app(world.app(world.annex<sum>(), {world.lit_nat(defs.size()), T}), defs);
158}
159
160} // namespace mim::plug::autodiff
static void reg_phases(Flags2Phases &phases)
Definition autodiff.cpp:16
A (possibly paramterized) Array.
Definition tuple.h:305
static auto isa(const Def *def)
Definition axm.h:112
Base class for all Defs.
Definition def.h:289
World & world() const noexcept
Definition def.h:1105
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:670
static void hook(Flags2Phases &phases)
Definition phase.h:51
A dependent function type.
Definition lam.h:14
auto doms(Projector auto f) const noexcept
Definition lam.h:40
static const Pi * isa_cn(const Def *d)
Definition lam.h:50
const Def * dom() const
Definition lam.h:38
const Def * codom() const
Definition lam.h:39
A dependent tuple type.
Definition tuple.h:39
const Def * app(const Def *callee, const Def *arg)
Definition world.cpp:416
The automatic differentiation Plugin
Definition autodiff.h:7
const Pi * autodiff_type_fun_pi(const Pi *)
Definition autodiff.cpp:85
const Def * op_sum(const Def *T, Defs)
Definition autodiff.cpp:154
const Def * autodiff_type_fun(const Def *)
Definition autodiff.cpp:107
const Def * zero_def(const Def *T)
Definition autodiff.cpp:133
const Def * tangent_type_fun(const Def *)
Definition autodiff.cpp:56
const Def * zero_pullback(const Def *E, const Def *A)
Definition autodiff.cpp:44
const Def * id_pullback(const Def *)
Definition autodiff.cpp:33
void register_normalizers(Normalizers &normalizers)
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...
Definition autodiff.cpp:61
Definition ast.h:16
fe::View< const Def * > Defs
Definition def.h:96
fe::Vector< const Def * > DefVec
Definition def.h:98
ankerl::unordered_dense::map< flags_t, std::function< std::unique_ptr< Phase >(World &)> > Flags2Phases
Maps an axiom of a Phase to a function that creates one.
Definition plugin.h:30
#define MIM_REPL(__phases, __annex,...)
Definition phase.h:457
#define MIM_PLUGIN_ENTRY(p)
Defines a Plugin's entry point; the body fills in the plugin handed to it, as in MIM_PLUGIN_ENTRY(dem...
Definition plugin.h:196