简体   繁体   English

如何绘制grad(f(x,y))?

[英]How to plot grad(f(x,y))?

I want to calculate and plot a gradient of any scalar function of two variables. 我想计算并绘制两个变量的任何标量函数的梯度。 If you really want a concrete example, lets say f=x^2+y^2 where x goes from -10 to 10 and same for y. 如果你真的想要一个具体的例子,让我们说f = x ^ 2 + y ^ 2,其中x从-10到10,对y来说是相同的。 How do I calculate and plot grad(f)? 如何计算和绘制grad(f)? The solution should be vector and I should see vector lines. 解决方案应该是矢量,我应该看到矢量线。 I am new to python so please use simple words. 我是python的新手所以请使用简单的单词。

EDIT: 编辑:

@Andras Deak: thank you for your post, i tried what you suggested and instead of your test function (fun=3*x^2-5*y^2) I used function that i defined as V(x,y); @Andras Deak:谢谢你的帖子,我尝试了你的建议,而不是你的测试功能(fun = 3 * x ^ 2-5 * y ^ 2)我使用了我定义为V(x,y)的函数; this is how the code looks like but it reports an error 这是代码的样子,但报告错误

import numpy as np
import math 
import sympy
import matplotlib.pyplot as plt

def V(x,y):
    t=[]
    for k in range (1,3): 
        for l in range (1,3):
            t.append(0.000001*np.sin(2*math.pi*k*0.5)/((4*(math.pi)**2)* (k**2+l**2)))  
            term = t* np.sin(2 * math.pi * k * x/0.004) * np.cos(2 * math.pi * l * y/0.004)
            return term 
    return term.sum()

x,y=sympy.symbols('x y')
fun=V(x,y)
gradfun=[sympy.diff(fun,var) for var in (x,y)]
numgradfun=sympy.lambdify([x,y],gradfun)

X,Y=np.meshgrid(np.arange(-10,11),np.arange(-10,11))
graddat=numgradfun(X,Y)
plt.figure()
plt.quiver(X,Y,graddat[0],graddat[1])
plt.show()

AttributeError: 'Mul' object has no attribute 'sin'

And lets say I remove sin, I get another error: 让我说我删除罪,我得到另一个错误:

TypeError: can't multiply sequence by non-int of type 'Mul'

I read tutorial for sympy and it says "The real power of a symbolic computation system such as SymPy is the ability to do all sorts of computations symbolically". 我阅读了针对sympy的教程并且它说“像SymPy这样的符号计算系统的真正力量是能够象征性地进行各种计算”。 I get this, I just dont get why I cannot multiply x and y symbols with float numbers. 我得到了这个,我只是不明白为什么我不能将x和y符号乘以浮点数。

What is the way around this? 这是怎么回事? :( Help please! :( 请帮忙!

UPDATE UPDATE

@Andras Deak: I wanted to make things shorter so I removed many constants from the original formulas for V(x,y) and Cn*Dm. @Andras Deak:我想让事情更短,所以我从V(x,y)和Cn * Dm的原始公式中删除了许多常量。 As you pointed out, that caused the sin function to always return 0 (i just noticed). 正如你所指出的,这导致sin函数总是返回0(我刚注意到)。 Apologies for that. 为此道歉。 I will update the post later today when i read your comment in details. 我将在今天晚些时候更新帖子,当时我会详细阅读您的评论。 Big thanks! 十分感谢!

UPDATE 2 I changed coefficients in my expression for voltage and this is the result: 更新2我在表达式中更改了电压系数,这是结果:

情节

It looks good except that the arrows point in the opposite direction (they are supposed to go out of the reddish dot and into the blue one). 它看起来不错,只是箭头指向相反的方向(它们应该从红色点出来并进入蓝色点)。 Do you know how I could change that? 你知道我怎么能改变它吗? And if possible, could you please tell me the way to increase the size of the arrows? 如果可能的话,你能告诉我增加箭头大小的方法吗? I tried what was suggested in another topic ( Computing and drawing vector fields ): 我尝试了另一个主题( 计算和绘图矢量字段 )中的建议:

skip = (slice(None, None, 3), slice(None, None, 3))

This plots only every third arrow and matplotlib does the autoscale but it doesnt work for me (nothing happens when i add this, for any number that i enter) You were already of huge help , i cannot thank you enough! 这只绘制了每三个箭头和matplotlib进行自动缩放,但它对我不起作用(当我添加此项时,没有任何反应,对于我输入的任何数字)你已经得到了很大的帮助,我不能够感谢你!

Here's a solution using sympy and numpy . 这是一个使用sympynumpy的解决方案。 This is the first time I use sympy, so others will/could probably come up with much better and more elegant solutions. 这是我第一次使用sympy,因此其他人/可能会提出更好,更优雅的解决方案。

import sympy

#define symbolic vars, function
x,y=sympy.symbols('x y')
fun=3*x**2-5*y**2

#take the gradient symbolically
gradfun=[sympy.diff(fun,var) for var in (x,y)]

#turn into a bivariate lambda for numpy
numgradfun=sympy.lambdify([x,y],gradfun)

now you can use numgradfun(1,3) to compute the gradient at (x,y)==(1,3) . 现在你可以使用numgradfun(1,3)计算(x,y)==(1,3)处的梯度。 This function can then be used for plotting, which you said you can do. 然后,此功能可用于绘图,您可以这样做。

For plotting, you can use, for instance, matplotlib 's quiver , like so: 对于绘图,您可以使用matplotlibquiver ,例如:

import numpy as np
import matplotlib.pyplot as plt

X,Y=np.meshgrid(np.arange(-10,11),np.arange(-10,11))
graddat=numgradfun(X,Y)

plt.figure()
plt.quiver(X,Y,graddat[0],graddat[1])
plt.show()

在此输入图像描述

UPDATE UPDATE

You added a specification for your function to be computed. 您为要计算的函数添加了规范。 It contains the product of terms depending on x and y , which seems to break my above solution. 它包含取决于xy的术语的乘积,这似乎打破了我的上述解决方案。 I managed to come up with a new one to suit your needs. 我设法想出一个新的,以满足您的需求。 However, your function seems to make little sense. 但是,你的功能似乎毫无意义。 From your edited question: 来自您编辑的问题:

t.append(0.000001*np.sin(2*math.pi*k*0.5)/((4*(math.pi)**2)* (k**2+l**2)))
term = t* np.sin(2 * math.pi * k * x/0.004) * np.cos(2 * math.pi * l * y/0.004)

On the other hand, from your corresponding comment to this answer: 另一方面,从您对应答案的相应评论:

V(x,y) = Sum over n and m of [Cn * Dm * sin(2pinx) * cos(2pimy)]; V(x,y)= [Cn * Dm * sin(2pinx)* cos(2pimy)]的n和m之和; sum goes from -10 to 10; 总和从-10到10; Cn and Dm are coefficients, and i calculated that CkDl = sin(2pik)/(k^2 +l^2) (i used here k and l as one of the indices from the sum over n and m). Cn和Dm是系数,并且我计算出CkD1 = sin(2pik)/(k ^ 2 + 1 ^ 2)(这里我使用k和l作为来自n和m之和的索引之一)。

I have several problems with this: both sin(2*pi*k) and sin(2*pi*k/2) (the two competing versions in the prefactor are always zero for integer k , giving you a constant zero V at every (x,y) . Furthermore, in your code you have magical frequency factors in the trigonometric functions, which are missing from the comment. If you multiply your x by 4e-3 , you drastically change the spatial dependence of your function (by changing the wavelength by roughly a factor of a thousand). So you should really decide what your function is. 我有几个问题: sin(2*pi*k)sin(2*pi*k/2) (前因子中的两个竞争版本对于整数k总是为零,每次给你一个恒定的零V (x,y) 。此外,在你的代码中,你在三角函数中有神奇的频率因子,它们在评论中缺失。如果你将x乘以4e-3 ,你就会大大改变函数的空间依赖性(通过改变波长大约是一千倍。所以你应该真正决定你的功能是什么。

So here's a solution, where I assumed 所以这是一个解决方案,我假设了

V(x,y)=sum_{k,l = 1 to 10} C_{k,l} * sin(2*pi*k*x)*cos(2*pi*l*y), with V(x,y)= sum_ {k,l = 1到10} C_ {k,l} * sin(2 * pi * k * x)* cos(2 * pi * l * y),
C_{k,l}=sin(2*pi*k/4)/((4*pi^2)*(k^2+l^2))*1e-6 C_ {K,L} = SIN(2 * PI * K / 4)/((4 * PI ^ 2)*(K ^ 2 + 1 ^ 2))* 1E-6

This is a combination of your various versions of the function, with the modification of sin(2*pi*k/4) in the prefactor in order to have a non-zero function. 这是函数的各种版本的组合,在prefactor中修改sin(2*pi*k/4)以具有非零函数。 I expect you to be able to fix the numerical factors to your actual needs, after you figure out the proper mathematical model. 在您找到合适的数学模型后,我希望您能够根据实际需要确定数值因子。

So here's the full code: 所以这是完整的代码:

import sympy as sp
import numpy as np
import matplotlib.pyplot as plt

def CD(k,l):
    #return sp.sin(2*sp.pi*k/2)/((4*sp.pi**2)*(k**2+l**2))*1e-6
    return sp.sin(2*sp.pi*k/4)/((4*sp.pi**2)*(k**2+l**2))*1e-6

def Vkl(x,y,k,l):
    return CD(k,l)*sp.sin(2*sp.pi*k*x)*sp.cos(2*sp.pi*l*y)

def V(x,y,kmax,lmax):
    k,l=sp.symbols('k l',integers=True)
    return sp.summation(Vkl(x,y,k,l),(k,1,kmax),(l,1,lmax))


#define symbolic vars, function
kmax=10
lmax=10
x,y=sp.symbols('x y')
fun=V(x,y,kmax,lmax)

#take the gradient symbolically
gradfun=[sp.diff(fun,var) for var in (x,y)]

#turn into bivariate lambda for numpy
numgradfun=sp.lambdify([x,y],gradfun,'numpy')
numfun=sp.lambdify([x,y],fun,'numpy')

#plot
X,Y=np.meshgrid(np.linspace(-10,10,51),np.linspace(-10,10,51))
graddat=numgradfun(X,Y)
fundat=numfun(X,Y)

hf=plt.figure()
hc=plt.contourf(X,Y,fundat,np.linspace(fundat.min(),fundat.max(),25))
plt.quiver(X,Y,graddat[0],graddat[1])
plt.colorbar(hc)
plt.show()

I defined your V(x,y) function using some auxiliary functions for transparence. 我使用一些辅助功能来定义你的V(x,y)函数以实现透明度。 I left the summation cut-offs as literal parameters, kmax and lmax : in your code these were 3, in your comment they were said to be 10, and anyway they should be infinity. 我把总和截止值作为文字参数, kmaxlmax :在你的代码中这些是3,在你的评论中他们被认为是10,无论如何他们应该是无限的。

The gradient is taken the same way as before, but when converting to a numpy function using lambdify you have to set an additional string parameter, 'numpy' . 渐变采用与以前相同的方式,但是当使用lambdify转换为numpy函数时,您必须设置一个额外的字符串参数'numpy' This will alow the resulting numpy lambda to accept array input (essentially it will use np.sin instead of math.sin and the same for cos ). 这将使得生成的numpy lambda接受数组输入(基本上它将使用np.sin而不是math.sin ,而cos则相同)。

I also changed the definition of the grid from array to np.linspace : this is usually more convenient. 我还将网格的定义从array更改为np.linspace :这通常更方便。 Since your function is almost constant at integer grid points, I created a denser mesh for plotting (51 points while keeping your original limits of (-10,10) fixed). 由于你的函数在整数网格点几乎不变,我创建了一个更密集的网格用于绘图(51点,同时保持原始极限(-10,10)固定)。

For clarity I included a few more plots: a contourf to show the value of the function (contour lines should always be orthogonal to the gradient vectors), and a colorbar to indicate the value of the function. 为清楚起见,我还包括了几个图:一个用于显示函数值的contourf (轮廓线应始终与梯度向量正交),以及一个用于指示函数值的contourf条。 Here's the result: 这是结果:

在此输入图像描述

The composition is obviously not the best, but I didn't want to stray too much from your specifications. 组成显然不是最好的,但我不想偏离你的规格太多。 The arrows in this figure are actually hardly visible, but as you can see (and also evident from the definition of V ) your function is periodic, so if you plot the same thing with smaller limits and less grid points, you'll see more features and larger arrows. 这个图中的箭头实际上几乎看不到,但正如你所看到的(并且从V的定义中也很明显),你的函数是周期性的,所以如果用较小的限制和较少的网格点绘制相同的东西,你会看到更多功能和更大的箭头。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM