|
import sympy as sp |
|
import numpy as np |
|
import pylab as pl |
|
|
|
d, n = sp.symbols('d n', integer=True, positive=True) |
|
eps = sp.symbols('eps', real=True, positive=True) |
|
|
|
jl_bound = 4 * sp.log(n) / (eps ** 2 / 2 - eps ** 3 / 3) |
|
|
|
# solve the inverted expression as sympy is much faster as finding the roots of |
|
# a polynomial and the point eps=0 is not interesting to us anyway. |
|
solutions = sp.solve(sp.Equality(1 / d, 1 / jl_bound), eps) |
|
|
|
for sol_idx, sol in enumerate(solutions): |
|
print "Solution #%d" % sol_idx |
|
sp.pprint(sol) |
|
print |
|
|
|
|
|
def plot_with_sympy(d_range=np.logspace(1, 4, 16), n_value=100): |
|
"""Plot the complex roots of the JL bound polynomial""" |
|
|
|
for sol_idx, sol in enumerate(solutions): |
|
real = np.zeros(d_range.shape[0]) |
|
imag = np.zeros(d_range.shape[0]) |
|
for i, d_value in enumerate(d_range): |
|
eps_value = sol.evalf(subs={d: d_value, n: n_value}, chop=True) |
|
real[i], imag[i] = eps_value.as_real_imag() |
|
pl.plot(real, imag, label='solution #%d' % sol_idx) |
|
pl.legend(loc='best') |
|
pl.xlabel("Real part of the root") |
|
pl.ylabel("Imaginary part of the root") |
|
pl.title("Roots path in the complex plan:\n" |
|
"n=100, d in range 10-10000") |
|
pl.show() |
|
|
|
|
|
|
|
# manual inspection of the three solutions tells us that the minimal positive |
|
# real solution is the second among 3 (one negative real and two complex with a |
|
# positive real part and a sometimes null imaginary part) |
|
sol = solutions[1] |
|
|
|
# Unfortunately those evaluations are not numerically stable when using numpy |
|
|
|
jl_eps_mpmath = sp.lambdify((d, n), sol, "mpmath") |
|
jl_eps_numpy = sp.lambdify((d, n), sol, "numpy") |
|
|
|
|
|
print "Numerical evaluation with lambdify:" |
|
print |
|
|
|
n_value = 100 |
|
|
|
for d_value in np.logspace(1, 4, 4): |
|
print "mpmath: d=%d, n=%d, eps=%r" % ( |
|
d_value, n_value, jl_eps_mpmath(d_value, n_value)) |
|
print "numpy: d=%d, n=%d, eps=%r" % ( |
|
d_value, n_value, jl_eps_numpy(d_value, n_value)) |
|
print |
|
|
|
|
|
# Second bad news: sympy cannot find an explicit closed form formula of the |
|
# real and imaginary parts: uncommenting and executing the following will |
|
# trigger a RuntimeException after a while: |
|
|
|
# RuntimeError: maximum recursion depth exceeded while calling a Python object |
|
|
|
#sp.expand(sol, complex=True) |
Well, I have no idea what this is. It will be some time before I even understand the problem. However, I wrote an implementation of Sammon's mapping in MATLAB last year. JL appears to be similar to Sammon, in that it maps points from a higher-dimensional space to lower dimensions while preserving Euclidean distances. I know this is a very crude likeness, but are the two indeed similar? Where do you intend to use this?