I'm new to python and i've become very interested in it's ability to produce fractal images. I've written a few simple ones myself, but I just discovered a script for Mandelbrot fractals... it produces beautiful, full color images at your desired resolution.... but the code is obfuscated to look like ASCII art of a Mandelbrot.... really cool, but silly if you want to read it easily. It contains functions I haven't learned yet in python, so if someone can indent the script to look like normal python script, that'd be great.
The script:
_ = (
255,
lambda
V ,B,c
:c and Y(V*V+B,B, c
-1)if(abs(V)<6)else
( 2+c-4*abs(V)**-0.4)/i
) ;v, x=7200,4800;C=range(v*x
);import struct;P=struct.pack;M,\
j ='<QIIHHHH',open('M.bmp','wb').write
for X in j('BM'+P(M,v*x*3+26,26,12,v,x,1,24))or C:
i ,Y=_;j(P('BBB',*(lambda T:(T*80+T**9
*i-950*T **99,T*70-880*T**18+701*
T **9 ,T*i**(1-T**45*2)))(sum(
[ Y(0,(A%3/3.+X%v+(X/v+
A/3/3.-x/2)/1j)*2.5
/x -2.7,i)**2 for \
A in C
[:9]])
/9)
) )
As I said, the art is cool, but too hard to read! If someone can do this, I'd be greatly thankful.
Note: This is not meant as a definitive answer, but as an effort for a step-by-step de-obfuscation. If you can provide an additional step to make things clearer, it would be great if you could add it to this answer.
Okay, let's start by giving the code proper newlines and indentation:
_ = (255,
lambda V, B, c: c and Y(V*V + B, B, c-1) if(abs(V) < 6) else (2 + c - 4 * abs(V)**-0.4)/i)
v, x = 7200, 4800
C = range(v*x)
import struct
P=struct.pack
M, j = '<QIIHHHH', open('M.bmp','wb').write
for X in j('BM' + P(M, v*x*3+26, 26, 12, v, x, 1, 24)) or C:
i, Y = _
j(
P('BBB',
*(lambda T: (T * 80 + T**9 * i - 950 * T**99, T*70 - 880* T**18 + 701* T**9, T* i**(1-T**45*2)))(
sum([Y(0, (A%3/3. + X%v + (X/v + A/3/3. - x/2)/1j) * 2.5/x - 2.7, i)**2 for A in C[:9]]) / 9
)
)
)
A bit better, but still confusing. For example, defining P as an alias for struct.pack, and all those two-declarations-in-one lines. If we get rid of them, and move the lambda definition outside the loop, we get this:
import struct
i = 255
Y = lambda V, B, c: c and Y(V*V + B, B, c-1) if(abs(V) < 6) else (2 + c - 4 * abs(V)**-0.4)/i
v = 7200
x = 4800
C = range(v*x)
M = '<QIIHHHH'
color = lambda T: (T * 80 + T**9 * i - 950 * T**99, T*70 - 880* T**18 + 701 * T**9, T * i**(1-T**45*2))
f = open('M.bmp','wb')
for X in f.write('BM' + struct.pack(M, v*x*3+26, 26, 12, v, x, 1, 24)) or C:
f.write(
struct.pack('BBB',
*color(sum([Y(0, (A%3/3. + X%v + (X/v + A/3/3. - x/2)/1j) * 2.5/x - 2.7, i)**2 for A in C[:9]]) / 9))
)
Things are starting to get a bit clearer now. The loop is writing color values for each pixel, and the lambda returns a 3-tuple, representing the blue, green and red values of each pixel.
import struct
i = 255
Y = lambda V, B, c: c and Y(V*V + B, B, c-1) if abs(V) < 6 else (
(2 + c - 4 * abs(V)**-0.4)/i)
v = 7200
x = 4800
C = range(v*x)
M = '<QIIHHHH'
color = lambda T: (T * 80 + T**9 * i - 950 * T**99,
T*70 - 880* T**18 + 701 * T**9,
T * i**(1-T**45*2))
f = open('M.bmp', 'wb')
for X in f.write('BM' + struct.pack(M, v*x*3+26, 26, 12, v, x, 1, 24)) or C:
f.write(struct.pack('BBB',
*color(sum(
[Y(0, (A%3/3. + X%v + (X/v + A/3/3. - x/2)/1j) * 2.5/x - 2.7, i)**2
for A in C[:9]]) / 9))
)
As per your comment, the asterisk (the star, *) unpacks a list into an argument list.
After many hours, here's the nearly 100 MB image produced of the Mandelbrot set:
Related
I can't get the results o the next equation using Sympy, maybe I'm using the wrong tool for solve this kind of problems, this is the code i'm using:
import sympy as sym
ss1=4.16
as1=0.97
fs1=0.38
cs1=51.29
ss2=4.16
as2=0.95
fs2=0.37
cs2=51.42
ss3=63.36
as3=13.58
fs3=3.06
cs3=6.88
ss5=1.94
as5=0.00
fs5=0.00
cs5=32.05
ss6=21.43
as6=3.82
fs6=52.28
cs6=4.56
ss7=7.45
as7=4.58
fs7=0.42
cs7=0.19
c,a,f,s,r1,r2,r3,r5,r6,r7 = sym.symbols('c,a,f,s,r1,r2,r3,r5,r6,r7')
eq1 = sym.Eq(cs1*r1+cs2*r2+cs3*r3+cs5*r5+cs6*r6+cs7*r7,c)
eq2 = sym.Eq(ss1*r1+ss2*r2+ss3*r3+ss5*r5+ss6*r6+ss7*r7,s)
eq3 = sym.Eq(as1*r1+as2*r2+as3*r3+as5*r5+as6*r6+as7*r7,a)
eq4 = sym.Eq(fs1*r1+fs2*r2+fs3*r3+fs5*r5+fs6*r6+fs7*r7,f)
eq5 = sym.Eq(r1+r2+r3+r5+r6+r7,1)
eq6 = sym.Eq(((100*c)/(2.8*s+1.18*a+0.65*f)), 98.5)
result = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6],(c,a,f,s,r1,r2,r3,r5,r6,r7))
print(result)
The result I get is this:
{c: 138.036371184195*r3 - 9.67494414291396*r5 + 59.8621991124251*r6 + 1.33640428817057*r7 + 19.9838541859147, a: -15.4586724898762*r3 - 2.44154705493631*r5 - 13.54879986345*r6 - 4.45713912125701*r7 + 5.78633012524389, f: -11.3543362449381*r3 - 1.11577352746816*r5 + 43.700600068275*r6 - 3.99356956062851*r7 + 2.78816506262195, s: 59.2*r3 - 2.22*r5 + 17.27*r6 + 3.29*r7 + 4.16, r1: -1404.43362449381*r3 - 74.5773527468157*r5 - 820.939993172501*r6 - 404.356956062851*r7 + 241.816506262195, r2: 1403.43362449381*r3 + 73.5773527468157*r5 + 819.939993172501*r6 + 403.356956062851*r7 - 240.816506262195}
Maybe I'm using the wrong tool, as you can see the goal is to find the right values for r1, r2, r3, r4, r6, r7 (the sum of all r's = 1) which satisfies the eq6 to be = 98.5
You have 1 equation and 4 variables (a, c, f, s): you need other 3 equations to find the values of all variables. Then you can do:
solve((eq1, eq2, eq3, eq4), (c, s, a, f))
I have a sympy expression I want to put numerical values in after differentiating it. The variables I want to replace are all the x[i], y[i] and R_abs[i] in the last expression and are numpy arrays a la
rx=np.array([-0.357, -0.742, -1.078, 0.206])
But trying subs or replace either doesn't do anything or raises the error that Symbols dont allow indexation for for example e1.subs(x[1],rx[0]). I pretty much went through every iteration I could think of to no avail.
import sympy as sp
r0,ge_x,ge_y,bx,by = sp.symbols('r0,ge_x,ge_y,bx,by', real=True) #Main symbols
i,x,y,R_abs = sp.symbols('i,x,y,R_abs', real=True) #Helper symbols
n=4
s2=sp.Sum((bx+r0*sp.Indexed('x',i)/sp.Indexed('R_abs',i)+ge_x*sp.Indexed('x',i)+ge_y*sp.Indexed('y',i)-sp.Indexed('x',i))**2+(by+r0*sp.Indexed('y',i)/sp.Indexed('R_abs',i)-ge_x*sp.Indexed('y',i)+ge_y*sp.Indexed('x',i)-sp.Indexed('y',i))**2,(i,1,n))
e1=sp.Eq(sp.diff(s2,bx).doit(),0)
With e1 then being
Eq(8*bx + 2*ge_x*x[1] + 2*ge_x*x[2] + 2*ge_x*x[3] + 2*ge_x*x[4] + 2*ge_y*y[1] + 2*ge_y*y[2] + 2*ge_y*y[3] + 2*ge_y*y[4] + 2*r0*x[4]/R_abs[4] + 2*r0*x[3]/R_abs[3] + 2*r0*x[2]/R_abs[2] + 2*r0*x[1]/R_abs[1] - 2*x[1] - 2*x[2] - 2*x[3] - 2*x[4], 0)
In here I would like to replace all the x, y, and R_abs with their numerical values.
I've always struggled with indexing in SymPy. Turns out, making Function instances are way easier than indexing instances of Symbol. It also makes notation simpler.
Also note that by using strings in your expression, I think SymPy makes its own symbols with those same string names but they can't be accessed with yours since your symbols are different. At least that's what happens sometimes to me.
Here is a working sample:
import sympy as sp
r0, ge_x, ge_y, bx, by = sp.symbols("r0 ge_x ge_y bx by", real=True) # main symbols
# define functions that will take the role of indexed symbols
x = sp.Function("x")
y = sp.Function("y")
R_abs = sp.Function("R_abs")
i = sp.Symbol("i", positive=True, integer=True)
n = 4
s2 = sp.Sum((bx + r0 * x(i) / R_abs(i) + ge_x * x(i) + ge_y * y(i) - x(i)) ** 2 +
(by + r0 * y(i) / R_abs(i) - ge_x * y(i) + ge_y * x(i) - y(i)) ** 2, (i, 1, n))
s2_prime = sp.diff(s2, bx).doit().simplify()
print(s2_prime)
# whatever lists you want. Can even be an instance of `np.ndarray`
# note that you summed from 1 to n so the 0th element will not be used
x_array = [0, 1, 2, 3, 4]
y_array = [4, 3, 2, 1, 0]
R_abs_array = [-10, 10, 5, 4, 3]
# define a function to access these array elements
x_function = lambda index: x_array[index]
y_function = lambda index: y_array[index]
R_abs_function = lambda index: R_abs_array[index]
# no idea why subs does not work and you MUST keep the same name for the variable.
# you can't have for example `evaluated_s2_prime = ...`.
# Probably something to do with forcing sp to remove references to `x`?
s2_prime = s2_prime.replace(x, x_function).replace(y, y_function).replace(R_abs, R_abs_function)
print(s2_prime)
Producing:
8*bx + 2*ge_x*x(1) + 2*ge_x*x(2) + 2*ge_x*x(3) + 2*ge_x*x(4) + 2*ge_y*y(1) + 2*ge_y*y(2) + 2*ge_y*y(3) + 2*ge_y*y(4) + 2*r0*x(4)/R_abs(4) + 2*r0*x(3)/R_abs(3) + 2*r0*x(2)/R_abs(2) + 2*r0*x(1)/R_abs(1) - 2*x(1) - 2*x(2) - 2*x(3) - 2*x(4)
8*bx + 20*ge_x + 12*ge_y + 31*r0/6 - 20
I am trying to convert decimal degrees to degrees, minutes and seconds using the following code:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return [d, m, sd]
full = 500/60
print(full)
print(dd2dms(full))
But I get the following output:
8.333333333333334 [8, 20, 0]
I would like to get the output as:
8.333333333333334 8:20:0
How can I achieve this? I am taking baby steps in learning Python. :)
Here's a little class built around your angle that has a conversion function for dms and also a nicely formatted string representation:
class Angle(float):
def __init__(self, degrees):
self.degrees = degrees
def __repr__(self):
return "<Angle of {}° ({})>".format(self.degrees, self.dms)
#property
def dms(self):
deg = self.degrees
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return "{:g}:{:g}:{:g}".format(d, m, sd)
Demo:
In[1]: a = Angle(90.173)
In[2]: print(a)
90.173
In[3]: print(a.dms)
90:10:22.8
In[4]: a
Out[4]: <Angle of 90.173° (90:10:22.8)>
Since it inherits from float, you can even calculate with it. Note though that the return value will be a normal float (not an Angle) without further adaption of the class.
If it is just for display purposes something like:
print("{0}:{1}:{2}".format(*dd2dms(full)))
If dd2dms returns an array, you just need to extract the array elements, convert them to strings and then concatenate. If you're just beginning to learn Python, try something easy like this:
result = dd2dms(full)
print(str(result[0]) + ":" + str(result[1]) + ":" + str(result[2]))
where result[i] returns the i-th element of array result, str() constructs a string from its argument, and then you can just concatenate the strings with +.
Remove square brackets from return statement (to return a tuple instead a list):
return d, m, sd
and format output string:
"%s:%s:%s" % dd2dms(full)
Full snippet:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return d, m, sd
full = 500/60
print(full)
print("%s:%s:%s" % dd2dms(full))
In python 3.6 you can use use literal string interpolation https://www.python.org/dev/peps/pep-0498/ to do the following:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return f'{d}:{m}:{sd}'
You can even do computations in the strings
for instance, you could do :
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
return f'{d}:{m}:{(md - m) * 60}'
However, it's usually better to declare your variables explicitly.
In order to calculate derivatives and other expressions I used the sympy package and said that T = sy.Symbol('T') now that I have calculated the right expression:
E= -T**2*F_deriv_T(T,rho)
where
def F_deriv_rho(T,rho):
ret = 0
for n in range(5):
for m in range(4):
inner= c[n,m]*g_rho_deriv_rho_np*g_T_np
ret += inner
return ret
that looks like this:
F_deriv_rho: [0.0 7.76971e-5*T 0.0001553942*T**2*rho
T*(-5.14488e-5*log(rho) - 5.14488e-5)*log(T) + T*(1.22574e-5*log(rho)+1.22574e-5)*log(T) + T*(1.89488e-5*log(rho) + 1.89488e-5)*log(T) + T(2.29441e-5*log(rho) + 2.29441e-5)*log(T) + T*(7.49956e-5*log(rho) + 7.49956e-5)*log(T)
T**2*(-0.0001028976*rho*log(rho) - 5.14488e-5*rho)*log(T) + T**2*(2.45148e-5*rho*log(rho) + 1.22574e-5*rho)*log(T) + T**2*(3.78976e-5*rho*log(rho) + 1.89488e-5*rho)*log(T) + T**2*(4.58882e-5*rho*log(rho) + 2.29441e-5*rho)*log(T) + T**2*(0.0001499912*rho*log(rho) + 7.49956e 5*rho)*log(T)]
with python I would like to change T (and rho) as a symbol to a value. How could I do that?
So, I would like to create 10 numbers like T_def = np.arange(2000, 10000, 800)and exchange all my sy.symbol(T) by iterating through the 10 values I created in the array.
Thanks for your help
I have found the solution according to this post:
How to substitute multiple symbols in an expression in sympy?
by usings "subs":
>>> from sympy import Symbol
>>> x, y = Symbol('x y')
>>> f = x + y
>>> f.subs({x:10, y: 20})
>>> f
30
There's more for this kinda thing here: http://docs.sympy.org/latest/tutorial/basic_operations.html
EDIT: A faster way would be by using "lamdify" as suggested by #Bjoern Dahlgren
There exists one very good linear interpolation method. It performs linear interpolation requiring at most one multiply per output sample. I found its description in a third edition of Understanding DSP by Lyons. This method involves a special hold buffer. Given a number of samples to be inserted between any two input samples, it produces output points using linear interpolation. Here, I have rewritten this algorithm using Python:
temp1, temp2 = 0, 0
iL = 1.0 / L
for i in x:
hold = [i-temp1] * L
temp1 = i
for j in hold:
temp2 += j
y.append(temp2 *iL)
where x contains input samples, L is a number of points to be inserted, y will contain output samples.
My question is how to implement such algorithm in ANSI C in a most effective way, e.g. is it possible to avoid the second loop?
NOTE: presented Python code is just to understand how this algorithm works.
UPDATE: here is an example how it works in Python:
x=[]
y=[]
hold=[]
num_points=20
points_inbetween = 2
temp1,temp2=0,0
for i in range(num_points):
x.append( sin(i*2.0*pi * 0.1) )
L = points_inbetween
iL = 1.0/L
for i in x:
hold = [i-temp1] * L
temp1 = i
for j in hold:
temp2 += j
y.append(temp2 * iL)
Let's say x=[.... 10, 20, 30 ....]. Then, if L=1, it will produce [... 10, 15, 20, 25, 30 ...]
Interpolation in the sense of "signal sample rate increase"
... or i call it, "upsampling" (wrong term, probably. disclaimer: i have not read Lyons'). I just had to understand what the code does and then re-write it for readability. As given it has couple of problems:
a) it is inefficient - two loops is ok but it does multiplication for every single output item; also it uses intermediary lists(hold), generates result with append (small beer)
b) it interpolates wrong the first interval; it generates fake data in front of the first element. Say we have multiplier=5 and seq=[20,30] - it will generate [0,4,8,12,16,20,22,24,28,30] instead of [20,22,24,26,28,30].
So here is the algorithm in form of a generator:
def upsampler(seq, multiplier):
if seq:
step = 1.0 / multiplier
y0 = seq[0];
yield y0
for y in seq[1:]:
dY = (y-y0) * step
for i in range(multiplier-1):
y0 += dY;
yield y0
y0 = y;
yield y0
Ok and now for some tests:
>>> list(upsampler([], 3)) # this is just the same as [Y for Y in upsampler([], 3)]
[]
>>> list(upsampler([1], 3))
[1]
>>> list(upsampler([1,2], 3))
[1, 1.3333333333333333, 1.6666666666666665, 2]
>>> from math import sin, pi
>>> seq = [sin(2.0*pi * i/10) for i in range(20)]
>>> seq
[0.0, 0.58778525229247314, 0.95105651629515353, 0.95105651629515364, 0.58778525229247325, 1.2246063538223773e-016, -0.58778525229247303, -0.95105651629515353, -0.95105651629515364, -0.58778525229247336, -2.4492127076447545e-016, 0.58778525229247214, 0.95105651629515353, 0.95105651629515364, 0.58778525229247336, 3.6738190614671318e-016, -0.5877852522924728, -0.95105651629515342, -0.95105651629515375, -0.58778525229247347]
>>> list(upsampler(seq, 2))
[0.0, 0.29389262614623657, 0.58778525229247314, 0.76942088429381328, 0.95105651629515353, 0.95105651629515364, 0.95105651629515364, 0.7694208842938135, 0.58778525229247325, 0.29389262614623668, 1.2246063538223773e-016, -0.29389262614623646, -0.58778525229247303, -0.76942088429381328, -0.95105651629515353, -0.95105651629515364, -0.95105651629515364, -0.7694208842938135, -0.58778525229247336, -0.29389262614623679, -2.4492127076447545e-016, 0.29389262614623596, 0.58778525229247214, 0.76942088429381283, 0.95105651629515353, 0.95105651629515364, 0.95105651629515364, 0.7694208842938135, 0.58778525229247336, 0.29389262614623685, 3.6738190614671318e-016, -0.29389262614623618, -0.5877852522924728, -0.76942088429381306, -0.95105651629515342, -0.95105651629515364, -0.95105651629515375, -0.76942088429381361, -0.58778525229247347]
And here is my translation to C, fit into Kratz's fn template:
/**
*
* #param src caller supplied array with data
* #param src_len len of src
* #param steps to interpolate
* #param dst output param will be filled with (src_len - 1) * steps + 1 samples
*/
float* linearInterpolation(float* src, int src_len, int steps, float* dst)
{
float step, y0, dy;
float *src_end;
if (src_len > 0) {
step = 1.0 / steps;
for (src_end = src+src_len; *dst++ = y0 = *src++, src < src_end; ) {
dY = (*src - y0) * step;
for (int i=steps; i>0; i--) {
*dst++ = y0 += dY;
}
}
}
}
Please note the C snippet is "typed but never compiled or run", so there might be syntax errors, off-by-1 errors etc. But overall the idea is there.
In that case I think you can avoid the second loop:
def interpolate2(x, L):
new_list = []
new_len = (len(x) - 1) * (L + 1)
for i in range(0, new_len):
step = i / (L + 1)
substep = i % (L + 1)
fr = x[step]
to = x[step + 1]
dy = float(to - fr) / float(L + 1)
y = fr + (dy * substep)
new_list.append(y)
new_list.append(x[-1])
return new_list
print interpolate2([10, 20, 30], 3)
you just calculate the member in the position you want directly. Though - that might not be the most efficient to do it. The only way to be sure is to compile it and see which one is faster.
Well, first of all, your code is broken. L is not defined, and neither is y or x.
Once that is fixed, I run cython on the resulting code:
L = 1
temp1, temp2 = 0, 0
iL = 1.0 / L
y = []
x = range(5)
for i in x:
hold = [i-temp1] * L
temp1 = i
for j in hold:
temp2 += j
y.append(temp2 *iL)
And that seemed to work. I haven't tried to compile it, though, and you can also improve the speed a lot by adding different optimizations.
"e.g. is it possible to avoid the second loop?"
If it is, then it's possible in Python too. And I don't see how, although I don't see why you would do it the way you do. First creating a list of L length of i-temp is completely pointless. Just loop L times:
L = 1
temp1, temp2 = 0, 0
iL = 1.0 / L
y = []
x = range(5)
for i in x:
hold = i-temp1
temp1 = i
for j in range(L):
temp2 += hold
y.append(temp2 *iL)
It all seems overcomplicated for what you get out though. What are you trying to do, actually? Interpolate something? (Duh it says so in the title. Sorry about that.)
There are surely easier ways of interpolating.
Update, a much simplified interpolation function:
# A simple list, so it's easy to see that you interpolate.
indata = [float(x) for x in range(0, 110, 10)]
points_inbetween = 3
outdata = [indata[0]]
for point in indata[1:]: # All except the first
step = (point - outdata[-1]) / (points_inbetween + 1)
for i in range(points_inbetween):
outdata.append(outdata[-1] + step)
I don't see a way to get rid of the inner loop, nor a reason for wanting to do so.
Converting it to C I'll leave up to someone else, or even better, Cython, as C is a great langauge of you want to talk to hardware, but otherwise just needlessly difficult.
I think you need the two loops. You have to step over the samples in x to initialize the interpolator, not to mention copy their values into y, and you have to step over the output samples to fill in their values. I suppose you could do one loop to copy x into the appropriate places in y, followed by another loop to use all the values from y, but that will still require some stepping logic. Better to use the nested loop approach.
(And, as Lennart Regebro points out) As a side note, I don't see why you do hold = [i-temp1] * L. Instead, why not do hold = i-temp, and then loop for j in xrange(L): and temp2 += hold? This will use less memory but otherwise behave exactly the same.
Heres my try at a C implementation for your algorithm. Before trying to further optimize it id suggest you profile its performance with all compiler optimizations enabled.
/**
*
* #param src caller supplied array with data
* #param src_len len of src
* #param steps to interpolate
* #param dst output param needs to be of size src_len * steps
*/
float* linearInterpolation(float* src, size_t src_len, size_t steps, float* dst)
{
float* dst_ptr = dst;
float* src_ptr = src;
float stepIncrement = 1.0f / steps;
float temp1 = 0.0f;
float temp2 = 0.0f;
float hold;
size_t idx_src, idx_steps;
for(idx_src = 0; idx_src < src_len; ++idx_src)
{
hold = *src_ptr - temp1;
temp1 = *src_ptr;
++src_ptr;
for(idx_steps = 0; idx_steps < steps; ++idx_steps)
{
temp2 += hold;
*dst_ptr = temp2 * stepIncrement;
++dst_ptr;
}
}
}