3
$\begingroup$

I'm investigating matrix assembly and one of the things I'm trying to do is add specific values to a larger matrix based on a list which is essentially defining connectivity of nodes.

For example: Start with a null matrix, for this example a 6x6 will be selected.

m=ConstantArray[0, {6, 6}]

If I want to add 2 to the value at position {3,4} I can use the following code.

m=ReplacePart[m, {3, 4} -> (Part[m, 3, 4] + 2)]

What I would like to do is feed this process via lists though. Creating a list of values to be added.

v={{2},{3},{4}}

Creating a list of locations to add these values.

locs = {{1, 2},{1,3},{1,4}}

Combining these two lists so that it gives {value, loc} as a new list

comb = Partition[Flatten[Riffle[v, locs]], 3]

Trying to map this new list comb across the matrix m using ReplacePart

f = ReplacePart[m,{#[[2]], #[[3]]} -> (Part[m, #[[2]], #[[3]]] + #[[1]])] & /@ comb

If my feeder list (comb) only have a single entry, the code runs, but I get an extra level added to m, which I can Flatten out afterwards.

EDIT Values may be added to the same location several times and so methods need to keep a running total.

However, once I get two or more entries to my feeder list I get a new matrix for each iteration, which makes sense but not what I want.

I'm missing something here as it's creating a new entry for each time the list is mapped across. When the intention is to add the values in the list to the existing values at a certain position and return the new updated matrix.

I'm guessing to get a single matrix with all of the values added to the exising values will need an approach that writes the new matrix to m before running the next element in the list. Perhaps using something similar to a Do or maybe Module (which are a complete mystery to me).

I'm hoping my code and description is clear and that I'm not too far away from a working solution.

Any help gratefully appreciated.

$\endgroup$
4
  • $\begingroup$ Possible duplicate: (37566) $\endgroup$
    – Mr.Wizard
    Jun 16, 2015 at 2:55
  • $\begingroup$ I think the question that you've linked to provides a solution to the question that I've asked, however the methods provided are specific ONLY for SparseArray (a function I was unaware of yesterday). The answers to my question have shown several different ways of achieving the same behaviour as my question was not function specific. I personally find the answers to my question to be broad and show comparisons between methods and style which has to be positive for someone learning Mathematica by creating behaviour using several different methods. Please can you reopen. $\endgroup$ Jun 16, 2015 at 6:38
  • 2
    $\begingroup$ Are you aware that you can convert a SparseArray to a standard array (nested List) using Normal? $\endgroup$
    – Mr.Wizard
    Jun 16, 2015 at 7:06
  • $\begingroup$ No, every day is a school day, thanks for the pointer... I'm going to have a busy afternoon working through and deconstructing all of the solutions below. I'm genuinely grateful for them it's given me a lot to think about and tinker with. $\endgroup$ Jun 16, 2015 at 7:08

5 Answers 5

10
$\begingroup$

(Untested, so CW)

Consider using MapThread[] along with ReplacePart[]:

MapThread[ReplacePart[m, #1 :> Extract[m, #1] + #2] &, {{{1, 2}, {1, 3}, {1, 4}}, {2, 3, 4}}]

Another possibility involves the use of SparseArray[]:

SetSystemOptions["SparseArrayOptions" -> {"TreatRepeatedEntries" -> 1}];
pos = {{1, 2}, {1, 3}, {1, 4}};
add = {2, 3, 4};
m + SparseArray[Thread[pos -> add], Dimensions[m]]

The little piece of black magic on top is needed if you have repeated entries in pos; this will add up the corresponding entries together.

$\endgroup$
10
  • $\begingroup$ That's the way to do it... +uno $\endgroup$
    – ciao
    Jun 15, 2015 at 20:24
  • 1
    $\begingroup$ This "Mathematica Gedanken Edition" is very difficult to use, admittedly. More seriously, I'm glad to hear that it works. :) $\endgroup$ Jun 15, 2015 at 20:28
  • 1
    $\begingroup$ @J. M. That's plagiarism. "Mathematica Gedanken Edition" is trademarked $\endgroup$ Jun 15, 2015 at 21:01
  • 1
    $\begingroup$ @bel, BTW, who's handling customer complaints for that? The interface is really not the best… ;P $\endgroup$ Jun 15, 2015 at 21:17
  • 1
    $\begingroup$ @J. M. You surely have the "Student Gedanken Edition". You should upgrade to the NKGW (namely "New Kind of Gedanken Workbench") which provides many of the new Lotus-123 and WordPerfect enhancements, including copy-paste and automatic line numbering. $\endgroup$ Jun 15, 2015 at 21:23
4
$\begingroup$

The "TreatRepeatedEntries" method may be impossible to beat but the more mundane method is to use AddTo. Starting with the definitions in your question:

v = {{2}, {3}, {4}};

locs = {{1, 2}, {1, 3}, {1, 4}};

m = ConstantArray[0, {6, 6}];

comb = Partition[Flatten[Riffle[v, locs]], 3];

We need merely:

m[[##2]] += # & @@@ comb;

Now:

m
{{0, 2, 3, 4, 0, 0},
 {0, 0, 0, 0, 0, 0},
 {0, 0, 0, 0, 0, 0},
 {0, 0, 0, 0, 0, 0},
 {0, 0, 0, 0, 0, 0},
 {0, 0, 0, 0, 0, 0}}
$\endgroup$
5
  • $\begingroup$ SA can be beat handily so long as cardinality of changes is not too crazy.... and +1 on the shorty... $\endgroup$
    – ciao
    Jun 16, 2015 at 7:14
  • $\begingroup$ @ciao Interesting. I think when working on problems like (8178714) I found "TreatRepeatedEntries" faster than any other (non-compiled) method I tried. Could you give an example? Or better yet post an answer with your method and timings? (Oh, and thanks.) $\endgroup$
    – Mr.Wizard
    Jun 16, 2015 at 7:18
  • $\begingroup$ Sure - I cobbled it up when I saw question as throw-away, s/b easy to recreate, will do in next 24 or so (and test to verify the Q&D result) - basically I'd linearized the indices, gathered, totaled that, then fed as an AddTo, then reshaped... stay tuned $\endgroup$
    – ciao
    Jun 16, 2015 at 7:24
  • $\begingroup$ @Mr.Wizard neat and +1..."TreatRepeatedEntires" was another of the wonderful new things like PartitionMap...obviously I make comments about efficiency... $\endgroup$
    – ubpdqn
    Jun 16, 2015 at 10:27
  • $\begingroup$ @Mr.Wizard: umm, let's chalk this up to shooting my mouth off early - re-did from memory my method, it behaves similarly to your neat short answer: it can beat SA when number of changes is relatively small (so overhead of SA creation takes its toll), and slower when cardinality high, with the downside of unnecessary verbosity of code - not worth posting (unless I can figure out how to beat GatherBy 's functionlaity) $\endgroup$
    – ciao
    Jun 17, 2015 at 3:48
3
$\begingroup$

put all your added values in a sparse array of same dimension and add the whole matrix:

 m = ConstantArray[0, {6, 6}]
 v = {2, 3, 4, 6}
 locs = {{1, 2}, {3, 3}, {1, 4}, {1, 2}}

the GatherBy in here is taking care of repeated positions (Note @GuessWhoItIs shows a cleaner approach , this does not rely on remembering an undocumented system option however.. )

 m + SparseArray[#[[1, 1]] -> Total[#[[All, 2 ]]] & /@ 
     GatherBy[ Transpose[{locs, v}] , #[[1]] & ], 
     Dimensions@m] // MatrixForm

enter image description here

just this if you know you don't repeat positions:

 m + SparseArray[Rule @@@ Transpose[{locs, v}], Dimensions@m];
$\endgroup$
1
  • $\begingroup$ I was about to post something similar. +1 If you migrate the list of locs and v's out of the sparse array you can handle repeat positions. m = m + Total[ SparseArray[#1 -> #2, Dimensions[m]] & @@@ Transpose@{locs, v}] at the cost of some overhead with multiple sparse arrays. $\endgroup$
    – N.J.Evans
    Jun 15, 2015 at 21:48
2
$\begingroup$

How about using MapAt?

pos = {{1, 2}, {1, 3}, {1, 4}, {2, 6}, {2, 6}};
add = {2, 3, 4, 10, 11};

Block[{i = 0, a = Reverse@add[[Ordering@pos]]}, 
 MapAt[(i++; Plus[#, a[[i]]]) &, m, pos]]

Not the most elegant, but seems to work well, and handles repeat indices. It seems MapAt does some weird ordering of the indices before applying them. Maybe the community can comment on why.

$\endgroup$
2
$\begingroup$
m = ConstantArray[0, {6, 6}]
v = {{2}, {3}, {4}}
locs = {{1, 2}, {1, 3}, {1, 4}}
comb = Partition[Flatten[Riffle[v, locs]], 3]

result = Fold[
             ReplacePart[#1, {#2[[2]], #2[[3]]} -> 
                  Part[#1, #2[[2]], #2[[3]]] + #2[[1]]] & , m, comb]
{{0, 2, 3, 4, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},   
   {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},  {0, 0, 0, 0, 0, 0}}

Positions might be duplicated.
For example with locs={{1,2},{1,2},{1,2}} and v = {{2}, {3}, {4}}it gives the total 2+3+4 at position {1,2} :

{{0, 9, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, 
   {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}}
$\endgroup$
5
  • $\begingroup$ Thanks for the solution, if the locs are all duplicates, say {{1,2},{1,2},{1,2}} for example, this solution returns the last value in v at the matrix location though, rather than being the sum which would be 9. Whilst duplicate locations will be rare they still may occur and I would like to add them on to the existing value if possible. $\endgroup$ Jun 15, 2015 at 20:14
  • $\begingroup$ That is a error from me: Part[m, ...] should be Part[#1,...]. I'm going to correct this error. Sorry $\endgroup$
    – andre314
    Jun 15, 2015 at 20:24
  • 1
    $\begingroup$ Error corrected ! $\endgroup$
    – andre314
    Jun 15, 2015 at 20:25
  • $\begingroup$ No apology needed, thanks for taking the time to answer :) $\endgroup$ Jun 15, 2015 at 20:30
  • 2
    $\begingroup$ You should add a note that positions might be duplicated to the question $\endgroup$
    – george2079
    Jun 15, 2015 at 21:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.