Re: Fuzzy Union in C++



Hi!

Dmitry A. Kazakov wrote:

This is what I meant. It cannot work for the challenge I posed: to write a
unit-aware console calculator. The authors are well aware of that. They
stress that all checks are strictly compile-time. They explicitly talk
about different types. I.e. unit expressions cannot be evaluated at
run-time, which is required by the challenge.

But you could use the same mathematical design, but using std::vector or
std::map with fixed size even if you want to add some special counters you
can extend length.

In my job I quite often use unit which is is not quite SI it is [veh/s]
(veh - vehicle).

So in position of the vector or map I can place value - like:

unit[0] = -1 // [s]^-1
unit[10] = 1 // [veh] = 1

in std::map<std::strnig,int> it is easier:

unit["s"] = -1
unit["veh"] = 1

Of course in runtime is in runtime. So if you want to check errors, you have
to deal with exceptions, or runtime asserts.

For an alternative see:

http://www.dmitry-kazakov.de/ada/units.htm

Interesting.

If you need to do conversion like in your nice software. You need to wrap
std::map with proper class to avoid mixing e.g. hours and seconds. But I
hope idea is clear.

But it is still possible to define basic constant units, and also
multipliers like "u = 10^-6". and after that you can write a [h] = b[s],
when you want to recalculate b in [s] to a in hours.

I have also implemented a C++ library based on similar approach though a
somewhat weaker, because C++ does not support subtyping constraints. But
it is a commercial product, so I cannot show the sources.

Ok. I understand. However for boundaries again I will recommend:
http://www.msobczak.com/prog/typegen/
or directly
http://www.msobczak.com/prog/bin/range.tar.gz

No. Basically it is a language weakness if it forces you to take such
decision. Especially if it does this early in design. The language should
support polymorphism. Static resolution of dispatch targets is mere a
matter of optimization, not of software design. Templates miss here badly.

Hmm... But I use templates mainly to increase speed and shorten the code.
I am not forced to use them. E.g. my Kohonen network: http://knnl.sf.net
I can design as dynamically created, but the binary code is faster. And
second thing, it is cheaper to find errors.

1. Many (most of) mathematical operations do not take closures of this
kind. Examples: matrix multiplication, or for that matter, Zadeh extension
principle. For instance fuzzy numbers: A + B:

(A+B)(w) =def=
= Sup min {A(x), B(y)}
x,y|w=x+y

And, what is the problem? Of course there is a big question if sup is a max
on proper (quite dense) set. If not you have to somehow implement limit
counting to calculate sup. And if I could implement a solver for w=x+y,
then all that is possible as bindings.

But if I could write:
(A+B)(w) =def=
= Sup min {A(x), B(y)}
x,y|w=x+y

Especially if, I can transform the problem to:
(A+B)(w) =def=
= Sup min {A(x), B(w-x)}
x,y|w=x+y

Below is a skeleton of such a procedure in R language, but for division of
thow fuzzy numbers. If you do not know R language, I made comments,
after "#" sign.

rm(list=ls()) # clear workspace

#define triangle function for MF
strianglemf <-function ( x,a,b ) {
max (0, 1-abs(1/(b-a)*(2*x-a-b)))
}

# set trangle function for velocity
mi_v<-function(v) {
strianglemf(v,30,130)
}

# similar for distance
mi_d<-function(d) {
strianglemf(d,12.5/1000,50/1000)
}

# function to calculate min of two values but using inverse operation for /.
ext_helper<-function(q,d) {
min(mi_v(q*d),mi_d(d))
}

# distance range in [km].
distance<-seq(from=0,to=70/1000,by=0.0001)

# recalculation to [m]
dst<-distance*1000

# extension rule nothing is calculated
# similar can be in C++, by usage of binders
ext_rule<-function(q) {
mid<-as.numeric(lapply(distance, function(d)
{ ext_helper(q,d) } ))
max(mid)
}

# velocity ranges
velocity<-seq(from=0,to=150,by=1)

# set real membership for the velocity
mivel<-as.numeric(lapply(velocity, function(velocity)
{ mi_v(velocity) } ))

# set real membership for the distance
midist<-as.numeric(lapply(distance, function(distance)
{ mi_d(distance) } ))

# ranges for flow
# flow = velocity / distance
flow<-seq(from=0,to=12000,by=1)

# apply extension rule
# in C++ it is a applying a binder on data.
miflow<-as.numeric(lapply(flow,function(flow)
{ ext_rule(flow) } ))

2. The problem we are discussing is not in "+". The problem is in "int".
Instead of one type int (the fuzzy set domain) you have an unknown in
advance number of:

Array<string> A;
Array<int> B
Array<Read_From_Soruce_File> C
Array<Ask_The_Operator> D

A = f(B, C, D);

So in that case you are forced to use a basic type and derive wrapers for
std::string, int, Read_From_Source_file, Ask_The_Operator.

3. The question of OP concerned the case where the parameter type (the
domain set) was whole R. For R fuzzy union is incomputable in the form you
have described:

for (r=-oo; r<=+oo, r+=0)
{
C(r) = max(A(r),B(r));
}

I understand, but the problem is at the very beginning, in the computer
there is NO R axis. So if you need true R, you have to implement symbolic
algebra package, or just limits for simple cases. Some adaptive solvers for
originary differential equations has such a optimisation that they
calculate step, here is epsilon (predicted error) will reach e.g. 10^-4
then limit is reached.

OP decomposed f:R->[0,1] into a *finite* set of functions of special kind
(singletons). This is an assumption that does not hold for all fuzzy sets.

E.g.?

For this reason it cannot be taken as a universal form of. Other
representations might have functions of different classes, which cannot be
represented in the form chosen. The rules of decomposition of AND, OR etc
heavily depend on the class of function considered. You cannot take for
granted that a plain enumeration of points would work in all cases for
everything possible. It just don't.

Yes. but It depends on the assumptions of the project. Every time you will
have a percent of simplifications, isn't it?

Of course you can, but it will not be based on templates alone.

Of course, I never told that.

For
simplicity consider design of a tree node. A note test a feature. A
feature value is a fuzzy set.

template<class Domain> class Node {...

Domain is the template parameter of the fuzzy set the feature tested by
Node will have as a value. Now the function Get_Child cannot be expressed
as a template, because you don't have the template parameter of the child:

Node<class ?>& Get_Child (unsigned Child_No) const;

It can be any.

I understand, but of course this is not reasonable in dynamical case. But
just for your knowledge, if you can define in compilation time any tree, I
mean if there is a need to build a static tree especially of types. E.g. if
you want to do some kind of dispatching of types like type sorting.

Once I need similar functionality to consider power function in many
implementations. I mean I designed library that dependently which type was
used than proper algorithm was chosen and proper result was generated:

p = x^y

and if y is integer there is very accurate algorithm for that.
if x is real and y is real result is real and algorithm is not accurate.
If x is integer and y is real result is real.
If any type is complex, than the result is complex.

I do not implement, but it is possible to do the same for matrices.

But the core of implementation was to define a partial ordering decision
tree for result type, and for usage of algorithm.

And there are containers for similar things:
http://www.boost.org/libs/mpl/doc/refmanual/classes.html

Especially are interested: map, and list, because there is implemented your
question about template implementation of "Node", but of course in template
meaning. So the tree is fixed in compilation, but, remember that templates
are as intelligent that useless code is not instantiated so it disappears
from the code. So:

template < typename T >
void fun (T & t){};

template < >
void fun<double> ( double & t ) {
//body
}

template < >
void fun<int> ( int & t ) {
//body
}

is completely other than

void fun( double & t ) {
//body
}

void fun( int & t ) {
//body
}

Even if usage is the same:

int main()
{
// in both cases
fun (1);
fun (1.0);
}

but if you will have a code like:

int main()
{
// you do not use any fun (...)
}

In the first code all fun functions disappears, in the second you will still
have all amount of function in the code.

So if you will generate a code of huge template tree using Boost MPL, but
only few you will use, the result will be small and fast.

Regards.

--
|\/\/|   Seweryn Habdank-Wojewódzki
`\/\/'

.