Defining a range of symbols whose bounds are OTHER symbols - python

I'm trying to express a summation over an arbitrary (but finite) number of symbols, which I wish to be given by another symbol. For instance, is it possible to say:
N,ci,cj = symbols('N,c_i,c_j')
# pseudocode
k = sum(ci+cj,(ci,0,N),(cj,0,N))
or, more literally,
k = sum(ci+cj, (ci != cj))
My instinct is that it isn't, but I do wish sympy would implement support for it!
UPDATE
It appears sympy offers provisions for indexed variables. Namely:
x = IndexedBase('x')
i,j = symbols('i j',cls=Idx)
however, the you can an error when attempting:
y = Sum(x[i], (i, 0, 2))
Which is:
ValueError: Invalid limits given: ((i, 1, 5),)

You can use a Function, like x = symbols('x', cls=Function) and x(i). Indexed should also work, but it looks like Sum has a bug that disallows Idx. It works if you just use i = symbols('i'), though.

Related

Is there a way to define an piecewise function in sympy where the intervals depend on indexed symbols?

As the title says. I have created an indexed symbol 't' and I wish to create a piecewise function that is (-1)^k when t[1]+t[2]+...+t[k]<=x<t[1]+t[2]+...+t[k]+t[k+1], up to a given maximum value of k=n. My code is currently as follows:
def SumToN(x,n):
result = 0
for i in range(1,n+1):
result += x[i]
return result
t = sp.IndexedBase('t')
x = sp.Symbol('x')
k = sp.Symbol('k')
uFunc = sp.Piecewise(((-1)^(k-1),SumToN(t,k)<=x<SumToN(t,k+1)) for k in range(1,n+1))
When I run this, I get the following error:
TypeError: cannot determine truth value of Relational
I understand that it is having trouble with being able to confirm which region x is in, but when I try this with say 3 symbols t1, t2, and t3, I get no errors. Am I doing something clearly wrong, and is there a way to do this?
You cannot create a compound inequality with symbols, only numbers. So 1<2<3 works but 1<Symbol('x')<3 must be written as x = Symbol('x'); And(1 < x, x < 3). Also, Piecewise does not work with an iterator, so try:
Piecewise(*[((-1)^(k-1), And(SumToN(t,k)<=x, x<SumToN(t,k+1)))
for k in range(1,n+1)])

Does numpy.corrcoef calculate correlation within matrices when two matrices are provided (intra-correlation)?

Here I'm calculating the Pearson correlation such that I'm accounting for every comparison.
x = pd.DataFrame({'a':[3,6,4,7,9],'b':[6,2,4,1,5],'c':[7,9,1,2,9]},index=['aa','bb','cc','dd','ee']).T
y = pd.DataFrame({'A':[9,4,1,3,5],'B':[9,8,9,5,7],'C':[1,1,3,1,2]},index=['aa','bb','cc','dd','ee']).T
table = pd.DataFrame(columns=['Correlation Coeff'])
for i in range(0, len(x)):
for j in range(0, len(y)):
xf = list(x.iloc[i])
yf = list(y.iloc[j])
n = np.corrcoef(xf,yf)[0,1]
name = x.index[i]+'|'+y.index[j]
table.at[name, 'Correlation Coeff'] = n
table
This is the result:
Correlation Coeff
a|A -0.232973
a|B -0.713392
a|C -0.046829
b|A 0.601487
b|B 0.662849
b|C 0.29654
c|A 0.608993
c|B 0.16311
c|C -0.421398
Now when I just apply these tables directly to numpy's function, removing duplicate values and 'ones' it looks this this.
x = pd.DataFrame({'a':[3,6,4,7,9],'b':[6,2,4,1,5],'c':[7,9,1,2,9]},index=['aa','bb','cc','dd','ee']).T.to_numpy()
y = pd.DataFrame({'A':[9,4,1,3,5],'B':[9,8,9,5,7],'C':[1,1,3,1,2]},index=['aa','bb','cc','dd','ee']).T.to_numpy()
n = np.corrcoef(x,y)
n = n.tolist()
n = [element for sub in n for element in sub]
# Rounding to ensure no duplicates are being picked up.
rnd = [round(num, 13) for num in n]
X = [i for i in rnd if i != 1]
X = list(dict.fromkeys(X))
X
[-0.3231828652987,
0.3157400783243,
-0.232972779074,
-0.7133922984085,
-0.0468292905791,
0.3196502842345,
0.6014868821052,
0.6628489803599,
0.2965401263095,
0.608993434846,
0.1631095635753,
-0.4213976904463,
0.2417468892076,
-0.5841782301194,
0.3674842076296]
There are 6 extra values (in bold) not accounted for. I'm assuming that they are correlation values calculated within a single matrix and if so, why? Is there a way to use this function without generating these additional values?
You are right in assuming that those are the correlations from variables within x and y, and so far as I can tell there is no way to turn this behaviour off.
You can see that this is true by looking at the implementation of numpy.corrcoef. As expected, most of the heavy lifting is being done by a separate function that computes covariance - if you look at the implementation of numpy.cov, particularly line 2639, you will see that, if you supply an additional y argument, this is simply concatenated onto x before computing the covariance matrix.
If necessary, it wouldn't be too hard to implement your own version of corrcoef that works how you want. Note that you can do this in pure numpy, which in most cases will be faster than the iterative approach from the example code above.

Displaying solutions within a certain range when using sympy solve command

I can be considered pretty much new to python and coding in general so forgive me for my ignorance.
I'm trying to solve a system of trigonometric functions in python, and I'm doing so using the solve command from sympy. However, this method returns only a finite number of solutions, two in this particular case.
I've read through the documentation and it seems that to get an expression for all the solutions solveset is to be used instead. However, I do not want all the solutions to be displayed, but rather only a finite amount which is contained within a certain range.
Here's the example:
from sympy import *
x, y = symbols('x, y')
eq1 = Eq(y - sin(x), 0)
eq2 = Eq(y - cos(x), 0)
sol = solve([eq1, eq2], [x, y])
print(sol)
which only returns the first two solutions in the positive x range.
How could I do to, for example, display all the solutions within the x range [-2pi, 2pi]?
I'd want them in explicit form rather than written in term of some multiplier since I then need to convert them into numerical form.
Thank you in advance.
SymPy can really take you down rabbit holes. I agree with kampmani's solution, only if you can easily solve for y on your own. However, in more general cases and in higher dimensions, his solution does not hold.
For example, the following will be slightly more tricky:
eq1 = Eq(z - x*y, 0)
eq2 = Eq(z - cos(x) - sin(y), 0)
eq3 = Eq(z + x*y, 0)
So here I am; killing a fly with a bazooka. The problem is that one was able to simplify the set of equations into a single equation with a single variable. But what if you can't do that (for example, if it was a larger system)?
In this case, one needs to use nonlinsolve to solve the system of equations. But this does not provide numeric solutions directly and does not have a domain argument.
So the following code unpacks the solutions. It goes through each tuple in the set of solutions and finds the numeric solutions for each component in the tuple. Then in order to get the full list, you need a Cartesian Product of each of those components. Repeat this for each tuple in the set of solutions.
The following should work for almost any system of equations in any dimension greater than 1. It produces numeric solutions in the cube whose boundaries are the domains variable.
from sympy import *
import itertools # used for cartesian product
x, y, z = symbols('x y z', real=True)
domains = [Interval(-10, 10), Interval(-10, 10), Interval(-10, 10)] # The domain for each variable
eq1 = z - x*y
eq2 = z - cos(x) - sin(y)
eq3 = z + x*y
solutions = nonlinsolve([eq1, eq2, eq3], [x, y, z]) # the recommended function for this situation
print("---------Solution set----------")
print(solutions) # make sure the solution set is reasonable. If not, assertion error will occur
_n = Symbol("n", integer=True) # the solution set often seems to contain these symbols
numeric_solutions = []
assert isinstance(solutions, Set) # everything that I had tried resulted in a FiniteSet output
for solution in solutions.args: # loop through the different kinds of solutions
assert isinstance(solution, Tuple) # each solution should be a Tuple if in 2D or higher
list_of_numeric_values = [] # the list of lists of a single numerical value
for i, element in enumerate(solution):
if isinstance(element, Set):
numeric_values = list(element.intersect(domains[i]))
else: # assume it is an Expr
assert isinstance(element, Expr)
if _n.name in [s.name for s in element.free_symbols]: # if n is in the expression
# change our own _n to the solutions _n since they have different hidden
# properties and they cannot be substituted without having the same _n
_n = [s for s in element.free_symbols if s.name == _n.name][0]
numeric_values = [element.subs(_n, n)
for n in range(-10, 10) # just choose a bunch of sample values
if element.subs(_n, n) in domains[i]]
elif len(element.free_symbols) == 0: # we just have a single, numeric number
numeric_values = [element] if element in domains[i] else []
else: # otherwise we just have an Expr that depends on x or y
# we assume this numerical value is in the domain
numeric_values = [element]
# note that we may have duplicates, so we remove them with `set()`
list_of_numeric_values.append(set(numeric_values))
# find the resulting cartesian product of all our numeric_values
numeric_solutions += itertools.product(*list_of_numeric_values)
# remove duplicates again to be safe with `set()` but then retain ordering with `list()`
numeric_solutions = list(set(numeric_solutions))
print("--------`Expr` values----------")
for i in numeric_solutions:
print(list(i)) # turn it into a `list` since the output below is also a `list`.
print("--------`float` values---------")
for i in numeric_solutions:
print([N(j) for j in i]) # could have been converted into a `tuple` instead
In particular, it produces the following output for the new problem:
---------Solution set----------
FiniteSet((0, ImageSet(Lambda(_n, 2*_n*pi + 3*pi/2), Integers), 0))
--------`Expr` values----------
[0, -5*pi/2, 0]
[0, -pi/2, 0]
[0, 3*pi/2, 0]
--------`float` values---------
[0, -7.85398163397448, 0]
[0, -1.57079632679490, 0]
[0, 4.71238898038469, 0]
It was a lot of effort and probably not worth it but oh well.
By using solveset you can restrict the solutions with domain argument. For evaluating numerical results use .evalf() or another similar method.
from sympy import Interval, symbols, solveset, sin, cos, pi
x = symbols('x')
sol = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
print(sol)
print(sol.evalf())
Output
FiniteSet(-7*pi/4, -3*pi/4, pi/4, 5*pi/4)
FiniteSet(-5.49778714378214, -2.35619449019234, 0.785398163397448, 3.92699081698724)
I hope this helps!
Thanks to the brilliant suggestion from #kampmani it is possible to achieve the desired result.
For start, the FiniteSet elements are not indexed and cannot be used, so the FiniteSet has to be converted into a list:
solx_array = []
#
#
#
solx = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
solx_array=list(solx)
The next step is to find the y coordinate of the intersection point given its x coordinate. The final code should look somewhat similar to this:
from sympy import Interval, symbols, solveset, sin, cos, pi
sol_array = []
x = symbols('x')
solx = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
solx_array=list(solx)
for i in range(len(solx_array)):
soly = cos(solx_array[i])
sol_array.append(str(solx_array[i] + soly))
for i in range(len(sol_array)):
print(sol_array[i])
Still don't know how to convert the results into numerical form though, any idea is very appreciated.

Define a matrix depending on variable in Mathematica

I am translating my code from Python to Mathematica. I am trying to define a matrix, whose values depend on a variable chosen by the user, called kappa.
In Python the code looked like that:
def getA(kappa):
matrix = zeros((n, n), float)
for i in range(n):
for j in range(n):
matrix[i][j] = 2*math.cos((2*math.pi/n)*(abs(j-i))*kappa)
n = 5
return matrix
What I have done so far in Mathematica is the following piece of code:
n = 5
getA[kappa_] :=
A = Table[0.0, {n}, {n}];
For[i = 0, i < n, i++,
For[ j = 0, j < n, j++,
A[[i, j]] = 2*Cos[(2*pi/n)*(abs (j - i))*kappa]]];
b = getA[3]
But when I try to evaluate this matrix for a value of kappa equal to 3, I get the following error:
Set::partd: "Part specification A[[i,j]] is longer than depth of object.
How can I fix it?
Try something like this
n = 5;
A = Table[2*Cos[(2 \[Pi]/n) (Abs[ j - i]) \[Kappa]], {i, 1, n}, {j, 1, n}];
b = A /. \[Kappa]->3
I'll leave you to package this into a function if you want to.
You write that you are trying to translate Python into Mathematica; your use of For loops suggests that you are trying to translate to C-in-Mathematica. The first rule of Mathematica club is don't use loops.
Besides that you've made a number of small syntactical errors, such as using abs() where you should have had Abs[] (Mathematica's built-in functions all have names beginning with a capital letter, they wrap their arguments in [ and ], not ( and )), pi is not the name of the value of the ratio of a circle's diameter to its radius (it's called \[Pi]). Note too that I've omitted the multiplication operator which is often not required.
In your particular case, this would be the fastest and the most straightforward solution:
getA[κ_, n_] := ToeplitzMatrix[2 Cos[2 π κ Range[0, n - 1] / n]]

"'generator' object is not subscriptable" error

Why am I getting this error, from line 5 of my code, when attempting to solve Project Euler Problem 11?
for x in matrix:
p = 0
for y in x:
if p < 17:
currentProduct = int(y) * int(x[p + 1]) * int(x[p + 2]) * int(x[p + 3])
if currentProduct > highestProduct:
print(currentProduct)
highestProduct = currentProduct
else:
break
p += 1
'generator' object is not subscriptable
Your x value is is a generator object, which is an Iterator: it generates values in order, as they are requested by a for loop or by calling next(x).
You are trying to access it as though it were a list or other Sequence type, which let you access arbitrary elements by index as x[p + 1].
If you want to look up values from your generator's output by index, you may want to convert it to a list:
x = list(x)
This solves your problem, and is suitable in most cases. However, this requires generating and saving all of the values at once, so it can fail if you're dealing with an extremely long or infinite list of values, or the values are extremely large.
If you just needed a single value from the generator, you could instead use itertools.islice(x, p) to discard the first p values, then next(...) to take the one you need. This eliminate the need to hold multiple items in memory or compute values beyond the one you're looking for.
import itertools
result = next(itertools.islice(x, p))
As an extension to Jeremy's answer some thoughts about the design of your code:
Looking at your algorithm, it appears that you do not actually need truly random access to the values produced by the generator: At any point in time you only need to keep four consecutive values (three, with an extra bit of optimization). This is a bit obscured in your code because you mix indexing and iteration: If indexing would work (which it doesn't), your y could be written as x[p + 0].
For such algorithms, you can apply kind of a "sliding window" technique, demonstrated below in a stripped-down version of your code:
import itertools, functools, operator
vs = [int(v) for v in itertools.islice(x, 3)]
for v in x:
vs.append(int(v))
currentProduct = functools.reduce(operator.mul, vs, 1)
print(currentProduct)
vs = vs[1:]

Categories