I recently asked this question about solving a system of nonlinear equations, over on Math SE. I am now attempting to implement the accepted answer using SymPy
in Python
. I'll begin by briefly recreating the solution here.
Problem.
Suppose I have three receivers, with coordinates given by vectors p1
, p2
, and p3
. Further, I have a transmitter, emitting radio pulses at unknown times t1, t2, t3, t4, ...
with known speed c
, and which has unknown initial coordinates at time t = t1
given by the vector x0 = <x,y,z>
and is moving with an unknown constant velocity given by the vector v
. My data consists of the pulse arrival time at each station, denoted air
(for arrival of time of signal i
at receiver r
).
My goal is to determine this velocity. The solution given in the attached answer is as follows:
The position of the transmitter at time
t
will be given byxo = v(t - t1)
. Hence, the distance between the transmitter and therth
receiver at the time theith
pulse is emitted will be given by:
||(ti - t1)v x0 - pr||
Therefore, the time of arrival at the
rth
receiver is given by:
ti (1/c) * ||(ti - t1)v x0 - pr||
Which we know to be
air
. Therefore:
c**2(air - ti)**2 = ||v||**2(ti - t1)**2 2(ti -t1)<v, c0 - pr>
(where
<a, b>
denotes the scalar/dot product of vectorsa
andb
).This results in a system of
# receivers x # pulses
equations.
(see the attached answer for far nicer formatting)
Implementation.
I'm most familiar with the SymPy
package, so I'm attempting to implement a solution using its solvers. I'm a bit stuck, however, on where to begin.
The number of pulses is not known before hand thus, as SymPy
is a symbolic solver, I must be able to dynamically define variables t1, t2, t3... tn
.
My rough outline for an implementation is as follows:
import sympy as sp
def find_transmitter(arrival_times):
equations = []
for receiver in arrival_times:
for pulse in arrival_times:
equations.append(sp.Eq(c**2(pulse - ti...
sp.solve(equations...
Where arrival_times
is a list, where each column corresponds to a receiver, and each row corresponds to a pulse. I.e.:
[[a11, a21, a31...],[a12, a22, a32...],[a13, a23, a33...]]
It doesn't occur to me that there's a nice alternative to this approach. I'm not sure, however, e.g. how to dynamically define enough ti
's and iterate through them. That is, e.g., I'll have an unknown number of equations looking like:
equations.append(sp.Eq(c**2(pulse - t1...
equations.append(sp.Eq(c**2(pulse - t2...
equations.append(sp.Eq(c**2(pulse - t3...
equations.append(sp.Eq(c**2(pulse - t4...
equations.append(sp.Eq(c**2(pulse - t5...
.
.
.
Is there a nice way to do this? Very likely, I'm overlooking some obvious solution.
CodePudding user response:
Perhaps surprisingly, can dynamically create any number of symbols with symbols
using space as a delimiter!
You can also subscript with _N
arrival_times_count = 100 # from data length
chunksize = 10 # some factor of arrival_times_count
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i:i n]
from sympy import *
# create a huge collection
collection = symbols(" ".join(f"t_{x}" for x in range(arrival_times_count)), real=True)
# fold it into rows
collection = list(chunks(collection, chunksize))
>>> for row in collection:
... print(row)
...
(t_0, t_1, t_2, t_3, t_4, t_5, t_6, t_7, t_8, t_9)
(t_10, t_11, t_12, t_13, t_14, t_15, t_16, t_17, t_18, t_19)
(t_20, t_21, t_22, t_23, t_24, t_25, t_26, t_27, t_28, t_29)
(t_30, t_31, t_32, t_33, t_34, t_35, t_36, t_37, t_38, t_39)
(t_40, t_41, t_42, t_43, t_44, t_45, t_46, t_47, t_48, t_49)
(t_50, t_51, t_52, t_53, t_54, t_55, t_56, t_57, t_58, t_59)
(t_60, t_61, t_62, t_63, t_64, t_65, t_66, t_67, t_68, t_69)
(t_70, t_71, t_72, t_73, t_74, t_75, t_76, t_77, t_78, t_79)
(t_80, t_81, t_82, t_83, t_84, t_85, t_86, t_87, t_88, t_89)
(t_90, t_91, t_92, t_93, t_94, t_95, t_96, t_97, t_98, t_99)
chunks
function from How do you split a list into evenly sized chunks?
However, there's a good chance this is an XY problem and you may be better off doing something like packing your data into a pandas.DataFrame, lambdifying your equation, and then calling .apply()
to find the result of each row
CodePudding user response:
A dirty way to dynamically define variables is to make a string pattern (with python code) and then process it with exec
. So you can have access to the variables without passing though list, tuples, ... The best way would be to use substitution (vector -> components), hence you work with a vector & lambdify to perform componentwise operations.
from sympy import *
n = 4
t_s = ( ''.join((i, str(j))) for i, j in zip('t'*n, range(1, n 1)))
exec( f"{', '.join(t_s)} = symbols('t1:{n 1}')")
print(t2, type(t2))
Output
t2 <class 'sympy.core.symbol.Symbol'>