Add `jet_poly` example with total derivative
Output:
d/dx(u) = u_x; d/dx(v) = v_x; d/dx(u_x) = u_xx; d/dx(u_y) = u_xy; d/dx(u_z) = u_xz; d/dx(v_x) = v_xx; d/dx(v_y) = v_xy; d/dx(v_z) = v_xz; d/dx(u_xx) = u_xxx; d/dx(u_xy) = u_xxy; d/dx(u_xz) = u_xxz; d/dx(u_yy) = u_xyy; d/dx(u_yz) = u_xyz; d/dx(u_zz) = u_xzz; d/dx(v_xx) = v_xxx; d/dx(v_xy) = v_xxy; d/dx(v_xz) = v_xxz; d/dx(v_yy) = v_xyy; d/dx(v_yz) = v_xyz; d/dx(v_zz) = v_xzz;
d/dy(u) = u_y; d/dy(v) = v_y; d/dy(u_x) = u_xy; d/dy(u_y) = u_yy; d/dy(u_z) = u_yz; d/dy(v_x) = v_xy; d/dy(v_y) = v_yy; d/dy(v_z) = v_yz; d/dy(u_xx) = u_xxy; d/dy(u_xy) = u_xyy; d/dy(u_xz) = u_xyz; d/dy(u_yy) = u_yyy; d/dy(u_yz) = u_yyz; d/dy(u_zz) = u_yzz; d/dy(v_xx) = v_xxy; d/dy(v_xy) = v_xyy; d/dy(v_xz) = v_xyz; d/dy(v_yy) = v_yyy; d/dy(v_yz) = v_yyz; d/dy(v_zz) = v_yzz;
d/dz(u) = u_z; d/dz(v) = v_z; d/dz(u_x) = u_xz; d/dz(u_y) = u_yz; d/dz(u_z) = u_zz; d/dz(v_x) = v_xz; d/dz(v_y) = v_yz; d/dz(v_z) = v_zz; d/dz(u_xx) = u_xxz; d/dz(u_xy) = u_xyz; d/dz(u_xz) = u_xzz; d/dz(u_yy) = u_yyz; d/dz(u_yz) = u_yzz; d/dz(u_zz) = u_zzz; d/dz(v_xx) = v_xxz; d/dz(v_xy) = v_xyz; d/dz(v_xz) = v_xzz; d/dz(v_yy) = v_yyz; d/dz(v_yz) = v_yzz; d/dz(v_zz) = v_zzz;
d/dx(u^2) = 2*u*u_x
d/dy(u^3) = 3*u^2*u_y
d/dz(u*v) = v*u_z + u*v_z
d/dz(u_x*v_y) = v_y*u_xz + u_x*v_yz
Comments welcome. Maybe a part of this could be interesting to have in the library?
Personally I would like to be able to use this functionality to calculate large expressions like in my Ph.D. thesis (but faster):
(-10*u_y^3*u_xx*u_yy*u_xxx + 20*u_x*u_y^2*u_xy*u_yy*u_xxx - 10*u_x^2*u_y*u_yy^2*u_xxx + 20*u_y^3*u_xx*u_xy*u_xxy - 40*u_x*u_y^2*u_xy^2*u_xxy + 10*u_x*u_y^2*u_xx*u_yy*u_xxy + 10*u_x^3*u_yy^2*u_xxy - 10*u_y^3*u_xx^2*u_xyy + 40*u_x^2*u_y*u_xy^2*u_xyy - 10*u_x^2*u_y*u_xx*u_yy*u_xyy - 20*u_x^3*u_xy*u_yy*u_xyy + 10*u_x*u_y^2*u_xx^2*u_yyy - 20*u_x^2*u_y*u_xx*u_xy*u_yyy + 10*u_x^3*u_xx*u_yy*u_yyy - 10*u_y^4*u_xy*u_xxxx + 10*u_x*u_y^3*u_yy*u_xxxx + 10*u_y^4*u_xx*u_xxxy + 20*u_x*u_y^3*u_xy*u_xxxy - 30*u_x^2*u_y^2*u_yy*u_xxxy - 30*u_x*u_y^3*u_xx*u_xxyy + 30*u_x^3*u_y*u_yy*u_xxyy + 30*u_x^2*u_y^2*u_xx*u_xyyy - 20*u_x^3*u_y*u_xy*u_xyyy - 10*u_x^4*u_yy*u_xyyy - 10*u_x^3*u_y*u_xx*u_yyyy + 10*u_x^4*u_xy*u_yyyy - 2*u_y^5*u_xxxxx + 10*u_x*u_y^4*u_xxxxy - 20*u_x^2*u_y^3*u_xxxyy + 20*u_x^3*u_y^2*u_xxyyy - 10*u_x^4*u_y*u_xyyyy + 2*u_x^5*u_yyyyy)*xi1*xi2
The API in the example is pretty small but effective:
typedef jet_ctx_struct jet_ctx_t[1];
void jet_ctx_init(jet_ctx_t jctx, const char** base_vars, slong base_nvars, const char** fibre_vars, slong fibre_nvars, slong max_diff_order);
void jet_ctx_clear(jet_ctx_t jctx);
void fmpq_mpoly_jet_total_derivative(fmpq_mpoly_t res, const fmpq_mpoly_t f, slong base_var, fmpq_mpoly_ctx_t ctx, jet_ctx_t jctx);
The jet_ctx_t stores the variable names and a lookup table for the total derivatives of individual variables, which allows implementing the general total derivative function (which could be made more generic than just using fmpq_mpoly) using the chain rule. Arguably the variable names could live outside the struct, but on the other hand the variable names are kind of important for the total derivative operation to make sense.
Is this something that would be of interest for inclusion in the library (rather than in an example)?