/*
C:\Users\ebata\yamaguchi\src_light
■このサンプルプログラムのキモ
(1)座標を入力して一番近いpostGISのノードIDを検知して、ダイクストラ計算を行うこと
(2)ユーザ心理を計算するファジィ推論を行うこと
が入っているプログラムである。
■自動計算を実施するバッチ、dummy.batの内容は以下の通り
go run light-sim.go data/mod_20220522holyday_visitor.csv data/new_20220522holyday_visitor.csv
go run light-sim.go data/mod_20220521holyday_visitor.csv data/new_20220521holyday_visitor.csv
go run light-sim.go data/mod_20220522holyday.csv data/new_20220522holyday.csv
go run light-sim.go data/mod_20220518weekday.csv data/new_20220518weekday.csv
■その入力用のcsvファイルの一つである、"data/mod_20220522holyday_visitor.csv"の内容は以下の通り
34.172891,131.456346,34.182418,131.474987,21
34.163801,131.444142,34.164454,131.449142,62
34.158856,131.435881,34.164727,131.431189,52
(中略)
34.146351,131.461154,34.167045,131.448468,20
34.145639,131.449237,34.149603,131.432828,29
■"data/bike_stations.csv"の中身
index,station,address,lat,lon,initial_stock,max_stock
1,null,null,34.102543,131.392639,20,20
2,null,null,34.102543,131.392639,20,20
3,null,null,34.102543,131.392639,20,20
4,山口県庁前バス停,山口市春日町2086-4,34.183621,131.471688,20,20
5,null,null,34.102543,131.392639,20,20
6,山口市役所 駐輪場,山口市亀山町2-1,34.178056,131.474204,20,20
7,一の坂川交通交流広場,山口市中河原7-1,34.17960577,131.4783006,20,20
8,null,null,34.102543,131.392639,20,20
9,コープやまぐちこことどうもん店 駐輪場,山口市道場門前1-1-18,34.174234,131.474885,20,20
10,山口駅 駐輪場,山口市惣太夫町288-9,34.172219,131.480013,20,20
11,山口市教育委員会 駐輪場,山口市中央5-14-22,34.170346,131.46915,20,20
12,null,null,34.102543,131.392639,20,20
13,ファミリーマート山口泉都町店,山口市泉都町9-2,34.167346,131.462048,20,20
14,防長苑,山口市熊野町4-29,34.167204,131.45973,20,20
15,null,null,34.102543,131.392639,20,20
16,ホテルニュータナカ,山口市湯田温泉2-6-24,34.163937,131.456115,20,20
17,null,null,34.102543,131.392639,20,20
18,湯田温泉駅 駐輪場,山口市今井町146-6,34.159954,131.459967,20,20
19,アルク平川店,山口市平井724-1,34.152742,131.464199,20,20
20,山口大学(正門),山口市吉田1677-1,34.149852,131.466214,20,20
21,小郡総合支所 駐輪場,山口市小郡下郷609番地1,34.102543,131.392639,20,20
22,KDDI維新ホール 駐輪場,山口市小郡令和1丁目1番地,34.09368,131.394,20,20
23,風の並木通り(新山口駅南口側),山口市小郡金町1-1付近,34.092322,131.397667,20,20
24,平成公園 駐車場内,山口市小郡平成町3-1,34.088168,131.401698,20,20
25,null,null,34.102543,131.392639,20,20
26,アルク小郡店,山口市小郡下郷2273番地1,34.097135,131.391295,20,20
27,null,null,34.102543,131.392639,20,20
*/
package main
import (
"database/sql"
"encoding/csv"
"fmt"
"log"
"os"
"strconv"
_ "github.com/lib/pq"
)
// 自転車の型
type BikeParam struct {
id int // 自転車の識別番号
destinationId int // 出発座標番号(*1)
arrivalId int // 到着座標番号
// (*1)
// 名前がdestination(目的地)となっているのは、バスがユーザを迎えに行き、載せた時点が「出発」扱いであった名残。
// 自転車の場合は迎えに行く動作が無いので、名称変更が望ましい。
}
// 座標情報
type LocInfo struct {
Lng float64 // 経度
Lat float64 // 緯度
Source int // 地図DBのID
}
// 自転車の拠点
type BikeStation struct {
location LocInfo // 拠点の位置
initialStock int // 最初に配置する自転車の台数
stationName string // 拠点の名称
}
// ステーションからの自転車の出入り
type StationStock struct {
outgoing int
incoming int
}
var stationstock [40]StationStock
const STATIONS_PATH string = "data/bike_stations.csv"
// 拠点情報を読み込み、BikeStation型の配列を返す
func getStationInfo(dbMap *sql.DB) []BikeStation {
// ファイルをオープン
csvFile, err := os.Open(STATIONS_PATH)
if err != nil {
log.Fatal(err)
}
defer csvFile.Close()
// CSVファイルの中身を読み込み
r := csv.NewReader(csvFile)
rows, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}
stationInfo := []BikeStation{} // 出力変数
// 行ごとに
for i, row := range rows {
if i == 0 {
continue // CSVのヘッダー行を無視
}
lat, err := strconv.ParseFloat(row[3], 64)
if err != nil {
log.Fatal(err)
}
lng, err := strconv.ParseFloat(row[4], 64)
if err != nil {
log.Fatal(err)
}
initialStockVal, err := strconv.Atoi(row[5])
if err != nil {
log.Fatal(err)
}
stationNameVal := row[1]
// CSV読み込み時点の座標のログ(地図DBによって補正する前の座標)
//log.Println("csv read result:", lng, lat, initialStock)
// 地図DBを参照することによって、ステーションの位置をノードの位置にする
source, lngModified, latModified := fixPosition(dbMap, lng, lat)
loc := LocInfo{
Lng: lngModified,
Lat: latModified,
Source: source,
}
// 読み込めていることの確認ログ
//log.Println("Station", (i - 1), lngModified, latModified, initialStock, source)
bikeStation := BikeStation{
location: loc,
initialStock: initialStockVal,
stationName: stationNameVal,
}
stationInfo = append(stationInfo, bikeStation)
}
return stationInfo
}
// Scan用の仮変数
var source int
var longitude float64
var latitude float64
var dist float64
// 指定した座標に近いDB上の座標を取得
func fixPosition(db *sql.DB, _x1, _y1 float64) (int, float64, float64) {
upperLimitMeter := 1500.0 // 近傍ノードの上限を1500 mに設定
str := fmt.Sprintf(
// 修正前: ways (道) の中から最近傍を取得
// "SELECT source, x1 AS longitude, y1 AS latitude, ST_Distance('SRID=4326;POINT(%v %v)'::GEOGRAPHY, the_geom) AS dist FROM ways WHERE ST_DWithin(the_geom, ST_GeographyFromText('SRID=4326;POINT(%v %v)'), %.1f) ORDER BY dist LIMIT 1",
// 修正後: ways_vertices_pgr (点座標) の中から最近傍を取得
"SELECT id AS source, lon AS longitude, lat AS latitude, ST_Distance('SRID=4326;POINT(%v %v)'::GEOGRAPHY, the_geom) AS dist FROM ways_vertices_pgr WHERE ST_DWithin(the_geom, ST_GeographyFromText('SRID=4326;POINT(%v %v)'), %.1f) ORDER BY dist LIMIT 1",
_x1, _y1, _x1, _y1, upperLimitMeter,
)
//fmt.Println(str)
rows, err := db.Query(str)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
foundGoodMapNode := false
for rows.Next() {
foundGoodMapNode = true
if err := rows.Scan(&source, &longitude, &latitude, &dist); err != nil {
fmt.Println(err)
}
//fmt.Println(source, longitude, latitude, dist)
}
if !foundGoodMapNode {
log.Println("Warning: in func fixPosition: Good Map Node not found for query point (",
_x1, ",", _y1, ")")
}
return source, longitude, latitude
}
/*
func getShortestDistanceToBikeStation(dbMap *sql.DB, node int) int {
StationId := -1
distance := 1000000.0
for i := 0; i < 2; i++ {
rowsDijkstra, errDijkstra := dbMap.Query(
"SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost FROM ways', $1::bigint , $2::bigint , directed:=false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq",
node,
Station[i].Node)
if errDijkstra != nil {
log.Fatal(errDijkstra)
}
defer rowsDijkstra.Close()
var agg_cost float64
for rowsDijkstra.Next() {
var x1, y1, x2, y2 float64
var seq, source, target int
err := rowsDijkstra.Scan(&seq, &source, &target, &x1, &y1, &x2, &y2, &agg_cost)
if err != nil {
fmt.Println(err)
}
}
if distance > agg_cost {
distance = agg_cost
StationId = i
}
}
return StationId
}
*/
// 江端修正版
func getDijkstraPath(dbMap *sql.DB, locInfoStart, locInfoGoal LocInfo) ([]LocInfo, float64) {
//log.Println("getDijkstraPath", locInfoStart, locInfoGoal)
var path []LocInfo // 経路 (返り値の一つ目)
var totalDistanceKm float64
rowsDijkstra, errDijkstra := dbMap.Query(
"SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost FROM ways', $1::bigint , $2::bigint , directed:=false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq",
locInfoStart.Source,
locInfoGoal.Source)
if errDijkstra != nil {
log.Fatal(errDijkstra)
}
defer rowsDijkstra.Close()
var agg_cost float64
isFirstCheck := true
isSourceCheck := true
for rowsDijkstra.Next() {
var x1, y1, x2, y2 float64
var seq int
var target int
// まずnodeを読む
if err := rowsDijkstra.Scan(&seq, &source, &target, &x1, &y1, &x2, &y2, &agg_cost); err != nil {
fmt.Println(err)
}
// 最初の1回だけ入る
if isFirstCheck {
if source == locInfoStart.Source {
isSourceCheck = true
} else {
isSourceCheck = false
}
isFirstCheck = false
}
var loc LocInfo
if isSourceCheck {
loc.Source = source
loc.Lng = x1
loc.Lat = y1
} else {
loc.Source = target
loc.Lng = x2
loc.Lat = y2
}
loc.Source = target
path = append(path, loc)
}
// ラストノードだけは手入力
path = append(path, locInfoGoal)
totalDistanceKm = agg_cost / 1000.0
return path, totalDistanceKm
}
// 一番近いステーションのIDを取得
func getNearestStation(dbMap *sql.DB, stationInfo []BikeStation, queryLocation LocInfo) (int, float64) {
bestDistanceKm := 1000.0 // 十分に大きい数
var bestStationId int
for i := 0; i < len(stationInfo); i++ {
// ダイクストラ法による経路で決定する距離
// SQLクエリを繰り返し実行するため、処理が遅くなる可能性がある
_, distKm := getDijkstraPath(dbMap, queryLocation, stationInfo[i].location)
// 直線距離の概算値(代替の計算方法)
//distKm, _ := distanceKm(queryLocation.Lng, queryLocation.Lat,
// stationInfo[i].location.Lng, stationInfo[i].location.Lat)
//log.Println("to station", i, "distanceKm=", distKm)
if distKm < bestDistanceKm {
bestDistanceKm = distKm
bestStationId = i
}
}
return bestStationId, bestDistanceKm
}
func main() {
dbMap, err := sql.Open("postgres",
"user=postgres password=password host=192.168.0.23 port=15432 dbname=yama_db sslmode=disable")
log.Println("------------------ map db open ------------------")
if err != nil {
log.Fatal("OpenError: ", err)
}
defer dbMap.Close()
// バイクステーション情報の読み込み
stationInfo := getStationInfo(dbMap)
//fmt.Println(stationInfo)
//file2, err2 := os.Open("data/new_20220518weekday.csv")
file2, err2 := os.Open(os.Args[1])
if err2 != nil {
log.Fatal(err2)
}
defer file2.Close()
r2 := csv.NewReader(file2)
rows2, err2 := r2.ReadAll() // csvを一度に全て読み込む
if err != nil {
log.Fatal(err2)
}
//file3, err3 := os.Create("data/calc2_new_20220518weekday.csv") // 第2パラメータ
file3, err3 := os.Create(os.Args[2]) // 第2パラメータ
if err3 != nil {
panic(err)
}
w := csv.NewWriter(file3)
output := []string{"id", "age", "origin_loc_Lng", "origin_loc_Lat", "dest_loc_Lng", "dest_loc_Lat", "distance_from_origin", "distance_between_stations", "distance_to_dest", "complain"}
if err = w.Write(output); err != nil {
log.Fatal(err)
}
for id, row := range rows2 {
/*
origin_lng := 131.4686813247102
origin_lat := 34.17901518198008
dest_lng := 131.45836175237153
dest_lat := 34.160484344205294
*/
origin_lng, err := strconv.ParseFloat(row[1], 64)
if err != nil {
log.Fatal(err)
}
origin_lat, err := strconv.ParseFloat(row[0], 64)
if err != nil {
log.Fatal(err)
}
dest_lng, err := strconv.ParseFloat(row[3], 64)
if err != nil {
log.Fatal(err)
}
dest_lat, err := strconv.ParseFloat(row[2], 64)
if err != nil {
log.Fatal(err)
}
age, err := strconv.ParseFloat(row[4], 64) // 年齢も実数扱いする
if err != nil {
log.Fatal(err)
}
// 最接近のステーションを選ぶ
var origin_loc, dest_loc LocInfo
origin_loc.Source, origin_loc.Lng, origin_loc.Lat = fixPosition(dbMap, origin_lng, origin_lat)
dest_loc.Source, dest_loc.Lng, dest_loc.Lat = fixPosition(dbMap, dest_lng, dest_lat)
stationId_from_origin, distance_from_origin := getNearestStation(dbMap, stationInfo, origin_loc) // Originから最初のステーション
stationId_to_dest, distance_to_dest := getNearestStation(dbMap, stationInfo, dest_loc) // 最後のステーションからDest
fmt.Println(stationInfo[stationId_from_origin])
stationstock[stationId_from_origin].outgoing++
fmt.Println(stationInfo[stationId_to_dest])
stationstock[stationId_to_dest].incoming++
_, distance_between_stations := getDijkstraPath(dbMap, stationInfo[stationId_from_origin].location, stationInfo[stationId_to_dest].location)
_, shortest_distance_total := getDijkstraPath(dbMap, origin_loc, dest_loc)
fmt.Println("stationId_from_origin, distance_from_origin", stationId_from_origin, distance_from_origin)
fmt.Println("stationId_to_dest, distance_to_dest", stationId_to_dest, distance_to_dest)
fmt.Println("distance_between_stations", distance_between_stations)
fmt.Println("shortest_distance_total", shortest_distance_total)
// 出発 ― _x[km]の歩行 ― 最初のステーション ― _y[km]の自転車走行 ― 最後のステーション ― _[km]の歩行 ― 到着
complain := fuzzy_reasoning(distance_from_origin, distance_between_stations, distance_to_dest, age)
fmt.Println("complain", complain)
output := []string{
fmt.Sprint(id),
fmt.Sprint(age),
fmt.Sprint(origin_loc.Lng),
fmt.Sprint(origin_loc.Lat),
fmt.Sprint(dest_loc.Lng),
fmt.Sprint(dest_loc.Lat),
fmt.Sprint(distance_from_origin),
fmt.Sprint(distance_between_stations),
fmt.Sprint(distance_to_dest),
fmt.Sprint(complain)}
fmt.Println(output)
if err = w.Write(output); err != nil {
log.Fatal(err)
}
}
// test
output = []string{"stationid", "outgoing", "incoming"}
if err = w.Write(output); err != nil {
log.Fatal(err)
}
for i := 0; i < 40; i++ {
output = []string{fmt.Sprint(i + 1), fmt.Sprint(stationstock[i].outgoing), fmt.Sprint(stationstock[i].incoming)}
// i+1しているのは、1からスタートする為
if err = w.Write(output); err != nil {
log.Fatal(err)
}
}
defer w.Flush()
if err := w.Error(); err != nil {
log.Fatal(err)
}
}
func fuzzy_reasoning(distance_from_origin, distance_between_stations, distance_to_dest, age float64) float64 {
////// パラメータの作成
// 絶対的歩行距離
walk := min_2(distance_from_origin, distance_to_dest)
// 総体的自転車移動距離
ratio := distance_between_stations / (distance_from_origin + distance_between_stations + distance_to_dest)
// 絶対的自転車移動距離
bike := distance_between_stations
// Age(前件部)
Age_Less := new_condition_MF3(45, 20, "LESS")
Age_Common := new_condition_MF3(45, 20, "COMMON") // 中央が 42歳
Age_More := new_condition_MF3(45, 20, "MORE")
// 絶対的歩行距離(前件部)
Walk_Less := new_condition_MF3(0.4, 0.1, "LESS")
Walk_Common := new_condition_MF3(0.4, 0.1, "COMMON")
Walk_More := new_condition_MF3(0.4, 0.1, "MORE")
// 相対的自転車移動比率(former)
Bike_Ratio_Less := new_condition_MF3(0.7, 0.1, "LESS")
Bike_Ratio_Common := new_condition_MF3(0.7, 0.1, "COMMON")
Bike_Ratio_More := new_condition_MF3(0.7, 0.1, "MORE")
// 絶対的自転車距離(前件部)
Bike_Less := new_condition_MF3(1.5, 1.0, "LESS")
Bike_Common := new_condition_MF3(1.5, 1.0, "COMMON")
Bike_More := new_condition_MF3(1.5, 1.0, "MORE")
// Complain(後件部)
Complain_LessLess := new_action_MF5(0.5, 0.25, "LESSLESS")
Complain_Less := new_action_MF5(0.5, 0.25, "LESS")
Complain_Common := new_action_MF5(0.5, 0.25, "COMMON")
Complain_More := new_action_MF5(0.5, 0.25, "MORE")
Complain_MoreMore := new_action_MF5(0.5, 0.25, "MOREMORE")
// [Rule A00]
Rule_A00 := min_2(Age_Less.func_X(age), Walk_Less.func_X(walk))
Complain_LessLess.func_Max(Rule_A00)
//fmt.Println("Rule_A00", Rule_A00)
// [Rule A01]
Rule_A01 := min_2(Age_Less.func_X(age), Walk_Common.func_X(walk))
Complain_Less.func_Max(Rule_A01)
//fmt.Println("Rule_A01", Rule_A01)
// [Rule A02]
Rule_A02 := min_2(Age_Less.func_X(age), Walk_More.func_X(walk))
Complain_Common.func_Max(Rule_A02)
//fmt.Println("Rule_A02", Rule_A02)
// [Rule A10]
Rule_A10 := min_2(Age_Common.func_X(age), Walk_Less.func_X(walk))
Complain_Common.func_Max(Rule_A10)
//fmt.Println("Rule_A10", Rule_A10)
// [Rule A11]
Rule_A11 := min_2(Age_Common.func_X(age), Walk_Common.func_X(walk))
Complain_Common.func_Max(Rule_A11)
//fmt.Println("Rule_A11", Rule_A11)
// [Rule A12]
Rule_A12 := min_2(Age_Common.func_X(age), Walk_More.func_X(walk))
Complain_More.func_Max(Rule_A12)
//fmt.Println("Rule_A12", Rule_A12)
// [Rule A20]
Rule_A20 := min_2(Age_More.func_X(age), Walk_Less.func_X(walk))
Complain_Common.func_Max(Rule_A20)
//fmt.Println("Rule_A20", Rule_A20)
// [Rule A21]
Rule_A21 := min_2(Age_More.func_X(age), Walk_Common.func_X(walk))
Complain_More.func_Max(Rule_A21)
//fmt.Println("Rule_A21", Rule_A21)
// [Rule A22]
Rule_A22 := min_2(Age_More.func_X(age), Walk_More.func_X(walk))
Complain_MoreMore.func_Max(Rule_A22)
//fmt.Println("Rule_A22", Rule_A22)
// [Rule B00]
Rule_B00 := Bike_Ratio_Less.func_X(ratio)
Complain_MoreMore.func_Max(Rule_B00)
//fmt.Println("Rule_B00", Rule_B00)
// [Rule B01]
Rule_B01 := Bike_Ratio_Common.func_X(ratio)
Complain_Common.func_Max(Rule_B01)
//fmt.Println("Rule_B01", Rule_B01)
// [Rule B02]
Rule_B02 := Bike_Ratio_More.func_X(ratio)
Complain_LessLess.func_Max(Rule_B02)
//fmt.Println("Rule_B02", Rule_B02)
// [Rule C00]
Rule_C00 := min_2(Age_Less.func_X(age), Bike_Less.func_X(bike))
Complain_LessLess.func_Max(Rule_C00)
//fmt.Println("Rule_C00", Rule_C00)
// [Rule C01]
Rule_C01 := min_2(Age_Less.func_X(age), Bike_Common.func_X(bike))
Complain_LessLess.func_Max(Rule_C01)
//fmt.Println("Rule_C01", Rule_C01)
// [Rule C02]
Rule_C02 := min_2(Age_Less.func_X(age), Bike_More.func_X(bike))
Complain_LessLess.func_Max(Rule_C02)
//fmt.Println("Rule_C02", Rule_C02)
// [Rule C10]
Rule_C10 := min_2(Age_Common.func_X(age), Bike_Less.func_X(bike))
Complain_Less.func_Max(Rule_C10)
//fmt.Println("Rule_C10", Rule_C10)
// [Rule C11]
Rule_C11 := min_2(Age_Common.func_X(age), Bike_Common.func_X(bike))
Complain_Common.func_Max(Rule_C11)
//fmt.Println("Rule_C11", Rule_C11)
// [Rule C12]
Rule_C12 := min_2(Age_Common.func_X(age), Bike_More.func_X(bike))
Complain_More.func_Max(Rule_C12)
//fmt.Println("Rule_C12", Rule_C12)
// [Rule C20]
Rule_C20 := min_2(Age_More.func_X(age), Bike_Less.func_X(bike))
Complain_Common.func_Max(Rule_C20)
//fmt.Println("Rule_C20", Rule_C20)
// [Rule C21]
Rule_C21 := min_2(Age_More.func_X(age), Bike_Common.func_X(bike))
Complain_More.func_Max(Rule_C21)
//fmt.Println("Rule_C21", Rule_C21)
// [Rule C22]
Rule_C22 := min_2(Age_More.func_X(age), Bike_More.func_X(bike))
Complain_MoreMore.func_Max(Rule_C22)
//fmt.Println("Rule_C22", Rule_C22)
// Reasoning calculations
numerator :=
Complain_LessLess.func_X()*Complain_LessLess.func_Y() +
Complain_Less.func_X()*Complain_Less.func_Y() +
Complain_Common.func_X()*Complain_Common.func_Y() +
Complain_More.func_X()*Complain_More.func_Y() +
Complain_MoreMore.func_X()*Complain_MoreMore.func_Y()
denominator :=
Complain_LessLess.func_Y() +
Complain_Less.func_Y() +
Complain_Common.func_Y() +
Complain_More.func_Y() +
Complain_MoreMore.func_Y()
reasoning := numerator / denominator
return reasoning
}
func max_2(a, b float64) float64 {
if a > b {
return a
} else {
return b
}
}
func min_2(a, b float64) float64 {
if a > b {
return b
} else {
return a
}
}
type condition_MF3 struct { // Base class for condition_MF3
center float64
width float64
express string
}
func new_condition_MF3(_center, _width float64, _express string) *condition_MF3 {
c3 := new(condition_MF3)
c3.center = _center
c3.width = _width
c3.express = _express
return c3
}
// Class for the membership function (3 mountains) of the former case
func (c3 *condition_MF3) func_X(_x float64) float64 {
// x,y denote coordinates on the membership function
x := _x
y := 0.0 // The value of y is always greater than or equal to 0 and less than or equal to 1
if c3.express == "LESS" {
if x <= c3.center-c3.width {
y = 1.0
} else if x <= c3.center {
y = -1.0 / c3.width * (x - c3.center)
} else {
y = 0.0
}
} else if c3.express == "COMMON" {
if x <= c3.center-c3.width {
y = 0.0
} else if x <= c3.center {
y = 1.0/c3.width*(x-c3.center) + 1.0
} else if x <= c3.center+c3.width {
y = -1.0/c3.width*(x-c3.center) + 1.0
} else {
y = 0.0
}
} else if c3.express == "MORE" {
if x <= c3.center {
y = 0.0
} else if x <= c3.center+c3.width {
y = 1.0 / c3.width * (x - c3.center)
} else {
y = 1.0
}
} else {
fmt.Println("MF3: wrong expression")
os.Exit(1)
}
return y
}
type condition_MF5 struct { // Base class for condition_MF5
center float64
width float64
express string
}
func new_condition_MF5(_center, _width float64, _express string) *condition_MF5 {
c5 := new(condition_MF5)
c5.center = _center
c5.width = _width
c5.express = _express
return c5
}
func (c5 *condition_MF5) func_X(_x float64) float64 {
// Class for the former membership function (5 mountains)
// x,y are the coordinates on the membership function
x := _x
y := 0.0 // The value of y is always greater than or equal to 0 and less than or equal to 1
if c5.express == "LESSLESS" {
if x <= c5.center-2.0*c5.width {
y = 1.0
} else if x <= c5.center-c5.width {
y = -1.0/c5.width*(x-(c5.center-2.0*c5.width)) + 1.0
} else {
y = 0.0
}
} else if c5.express == "LESS" {
if x <= c5.center-2.0*c5.width {
y = 0.0
} else if x <= c5.center-c5.width {
y = 1.0/c5.width*(x-(c5.center-c5.width)) + 1.0
} else if x <= c5.center {
y = -1.0/c5.width*(x-(c5.center-c5.width)) + 1.0
} else {
y = 0.0
}
} else if c5.express == "COMMON" {
if x <= c5.center-c5.width {
y = 0.0
} else if x <= c5.center {
y = 1.0/c5.width*(x-c5.center) + 1.0
} else if x <= c5.center+c5.width {
y = -1.0/c5.width*(x-c5.center) + 1.0
} else {
y = 0.0
}
} else if c5.express == "MORE" {
if x <= c5.center {
y = 0.0
} else if x <= c5.center+c5.width {
y = 1.0/c5.width*(x-(c5.center+c5.width)) + 1.0
} else if x <= c5.center+2.0*c5.width {
y = -1.0/c5.width*(x-(c5.center+c5.width)) + 1.0
} else {
y = 0.0
}
} else if c5.express == "MOREMORE" {
if x <= c5.center+c5.width {
y = 0.0
} else if x <= c5.center+2.0*c5.width {
y = 1.0/c5.width*(x-(c5.center+2.0*c5.width)) + 1.0
} else {
y = 1.0
}
} else {
fmt.Println("MF5 func_X(): wrong expression")
os.Exit(1)
}
return y
}
/////////////////////////////
type action_MF5 struct { // Base class for action_MF5
center float64
width float64
express string
x float64
y float64
}
type action_MF3 struct { // Base class for action_MF3
center float64
width float64
express string
x float64
y float64
}
func new_action_MF5(_center, _width float64, _express string) *action_MF5 {
a5 := new(action_MF5)
a5.center = _center
a5.width = _width
a5.express = _express
if a5.express == "LESSLESS" {
a5.x = a5.center - 2.0*a5.width
} else if a5.express == "LESS" {
a5.x = a5.center - a5.width
} else if a5.express == "COMMON" {
a5.x = a5.center
} else if a5.express == "MORE" {
a5.x = a5.center + a5.width
} else if a5.express == "MOREMORE" {
a5.x = a5.center + 2.0*a5.width
} else {
fmt.Println("new_action_MF5: wrong scale expression")
os.Exit(-1)
}
a5.y = 0.0
return a5
}
func new_action_MF3(_center, _width float64, _express string) *action_MF3 {
a3 := new(action_MF3)
a3.center = _center
a3.width = _width
a3.express = _express
if a3.express == "LESS" {
a3.x = a3.center - a3.width
} else if a3.express == "COMMON" {
a3.x = a3.center
} else if a3.express == "MORE" {
a3.x = a3.center + a3.width
} else {
fmt.Println("new_action_MF3: wrong scale expression")
os.Exit(-1)
}
a3.y = 0.0
return a3
}
// The latter membership function (5 mountains) class
func (a5 *action_MF5) func_Y() float64 {
return a5.y
}
// The latter membership function (3 mountains) class
func (a3 *action_MF3) func_Y() float64 {
return a3.y
}
func (a5 *action_MF5) func_Max(b float64) {
a5.y = max_2(b, a5.y)
}
func (a3 *action_MF3) func_Max(b float64) {
a3.y = max_2(b, a3.y)
}
func (a5 *action_MF5) func_X() float64 {
return a5.x
}
func (a3 *action_MF3) func_X() float64 {
return a3.x
}