| Crates.io | velvetry |
| lib.rs | velvetry |
| version | 0.1.0 |
| created_at | 2026-01-05 18:24:01.138961+00 |
| updated_at | 2026-01-05 18:24:01.138961+00 |
| description | A type-safe fuzzy logic control library for Rust |
| homepage | |
| repository | https://github.com/wack/velvetry |
| max_upload_size | |
| id | 2024334 |
| size | 66,084 |
A type-safe, production-ready fuzzy logic control library for Rust.
f64, f32, or custom numeric types for both domain and membership valuesnum-traits for numeric abstractionsAdd this to your Cargo.toml:
[dependencies]
fuzzy_control = "0.1.0"
use fuzzy_control::*;
use fuzzy_control::membership_functions::*;
use fuzzy_control::defuzzification::*;
use std::collections::HashMap;
fn main() -> Result<(), MembershipError> {
// Create input variable: Temperature (0-100°C)
let mut temperature = LinguisticVariable::new("temperature", (0.0, 100.0));
temperature.add_set(FuzzySet::new(
"cold",
Box::new(Triangular::new(0.0, 0.0, 50.0)?)
));
temperature.add_set(FuzzySet::new(
"hot",
Box::new(Triangular::new(50.0, 100.0, 100.0)?)
));
// Create output variable: Fan Speed (0-100%)
let mut fan_speed = LinguisticVariable::new("fan_speed", (0.0, 100.0));
fan_speed.add_set(FuzzySet::new(
"low",
Box::new(Triangular::new(0.0, 0.0, 50.0)?)
));
fan_speed.add_set(FuzzySet::new(
"high",
Box::new(Triangular::new(50.0, 100.0, 100.0)?)
));
// Create rules
let rules = vec![
FuzzyRule::new(
vec![Condition::new("temperature", "cold")],
RuleOperator::And,
vec![Consequent::new("fan_speed", "low")],
),
FuzzyRule::new(
vec![Condition::new("temperature", "hot")],
RuleOperator::And,
vec![Consequent::new("fan_speed", "high")],
),
];
// Build controller
let controller = FuzzyController::builder()
.add_input(temperature)
.add_output(fan_speed)
.add_rules(rules)
.build();
// Evaluate with input
let inputs = HashMap::from([("temperature".to_string(), 75.0)]);
let outputs = controller.evaluate_centroid(&inputs)?;
println!("Temperature: 75°C → Fan Speed: {:.1}%", outputs["fan_speed"]);
Ok(())
}
Define how crisp values map to fuzzy membership degrees:
// Triangular membership function
let cold = Triangular::new(0.0, 0.0, 50.0)?;
// Trapezoidal for flat peaks
let comfortable = Trapezoidal::new(15.0, 20.0, 25.0, 30.0)?;
// Gaussian for smooth transitions
let warm = Gaussian::new(25.0, 5.0)?;
Group fuzzy sets under a named variable:
let mut temperature = LinguisticVariable::new("temperature", (0.0, 100.0));
temperature.add_set(cold_set);
temperature.add_set(warm_set);
temperature.add_set(hot_set);
Express control logic in IF-THEN format:
// Simple rule
let rule = FuzzyRule::new(
vec![Condition::new("temperature", "hot")],
RuleOperator::And,
vec![Consequent::new("fan_speed", "high")],
);
// Rule with multiple conditions
let rule = FuzzyRule::new(
vec![
Condition::new("temperature", "hot"),
Condition::new("humidity", "high"),
],
RuleOperator::And,
vec![Consequent::new("fan_speed", "max")],
);
// Rule with weight
let rule = rule.with_weight(MembershipDegree::new(0.8)?);
Convert fuzzy outputs to crisp values:
// Centroid (most common)
let defuzz = Centroid::new(200)?;
let outputs = controller.evaluate(&inputs, &defuzz)?;
// Or use the convenience method
let outputs = controller.evaluate_centroid(&inputs)?;
// Other methods available:
// - Bisector
// - MeanOfMaximum
// - SmallestOfMaximum
// - LargestOfMaximum
// - WeightedAverage
Generate SVG visualizations for analysis and debugging:
use fuzzy_control::visualization::*;
// Plot membership functions
let svg = plot_membership_functions(&temperature, None)?;
std::fs::write("membership.svg", svg)?;
// Visualize fuzzification
let svg = plot_fuzzification(&temperature, 35.0, None)?;
std::fs::write("fuzzification.svg", svg)?;
// Show rule firing strengths
let inputs = HashMap::from([("temperature".to_string(), 35.0)]);
let svg = plot_rule_evaluation(&controller, &inputs, None)?;
std::fs::write("rules.svg", svg)?;
// Visualize output aggregation
let defuzz = Centroid::new(200)?;
let svg = plot_output_aggregation(
&controller,
"fan_speed",
&inputs,
&defuzz,
None
)?;
std::fs::write("aggregation.svg", svg)?;
// Generate control surface (2-input systems)
let svg = plot_control_surface(
&controller,
"temperature",
"humidity",
"fan_speed",
&defuzz,
30,
None
)?;
std::fs::write("surface.svg", svg)?;
Use different numeric types for domain and membership:
// f32 for memory efficiency
let controller = FuzzyController::<f32, f32>::builder()
.add_input(temp)
.build();
// Mix types: f64 domain, f32 membership
let controller = FuzzyController::<f64, f32>::builder()
.add_input(temp)
.build();
Provide custom T-norms and S-norms:
use fuzzy_control::operators::*;
let controller = FuzzyController::builder()
.with_t_norm(Box::new(ProductTNorm))
.with_s_norm(Box::new(ProbabilisticSumSNorm))
.add_input(temp)
.build();
Add multiple variables and rules at once:
let controller = FuzzyController::builder()
.add_inputs(vec![temp, humidity, pressure])
.add_outputs(vec![fan, heater, ventilation])
.add_rules(vec![rule1, rule2, rule3])
.build();
See the examples/ directory for complete examples:
temperature_control.rs - Simple temperature control systemhvac_system.rs - Multi-input HVAC controllervisualizations.rs - Generate all visualization typesRun examples with:
cargo run --example temperature_control
cargo run --example hvac_system
cargo run --example visualizations
Run the comprehensive test suite:
cargo test
Run tests with output:
cargo test -- --nocapture
The library is organized into several modules:
MembershipDegree, Float traitmembership_functions moduleFuzzySet, LinguisticVariableFuzzyRule, Condition, ConsequentTNorm, SNorm traits and implementationsFuzzyController and builderContributions are welcome! Please feel free to submit pull requests or open issues.
This project is dual-licensed under MIT or Apache-2.0. You may choose either license.