OOP in Matlab - What to do when the output is a different class following method?

3 views (last 30 days)
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).
A quick note here that will be relevant later - 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 at least 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)
Anytime I multiply an object of the class "Bx" with any other object (be it another "Bx", or a DOM_INT, or a DOM_IDENT, or any other class), then the output will belong to the class "DOM_EXPR" type "_mult", described above 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.
The issue arises as follows. Sometimes a situation might arise where "Bx" objects appear exclusively as part of "_mult" objects. 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 in the link below.

Accepted Answer

Christopher Creutzig
Christopher Creutzig on 7 Aug 2013
Happens to work for me, in 2013a, but I think that is not the point, as that is probably coincidental for this particular example.
The problem with the _mult method you defined is that when you overload a function like _mult in MuPAD, you're actually responsible for the full multiplication, not just that of two arguments. I.e., in your last call, Bx::_mult is called with four arguments: 6, Bx(5,4), 3, Bx(4,3). It has to handle that, since MuPAD doesn't know whether you even want things like commutativity with numbers.
Here's what I think the structure you'll want could look like:
Bx::mult2 := proc(a : Bx, b : Bx)
... multiply two Bx objects ...
end_proc:
Bx::multBx := proc() // multiply arbitrarily many Bx
local n, left, right;
begin
case args(0)
of 0 do return(1);
of 1 do return(args(1));
of 2 do return(Bx::mult2(args()));
otherwise
n := floor(args(0)/2); % split here
left := Bx::multBx(args(1..n));
right := Bs::multBx(args(n+1..args(0)));
return(Bx::mult2(left, right));
end_case;
end_proc:
Bx::_mult := proc()
local Bxes, others, dummy;
begin
[Bxes, others, dummy] := split([args()], testtype, Bx);
assert(dummy = []);
others := _mult(op(others));
Bxes := Bx::multBx(op(Bxes));
if others = 1 then
return(Bxes);
elif Bxes = 1 then % can this happen?
return(others);
else
return(hold(_mult)(Bxes, others));
end_if;
end_proc:
  1 Comment
Kobye
Kobye on 7 Aug 2013
Edited: Kobye on 7 Aug 2013
Thanks a lot for your answer. Yes, the last bit also works for me. It is the penultimate bit that doesn't work, even when I run your proposed code. First let's try it with my code:
Input:
a:=6*Bx(5,4); => n.b. type is "_mult"
b:=3*Bx(4,3); => n.b. type is "_mult"
c:=a*b
Output:
(3*Bx(4, 3))*(6*Bx(5, 4))
This is not what I want. If I run your code, I get:
Bx(5, 4)*(6*(((binomial(3/4, Bx(4, 3))*binomial(3/7, Bx(7, 6)))/binomial(33/28, Bx(4, 3) + Bx(7, 6)))*Bx(33/28, Bx(4, 3) + Bx(7, 6))))
Which means clearly something has gone wrong.
Just a note; I wrote the first bit of code that you left blank. I'm not fully sure of the syntax proc(a : Bx, b : Bx) but I assume it means that inputs a,b must both be of type Bx.
In which case:
Bx::mult2 := proc(a : Bx, b : Bx)
//multiply two Bx objects
local n1,k1,n2,k2,c;
begin
n1 := op(a,1);
k1 := op(a,2);
n2 := op(b,1);
k2 := op(b,2);
c := binomial(n1,k1)*binomial(n2,k2)/binomial(_plus(n1,n2),_plus(k1,k2));
freeze(_mult)(c,Bx(n1+n2,k1+k2));
end_proc:
EDIT: I changed something in the first bit of code (no more freeze) as follows:
Bx::mult2 := proc(a : Bx, b : Bx)
//multiply two Bx objects
local n1,k1,n2,k2,c;
begin
n1 := op(a,1);
k1 := op(a,2);
n2 := op(b,1);
k2 := op(b,2);
c:=binomial(n1,k1)*binomial(n2,k2)/binomial(_plus(n1,n2),_plus(k1,k2));
_mult(c,Bx(n1+n2,k1+k2));
end_proc:
Everything now almost works. If I repeat my a*b from before, I get:
18*((5*Bx(9, 7))/9)
Almost perfect, except that I want the 18*5/9 to be evaluated and combined into one rational! Thanks again for the help, this is much appreciated!
EDIT 2: I saw the hold command in the code instead of freeze. So I said c:=a*b. The first time I call c, it is of the form shown above. Hold only stops execution temporarily. So the next time I call c, it has done what I want, and combined the coefficients into one!
It would be nice though if it did from the first time, but maybe I'm being picky.

Sign in to comment.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!