10
$\begingroup$

You need to set up the LinkSnooper to follow the steps from this question:


So, if you have it set for a notebook or $FrontEndSession you can check that a Button's action in:

DynamicModule[{x}, {Button["+", FEPrivate`Set[x, 1]], Dynamic[x]}]

nicely changes x to 1 without calling the Kernel. However, a slightly complicated example needs Kernel's help:

DynamicModule[{x}, {Button["+", FEPrivate`Set[x, 1 + 1]], Dynamic[x]}]

There is no Plus in FEPrivate` context. But we know that the FrontEnd knows how to add:

DynamicModule[{x},
 {Slider[Dynamic[x], {1, 20}], Style[1, FontSize -> Dynamic[x + 12]]}
]

The question is, how to force it to add, in the first/second example, without calling the Kernel.


Here is a hilarious trick (not useful in general):

DynamicModule[{x}, {Slider[Dynamic[x], {1, 20}], Dynamic[11 + x]}]

DynamicModule[{x}, 
  {Slider[Dynamic[x], {1, 20}], 
   Style[Dynamic@CurrentValue[FontSize], FontSize -> Dynamic[11 + x]]
  }
]

enter image description here

$\endgroup$
4
  • $\begingroup$ not sure but I think that what the second kernel that always is started with newer versions might be used for? $\endgroup$ Oct 4 '16 at 15:59
  • $\begingroup$ @AlbertRetey my point is rather how to write 1+1 so that FE adds it by itself instead of sending to the main Kernel. And I don't care how it will be done :) $\endgroup$
    – Kuba
    Oct 4 '16 at 18:40
  • $\begingroup$ actually my hopes that this will be answered rest entirely on you $\endgroup$ Oct 4 '16 at 23:37
  • $\begingroup$ @AlbertRetey and others, the state of art is you can't do that. FE's kernel is limited and addition in mathematica is a complex task as it supports not only machine precision but exact numbers etc, not to mention arbitrary expressions. FE can't handle that so this path is closed for users. More or less what JF told me year ago or so, forgot to add this comment. $\endgroup$
    – Kuba
    Aug 10 '17 at 10:50
2
$\begingroup$

This is not the most serious answer, but you could use the following. Its most practical use is if you want a simple counter or something like that. It works with binary representations of numbers, where True means 1 and False means 0. It is based on the concept of a half adder. Don't be fooled by the quasi informative comments that may give the impression that the code is understandable, in the end the code has become quite write-only.

nn = 2;

(*first argument (input) is the carry*)
(*output[[1]] is the value *)
(*output[[2,1]] is the carry *)
(*output[[2,2;;]] is the rest of the numbers that have to be added *)
retainingHalfAdd =
  Function @@
   Function[Null, Hold@{Xor[#, #2], {# && #2, ##3, False}}, HoldAll] @@
        Part @@@ Hold @@ ({Slot @@ {1}, #} & /@ Range[nn + 1]);

cyclerReducer = 
  List @@@
   Function@Evaluate@
     Join[
      Hold[#[[1, 2]]],
      Apply[Part,
       Hold@Evaluate@
         Append[{Slot @@ {1}, 2, #} & /@ Range[2, nn + 1], {#, 1, 1}]
       , {2}]
      ];

halfAddStep = cyclerReducer@{retainingHalfAdd[#[[1]]], #[[2]]} &;
argListHeld =
  List @@@ Hold@Evaluate@
     Append[
      List @@@ Hold@Evaluate@
         Prepend[
          Part @@@ 
           Hold @@
            ({Slot @@ {1}, # } & /@ Range[nn]), 
          True],
      ConstantArray[False, nn + 1]
      ];

plusOne = 
  Part @@@ Function@Evaluate@
     Append[Nest[Unevaluated@halfAddStep /@ # &, argListHeld, nn + 1],
       2];

plusOneCode = 
  plusOne //.
   Join[
    Join @@ 
     OwnValues /@ 
      Unevaluated@{argListHeld, halfAddStep, cyclerReducer, listRHA, 
        retainingHalfAdd, nn}
    ,
    {And -> FEPrivate`And, Xor[x_, y_] :>  SameQ[SameQ[x, y], False]}
    ];

Here are some tests to see how plusOne can be used

testFu =
  FromDigits[Reverse@#, 2] &@
    Boole@Most@
      Nest[plusOne, OddQ /@ Reverse@IntegerDigits[#, 2, nn], #2] &;
plusOne[ConstantArray[False, nn]]
Nest[plusOne, ConstantArray[False, nn], 2^nn]
{True,False,False}
{False,False,True}

We can use the function like plus, but we will have to add 1 a lot of times.

testFu[#, #2] == Mod[# + #2, 2^nn] &[324232, 3445]
True

Using the following, we see that it can work completely in the Front End. We use plusOneCode instead of plusOne, because a few modifications were needed to make it work Front-End-only. Click the square a few times to toggle the colour. Uncomment the comment to see that we are counting using x.

ReleaseHold[
 ReplaceAll[#, 
    Join[OwnValues[
      plusOneCode], {startCount -> ConstantArray[False, nn + 1], 
      targetCount -> Append[ConstantArray[False, nn], True]}]] &@
  Hold@
   DynamicModule[{x, y = False},
    x = startCount;
    Column@
     {
      Graphics@
       {
        DynamicBox[If[y, RGBColor[1, 0, 1], RGBColor[0, 1, 0]]],
        EventHandler[
         Rectangle[{0, 0}], "MouseDown" :> (  

           FEPrivate`Set[y, 
            SameQ[FEPrivate`Set[x, plusOneCode[x]], targetCount]]
           )

         ]
        }
      (*,
      Dynamic[Column@x]*)
      }
    ]
 ]

The following shows that doings things in the kernel is about 20x faster

feTimingFunc = (Function @@ Nest[plusOneCode /@ # &, Hold[#], 500]);
kerTimingFunc = (Function @@ Nest[plusOne /@ # &, Hold[#], 500]);
kerRes = kerTimingFunc[ConstantArray[False, nn + 1]]; // 
  AbsoluteTiming // First
ReleaseHold[
   Hold[
     feRes = FE`Evaluate@feTimingFunc[input]
     ] /. 
    Append[OwnValues[feTimingFunc], 
     input -> ConstantArray[False, nn + 1]]
   ] // AbsoluteTiming // First
0.013697
0.210329

Somehow something breaks down if we nest the function more than 500 times, probably the front ends recursion limit is reached. Timings are quite acceptable compared to other delays when using Dynamic, in my opinion.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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