There are several problems here:
- The range is supposed to be defined over integers;
- The number of items the range would generate is too large, you have to take huge steps;
- The formula takes
x as a variable, but you seem to define a np.array(x_range) in y; and more importantly
- you use
eval(..), but eval(..) usually a string or another object that can be parsed;
- you do not give
graph(..) a formula as first element: before Python calls graph(..) it first evaluates the operands.
In my opinion, the best way to achieve this is using a lambda-expression:
import matplotlib.pyplot as plt
import numpy as np
def graph(formula, x_range):
x = np.array(x_range)
#^ use x as range variable
y = formula(x)
#^ ^call the lambda expression with x
#| use y as function result
plt.plot(x,y)
plt.show()
graph(lambda x : ((np.log(x)*10**6)/np.log(2)) + 1, range(0, 435*10**15,10**12))
# ^use a lambda expression ^range over integers
# take huge steps
This generates the following image:

EDIT:
based on your comment you want time on the y-axis and the function on the x-axis, this can simply be achieved by assigning to the other variables like:
import matplotlib.pyplot as plt
import numpy as np
def graph(formula, x_range):
y = np.array(x_range)
x = formula(y)
plt.plot(x,y)
plt.show()
graph(lambda x : ((np.log(x)*10**6)/np.log(2)) + 1, range(0,435*10**15,10**12))
Note that you do not need to change the name of the variable in the lambda-expression: indeed when you call the lambda expression, the local x will simply be the y of the caller.
evalworks on astring. Nevertheless it is not seen as very elegant anyway.