0

I want to extend the Maple CodeGeneration[C] by a handler for the piecewise function (no idea why it is not included). To this end I did:

with(CodeGeneration):
with(LanguageDefinition):

LanguageDefinition:-Define("NewC", extend="C",
    AddFunction("piecewise", anything::numeric,
        proc()
            local i;
            Printer:-Print("if(",_passed[1],"){",_passed[2],"}");
            for i from 3 to _npassed-2 by 2 do
                Printer:-Print("else if(",_passed[i],"){",_passed[i+1],"}");
            end do;
            Printer:-Print("else{",_passed[_npassed],"}");
        end proc,
    numeric=double)
);

Note that I am using if else statements in favour of case statements on puropose. Here is an example code to translate:

myp:=proc(x::numeric)
    piecewise(x>1,1*x,x>2,2*x,x>3,3*x,0);
end proc:
Translate(myp, language="NewC");

The output is

void myp (double x)
{
    if(0.1e1 < x){x}else if(0.2e1 < x){0.2e1 * x}else if(0.3e1 < x){0.3e1 * x}else{0};
    ;
}

For a valid C-routine I obviously need to replace the curly brackets like

{x}

by something like

{result=x;}

and analogous for the others. I could do this by hand by modifying the strings in the above AddFunction statement. But then the variable name result is not known to the code generator, so there will not be any declaration nor will the value of result be returned as needed to match the routine myp or any more complicated procedure in which the result of piecewise may be assigned to some other variable or used in computations. So how do I treat this properly in the CodeGeneration routines? I.e. how can I get a valid variable name etc.

highsciguy
  • 2,569
  • 3
  • 34
  • 59

1 Answers1

1

How about something like this?

restart:

with(CodeGeneration):
with(LanguageDefinition):

LanguageDefinition:-Define("NewC", extend="C",
    AddFunction("piecewise", anything::numeric,
        proc()
            local i;
            Printer:-Print("( (",_passed[1],") ? ",_passed[2]);
            for i from 3 to _npassed-2 by 2 do
                Printer:-Print(" : (",_passed[i],") ? ",_passed[i+1]);
            end do;
            Printer:-Print(" : ",_passed[_npassed]," ) ");
        end proc,
    numeric=double)
);

myp:=proc(x::numeric) local result::numeric;
    result := piecewise(x>3,3*x,x>2,2*x,x>1,1*x,0);
end proc:

Translate(myp, language="NewC");

double myp (double x)
{
  double result;
  result = ( (0.3e1 < x) ? 0.3e1 * x : (0.2e1 < x) ? 0.2e1 * x : (0.1e1 < x) ? x : 0 ) ;
  return(result);
}

[edited, to add the material below]

It turns out that CodeGeneration[C] does handle piecewise, but only if the optimize option is supplied. (I'll submit a bug report, that it should be handled by default.)

restart:

with(CodeGeneration):
with(LanguageDefinition):
myp:=proc(x::numeric) local result::numeric;
     result:=piecewise(x>3,3*x,x>2,2*x,x>1,1*x,0);
end proc;

         myp := proc(x::numeric)
         local result::numeric;
           result := piecewise(3 < x, 3*x, 2 < x, 2*x, 1 < x, x, 0)
         end proc;

Translate(myp, language="C", optimize);

double myp (double x)
{
  double result;
  double s1;
  if (0.3e1 < x)
    s1 = 0.3e1 * x;
  else if (0.2e1 < x)
    s1 = 0.2e1 * x;
  else if (0.1e1 < x)
    s1 = x;
  else
    s1 = 0.0e0;
  result = s1;
  return(result);
}

As you can see, piecewise is handled above by translation to a separate if(){..} block, with assignment to an introduced temporary variable. That temporary is subsequently used wherever the piecewise call existed in the Maple procedure. And the temporary is declared. Nice and automatic. So that might suffice for your use of piecewise.

You also asked how you could both introduce and declare such temporary variables in your own extensions (if I understand you rightly). Continuing on in the same Maple session from above, here are some ideas along those lines. An unassigned global name is generated. The myp procedure is put into inert form, to which the new local variable is added. And then that altered inert form is turned back into an actual procedure. As an illustration, I used a modified version of your original extension to handle piecewise. This all produces something close to acceptable. The only snag is that the assignment statement,

result = temporary_variable;

is out of place! It lies before the piecewise translation block. I don't yet see how to repair that in the method.

LanguageDefinition:-Define("NewC", extend="C",
    AddFunction("piecewise", anything::numeric,
        proc()
            global T;
            local i, t;
            t:=convert(T,string);
            Printer:-Print(t,";\n");
            Printer:-Print("  if (",_passed[1],
                           ")\n    { ",t," = ",_passed[2],"; }\n");
            for i from 3 to _npassed-2 by 2 do
                Printer:-Print("  else if (",_passed[i],")\n    { ",
                               t," = ",_passed[i+1],"; }\n");
            end do;
            Printer:-Print("  else { ",t," = ",_passed[_npassed],"; }");
        end proc,
    numeric=double)
):

T:=`tools/genglobal`('s'):

newmyp := FromInert(subsindets(ToInert(eval(myp)),'specfunc(anything,_Inert_LOCALSEQ)',
           z->_Inert_LOCALSEQ(op(z),
                              _Inert_DCOLON(_Inert_NAME(convert(T,string)),
                                            _Inert_NAME("numeric",
                                               _Inert_ATTRIBUTE(_Inert_NAME("protected",
                                               _Inert_ATTRIBUTE(_Inert_NAME("protected")
          ))))))));

             newmyp := proc(x::numeric)
             local result::numeric, s::numeric;
               result := piecewise(3 < x, 3*x, 2 < x, 2*x, 1 < x, x, 0)
             end proc;

Translate(newmyp, language="NewC");

double newmyp (double x)
{
  double result;
  double s;
  result = s;
  if (0.3e1 < x)
    { s = 0.3e1 * x; }
  else if (0.2e1 < x)
    { s = 0.2e1 * x; }
  else if (0.1e1 < x)
    { s = x; }
  else { s = 0; };
  return(result);
}

If you rerun the last three statements above (from the assignment to T, through to the Translate call) then you should see a new temp variable used, such as s0. And then s1 if repeated yet again. And so on.

Perhaps this will give you some more ideas to work with. Cheers.

acer
  • 6,671
  • 15
  • 15
  • This works of course and is probably the best solution for the piecewise function. But what can I do in the general case where I need to do some intermediate computations to evaluate a function in C language? How can I get valid variable names? – highsciguy Apr 10 '12 at 13:51