{"id":26633550,"url":"https://github.com/vincent-picaud/missionimpossible","last_synced_at":"2025-10-10T19:15:51.916Z","repository":{"id":165658975,"uuid":"227103966","full_name":"vincent-picaud/MissionImpossible","owner":"vincent-picaud","description":"A concise C++17 implementation of automatic differentiation (operator overloading)","archived":false,"fork":false,"pushed_at":"2022-03-16T07:37:48.000Z","size":270,"stargazers_count":21,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-10T17:59:46.277Z","etag":null,"topics":["autodiff","automatic-differentiation","cpp","cpp17"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vincent-picaud.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-12-10T11:33:32.000Z","updated_at":"2024-10-01T13:35:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"cff2bcea-5c7b-44e3-a8b5-33299b6a34aa","html_url":"https://github.com/vincent-picaud/MissionImpossible","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/vincent-picaud/MissionImpossible","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vincent-picaud%2FMissionImpossible","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vincent-picaud%2FMissionImpossible/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vincent-picaud%2FMissionImpossible/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vincent-picaud%2FMissionImpossible/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vincent-picaud","download_url":"https://codeload.github.com/vincent-picaud/MissionImpossible/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vincent-picaud%2FMissionImpossible/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279005038,"owners_count":26083827,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autodiff","automatic-differentiation","cpp","cpp17"],"created_at":"2025-03-24T15:15:28.508Z","updated_at":"2025-10-10T19:15:51.910Z","avatar_url":"https://github.com/vincent-picaud.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+OPTIONS: H:3 toc:t \\n:nil ::t |:t ^:{} -:t f:t *:t tex:t d:t tags:not-in-toc\n#+HTML_HEAD_EXTRA: \u003cstyle type=\"text/css\"\u003e blockquote {background:#EEEEEE; padding: 3px 13px}    \u003c/style\u003e\n#+HTML_HEAD_EXTRA: \u003cstyle type=\"text/css\"\u003e pre {background:#EEEEEE; padding: 3px 13px}    \u003c/style\u003e\n#+TITLE: Mission : Impossible (AutoDiff)\n\n[[https://travis-ci.org/vincent-picaud/MissionImpossible][file:https://travis-ci.org/vincent-picaud/MissionImpossible.svg?branch=master]]\n\n* Table of contents                                            :TOC:noexport:\n- [[#what-is-it][What is it?]]\n  - [[#news][News]]\n- [[#compilation][Compilation]]\n  - [[#meson][Meson]]\n  - [[#cmake][CMake]]\n- [[#examples][Examples]]\n  - [[#jacobian-example][Jacobian example]]\n  - [[#complex-number-example][Complex number example]]\n  - [[#hessian-action-hv-directional-derivatives][Hessian action Hv, directional derivatives]]\n  - [[#third-order-example][Third order example]]\n- [[#mission--impossible-survival-guide][Mission : Impossible, survival guide]]\n  - [[#ad-types][AD types]]\n  - [[#computing-derivatives][Computing derivatives]]\n  - [[#tape][Tape]]\n  - [[#local-tape][Local Tape]]\n\n* What is it?\n\n  *Mission : Impossible* is a concise header only C++17 implementation of\n  automatic differentiation based on operator overloading. For repeated\n  calculations it is very easy to define an auto-destructive local tape,\n  hence the wink to the \"Mission: Impossible\" series.\n\n  [[file:figures/tape.jpeg][file:./figures/tape.jpeg]]\n\n  Illustrates local tape usage:\n\n  #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/local_tape.cpp\n  #+END_SRC\n\n  #+RESULTS:\n  #+BEGIN_src cpp :eval never\n  #include \"MissionImpossible/MissionImpossible.hpp\"\n\n  #include \u003carray\u003e\n  #include \u003ciostream\u003e\n\n  using namespace MissionImpossible;\n\n  // A C++ function\n  template \u003ctypename T\u003e\n  T\n  Rosenbrock(const std::array\u003cT, 2\u003e\u0026 X)\n  {\n    return (1 - X[0]) * (1 - X[0]) + 10 * (X[1] - X[0] * X[0]) * (X[1] - X[0] * X[0]);\n  }\n\n  // A C++ function that adds gradient computation\n  template \u003ctypename T\u003e\n  T\n  Rosenbrock(const std::array\u003cT, 2\u003e\u0026 X, std::array\u003cT, 2\u003e\u0026 grad)\n  {\n    MissionImpossible_Tape\u003cT\u003e local_tape;  // a local thread_local tape\n\n    std::array\u003cAD\u003cT\u003e, 2\u003e ad_X;\n\n    ad_X[0] = X[0];\n    ad_X[1] = X[1];\n\n    AD\u003cT\u003e ad_f = Rosenbrock(ad_X);\n\n    Tape_Vector\u003cT\u003e ad_grad_f = gradient(local_tape, ad_f);  // use the local tape for ∇f\n\n    grad[0] = ad_grad_f[ad_X[0]];\n    grad[1] = ad_grad_f[ad_X[1]];\n\n    return ad_f.value();\n\n    // here the local tape is destroyed (in fact re-winded to avoid\n    // useless new/delete)\n  }\n\n  int\n  main()\n  {\n    std::array\u003cfloat, 2\u003e X{3., 4.};\n    std::array\u003cfloat, 2\u003e grad;\n\n    float f = Rosenbrock(X, grad);\n\n    std::cout \u003c\u003c \"f  = \" \u003c\u003c f \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"∇f = [ \" \u003c\u003c grad[0] \u003c\u003c \", \" \u003c\u003c grad[1] \u003c\u003c \" ]\" \u003c\u003c std::endl;\n  }\n  #+END_src\n\n  prints\n\n  #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\n$(pwd)/build/examples/local_tape\n  #+END_SRC\n\n  #+RESULTS:\n  #+BEGIN_src cpp :eval never\n  f  = 254\n  ∇f = [ 604, -100 ]\n  #+END_src\n\n  This library has been optimized for unstructured (= not vectorized)\n  first order derivatives. Its speed must be comparable to [[https://github.com/rjhogan/Adept-2][Adept]] as it\n  relies on the same kind of approach [1].\n\n  #+begin_quote\n  [1], Srajer, Filip, Zuzana Kukelova, and Andrew Fitzgibbon. \"A\n  benchmark of selected algorithmic differentiation tools on some\n  problems in computer vision and machine learning.\" Optimization\n  Methods and Software 33.4-6 (2018): 889-906.\n  #+end_quote\n\n  The library supports arbitrary derivatives order but has not been\n  optimized for that. By example for second order derivatives the\n  symmetry ∂ij=∂ji is not taken into account. This leads to a fast\n  growing number of redundant computations as derivation order increases\n  (except for a function of _one_ variable).  For higher order\n  derivatives, dedicated approaches like [2,3] are more effective.\n\n  #+begin_quote\n  [2], Wang, Mu, Assefaw Gebremedhin, and Alex Pothen. \"Capitalizing on\n  live variables: new algorithms for efficient Hessian computation via\n  automatic differentiation.\" Mathematical Programming Computation 8.4\n  (2016): 393-433.\n  #+end_quote\n\n  #+begin_quote\n  [3], Gower, Robert Mansel, and Artur L. Gower. \"Higher-order reverse\n  automatic differentiation with emphasis on the third-order.\"\n  Mathematical Programming 155.1-2 (2016): 81-103.\n  #+end_quote\n\n** News\n\n  - [2022-03-16 Wed 08:32] \\\\\n    min/max and complex comparison bugs fix thanks to Léopold Delahaye\n  \n  - [2020-02-09 Sun 19:44] \\\\\n    Adding a CMake build solution + CI + code cleaning. *v0.1.1*.\n\n  - [2020-02-09 Sun 11:56] \\\\\n    After an effort to provide some documentation and a =sqrt()= bug\n    fix (thanks to Fabrice Gaudier), I think it's time to release the\n    *v0.1.0* version!\n\n  - [2020-01-07 Tue 12:27] \\\\\n    This a *pre-release*. Some developments remain to be done\n    (implementing special functions, adding examples and benchmarks)\n    but the design and API is not expected to change a lot.\n\n* Compilation\n\n** Meson \n\n   The library main build tool is the [[https://mesonbuild.com/][meson]] build system.\n\n   If you are not familiar with meson, the compilation procedure is as\n   follows:\n\n   #+BEGIN_SRC sh :eval never\ngit clone https://github.com/vincent-picaud/MissionImpossible.git\ncd MissionImpossible/\nmeson build\ncd build\nninja test\n   #+END_SRC \n\n   To get an *optimized* version, use:\n\n   #+BEGIN_SRC sh :eval never\nmeson --buildtype=release -Db_ndebug=true build-release\n   #+END_SRC\n\n** CMake \n\nFor your convenience I also provide CMake solution.\n\n*Note:* the CMake solution does not run tests.\n\n* Examples\n\n  These examples can be found in the =build/examples/= directory.\n\n** Jacobian example\n\n   Illustrates *forward-mode* and *reverse-mode* support. The first one is\n   convenient to compute the Jacobian column by column. The second one is\n   effective to compute gradients (or equivalently to compute the\n   Jacobian row by row).\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/Jacobian.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   #include \u003ciostream\u003e\n\n   using namespace MissionImpossible;\n\n   int\n   main()\n   {\n     AD\u003cdouble\u003e r = 2, theta = 0.1;\n\n     AD\u003cdouble\u003e y1 = r * cos(theta);\n     AD\u003cdouble\u003e y2 = r * sin(theta);\n\n     //////////////////////////////////\n     // Computes Jacobian row by row //\n     //////////////////////////////////\n     //\n     // -\u003e AKA reverse-mode\n     //\n     std::cout \u003c\u003c \"Jacobian row by row\" \u003c\u003c std::endl;\n\n     auto Jacobian_row_y1 = Jacobian_row(y1);  // ∇y1 (or equivalently gradient(y1))\n\t\t\t\t\t       // computes ∂ᵣy¹, ∂ₒy¹\n\n     auto Jacobian_row_y2 = Jacobian_row(y2);  // ∇y2 (or equivalently gradient(y2))\n\t\t\t\t\t       // computes ∂ᵣy², ∂ₒy²\n\n     std::cout \u003c\u003c \"∇y1(r,θ) = [ ∂ᵣy¹, ∂ₒy¹ ] = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_row_y1[r] \u003c\u003c \", \";\n     std::cout \u003c\u003c std::setw(20) \u003c\u003c Jacobian_row_y1[theta] \u003c\u003c std::endl;\n\n     std::cout \u003c\u003c \"∇y2(r,θ) = [ ∂ᵣy², ∂ₒy² ] = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_row_y2[r] \u003c\u003c \", \";\n     std::cout \u003c\u003c std::setw(20) \u003c\u003c Jacobian_row_y2[theta] \u003c\u003c std::endl;\n\n     ////////////////////////////////////////\n     // Computes Jacobian column by column //\n     ////////////////////////////////////////\n     //\n     // -\u003e AKA forward-mode\n     //\n     std::cout \u003c\u003c std::endl \u003c\u003c \"Jacobian column by column\" \u003c\u003c std::endl;\n\n     auto Jacobian_column_r     = Jacobian_column(r);      // r column computes ∂ᵣy¹, ∂ᵣy²\n     auto Jacobian_column_theta = Jacobian_column(theta);  // θ column compules ∂ₒy¹, ∂ₒy²\n\n     std::cout \u003c\u003c \"∂ᵣy¹ = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_column_r[y1] \u003c\u003c \"\\t\"\n\t       \u003c\u003c \"∂ᵣy² = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_column_theta[y1] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂ₒy¹ = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_column_r[y2] \u003c\u003c \"\\t\"\n\t       \u003c\u003c \"∂ₒy² = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_column_theta[y2] \u003c\u003c std::endl;\n   }\n   #+END_src\n\n   prints\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/Jacobian\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_example\n   Jacobian row by row\n   ∇y1(r,θ) = [ ∂ᵣy¹, ∂ₒy¹ ] =             0.995004,            -0.199667\n   ∇y2(r,θ) = [ ∂ᵣy², ∂ₒy² ] =            0.0998334,              1.99001\n\n   Jacobian column by column\n   ∂ᵣy¹ =             0.995004\t∂ᵣy² =            -0.199667\n   ∂ₒy¹ =            0.0998334\t∂ₒy² =              1.99001\n   #+END_example\n\n\n** Complex number example\n\n   Illustrates complex number support:\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/ad_complex.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   #include \u003ccomplex\u003e\n   #include \u003ciostream\u003e\n\n   using namespace MissionImpossible;\n\n   void\n   most_efficient()\n   {\n     using T = std::complex\u003cdouble\u003e;\n\n     AD\u003cT\u003e z0 = T(1, 2), Z;\n\n     Z = 4 * exp(2 * z0 * z0);\n\n     auto dZ = gradient(Z);\n\n     std::cout \u003c\u003c \" f = \" \u003c\u003c Z \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"df = \" \u003c\u003c dZ[z0] \u003c\u003c std::endl;\n   }\n\n   template \u003ctypename F\u003e\n   void\n   more_versatile(F f)\n   {\n     AD\u003cdouble\u003e x(1), y(2);\n     std::complex\u003cAD\u003cdouble\u003e\u003e z0(x, y), Z;\n\n     Z = f(z0);\n\n     AD\u003cdouble\u003e u = Z.real(), v = Z.imag();\n\n     const auto grad_u = gradient(u);\n\n     // assumes that Z is holomorph\n     //\n     std::cout \u003c\u003c \" f = \" \u003c\u003c Z \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"df = \" \u003c\u003c grad_u[x] \u003c\u003c \", \";\n     std::cout \u003c\u003c -grad_u[y] \u003c\u003c std::endl;\n\n     // Cauchy-Riemann\n     //\n     const auto grad_v = gradient(v);\n\n     std::cout \u003c\u003c \"--\u003e Cauchy-Riemann check:\" \u003c\u003c std::endl;\n     std::cout \u003c\u003c grad_u[x] \u003c\u003c \" ?= \" \u003c\u003c grad_v[y] \u003c\u003c std::endl;\n     std::cout \u003c\u003c grad_u[y] \u003c\u003c \" ?= \" \u003c\u003c -grad_v[x] \u003c\u003c std::endl;\n   }\n\n   int\n   main()\n   {\n     std::cout \u003c\u003c \"          f1:   \" \u003c\u003c std::endl;\n     most_efficient();\n\n     //================\n\n     auto f_holomorph     = [](const auto\u0026 z) { return 4 * exp(2 * z * z); };\n     auto f_not_holomorph = [](const auto\u0026 z) { return sqrt(z * conj(z)); };\n\n     std::cout \u003c\u003c std::endl \u003c\u003c \"Holomorph f1:   \" \u003c\u003c std::endl;\n     more_versatile(f_holomorph);\n\n     std::cout \u003c\u003c std::endl \u003c\u003c \"Not holomorph f2: \" \u003c\u003c std::endl;\n     more_versatile(f_not_holomorph);\n   }\n   #+END_src\n\n   prints:\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/ad_complex\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_example\n\t     f1:   \n    f = (-0.00144263,+0.0098095)\n   df = (-0.0842465,+0.0276969)\n\n   Holomorph f1:   \n    f = (-0.00144263,+0.0098095)\n   df = -0.0842465, +0.0276969\n   --\u003e Cauchy-Riemann check:\n   -0.0842465 ?= -0.0842465\n   -0.0276969 ?= -0.0276969\n\n   Not holomorph f2: \n    f = (+2.23607,+0)\n   df = +0.447214, -0.894427\n   --\u003e Cauchy-Riemann check:\n   +0.447214 ?= +0\n   +0.894427 ?= -0\n   #+END_example\n\n** Hessian action Hv, directional derivatives\n\n   Illustrates Hessian action Hv=∇ \u003c∇f,v\u003e computation:\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/Hv.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   using namespace MissionImpossible;\n\n   int\n   main()\n   {\n     AD\u003cAD\u003cdouble\u003e\u003e x0(3), x1(4), y;\n\n     y = (1 - x0) * (1 - x0) + 10 * (x1 - x0 * x0) * (x1 - x0 * x0);\n\n     std::cout \u003c\u003c \"f = \" \u003c\u003c y \u003c\u003c std::endl;\n\n     auto y_gradient = gradient(y);  // Computes ∇f\n\n     std::cout \u003c\u003c \"∇f= \" \u003c\u003c y_gradient[x0] \u003c\u003c \", \";\n     std::cout \u003c\u003c y_gradient[x1] \u003c\u003c std::endl;\n\n     AD\u003cdouble\u003e z;\n\n     double v0(5), v1(6);\n\n     z = v0 * y_gradient[x0] + v1 * y_gradient[x1];  // Computes z=\u003c∇f,v\u003e\n\n     auto z_gradient = gradient(z);  // Computes Hv = ∇z = ∇ \u003c∇f,v\u003e\n\n     std::cout \u003c\u003c \"Hv= \" \u003c\u003c z_gradient[x0] \u003c\u003c \", \";\n     std::cout \u003c\u003c z_gradient[x1] \u003c\u003c std::endl;\n   }\n   #+END_src\n\n   prints\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/Hv\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_example\n   f = +254\n   ∇f= +604, -100\n   Hv= +3890, -480\n   #+END_example\n\n** Third order example \n\n   Illustrates nested computations support\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/nested.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   #include \u003ciostream\u003e\n\n   using namespace MissionImpossible;\n\n   template \u003ctypename T\u003e\n   auto\n   Rosenbrock(const T\u0026 x0, const T\u0026 x1)\n   {\n     return (1 - x0) * (1 - x0) + 10 * (x1 - x0 * x0) * (x1 - x0 * x0);\n   }\n\n   // Third order demo\n   int\n   main()\n   {\n     AD\u003cAD\u003cAD\u003cdouble\u003e\u003e\u003e x0(3), x1(4), y;\n\n     y = Rosenbrock(x0, x1);\n\n     auto grad = gradient(y);\n\n     auto Hessian_x0_row = gradient(grad[x0]);\n     auto Hessian_x1_row = gradient(grad[x1]);\n\n     auto third_order_x0_x0_row = gradient(Hessian_x0_row[x0]);\n     auto third_order_x0_x1_row = gradient(Hessian_x0_row[x1]);\n     auto third_order_x1_x0_row = gradient(Hessian_x1_row[x0]);\n     auto third_order_x1_x1_row = gradient(Hessian_x1_row[x1]);\n\n     std::cout \u003c\u003c \"f     = \" \u003c\u003c y \u003c\u003c std::endl;\n     std::cout \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂₀f   = \" \u003c\u003c grad[x0] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂₁f   = \" \u003c\u003c grad[x1] \u003c\u003c std::endl;\n     std::cout \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂²₀₀f = \" \u003c\u003c Hessian_x0_row[x0] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂²₀₁f = \" \u003c\u003c Hessian_x0_row[x1] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂²₁₀f = \" \u003c\u003c Hessian_x1_row[x0] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂²₁₁f = \" \u003c\u003c Hessian_x1_row[x1] \u003c\u003c std::endl;\n     std::cout \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂³₀₀₀f = \" \u003c\u003c third_order_x0_x0_row[x0] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂³₀₀₁f = \" \u003c\u003c third_order_x0_x0_row[x1] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂³₀₁₀f = \" \u003c\u003c third_order_x0_x1_row[x0] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂³₀₁₁f = \" \u003c\u003c third_order_x0_x1_row[x1] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂³₁₀₀f = \" \u003c\u003c third_order_x1_x0_row[x0] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂³₁₀₁f = \" \u003c\u003c third_order_x1_x0_row[x1] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂³₁₁₀f = \" \u003c\u003c third_order_x1_x1_row[x0] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂³₁₁₁f = \" \u003c\u003c third_order_x1_x1_row[x1] \u003c\u003c std::endl;\n   }\n   #+END_src\n\n   which prints\n\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/nested\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_example\n   f     = +254\n\n   ∂₀f   = +604\n   ∂₁f   = -100\n\n   ∂²₀₀f = +922\n   ∂²₀₁f = -120\n   ∂²₁₀f = -120\n   ∂²₁₁f = +20\n\n   ∂³₀₀₀f = +720\n   ∂³₀₀₁f = -40\n   ∂³₀₁₀f = -40\n   ∂³₀₁₁f = +0\n   ∂³₁₀₀f = -40\n   ∂³₁₀₁f = +0\n   ∂³₁₁₀f = +0\n   ∂³₁₁₁f = +0\n   #+END_example\n\n   # figures/tape.jpeg http://pixorblog.files.wordpress.com/2020/01/tape.jpeg\n   # ./figures/tape.jpeg http://pixorblog.files.wordpress.com/2020/01/tape-1.jpeg\n\n* Mission : Impossible, survival guide\n\n  This part focuses on the things to know to properly use this library.\n\n** AD types\n\n   To compute derivatives you must use =AD\u003cT\u003e= types in place of the usual\n   =T= types (where =T= represents a real type like =float= or =double=):\n   - =AD\u003cT\u003e= for first order derivatives\n   - =AD\u003cAD\u003cT\u003e\u003e= for second order derivatives\n   - =AD\u003cAD\u003cAD\u003cT\u003e\u003e\u003e= for third order derivatives\n   - ...\n\n   *Note:* you must *always* initialize =AD\u003cT\u003e= variables before using them (in\n   order to register them in the tape).\n\n   *Example:*\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/doc/ad.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   using namespace MissionImpossible;\n\n   int\n   main()\n   {\n     // GOOD\n     //================\n     AD\u003cdouble\u003e x1, y1;\n\n     x1 = 1;       // initializes x1\n     y1 = 2 * x1;  // before usage\n\n     auto grad1 = gradient(y1);  // OK\n\n     // BAD\n     //================\n     AD\u003cdouble\u003e x2, y2;\n\n     y2 = 2 * x2;  // use of x2 without initialization\n                   // triggers an assert(0) in DEBUG mode\n\n     auto grad2 = gradient(y2); // undefined behavior\n   }\n   #+END_src\n\n*** Constant scalar parameter \n\n    The origin of the problem is not attached to this library, by example:\n\n    #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/doc/underlying_type.cpp\n    #+END_SRC\n\n    #+RESULTS:\n    #+BEGIN_src cpp :eval never\n    #include \u003cvector\u003e\n\n    // BAD\n    template \u003ctypename T\u003e\n    void\n    scale_v1(const T scalar, std::vector\u003cT\u003e\u0026 v)\n    {\n      // version 1\n    }\n\n    // GOOD\n    template \u003ctypename T\u003e\n    void\n    scale_v2(const typename std::vector\u003cT\u003e::value_type scalar, std::vector\u003cT\u003e\u0026 v)\n    {\n      // version 2\n    }\n\n    int\n    main()\n    {\n      std::vector\u003cdouble\u003e v(10);\n\n      scale_v1(2, v);  // \u003c- does not compile\n                       // \"...deduced conflicting types for parameter ‘T’ (‘int’ and ‘double’)...\"\n\n      scale_v2(2, v);  // \u003c- OK\n    }\n    #+END_src\n\n    The use of =typename std::vector\u003cT\u003e::value_type= avoids type conflict as\n    now only one expression (here =std::vector\u003cT\u003e=) is used to deduce the\n    type of T (further detail: [[https://en.cppreference.com/w/cpp/types/type_identity][cppreference: type_identity]]).\n\n    Back to this \"Mission : Impossible\" library, if one wants to define a\n    function that takes a *scalar* constant =10= and computes =10*x*x=, you must\n    use =Underlying_Type_t= (as a emplacement of =typename\n    std::vector\u003cT\u003e::value_type= in the previous example):\n\n    #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/doc/underlying_type_2.cpp\n    #+END_SRC\n\n    #+RESULTS:\n    #+BEGIN_src cpp :eval never\n    #include \"MissionImpossible/MissionImpossible.hpp\"\n\n    using namespace MissionImpossible;\n\n    template \u003ctypename T\u003e\n    T\n    my_function(const AD_Underlying_Type_t\u003cT\u003e scalar_constant, const T x)\n    {\n      return scalar_constant * x * x;\n    }\n\n    int\n    main()\n    {\n      AD\u003cAD\u003cdouble\u003e\u003e x = 2, y;\n\n      y = my_function(10, x);\n\n      auto dy  = Jacobian_row(y);      // auto = Tape_Vector\u003cAD\u003cdouble\u003e\u003e\n      auto d2y = Jacobian_row(dy[x]);  // auto = Tape_Vector\u003cdouble\u003e\n\n      std::cout \u003c\u003c \"y   = \" \u003c\u003c y \u003c\u003c std::endl;\n      std::cout \u003c\u003c \"dy  = \" \u003c\u003c dy[x] \u003c\u003c \" dx\" \u003c\u003c std::endl;\n      std::cout \u003c\u003c \"d2y = \" \u003c\u003c d2y[x] \u003c\u003c \" dx⊗dx\" \u003c\u003c std::endl;\n    }\n    #+END_src\n\n    which prints:\n\n    #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/doc/underlying_type_2\n    #+END_SRC\n\n    #+RESULTS:\n    #+BEGIN_example\n    y   = +40\n    dy  = +40 dx\n    d2y = +20 dx⊗dx\n    #+END_example\n\n*** =underlying_value()=\n\n    Maybe the last function to know, but to use with care (as it shortcuts the flow of \n    tape recording), is =underlying_value()=. This function\n    returns the underlying stored value. By example:\n\n    #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/doc/underlying_value.cpp\n    #+END_SRC\n\n    #+RESULTS:\n    #+BEGIN_src cpp :eval never\n    #include \"MissionImpossible/MissionImpossible.hpp\"\n\n    using namespace MissionImpossible;\n\n    int\n    main()\n    {\n      AD\u003cAD\u003cdouble\u003e\u003e x = 2, y;\n\n      y = 10 * x * x;\n\n      auto dy  = Jacobian_row(y);      // auto = Tape_Vector\u003cAD\u003cdouble\u003e\u003e\n      auto d2y = Jacobian_row(dy[x]);  // auto = Tape_Vector\u003cdouble\u003e\n\n      double value_y   = underlying_value(y);\n      double value_dy  = underlying_value(dy[x]);\n      double value_d2y = underlying_value(d2y[x]);\n\n      std::cout \u003c\u003c \"y   = \" \u003c\u003c value_y \u003c\u003c std::endl;\n      std::cout \u003c\u003c \"dy  = \" \u003c\u003c value_dy \u003c\u003c \" dx\" \u003c\u003c std::endl;\n      std::cout \u003c\u003c \"d2y = \" \u003c\u003c value_d2y \u003c\u003c \" dx⊗dx\" \u003c\u003c std::endl;\n    }\n    #+END_src\n\n    #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/doc/underlying_value\n    #+END_SRC\n\n    #+RESULTS:\n    #+BEGIN_example\n    y   = 40\n    dy  = 40 dx\n    d2y = 20 dx⊗dx\n    #+END_example\n\n** Computing derivatives\n\n   A differential evaluated at point X, dfₓ is a linear application\n   that can be represented (given basis) by a matrix (also known as\n   Jacobian) of components ∂ⱼfⁱ where i denotes rows and j columns.\n   You can compute the Jacobian:\n   - row by row using the =Jacobian_row()= function (fix i and compute ∂ⱼfⁱ\n     for all j)\n   - column by column using the =Jacobian_column()= (fix j and compute ∂ⱼfⁱ\n     for all i)\n\n   *Note:* in applications we often encounter real functions. In that case\n   there is only one row and the (total) differential is simply \n\n   dfₓ=Σ ∂ᵢf dxⁱ\n\n   It is clearly better to compute df row by row as we\n   only have one row. We get a \"row vector\" that can be used to\n   represent the gradient of f (\"column vector\", denoted by ∇fₓ):\n\n   dfₓ.h = \u003c∇fₓ,h\u003e\n\n   That is the reason why there is an alias of the =Jacobian_row()=\n   function which is =gradient()=.\n\n   *Example:* (I just reproduced the already given Jacobian example).\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/doc/Jacobian.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   #include \u003ciostream\u003e\n\n   using namespace MissionImpossible;\n\n   int\n   main()\n   {\n     AD\u003cdouble\u003e r = 2, theta = 0.1;\n\n     AD\u003cdouble\u003e y1 = r * cos(theta);\n     AD\u003cdouble\u003e y2 = r * sin(theta);\n\n     //////////////////////////////////\n     // Computes Jacobian row by row //\n     //////////////////////////////////\n     //\n     // -\u003e AKA reverse-mode\n     //\n     std::cout \u003c\u003c \"Jacobian row by row\" \u003c\u003c std::endl;\n\n     auto Jacobian_row_y1 = Jacobian_row(y1);  // ∇y1 (or equivalently gradient(y1))\n                                               // computes ∂ᵣy¹, ∂ₒy¹\n\n     auto Jacobian_row_y2 = Jacobian_row(y2);  // ∇y2 (or equivalently gradient(y2))\n\t\t\t\t\t       // computes ∂ᵣy², ∂ₒy²\n\n     std::cout \u003c\u003c \"∇y1(r,θ) = [ ∂ᵣy¹, ∂ₒy¹ ] = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_row_y1[r] \u003c\u003c \", \";\n     std::cout \u003c\u003c std::setw(20) \u003c\u003c Jacobian_row_y1[theta] \u003c\u003c std::endl;\n\n     std::cout \u003c\u003c \"∇y2(r,θ) = [ ∂ᵣy², ∂ₒy² ] = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_row_y2[r] \u003c\u003c \", \";\n     std::cout \u003c\u003c std::setw(20) \u003c\u003c Jacobian_row_y2[theta] \u003c\u003c std::endl;\n\n     ////////////////////////////////////////\n     // Computes Jacobian column by column //\n     ////////////////////////////////////////\n     //\n     // -\u003e AKA forward-mode\n     //\n     std::cout \u003c\u003c std::endl \u003c\u003c \"Jacobian column by column\" \u003c\u003c std::endl;\n\n     auto Jacobian_column_r     = Jacobian_column(r);      // r column computes ∂ᵣy¹, ∂ᵣy²\n     auto Jacobian_column_theta = Jacobian_column(theta);  // θ column compules ∂ₒy¹, ∂ₒy²\n\n     std::cout \u003c\u003c \"∂ᵣy¹ = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_column_r[y1] \u003c\u003c \"\\t\"\n\t       \u003c\u003c \"∂ᵣy² = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_column_theta[y1] \u003c\u003c std::endl;\n     std::cout \u003c\u003c \"∂ₒy¹ = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_column_r[y2] \u003c\u003c \"\\t\"\n\t       \u003c\u003c \"∂ₒy² = \" \u003c\u003c std::setw(20) \u003c\u003c Jacobian_column_theta[y2] \u003c\u003c std::endl;\n   }\n   #+END_src\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/doc/Jacobian\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_example\n   Jacobian row by row\n   ∇y1(r,θ) = [ ∂ᵣy¹, ∂ₒy¹ ] =             0.995004,            -0.199667\n   ∇y2(r,θ) = [ ∂ᵣy², ∂ₒy² ] =            0.0998334,              1.99001\n\n   Jacobian column by column\n   ∂ᵣy¹ =             0.995004\t∂ᵣy² =            -0.199667\n   ∂ₒy¹ =            0.0998334\t∂ₒy² =              1.99001\n   #+END_example\n\n   *Note:* there are also variants of the =Jacobian_row()= and\n   =Jacobian_column()= that use local tape. In that case have a look at the \"local tape\" section and use:\n\n   #+BEGIN_SRC cpp :eval never \nMissionImpossible_Tape\u003cdouble\u003e local_tape;\n\n// ... computations ...\n\nauto row_by_row       = Jacobian_row(local_tape, y1);  // or equivalently gradient(local_tape,y1)\nauto column_by_column = Jacobian_column(local_tape, r);\n   #+END_SRC\n\n** Tape \n\n   A =local_thread= tape is globally stored. You can access it by:\n\n   #+BEGIN_SRC cpp :eval never \n\ntape\u003cT\u003e();          // returns a reference Tape\u003cT\u003e\u0026 to the tape associated to AD\u003cT\u003e\ntape\u003cAD\u003cT\u003e\u003e();      // returns a reference Tape\u003cT\u003e\u0026 to the tape associated to AD\u003cAD\u003cT\u003e\u003e\ntape\u003cAD\u003cAD\u003cT\u003e\u003e\u003e();  // returns a reference Tape\u003cT\u003e\u0026 to the tape associated to AD\u003cAD\u003cAD\u003cT\u003e\u003e\u003e\n                    // etc...\n   #+END_SRC\n\n   From the library user perspective, you can use these methods:\n\n   - =statement_size()=: returns the number of statements (= number of\n     expresisons + number of declared =AD\u003cT\u003e= variables).\n   - =memory_size()=: used memory to store all the statements\n   - =allocated_memory_size()=: preallocated memory \n\n   - =reset()= rewinds the tape at the beginning, does not release currently\n     allocated tape memory. *Attention*: this *invalidates* all previously\n     declared =AD\u003cT\u003e= variables.\n   - =clear()= rewinds the tape at the beginning and releases allocated\n     memory. *Attention*: this *invalidates* all previously declared =AD\u003cT\u003e=\n     variables.\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/doc/tape_info.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   using namespace MissionImpossible;\n\n   int\n   main()\n   {\n     auto print_tape_size = [](auto msg) {\n       std::cout \u003c\u003c std::endl \u003c\u003c \"\u003e\u003e\u003e \" \u003c\u003c msg \u003c\u003c std::endl;\n       std::cout \u003c\u003c \"statements                : \" \u003c\u003c tape\u003cdouble\u003e().statement_size() \u003c\u003c std::endl;\n       std::cout \u003c\u003c \"memory           (kBytes) : \" \u003c\u003c tape\u003cdouble\u003e().memory_size()/1024 \u003c\u003c std::endl;\n       std::cout \u003c\u003c \"allocated memory (kBytes) : \" \u003c\u003c tape\u003cdouble\u003e().allocated_memory_size()/1024 \u003c\u003c std::endl;\n     };\n\n     print_tape_size(\"Initial tape state (contains a small amount of preallocated memory)\");\n\n     for (size_t i = 1; i \u003c 1000; ++i)\n     {\n       AD\u003cdouble\u003e x0 = 2, x1 = 3, y;\n\n       y = 4 * x0 + 2 * x1;\n     }\n\n     print_tape_size(\"Final tape state (tape has allocated some fresh memory)\");\n\n     tape\u003cdouble\u003e().reset();\n     print_tape_size(\"after tape.reset() (the extra allocated memory is not released)\");\n\n     tape\u003cdouble\u003e().clear();\n     print_tape_size(\"after tape.clear() (releases extra memory and starts with a new tape)\");\n   }\n   #+END_src\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/doc/tape_info\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_example\n\n   \u003e\u003e\u003e Initial tape state (contains a small amount of preallocated memory)\n   statements                : 0\n   memory           (kBytes) : 0\n   allocated memory (kBytes) : 24\n\n   \u003e\u003e\u003e Final tape state (tape has allocated some fresh memory)\n   statements                : 2997\n   memory           (kBytes) : 54\n   allocated memory (kBytes) : 64\n\n   \u003e\u003e\u003e after tape.reset() (the extra allocated memory is not released)\n   statements                : 0\n   memory           (kBytes) : 0\n   allocated memory (kBytes) : 64\n\n   \u003e\u003e\u003e after tape.clear() (releases extra memory and starts with a new tape)\n   statements                : 0\n   memory           (kBytes) : 0\n   allocated memory (kBytes) : 24\n   #+END_example\n\n** Local Tape\n\n   If you want to do local computations and rewind the tape afterward\n   you can use a local tape.\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/doc/local_tape_memory.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   using namespace MissionImpossible;\n\n   int\n   main()\n   {\n     auto print_tape_size = [](auto msg) {\n       std::cout \u003c\u003c std::endl \u003c\u003c \"\u003e\u003e\u003e \" \u003c\u003c msg \u003c\u003c std::endl;\n       std::cout \u003c\u003c \"statements : \" \u003c\u003c tape\u003cdouble\u003e().statement_size() \u003c\u003c std::endl;\n       std::cout \u003c\u003c \"memory     : \" \u003c\u003c tape\u003cdouble\u003e().memory_size() \u003c\u003c std::endl;\n     };\n\n     print_tape_size(\"Initial tape state\");\n\n     AD\u003cdouble\u003e x0 = 2, x1 = 3, y;\n\n     y = 4 * x0 + 2 * x1;\n\n     auto grad = gradient(y);\n\n     std::cout \u003c\u003c std::endl\n\t       \u003c\u003c \"f: \" \u003c\u003c y \u003c\u003c \", grad: [ \" \u003c\u003c grad[x0] \u003c\u003c \", \" \u003c\u003c grad[x1] \u003c\u003c \" ]\" \u003c\u003c std::endl;\n\n     print_tape_size(\"Final tape state\");\n\n     std::cout  \u003c\u003c std::endl \u003c\u003c \"[[ Same computation but using a local tape ]]\" \u003c\u003c std::endl;\n\n     print_tape_size(\"Initial tape state\");\n\n     {\n       MissionImpossible_Tape\u003cdouble\u003e local_tape;\n\n       AD\u003cdouble\u003e x0 = 2, x1 = 3, y;\n\n       y = 4 * x0 + 2 * x1;\n\n       auto grad = gradient(local_tape, y);  // \u003c- here gradient use the local_tape\n\n       std::cout \u003c\u003c std::endl\n\t\t \u003c\u003c \"f: \" \u003c\u003c y \u003c\u003c \", grad: [ \" \u003c\u003c grad[x0] \u003c\u003c \", \" \u003c\u003c grad[x1] \u003c\u003c \" ]\" \u003c\u003c std::endl;\n     }\n\n     print_tape_size(\"Final tape state (global tape state has not changed)\");\n   }\n   #+END_src\n\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"example\" :results output :exports results\n$(pwd)/build/examples/doc/local_tape_memory\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_example\n\n   \u003e\u003e\u003e Initial tape state\n   statements : 0\n   memory     : 8\n\n   f: +14, grad: [ +4, +2 ]\n\n   \u003e\u003e\u003e Final tape state\n   statements : 3\n   memory     : 64\n\n   [[ Same computation but using a local tape ]]\n\n   \u003e\u003e\u003e Initial tape state\n   statements : 3\n   memory     : 64\n\n   f: +14, grad: [ +4, +2 ]\n\n   \u003e\u003e\u003e Final tape state (global tape state has not changed)\n   statements : 3\n   memory     : 64\n   #+END_example\n\n\n   If you use a local tape you must take care of only\n   using =AD\u003cT\u003e= declared in the scope of this local tape. By example:\n\n   #+BEGIN_SRC sh :eval no-export :wrap \"src cpp :eval never\" :results output :exports results\ncat $(pwd)/examples/doc/local_tape.cpp\n   #+END_SRC\n\n   #+RESULTS:\n   #+BEGIN_src cpp :eval never\n   #include \"MissionImpossible/MissionImpossible.hpp\"\n\n   using namespace MissionImpossible;\n\n   int\n   main()\n   {\n     // GOOD\n     //================\n     {\n       MissionImpossible_Tape\u003cdouble\u003e local_tape;\n\n       AD\u003cdouble\u003e x0 = 2, x1 = 3, y;\n\n       y = 4 * x0 + 2 * x1;\n\n       auto grad = gradient(local_tape, y);\n     }\n\n     // GOOD\n     //================\n     AD\u003cdouble\u003e a = 1;  // Ok, as \"a\" is not used in local_tape scope\n\n     {\n       MissionImpossible_Tape\u003cdouble\u003e local_tape;\n\n       AD\u003cdouble\u003e x0 = 2, x1 = 3, y;\n\n       y = 4 * x0 + 2 * x1;\n\n       auto grad = gradient(local_tape, y);\n     }\n\n     //  BAD\n     //================\n     {\n       MissionImpossible_Tape\u003cdouble\u003e local_tape;\n\n       AD\u003cdouble\u003e x0 = 2, x1 = 3, y;\n\n       y = 4 * x0 + 2 * x1 + a;  // BAD: \"a\" was not declared in the tape scope\n\n       auto grad = gradient(local_tape, y);  // Undefined behavior. In\n\t\t\t\t\t     // DEBUG mode triggers an\n\t\t\t\t\t     // assert(0)\n     }\n   }\n   #+END_src\n\n   *Note:* local tapes can be nested too (but you still have to respect\n   variable scopes!).\n\n \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvincent-picaud%2Fmissionimpossible","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvincent-picaud%2Fmissionimpossible","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvincent-picaud%2Fmissionimpossible/lists"}