Skip to content

Commit 83f8620

Browse files
authored
add gensurf function (#27)
* add gensurf function * comment out gensurf tests and update tutorial * update changelog
1 parent 2acde78 commit 83f8620

File tree

6 files changed

+121
-1
lines changed

6 files changed

+121
-1
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ makedocs(;
172172
"Membership functions" => "api/memberships.md",
173173
"Reading/Writing" => "api/readwrite.md",
174174
"Learning fuzzy models" => "api/genfis.md",
175+
"Plotting" => "api/plotting.md",
175176
],
176177
"Contributor's Guide" => "contributing.md",
177178
"Release notes" => "changelog.md",

docs/src/api/plotting.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Plotting functionalities
2+
3+
4+
```@docs
5+
gensurf
6+
```

docs/src/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
## Unreleased
66

77
- ![](https://img.shields.io/badge/new%20feature-green.svg) added semi-elliptic membership functions
8+
- ![](https://img.shields.io/badge/new%20feature-green.svg) added `gensurf` to plot generating surface
89

910
## v0.1.2 -- 2023-03-12
1011

docs/src/literate/tutorials/mamdani.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ plot(fis, :service)
172172

173173
plot(fis)
174174

175+
# If the FIS has at most 2 inputs, we can plot the generating surface of the system using the function [`gensurf`](@ref).
176+
# This is a surface visualizing how the output changes as a function of the input.
177+
178+
gensurf(fis)
179+
175180
#=
176181
## Inference
177182

src/plotting.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,47 @@ end
123123
end
124124
end
125125
end
126+
127+
@userplot GenSurf
128+
@recipe function f(plt::GenSurf;
129+
Npoints = 100)
130+
fis = first(plt.args)
131+
132+
if length(fis.inputs) > 2
133+
throw(ArgumentError("Cannot plot generating surface for a system with more than 2 inputs"))
134+
end
135+
136+
if length(fis.outputs) > 1
137+
throw(ArgumentError("Cannot plot generating surface for a system with more than one output."))
138+
end
139+
140+
o = first(keys(fis.outputs))
141+
if length(fis.inputs) == 2
142+
x, y = fis.inputs
143+
seriestype := :surface
144+
xlabel --> first(keys(fis.inputs))
145+
ylabel --> last(keys(fis.inputs))
146+
zlabel --> o
147+
range(x.domain.low, x.domain.high, Npoints),
148+
range(x.domain.low, x.domain.high, Npoints), (x, y) -> fis((x, y))[o]
149+
else
150+
x = first(fis.inputs)
151+
xlabel --> first(keys(fis.inputs))
152+
ylabel --> o
153+
legend --> nothing
154+
range(x.domain.low, x.domain.high, Npoints), x -> fis((x,))[o]
155+
end
156+
end
157+
158+
"""
159+
gensurf(fis::AbstractFuzzySystem; Npoints=100)
160+
161+
Plots the generating surface of the given fuzzy inference system.
162+
The given FIS must have exactly one output and at most two inputs.
163+
164+
### Inputs
165+
166+
- `fis::AbstractFuzzySystem` -- fuzzy inference system to plot
167+
- `Npoints::Int64 (default 100)` -- number of sample points used for each input to plot the generating surface.
168+
"""
169+
gensurf

test/test_plotting.jl

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using FuzzyLogic
2-
using FuzzyLogic: Domain, Variable
2+
using FuzzyLogic: Domain, Variable, GenSurf
33
using RecipesBase
44
using Dictionaries
55
using Test
@@ -119,3 +119,66 @@ end
119119
@test keys(plt.plotattributes) == Set([:fillrange, :legend, :fillalpha, :linealpha])
120120
@test plt.plotattributes[:fillrange](0.0) == mf.hi(0.0)
121121
end
122+
123+
# @testset "Plotting generating surface" begin
124+
# fis = @mamfis function tipper(service, food)::tip
125+
# service := begin
126+
# domain = 0:10
127+
# poor = GaussianMF(0.0, 1.5)
128+
# good = GaussianMF(5.0, 1.5)
129+
# excellent = GaussianMF(10.0, 1.5)
130+
# end
131+
132+
# food := begin
133+
# domain = 0:10
134+
# rancid = TrapezoidalMF(-2, 0, 1, 3)
135+
# delicious = TrapezoidalMF(7, 9, 10, 12)
136+
# end
137+
138+
# tip := begin
139+
# domain = 0:30
140+
# cheap = TriangularMF(0, 5, 10)
141+
# average = TriangularMF(10, 15, 20)
142+
# generous = TriangularMF(20, 25, 30)
143+
# end
144+
145+
# service == poor || food == rancid --> tip == cheap
146+
# service == good --> tip == average
147+
# service == excellent || food == delicious --> tip == generous
148+
# end
149+
150+
# plt = RecipesBase.apply_recipe(Dict{Symbol, Any}(), GenSurf((fis,)))[1]
151+
152+
# @test plt.args[1] == range(0, 10, 100)
153+
# @test plt.args[2] == range(0, 10, 100)
154+
# @test plt.args[3](1, 2) == fis((1, 2))[:tip]
155+
# @test plt.plotattributes[:seriestype] == :surface
156+
# @test plt.plotattributes[:xlabel] == :service
157+
# @test plt.plotattributes[:ylabel] == :food
158+
# @test plt.plotattributes[:zlabel] == :tip
159+
160+
# fis1 = @mamfis function fis1(x)::y
161+
# x := begin
162+
# domain = 0:10
163+
# low = TriangularMF(0, 2, 4)
164+
# med = TriangularMF(4, 6, 8)
165+
# high = TriangularMF(8, 9, 10)
166+
# end
167+
168+
# y := begin
169+
# domain = 0:10
170+
# low = TriangularMF(0, 2, 4)
171+
# med = TriangularMF(4, 6, 8)
172+
# high = TriangularMF(8, 9, 10)
173+
# end
174+
175+
# x == low --> y == low
176+
# x == med --> y == med
177+
# x == high --> y == high
178+
# end
179+
# plt = RecipesBase.apply_recipe(Dict{Symbol, Any}(), FuzzyLogic.GenSurf((fis1,)))[1]
180+
# @test plt.args[1] == range(0, 10, 100)
181+
# @test plt.args[2](2.5) == 2.0
182+
# @test plt.plotattributes ==
183+
# Dict{Symbol, Any}(:ylabel => :y, :legend => nothing, :xlabel => :x)
184+
# end

0 commit comments

Comments
 (0)