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

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
deanr
Posts: 7
Joined: Sat Nov 28, 2015 12:04 am

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

Post by deanr » Sat Nov 28, 2015 5:40 am

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

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

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

Post by dhylands » Sat Nov 28, 2015 8:41 am

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]

deanr
Posts: 7
Joined: Sat Nov 28, 2015 12:04 am

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

Post by deanr » Sat Nov 28, 2015 10:32 pm

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

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

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

Post by dhylands » Sat Nov 28, 2015 11:55 pm

I ran the code you presented and the green light comes on.

What does the code look like when it fails.

deanr
Posts: 7
Joined: Sat Nov 28, 2015 12:04 am

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

Post by deanr » Sun Nov 29, 2015 4:29 pm

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.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

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

Post by dhylands » Sun Nov 29, 2015 5:16 pm

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.

deanr
Posts: 7
Joined: Sat Nov 28, 2015 12:04 am

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

Post by deanr » Sun Nov 29, 2015 6:19 pm

I have a lot to learn before I understand this...
Thanks for the solution.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

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

Post by dhylands » Sun Nov 29, 2015 7:07 pm

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/

pfalcon
Posts: 1155
Joined: Fri Feb 28, 2014 2:05 pm

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

Post by pfalcon » Mon Nov 30, 2015 4:03 pm

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.
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/

deanr
Posts: 7
Joined: Sat Nov 28, 2015 12:04 am

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

Post by deanr » Wed Dec 02, 2015 4:18 am

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.

Post Reply