/* * optimization.cc * * Copyright 2017 Luka Marohnić * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * __________________________________________________________________________ * |Example of using 'extrema', 'minimize' and 'maximize' functions to solve| * |the set of exercises found in: | * |https://math.feld.cvut.cz/habala/teaching/veci-ma2/ema2r3.pdf | * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * 1) Input: * extrema(2x^3+9x*y^2+15x^2+27y^2,[x,y]) * Result: we get local minimum at origin, local maximum at (-5,0) * and (-3,2),(-3,-2) as saddle points. * * 2) Input: * extrema(x^3-2x^2+y^2+z^2-2x*y+x*z-y*z+3z,[x,y,z]) * Result: the given function has local minimum at (2,1,-2). * * 3) Input: * minimize(x^2+2y^2,x^2-2x+2y^2+4y=0,[x,y]); * maximize(x^2+2y^2,x^2-2x+2y^2+4y=0,[x,y]) * Result: the minimal value of x^2+2y^2 is 0 and the maximal is 12. * * 4) We need to minimize f(x,y,z)=x^2+(y+3)^2+(z-2)^2 for points (x,y,z) * lying in plane x+y-z=1. Since the feasible area is not bounded, we're * using the function 'extrema' because obviously the function has single * local minimum. * Input: * extrema(x^2+(y+3)^2+(z-2)^2,x+y-z=1,[x,y,z]) * Result: the point closest to P in x+y-z=1 is (2,-1,0), and the distance * is equal to sqrt(f(2,-1,0))=2*sqrt(3). * * 5) We're using the same method as in exercise 4. * Input: * extrema((x-1)^2+(y-2)^2+(z+1)^2,[x+y+z=1,2x-y+z=3],[x,y,z]) * Result: the closest point is (2,0,-1) and the corresponding distance * equals to sqrt(5). * * 6) First we need to determine the feasible area. Plot its bounds with: * implicitplot(x^2+(y+1)^2-4,x,y);line(y=-1);line(y=x+1) * Now we see that the feasible area is given by set of inequalities: * cond:=[x^2+(y+1)^2<=4,y>=-1,y<=x+1] * Draw this area with: * plotinequation(cond,[x,y],xstep=0.05,ystep=0.05) * Now calculate global minimum and maximum of f(x,y)=x^2+4y^2 on that area: * f(x,y):=x^2+4y^2; * minimize(f(x,y),cond,[x,y]);maximize(f(x,y),cond,[x,y]) * Result: the minimum is 0 and the maximum is 8. * * 7) Input: * minimize(x^2+y^2-6x+6y,x^2+y^2<=4,[x,y]); * maximize(x^2+y^2-6x+6y,x^2+y^2<=4,[x,y]) * Result: the minimum is 4-12*sqrt(2) and the maximum is 4+12*sqrt(2). * * 8) Input: * extrema(y,y^2+2x*y=2x-4x^2,[x,y]) * Result: we obtain (1/2,-1) as local minimum and (1/6,1/3) as local * maximum of f(x,y)=y. Therefore, the maximal value is y(1/2)=-1 * and the maximal value is y(1/6)=1/3. * * The above set of exercises could be turned into an example Xcas worksheet. */ #include "giacPCH.h" #include "optimization.h" #include "giac.h" #include using namespace std; using namespace std; #ifndef NO_NAMESPACE_GIAC namespace giac { #endif // ndef NO_NAMESPACE_GIAC gen make_idnt(const char* name,int index=-1,bool intern=true) { stringstream ss; if (intern) ss << " "; ss << string(name); if (index>=0) ss << index; return identificateur(ss.str().c_str()); } /* * Return true iff the expression 'e' is constant with respect to * variables in 'vars'. */ bool is_constant_wrt_vars(const gen & e,vecteur & vars,GIAC_CONTEXT) { for (const_iterateur it=vars.begin();it!=vars.end();++it) { if (!is_constant_wrt(e,*it,contextptr)) return false; } return true; } /* * Return true iff the expression 'e' is rational with respect to * variables in 'vars'. */ bool is_rational_wrt_vars(const gen & e,vecteur & vars,GIAC_CONTEXT) { for (const_iterateur it=vars.begin();it!=vars.end();++it) { vecteur l(rlvarx(e,*it)); if (l.size()>1) return false; } return true; } /* * Solves a system of equations. * This function is based on _solve but handles cases where a variable * is found inside trigonometric, hyperbolic or exponential functions. */ vecteur solve2(vecteur & e_orig,vecteur & vars_orig,GIAC_CONTEXT) { int m=e_orig.size(),n=vars_orig.size(),i=0; for (;i_VECTptr->at(i)); if (deps[i].type==_IDNT) r[i]=c; else if (deps[i].is_symb_of_sommet(at_exp) && is_strictly_positive(c,contextptr)) r[i]=_ratnormal(ln(c,contextptr),contextptr); else if (deps[i].is_symb_of_sommet(at_tan)) r[i]=_ratnormal(2*atan(c,contextptr),contextptr); else break; } if (i==n) ret.push_back(r); } return ret; } /* * Traverse the tree of symbolic expression 'e' and collect all points of * transition in piecewise subexpressions, no matter of the inequality sign. * Nested piecewise expressions are not supported. */ void gather_piecewise_transitions(const gen & e,vecteur & cv,GIAC_CONTEXT) { if (e.type!=_SYMB) return; gen & f = e._SYMBptr->feuille; if (f.type==_VECT) { for (const_iterateur it=f._VECTptr->begin();it!=f._VECTptr->end();++it) { if (e.is_symb_of_sommet(at_piecewise) || e.is_symb_of_sommet(at_when)) { if (it->is_symb_of_sommet(at_equal) || it->is_symb_of_sommet(at_different) || it->is_symb_of_sommet(at_inferieur_egal) || it->is_symb_of_sommet(at_superieur_egal) || it->is_symb_of_sommet(at_inferieur_strict) || it->is_symb_of_sommet(at_superieur_strict)) { vecteur & w = *it->_SYMBptr->feuille._VECTptr; cv.push_back(w[0].type==_IDNT?w[1]:w[0]); } } else { gather_piecewise_transitions(*it,cv,contextptr); } } } else gather_piecewise_transitions(f,cv,contextptr); } bool next_binary_perm(vector & perm,int to_end=0) { if (to_end==int(perm.size())) return false; int end=int(perm.size())-1-to_end; perm[end]=!perm[end]; return perm[end]?true:next_binary_perm(perm,to_end+1); } /* * Determine critical points of function f under constraints g<=0 and h=0 using * Karush-Kuhn-Tucker conditions. */ vecteur critical_kkt(gen & f,vecteur & g,vecteur & h,vecteur & vars_orig,GIAC_CONTEXT) { int n=vars_orig.size(),m=g.size(),l=h.size(); vecteur vars(vars_orig),gr_f(*_grad(makesequence(f,vars_orig),contextptr)._VECTptr),mug; matrice gr_g,gr_h; vars.resize(n+m+l); for (int i=0;i is_mu_zero(m,false); matrice cv; do { vecteur e(eqv); vecteur v(vars); for (int i=m-1;i>=0;--i) { if (is_mu_zero[i]) { e=subst(e,v[n+i],gen(0),false,contextptr); v.erase(v.begin()+n+i); } else e.push_back(g[i]); } cv=mergevecteur(cv,solve2(e,v,contextptr)); } while(next_binary_perm(is_mu_zero)); vars.resize(n); for (int i=cv.size()-1;i>=0;--i) { cv[i]._VECTptr->resize(n); for (int j=0;j=0;--i) { if (cv[i].is_symb_of_sommet(at_and)) cv.erase(cv.begin()+i); else cv[i]=vecteur(1,cv[i]); } return cv; } /* * Compute global minimum mn and global maximum mx of function f(vars) under * conditions g<=0 and h=0. The list of points where global minimum is achieved * is returned. */ vecteur global_extrema(gen & f,vecteur & g,vecteur & h,vecteur & vars,gen & mn,gen & mx,GIAC_CONTEXT) { int n=vars.size(); matrice cv(n==1?critical_univariate(f,*vars[0]._IDNTptr,contextptr):critical_kkt(f,g,h,vars,contextptr)); if (cv.empty()) return vecteur(0); bool min_set=false,max_set=false; matrice min_locations; for (const_iterateur it=cv.begin();it!=cv.end();++it) { gen val(subst(f,vars,*it,false,contextptr)); if (min_set && is_exactly_zero(val-mn)) { if (find(min_locations.begin(),min_locations.end(),*it)==min_locations.end()) min_locations.push_back(*it); } else if (!min_set || is_greater(mn,val,contextptr)) { mn=val; min_set=true; min_locations=vecteur(1,*it); } if (!max_set || is_strictly_greater(val,mx,contextptr)) { mx=val; max_set=true; } } if (n==1) { for (int i=0;ifeuille._VECTptr; if (ops[0].type!=_IDNT) return 0; oldvars[i]=ops[0]; if (ops[1].is_symb_of_sommet(at_interval)) { // variable is required to be in range specified by ops[1] range=*ops[1]._SYMBptr->feuille._VECTptr; bool has_lower=!is_inf(-range[0]),has_upper=!is_inf(range[1]); if (has_lower && has_upper) assume_t_in_ab(vars[i],range[0],range[1],true,true,contextptr); else if (has_lower) giac_assume(symb_superieur_strict(vars[i],range[0]),contextptr); else if (has_upper) giac_assume(symb_inferieur_strict(vars[i],range[1]),contextptr); } else initial.push_back(ops[1]); } else if (v.type!=_IDNT) return 0; else oldvars[i]=v; } // return detected variables return oldvars; } /* * Function 'minimize' minimizes a multivariate continuous function on a * closed and bounded region using the method of Lagrange multipliers. The * feasible region is specified by bounding variables or by adding one or * more (in)equality constraints. * * Usage * ^^^^^ * minimize(obj,[constr],vars,[opt]) * * Parameters * ^^^^^^^^^^ * - obj : objective function to minimize * - constr (optional) : single equality or inequality constraint or * a list of constraints, if constraint is given as * expression it is assumed that it is equal to zero * - vars : single variable or a list of problem variables, where * optional bounds of a variable may be set by appending '=a..b' * - location (optional) : the option keyword 'locus' or 'coordinates' or 'point' * * Objective function must be continuous in all points of the feasible region, * which is assumed to be closed and bounded. If one of these condinitions is * not met, the final result may be incorrect. * * When the fourth argument is specified, point(s) at which the objective * function attains its minimum value are also returned as a list of vector(s). * The keywords 'locus', 'coordinates' and 'point' all have the same effect. * For univariate problems, a vector of numbers (x values) is returned, while * for multivariate problems it is a vector of vectors, i.e. a matrix. * * The function attempts to obtain the critical points in exact form, if the * parameters of the problem are all exact. It works best for problems in which * the lagrangian function gradient consists of rational expressions. Points at * which the function is not differentiable are also considered critical. This * function also handles univariate piecewise functions. * * If no critical points were obtained, the return value is undefined. * * Examples * ^^^^^^^^ * minimize(sin(x),[x=0..4]) * >> sin(4) * minimize(asin(x),x=-1..1) * >> -pi/2 * minimize(x^2+cos(x),x=0..3) * >> 1 * minimize(x^4-x^2,x=-3..3,locus) * >> -1/4,[-sqrt(2)/2] * minimize(abs(x),x=-1..1) * >> 0 * minimize(x-abs(x),x=-1..1) * >> -2 * minimize(abs(exp(-x^2)-1/2),x=-4..4) * >> 0 * minimize(piecewise(x<=-2,x+6,x<=1,x^2,3/2-x/2),x=-3..2) * >> 0 * minimize(x^2-3x+y^2+3y+3,[x=2..4,y=-4..-2],point) * >> -1,[[2,-2]] * minimize(2x^2+y^2,x+y=1,[x,y]) * >> 2/3 * minimize(2x^2-y^2+6y,x^2+y^2<=16,[x,y]) * >> -40 * minimize(x*y+9-x^2-y^2,x^2+y^2<=9,[x,y]) * >> -9/2 * minimize(sqrt(x^2+y^2)-z,[x^2+y^2<=16,x+y+z=10],[x,y,z]) * >> -4*sqrt(2)-6 * minimize(x*y*z,x^2+y^2+z^2=1,[x,y,z]) * >> -sqrt(3)/9 * minimize(sin(x)+cos(x),x=0..20,coordinates) * >> -sqrt(2),[5*pi/4,13*pi/4,21*pi/4] * minimize((1+x^2+3y+5x-4*x*y)/(1+x^2+y^2),x^2/4+y^2/3=9,[x,y]) * >> -2.44662490691 * minimize(x^2-2x+y^2+1,[x+y<=0,x^2<=4],[x,y],locus) * >> 1/2,[[1/2,-1/2]] * minimize(x^2*(y+1)-2y,[y<=2,sqrt(1+x^2)<=y],[x,y]) * >> -4 * minimize(4x^2+y^2-2x-4y+1,4x^2+y^2=1,[x,y]) * >> -sqrt(17)+2 * minimize(cos(x)^2+cos(y)^2,x+y=pi/4,[x,y],locus) * >> (-sqrt(2)+2)/2,[[5*pi/8,-3*pi/8]] * minimize(x^2+y^2,x^4+y^4=2,[x,y]) * >> 1.41421356237 * minimize(z*x*exp(y),z^2+x^2+exp(2y)=1,[x,y,z]) * >> -sqrt(3)/9 */ gen _minimize(const gen & args,GIAC_CONTEXT) { if (args.type==_STRNG && args.subtype==-1) return args; if (args.type!=_VECT || args.subtype!=_SEQ__VECT || args._VECTptr->size()>4) return gentypeerr(contextptr); vecteur & argv = *args._VECTptr,g,h; bool location=false; int nargs=argv.size(); if (argv.back()==at_coordonnees || argv.back()==at_lieu || argv.back()==at_point) { location=true; --nargs; } if (nargs==3) { vecteur constr(argv[1].type==_VECT ? *argv[1]._VECTptr : vecteur(1,argv[1])); for (const_iterateur it=constr.begin();it!=constr.end();++it) { if ((*it).is_symb_of_sommet(at_equal)) h.push_back(equal2diff(*it)); else if ((*it).is_symb_of_sommet(at_superieur_egal) || (*it).is_symb_of_sommet(at_inferieur_egal)) g.push_back(*it); else h.push_back(*it); } } vecteur vars,oldvars,initial; int n; // number of variables if ((n=(oldvars=parse_varlist(argv,nargs,vars,initial,contextptr)).size())==0 || !initial.empty()) return gensizeerr(contextptr); for (int i=0;ifeuille._VECTptr; g[i]=gi.is_symb_of_sommet(at_inferieur_egal)?s[0]-s[1]:s[1]-s[0]; } vecteur range; for (const_iterateur it=vars.begin();it!=vars.end();++it) { find_range(*it,range,contextptr); range=*range[0]._VECTptr; if (!is_inf(-range[0])) g.push_back(range[0]-*it); if (!is_inf(range[1])) g.push_back(*it-range[1]); if (n>1) purgenoassume(*it,contextptr); } gen f(subst(argv[0],oldvars,vars,false,contextptr)); g=subst(g,oldvars,vars,false,contextptr); h=subst(h,oldvars,vars,false,contextptr); gen mn,mx; vecteur loc(global_extrema(f,g,h,vars,mn,mx,contextptr)); if (loc.empty()) return undef; if (location) return makesequence(_simplify(mn,contextptr),_simplify(loc,contextptr)); return _simplify(mn,contextptr); } static const char _minimize_s []="minimize"; static define_unary_function_eval (__minimize,&_minimize,_minimize_s); define_unary_function_ptr5(at_minimize,alias_at_minimize,&__minimize,0,true) /* * 'maximize' takes the same arguments as the function 'minimize', but * maximizes the objective function. See 'minimize' for details. * * Examples * ^^^^^^^^ * maximize(cos(x),x=1..3) * >> cos(1) * maximize(piecewise(x<=-2,x+6,x<=1,x^2,3/2-x/2),x=-3..2) * >> 4 * minimize(x-abs(x),x=-1..1) * >> 0 * maximize(x^2-3x+y^2+3y+3,[x=2..4,y=-4..-2]) * >> 11 * maximize(x*y*z,x^2+2*y^2+3*z^2<=1,[x,y,z],point) * >> sqrt(2)/18,[[-sqrt(3)/3,sqrt(6)/6,-1/3],[sqrt(3)/3,-sqrt(6)/6,-1/3], * [-sqrt(3)/3,-sqrt(6)/6,1/3],[sqrt(3)/3,sqrt(6)/6,1/3]] * maximize(x^2-x*y+2*y^2,[x=-1..0,y=-1/2..1/2],coordinates) * >> 2,[[-1,1/2]] * maximize(x*y,[x+y^2<=2,x>=0,y>=0],[x,y],locus) * >> 4*sqrt(6)/9,[[4/3,sqrt(6)/3]] * maximize(y^2-x^2*y,y<=x,[x=0..2,y=0..2]) * >> 4/27 * maximize(2x+y,4x^2+y^2=8,[x,y]) * >> 4 * maximize(x^2*(y+1)-2y,[y<=2,sqrt(1+x^2)<=y],[x,y]) * >> 5 * maximize(4x^2+y^2-2x-4y+1,4x^2+y^2=1,[x,y]) * >> sqrt(17)+2 * maximize(3x+2y,2x^2+3y^2<=3,[x,y]) * >> sqrt(70)/2 * maximize(x*y,[2x+3y<=10,x>=0,y>=0],[x,y]) * >> 25/6 * maximize(x^2+y^2+z^2,[x^2/16+y^2+z^2=1,x+y+z=0],[x,y,z]) * >> 8/3 * assume(a>0);maximize(x^2*y^2*z^2,x^2+y^2+z^2=a^2,[x,y,z]) * >> a^6/27 */ gen _maximize(const gen & g,GIAC_CONTEXT) { if (g.type==_STRNG && g.subtype==-1) return g; if (g.type!=_VECT || g.subtype!=_SEQ__VECT || g._VECTptr->size()<2) return gentypeerr(contextptr); vecteur gv(*g._VECTptr); gv[0]=-gv[0]; gen res=_minimize(_feuille(gv,contextptr),contextptr); if (res.type==_VECT && res._VECTptr->size()>0) { res._VECTptr->front()=-res._VECTptr->front(); } else if (res.type!=_VECT) res=-res; return res; } static const char _maximize_s []="maximize"; static define_unary_function_eval (__maximize,&_maximize,_maximize_s); define_unary_function_ptr5(at_maximize,alias_at_maximize,&__maximize,0,true) /* * Partition nonnegative integer m into n nonnegative integers x1,x2,...,xn. All * possible combinations are put in c. */ void partition_m_in_n_terms (int m,int n,vector & c,vint p=vint(0)) { if (p.empty()) p=vint(n,0); for (int i=0;i::const_iterator it=t.second.begin();it!=t.second.end();++it) { cout << it->first << "^" << it->second << " "; } cout << endl; } */ impldiff derive_diffterms(impldiff & terms,int n,int m,vint & sig) { while (!sig.empty() && sig.back()==0) { sig.pop_back(); } if (sig.empty()) return terms; int k=int(sig.size())-1; impldiff tv; for (impldiff::const_iterator it=terms.begin();it!=terms.end();++it) { int c=it->second; diffterm t(it->first); map h_orig(it->first.second); ++t.first.at(k); tv[t]+=c; --t.first.at(k); map h(h_orig); for (map::const_iterator jt=h_orig.begin();jt!=h_orig.end();++jt) { vint v(jt->first); int p(jt->second); if (p==0) continue; if (p==1) h.erase(h.find(v)); else --h[v]; ++v[k]; ++h[v]; t.second=h; tv[t]+=c*p; --h[v]; --v[k]; ++h[v]; } t.second=h_orig; for (int i=0;i & pdv,const vint & sig) { gen ret; #ifndef NO_STDEXCEPT try { #endif ret=pdv.at(sig); #ifndef NO_STDEXCEPT } catch (out_of_range & e) { ret=undef; } #endif return ret; } gen compute_pd(gen & f,vecteur & vars,map & pdv,vint & sig,GIAC_CONTEXT) { gen pd(get_pd(pdv,sig)); if (!is_undef(pd)) return pd; vecteur v(1,f); bool do_derive=false; assert(vars.size()<=sig.size()); for (int i=0;i0) { v=mergevecteur(v,vecteur(sig[i],vars[i])); do_derive=true; } } if (do_derive) return pdv[sig]=_derive(_feuille(v,contextptr),contextptr); return f; } void compute_h(vecteur & g,vecteur & vars,vector & grv, map & pdg,map & pdh,int order,GIAC_CONTEXT) { if (g.empty()) return; vector hsigv; matrice A; vecteur b(g.size()*grv.size(),gen(0)); for (int i=0;ifirst.first),hsig; int cf=it->second; sig.push_back(i); gen t(gen(cf)*compute_pd(g[i],vars,pdg,sig,contextptr)); for (map::const_iterator ht=it->first.second.begin();ht!=it->first.second.end();++ht) { if (ht->second==0) continue; sig=ht->first; if (sum_vint(sig,true)second); } else { assert(ht->second==1); hsig=sig; } } if (hsig.empty()) b[grv.size()*i+j]-=t; else { int k=0; for (;k & terms,vint & sig,impldiff & match,vint & excess) { int n=sig.size(); excess=sig; for (map::const_iterator it=terms.begin();it!=terms.end();++it) { vint ex(n,0); int i=0; for (;ifirst.at(i))<0) break; } if (isecond; } } } map implicit_partial_derivatives( gen & f,vecteur & g,vecteur & vars,int order,vint sig,map & cterms, map & pdf,map & pdg,GIAC_CONTEXT) { int m=g.size(); int n=vars.size()-m; map pdh,pdv; pdv[vint(n,0)]=f; if (order==0) return pdv; vector c; vint excess,init_f(n+m,0); diffterm init_term; init_term.first=init_f; impldiff init_terms; init_terms[init_term]=1; vector grv; for (int k=1;k<=order;++k) { grv.clear(); c.clear(); partition_m_in_n_terms(k,n,c); for (vector::iterator it=c.begin();it!=c.end();++it) { impldiff terms=init_terms; find_nearest_terms(cterms,*it,terms,excess); if (sum_vint(excess)>0) { terms=derive_diffterms(terms,n,m,excess); cterms[*it]=terms; } grv.push_back(terms); } compute_h(g,vars,grv,pdg,pdh,k,contextptr); } gen pd; for (vector::const_iterator ct=c.begin();ct!=c.end();++ct) { if (!sig.empty() && sig!=*ct) continue; impldiff & terms=cterms[*ct]; pd=gen(0); for (impldiff::const_iterator it=terms.begin();it!=terms.end();++it) { vint sig(it->first.first); int cf=it->second; gen t(gen(cf)*compute_pd(f,vars,pdf,sig,contextptr)); if (!is_zero(t)) { for (map::const_iterator jt=it->first.second.begin();jt!=it->first.second.end();++jt) { if (jt->second==0) continue; gen h(get_pd(pdh,jt->first)); assert(!is_undef(h)); t=t*pow(h,jt->second); } pd+=t; } } pdv[*ct]=_ratnormal(pd,contextptr); } return pdv; } vint rearrange_vars(matrice J,GIAC_CONTEXT) { vint v; int m=J.size(); matrice tJ(mtran(J)); for (int i=0;i pdv,int n) { vecteur gr; for (int i=0;i pdv,int n) { matrice hess; for (int i=0;i,[P]) * implicitdiff(constr,[depvars],y,diffvars) * * Parameters * ^^^^^^^^^^ * - f : expression * - constr : (list of) equation(s) * - depvars : (list of) dependent variable(s), each of them given * either as a symbol, e.g. y, or a function, e.g. y(x,z) * - diffvars : sequence of variables w.r.t. which the differentiation * will be carried out * - vars : list all variables on which f depends such that * : dependent variables come after independent ones * - P : (list of) coordinate(s) to compute derivatives at * - y : (list of) dependent variable(s) to differentiate w.r.t. * diffvars, each of them given as a symbol * * The return value is partial derivative specified by diffvars. If * 'order_size=m' is given as the fourth argument, all partial derivatives of * order m will be computed and returned as vector for m=1, matrix for m=2 or * table for m>2. The first two cases produce gradient and hessian of f, * respectively. For m>2, the partial derivative * pd=d^m(f)/(d^k1(x1)*d^k2(x2)*...*d^kn(xn)) is saved under key [k1,k2,...kn]. * If P is specified, pd(P) is saved. * * Examples * ^^^^^^^^ * implicitdiff(x^2*y+y^2=1,y,x) * >> -2*x*y/(x^2+2*y) * implicitdiff(R=P*V/T,P,T) * >> P/T * implicitdiff([x^2+y=z,x+y*z=1],[y(x),z(x)],y,x) * >> (-2*x*y-1)/(y+z) * implicitdiff([x^2+y=z,x+y*z=1],[y(x),z(x)],[y,z],x) * >> [(-2*x*y-1)/(y+z),(2*x*z-1)/(y+z)] * implicitdiff(y=x^2/z,y,x) * >> 2x/z * implicitdiff(y=x^2/z,y,z) * >> -x^2/z^2 * implicitdiff(y^3+x^2=1,y,x) * >> -2*x/(3*y^2) * implicitdiff(y^3+x^2=1,y,x,x) * >> (-8*x^2-6*y^3)/(9*y^5) * implicitdiff(a*x^3*y-2y/z=z^2,y(x,z),x) * >> -3*a*x^2*y*z/(a*x^3*z-2) * implicitdiff(a*x^3*y-2y/z=z^2,y(x,z),x,z) * >> (12*a*x^2*y-6*a*x^2*z^3)/(a^2*x^6*z^2-4*a*x^3*z+4) * implicitdiff([-2x*z+y^2=1,x^2-exp(x*z)=y],[y(x),z(x)],y,x) * >> 2*x/(y*exp(x*z)+1) * implicitdiff([-2x*z+y^2=1,x^2-exp(x*z)=y],[y(x),z(x)],[y,z],x) * >> [2*x/(y*exp(x*z)+1),(2*x*y-y*z*exp(x*z)-z)/(x*y*exp(x*z)+x)] * implicitdiff([a*sin(u*v)+b*cos(w*x)=c,u+v+w+x=z,u*v+w*x=z],[u(x,z),v(x,z),w(x,z)],u,z) * >> (a*u*x*cos(u*v)-a*u*cos(u*v)+b*u*x*sin(w*x)-b*x*sin(w*x))/ * (a*u*x*cos(u*v)-a*v*x*cos(u*v)+b*u*x*sin(w*x)-b*v*x*sin(w*x)) * implicitdiff(x*y,-2x^3+15x^2*y+11y^3-24y=0,y(x),x$2) * >> (162*x^5*y+1320*x^4*y^2-320*x^4-3300*x^3*y^3+800*x^3*y+968*x^2*y^4-1408*x^2*y^2+ * 512*x^2-3630*x*y^5+5280*x*y^3-1920*x*y)/(125*x^6+825*x^4*y^2-600*x^4+ * 1815*x^2*y^4-2640*x^2*y^2+960*x^2+1331*y^6-2904*y^4+2112*y^2-512) * implicitdiff((x-u)^2+(y-v)^2,[x^2/4+y^2/9=1,(u-3)^2+(v+5)^2=1],[v(u,x),y(u,x)],u,x) * >> (-9*u*x-4*v*y+27*x-20*y)/(2*v*y+10*y) * implicitdiff(x*y*z,-2x^3+15x^2*y+11y^3-24y=0,[x,z,y],order_size=1) * >> [(2*x^3*z-5*x^2*y*z+11*y^3*z-8*y*z)/(5*x^2+11*y^2-8),x*y] * implicitdiff(x*y*z,-2x^3+15x^2*y+11y^3-24y=0,[x,z,y],order_size=2,[1,-1,0]) * >> [[64/9,-2/3],[-2/3,0]] * pd:=implicitdiff(x*y*z,-2x^3+15x^2*y+11y^3-24y=0,[x,z,y],order_size=4,[0,z,0]);pd[4,0,0] * >> -2*z */ gen _implicitdiff(const gen & g,GIAC_CONTEXT) { if (g.type==_STRNG && g.subtype==-1) return g; if (g.type!=_VECT || g.subtype!=_SEQ__VECT || g._VECTptr->size()<2) return gentypeerr(contextptr); vecteur & gv = *g._VECTptr; gen & f = gv[0]; if (int(gv.size())<3) return gensizeerr(contextptr); int ci=gv[0].type!=_VECT && !gv[0].is_symb_of_sommet(at_equal)?1:0; vecteur freevars,depvars,diffdepvars; gen_map diffvars; // get the constraints as a list of vanishing expressions vecteur constr(gv[ci].type==_VECT?*gv[ci]._VECTptr:vecteur(1,gv[ci])); for (int i=0;ifeuille._VECTptr; if (v.front()!=at_order_size || !v.back().is_integer()) return gentypeerr(contextptr); order=v.back().val; if (order<=0) return gendimerr(contextptr); compute_all=true; } // get dependency specification vecteur deplist(gv[ci+1].type==_VECT?*gv[ci+1]._VECTptr:vecteur(1,gv[ci+1])); if (compute_all) { // vars must be specified as x1,x2,...,xn,y1,y2,...,ym int nd=deplist.size(); if (nd<=m) return gensizeerr(contextptr); for (int i=0;itype==_IDNT) depvars.push_back(*it); else if (it->is_symb_of_sommet(at_of)) { vecteur fe(*it->_SYMBptr->feuille._VECTptr); depvars.push_back(fe.front()); if (fe.back().type==_VECT) { for (int i=0;isize());++i) { gen & x = fe.back()._VECTptr->at(i); if (find(freevars.begin(),freevars.end(),x)==freevars.end()) freevars.push_back(x); } } else freevars.push_back(fe.back()); } else return gentypeerr(contextptr); } // get diffvars for (const_iterateur it=gv.begin()+dvi;it!=gv.end();++it) { gen v(eval(*it,contextptr)); gen x; if (v.type==_IDNT) diffvars[(x=v)]+=1; else if (v.type==_VECT && v.subtype==_SEQ__VECT) diffvars[(x=v._VECTptr->front())]+=v._VECTptr->size(); else return gentypeerr(contextptr); if (find(freevars.begin(),freevars.end(),x)==freevars.end()) freevars.push_back(x); } } int n=freevars.size(); // number of independent variables if (m!=int(depvars.size())) return gensizeerr(contextptr); vecteur vars(mergevecteur(freevars,depvars)); // list of all variables // check whether the conditions of implicit function theorem hold if (!ck_jacobian(constr,vars,contextptr)) return gendimerr(contextptr); // build partial derivative specification 'sig' vint sig(n,0); // sig[i]=k means: derive k times with respect to ith independent variable map cterms; map pdf,pdg; if (compute_all) { vecteur pt; if (int(gv.size())>4) { pt=gv[4].type==_VECT?*gv[4]._VECTptr:vecteur(1,gv[4]); if (int(pt.size())!=n+m) return gensizeerr(contextptr); } map pdv=implicit_partial_derivatives(f,constr,vars,order,vint(0),cterms,pdf,pdg,contextptr); if (order==1) { vecteur gr(pdv2gradient(pdv,n)); return pt.empty()?gr:_ratnormal(subst(gr,vars,pt,false,contextptr),contextptr); } else if (order==2) { matrice hess(pdv2hessian(pdv,n)); return pt.empty()?hess:_ratnormal(subst(hess,vars,pt,false,contextptr),contextptr); } else { vector c; partition_m_in_n_terms(order,n,c); gen_map ret_pdv; for (vector::const_iterator it=c.begin();it!=c.end();++it) { vecteur v; for (int i=0;iat(i))); } ret_pdv[v]=pt.empty()?pdv[*it]:_ratnormal(subst(pdv[*it],vars,pt,false,contextptr),contextptr); } return ret_pdv; } } for (gen_map::const_iterator it=diffvars.begin();it!=diffvars.end();++it) { int i=0; for (;ifirst==freevars[i]) { sig[i]=it->second.val; break; } } assert(i pdv=implicit_partial_derivatives(f,constr,vars,order,sig,cterms,pdf,pdg,contextptr); return _ratnormal(pdv[sig],contextptr); } vecteur ret; if (diffdepvars.empty()) { assert(m==1); diffdepvars=vecteur(1,depvars.front()); } for (const_iterateur it=diffdepvars.begin();it!=diffdepvars.end();++it) { if (find(depvars.begin(),depvars.end(),*it)==depvars.end()) { // variable *it is not in depvars, so it's treated as a constant ret.push_back(gen(0)); continue; } gen fit(*it); pdf.clear(); map pdv=implicit_partial_derivatives(fit,constr,vars,order,sig,cterms,pdf,pdg,contextptr); ret.push_back(_ratnormal(pdv[sig],contextptr)); } return ret.size()==1?ret.front():ret; } static const char _implicitdiff_s []="implicitdiff"; static define_unary_function_eval (__implicitdiff,&_implicitdiff,_implicitdiff_s); define_unary_function_ptr5(at_implicitdiff,alias_at_implicitdiff,&__implicitdiff,0,true) /* * Return kth term of Taylor series for the multivariate function f under * condition g=0 in the neighborhood of a. This function uses the multinomial * formula to compute 1/k!*(sum(i=1)^n (xi-ai)*d/dxi)^k f(x)|a. */ gen compute_implicit_taylor_term(gen & f,vecteur & g,vecteur & a,vecteur & vars,int k, map & cterms,map & pdf,map & pdg,GIAC_CONTEXT) { int n=int(vars.size())-int(g.size()); vector sigv; partition_m_in_n_terms(k,n,sigv); gen term(0); map pdv; if (!g.empty()) pdv=implicit_partial_derivatives(f,g,vars,k,vint(0),cterms,pdf,pdg,contextptr); for (vector::const_iterator it=sigv.begin();it!=sigv.end();++it) { gen pd; if (g.empty()) { vecteur args(1,f); for (int i=0;iat(i);++j) { args.push_back(vars[i]); } } pd=_derive(_feuille(args,contextptr),contextptr); } else pd=pdv[*it]; pd=subst(pd,vars,a,false,contextptr); for (int i=0;iat(i); if (ki==0) continue; pd=pd*pow(vars[i]-a[i],ki)/factorial(ki); } term+=pd; } return term; } gen_map find_extrema(gen & f,vecteur & g,vecteur & vars,vecteur & initial,int order_size,GIAC_CONTEXT) { int nv=int(vars.size()),m=int(g.size()),n=nv-m; vecteur fvars(vars); fvars.resize(n); // determine critical points map cterms; map pdf,pdg,pdv; vecteur gr; if (m>0) { pdv=implicit_partial_derivatives(f,g,vars,1,vint(0),cterms,pdf,pdg,contextptr); gr=pdv2gradient(pdv,n); } else gr=*_grad(makesequence(f,vars),contextptr)._VECTptr; matrice cv,h; gen_map res; vecteur eqv(mergevecteur(gr,g)); if (initial.empty()) cv=solve2(eqv,vars,contextptr); else { vecteur fsol(*_fsolve(makesequence(eqv,vars,initial),contextptr)._VECTptr); if (!fsol.empty()) cv.push_back(fsol); } // compute hessian if (m>0) { pdv=implicit_partial_derivatives(f,g,vars,2,vint(0),cterms,pdf,pdg,contextptr); h=pdv2hessian(pdv,n); } else h=*_hessian(makesequence(f,vars),contextptr)._VECTptr; // variables for Taylor expansion used in higher order test vecteur taylor_terms; vecteur a(nv); for (int i=0;i=0;--i) { int cls=_CPCLASS_UNDECIDED; // second order test (using hessian) matrice H(*_evalf(subst(h,vars,cv[i],false,contextptr),contextptr)._VECTptr); bool ismax=true,ismin=true; int k=0; vecteur eigvals(*_eigenvals(H,contextptr)._VECTptr); for (;k=2) { // second order test was inconclusive, apply the extremum test which // requires computation of higher derivatives if (n==1 && order_size>2) { // univariate extremum test int k=2; // degree of the derivative gen df; while (k1) { // higher derivative test for multivariate functions by Salvador Gigena // (Theorem 6.5 on page 27 in: https://arxiv.org/abs/1303.3184) for (int k=2;k<=order_size;++k) { if (int(taylor_terms.size())first._VECTptr->front()]=it->second; else if (arr.empty()) { res=cv; break; } else { vecteur v(n); for (int i=0;ifirst._VECTptr->at(i),contextptr); } res[v]=it->second; } } return res; } /* * 'extrema' attempts to find all points of strict local minima/maxima of a * smooth (uni/multi)variate function subject to one or more equality * constraints. The implemented method uses Lagrange multipliers. * * Usage * ^^^^^ * extrema(expr,[constr],vars,[order_size]) * * Parameters * ^^^^^^^^^^ * - expr : differentiable expression * - constr (optional) : (list of) equality constraint(s) * - vars : (list of) problem variable(s) * - order_size (optional) : specify 'order_size=' to * bound the order of the derivatives being * inspected when classifying the critical points * * The number of constraints must be less than the number of variables. When * there are more than one constraint/variable, they must be specified in * form of list. * * Variables may be specified with symbol, e.g. 'var', or by using syntax * 'var=a..b', which restricts the variable 'var' to the open interval (a,b), * where a and b are real numbers or +/-infinity. If variable list includes a * specification of initial point, such as, for example, [x=1,y=0,z=2], then * numeric solver is activated to find critical point in the vicinity of the * given point. In this case, the single critical point, if found, is examined. * * The function attempts to find the critical points in exact form, if the * parameters of the problem are all exact. It works best for problems in which * the gradient of lagrangian function consists of rational expressions. The * result may be inexact, however, if exact solutions could not be obtained. * * For classifying critical points, the bordered hessian method is used first. * It is only a second order test, so it may be inconclusive in some cases. In * these cases function looks at higher-order derivatives, up to order * specified by 'order_size' option (the extremum test). Set 'order_size' to 1 * to use only the bordered hessian test or 0 to output critical points without * attempting to classify them. Setting 'order_size' to 2 or higher will * activate checking for saddle points and inspecting higher derivatives (up to * 'order_size') to determine the nature of some or all unclassified critical * points. By default 'order_size' equals to 12. * * The return value is a sequence with two elements: list of strict local * minima and list of strict local maxima. If only critical points are * requested (by setting 'order_size' to 0), the output consists of a single * list, as no classification was attempted. For univariate problems the points * are real numbers, while for multivariate problems they are specified as * lists of coordinates, so "lists of points" are in fact matrices with rows * corresponding to points in multivariate cases, i.e. vectors in univariate * cases. * * The function prints out information about saddle/inflection points and also * about critical points for which no decision could be made, so that the user * can inspect candidates for local extrema by plotting the graph, for example. * * Examples * ^^^^^^^^ * extrema(-2*cos(x)-cos(x)^2,x) * >> [0],[pi] * extrema((x^3-1)^4/(2x^3+1)^4,x=0..inf) * >> [1],[] * extrema(x/2-2*sin(x/2),x=-12..12) * >> [2*pi/3,-10*pi/3],[10*pi/3,-2*pi/3] * extrema(x-ln(abs(x)),x) * >> [1],[] * assume(a>=0);extrema(x^2+a*x,x) * >> [-a/2],[] * extrema(x^7+3x^6+3x^5+x^4+2x^2-x,x) * >> [0.225847362349],[-1.53862319761] * extrema((x^2+x+1)/(x^4+1),x) * >> [],[0.697247087784] * extrema(x^2+exp(-x),x) * >> [0.351733711249],[] * extrema(exp(-x)*ln(x),x) * >> [],[1.76322283435] * extrema(tan(x)*(x^3-5x^2+1),x=-0.5) * >> [-0.253519032024],[] * extrema(tan(x)*(x^3-5x^2+1),x=0.5) * >> [],[0.272551772027] * extrema(exp(x^2-2x)*ln(x)*ln(1-x),x=0.5) * >> [],[0.277769149124] * extrema(ln(2+x-sin(x)^2),x=0..2*pi) * >> [],[] (needed to compute third derivative to drop critical points pi/4 and 5pi/4) * extrema(x^3-2x*y+3y^4,[x,y]) * >> [[12^(1/5)/3,(12^(1/5))^2/6]],[] * extrema((2x^2-y)*(y-x^2),[x,y]) //Peano surface * >> [],[] (non-strict local maximum at origin) * extrema(5x^2+3y^2+x*z^2-z*y^2,[x,y,z]) * >> [],[] (non-strict local minimum at origin) * extrema(3*atan(x)-2*ln(x^2+y^2+1),[x,y]) * >> [],[[3/4,0]] * extrema(x*y,x+y=1,[x,y]) * >> [],[[1/2,1/2]] * extrema(sqrt(x*y),x+y=2,[x,y]) * >> [],[[1,1]] * extrema(x*y,x^3+y^3=16,[x,y]) * >> [],[[2,2]] * extrema(x^2+y^2,x*y=1,[x=0..inf,y=0..inf]) * >> [[1,1]],[] * extrema(ln(x*y^2),2x^2+3y^2=8,[x,y]) * >> [],[[2*sqrt(3)/3,-4/3],[-2*sqrt(3)/3,-4/3],[2*sqrt(3)/3,4/3],[-2*sqrt(3)/3,4/3]] * extrema(y^2+4y+2x-x^2,x+2y=2,[x,y]) * >> [],[[-2/3,4/3]] * assume(a>0);extrema(x/a^2+a*y^2,x+y=a,[x,y]) * >> [[(2*a^4-1)/(2*a^3),1/(2*a^3)]],[] * extrema(6x+3y+2z,4x^2+2y^2+z^2=70,[x,y,z]) * >> [[-3,-3,-4]],[[3,3,4]] * extrema(x*y*z,x+y+z=1,[x,y,z]) * >> [],[[1/3,1/3,1/3]] * extrema(x*y^2*z^2,x+y+z=5,[x,y,z]) * >> [],[[1,2,2]] * extrema(4y-2z,[2x-y-z=2,x^2+y^2=1],[x,y,z]) * >> [[2*sqrt(13)/13,-3*sqrt(13)/13,(7*sqrt(13)-26)/13]], * [[-2*sqrt(13)/13,3*sqrt(13)/13,(-7*sqrt(13)-26)/13]] * extrema((x-3)^2+(y-1)^2+(z-1)^2,x^2+y^2+z^2=4,[x,y,z]) * >> [[6*sqrt(11)/11,2*sqrt(11)/11,2*sqrt(11)/11]], * [[-6*sqrt(11)/11,-2*sqrt(11)/11,-2*sqrt(11)/11]] * extrema(x+3y-z,2x^2+y^2=z,[x,y,z]) * >> [],[[1/4,3/2,19/8]] * extrema(2x*y+2y*z+x*z,x*y*z=4,[x,y,z]) * >> [[2,1,2]],[] * extrema(x+y+z,[x^2+y^2=1,2x+z=1],[x,y,z]) * >> [[sqrt(2)/2,-sqrt(2)/2,-sqrt(2)+1]],[[-sqrt(2)/2,sqrt(2)/2,sqrt(2)+1]] * assume(a>0);extrema(x+y+z,[y^2-x^2=a,x+2z=1],[x,y,z]) * >> [[-sqrt(3)*sqrt(a)/3,2*sqrt(3)*sqrt(a)/3,(sqrt(3)*sqrt(a)+3)/6]], * [[sqrt(3)*sqrt(a)/3,-2*sqrt(3)*sqrt(a)/3,(-sqrt(3)*sqrt(a)+3)/6]] * extrema((x-u)^2+(y-v)^2,[x^2/4+y^2/9=1,(u-3)^2+(v+5)^2=1],[u,v,x,y]) * >> [[2.35433932354,-4.23637555425,0.982084902545,-2.61340692712]], * [[3.41406613851,-5.91024679428,-0.580422508346,2.87088778158]] * extrema(x2^6+x1^3+4x1+4x2,x1^5+x2^4+x1+x2=0,[x1,x2]) * >> [[-0.787457596325,0.758772338924],[-0.784754836317,-1.23363062357]], * [[0.154340184382,-0.155005038065]] * extrema(x*y,-2x^3+15x^2*y+11y^3-24y=0,[x,y]) * >> [[sqrt(17)*sqrt(3*sqrt(33)+29)/17,sqrt(-15*sqrt(33)+127)*sqrt(187)/187], * [-sqrt(17)*sqrt(3*sqrt(33)+29)/17,-sqrt(-15*sqrt(33)+127)*sqrt(187)/187], * [sqrt(-3*sqrt(33)+29)*sqrt(17)/17,-sqrt(15*sqrt(33)+127)*sqrt(187)/187], * [-sqrt(-3*sqrt(33)+29)*sqrt(17)/17,sqrt(15*sqrt(33)+127)*sqrt(187)/187]], * [[1,1],[-1,-1],[0,0]] * extrema(x2^4-x1^4-x2^8+x1^10,[x1,x2],order_size=1) * >> [[0,0],[0,-(1/2)^(1/4)],[0,(1/2)^(1/4)],[-(2/5)^(1/6),0],[-(2/5)^(1/6),-(1/2)^(1/4)], * [-(2/5)^(1/6),(1/2)^(1/4)],[(2/5)^(1/6),0],[(2/5)^(1/6),-(1/2)^(1/4)],[(2/5)^(1/6),(1/2)^(1/4)]] * extrema(x2^4-x1^4-x2^8+x1^10,[x1,x2]) * >> [[(2/5)^(1/6),0],[-(2/5)^(1/6),0]],[[0,(1/2)^(1/4)],[0,-(1/2)^(1/4)]] * extrema(x2^6+x1^3+4x1+4x2,x1^5+x2^4+x1+x2=0,[x1,x2]) * >> [[-0.787457596325,0.758772338924],[-0.784754836317,-1.23363062357]], * [[0.154340184382,-0.155005038065]] * extrema(x2^6+x1^3+2x1^2-x2^2+4x1+4x2,x1^5+x2^4+x1+x2=0,[x1,x2]) * >> [[-0.662879934158,-1.18571027742],[0,0]],[[0.301887394815,-0.314132376868]] * extrema(3x^2-2x*y+y^2-8y,[x,y]) * >> [[2,6]],[] * extrema(x^3+3x*y^2-15x-12y,[x,y]) * >> [[2,1]],[[-2,-1]] * extrema(4x*y-x^4-y^4,[x,y]) * >> [],[[1,1],[-1,-1]] * extrema(x*sin(y),[x,y]) * >> [],[] * extrema(x^4+y^4,[x,y]) * >> [[0,0]],[] * extrema(x^3*y-x*y^3,[x,y]) //dog saddle at origin * >> [],[] * extrema(x^2+y^2+z^2,x^4+y^4+z^4=1,[x,y,z]) * >> [[0,0,1],[0,0,-1]],[] * extrema(3x+3y+8z,[x^2+z^2=1,y^2+z^2=1],[x,y,z]) * >> [[-3/5,-3/5,-4/5]],[[3/5,3/5,4/5]] * extrema(2x^2+y^2,x^4-x^2+y^2=5,[x,y]) * >> [[0,-sqrt(5)],[0,sqrt(5)]], * [[-1/2*sqrt(6),-1/2*sqrt(17)],[-1/2*sqrt(6),1/2*sqrt(17)], * [1/2*sqrt(6),-1/2*sqrt(17)],[1/2*sqrt(6),1/2*sqrt(17)]] * extrema((3x^4-4x^3-12x^2+18)/(12*(1+4y^2)),[x,y]) * >> [[2,0]],[[0,0]] * extrema(x-y+z,[x^2+y^2+z^2=1,x+y+2z=1],[x,y,z]) * >> [[(-2*sqrt(70)+7)/42,(4*sqrt(70)+7)/42,(-sqrt(70)+14)/42]], * [[(2*sqrt(70)+7)/42,(-4*sqrt(70)+7)/42,(sqrt(70)+14)/42]] * extrema(ln(x)+2*ln(y)+3*ln(z)+4*ln(u)+5*ln(v),x+y+z+u+v=1,[x,y,z,u,v]) * >> [],[[1/15,2/15,1/5,4/15,1/3]] * extrema(x*y*z,-2x^3+15x^2*y+11y^3-24y=0,[x,y,z]) * >> [],[] * extrema(x+y-exp(x)-exp(y)-exp(x+y),[x,y]) * >> [],[[ln(-1/2*(-sqrt(5)+1)),ln(-1/2*(-sqrt(5)+1))]] * extrema(x^2*sin(y)-4*x,[x,y]) // has two saddle points * >> [],[] * extrema((1+y*sinh(x))/(1+y^2+tanh(x)^2),[x,y]) * >> [],[[0,0]] * extrema((1+y*sinh(x))/(1+y^2+tanh(x)^2),y=x^2,[x,y]) * >> [[1.42217627369,2.02258535346]],[[8.69443642205e-16,7.55932246971e-31]] */ gen _extrema(const gen & g,GIAC_CONTEXT) { if (g.type==_STRNG && g.subtype==-1) return g; if (g.type!=_VECT || g.subtype!=_SEQ__VECT) return gentypeerr(contextptr); vecteur & gv = *g._VECTptr,constr; int order_size=5; // will not compute derivatives of order higher than 'order_size' int ngv=gv.size(); if (gv.back().is_symb_of_sommet(at_equal)) { vecteur & v = *gv.back()._SYMBptr->feuille._VECTptr; if (v[0]==at_order_size && is_integer(v[1])) { order_size=v[1].val; --ngv; } } if (ngv<2 || ngv>3) return gensizeerr(contextptr); // get the variables int nv; vecteur vars,oldvars,initial; // parse variables and their ranges, if given if ((nv=(oldvars=parse_varlist(gv,ngv,vars,initial,contextptr)).size())==0) return gentypeerr(contextptr); if (!initial.empty() && int(initial.size())first); } return cv; } // return sequence of minima and maxima in separate lists and report non-extrema vecteur minv,maxv; for (gen_map::const_iterator it=res.begin();it!=res.end();++it) { gen dispt(nv==1?symb_equal(oldvars[0],it->first):_zip(makesequence(at_equal,oldvars,it->first),contextptr)); switch(it->second.val) { case _CPCLASS_MIN: minv.push_back(it->first); break; case _CPCLASS_MAX: maxv.push_back(it->first); break; case _CPCLASS_SADDLE: cout << dispt << (nv==1?": inflection point":": saddle point") << endl; break; case _CPCLASS_POSSIBLE_MIN: cout << dispt << ": possible local minimum" << endl; break; case _CPCLASS_POSSIBLE_MAX: cout << dispt << ": possible local maximum" << endl; break; case _CPCLASS_UNDECIDED: cout << dispt << ": unclassified critical point" << endl; break; } } return makesequence(minv,maxv); } static const char _extrema_s []="extrema"; static define_unary_function_eval (__extrema,&_extrema,_extrema_s); define_unary_function_ptr5(at_extrema,alias_at_extrema,&__extrema,0,true) /* * Compute the value of expression f(var) (or |f(var)| if 'absolute' is true) * for var=a. */ gen compf(const gen & f,identificateur & x,gen & a,bool absolute,GIAC_CONTEXT) { gen val(subst(f,x,a,false,contextptr)); return _evalf(absolute?_abs(val,contextptr):val,contextptr); } /* * find zero of expression f(x) for x in [a,b] using Brent solver */ gen find_zero(const gen & f,identificateur & x,gen & a,gen & b,GIAC_CONTEXT) { gen I(symb_interval(a,b)); gen var(symb_equal(x,I)); vecteur sol(*_fsolve(makesequence(f,var,_BRENT_SOLVER),contextptr)._VECTptr); return sol.empty()?(a+b)/2:sol[0]; } /* * Find point of maximum/minimum of unimodal expression f(x) in [a,b] using the * golden-section search. */ gen find_peak(const gen & f,identificateur & x,gen & a_orig,gen & b_orig,GIAC_CONTEXT) { gen a(a_orig),b(b_orig); gen c(b-(b-a)/GOLDEN_RATIO),d(a+(b-a)/GOLDEN_RATIO); while (is_strictly_greater(_abs(c-d,contextptr),epsilon(contextptr),contextptr)) { gen fc(compf(f,x,c,true,contextptr)),fd(compf(f,x,d,true,contextptr)); if (is_strictly_greater(fc,fd,contextptr)) b=d; else a=c; c=b-(b-a)/GOLDEN_RATIO; d=a+(b-a)/GOLDEN_RATIO; } return (a+b)/2; } /* * Compute n Chebyshev nodes in [a,b]. */ vecteur chebyshev_nodes(gen & a,gen & b,int n,GIAC_CONTEXT) { vecteur nodes(1,a); for (int i=1;i<=n;++i) { nodes.push_back(_evalf((a+b)/2+(b-a)*symbolic(at_cos,((2*i-1)*cst_pi/(2*n)))/2,contextptr)); } nodes.push_back(b); return *_sort(nodes,contextptr)._VECTptr; } /* * Implementation of Remez method for minimax polynomial approximation of a * continuous bounded function, which is not necessary differentiable in all * points of (a,b). * * Source: http://homepages.rpi.edu/~tasisa/remez.pdf * * Usage * ^^^^^ * minimax(expr,var=a..b,n,[opts]) * * Parameters * ^^^^^^^^^^ * - expr : expression to be approximated on [a,b] * - var : variable * - a,b : bounds of the function domain * - n : degree of the minimax approximation polynomial * - opts (optional) : sequence of options * * This function uses 'compf', 'find_zero' and 'find_peak'. It does not use * derivatives to determine points of local extrema of error function, but * instead implements the golden search algorithm to find these points in the * exchange phase of Remez method. * * The returned polynomial may have degree lower than n, because the latter is * decremented during execution of the algorithm if there is no unique solution * for coefficients of a nth degree polynomial. After each decrement, the * algorithm is restarted. If the degree of resulting polynomial is m' which limits the number of * iterations. By default, it is unlimited. * * Be aware that, in some cases, the result with high n may be unsatisfying, * producing larger error than the polynomials for smaller n. This happens * because of the rounding errors. Nevertheless, a good approximation of an * "almost" smooth function can usually be obtained with n less than 30. Highly * oscillating functions containing sharp cusps and spikes will probably be * approximated poorly. * * Examples * ^^^^^^^^ * minimax(x*exp(-x),x=0..10,24) * minimax(x*sin(x),x=0..10,25) * minimax(ln(2+x-sin(x)^2),x=0..2*pi,20) * minimax(cos(x^2-x+1),x=-2..2,40) * minimax(atan(x),x=-5..5,25) * minimax(tanh(sin(9x)),x=-1/2..1/2,40) * minimax(abs(x),x=-1..1,20) * minimax(abs(x)*sqrt(abs(x)),x=-2..2,15) * minimax(min(1/cosh(3*sin(10x)),sin(9x)),x=-0.3..0.4,25) * minimax(when(x==0,0,exp(-1/x^2)),x=-1..1,30) */ gen _minimax(const gen & g,GIAC_CONTEXT) { if (g.type==_STRNG && g.subtype==-1) return g; if (g.type!=_VECT || g.subtype!=_SEQ__VECT) return gentypeerr(contextptr); vecteur & gv = *g._VECTptr; if (gv.size()<3) return gensizeerr(contextptr); if (!gv[1].is_symb_of_sommet(at_equal) || !is_integer(gv[2])) return gentypeerr(contextptr); // detect parameters vecteur s(*gv[1]._SYMBptr->feuille._VECTptr); if (s[0].type!=_IDNT || !s[1].is_symb_of_sommet(at_interval)) return gentypeerr((contextptr)); identificateur x(*s[0]._IDNTptr); s=*s[1]._SYMBptr->feuille._VECTptr; gen a(_evalf(s[0],contextptr)),b(_evalf(s[1],contextptr)); if (!is_strictly_greater(b,a,contextptr)) return gentypeerr(contextptr); gen & f=gv[0]; int n=gv[2].val; gen threshold(1.02); // threshold for stopping criterion // detect options int limit=0; //bool poly=true; for (const_iterateur it=gv.begin()+3;it!=gv.end();++it) { if (it->is_symb_of_sommet(at_equal)) { vecteur & p=*it->_SYMBptr->feuille._VECTptr; if (p[0]==at_limit) { if (!is_integer(p[1]) || !is_strictly_positive(p[1],contextptr)) return gentypeerr(contextptr); limit=p[1].val; } } else if (is_integer(*it)) { switch (it->val) { // case _FRAC: // poly=false; // break; } } } // create Chebyshev nodes to start with vecteur nodes(chebyshev_nodes(a,b,n,contextptr)); gen p,best_p,best_emax,emax,emin; int iteration_count=0; while (true) { // iterate the algorithm iteration_count++; if (n<1 || (limit>0 && iteration_count>limit)) break; // compute polynomial p matrice m; vecteur fv; for (int i=0;iempty()) { // Solution is not unique, it contains a symbol. // Decrease n and start over. nodes=chebyshev_nodes(a,b,--n,contextptr); continue; } p=gen(0); for (int i=0;i0 && i= E is required to continue, also check // if the threshold is reached, i.e. the difference // between emax and emin is at least fifty times // smaller than emax if (is_strictly_greater(sol.back(),emin,contextptr) || is_greater(threshold*emin,emax,contextptr)) { break; } } cout << "max. absolute error: " << best_emax << endl; return best_p; } static const char _minimax_s []="minimax"; static define_unary_function_eval (__minimax,&_minimax,_minimax_s); define_unary_function_ptr5(at_minimax,alias_at_minimax,&__minimax,0,true) /* * North-West-Corner method giving the initial feasible solution to the * transportatiom problem with given supply and demand vectors. It handles * degeneracy cases (assignment problems, for example, always have degenerate * solutions). */ matrice north_west_corner(vecteur & supply,vecteur & demand,GIAC_CONTEXT) { int m=supply.size(),n=demand.size(); gen eps(exact(epsilon(contextptr)/2,contextptr)); matrice X; for (int k=0;kat(j)=a; int k=i+j; if (u+a==supply[i]) ++i; if (v+a==demand[j]) ++j; if (iat(j)=eps; } } return X; } /* * Stepping stone path method for determining a closed path "jumping" from one * positive element of X to another in the same row or column. */ vector stepping_stone_path(vector & path_orig,matrice & X,GIAC_CONTEXT) { vector path(path_orig); int I=path.back().first,J=path.back().second; int m=X.size(),n=X.front()._VECTptr->size(); if (path.size()>1 && path.front().second==J) return path; bool hrz=path.size()%2==1; for (int i=0;i<(hrz?n:m);++i) { int cnt=0; for (vector::const_iterator it=path.begin();it!=path.end();++it) { if ((hrz && it->second==i) || (!hrz && it->first==i)) ++cnt; } if (cnt<2 && !is_exactly_zero(X[hrz?I:i][hrz?i:J])) { path.push_back(make_pair(hrz?I:i,hrz?i:J)); vector fullpath(stepping_stone_path(path,X,contextptr)); if (!fullpath.empty()) return fullpath; path.pop_back(); } } return vector(0); } /* * Implementation of MODI (modified ditribution) method. It handles degenerate * solutions if they appear during the process. */ void modi(matrice & P_orig,matrice & X,gen & M,GIAC_CONTEXT) { matrice P(P_orig); int m=X.size(),n=X.front()._VECTptr->size(); vecteur u(m),v(n); gen eps(exact(epsilon(contextptr)/2,contextptr)); if (M.type==_IDNT) { gen largest(0); for (int i=0;i path; path.push_back(make_pair(I,J)); path=stepping_stone_path(path,X,contextptr); gen d(X[path.at(1).first][path.at(1).second]); for (vector::const_iterator it=path.begin()+3;itfirst][it->second]),contextptr); } for (int i=0;iat(path.at(i).second); gen x(Xij+(i%2?-d:d)); bool has_zero=false; for (vector::const_iterator it=path.begin();it!=path.end();++it) { if (is_exactly_zero(X[it->first][it->second])) { has_zero=true; break; } } if ((!is_exactly_zero(x) && is_strictly_greater(gen(1)/gen(2),x,contextptr)) || (is_exactly_zero(x) && has_zero)) x=eps; Xij=x; } } X=*exact(_epsilon2zero(_evalf(X,contextptr),contextptr),contextptr)._VECTptr; } /* * Function 'tpsolve' solves a transportation problem using MODI method. * * Usage * ^^^^^ * tpsolve(supply,demand,cost_matrix) * * Parameters * ^^^^^^^^^^ * - supply : source capacity (vector of m positive integers) * - demand : destination demand (vector of n positive integers) * - cost_matrix : real matrix C=[c_ij] of type mXn where c_ij is cost of * transporting an unit from ith source to jth destination * (a nonnegative number) * * Supply and demand vectors should contain only positive integers. Cost matrix * must be consisted of nonnegative real numbers, which do not have to be * integers. There is a possibility of adding a certain symbol to cost matrix, * usually M, to indicate the "infinite cost", effectively forbidding the * transportation on a certain route. The notation of the symbol may be chosen * arbitrarily, but must be used consistently within a single problem. * * The return value is a sequence of total (minimal) cost and matrix X=[x_ij] * of type mXn where x_ij is equal to number of units which have to be shipped * from ith source to jth destination, for all i=1,2,..,m and j=1,2,..,n. * * This function uses 'north_west_corner' to determine initial feasible * solution and then applies MODI method to optimize it (function 'modi', which * uses 'stepping_stone_path'). Also, it is capable of handling degeneracy of * the initial solution and during iterations of MODI method. * * If the given problem is not balanced, i.e. if supply exceeds demand or vice * versa, dummy supply/demand points will be automatically added to the * problem, augmenting the cost matrix with zeros. Resulting matrix will not * contain dummy point. * * Examples * ^^^^^^^^ * Balanced transportation problem: * tpsolve([12,17,11],[10,10,10,10],[[500,750,300,450],[650,800,400,600],[400,700,500,550]]) * >> 2020,[[0,0,2,10],[0,9,8,0],[10,1,0,0]] * Non-balanced transportation problem: * tpsolve([7,10,8,8,9,6],[9,6,12,8,10],[[36,40,32,43,29],[28,27,29,40,38],[34,35,41,29,31],[41,42,35,27,36],[25,28,40,34,38],[31,30,43,38,40]]) * >> [[0,0,2,0,5],[0,0,10,0,0],[0,0,0,0,5],[0,0,0,8,0],[9,0,0,0,0],[0,6,0,0,0]] * Transportation problem with forbidden routes: * tpsolve([95,70,165,165],[195,150,30,45,75],[[15,M,45,M,0],[12,40,M,M,0],[0,15,25,25,0],[M,0,M,12,0]]) * >> [[20,0,0,0,75],[70,0,0,0,0],[105,0,30,30,0],[0,150,0,15,0]] * Assignment problem: * tpsolve([1,1,1,1],[1,1,1,1],[[10,12,9,11],[5,10,7,8],[12,14,13,11],[8,15,11,9]]) * >> [[0,0,1,0],[1,0,0,0],[0,1,0,0],[0,0,0,1]] */ gen _tpsolve(const gen & g,GIAC_CONTEXT) { if (g.type==_STRNG && g.subtype==-1) return g; if (g.type!=_VECT || g.subtype!=_SEQ__VECT) return gentypeerr(contextptr); vecteur & gv = *g._VECTptr; if (gv.size()<3) return gensizeerr(contextptr); if (gv[0].type!=_VECT || gv[1].type!=_VECT || gv[2].type!=_VECT || !ckmatrix(*gv[2]._VECTptr)) return gentypeerr(contextptr); vecteur supply(*gv[0]._VECTptr),demand(*gv[1]._VECTptr); matrice P(*gv[2]._VECTptr); vecteur sy(*_lname(P,contextptr)._VECTptr); int m=supply.size(),n=demand.size(); if (sy.size()>1 || m!=int(P.size()) || n!=int(P.front()._VECTptr->size())) return gensizeerr(contextptr); gen M(sy.size()==1 && sy[0].type==_IDNT?sy[0]:0); gen ts(_sum(supply,contextptr)),td(_sum(demand,contextptr)); if (ts!=td) { cout << "Warning: transportation problem is not balanced" << endl; if (is_greater(ts,td,contextptr)) { demand.push_back(ts-td); P=mtran(P); P.push_back(vecteur(m,0)); P=mtran(P); } else { supply.push_back(td-ts); P.push_back(vecteur(n,0)); } } matrice X(north_west_corner(supply,demand,contextptr)); modi(P,X,M,contextptr); if (is_strictly_greater(ts,td,contextptr)) { X=mtran(X); X.pop_back(); X=mtran(X); } else if (is_strictly_greater(td,ts,contextptr)) X.pop_back(); gen cost(0); for (int i=0;i & invdiff,GIAC_CONTEXT) { pint I=make_pair(n,k); assert(n<=k); gen res(invdiff[I]); if (!is_zero(res)) return res; if (n==0) return invdiff[I]=yv[k]; if (n==1) return invdiff[I]=(xv[k]-xv[0])/(yv[k]-yv[0]); gen d1(compute_invdiff(n-1,n-1,xv,yv,invdiff,contextptr)); gen d2(compute_invdiff(n-1,k,xv,yv,invdiff,contextptr)); return invdiff[I]=(xv[k]-xv[n-1])/(d2-d1); } gen thiele(int k,vecteur & xv,vecteur & yv,identificateur & var,map< pint,gen > & invdiff,GIAC_CONTEXT) { if (k==int(xv.size())) return gen(0); gen phi(compute_invdiff(k,k,xv,yv,invdiff,contextptr)); return (var-xv[k-1])/(phi+thiele(k+1,xv,yv,var,invdiff,contextptr)); } /* * 'thiele' computes rational interpolation for the given list of points using * Thiele's method with continued fractions. * * Source: http://www.astro.ufl.edu/~kallrath/files/pade.pdf (see page 19) * * Usage * ^^^^^ * thiele(data,v) * or thiele(data_x,data_y,v) * * Parameters * ^^^^^^^^^^ * - data : list of points [[x1,y1],[x2,y2],...,[xn,yn]] * - v : identifier (may be any symbolic expression) * - data_x : list of x coordinates [x1,x2,...,xn] * - data_y : list of y coordinates [y1,y2,...,yn] * * The return value is an expression R(v), where R is rational interpolant of * the given set of points. * * Note that the interpolant may have singularities in * [min(data_x),max(data_x)]. * * Example * ^^^^^^^ * Function f(x)=(1-x^4)*exp(1-x^3) is sampled on interval [-1,2] in 13 * equidistant points: * * data_x:=[-1,-0.75,-0.5,-0.25,0,0.25,0.5,0.75,1,1.25,1.5,1.75,2], * data_y:=[0.0,2.83341735599,2.88770329586,2.75030303645,2.71828182846, * 2.66568510781,2.24894558809,1.21863761951,0.0,-0.555711613283, * -0.377871362418,-0.107135851128,-0.0136782294833] * * To obtain rational function passing through these points, input: * thiele(data_x,data_y,x) * Output: * (-1.55286115659*x^6+5.87298387514*x^5-5.4439152812*x^4+1.68655817708*x^3 * -2.40784868317*x^2-7.55954205222*x+9.40462512097)/(x^6-1.24295718965*x^5 * -1.33526268624*x^4+4.03629272425*x^3-0.885419321*x^2-2.77913222418*x+3.45976823393) */ gen _thiele(const gen & g,GIAC_CONTEXT) { if (g.type==_STRNG && g.subtype==-1) return g; if (g.type!=_VECT || g.subtype!=_SEQ__VECT) return gentypeerr(contextptr); vecteur & gv = *g._VECTptr; if (gv.size()<2) return gensizeerr(contextptr); vecteur xv,yv; gen x; if (gv[0].type!=_VECT) return gentypeerr(contextptr); if (ckmatrix(gv[0])) { matrice m(mtran(*gv[0]._VECTptr)); if (m.size()!=2) return gensizeerr(contextptr); xv=*m[0]._VECTptr; yv=*m[1]._VECTptr; x=gv[1]; } else { if (gv[1].type!=_VECT) return gentypeerr(contextptr); if (gv[0]._VECTptr->size()!=gv[1]._VECTptr->size()) return gensizeerr(contextptr); xv=*gv[0]._VECTptr; yv=*gv[1]._VECTptr; x=gv[2]; } gen var(x.type==_IDNT?x:identificateur(" x")); map< pint,gen > invdiff; gen rat(yv[0]+thiele(1,xv,yv,*var._IDNTptr,invdiff,contextptr)); if (x.type==_IDNT) { // detect singularities gen den(_denom(rat,contextptr)); matrice sing; if (*_lname(den,contextptr)._VECTptr==vecteur(1,x)) { for (int i=0;i