TIF_NGANJUK_E41211992/lib/logic/fuzzy_mamdani.dart

268 lines
9.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import '../data/disease_data.dart';
import '../models/symptom.dart';
import '../models/disease.dart';
class FuzzyMamdani {
Map<String, double> calculateMembership(double value) {
if (value < 0 || value > 1) {
throw ArgumentError(
'Nilai bobot gejala harus berada dalam rentang 0 1');
}
Map<String, double> memberships = {
'rendah': 0.0,
'sedang': 0.0,
'tinggi': 0.0,
};
if (value <= 0.4) {
memberships['rendah'] = 1.0;
} else if (value <= 0.5) {
memberships['rendah'] = (0.5 - value) / (0.5 - 0.4);
}
if (value >= 0.4 && value <= 0.5) {
memberships['sedang'] = (value - 0.4) / (0.5 - 0.4);
} else if (value > 0.5 && value <= 0.7) {
memberships['sedang'] = 1.0;
} else if (value > 0.7 && value <= 0.8) {
memberships['sedang'] = (0.8 - value) / (0.8 - 0.7);
}
if (value >= 0.7 && value <= 0.8) {
memberships['tinggi'] = (value - 0.7) / (0.8 - 0.7);
} else if (value > 0.8) {
memberships['tinggi'] = 1.0;
}
return memberships;
}
Map<String, Map<String, double>> fuzzify(Map<String, double> userInputs) {
if (userInputs.isEmpty) {
throw ArgumentError('Input gejala tidak boleh kosong');
}
for (var symptomId in userInputs.keys) {
if (!symptoms.any((s) => s.id == symptomId)) {
throw ArgumentError('ID gejala tidak valid: $symptomId');
}
}
Map<String, Map<String, double>> memberships = {};
userInputs.forEach((symptomId, value) {
memberships[symptomId] = calculateMembership(value);
});
return memberships;
}
Map<String, Map<String, dynamic>> forwardChaining(
Map<String, Map<String, double>> memberships) {
Map<String, Map<String, dynamic>> diseaseResults = {};
Map<String, Symptom> symptomMap = {
for (var symptom in symptoms) symptom.id: symptom
};
for (var rule in rules) {
int totalSymptoms = rule.symptomIds.length;
int matchedSymptoms = 0;
List<String> matchedSymptomIds = [];
Map<String, double> totalMembership = {
'rendah': 0.0,
'sedang': 0.0,
'tinggi': 0.0,
};
double totalWeight = 0.0;
for (var symptomId in rule.symptomIds) {
if (memberships.containsKey(symptomId)) {
matchedSymptoms++;
matchedSymptomIds.add(symptomId);
totalMembership['rendah'] =
totalMembership['rendah']! + memberships[symptomId]!['rendah']!;
totalMembership['sedang'] =
totalMembership['sedang']! + memberships[symptomId]!['sedang']!;
totalMembership['tinggi'] =
totalMembership['tinggi']! + memberships[symptomId]!['tinggi']!;
totalWeight += symptomMap[symptomId]!.weight;
}
}
if (matchedSymptoms > 0) {
double matchPercentage = matchedSymptoms / totalSymptoms;
double weightFactor = totalWeight / matchedSymptoms;
Map<String, double> ruleScores = {
'rendah': (totalMembership['rendah']! / matchedSymptoms) *
matchPercentage *
weightFactor,
'sedang': (totalMembership['sedang']! / matchedSymptoms) *
matchPercentage *
weightFactor,
'tinggi': (totalMembership['tinggi']! / matchedSymptoms) *
matchPercentage *
weightFactor,
};
diseaseResults[rule.diseaseId] = {
'scores': ruleScores,
'matchPercentage': matchPercentage,
'matchedSymptoms': matchedSymptomIds,
'totalWeight': totalWeight,
};
}
}
Map<String, String> recommendations = {};
Map<String, double> tempConfidenceScores = defuzzify(diseaseResults);
diseaseResults.forEach((diseaseId, data) {
double confidence = tempConfidenceScores[diseaseId] ?? 0.0;
double totalWeight = data['totalWeight'] as double;
List<String> matchedSymptoms = data['matchedSymptoms'] as List<String>;
if (diseaseId == 'P1') {
if (confidence > 40 && totalWeight > 1.5) {
recommendations[diseaseId] =
'Segera lakukan penyemprotan intensif dengan fungisida difenokonazol setiap 5 hari.';
} else {
recommendations[diseaseId] =
'Gunakan fungisida difenokonazol dengan interval penyemprotan 7 hari.';
}
} else if (diseaseId == 'P2') {
if (confidence > 30 && matchedSymptoms.contains('GP15')) {
recommendations[diseaseId] =
'Lakukan pengendalian dengan insektisida tambahan karena ada indikasi serangan hama.';
} else {
recommendations[diseaseId] =
'Gunakan mulsa plastik untuk mencegah penyebaran.';
}
} else if (diseaseId == 'P3') {
if (confidence > 50 && totalWeight > 2.0) {
recommendations[diseaseId] =
'Segera lakukan drainase lahan dan gunakan fungisida berbahan aktif metalaksil.';
} else {
recommendations[diseaseId] =
'Gunakan fungisida berbahan aktif metalaksil dengan interval 7 hari.';
}
} else if (diseaseId == 'P4') {
if (confidence > 40 && matchedSymptoms.contains('GP9')) {
recommendations[diseaseId] =
'Gunakan fungisida berbahan aktif azoksistrobin dan lakukan sanitasi lahan.';
} else {
recommendations[diseaseId] =
'Gunakan fungisida berbahan aktif azoksistrobin dengan interval 7 hari.';
}
} else if (diseaseId == 'P5') {
if (confidence > 30 && matchedSymptoms.contains('GP16')) {
recommendations[diseaseId] =
'Lakukan pemangkasan daun yang terinfeksi dan gunakan fungisida berbahan aktif mankozeb.';
} else {
recommendations[diseaseId] =
'Gunakan fungisida berbahan aktif mankozeb dengan interval 7 hari.';
}
} else if (diseaseId == 'P6') {
if (confidence > 50 && matchedSymptoms.contains('GP18')) {
recommendations[diseaseId] =
'Lakukan penggantian media tanam dan gunakan fungisida berbahan aktif karbendazim.';
} else {
recommendations[diseaseId] =
'Gunakan fungisida berbahan aktif karbendazim dengan interval 7 hari.';
}
} else if (diseaseId == 'P7') {
if (confidence > 50 && matchedSymptoms.contains('GP20')) {
recommendations[diseaseId] =
'Lakukan pembakaran tanaman yang terinfeksi dan gunakan bakterisida berbahan aktif streptomisin.';
} else {
recommendations[diseaseId] =
'Gunakan bakterisida berbahan aktif streptomisin dengan interval 7 hari.';
}
}
});
List<String> highConfidenceDiseases = tempConfidenceScores.entries
.where((entry) => entry.value > 30)
.map((entry) => entry.key)
.toList();
if (highConfidenceDiseases.length > 1) {
List<String> diseaseNames = highConfidenceDiseases.map((diseaseId) {
final disease = diseases.firstWhere(
(d) => d.id == diseaseId,
orElse: () => Disease(
id: diseaseId,
name: 'Penyakit Tidak Diketahui',
symptomIds: [],
solutions: []),
);
return disease.name;
}).toList();
String generalRecommendation =
'Konsultasikan dengan ahli pertanian karena ada indikasi infeksi ganda (${diseaseNames.join(", ")}).';
highConfidenceDiseases.forEach((diseaseId) {
recommendations[diseaseId] =
(recommendations[diseaseId] ?? '') + '\n$generalRecommendation';
});
}
diseaseResults.forEach((diseaseId, data) {
data['recommendation'] = recommendations[diseaseId] ?? '';
});
return diseaseResults;
}
Map<String, double> defuzzify(
Map<String, Map<String, dynamic>> diseaseResults) {
Map<String, double> results = {};
const double lowValue = 20;
const double mediumValue = 60;
const double highValue = 90;
diseaseResults.forEach((diseaseId, data) {
Map<String, double> scores = data['scores'] as Map<String, double>;
double matchPercentage = data['matchPercentage'] as double;
double numerator = 0.0;
double denominator = 0.0;
numerator += lowValue * scores['rendah']!;
numerator += mediumValue * scores['sedang']!;
numerator += highValue * scores['tinggi']!;
denominator += scores['rendah']! + scores['sedang']! + scores['tinggi']!;
if (denominator == 0) {
results[diseaseId] = 0.0;
} else {
double centroid = numerator / denominator;
double adjustmentFactor = 2.4 + (1.05 * (1 - matchPercentage));
double confidence = (centroid * matchPercentage) * adjustmentFactor;
results[diseaseId] = confidence.clamp(0, 100);
results[diseaseId] =
double.parse(results[diseaseId]!.toStringAsFixed(1));
}
});
return results;
}
Map<String, Map<String, dynamic>> diagnose(Map<String, double> userInputs) {
var memberships = fuzzify(userInputs);
var diseaseResults = forwardChaining(memberships);
var confidenceScores = defuzzify(diseaseResults);
Map<String, Map<String, dynamic>> finalResults = {};
diseaseResults.forEach((diseaseId, data) {
finalResults[diseaseId] = {
'confidence': confidenceScores[diseaseId] ?? 0.0,
'matchPercentage': data['matchPercentage'],
'matchedSymptoms': data['matchedSymptoms'],
'recommendation': data['recommendation'],
'totalWeight': data['totalWeight'],
};
});
return finalResults;
}
}