A
download fuzzy.py
Language: Python
Copyright: (C) 2000 Terry Hancock
LOC: 123
Project Info
Light Princess, The(light-princess)
Server: SourceForge
Type: cvs
...ss\light‑princess\EBAgents\
   emotion.py
   emotion_test.py
   fuzzy.py
   quality.py
   quality_test.py
   rndtest.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# Fuzzy Logic and Fuzzy Hedge data types
#
#	This Python module is (C) 2000 Terry Hancock
#	and released under the Design Science License.
#
#	Details can be found in the file "COPYING" which
#	you should have received with this file.  You
#	may also find this license at http://www.dsl.org.
#
#	In particular, please note the following disclaimers:
#
#	NO WARRANTY:
#	-----------
#	THE WORK IS PROVIDED "AS IS," AND COMES WITH ABSOLUTELY NO WARRANTY,
#	EXPRESS OR IMPLIED, TO THE EXTENT PERMITTED BY APPLICABLE LAW,
#	INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY
#	OR FITNESS FOR A PARTICULAR PURPOSE.
#
#
#	DISCLAIMER OF LIABILITY:
#	-----------------------
#	IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
#	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
#	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
#	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
#	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
#	IN ANY WAY OUT OF THE USE OF THIS WORK, EVEN IF ADVISED OF THE
#	POSSIBILITY OF SUCH DAMAGE.
#	
#
####################################################################
#
#	FUZZY CLASS
#
#	Type "fuzzy" is a fuzzy logic value, which may
#       be thought of as an adjective that may be applied
#       to a noun (typically a program object) to some
#       degree. 
#
#	*fuzzy* objects have a value on the real interval
#	[0,1] (inclusive or closed interval).
#
#	Note that we ignore the fact that we are actually
#	using rational (i.e. floating point) numbers.
#
#####################################################################	
#
#	FUZZY LOGIC OPERATIONS
#
#	Unfortunately, programming languages like Python which
#	are based on a crisp logic paradigm are not well-suited
#	to implementing fuzzy logic directly (IF-THEN statements,
#	do not behave at all the same way with fuzzy logic
#	conditions -- they have to spawn multiple threads, which
#	is completely different from the crisp logic design).
#
#	However, we must define basic math operations on fuzzy
#	logic as a datatype.  Python doesn't let us overload the
#	*logical* AND, OR, XOR, and NOT operations, so we have
#	to use the so-called *bitwise* versions: &, |, ^, and ~.
#
#	Furthermore, we need to have fuzzy equality statements.
#	which have a fuzzy result telling *to what degree* the
#	arguments are equal.  However, we can't do this by
#	overloading "==" both because it would break things, and
#	because Python simply doesn't allow it.
#
#	So, we are forced to compromise by overloading "-" to do
#	the job.  While we're at it, we'll also overload "<<" and
#	">>" to have their usual math-notation meaning -- i.e. 
#	very much greater than and very much less than.  Which
#	just amounts to hedging and comparing.
#
#	The "+" is overloaded so that ~(a - b) = (a + b), i.e. it
#       is the complement to the fuzzy equality of "-", however,
#	I don't really recommend using it, since it's potentially
#	confusing.
#
#	The precedence rules do more or less what you'd expect
#	-- they have the same relationships as the boolean 
#	operations "and", "or" etc to the comparison operators,
#	"==", "!=" etc.
#
#	Awkwardly, "<<" and ">>" have lower precedence than
#	"-" so they will be applied afterwards.  However, this
#	should never come up, since it's a pretty way to code --
#	use the logical operators instead.
#	
#	Note that in crisp logic, (a == b) is equivalent to 
#	(not (a xor b)), but that this is not true in fuzzy
#	logic.  The difference is most pronounced for the
#	case that a == b == 0.5, in which case, we do not really
#	know that the two are "not anticorrelated" which is the
#	extended meaning of "not xor," so the return value is
#	0.5.  However, they are definitely equal, so "-" returns
#	1.0.  You can verify this for yourself by looking at
#	the mathematical definitions below.
#
####################################################################
#
#	DEFUZZIFICATION
#
#	When a crisp logic value is required by Python (such
#	as when a boolean comparison operator is used, or 
#	when the fuzzy value is a boolean condition in a program
#	statement, the __nonzero__ or __cmp__ operations are
#	invoked, which force a transformation from fuzzy to
#	crisp logic, according to the current default logical
#	defuzzification method, which is initially defaulted
#	to "quartile-guttered perchance", which means:
#
#	f > 0.75 --> always TRUE
#	f < 0.25 --> always FALSE
#
#	(these are the "gutters")
#
#	0.25 < f < 0.75	--> weighted random choice, increasing
#				from 0% chance at 0.25 to 100%
#				chance at 0.75.  This is meant
#				to mimic real decision-making
#				in the face of uncertain data:
#				we usually treat the data as
#				certain above some level, and
#				then "make a guess" when the
#				data is very uncertain.
#
###################################################################
#
#	FUZZIFICATION
#
#	Two datatypes can be converted to fuzzy sensibly:
#
#	Boolean / Integer
#
#	Since there's no explicit boolean type in Python, integers
#	are used.  Thus, my fuzzy() __init__ function assumes
#	that an integer represents a boolean: 0-->FALSE and
#	1-->TRUE.  These are defaulted to 0.1 and 0.9 fuzzy
#	logic values, allowing slightly higher and lower truth
#	values to exist -- these will be guttered to the ends,
#	though if defuzzification is needed.
#
#	Floating Point
#
#	Measurements should be converted explicitly, by setting
#	bounds and center points, by providing extra arguments
#	to fuzzy(value, lower, upper, center).  Otherwise,
#	defaults are assumed:
#
#	fuzzy(value, lower, upper, center)	--> quadratic map
#	fuzzy(value, lower, upper)		--> linear map
#	fuzzy(value)				--> literal from [0,1]
#
#	(in the last case, negative or >1 values are silently
#	 limited to 0 or 1 respectively).
#
#	If anybody has ideas for other conversions, let me know. :)
#
####################################################################

# Useful constants:

CRISP_TRUE  = 0.9	# By default, crisp logic values are interpreted as not quite absolute
CRISP_FALSE = 0.1
FuzzyType   = "Fuzzy"
HedgeType   = "Hedge"

class	fuzzy :
	#CONSTRUCTOR
	def __init__(self, value=0.5, memb=None ):

		import types

		if	(type(value) == types.InstanceType) and (value.__class__==fuzzy): 
			self.data = value.data					# If fuzzy already, just copy the data.

		elif	type(value) == types.IntType :				# Fuzzify crisp logic to 0.1 or 0.9
			if 	value   :	self.data = CRISP_FALSE
			else		:	self.data = CRISP_TRUE
			
		elif	type(value) == types.FloatType :				
			if memb==None :						# Fuzzify float in [0,1]
				if 0.0 <= value <= 1.0 : self.data = value
				elif value < 0.0 :	 self.data = 0.0	# Out-of-bounds are silently clipped
				else             :	 self.data = 1.0
			
			elif	(type(memb) == types.TupleType) and (len(memb) == 4) :	# Allow trapezoidal membership functions
				for i in range(4) :
					if (type(memb[i]) != types.FloatType) or ((i>0) and (memb[i] < memb[i-1])) : break
				else: 
					if	           value <  memb[0] :	self.data = 0.0
					elif 	memb[0] <  value <  memb[1] :	self.data = (value - memb[0]) / (memb[1] - memb[0])
					elif 	memb[1] <= value <= memb[2] :	self.data = 1.0
					elif	memb[2] <  value <  memb[3] :	self.data = (value - memb[3]) / (memb[2] - memb[3])
					else                                :	self.data = 0.0
					return

				raise TypeError, """Trapezoidal membership function problem: 
				                    \tmust be 4-tuple of floats in ascending order.
						    \tas in (-10.0, 0.8, 23.2, 23.2)."""
		else :
			raise TypeError, "Cannot convert type to fuzzy logic values?"
	
	def display(self):	print self.data
	
	# PRINTING and REPRESENTING
	def __repr__	(self):	return "%.3f" % self.data
	def __str__	(self):	return "%.3f" % self.data	# This might be better replaced by a descriptive hedge representation

	# BOOLEAN COMPARISONS
	def __cmp__	(self, other):				# Simple crisp comparison.
		if   (self.data < other.data) :	return -1
		elif (self.data > other.data) : return  1
		else :                          return  0
	
	#							# Returns whichever fuzzy state is "most true"
	#	v= [    ((self -  fuzzy(other)).data,  0), 	# THIS IS PROBABLY THE WRONG WAY TO DO THIS!
	#		((self << fuzzy(other)).data, -1), 	# Yep, that's why I replaced it.  This is the
	#		((self >> fuzzy(other)).data,  1) ]	# "most true" switch  I want to make a callable
	#	v.sort()					# object that does this
	#	v.reverse()
	#	return v[0][1]
		
	def __nonzero__	(self):		return perchance(self, 0.25)

	# FUZZY COMPARISONS
	def __sub__	(self, other):	return fuzzy( 1.0 - abs(self.data - fuzzy(other).data) )
	def __rsub__	(self, other):	return other - self

	
	def __add__	(self, other):	return ~(self - fuzzy(other))
	def __radd__	(self, other):	return other + self
	
	def __lshift__	(self, other):	
		other_data = fuzzy(other).data
		if other_data == 0.0 :  return 1.0
		else		     :  return fuzzy(max( (other_data - self.data) / other_data, 		0.0 ) )
		
	def __rshift__	(self, other):	
		other_data = fuzzy(other).data
		if other_data == 1.0 :  return 1.0
		else 		     :  return fuzzy(max( (self.data  - other_data) / (1.0 - other_data), 	0.0 ) )

	# FUZZY LOGIC OPERATIONS
	def __and__	(self, other):	return fuzzy(min(self.data, other.data))	# Fuzzy AND: min(A, B)
	def __or__	(self, other):	return fuzzy(max(self.data, other.data))	# Fuzzy OR:  max(A, B)
	def __invert__	(self):		return fuzzy(1.0 - self.data)			# Fuzzy NOT: 1 - A
	def __xor__	(self, other):	return ( (self | other) & ~(self & other) )	# XOR = ((a OR b) AND NOT (a AND b))

	def __rand__	(self, other):	return other & self
	def __ror__	(self, other):	return other | self
	def __rxor__	(self, other):	return other ^ self
	
	def __coerce__	(self, other): # return other.__coerce__(self)	# Leave any coersions to other object
					# This is now a copy of the code in "quality.py"
		import types
		if  	(type(other) == types.InstanceType) and (other.__class__==fuzzy): 
				return ( fuzzy(self.data), other )
				
		elif	(type(other) == types.InstanceType) and (other.__class__==hedge): 
				return ( fuzzy(self.data), other )


	def __rmul__	(self, other):	return other.__mul__(self)	# Force other to handle '*' operation.
										# Only needed for class inheritance problem
	
#	This should be covered by the __rmul__ method of the Hedge type.
#
#	# HEDGE APPLICATION
#	def __mul__	(self, other):
#		if (type(other) is InstanceType) and (other.type = HedgeType) :	return other.applyhedge(self)
#		else                                                          :	raise TypeError, "Illegal fuzzy hedge expression."

def perchance(fval, lowgutter=0.25, highgutter=None) :

	from random import *

	if (highgutter==None): highgutter = lowgutter
	
	if 	fval.data < lowgutter 		: return 0					# Gutters -- always return extremes
	elif	fval.data > 1.0 - highgutter 	: return 1
	else :
		v = (fval.data-lowgutter)/(1.0-highgutter-lowgutter)
		r = random()
		# print "DEBUG perchance:", v, r
		if v > r  : return 1	# Middle  -- weighted random value
		else      : return 0

def crisp(fval) :										# Absolute crisp logic conversion
	if	fval.data < 0.5		: return 0
	else				: return 1


#########################################################################
#
#	HEDGE CLASS
#
#	Type "hedge" is a modifier of type "fuzzy", and may
#       be thought of as an adverb modifying the fuzzy
#       adjective.  Such as "very".
#
#	Quantitatively, the hedge is a fuzzy membership
#	operator which maps a fuzzy-valued argument to a 
#       fuzzy-valued result.  Combining these operators is
#	acheived by functional multiplication.
#
#	In fact, *hedges* in general are functions mapping
#	from the domain [0,1] to the range [0,1].
#
####################################################################
#
#	FUNCTIONAL FORM OF HEDGES and HEDGE EXPRESSIONS
#
#	This implementation starts handling hedges as tuples
#	of functions with arguments, like this:
#
#	( (func1name, (arg1, arg2, ... )), (func2name, (arg1, arg2, ...)), ... )
#
#	COMPOSITION:
#	When two hedges are combined, they are composed by concatenating
#	the tuples into a new one, which will then be a longer tuple. This
#	means that they will tend to grow, but the assumption is that
#	nesting won't really be very deep in practice.  However, nothing
#	except memory constrains the complexity of composite hedges.
#
#	Simple hedges can be provided by user functions, or by using
#	a function defined in this module (I plan to support generalized
#	power functions, linear functions, and gaussians).  They are
#	represented as primitive hedges (actually a tuple of one element).
#
#	For increased generality, the functions can have additional
#	arguments containing function parameters.
#
#	APPLICATION:
#	When a hedge (or hedge expression) is combined with a fuzzy
#	logic value, the result is *application* of the hedge expression
#	(a composite hedge) to the fuzzy logic value, thus returning
#	a new fuzzy logic value.  Both input and output values are
#	clipped to the [0,1] interval through use of the fuzzy()
#	method above.
#
#	To apply the expression, the rightmost function is evaluated,
#	the result is clipped to [0,1], then the next rightmost function
#	is applied to it. That, in turn, is clipped and so on, until
#	all the functions have been used up.
#
#	NOTE:
#	It makes no difference whether an expression is evaluated
#	as:
#
#	(hedge1 * hedge2 * hedge3) * fuzzy1
#
#	or
#
#	hedge1 * (hedge2 * (hedge3 * fuzzy1)))
#
#	because the functional definition of the two is the same.
#	(That is, if you work it out mathematically, it will 
#	always be doing the same calculation, though the order
#	of operations may vary).  Order of calculation issues
#	found in general floating point operations should not
#	be an issue since all values are in [0,1], and should
#	therefore be reasonably well-behaved.  Even divide-by
#	zero problems shouldn't be too bad since the result
#	will be clipped to 1.
#
#	I do not define what will happen for cases in which
#	the usual order is not followed, such as:
#
#	hedge1 * fuzzy1 * hedge2 * hedge3
#
#	In English, Spanish, French, and Japanese, adverbs customarily
#	come _before_ adjectives, even though the relationship between
#	nouns and adjectives varies.  There may be other languages
#	where that's not true, of course.)
#
#	I haven't attempted to check this, but I may look into it
#	later.  For now, it's better to follow the expected order.
#	(i.e. keep hedges to the left of the fuzzy, and note that
#	there can only be ZERO or ONE fuzzy in the expression,
#	attempting to compose (or "multiply") two fuzzies will
#	result in a type error.
#	
#	WHY?
#	I changed over to this method after examining the literature
#	on hedges, and realizing that no one would agree on how they
#	should be represented.  This method is general enough to
#	include all functional representations, and store composite
#	hedges as well (although at some cost in efficiency).  It
#	seemed that generality was the only way to make the code
#	acceptable to potential users.
#
#########################################################################
#
#
#	Useful Hedge functions:
#
#	For convenience sake, here are some commonly used functions in the correct form:

def powerhedge(x, a, x0, alpha):	return a*(x - x0)**alpha;	# Generalized power function

def trapezoid (x, a, b, c, d):						# Trapezoidal membership function
	
	# print "trapezoid", x, a, b, c, d

	if 	(    x <= a): 		return 0.
	elif	(a < x <= b):		return (x-a)/(b-a)
	elif	(b < x <= c):		return 1.
	elif	(c < x <= d):		return (d-x)/(d-c)
	elif	(d < x    ):		return 0.
	else:				raise AssertionError, "Impossible state in Trapezoid"

def gaussian (x, x0, s):	return exp( -( (x - x0) / s )**2 )	# Generalized gaussian

#
#
# Hedge Class itself:
#
#
	
class hedge :
	def __init__(self, value=((powerhedge, (1., 0.,  1.)),) ):
		import types
		if	(type(value) == types.InstanceType) and (value.__class__ == hedge) :		# If already a hedge, copy
				self.data = value.data
			
		elif	(type(value) == types.TupleType) :						# Check for proper form, then
				for (func, args) in  value:						# convert to hedge form, should
					if   (type(func) != types.FunctionType) : break		# be some number of functions
					elif (type(args) != types.TupleType)    : break
				else:	
					self.data = value
					return
				raise TypeError, "Improper hedge definition."			# User messed up the hedge format
				
		else:		raise TypeError, "Cannot convert type to hedge."		# Tried to convert something else
												# into a hedge (such as an integer
												# or fuzzy, which doesn't make sense).
					
	def __mul__	(self, other) :
		import types
		if 	(type(other) == types.InstanceType) and (other.__class__==hedge) :	# Compose two hedges by concatenation
				return hedge( other.data + self.data )
				
		elif	(type(other) == types.InstanceType) and (other.__class__==fuzzy) :	# Apply hedge to fuzzy
				x = other.data
				for (func, args) in self.data:
					if   (x < 0.0) : x = 0.0				# DOMAIN CLIP
					elif (x > 1.0) : x = 1.0
		
					x = apply(func, (x,) + args );				# Apply hedge function
			
					if   (x < 0.0) : x = 0.0				# RANGE CLIP
					elif (x > 1.0) : x = 1.0
				return fuzzy(x)
						
		else:	raise TypeError, "Illegal fuzzy hedge expression."

	def __rmul__	(self, other) :								# Reversed order application
		return	self * other

	def __coerce__	(self, other) :	return None	# Leave coersion to other objects
		
#
#	And a few common hedge definitions:
#
unithedge = hedge();
very      = hedge( ((powerhedge, (1., 0.,  2.)),) );	
extremely = hedge( ((powerhedge, (1., 0.,  6.)),) );
soso      = hedge( ((powerhedge, (1., 0.5, 2.)),) );

About Koders | Resources | Downloads | Support | Black Duck | Submit Project | Terms of Service | DMCA | Privacy Policy | Site Map| Contact Us