Page 1 of 2

Array copy m1=m2 works only on the first pass.

Posted: Sat Nov 28, 2015 5:40 am
by deanr
First I am just learning python and playing with my pyboard. To understand the capabilities of the pyboard I run a 5x5 matrix inversion program a large number of times (and get a measurable delay) and at the end compare one of the result values to the known answer and light the green LED if it matches. To speed things up I loaded an array/matrix before the main loop and use a copy as the array that is manipulated into the solution. Using the m1=m2 statement worked without looping. After adding the repeat loop I kept getting the wrong answer light.
The number of passes was then set to 1 and the correct answer was indicated. Next I coded an individual item copy and replaced the m1=m2 statement and it gave the correct answer with any loop count >0.
To check, the python coding was put into Anaconda Spyder and there the array copy works as expected.
My pyboard responds with MicroPython v1.5-134-g5be60d6 on 2015-11-14 ...

Part of the coding:
itt =0
while itt<10000:
"""
m1=m2
"""
i=0
while i<col:
j=0
while j<n:
m1[j] = m2[j]
j+=1
i+=1

Re: Array copy m1=m2 works only on the first pass.

Posted: Sat Nov 28, 2015 8:41 am
by dhylands
If you wrap your code examples with [ code] and [ /code] (but don't put the spaces after the opening square brackets) then it will preserve all of the indentation and look something like this:

Code: Select all

while True:
    do something
In python lists are mutable. So if you do this:

Code: Select all

>>> m1 = [1, 2, 3, 4, 5, 6]
>>> m2 = m1
>>> m2[0] = 99
>>> m1
[99, 2, 3, 4, 5, 6]
then you'll see that m1 and m2 are really pointing to the same underlying object. To make a copy do it this way:

Code: Select all

>>> m1 = [1, 2, 3, 4, 5, 6]
>>> m2 = list(m1)
>>> m2[0] = 99
>>> m1
[1, 2, 3, 4, 5, 6]

Re: Array copy m1=m2 works only on the first pass.

Posted: Sat Nov 28, 2015 10:32 pm
by deanr
Same behavior with m1=list(m2).
I will try to load the entire code

Code: Select all

import pyb #Testing pyboard math speed.
pyb.LED(3).on()
#5x5 matrix y=A*X, X unknoown, coded A:Y:I, solved to I:X:Ainv,
#This code may be shortened to solve only for X at minimal waste
m2=[ [ 0.0 for i in range(11) ] for j in range(5) ]
m1=[ [ 0.0 for i in range(11) ] for j in range(5) ]
m2=[[2.3,5.5,7.5,8.2,7.2,3.4,1,0,0,0,0],
[4.3,2.2,2.1,6.3,9.4,6.5,0,1,0,0,0],
[0,6.6,5.8,4.2,8.1,8.8,0,0,1,0,0],
[3.6,3.6,-8.6,0,-3.4,-3.1,0,0,0,1,0],
[8.1,-4.5,2.6,6.7,5.1,5,0,0,0,0,1]]
col=11 #total collumbs n+1+n, n+1 to not solve Ainv.
n= 5 #rows
m= 4 #n-1
i=0
j=0
k=0
e=0.0
f=0.0
g=0.0
h=0.0
d=1e-4 #zero? close enough
h=-d
itt =0
while itt<1001: #Increase until desired delay 
 #reset to initial matrix
 """
 m1=list(m2)
 """
 i=0
 while i<n:
  j=0
  while j<col:
   m1[i][j] = m2[i][j]
   j+=1
  i+=1
#
 i=0        
 while  i < n: #for each row
  e=m1[i][i] #read the diagonal element
  if d>e and e>h: #deal with zero diagonal element?
   j=i
   if j>=n:
    pyb.LED(1).on() #error no solution
   while j<m:#look for a row with non-zero element
    j+=1
    g=m1[j][i]
    if g<h or g>d:#add the rows
     k=i #zeros assumed to left
     while k<col:
      m1[i][k]+=m1[j][k]
      k+=1
    j=n
    e=m1[i][i]#read the new diagonal element
  #scale row for 1 in diagonal
  e=1/e
  j=i #zeros assumed to left
  while j<col:
   m1[i][j]=m1[i][j]*e
   j+=1
  j=0
  while j<n:
   if j!=i:
    f=-m1[j][i]#value of element to eliminate
    k=i+1#k=i to actualy eliminate it and give clean I
    while k<col:
     m1[j][k]+=m1[i][k]*f
     k+=1
   j+=1
  i+=1
 itt+=1
 pyb.LED(4).toggle()
answer = 2.192383196 #X(0)
e = m1[0][5]-answer #error difference
if d>e and e>h:#little diff.
 pyb.LED(2).on()#green light on
else:
 pyb.LED(1).on()#red light on

Re: Array copy m1=m2 works only on the first pass.

Posted: Sat Nov 28, 2015 11:55 pm
by dhylands
I ran the code you presented and the green light comes on.

What does the code look like when it fails.

Re: Array copy m1=m2 works only on the first pass.

Posted: Sun Nov 29, 2015 4:29 pm
by deanr

Code: Select all

import pyb #Testing pyboard math speed.
pyb.LED(3).on()
#5x5 matrix y=A*X, X unknoown, coded A:Y:I, solved to I:X:Ainv,
#This code may be shortened to solve only for X at minimal waste
m2=[ [ 0.0 for i in range(11) ] for j in range(5) ]
m1=[ [ 0.0 for i in range(11) ] for j in range(5) ]
m2=[[2.3,5.5,7.5,8.2,7.2,3.4,1,0,0,0,0],
[4.3,2.2,2.1,6.3,9.4,6.5,0,1,0,0,0],
[0,6.6,5.8,4.2,8.1,8.8,0,0,1,0,0],
[3.6,3.6,-8.6,0,-3.4,-3.1,0,0,0,1,0],
[8.1,-4.5,2.6,6.7,5.1,5,0,0,0,0,1]]
col=11 #total collumbs n+1+n, n+1 to not solve Ainv.
n= 5 #rows
m= 4 #n-1
i=0
j=0
k=0
e=0.0
f=0.0
g=0.0
h=0.0
d=1e-4 #zero? close enough
h=-d
itt =0
while itt<1001: #Increase until desired delay 
 #reset to initial matrix
 m1=list(m2)
 """
 i=0
 while i<n:
  j=0
  while j<col:
   m1[i][j] = m2[i][j]
   j+=1
  i+=1
 """
 i=0        
 while  i < n: #for each row
  e=m1[i][i] #read the diagonal element
  if d>e and e>h: #deal with zero diagonal element?
   j=i
   if j>=n:
    pyb.LED(1).on() #error no solution
   while j<m:#look for a row with non-zero element
    j+=1
    g=m1[j][i]
    if g<h or g>d:#add the rows
     k=i #zeros assumed to left
     while k<col:
      m1[i][k]+=m1[j][k]
      k+=1
    j=n
    e=m1[i][i]#read the new diagonal element
  #scale row for 1 in diagonal
  e=1/e
  j=i #zeros assumed to left
  while j<col:
   m1[i][j]=m1[i][j]*e
   j+=1
  j=0
  while j<n:
   if j!=i:
    f=-m1[j][i]#value of element to eliminate
    k=i+1#k=i to actualy eliminate it and give clean I
    while k<col:
     m1[j][k]+=m1[i][k]*f
     k+=1
   j+=1
  i+=1
 itt+=1
 pyb.LED(4).toggle()
answer = 2.192383196 #X(0)
e = m1[0][5]-answer #error difference
if d>e and e>h:#little diff.
 pyb.LED(2).on()#green light on
else:
 pyb.LED(1).on()#red light on
This gives me a red light. When I then change "while itt<1001:" to "while itt<1:" it gives me the green light.

Re: Array copy m1=m2 works only on the first pass.

Posted: Sun Nov 29, 2015 5:16 pm
by dhylands
I see the problem.

m1 is actually a list with embedded lists.

Code: Select all

m1=list(m2)
only makes copies of the topmost lists. If you use:

Code: Select all

m1=[list(row) for row in m2]
then it will copy 2 levels. CPython has a copy.deepcopy() routinte that isn't currently implemented in MicroPython or you could have used that.

Making the change above, I get a green LED at the end.

Re: Array copy m1=m2 works only on the first pass.

Posted: Sun Nov 29, 2015 6:19 pm
by deanr
I have a lot to learn before I understand this...
Thanks for the solution.

Re: Array copy m1=m2 works only on the first pass.

Posted: Sun Nov 29, 2015 7:07 pm
by dhylands
The big thing to remember is that when you pass around list objects, you're really doing the C equivalent thing of passing around pointers.

If you had declared the original list using parenthesis then it makes an immutable list, and you would have gotten an error when you tried to modify it.

Code: Select all

MicroPython v1.5.1-33-g9bc8568 on 2015-11-27; PYBv1.0 with STM32F405RG
Type "help()" for more information.
>>> 
>>> x = (1, 2, 3)
>>> x[0] = 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> y = list(x)
>>> y[0] = 4
>>> x
(1, 2, 3)
>>> y
[4, 2, 3]
This web page has some insights into mutable versus immutable objects: http://codehabitude.com/2013/12/24/pyth ... immutable/

Re: Array copy m1=m2 works only on the first pass.

Posted: Mon Nov 30, 2015 4:03 pm
by pfalcon
The big thing to remember is that when you pass around list objects, you're really doing the C equivalent thing of passing around pointers.
And not less big thing to remember that almost any higher-level language (from Java) does it like that - objects are passed by reference.
CPython has a copy.deepcopy() routinte that isn't currently implemented in MicroPython or you could have used that.
It's of course available in micropython-lib.

Re: Array copy m1=m2 works only on the first pass.

Posted: Wed Dec 02, 2015 4:18 am
by deanr
I found a math quirk while reviewing my results. Try multiplying:
0.3117148*3.20806
This was supposed to be one in the identity matrix diagonal but showed up as 0.0
Any change in the lowest decimal gives a good answer. Strange bug.