Skip to main content

variable definitions - How to Set parts of indexed lists?


I would like to assign a list to an indexed variable and then change it using Part and Set like this:


matM[i] = ConstantArray[1, {10, 10}];
matM[i][[1,10]] = 10


Unfortunately I will get the error:



Set::setps: "matM[i] in the part assignment is not a symbol. "



Using Subscript will not work either:


Subscript[matM,i] = ConstantArray[1, {10, 10}];
Subscript[matM,i][[1,10]] = 10

will give the same error.


How can this be done?




Answer



I can offer a sort of a solution, which has its shortcomings, but will allow you (more or less) to use the syntax you want. The idea is that you mark symbols you need, as "references", and then execute your code in a special dynamic environment where Part is overloaded.


Implementation


Here is the code. This function will mark you symbol as a reference.


ClearAll[makeReference];
makeReference[sym_Symbol] :=
sym /: Set[sym[index_], rhs_] :=
With[{index = index},
With[{heldVal = Hold[sym[index]] /. DownValues[sym]},
If[heldVal === Hold[sym[index]],

Module[{ref},
AppendTo[DownValues[sym],
HoldPattern[sym[index]] :> ref]
]
];
Hold[sym[index]] /. DownValues[sym] /. Hold[s_] :> (s = rhs);
]];

What happens is that we "softly" overload Set on this particular symbol via UpValues, so that an intermediate symbol is inserted where the actual data will be stored, and our symbol (for a given index) refers to that intermediate symbol. Since the latter has no restrictions on part assignments, we can assign parts of it directly at O(1) time.


However, the subtlety is that when we call Set[Part[s[ind],1,2],something], Set holds its first argument, and therefore, s can not communicate to Set that this is special (UpValues won't work here since the s is too deep inside an expression - on level 2 - while UpValues are only looked at at level 1). To solve this problem, we will overload Part, but do it locally within a local dynamic environment, to make this operation safer. This is a dynamic environment:



ClearAll[withModifiedPart];
SetAttributes[withModifiedPart, HoldAll];
withModifiedPart[code_] :=
Internal`InheritedBlock[{Part},
Unprotect[Part];
Part /: Set[Part[sym_Symbol[index_], inds__], rhs_] :=
With[{index = index},
Hold[sym[index]] /. DownValues[sym] /.
Hold[s_] :> (s[[inds]] = rhs);
];

Protect[Part];
code];

Tests


Now, we can test this:


ClearAll[a];
makeReference[a];

and then


withModifiedPart[

a[1] = Range[10];
a[1][[2]] = 100;
a[1]
]


 {1, 100, 3, 4, 5, 6, 7, 8, 9, 10}

Let's now measure some timings:


withModifiedPart[

a[1] = Range[100000];
Do[a[1][[i]] = a[1][[i]] + 1, {i, a[1]}];
a[1]
] // Short // AbsoluteTiming


  {1.5126953,{2,3,4,5,6,7,8,9,<<99984>>,99994,99995,99996,99997,
99998,99999,100000,100001}}

We can compare this to the time it takes for direct assignments:



aa = Range[100000];
Do[aa[[i]] = aa[[i]] + 1, {i, aa}]; // Short // AbsoluteTiming


 {0.2470703,Null}

So, for massive assignments, we are about 6 times slower (which I think is OK). We can also see how costly is the overloaded Part for normal assignments:


withModifiedPart[
aa=Range[100000];
Do[aa[[i]]=aa[[i]]+1,{i,aa}]

];//Short//AbsoluteTiming


  {0.2822266,Null}

from where it looks that those are slower by about 15 percents.


Conclusions


The suggested solution requires 2 modifications to your code:



  • call makeReference on symbols which you wish to use as indexed symbols with part assignment, prior to assigning to them.


  • Execute all your code containing such assignments, inside the withModifiedPart environment.


So, your original code will be changed to


ClearAll[matM];
makeReference[matM];

withModifiedPart[
matM[i] = ConstantArray[1, {10, 10}];
matM[i][[1, 10]] = 10
]


What about safety? I would argue that, for this particular case, modifying Part is safe enough. This is because, first, Part is overloaded softly, via UpValues. This means that, when Part is not inside some head which holds arguments, it will execute normally before it would even "think" of a new definition. Now, when it is inside some head which holds arguments, the new definition will only work if that head is Set. Note that no ne rules were added to the Set itself. And since normally, assignments to indexed variables are not allowed anyway, we don't risk breaking some internal behavior.


The performance here is worse than for direct assignments to symbol's parts, but overall seems acceptable.


Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

Is there a way to do conditional matrix loop using 'continue'

I have the following: n = 3; m = 5; ww = RandomReal[{0, 0.1}, {n, n}]; uu = RandomReal[{0, 1}, {m, n}]; pp = RandomReal[{0, 1}, {n, n}]; ss = RandomInteger[{0, 5}, {m, n}]; Grid[{{"ww", "uu", "pp", "ss"}, {ww // TableForm, uu // TableForm, pp // TableForm, ss // TableForm}}, Spacings -> {5, 2}, Dividers -> All] where I would like to look at every element of matrix ss and produce a matrix tt , with zeroes at the locations in ss which have zeroes, and in all other positions do the following: tt = (-1/Subscript[ww, m]) Log[(1 - uu)/(Subscript[pp, m - 1])], where Subscript[ww, m] is the value at index of ww matrix and where Subscript[pp, m - 1] is the value at index-1 of pp matrix. So for example if the first value ever read from matrix ss happens to be 2, then value taken from matrix ww would be from the row 2, but from pp would be from row 1. Also how to tell difference between a 0 as a valid value from within the matrix elemen...