Saturday, December 28, 2013

Zero in Software Development

Zero may be nothing to you, but it has caused many problems through history.  In computer programming it is particularly important since incorrect handling can cause minor bugs or worse.  In fact, in a well-documented disaster a rocket was lost due to a software defect related to the incorrect use of zero in a Fortran program.

I will give some examples of the sort of things that can go wrong if you don't handle zero properly.  But first a brief history.

History of Zero

First, we should distinguish between zero as a digit and zero as an integer.  The symbol for zero can just be used as a digit, ie, a placeholder in a positional based numbering system (eg, the zero in 307 represents that there are no "tens").  A symbol for an empty position was first used by the Babylonians.  But the idea of the integer zero was not invented till later by the Mayans and independently in India.

The Sumerians were probably the first to use a positional number system but not having a symbol for zero created problems.  If you wanted to send a message to the market that you had 30 goats to sell there was no way to distinguish between 30 and 3.  A farmer could avoid this problem by instead selling 29 or 31 goats but as numbers became more important the lack of a zero became problematic.

The Babylonians started with a similar system to the Sumerians, but eventually used the notation of a small mark to signify an empty position in a number.  Greek astronomers were the first to use a circle for zero as a placeholder, but it was not until centuries later that Indian mathematicians included zero as a full integer like one, two etc.  One reason that mathematicians probably avoided zero was the problem of what is the result of dividing a number by zero or the even weirder idea of 0/0 (zero divided by zero).

Zero in Algorithms

Sometimes having empty input makes no sense.  For example, how do you take the average of zero numbers?


  int average(int[] array)
  {
      int sum = 0;
      for (int i = 0; i < array.size(); ++i)
          sum += array[i];
      return sum/array.size();
  }

The above code will have a divide by zero error at run-time when presented with an empty array.  We can easily fix this:


  int average(int[] array)
  {
      if (array.size() > 0)
      {
          int sum = 0;
          for (int i = 0; i < array.size(); ++i)
              sum += array[i];
          return sum/array.size();
      }
      else
          return 0;  // May be more appropriate to return -1 to indicate an error, or throw an exception
  }

Now suppose we want to find the sum of an array of numbers.  Having just coded the above average() function we might be tempted to write:


  int sum(int[] array)
  {
      if (array.size() > 0)
      {
          int sum = 0;
          for (int i = 0; i < array.size(); ++i)
              sum += array[i];
          return sum;
      }
      else
          return 0;
  }

However, in this case, taking the sum of zero numbers does make sense.  The code should simply be written as:


  int sum(int[] array)
  {
      int sum = 0;
      for (int i = 0; i < array.size(); ++i)
          sum += array[i];
      return sum;
  }

[Actually, doing it the first way would be better in Fortran as we will see below.]

I will now look at a few ways some programming languages do not handle zero well.

Fortran

Fortran was the first programming language I learnt and you would expect that I would have a certain fondness for it.  Unfortunately, I always found it very peculiar.  One of the stupidest things in Fortran is that a FOR loop is always executed at least once.  This has resulted in countless bugs where boundary conditions were not handled properly.  To avoid bugs you usually have to wrap the FOR loop in an IF statement which protects against the empty condition.

Pascal

Soon after learning Fortran I learnt Pascal and it was an enormous relief.  It is much simpler to understand and elegant and, of course, it handles FOR loops correctly.  However, I later came to appreciate that it has some deficiencies when compared to C-like languages.  Ranges in Pascal are specified using inclusive bounds, so for example, the range "1..10" means the integers from 1 to 10 inclusive.  You can express a range with one element such as "1..1", but how do you express a range with zero elements?


A common mathematical notation is to express ranges with square brackets when the ends are included and round brackets when the ends are excluded.  Many things are simplified when an inclusive lower bound and an exclusive upper bound are used.  Some people refer to this as using "asymmetric bounds".  For example, the set of numbers from zero (inclusive) to one (exclusive) is shown as [0, 1).

Now you might wonder why you want to express a range with no elements.  Actually, it is often very important to be able to do this sort of thing.  Many algorithms work on data sets that may have zero elements as a degenerate case.  If these situations are not handled then the code will fail under situations like being presented with empty input.

It is better for many reasons to use asymmetric bounds for ranges (as is conventional in C), and I will discuss these reasons in a later post.  But one of the main advantages is that you can easily specify an empty (ie, zero-length) range.

Brief

In my first job I used to edit my C code in a word processor (WordStar), and this was a very painful experience.  Soon after its release (1985?) I started using the programmer's editor called Brief which was simply wonderful in comparison.  (Perhaps the best thing was the unlimited undo which was not restricted to changes in the text but other things like changes to the selection and layout - in fact the Undo for my hex editor, HexEdit, is modelled closely on it.)

Brief allowed you to "mark" text and copy or append it to its internal "clipboard".  There were several ways of marking text including "non-inclusive" mode.  Non-inclusive mode was very useful as it used the asymmetric bounds pattern mentioned above.  This allowed you to mark the start and one past the end of a block of text for further processing.

I found non-inclusive mode very useful for creating macros in Brief's built-in programming language.  I created several macros that built up text in the clipboard by appending to the clipboard in non-inclusive mode.  But there was one major problem - copying or appending to the clipboard generated an error when the start and end of the marked range were at the same place.  That is, you could not copy a zero-length selection.

I even wrote to the creators of Brief (who were obviously very clever guys for creating such a brilliant tool).  The reply I got was basically that there could be no use for a selection of zero length.  Of course, they were wrong as many of my macros failed under situations where they attempted to append a zero-length selection to the clipboard.

The moral is that even very clever people may not appreciate the importance of zero.

C

The good things about the C language is that in almost all cases zero is handled correctly.  Experienced C programmers, usually reach the point where they need only give cursory consideration to boundary conditions.  Generally, if you write the code in the simplest, most obvious, way it will just work under all inputs with no special code for boundary conditions.

Even the syntax of the language adopts the "zero is a normal number" approach.  For example, in C the semi-colon is used as a statement terminator (rather than a statement separator as in Pascal).  A compound statement which contains N statements will have N semi-colons - so a compound statement with no nested statements is not a special case.

Consider compound statements in Pascal and C:

Pascal:

  begin statement1; statement2 end;     (* 2 statements, 1 semi-colon *)
  begin statement1 end;                       (* 1 statement,  0 semi-colons *)
  begin end;                                         (* 0 statements, can't have -1 semi-colons! *)
C:

  { statement1; statement; }             /* 2 statements, 2 semi-colons */
  { statement1;  }                            /* 1 statement,  1 semi-colon */
  { }                                               /* 0 statements, 0 semi-colons */

How can this really be that important?  This could be important is if you have software that generates C code.  In some circumstances you may need to generate a compound statement containing no nested statements.  The syntax of C means you don't need to handle the boundary condition specially.

Also if you have ever edited much Pascal code you will know how painful it is to add (or move) statements at the end of a compound statement.  You have to go to the end of the previous line and add a semicolon.  Worse, if you forget to do so, as usually happens, you get syntax errors on the next build.

Note that C does not take this to extremes though.  Commas in function parameter lists are separators not terminators.


  func(param1, param2);   // 2 parameters, 1 comma
  func(param1);                // 1 parameter, 0 commas
  func();                           // 0 parameters, can't have -1 commas!

But you can use commas as terminators in array initialisation, though the last comma is optional.  This makes it easy to edit arrays, such as appending and reordering the elements.


  float array[] = {
    1.0,
    2.449489742,
    3.141592653,
  };

Malloc Madness

Given C's very good behaviour when it comes to zero it was very surprising to find a certain proposal in the draft C standard in the late 1980's.  Some on the C standard committee were keen to have malloc(0) return NULL.

All C compilers until then based their behaviour on the defacto standard, ie the behaviour of the original UNIX C compiler created by Dennis Ritchie (often referred to as K&R C).  This behaviour was for malloc(0) to return a valid pointer into the heap.  Of course, you could not do anything with the pointer (except compare it to other pointers) since it points to a zero-sized object.

In my opinion, anyone on the committee who was in favour of malloc(0) returning NULL showed such a lack of understanding of the C language and the importance of correctly handling zero that they should have immediately been asked to leave.  At the time I scanned a fair bit of source code I had already written and it revealed that more than half the calls (about 100 from memory) to malloc() could have been passed a value of zero under boundary conditions.

In other words there were calls to malloc() like this:


  data_t * p = malloc(nelts * sizeof(data_t));

where the algorithm relied on nelts possibly having the value zero and the return pointer p not being NULL in this case.

The main problem with the proposal is that malloc() can also return NULL when memory is exhausted.  Lots of existing code would have to be changed to distinguish the two cases where NULL could be returned.  So code like this:


  if ((ptr == malloc(n)) == NULL)
      // handle error

would have to be changed to:


  if ((ptr == malloc(n)) == NULL && n > 0)
      // handle error

However, a further problem with the proposal was that it would be then impossible to distinguish between different zero-sized objects.  Of course, a simple workaround would be:


  #define good_malloc(s) malloc((s) == 0 ? 1 : (s))

Of course, there is no point in creating a macro to fix a broken malloc() when malloc() should behave correctly.

There is an even more subtle problem.  Can you see it?

Luckily, sense (partially) prevailed in that malloc(0) is not required to return NULL.  There was some sort of compromise and the behaviour is "implementation-defined".  Fortunately, I have never heard of a C compiler stupid enough to return NULL from malloc(0).

Software Design Complexity


What is the main problem facing software development?

Ask a progammer, a software project manager, a systems analyst, a system architect, an academic or almost anyone who knows anything about software development and you will get various answers along the lines of:
  •  insufficient analysis and poorly understood requirements
  •  poorly documented requirements
  •  poor communication, especially of requirements
  •  poor estimates
  •  unrealistic deadlines (often due to poor estimates)
  •  inadequate testing (often due to unrealistic deadlines)
  •  changing requirements
  •  unmaintainable software (often due to frequently changing requirements)
(Please post any more below, if you think I have missed something important.)

If you look at all these answers you can see that the root cause is simply due to complexity.  The complexity of the original problem makes it hard to understand and communicate to others, which has the knock-on effect of poor estimates, etc.  Coping with change just magnifies the complexity.

The significant problem of unmaintainable software is I believe also due to complexity.  This can, and often is, due to large amount of duplicate and even unnecessary code.  However, the complexity may just be due to the design of the software, or simply that the design is not well understood - after all complexity is just a matter of understanding.

Coping with Complexity

So how do we cope with complexity?  In brief, and in general, we use a fundamental principle that has been used for thousands of years - divide and conquer.  An example of its importance is the division of labour, without which the rise of human civilisation would not have even been possible.

How can we use the divide and conquer principle in software development?  You have probably already guessed that the answer is we already do so, in a myriad of ways.  Thousands, if not millions, of people have been involved in creating all the things that go towards someone creating a piece of software - from the people who created the operatings system, compiler, run-time libraries etc, through to the hardware etc, all the way to your mum, who made the breakfast that keeps your neurons firing.

The principle of divide and conquer is also at the core of the actual design of software.  In fact just about everything you have or will ever read about creating software is concerned with the best way to break down a problem into manageable sub-problems.  This is also known as information hiding, modules, black box, separation of concerns, encapsulation, decoupling, cohesion, modularity, abstraction.  Its also the primary concern of modular programming, orthogonality, abstract data types, component-based software engineering, and object-oriented programming.  In a way it is even the basis of the most important paradigm in the history of programming, that of structured programming.

Of course, there are other ways to deal with complexity, but divide and conquer is by far the most important.  Before I look at some others though, I need to back-track a bit to consider the nature of complexity.

Understanding

The human brain is very powerful but it is limited in that it can only process a small amount of information at a time.  Luckily, due to its ability to learn and memorize, it can accomplish many tasks, just not all at the same time.  (Notwithstanding individual differences, which may have a slight sexual bias, I think the male and female brain are essentially the same in this regard.)

My definition of the complexity of a problem is how hard it is to understand.  Given a complex problem we can often divide it into sub-problems, so we don't need to understand the whole problem at once.  That is, given simple enough sub-problems the brain need only understand each individually, as well as how they are combined to form the main problem.

So even if a problem is too complex for an individual to solve, if it can be broken down into simple sub-problems each of which can be solved one at a time, then the total solution can eventually be solved.  In fact, different sub-problems may even be solved by different individuals.  Again, this is the principle of divide and conquer.

I mentioned above that there are other ways to deal with complexity.  Though I am not an expert on this I can think of a few:

* simplification, or removing information superfluous to the problem
* analogy, or finding a similar problem that is easier to conceptualize
* symbols, since the brain is highly visually-oriented, things like diagrams and notations may assist understanding
* commonality, or recognizing sub-problems that have the same solution

Conclusion

In brief, by far the main problem faced in creating software is complexity.  The main way we deal with complexity is using the principle of divide and conquer.  The main approach to creating software is (using divide and conquer) to create small well-defined components that perform one simple task and that expose a simple well-understood interface.

This is much easier said than done, since the best way to split a problem into sub-problems is not always obvious.  To some extent you have to obtain an understanding of the overall problem to be able to recognize the sub-problems and how to minimize the coupling between the different parts.  Moreover, some problems seem to be non-decomposable, though I believe that there are few if any problems that are inherently so.  Understanding of techniques and previous experience with analyzing similar problems is important for anyone designing software.

The study of the best way to create small, well-defined components (ie the best way to do the dividing) is the principle concern of software designers, from system architects down to lowly programmers.  It is the raison d'etre for almost all the various programming techniques, concepts, processes, paradigms, etc that are continually being invented.

Adaptive Fuzzy System

Adaptive Fuzzy Systems
Background
As of now the "smartness" of our fuzzy machines are dependent on the rules given. The greater the number of rules, the "smarter" the machine gets. However, this means that the performance of the fuzzy machines is restricted by the capabilities of the human brain. Therefore, how do we make the machines think for themselves and come up with rules of its own?
Consider the way the human beings learn. We all learn through experience and through experience we become smarter. Whether, it is the smell of lime, or the picture of our mother, we remember things as it is given to us. With memory, we improve on our actions or thoughts and by defintion become smarter. Fuzzy logic can be applied the same way. Instead, of depending on humans to put specifc fuzzy rules to deal with every situation, the machine should be able to produce its own rules through experience. This can be done with the Data In Rules Out(DIRO) method.
Data In Rule Out
The DIRO method is simple, put data in through a black box and rules come out as shown in the figure below.

Data In Rules Out
Nueral networks, which fill the black box is beyond the scope of this article. However, it is important to note that it is neural networks, which acts like the eyes and ears of an adaptive fuzzy system whose rule changes with experience. The adaptive fuzzy system tunes its rules as it samples new data. At first the rules change fast. This lets the fuzzy system find a working set of fuzzy rules. Then with more samples, the rules change and fine tunes itself and as the saying goes practice makes perfect.
Simple Case Study: An Adaptive Fuzzy Air Conditioner
As an example, consider an air-conditioner controller, which controls the speed of its motor according the the temperature of the surrounding. Fuzzy subsets and member functions are written for this application in the similar way as before. The two subsets used are the temperature of the surrounding as inputs and speed of the motor as outputs. As usual the two sets overlap to produce fuzzy patches. Because at first, five rules are defined, there are five patches. Each patch will cover data which are represented here as points. Using an expert, we can place logical data points on each patch of the system. The expert adjusts the speed of the motor according to what temperature of the room she or he feels appropriate.

The nueral nets used in this example are called the adaptive vector quantizers (AVQ) which are suppose to come up with rules by itself. Each web of nueron in the system is defined as AVQ points. As data comes in, the AVQ point tries to move closer to it. The nuerons then "compete" with each other and "wins" if its AVQ point is closest to the data. How the AVQ points move about is again beyond the scope of this report.
The whole system can be seen in the figure below. The big black dots represent the AVQ point and the small dots represent the data points.


Adaptive Fuzzy System
The number of rules taken now, simply depends on the number of AVQ points in the rule cells. If a cell has an AVQ point, the that rule is added add to our list of rules. Some cells which have more than one rule in it, are used more often making them more inportant. Note: It is more feasible usually to have not more than two rules per cell as this cuts down on bad rules. With more data the expert is no longer needed as the system starts generating its own rules. With practise the system may enventually win over the expert.
Conclusions
This method is just one of the many methods used to generate rules in an adaptive sytem. Thougha simple example was used to illustrate the system, it is important to realize how powerful this system is. In Japan, research is currently being made to use adaptive systems to model events in politics, history, medicine and even military planning. Imagine in the future, we may even have a Fuzzy British Prime Minister !!

Fuzzy Rules and Fuzzy Control

 Fuzzy Rules
Human beings make descisions based on rules. Although, we may not be aware of it, all the descisions we make are all based on computer like if-then statements. If the weather is fine, then we may decide to go out. If the forecast says the weather will be bad today, but fine tommorow, then we make a descision not to go today, and postpone it till tommorow. Rules associate ideas and relate one event to another.
Fuzzy machines, which always tend to mimic the behaviour of man, work the same way. However, the descision and the means of choosing that descision are replaced by fuzzy sets and the rules are replaced by fuzzy rules. Fuzzy rules also operate using a series of if-then statements. For instance, if X then A, if y then b, where A and B are all sets of X and Y. Fuzzy rules define fuzzy patches, which is the key idea in fuzzy logic.
A machine is made smarter using a concept designed by Bart Kosko called the Fuzzy Approximation Theorem(FAT). The FAT theorem generally states a finite number of patches can cover a curve as seen in the figure below. If the patches are large, then the rules are sloppy. If the patches are small then the rules are fine.


Fuzzy Patches
In a fuzzy system this simply means that all our rules can be seen as patches and the input and output of the machine can be associated together using these patches. Graphically, if the rule patches shrink, our fuzzy subset triangles gets narrower. Simple enough? Yes, bcause even novices can build control systems that beat the best math models of control theory. Naturally, it is math-free system.
Fuzzy Control
Fuzzy control, which directly uses fuzzy rules is the most important application in fuzzy theory. Using a procedure originated by Ebrahim Mamdani in the late 70s, three steps are taken to create a fuzzy controlled machine:
1)Fuzzification(Using membership functions to graphically describe a situation)
2)Rule evaluation(Application of fuzzy rules)
3)Defuzzification(Obtaining the crisp or actual results)
As a simple example on how fuzzy controls are constructed, consider the following classic situation: the inverted pendulum. Here, the problem is to balance a pole on a mobile platform that can move in only two directions, to the left or to the right. The angle between the platform and the pendulum and the angular velocity of this angle are chosen as the inputs of the system. The speed of the platform hence, is chosen as the corresponding output.
Step 1
First of all, the different levels of output (high speed, low speed etc.) of the platform is defined by specifying the membership functions for the fuzzy_sets. The graph of the function is shown below

Similary, the different angles between the platform and the pendulum and...

the angular velocities of specific angles are also defined

Note: For simplicity, it is assumed that all membership functions are spreaded equally. Hence, this explains why no actual scale is included in the graphs.
Step 2
The next step is to define the fuzzy rules. The fuzzy rules are mearly a series of if-then statements as mentioned above. These statements are usually derived by an expert to achieve optimum results. Some examples of these rules are:
i) If angle is zero and angular velocity is zero then speed is also zero. ii) If angle is zero and angular velocity is low then the speed shall be low.
The full set of rules is summarised in the table below. The dashes are for conditions, which have no rules ascociated with them. This is don eto simplify the situation.


-----
Speed Angle
------------ negative high negative low zero positive low positive high
v negative high ----------------------- negative high ------------------
e negative low------------------ negative lowzero--------
l zeronegative highnegative low zeropositive lowpositive high
o positive low---------zerolow--------------------
c------------------high---------- -------------------
An application of these rules is shown using specific values for angle and angular velocities. The values used for this example are 0.75 and 0.25 for zero and positive-low angles, and 0.4 and 0.6 for zero and negative-low angular velocities. These points sre on the graphs below.

Consider the rule "if angle is zero and angular velocity is zero, the speed is zero". The actual value belongs to the fuzzy set zero to a degree of 0.75 for "angle" and 0.4 for "angular velocity". Since this is an AND operation, the minimum criterion is used , and the fuzzy set zero of the variable "speed" is cut at 0.4 and the patches are shaded up to that area. This is illustrated in the figure below.

Similarly, the minimum criterion is used for the other three rule. The following figures show the result patches yielded by the rule "if angle is zero and angular velocity is negative low, the speed is negative low", "if angle is positive low and angular velocity is zero, then speed is positive low" and "if angle is positive low and angular velocity is negative low, the speed is zero".

The four results overlaps and is reduced to the following figure

Step 3: The result of the fuzzy controller as of know is a fuzzy set (of speed). In order to choose an appropriate representative value as the final output(crisp values), defuzzification must be done. There are numerous defuzzification methods, but the most common one used is the center of gravity of the set as shown below.

Introduction to Fuzzy Set

Introduction

Fuzzy logic has rapidly become one of the most successful of today's technologies for developing sophisticated control systems. The reason for which is very simple. Fuzzy logic addresses such applications perfectly as it resembles human decision making with an ability to generate precise solutions from certain or approximate information. It fills an important gap in engineering design methods left vacant by purely mathematical approaches (e.g. linear control design), and purely logic-based approaches (e.g. expert systems) in system design.

While other approaches require accurate equations to model real-world behaviors, fuzzy design can accommodate the ambiguities of real-world human language and logic. It provides both an intuitive method for describing systems in human terms and automates the conversion of those system specifications into effective models.

What does it offer?
The first applications of fuzzy theory were primaly industrial, such as process control for cement kilns. However, as the technology was further embraced, fuzzy logic was used in more useful applications. In 1987, the first fuzzy logic-controlled subway was opened in Sendai in northern Japan. Here, fuzzy-logic controllers make subway journeys more comfortable with smooth braking and acceleration. Best of all, all the driver has to do is push the start button! Fuzzy logic was also put to work in elevators to reduce waiting time. Since then, the applications of Fuzzy Logic technology have virtually exploded, affecting things we use everyday.
Take for example, the fuzzy washing machine . A load of clothes in it and press start, and the machine begins to churn, automatically choosing the best cycle. The fuzzy microwave, Place chili, potatoes, or etc in a fuzzy microwave and push single button, and it cooks for the right time at the proper temperature. The fuzzy car, manuvers itself by following simple verbal instructions from its driver. It can even stop itself when there is an obstacle immedeately ahead using sensors. But, practically the most exciting thing about it, is the simplicity involved in operating it.


What do ya mean fuzzy ??!!
Before illustrating the mechanisms which make fuzzy logic machines work, it is important to realize what fuzzy logic actually is. Fuzzy logic is a superset of conventional(Boolean) logic that has been extended to handle the concept of partial truth- truth values between "completely true" and "completely false". As its name suggests, it is the logic underlying modes of reasoning which are approximate rather than exact. The importance of fuzzy logic derives from the fact that most modes of human reasoning and especially common sense reasoning are approximate in nature.
The essential characteristics of fuzzy logic as founded by Zader Lotfi are as follows.


  • In fuzzy logic, exact reasoning is viewed as a limiting case of approximate reasoning.
  • In fuzzy logic everything is a matter of degree.
  • Any logical system can be fuzzified
  • In fuzzy logic, knowledge is interpreted as a collection of elastic or, equivalently , fuzzy constraint on a collection of variables
  • Inference is viewed as a process of propagation of elastic constraints.
The third statement hence, define Boolean logic as a subset of Fuzzy logic.
Fuzzy Sets

Fuzzy Set Theory was formalised by Professor Lofti Zadeh at the University of California in 1965. What Zadeh proposed is very much a paradigm shift that first gained acceptance in the Far East and its successful application has ensured its adoption around the world.
A paradigm is a set of rules and regulations which defines boundaries and tells us what to do to be successful in solving problems within these boundaries. For example the use of transistors instead of vacuum tubes is a paradigm shift - likewise the development of Fuzzy Set Theory from conventional bivalent set theory is a paradigm shift.
Bivalent Set Theory can be somewhat limiting if we wish to describe a 'humanistic' problem mathematically. For example, Fig 1 below illustrates bivalent sets to characterise the temperature of a room.


The most obvious limiting feature of bivalent sets that can be seen clearly from the diagram is that they are mutually exclusive - it is not possible to have membership of more than one set ( opinion would widely vary as to whether 50 degrees Fahrenheit is 'cold' or 'cool' hence the expert knowledge we need to define our system is mathematically at odds with the humanistic world). Clearly, it is not accurate to define a transiton from a quantity such as 'warm' to 'hot' by the application of one degree Fahrenheit of heat. In the real world a smooth (unnoticeable) drift from warm to hot would occur.

This natural phenomenon can be described more accurately by Fuzzy Set Theory. Fig.2 below shows how fuzzy sets quantifying the same information can describe this natural drift.
The whole concept can be illustrated with this example. Let's talk about people and "youthness". In this case the set S (the universe of discourse) is the set of people. A fuzzy subset YOUNG is also defined, which answers the question "to what degree is person x young?" To each person in the universe of discourse, we have to assign a degree of membership in the fuzzy subset YOUNG. The easiest way to do this is with a membership function based on the person's age.
young(x) = { 1, if age(x) <= 20,

(30-age(x))/10, if 20 < age(x) <= 30,

0, if age(x) > 30 }

A graph of this looks like:


Given this definition, here are some example values:
Person    Age    degree of youth
--------------------------------------
Johan     10        1.00 
Edwin     21        0.90
Parthiban 25        0.50
Arosha    26        0.40
Chin Wei  28        0.20
Rajkumar  83        0.00 

So given this definition, we'd say that the degree of truth of the statement "Parthiban is YOUNG" is 0.50.
Note: Membership functions almost never have as simple a shape as age(x). They will at least tend to be triangles pointing up, and they can be much more complex than that. Furthermore, membership functions so far is discussed as if they always are based on a single criterion, but this isn't always the case, although it is the most common case. One could, for example, want to have the membership function for YOUNG depend on both a person's age and their height (Arosha's short for his age). This is perfectly legitimate, and occasionally used in practice. It's referred to as a two-dimensional membership function. It's also possible to have even more criteria, or to have the membership function depend on elements from two completely different universes of discourse.

Fuzzy Set Operations.

Union

The membership function of the Union of two fuzzy sets A and B with membership functions and respectively is defined as the maximum of the two individual membership functions. This is called the maximum criterion.

The Union operation in Fuzzy set theory is the equivalent of the OR operation in Boolean algebra.

Intersection

The membership function of the Intersection of two fuzzy sets A and B with membership functions and respectively is defined as the minimum of the two individual membership functions. This is called the minimum criterion.
The Intersection operation in Fuzzy set theory is the equivalent of the AND operation in Boolean algebra.

Complement

The membership function of the Complement of a Fuzzy set A with membership function is defined as the negation of the specified membership function. This is caleed the negation criterion.
The Complement operation in Fuzzy set theory is the equivalent of the NOT operation in Boolean algebra.
The following rules which are common in classical set theory also apply to Fuzzy set theory.

De Morgans law

,

Associativity

Commutativity

Distributivity

Glossary
Universe of Discourse
The Universe of Discourse is the range of all possible values for an input to a fuzzy system.
Fuzzy Set
A Fuzzy Set is any set that allows its members to have different grades of membership (membership function) in the interval [0,1].
Support
The Support of a fuzzy set F is the crisp set of all points in the Universe of Discourse U such that the membership function of F is non-zero.
Crossover point
The Crossover point of a fuzzy set is the element in U at which its membership function is 0.5.
Fuzzy Singleton
A Fuzzy singleton is a fuzzy set whose support is a single point in U with a membership function of one.