4

I am new to Object Oriented Programming. After a lot of stressing, I have finally managed to define my own class of objects in matlab's symbolic engine, muPad. The engine has its own language, the syntax of which is very logical and similar to matlab itself.

I would like to note that it isn't necessary to know muPad to assist with this problem. I think anyone with relevant OOP experience will be able to provide more insight into the issue.

I will start with a bit of background. My class is called Bx. Its objects have two distinct properties, n and k such that n => k => 0. A properly defined object in "Bx" might thus look like Bx(0, 0), or Bx(2, 2) or Bx(7, 2)... Each object in "Bx" is unique; if n1 = n2 and k1 = k2 this means that Bx(n1, k1) = Bx(n2, k2).

Note that muPad has a built-in expressions class called "DOM_EXPR" that is a superclass of the subtypes "_mult", "_plus", etc. e.g. a + b would be type "_plus", a * b would be type "_mult" etc.

One of the operations that I have defined on my class is multiplication. The rule is as follows:

Bx(a, b) * Bx(c, d) = binomial(a, c) * binomial(b, d) / binomial(a+c, b+d) * Bx(a+c, b+d)

This now works perfectly in my code, as long as one of the two objects being multiplied belongs to the class "Bx". For example:

Input:   Bx(2, 1)*Bx(4, 2)
Output:  (3*Bx(6, 3))/5

Input:   2*y*Bx(2, 1)*Bx(4, 2)^2
Output:  ((4*y)/7)*Bx(10, 5)

The issue arises as follows. Anytime I multiply two objects of the class "Bx" together, then the output will belong to a different class, called "DOM_EXPR" and type, "_mult". The above outputs are a good example. This makes sense; 3/5*Bx(6, 3) is an expression, composed of objects of the classes "DOM_RAT" and "Bx", (4*y)/7*Bx(10, 5) consists of "DOM_RAT", "DOM_IDENT" and "Bx".

If I multiply such an expression with a pure "Bx", e.g.:

a:=6*Bx(5,4); => n.b. type is "_mult"
b:=Bx(4,3);   => n.b. type is "Bx"
c:=a*b

then I get the output: (10*Bx(9, 7))/3 as expected. This is because in the _mult operation definition for the class "Bx", I have defined how "Bx" objects should behave when multiplied with "_mult" type "DOM_EXPR" objects.

However, sometimes a situation might arise where both "Bx" objects appear as part of a "DOM_EXPR" object. An example below:

a:=6*Bx(5,4); => n.b. type is "_mult"
b:=3*Bx(4,3); => n.b. type is "_mult"
c:=a*b

Now the output looks like: (3*Bx(4, 3))*(6*Bx(5, 4))

This is not what I want. I want muPad to further evaluate this expression. If I took all the operands of the arguments and multiplied them together with the existing code, I would get:

Input:   6*Bx(5,4)*3*Bx(4,3)
Output:  10*Bx(9, 7)

This is correct and what I would like muPad to do when multiplying a and b above.

I would be much endebted for any insight into how I can correct my code to behave correctly. I am not necessarily looking for syntax, but perhaps more for how you, an seasoned OOP programmer, might implement what I am trying to do, and how it differs from what I am doing. Once I understand what is wrong with my approach, and how I can improve it, I can figure out the syntax myself.

I have pasted the full muPad code below. You can run it in matlab, just type mupadwelcome in the command window, open a new mupad notebook and paste the individual blocks of code in new lines.

Bx := newDomain("Bx"):
Bx::new := proc(n,k)
begin

//++++//
if args(0)<>2 then
error("There must be exactly two arguments")
end_if;

//----//
new(dom,n,k)

end_proc:

------------------------------------

Bx::print := proc(x)
begin

//++++//
"Bx(".expr2text(op(x,1),op(x,2)).")";

end_proc:

------------------------------------

Bx::_mult := proc(a,b)
local type1,type2,berns,c,berns1,c1,berns2,c2,n,k,ni,ki;
begin

//++++//
if select(args(),testtype,"_mult") = null()

then
lists := split(args(),testtype,Bx);
berns := [lists[1]];
c := _mult(lists[2]);
ni := [op(berns[1])][1];
ki := [op(berns[1])][2];

//----//
if nops(berns) >= 2 and [op(berns)][1] <> [op([op(berns)][1])][1]
then
delete berns[1];
coefficient:=1;

//
while nops(berns)>=1
do
n := op(berns[1],1);
k := op(berns[1],2);
prod := Bx(_plus(ni,n),_plus(ki,k));
coefficient := coefficient*binomial(n,k)*binomial(ni,ki)/binomial(_plus(n,ni),_plus(k,ki));
delete berns[1];
ni := op(prod,1);
ki := op(prod,2);
end_while;
//

c := _mult(coefficient,c);
case c
of 1 do Bx(ni,ki); break;
otherwise freeze(_mult)(c,dom(ni,ki));
end_case;

else
case c
of 1 do berns[1]; break;
otherwise freeze(_mult)(c,berns[1]);
end_case;

end_if;
//----//

//++++//
else
lists := split(args(),testtype,"_mult");
_mult(op(lists[1]),lists[2],lists[3])

end_if;
//++++//

end_proc:

------------------------------------

Bx::_power := proc(a,b)
local res;
begin

//++++//
case b
of 0 do 1; break;
of 1 do a; break;
otherwise
res:=a;

//----//
for i from 1 to b-1 do
res:=res*a;
end_for;
//----//

res;

end_case;
//++++//

end_proc:


a:=6*Bx(5,4)

b:=3*Bx(4,3)

6*Bx(5,4)*3*Bx(4,3)

Edit: Interestingly, if I don't define my own _mult and _power methods, muPad seems to do what I want when multiplying two mult objects containing "Bx", except evidently the actual "Bx" multiplication, see the image below.

mupad screenshot

Kobs
  • 209
  • 3
  • 8
  • I think the reason that your last example `6*Bx(5,4)*3*Bx(4,3)` worked as you expected is because MuPAD rearranged the terms so that the following is evaluated: `(6*3)*(Bx(5,4)*Bx(4,3)) == 18 * (5/9*Bx(9,7)) == 10*Bx(9,7)` (with multiplication assumed commutative). Perhaps you need to do this in your code; decompose all arguments into `Bx` objects and other internal types, multiply each group separately, then multiply the result from each group. – Amro Aug 06 '13 at 00:44
  • Now I dont fully understand mupad code, but I can see you are doing something along those lines, right? Perhaps what you are currently missing is to recursively look for `Bx` objects inside `_mult` types operands. For example `type(3*Bx(1,1))` is considered of type `_mult`, but you need to break it into `op(..,1)` and `op(..,2)` and possibly doing this recursively on each of those two (in case of more complicated expressions) until you split all the terms into `Bx` types and other builtin types, then do the multiplication accordingly to always end-up with at least one operand of type `Bx` – Amro Aug 06 '13 at 00:50
  • Hi Amro, thanks for taking the time to respond to my query! I should have commented my code better, but you figured it out. Indeed, I first run a check for "Bx" objects in the arguments. I then multiply those together (which is done recursively by _mult), resulting in a single, new Bx term. I then do the same for all non-Bx objects, giving a "coefficient", which I pre-multiply with the new Bx term. Job done. This fails if both args are of type _mult. The approach you propose is what I would like to do, however, I'm a little uncertain how to get muPad to look inside _mult types for Bx operands. – Kobs Aug 06 '13 at 02:00
  • I'm afraid I don't know enough of the mupad language to know how to do that step :) Perhaps if you add "symbolic-math" and "symbolic-computation" tags to your question, you would attract the right people (there is an active community at [Mathematica.SE](http://mathematica.stackexchange.com) who are probably more familiar with symbolic computation languages) – Amro Aug 06 '13 at 02:23
  • I looked through and I didn't see an operator handling your class (Bx) multiplied by a scalar. It could be in there but the syntax is very foreign to me. If this was say C++, I would define a Bx operator*(Bx, Bx) and Bx operator*(double, Bx). The first would handle Bx * Bx, resulting in C * Bx(a,b) still needing to be solved. The second would solve that equation. – Matt Jun 10 '15 at 19:06

0 Answers0