diff --git a/.Rbuildignore b/.Rbuildignore index 8ced6ca..1259e85 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -12,3 +12,7 @@ ^CRAN-SUBMISSION$ ^.github$ ^temp$ +^_pkgdown\.yml$ +^docs$ +^pkgdown$ +^index\.Rmd$ diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..49b4e39 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,206 @@ +# Copilot Instructions for lfl + +## Project Overview + +`lfl` is an R package providing various algorithms related to linguistic fuzzy logic: mining for linguistic fuzzy association rules, composition of fuzzy relations, performing perception-based logical deduction (PbLD), and forecasting time-series using fuzzy rule-based ensemble (FRBE). The package also contains basic fuzzy-related algebraic functions capable of handling missing values in different styles (Bochvar, Sobocinski, Kleene etc.), computation of Sugeno integrals and fuzzy transform. + +## Repository Structure + +- `R/` - R source code with roxygen2 documentation +- `src/` - C++ source code using Rcpp (if any) +- `tests/testthat/` - Unit tests using testthat framework (if any) +- `man/` - Generated documentation (auto-generated, do not edit manually) +- `vignettes/` - Package vignettes +- `temp/vignette-src/` - Original LaTeX vignette source (for reference, do not modify) +- `.github/workflows/` - CI/CD workflows for R CMD check, pkgdown + +## Development Setup + +### Prerequisites +- R >= 3.6 +- C++ compiler (for Rcpp components) +- Required R packages: Rcpp, devtools, roxygen2, testthat, knitr, rmarkdown + +### Building and Testing +```r +# Install dependencies +devtools::install_deps(dependencies = TRUE) + +# Build documentation +devtools::document() + +# Run R CMD check +devtools::check() + +# Build package +devtools::build() + +# Build pkgdown site +pkgdown::build_site() +``` + +## Coding Standards + +### R Code + +1. **Documentation**: All exported functions must have complete roxygen2 documentation including: + - `@title` and description + - `@param` for all parameters + - `@return` for return values + - `@seealso` for references to related functions + - `@examples` with working examples + - `@export` for exported functions + +2. **Naming Conventions**: + - Functions: lowercase with dots (e.g., `algebra()`, `is.algebra()`) + - Internal functions: prefix with `.` (e.g., `.mustBeNumericScalar()`) + - Variables: camelCase or lowercase + - Use consistent naming patterns within the package + +3. **Error Handling**: + - Use internal validation functions (e.g., `.mustBe()`, `.mustBeNumericScalar()`) + - Provide clear error messages + - Validate input parameters at function entry + +4. **Style**: + - Use consistent indentation (4 spaces) + - Use `<-` for assignment + - Follow existing code style in the package + - Keep functions focused and modular + +### C++ Code (if applicable) + +1. **Standards**: + - Use Rcpp for R/C++ interface + - Follow C++ best practices + - Add comments for complex algorithms + +2. **Documentation**: + - Use Rcpp attributes for exported functions: `// [[Rcpp::export]]` + - Add roxygen2 documentation above exported functions + - Internal functions should have clear comments + +## Dependencies + +### R Package Dependencies +- Runtime: Rcpp, foreach, forecast, plyr, tseries, e1071, tibble +- Build/Link: Rcpp +- Suggests: testthat, doMC, knitr, rmarkdown, R.rsp + +### Adding Dependencies +- Add to appropriate field in DESCRIPTION (Imports, Suggests, LinkingTo) +- Document why the dependency is needed +- Keep dependencies minimal + +## Documentation + +- Use roxygen2 for all documentation +- Run `devtools::document()` to regenerate man/*.Rd files +- README.md is generated from README.Rmd - edit the .Rmd file only +- Vignettes in `vignettes/*.Rmd` +- Package website built with pkgdown at https://beerda.github.io/lfl/ + +## Package Structure + +### Main Components + +1. **Fuzzy Algebras** (`algebra.R`, `algebraNA.R`): + - T-norms (Gödel, Goguen, Łukasiewicz) + - T-conorms, residua, bi-residua, negations + - Extensions for handling missing values + +2. **Fuzzy Sets** (`fsets.R`, `fcut.R`, `lcut.R`): + - Creation and manipulation of fuzzy sets + - Fuzzy partitioning of data + - Linguistic expressions + +3. **Linguistic Expressions** (`ctx.R`, `lingexpr.R`, `hedge.R`): + - Context definitions + - Evaluative linguistic expressions + - Linguistic hedges + +4. **Fuzzy Relations** (`compose.R`): + - Composition of fuzzy relations + - Various composition types + +5. **Association Rules** (`searchrules.R`, `farules.R`): + - Mining linguistic fuzzy association rules + - Rule manipulation and filtering + +6. **Perception-based Logical Deduction** (`pbld.R`, `fire.R`, `perceive.R`): + - PbLD inference method + - Rule firing and perception + +7. **Fuzzy Rule-Based Ensemble** (`frbe.R`, `frbemodel.R`): + - Time series forecasting + - Ensemble methods + +8. **Fuzzy Transform** (`ft.R`, `ftinv.R`): + - Fuzzy transform computation + - Inverse fuzzy transform + +9. **Sugeno Integral** (`sugeno.R`): + - Computation of Sugeno integrals + +## Common Tasks + +### Adding a New Function +1. Create function in appropriate R/*.R file +2. Add GPL-3 copyright header to the file if creating new file +3. Add roxygen2 documentation +4. Export if public function: `@export` +5. Add tests if test infrastructure exists +6. Run `devtools::document()` to update NAMESPACE and man/ +7. Run `devtools::check()` + +### Fixing a Bug +1. Add a failing test that reproduces the bug (if test infrastructure exists) +2. Fix the code +3. Verify test passes +4. Run `devtools::check()` + +### Adding a Vignette +1. Create new .Rmd file in `vignettes/` +2. Add appropriate YAML front matter +3. Include vignette metadata for indexing +4. Build with `devtools::build_vignettes()` + +## Best Practices + +1. **Never edit generated files**: NAMESPACE, man/*.Rd, RcppExports.cpp/R +2. **Check the package**: Run `devtools::check()` before committing significant changes +3. **Document as you code**: Add roxygen2 comments immediately +4. **Follow existing patterns**: Study similar functions in the codebase before implementing new features +5. **Maintain backward compatibility**: Deprecate functions properly before removal +6. **Keep vignettes current**: Update vignettes when adding significant new features + +## CI/CD Workflows + +- **rhub**: Additional platform testing +- **pkgdown**: Builds and deploys documentation site to GitHub Pages + +## Theoretical Background + +The package implements algorithms based on: +- Fuzzy natural logic / linguistic fuzzy logic +- Perception-based logical deduction (PbLD) +- Fuzzy relational calculus +- Linguistic fuzzy association rules +- Fuzzy rule-based ensemble (FRBE) + +Key references: +- Novák, V. (2008). A comprehensive theory of trichotomous evaluative linguistic expressions. +- Štěpnička, M., & Burda, M. (2017). Towards a calculus of perceptions. +- Burda, M., & Štěpnička, M. (2017). lfl: An R Package for Linguistic Fuzzy Logic. + +## Contributing + +Contributions should: +- Follow the existing code style +- Include appropriate documentation +- Pass R CMD check without errors or warnings +- Be compatible with R >= 3.6 + +## License + +GPL-3: This package is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. See the GNU General Public License for details. diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 0000000..3582ae2 --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,52 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master, devel] + pull_request: + branches: [main, master, devel] + +name: R-CMD-check.yaml + +permissions: read-all + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macos-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + #- {os: ubuntu-latest, r: 'oldrel-1'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + with: + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml new file mode 100644 index 0000000..bfc9f4d --- /dev/null +++ b/.github/workflows/pkgdown.yaml @@ -0,0 +1,49 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + release: + types: [published] + workflow_dispatch: + +name: pkgdown.yaml + +permissions: read-all + +jobs: + pkgdown: + runs-on: ubuntu-latest + # Only restrict concurrency for non-PR jobs + concurrency: + group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::pkgdown, local::. + needs: website + + - name: Build site + run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) + shell: Rscript {0} + + - name: Deploy to GitHub pages 🚀 + if: github.event_name != 'pull_request' + uses: JamesIves/github-pages-deploy-action@v4.5.0 + with: + clean: false + branch: gh-pages + folder: docs diff --git a/.gitignore b/.gitignore index d16d6b6..5e2517d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ config.status /autom4te.cache/ .~* inst/doc +docs/ diff --git a/DESCRIPTION b/DESCRIPTION index c2cdb13..6d255c8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -25,4 +25,4 @@ Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.3 Language: en-US -VignetteBuilder: R.rsp +VignetteBuilder: knitr, R.rsp diff --git a/README.Rmd b/README.Rmd index 8cba304..d3ab64c 100644 --- a/README.Rmd +++ b/README.Rmd @@ -16,7 +16,8 @@ knitr::opts_chunk$set( [![CRAN status](https://www.r-pkg.org/badges/version/lfl)](https://CRAN.R-project.org/package=lfl) [![codecov](https://codecov.io/gh/beerda/lfl/branch/master/graph/badge.svg)](https://app.codecov.io/gh/beerda/lfl) - +[![Downloads](https://cranlogs.r-pkg.org/badges/grand-total/lfl)](https://cran.r-project.org/package=lfl) +[![R-CMD-check](https://github.com/beerda/lfl/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/beerda/lfl/actions/workflows/R-CMD-check.yaml) # lfl @@ -29,7 +30,9 @@ integrals and fuzzy transform. ## Documentation -The complete documentation of the package is [available in this vignette](https://github.com/beerda/lfl/blob/master/vignettes/main.pdf). +For complete documentation, please visit the [package website](https://beerda.github.io/lfl/). + +The original academic article describing the package is also [available as a vignette](https://github.com/beerda/lfl/blob/master/vignettes/main.pdf). ## Installation diff --git a/_pkgdown.yml b/_pkgdown.yml new file mode 100644 index 0000000..99b629e --- /dev/null +++ b/_pkgdown.yml @@ -0,0 +1,157 @@ +url: https://beerda.github.io/lfl/ + +template: + bootstrap: 5 + light-switch: true + math-rendering: mathjax + +news: + cran_dates: false + +reference: + - title: "Fuzzy Algebras" + desc: > + Functions for creating and working with fuzzy algebras (t-norms, t-conorms, + residua, bi-residua, and negations). + contents: + - algebra + - is.algebra + - goedel.tnorm + - goguen.tnorm + - lukas.tnorm + - goedel.residuum + - goguen.residuum + - lukas.residuum + - mult + - minmax + + - title: "Handling Missing Values in Fuzzy Logic" + desc: > + Extensions of fuzzy algebras for handling missing values (NA) using various + approaches such as Kleene, Bochvar, Sobocinski, etc. + contents: + - sobocinski + - kleene + - dragonfly + - nelson + - lowerEst + - upperEst + + - title: "Fuzzy Sets and Linguistic Expressions" + desc: > + Functions for working with fuzzy sets, membership functions, and evaluative + linguistic expressions. + contents: + - fsets + - vars + - specs + - fcut + - lcut + - triangular + - raisedcosine + - ctx3 + - ctx3bilat + - ctx5 + - ctx5bilat + - as.ctx3 + - as.ctx5 + - lingexpr + - hedge + - defaultHedgeParams + + - title: "Fuzzy Partitioning" + desc: > + Functions for partitioning numeric data into fuzzy sets. + contents: + - equidist + - equifreq + + - title: "Composition of Fuzzy Relations" + desc: > + Functions for composing fuzzy relations with various composition types. + contents: + - compose + - quantifier + + - title: "Fuzzy Association Rules" + desc: > + Functions for mining and working with linguistic fuzzy association rules. + contents: + - searchrules + - farules + - is.farules + - as.data.frame.farules + - antecedents + - consequents + - rbcoverage + - reduce + + - title: "Perception-based Logical Deduction (PbLD)" + desc: > + Functions for performing inference using the Perception-based Logical Deduction + method on fuzzy rule bases. + contents: + - pbld + - fire + - perceive + - is.specific + - aggregateConsequents + + - title: "Fuzzy Rule-Based Ensemble (FRBE)" + desc: > + Functions for time series forecasting using fuzzy rule-based ensemble. + contents: + - frbe + - is.frbe + - evalfrbe + + - title: "Fuzzy Transform" + desc: > + Functions for fuzzy transform and its inverse. + contents: + - ft + - is.ft + - ftinv + + - title: "Sugeno Integral" + desc: > + Functions for computing Sugeno integrals. + contents: + - sugeno + + - title: "Defuzzification" + desc: > + Functions for defuzzification of fuzzy sets. + contents: + - defuzz + + - title: "Time Series Operations" + desc: > + Functions for time series analysis and forecasting. + contents: + - rollapply + - horizon + + - title: "Error Metrics" + desc: > + Functions for computing forecast error metrics. + contents: + - rmse + - smape + - mase + + - title: "Print and Display Methods" + desc: > + S3 methods for printing and displaying objects. + contents: + - starts_with("print.") + - starts_with("as.data.frame.") + + - title: "Deprecated Functions" + desc: > + Deprecated functions kept for backward compatibility. + contents: + - triangle + - raisedcos + - triangularShape + - raisedcosShape diff --git a/index.Rmd b/index.Rmd new file mode 100644 index 0000000..c008f60 --- /dev/null +++ b/index.Rmd @@ -0,0 +1,130 @@ +--- +output: github_document +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + fig.path = "man/figures/README-", + out.width = "100%" +) +``` + +# lfl - Linguistic Fuzzy Logic + + +[![CRAN status](https://www.r-pkg.org/badges/version/lfl)](https://CRAN.R-project.org/package=lfl) +[![codecov](https://codecov.io/gh/beerda/lfl/branch/master/graph/badge.svg)](https://app.codecov.io/gh/beerda/lfl) +[![Downloads](https://cranlogs.r-pkg.org/badges/grand-total/lfl)](https://cran.r-project.org/package=lfl) + + +The `lfl` package for [R statistical computing environment](https://www.r-project.org/) provides various algorithms related to **linguistic fuzzy logic**: mining for linguistic fuzzy association rules, composition of fuzzy relations, performing perception-based logical deduction (PbLD), and forecasting time-series using fuzzy rule-based ensemble (FRBE). The package also contains basic fuzzy-related algebraic functions capable of handling missing values in different styles (Bochvar, Sobocinski, Kleene etc.), computation of Sugeno integrals and fuzzy transform. + +## Key Features + +- **Fuzzy Algebras**: Support for various t-norms (Gödel, Goguen, Łukasiewicz) and corresponding t-conorms, residua, bi-residua, and negations +- **Missing Value Handling**: Extensions for handling NAs using Kleene, Bochvar, Sobocinski, Dragonfly, and other approaches +- **Linguistic Expressions**: Tools for working with evaluative linguistic expressions (e.g., "very small", "roughly medium", "extremely big") +- **Fuzzy Relations**: Composition of fuzzy relations with various extensions +- **Association Rules**: Mining for linguistic fuzzy association rules from data +- **Perception-based Logical Deduction (PbLD)**: Inference method specifically designed for linguistic fuzzy logic +- **Fuzzy Rule-Based Ensemble (FRBE)**: Time series forecasting using ensemble of forecasting methods +- **Fuzzy Transform**: Computing fuzzy transform and its inverse +- **Sugeno Integral**: Computation of Sugeno integrals + +## Installation + +To install the stable version from CRAN, type the following command within your R session: + +```r +install.packages("lfl") +``` + +You can also install the development version of `lfl` from [GitHub](https://github.com/) with: + +```r +install.packages("devtools") +devtools::install_github("beerda/lfl") +``` + +To start using the package, load it to the R session with: + +```{r} +library(lfl) +``` + +## Quick Examples + +### Creating Fuzzy Algebras + +```{r} +# Create a Gödel algebra +a <- algebra("goedel") + +# Compute fuzzy operations +a$n(c(0.5, 0.8, 0, 1)) # negation +a$t(c(0.8, 0.3), c(0.2, 1), c(1, 0)) # t-norm (conjunction) +a$s(c(0.8, 0.3), c(0.2, 1)) # t-conorm (disjunction) +a$r(c(0.8, 0.3), c(0.2, 1)) # residuum (implication) +``` + +### Composition of Fuzzy Relations + +```{r} +# Define fuzzy relations +R <- matrix(c(0.9, 0, 0, 0, + 1, 0.9, 0.8, 0, + 0.8, 0.8, 0.9, 1, + 0, 0.1, 0, 0.9), nrow=4) + +S <- matrix(c(1, 0.9, 0, 1, + 1, 0.2, 1, 0, + 0.1, 0.9, 0, 0.7, + 0.9, 0, 1, 0.1, + 0, 1, 1, 0.9), nrow=4) + +# Compute composition +compose(R, S, alg=algebra("lukasiewicz"), type="basic") +``` + +### Evaluative Linguistic Expressions + +```{r} +# Create context for linguistic expressions +context <- ctx3(low=0, center=50, high=100) + +# Create linguistic expressions +small <- lcut(1:100, context, atomic="sm") +medium <- lcut(1:100, context, atomic="me") +big <- lcut(1:100, context, atomic="bi") +``` + +## Documentation + +For complete documentation, please visit the [package website](https://beerda.github.io/lfl/) or read the vignettes: + +- **Introduction to lfl**: Overview of the package features +- **Fuzzy Algebras**: Detailed guide on fuzzy algebras and handling missing values +- **Composition of Fuzzy Relations**: Working with fuzzy relation compositions +- **Perception-based Logical Deduction**: Using PbLD for fuzzy inference +- **Fuzzy Rule-Based Ensemble**: Time series forecasting with FRBE + +You can also access the original academic article describing the package: +[Burda & Štěpnička (2017). lfl: An R Package for Linguistic Fuzzy Logic](https://github.com/beerda/lfl/blob/master/vignettes/main.pdf). + +## Contributing + +Contributions, suggestions, and bug reports are welcome. Please submit [issues](https://github.com/beerda/lfl/issues/) on [GitHub](https://github.com/). + +## Citation + +If you use this package in your research, please cite: + +``` +Burda, M., & Štěpnička, M. (2017). lfl: An R Package for Linguistic Fuzzy Logic. +``` + +## License + +This package is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/vignettes/frbe.Rmd b/vignettes/frbe.Rmd new file mode 100644 index 0000000..d1bbdc5 --- /dev/null +++ b/vignettes/frbe.Rmd @@ -0,0 +1,355 @@ +--- +title: "Fuzzy Rule-Based Ensemble (FRBE)" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Fuzzy Rule-Based Ensemble (FRBE)} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +```{r setup} +library(lfl) +``` + +# Fuzzy Rule-Based Ensemble (FRBE) + +The **Fuzzy Rule-Based Ensemble (FRBE)** is a method for time series forecasting that combines multiple forecasting methods using fuzzy rule-based weighting. FRBE adapts to different characteristics of the time series by using fuzzy if-then rules to determine the weight of each forecasting method. + +## Overview + +FRBE works by: + +1. **Decomposing** the time series into components (trend, seasonality, etc.) +2. **Characterizing** each component with fuzzy features +3. **Applying fuzzy rules** to determine weights for different forecasting methods +4. **Combining forecasts** from multiple methods using the fuzzy weights +5. **Producing** the final ensemble forecast + +## Basic Usage + +The simplest way to use FRBE is with the `frbe()` function: + +```{r} +# Create a sample time series +set.seed(123) +myts <- ts(1:100 + rnorm(100, sd=5), frequency=12) # monthly data + +# Fit FRBE model and forecast 10 steps ahead +fit <- frbe(myts, h=10) + +# View the forecast +fit$mean +``` + +The `fit` object contains: +- `mean`: Point forecasts +- `lower`: Lower prediction intervals (if computed) +- `upper`: Upper prediction intervals (if computed) +- Additional diagnostic information + +## Understanding Time Series Frequency + +The frequency parameter is crucial for FRBE: + +```{r} +# Hourly data (24 observations per day) +hourly_ts <- ts(1:168 + rnorm(168), frequency=24) + +# Daily data (7 observations per week) +daily_ts <- ts(1:70 + rnorm(70), frequency=7) + +# Monthly data (12 observations per year) +monthly_ts <- ts(1:60 + rnorm(60), frequency=12) + +# Quarterly data (4 observations per year) +quarterly_ts <- ts(1:40 + rnorm(40), frequency=4) +``` + +FRBE automatically adapts to the frequency. + +## Forecasting Horizons + +Specify different forecasting horizons with the `h` parameter: + +```{r} +# Short-term forecast (5 periods ahead) +fit_short <- frbe(myts, h=5) +fit_short$mean + +# Long-term forecast (20 periods ahead) +fit_long <- frbe(myts, h=20) +fit_long$mean +``` + +## Real-World Example: Trend and Seasonality + +Let's create a more realistic time series with trend and seasonality: + +```{r} +# Create time series with trend and seasonal pattern +t <- 1:120 +trend <- 0.5 * t +seasonal <- 10 * sin(2 * pi * t / 12) +noise <- rnorm(120, sd=2) +realistic_ts <- ts(trend + seasonal + noise, frequency=12) + +# Plot the time series +plot(realistic_ts, main="Time Series with Trend and Seasonality", + xlab="Time", ylab="Value") + +# Fit FRBE +fit <- frbe(realistic_ts, h=12) + +# View forecasts +fit$mean +``` + +## Visualizing Forecasts + +```{r} +# Create forecast plot +plot(realistic_ts, xlim=c(0, length(realistic_ts)/12 + 1.5), + main="FRBE Forecast", xlab="Time", ylab="Value") + +# Add forecast +lines(fit$mean, col="blue", lwd=2) + +# Add legend +legend("topleft", legend=c("Actual", "Forecast"), + col=c("black", "blue"), lty=1, lwd=c(1, 2)) +``` + +## Ensemble Components + +FRBE combines multiple base forecasting methods. The package uses methods from the `forecast` package: + +- **ETS** (Exponential Smoothing State Space) +- **ARIMA** (AutoRegressive Integrated Moving Average) +- **Theta** method +- **Random Walk with Drift** +- **Naive** methods + +Each method's contribution is weighted based on fuzzy rules that evaluate: +- Trend strength +- Seasonality strength +- Autocorrelation +- Stability +- Other time series characteristics + +## Evaluating Forecast Accuracy + +Use `evalfrbe()` to evaluate forecast accuracy: + +```{r} +# Split data into training and test +n_train <- 108 # First 9 years (9 * 12 months) +train_ts <- ts(realistic_ts[1:n_train], frequency=12) +test_vec <- realistic_ts[(n_train+1):length(realistic_ts)] + +# Fit on training data +fit <- frbe(train_ts, h=12) + +# Evaluate against test data +evaluation <- evalfrbe(fit, test_vec) +print(evaluation) +``` + +The evaluation includes: +- **RMSE** (Root Mean Squared Error) +- **MAE** (Mean Absolute Error) +- **MAPE** (Mean Absolute Percentage Error) +- **SMAPE** (Symmetric Mean Absolute Percentage Error) +- **MASE** (Mean Absolute Scaled Error) + +## Error Metrics + +The package provides individual error metric functions: + +### RMSE (Root Mean Squared Error) + +```{r} +forecast_values <- fit$mean +actual_values <- test_vec + +# Calculate RMSE +rmse(actual_values, forecast_values) +``` + +### SMAPE (Symmetric Mean Absolute Percentage Error) + +```{r} +smape(actual_values, forecast_values) +``` + +### MASE (Mean Absolute Scaled Error) + +```{r} +# MASE requires the training data for scaling +mase(actual_values, forecast_values, scale=train_ts) +``` + +## Advanced Usage: Time Series with Complex Patterns + +### Multiple Seasonality + +```{r} +# Create time series with multiple seasonal patterns +t <- 1:168 # One week of hourly data +daily_pattern <- 5 * sin(2 * pi * t / 24) # Daily cycle +weekly_pattern <- 3 * sin(2 * pi * t / 168) # Weekly cycle +trend <- 0.1 * t +noise <- rnorm(168, sd=1) + +complex_ts <- ts(trend + daily_pattern + weekly_pattern + noise, frequency=24) + +# Forecast with FRBE +fit_complex <- frbe(complex_ts, h=24) # Forecast next day + +# Display first few forecasts +head(fit_complex$mean) +``` + +### Intermittent Demand + +```{r} +# Create intermittent demand pattern (common in inventory management) +intermittent <- rpois(100, lambda=0.5) * abs(rnorm(100, mean=10, sd=3)) +intermittent_ts <- ts(intermittent, frequency=12) + +# Forecast +fit_intermittent <- frbe(intermittent_ts, h=6) +fit_intermittent$mean +``` + +## Rolling Window Forecasting + +For backtesting, use rolling window approach: + +```{r} +# Prepare data +data <- realistic_ts +n_test <- 12 # Test on last 12 observations +n_train <- length(data) - n_test + +# Initialize storage +forecasts <- numeric(n_test) +actuals <- numeric(n_test) + +# Rolling window forecast +for (i in 1:n_test) { + # Training data up to current point + train <- ts(data[1:(n_train + i - 1)], frequency=12) + + # Fit and forecast 1 step ahead + fit <- frbe(train, h=1) + forecasts[i] <- fit$mean[1] + actuals[i] <- data[n_train + i] +} + +# Calculate accuracy +rmse(actuals, forecasts) +``` + +## Handling Missing Values + +```{r eval=FALSE} +# FRBE can handle time series with missing values +# through the underlying forecast methods + +ts_with_na <- realistic_ts +ts_with_na[c(10, 20, 30)] <- NA + +# Some methods will interpolate or skip NAs +fit_na <- frbe(ts_with_na, h=6) +``` + +## When to Use FRBE + +FRBE is particularly useful for: + +1. **Complex time series**: When no single method consistently performs best +2. **Changing patterns**: When time series characteristics evolve +3. **Uncertain conditions**: When forecast uncertainty is high +4. **Ensemble benefits**: When combining multiple forecasters improves robustness + +## Comparison with Single Methods + +```{r} +# Compare FRBE with individual methods +n_train <- 108 # First 9 years +train <- ts(realistic_ts[1:n_train], frequency=12) +test <- realistic_ts[(n_train+1):length(realistic_ts)] + +# FRBE +frbe_fit <- frbe(train, h=12) +frbe_rmse <- rmse(test, frbe_fit$mean) + +# Individual methods (simplified comparison) +cat("FRBE RMSE:", frbe_rmse, "\n") +``` + +## Checking Model Fit + +```{r} +# Check if the model is an FRBE object +fit <- frbe(realistic_ts, h=6) +is.frbe(fit) +``` + +## Time Series Utilities + +### Rolling Apply + +The `rollapply()` function applies a function over a rolling window: + +```{r} +# Calculate rolling mean +data <- 1:20 +rollapply(data, width=5, FUN=mean) +``` + +### Horizon Function + +The `horizon()` function (discussed in linguistic expressions vignette) can be used with time series features. + +## Practical Tips + +1. **Choose appropriate frequency**: Ensure the `frequency` parameter matches your data +2. **Sufficient history**: Provide enough historical data (at least 2-3 seasonal cycles) +3. **Validate forecasts**: Use hold-out sets or cross-validation +4. **Monitor performance**: Regularly evaluate and update models +5. **Consider domain knowledge**: FRBE works best when combined with expert knowledge + +## Limitations + +- **Computational cost**: FRBE fits multiple models, which can be slow for large datasets +- **Parameter tuning**: Default fuzzy rules may not be optimal for all series +- **Interpretation**: Ensemble weights are not always transparent + +## Summary + +FRBE provides robust time series forecasting through: + +- **Ensemble approach**: Combines multiple forecasting methods +- **Fuzzy logic**: Uses fuzzy rules to weight methods adaptively +- **Flexibility**: Handles various time series patterns +- **Accuracy**: Often outperforms single-method approaches + +Key functions: +- `frbe()`: Fit FRBE and generate forecasts +- `is.frbe()`: Check if object is FRBE +- `evalfrbe()`: Evaluate forecast accuracy +- `rmse()`, `smape()`, `mase()`: Individual error metrics +- `rollapply()`: Rolling window operations + +## References + +- Štěpnička, M., & Burda, M. (2017). On the aggregation of forecasts using fuzzy rules. In *Proceedings of IFSA-SCIS*. +- Makridakis, S., Spiliotis, E., & Assimakopoulos, V. (2018). The M4 Competition: Results, findings, conclusion and way forward. *International Journal of Forecasting*, 34(4), 802-808. diff --git a/vignettes/fuzzy-algebras.Rmd b/vignettes/fuzzy-algebras.Rmd new file mode 100644 index 0000000..2515d4a --- /dev/null +++ b/vignettes/fuzzy-algebras.Rmd @@ -0,0 +1,292 @@ +--- +title: "Fuzzy Algebras and Missing Values" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Fuzzy Algebras and Missing Values} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +```{r setup} +library(lfl) +``` + +# Fuzzy Algebras + +Fuzzy algebras form the foundation of fuzzy logic operations. The `lfl` package provides support for three main types of algebras based on different t-norms. + +## T-norms and Their Algebras + +A **t-norm** (triangular norm) is a binary operation that generalizes the logical conjunction (AND) to fuzzy logic. The `lfl` package supports three standard t-norms: + +### Gödel T-norm (Minimum) + +The Gödel t-norm computes the minimum of its arguments: + +```{r} +a <- algebra("goedel") + +# T-norm (conjunction/AND) +a$t(0.7, 0.5) +a$t(c(0.8, 0.3), c(0.2, 1), c(1, 0)) + +# T-conorm (disjunction/OR) +a$s(0.7, 0.5) + +# Negation +a$n(c(0.5, 0.8, 0, 1)) + +# Residuum (implication) +a$r(0.7, 0.5) +a$r(0.5, 0.7) + +# Bi-residuum (equivalence) +a$b(0.7, 0.5) +``` + +### Goguen T-norm (Product) + +The Goguen t-norm uses multiplication: + +```{r} +a <- algebra("goguen") + +# T-norm (product) +a$t(0.7, 0.5) + +# T-conorm (probabilistic sum) +a$s(0.7, 0.5) + +# Residuum (quotient) +a$r(0.7, 0.5) +a$r(0.5, 0.7) +``` + +### Łukasiewicz T-norm + +The Łukasiewicz t-norm uses the formula: max(0, a + b - 1) + +```{r} +a <- algebra("lukasiewicz") + +# T-norm +a$t(0.7, 0.5) + +# T-conorm +a$s(0.7, 0.5) + +# Residuum +a$r(0.7, 0.5) +a$r(0.5, 0.7) +``` + +## Operations on Fuzzy Algebras + +Each algebra provides the following operations: + +- **`n(x)`**: Negation (NOT) +- **`ni(x)`**: Involutive negation (double negation equals identity) +- **`t(...)`**: T-norm (conjunction/AND) +- **`pt(...)`**: Product t-norm version +- **`s(...)`**: T-conorm (disjunction/OR) +- **`r(x, y)`**: Residuum (implication) +- **`b(x, y)`**: Bi-residuum (equivalence) + +Example using multiple operations: + +```{r} +a <- algebra("goedel") + +# Modus Ponens: if A implies B, and A is true (to degree p), then B +p <- 0.8 # truth degree of A +imp <- a$r(p, 0.6) # A implies B (with B having degree 0.6) +a$t(p, imp) # conclusion about B +``` + +## Standard Negation + +By default, algebras use standard negation. You can explicitly request standard negation: + +```{r} +a <- algebra("goedel", stdneg=TRUE) +a$n(0.7) +``` + +## Handling Missing Values + +Standard fuzzy algebras return `NA` when any operand is missing: + +```{r} +a <- algebra("goedel") +a$t(0.8, NA) +a$s(0.8, NA) +a$r(0.8, NA) +``` + +However, the `lfl` package provides several extensions to handle missing values in different ways. + +### Sobociński Extension + +The Sobociński approach treats `NA` as maximally uncertain: + +```{r} +a <- algebra("goedel") +a2 <- sobocinski(a) + +# Standard algebra +a$t(NA, 0.3) + +# Sobociński extension +a2$t(NA, 0.3) +a2$s(NA, 0.7) +a2$n(NA) +``` + +### Kleene Extension + +The Kleene extension is another approach to handling missing values: + +```{r} +a <- algebra("goedel") +a_kleene <- kleene(a) + +a_kleene$t(NA, 0.3) +a_kleene$s(NA, 0.7) +``` + +### Dragonfly Extension + +The Dragonfly extension provides yet another interpretation: + +```{r} +a <- algebra("lukasiewicz") +a_dragon <- dragonfly(a) + +a_dragon$t(NA, 0.3) +a_dragon$s(NA, 0.7) +``` + +### Nelson Extension + +The Nelson extension: + +```{r} +a <- algebra("goedel") +a_nelson <- nelson(a) + +a_nelson$t(NA, 0.3) +a_nelson$s(NA, 0.7) +``` + +### Lower and Upper Estimates + +The package also provides lower and upper estimate extensions: + +```{r} +a <- algebra("goedel") +a_lower <- lowerEst(a) +a_upper <- upperEst(a) + +# Lower estimate (pessimistic) +a_lower$t(NA, 0.7) + +# Upper estimate (optimistic) +a_upper$t(NA, 0.7) +``` + +## Practical Applications + +### Example: Medical Diagnosis + +Suppose we have degrees of belief about symptoms and want to combine them: + +```{r} +a <- algebra("lukasiewicz") + +# Degrees of belief in symptoms +fever <- 0.8 +cough <- 0.6 +fatigue <- 0.9 + +# Rule: IF fever AND cough AND fatigue THEN flu +# Compute degree of flu +flu_degree <- a$t(fever, cough, fatigue) +flu_degree + +# If we're uncertain about one symptom +fatigue_uncertain <- NA +a2 <- sobocinski(a) +flu_degree_uncertain <- a2$t(fever, cough, fatigue_uncertain) +flu_degree_uncertain +``` + +### Example: Control Systems + +Combining fuzzy control rules: + +```{r} +a <- algebra("goedel") + +# Temperature is high +temp_high <- 0.7 + +# Pressure is medium +pressure_med <- 0.5 + +# Rule: IF temp is high OR pressure is medium THEN reduce power +reduce_power <- a$s(temp_high, pressure_med) +reduce_power +``` + +## Direct T-norm Functions + +For convenience, the package also provides direct t-norm functions: + +```{r} +# Gödel t-norm (minimum) +goedel.tnorm(0.7, 0.5, 0.8) + +# Goguen t-norm (product) +goguen.tnorm(0.7, 0.5, 0.8) + +# Łukasiewicz t-norm +lukas.tnorm(0.7, 0.5, 0.8) +``` + +And corresponding residuum functions: + +```{r} +# Gödel residuum +goedel.residuum(0.7, 0.5) + +# Goguen residuum +goguen.residuum(0.7, 0.5) + +# Łukasiewicz residuum +lukas.residuum(0.7, 0.5) +``` + +## Checking Algebra Objects + +You can check if an object is a fuzzy algebra: + +```{r} +a <- algebra("goedel") +is.algebra(a) +``` + +## Summary + +Fuzzy algebras provide the mathematical foundation for fuzzy logic operations in the `lfl` package. The three main t-norms (Gödel, Goguen, and Łukasiewicz) offer different characteristics suitable for various applications. Extensions for handling missing values provide flexibility when dealing with uncertain or incomplete data. + +## References + +- Běhounek, L., & Novák, V. (2015). Towards fuzzy partial logic. In *Proceedings of the 2015 IEEE International Conference on Fuzzy Systems*. +- Běhounek, L., & Daňková, M. (2016). Relational compositions in Fuzzy Class Theory. *Fuzzy Sets and Systems*, 329, 70-89. diff --git a/vignettes/fuzzy-relations.Rmd b/vignettes/fuzzy-relations.Rmd new file mode 100644 index 0000000..a34a19c --- /dev/null +++ b/vignettes/fuzzy-relations.Rmd @@ -0,0 +1,314 @@ +--- +title: "Composition of Fuzzy Relations" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Composition of Fuzzy Relations} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +```{r setup} +library(lfl) +``` + +# Composition of Fuzzy Relations + +The composition of fuzzy relations is a fundamental operation in fuzzy relational calculus. The `lfl` package provides flexible tools for composing fuzzy relations using various algebras and composition types. + +## Basic Example: Medical Diagnosis + +Consider a medical diagnosis scenario where we want to infer patient-disease relationships from patient-symptom and symptom-disease relationships. + +### Define Relations + +```{r} +# Patient-Symptom relation (R) +# Rows: patients, Columns: symptoms +# Values: degree to which patient exhibits symptom +R <- matrix(c(0.9, 0, 0, 0, + 1, 0.9, 0.8, 0, + 0.8, 0.8, 0.9, 1, + 0, 0.1, 0, 0.9), nrow=4) +rownames(R) <- paste0("patient", 1:4) +colnames(R) <- c("tired", "cough", "fever", "blur.vis") + +print(R) +``` + +```{r} +# Symptom-Disease relation (S) +# Rows: symptoms, Columns: diseases +# Values: degree to which symptom indicates disease +S <- matrix(c(1, 0.9, 0, 1, + 1, 0.2, 1, 0, + 0.1, 0.9, 0, 0.7, + 0.9, 0, 1, 0.1, + 0, 1, 1, 0.9), nrow=4) +rownames(S) <- c("tired", "cough", "fever", "blur.vis") +colnames(S) <- c("pulm.hyp", "sleep.sick", "malaria", "hangover", "influenza") + +print(S) +``` + +### Basic Composition + +The basic composition combines the two relations to infer patient-disease relationships: + +```{r} +a <- algebra("lukasiewicz") +compose(R, S, alg=a, type="basic") +``` + +Interpretation: The result shows the degree to which each patient may have each disease based on their symptoms. + +## Different Algebras + +The choice of algebra affects the composition result: + +### Gödel (Minimum) Algebra + +```{r} +a_goedel <- algebra("goedel") +compose(R, S, alg=a_goedel, type="basic") +``` + +### Goguen (Product) Algebra + +```{r} +a_goguen <- algebra("goguen") +compose(R, S, alg=a_goguen, type="basic") +``` + +### Łukasiewicz Algebra + +```{r} +a_lukas <- algebra("lukasiewicz") +compose(R, S, alg=a_lukas, type="basic") +``` + +## Composition Types + +The `lfl` package supports various composition types: + +### Basic Composition + +The standard sup-t composition: + +```{r} +compose(R, S, alg=a_lukas, type="basic") +``` + +### Sub-composition + +Uses residuum instead of t-norm: + +```{r} +compose(R, S, alg=a_lukas, type="sub") +``` + +### Super-composition + +Uses bi-residuum: + +```{r} +compose(R, S, alg=a_lukas, type="super") +``` + +### Square Composition + +Combines sub and super compositions: + +```{r} +compose(R, S, alg=a_lukas, type="square") +``` + +## Matrix Multiplication Variant + +The `mult()` function provides a generalized matrix multiplication with custom operation: + +```{r} +# Custom operation: sum of product-t-norm +a <- algebra("lukasiewicz") +result <- mult(R, S, function(r, s) { + a$s(a$pt(r, s)) +}) +print(result) +``` + +## Excluding and Unavoidable Features + +The package supports compositions with excluding and unavoidable features, useful for modeling constraints. + +### Excluding Features + +Define features that should be excluded: + +```{r} +# Features to exclude (E matrix) +E <- matrix(c(0, 0, 1, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 0), nrow=4) +colnames(E) <- colnames(S) +rownames(E) <- rownames(S) + +print(E) +``` + +Compose with exclusions: + +```{r} +a <- algebra("lukasiewicz") +RS <- compose(R, S, alg=a, type="basic") +RE <- compose(R, E, alg=a, type="basic") + +# Exclude using product-t-norm with negation +a$pt(RS, a$n(RE)) +``` + +### Unavoidable Features + +Define features that must be present: + +```{r} +# Unavoidable features (U matrix) +U <- matrix(c(0, 0, 0, 1, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 1, 0, 0), nrow=4) +colnames(U) <- colnames(S) +rownames(U) <- rownames(S) + +print(U) +``` + +Compose with unavoidable features: + +```{r} +RU <- compose(R, U, alg=a, type="super") + +# Combine basic composition with unavoidable features +a$pt(RS, RU) +``` + +### Combined Exclusions and Unavoidable Features + +```{r} +# Combine all three: basic, exclude, and unavoidable +a$pt(RS, a$n(RE), RU) +``` + +## Generalized Quantifiers + +The package supports compositions with generalized quantifiers using Sugeno integrals: + +```{r} +# Define "at least 2" quantifier +qatleast <- sugeno( + measure = function(x) as.numeric(x >= 2), + relative = FALSE, + strong = TRUE, + alg = "goedel" +) + +# Apply quantifier in composition +qRS <- mult(R, S, function(r, s) { + qatleast(a$pt(r, s)) +}) + +print(qRS) +``` + +Combine with exclusions and unavoidable features: + +```{r} +a$pt(qRS, a$n(RE), RU) +``` + +## Practical Application Example + +### Recommendation System + +Suppose we have user-preference and preference-item relations: + +```{r} +# Users and their feature preferences +user_pref <- matrix(c( + 0.9, 0.2, 0.1, # User 1: likes action, dislikes romance, neutral on comedy + 0.1, 0.9, 0.7, # User 2: dislikes action, likes romance and comedy + 0.5, 0.5, 0.5 # User 3: neutral on all +), nrow=3, byrow=TRUE) +rownames(user_pref) <- paste0("user", 1:3) +colnames(user_pref) <- c("action", "romance", "comedy") + +# Movies and their genre features +movie_genre <- matrix(c( + 0.9, 0.1, 0.2, # Movie 1: mainly action + 0.2, 0.9, 0.3, # Movie 2: mainly romance + 0.1, 0.2, 0.9, # Movie 3: mainly comedy + 0.7, 0.5, 0.4 # Movie 4: mixed +), nrow=3) +rownames(movie_genre) <- c("action", "romance", "comedy") +colnames(movie_genre) <- paste0("movie", 1:4) + +# Recommend movies to users +a <- algebra("goguen") # Product algebra for recommendation +recommendations <- compose(user_pref, movie_genre, alg=a, type="basic") +print(recommendations) +``` + +Interpretation: Higher values indicate better recommendations. + +## Min-Max Composition + +For simple cases, you can use `minmax()` operations: + +```{r} +# Example using minmax for simpler composition +small_R <- matrix(c(0.8, 0.3, 0.5, 0.9), nrow=2) +small_S <- matrix(c(0.7, 0.6, 0.4, 0.8), nrow=2) + +# Manual min-max composition (for illustration) +result <- matrix(0, nrow=nrow(small_R), ncol=ncol(small_S)) +for(i in 1:nrow(small_R)) { + for(j in 1:ncol(small_S)) { + result[i,j] <- max(pmin(small_R[i,], small_S[,j])) + } +} +print(result) + +# Compare with Gödel composition (should be same) +compose(small_R, small_S, alg=algebra("goedel"), type="basic") +``` + +## Summary + +The composition of fuzzy relations is a powerful tool for: + +- **Inference**: Deriving new relationships from existing ones +- **Recommendation systems**: Combining user preferences with item features +- **Medical diagnosis**: Inferring diseases from symptoms +- **Control systems**: Relating inputs to outputs through intermediate variables + +Key features in `lfl`: + +- Multiple algebras (Gödel, Goguen, Łukasiewicz) +- Various composition types (basic, sub, super, square) +- Support for excluding and unavoidable features +- Generalized quantifiers via Sugeno integrals +- Flexible matrix operations with `mult()` + +## References + +- Bandler, W., & Kohout, L. J. (1980). Fuzzy power sets and fuzzy implication operators. *Fuzzy Sets and Systems*, 4(1), 13-30. +- Bělohlávek, R. (2002). *Fuzzy Relational Systems: Foundations and Principles*. Springer. +- Cao, N., Štěpnička, M., Burda, M., & Dolný, A. (2017). Excluding features in fuzzy relational compositions. *Expert Systems with Applications*, 81, 1-11. diff --git a/vignettes/introduction.Rmd b/vignettes/introduction.Rmd new file mode 100644 index 0000000..ee55e33 --- /dev/null +++ b/vignettes/introduction.Rmd @@ -0,0 +1,201 @@ +--- +title: "Introduction to lfl" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Introduction to lfl} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + fig.width = 6, + fig.height = 4 +) +``` + +# Introduction + +The `lfl` package for R provides various algorithms related to **linguistic fuzzy logic**. This package implements tools for using fuzzy relational calculus, linguistic fuzzy logic in data processing, and various fuzzy-based methods for classification and prediction. + +## Installation + +To install the stable version from CRAN: + +```{r eval=FALSE} +install.packages("lfl") +``` + +Or install the development version from GitHub: + +```{r eval=FALSE} +install.packages("devtools") +devtools::install_github("beerda/lfl") +``` + +Load the package: + +```{r} +library(lfl) +``` + +## Key Features + +The `lfl` package covers several important areas: + +### 1. Fuzzy Algebras + +The package provides support for various t-norms (Gödel, Goguen, Łukasiewicz) and their corresponding operations: + +```{r} +# Create a Gödel algebra +a <- algebra("goedel") + +# Negation +a$n(c(0.5, 0.8, 0, 1)) + +# T-norm (conjunction) +a$t(c(0.8, 0.3), c(0.2, 1), c(1, 0)) + +# T-conorm (disjunction) +a$s(c(0.8, 0.3), c(0.2, 1)) + +# Residuum (implication) +a$r(c(0.8, 0.3), c(0.2, 1)) +``` + +Different algebras can be created: + +```{r} +# Goguen algebra (product) +a_goguen <- algebra("goguen") + +# Łukasiewicz algebra +a_lukas <- algebra("lukasiewicz") +``` + +### 2. Handling Missing Values + +The package provides several approaches for handling missing values (NA) in fuzzy operations: + +```{r} +a <- algebra("goedel") +a2 <- sobocinski(a) + +# Standard algebra returns NA +a$t(NA, 0.3) + +# Sobocinski extension handles NA +a2$t(NA, 0.3) +``` + +### 3. Composition of Fuzzy Relations + +Fuzzy relations can be composed using various algebras and composition types: + +```{r} +# Define symptom-disease relation +S <- matrix(c(1, 0.9, 0, 1, + 1, 0.2, 1, 0, + 0.1, 0.9, 0, 0.7, + 0.9, 0, 1, 0.1, + 0, 1, 1, 0.9), nrow=4) +colnames(S) <- c("pulm.hyp", "sleep.sick", "malaria", "hangover", "influenza") +rownames(S) <- c("tired", "cough", "fever", "blur.vis") + +# Define patient-symptom relation +R <- matrix(c(0.9, 0, 0, 0, + 1, 0.9, 0.8, 0, + 0.8, 0.8, 0.9, 1, + 0, 0.1, 0, 0.9), nrow=4) +colnames(R) <- rownames(S) +rownames(R) <- paste0("patient", 1:4) + +# Compose relations to get patient-disease relation +a <- algebra("lukasiewicz") +compose(R, S, alg=a, type="basic") +``` + +### 4. Evaluative Linguistic Expressions + +The package supports creation and manipulation of evaluative linguistic expressions like "very small", "roughly medium", or "extremely big": + +```{r} +# Create context for linguistic expressions +ctx <- ctx3(low=0, center=50, high=100) + +# Create atomic expressions +sm_horiz <- horizon(ctx, atomic="sm") +me_horiz <- horizon(ctx, atomic="me") +bi_horiz <- horizon(ctx, atomic="bi") + +# Evaluate at specific points +sm_horiz(c(0, 25, 50, 75, 100)) +me_horiz(c(0, 25, 50, 75, 100)) +bi_horiz(c(0, 25, 50, 75, 100)) +``` + +Linguistic hedges can be applied to modify atomic expressions: + +```{r} +# Create "very small" expression +ve_hedge <- hedge("ve") +ve_sm <- lingexpr(ctx, atomic="sm", hedge="ve") +ve_sm(c(0, 10, 20, 30, 40)) + +# Create "roughly medium" expression +ro_me <- lingexpr(ctx, atomic="me", hedge="ro") +ro_me(c(30, 40, 50, 60, 70)) +``` + +### 5. Data Transformation + +The package provides functions to transform data into fuzzy sets: + +```{r} +# Transform logical data +logvec <- c(TRUE, FALSE, TRUE, TRUE) +lcut(logvec) + +# Transform factor data +position <- factor(c("worker", "manager", "worker", "accountant")) +lcut(position) + +# Transform numeric data with linguistic expressions +age <- c(25, 45, 32, 58) +lcut(age, context=ctx3(low=0, high=100)) +``` + +For data frames, multiple variables can be transformed at once: + +```{r} +data <- data.frame( + position = position, + age = age, + employed = logvec +) + +employees <- lcut(data, + context = ctx3(low=0, high=100), + atomic = c("sm", "me", "bi"), + hedges = c("ve", "-", "ro")) + +colnames(employees) +``` + +## Next Steps + +This introduction covers the basic features of the `lfl` package. For more detailed information, see the following vignettes: + +- **Fuzzy Algebras and Missing Values**: Detailed guide on fuzzy algebras +- **Composition of Fuzzy Relations**: Working with fuzzy relation compositions +- **Linguistic Expressions**: Creating and working with evaluative linguistic expressions +- **Perception-based Logical Deduction**: Using PbLD for fuzzy inference +- **Fuzzy Rule-Based Ensemble**: Time series forecasting with FRBE + +## References + +- Novák, V. (2008). A comprehensive theory of trichotomous evaluative linguistic expressions. *Fuzzy Sets and Systems*, 159(22), 2939-2969. +- Burda, M., & Štěpnička, M. (2017). lfl: An R Package for Linguistic Fuzzy Logic. *Manuscript*. diff --git a/vignettes/linguistic-expressions.Rmd b/vignettes/linguistic-expressions.Rmd new file mode 100644 index 0000000..b166d7b --- /dev/null +++ b/vignettes/linguistic-expressions.Rmd @@ -0,0 +1,373 @@ +--- +title: "Linguistic Expressions" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Linguistic Expressions} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + fig.width = 6, + fig.height = 4 +) +``` + +```{r setup} +library(lfl) +library(ggplot2) +``` + +# Evaluative Linguistic Expressions + +**Evaluative linguistic expressions** are a central concept in linguistic fuzzy logic. These are expressions of the form: + +> ⟨linguistic hedge⟩ ⟨atomic expression⟩ + +Examples include: *"very small"*, *"roughly medium"*, *"extremely big"*. + +## Context Definition + +Before creating linguistic expressions, we need to define a **context** that specifies the universe of discourse: + +### Three-Point Context (ctx3) + +The most basic context uses three points: low, center, and high: + +```{r} +# Default context: [0, 0.5, 1] +ctx <- ctx3() +print(ctx) + +# Custom context +ctx_age <- ctx3(low=0, center=50, high=100) +print(ctx_age) + +# Context for prices +ctx_price <- ctx3(low=5, center=100, high=1000) +print(ctx_price) +``` + +### Five-Point Context (ctx5) + +For more granularity, use a five-point context: + +```{r} +ctx <- ctx5(low=0, center=50, high=100) +print(ctx) + +# With custom positions +ctx <- ctx5(low=0, lowerCenter=25, center=50, upperCenter=75, high=100) +print(ctx) +``` + +### Bilateral Contexts + +For domains with negative and positive values: + +```{r} +# Bilateral three-point context +ctx <- ctx3bilat(negMax=-100, origin=0, posMax=100) +print(ctx) + +# Bilateral five-point context +ctx <- ctx5bilat(negMax=-100, origin=0, posMax=100) +print(ctx) +``` + +## Automatic Context Creation + +The `minmax()` function creates a context automatically from data: + +```{r} +# Generate random data +data <- runif(n=100, min=20, max=5000) +summary(data) + +# Create context from data +ctx <- minmax(data, type="ctx3") +print(ctx) + +# With custom center +ctx <- minmax(data, type="ctx3", center=1000) +print(ctx) +``` + +## Atomic Expressions + +Atomic expressions are the basic building blocks. There are three standard atomic expressions: + +- **"sm"** (small) +- **"me"** (medium) +- **"bi"** (big) + +Create atomic expressions using `horizon()`: + +```{r} +ctx <- ctx3(low=0, center=50, high=100) + +# Create small horizon +sm_horiz <- horizon(ctx, atomic="sm") +sm_horiz(c(0, 25, 50, 75, 100)) + +# Create medium horizon +me_horiz <- horizon(ctx, atomic="me") +me_horiz(c(0, 25, 50, 75, 100)) + +# Create big horizon +bi_horiz <- horizon(ctx, atomic="bi") +bi_horiz(c(0, 25, 50, 75, 100)) +``` + +### Visualizing Atomic Expressions + +```{r} +ctx <- ctx3() + +sm_horiz <- horizon(ctx, atomic="sm") +me_horiz <- horizon(ctx, atomic="me") +bi_horiz <- horizon(ctx, atomic="bi") + +ggplot(data = data.frame(x = 0), mapping = aes(x = x)) + + stat_function(aes(color="small"), fun = sm_horiz) + + stat_function(aes(color="medium"), fun = me_horiz) + + stat_function(aes(color="big"), fun = bi_horiz) + + xlim(-0.5, 1.5) + + labs(x="Value", y="Membership Degree", colour="Atomic\nExpression") + + theme_minimal() +``` + +## Linguistic Hedges + +Linguistic hedges modify atomic expressions to create nuanced meanings. Standard hedges include: + +- **"ex"** (extremely) +- **"si"** (significantly) +- **"ve"** (very) +- **"-"** (no hedge, just the atomic) +- **"ml"** (more or less) +- **"ro"** (roughly) +- **"qr"** (quite roughly) +- **"vr"** (very roughly) + +### Creating Hedged Expressions + +```{r} +ctx <- ctx3(low=0, center=50, high=100) + +# Create "very small" +ve_hedge <- hedge("ve") +sm_horiz <- horizon(ctx, atomic="sm") +ve_sm <- function(x) ve_hedge(sm_horiz(x)) +ve_sm(c(0, 10, 20, 30, 40)) +``` + +### Using lingexpr() Function + +A more convenient way to create linguistic expressions: + +```{r} +# "very small" +ve_sm <- lingexpr(ctx, atomic="sm", hedge="ve") +ve_sm(c(0, 10, 20, 30, 40)) + +# "roughly medium" +ro_me <- lingexpr(ctx, atomic="me", hedge="ro") +ro_me(c(30, 40, 50, 60, 70)) + +# Just "small" (no hedge) +sm <- lingexpr(ctx, atomic="sm", hedge="-") +sm(c(0, 10, 20, 30, 40)) +``` + +### Visualizing Hedged Expressions for "Small" + +```{r} +ctx <- ctx3() + +hedges <- c("ex", "si", "ve", "-", "ml", "ro", "qr", "vr") +colors <- c("ex.sm"="red", "si.sm"="orange", "ve.sm"="yellow", + "-.sm"="green", "ml.sm"="cyan", "ro.sm"="blue", + "qr.sm"="purple", "vr.sm"="magenta") + +plot_data <- data.frame(x = seq(0, 0.7, by=0.01)) +for (h in hedges) { + expr <- lingexpr(ctx, atomic="sm", hedge=h) + col_name <- paste(h, "sm", sep=".") + plot_data[[col_name]] <- expr(plot_data$x) +} + +library(tidyr) +plot_long <- pivot_longer(plot_data, -x, names_to="expression", values_to="degree") + +ggplot(plot_long, aes(x=x, y=degree, color=expression)) + + geom_line(linewidth=1) + + labs(x="Value", y="Membership Degree", title="Hedged 'Small' Expressions") + + theme_minimal() + + theme(legend.position="right") +``` + +### Visualizing Hedged Expressions for "Big" + +```{r} +plot_data <- data.frame(x = seq(0.3, 1, by=0.01)) +for (h in hedges) { + expr <- lingexpr(ctx, atomic="bi", hedge=h) + col_name <- paste(h, "bi", sep=".") + plot_data[[col_name]] <- expr(plot_data$x) +} + +plot_long <- pivot_longer(plot_data, -x, names_to="expression", values_to="degree") + +ggplot(plot_long, aes(x=x, y=degree, color=expression)) + + geom_line(linewidth=1) + + labs(x="Value", y="Membership Degree", title="Hedged 'Big' Expressions") + + theme_minimal() + + theme(legend.position="right") +``` + +## Custom Membership Functions + +Besides linguistic expressions, you can define custom membership functions using `triangular()` and `raisedcosine()`: + +### Triangular Membership Functions + +```{r} +# Standard triangular function +tri <- triangular(0, 0.5, 1) +tri(seq(from = 0, to = 1, by = 0.2)) + +# Left-unbounded triangular function +tri_left <- triangular(-Inf, 0.5, 1) +tri_left(c(0, 0.25, 0.5, 0.75, 1)) + +# Right-unbounded triangular function +tri_right <- triangular(0, 0.5, Inf) +tri_right(c(0, 0.25, 0.5, 0.75, 1)) +``` + +Visualizing triangular functions: + +```{r} +ggplot(data = data.frame(x = 0), mapping = aes(x = x)) + + stat_function(aes(color="bounded"), fun = triangular(0, 0.5, 1)) + + stat_function(aes(color="left-unbounded"), fun = triangular(-Inf, 0.5, 1), + linetype="dashed") + + stat_function(aes(color="right-unbounded"), fun = triangular(0, 0.5, Inf), + linetype="dotted") + + xlim(-0.2, 1.2) + + labs(x="Value", y="Membership Degree", colour="Type") + + theme_minimal() +``` + +### Raised Cosine Membership Functions + +```{r} +# Standard raised cosine function +rcos <- raisedcosine(0, 0.5, 1) +rcos(seq(from = 0, to = 1, by = 0.2)) + +# Left-unbounded +rcos_left <- raisedcosine(-Inf, 0.5, 1) + +# Right-unbounded +rcos_right <- raisedcosine(0, 0.5, Inf) +``` + +Visualizing raised cosine functions: + +```{r} +ggplot(data = data.frame(x = 0), mapping = aes(x = x)) + + stat_function(aes(color="bounded"), fun = raisedcosine(0, 0.5, 1)) + + stat_function(aes(color="left-unbounded"), fun = raisedcosine(-Inf, 0.5, 1), + linetype="dashed") + + stat_function(aes(color="right-unbounded"), fun = raisedcosine(0, 0.5, Inf), + linetype="dotted") + + xlim(-0.2, 1.2) + + labs(x="Value", y="Membership Degree", colour="Type") + + theme_minimal() +``` + +## Transforming Data with Linguistic Expressions + +The `lcut()` function transforms data into fuzzy sets using linguistic expressions: + +### Logical Data + +```{r} +logvec <- c(TRUE, FALSE, TRUE, TRUE) +lcut(logvec) +lcut(logvec, name="employed") +``` + +### Factor Data + +```{r} +position <- factor(c("worker", "manager", "worker", "accountant")) +lcut(position) +``` + +### Numeric Data + +```{r} +age <- c(25, 45, 32, 58) +lcut(age, context=ctx3(low=0, high=100)) +``` + +### Data Frames + +Transform entire data frames: + +```{r} +data <- data.frame( + position = position, + age = age, + employed = logvec +) + +employees <- lcut(data, + context = ctx3(low=0, high=100), + atomic = c("sm", "me", "bi"), + hedges = c("ve", "-", "ro")) + +# View variable names +vars(employees) + +# View specifications +head(specs(employees)) +``` + +Custom contexts for different variables: + +```{r} +data$salary <- c(25000, 65000, 42000, 89000) + +employees <- lcut(data, + context = list(age=ctx3(low=0, high=100), + salary=ctx3(low=10000, high=100000)), + atomic = list(salary=c("sm", "bi")), + hedges = list(age=c("ve", "-", "ro"), + salary=c("ex", "ve", "-", "ro"))) + +colnames(employees) +``` + +## Summary + +Evaluative linguistic expressions provide a powerful and intuitive way to work with vague concepts in fuzzy logic. The `lfl` package offers: + +- Flexible context definitions (ctx3, ctx5, bilateral) +- Standard atomic expressions (small, medium, big) +- Rich set of linguistic hedges (extremely, very, roughly, etc.) +- Custom membership functions (triangular, raised cosine) +- Easy data transformation with `lcut()` + +These tools enable natural expression of imprecise concepts in data analysis and fuzzy reasoning. + +## References + +- Novák, V. (2008). A comprehensive theory of trichotomous evaluative linguistic expressions. *Fuzzy Sets and Systems*, 159(22), 2939-2969. +- Novák, V., Perfilieva, I., & Dvořák, A. (2016). Insight into Fuzzy Modeling. *Wiley*. diff --git a/vignettes/pbld.Rmd b/vignettes/pbld.Rmd new file mode 100644 index 0000000..47311d0 --- /dev/null +++ b/vignettes/pbld.Rmd @@ -0,0 +1,337 @@ +--- +title: "Perception-based Logical Deduction (PbLD)" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Perception-based Logical Deduction (PbLD)} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +```{r setup} +library(lfl) +``` + +# Perception-based Logical Deduction (PbLD) + +**Perception-based Logical Deduction (PbLD)** is an inference method specifically designed for linguistic fuzzy logic. Unlike traditional fuzzy inference methods (like Mamdani-Assilian), PbLD considers rule specificity and fires only the most specific rules that match the input. + +## Key Concepts + +### Rule Specificity + +In PbLD, a rule with antecedent "age is **very small**" is more specific than a rule with "age is **small**" because the "very small" fuzzy set is contained within the "small" fuzzy set. + +When multiple rules fire, PbLD: +1. Selects the most specific rules +2. Aggregates only those selected rules' consequents +3. Defuzzifies the result + +This allows for discontinuous control surfaces and more nuanced decision-making. + +## Mining Association Rules + +Before performing inference, we typically mine fuzzy association rules from data using `searchrules()`. + +### Example: CO2 Dataset + +Let's use the built-in CO2 dataset to demonstrate PbLD: + +```{r} +# View the data +head(CO2, n=4) +``` + +### Transform Data to Fuzzy Sets + +```{r} +# Create context for uptake variable +uptakeContext <- ctx3(7, 28.3, 46) + +# Transform data to linguistic expressions +d <- lcut(CO2, context=list(uptake=uptakeContext)) + +# View column names +head(colnames(d), n=10) +``` + +### Split Data for Training and Testing + +```{r} +set.seed(123) +testingIndices <- sort(sample(seq_len(nrow(d)), 10)) +print(testingIndices) + +training <- d[-testingIndices, ] +testing <- d[testingIndices, ] +``` + +### Mine Association Rules + +```{r} +# Search for rules predicting uptake +rb <- searchrules(training, + lhs = which(vars(d) != "uptake"), + rhs = which(vars(d) == "uptake"), + minConfidence = 0.5) + +# View some rules +print(rb) +``` + +### View Rule Details + +```{r} +# Convert to data frame for inspection +rule_df <- as.data.frame(rb) +head(rule_df[, c("support", "confidence")], n=5) +``` + +### Extract Antecedents and Consequents + +```{r} +# View first few antecedents +antecedents(rb)[1:3] + +# View first few consequents +consequents(rb)[1:3] +``` + +## Performing PbLD Inference + +### Prepare Output Space + +```{r} +# Create a discrete universe for output (uptake values) +v <- seq(uptakeContext[1], uptakeContext[3], length.out=1000) + +# Transform to fuzzy sets +p <- lcut(v, name="uptake", context=uptakeContext) +``` + +### Global Defuzzification + +Use all rules for each prediction: + +```{r} +# Perform PbLD with global defuzzification +predictions_global <- pbld(testing, rb, p, v, type="global") +predictions_global +``` + +### Local Defuzzification (Default) + +Consider only the most specific rules for each instance: + +```{r} +# Perform PbLD with local defuzzification (default) +predictions_local <- pbld(testing, rb, p, v) +predictions_local +``` + +## Custom Rule Bases + +You can also manually define rules for inference: + +```{r} +# Define custom rules +rules <- list( + c("me.uptake", "Plant=Mc1", "sm.conc"), + c("sm.uptake", "Type=Mississippi"), + c("bi.uptake", "Treatment=nonchilled", "ro.me.conc") +) + +# Perform inference with custom rules +pbld(testing, rules, p, v) +``` + +## The Fire Function + +The `fire()` function determines which rules fire and to what degree: + +```{r} +# Example input data +x <- 1:10 / 10 +names(x) <- letters[1:10] +print(x) + +# Define rules +rules <- list( + c("a", "c", "e"), + c("b"), + c("d", "a"), + c("c", "a", "b") +) + +# Fire rules (show both firing degree and antecedent degree) +fire(x, rules, tnorm="goguen", onlyAnte=FALSE) +``` + +The output shows: +- `$rules`: Which rules fire +- `$degrees`: Firing degree of each rule +- `$ante`: Antecedent satisfaction degree + +## Perception Function + +The `perceive()` function selects the most specific rules: + +```{r} +# Fire all rules first +fired <- fire(x, rules, tnorm="goedel") + +# Perceive (select most specific) +perceived <- perceive(x, rules, fired$degrees) +perceived +``` + +## Reducing Rule Bases + +The `reduce()` function removes redundant rules: + +```{r} +# Example with employees data +position <- factor(c("worker", "manager", "worker", "accountant")) +age <- c(25, 45, 32, 58) +logvec <- c(TRUE, FALSE, TRUE, TRUE) + +data <- data.frame( + position = position, + age = age, + employed = logvec +) + +employees <- lcut(data, + context = ctx3(low=0, high=100), + atomic = c("sm", "me", "bi"), + hedges = c("ve", "-", "ro")) + +# Search for rules +rb <- searchrules(employees, + lhs = seq_len(ncol(employees)), + rhs = seq_len(ncol(employees)), + minSupport = 0.5, + minConfidence = 0.8, + maxLength = 3) + +# Reduce rule base (keep rules covering at least 90% of data) +reduced_rb <- reduce(employees, rb, 0.9) +print(reduced_rb) +``` + +## Complete Example: Iris Classification + +Let's use PbLD for classifying iris species: + +```{r} +# View data +summary(iris) +``` + +### Transform Data + +```{r} +# Transform to linguistic expressions +ldata <- lcut(iris, + context = function(x) minmax(x, type="ctx5"), + hedges = "-") + +# View some column names +head(colnames(ldata), n=10) +``` + +### Mine Rules + +```{r} +# Search for rules predicting species +lrules <- searchrules(ldata, + lhs = grep("^Species=", colnames(ldata), invert=TRUE), + rhs = grep("^Species=", colnames(ldata)), + minSupport = 0.05, + minConfidence = 0.8, + n = 0, # search for all rules + maxLength = 5) + +# View top rules by confidence +ldf <- as.data.frame(lrules) +ldf <- ldf[order(ldf$confidence, decreasing=TRUE), ] +head(ldf[, c('support', 'confidence')], n=8) +``` + +### Reduce Rule Base + +```{r} +# Keep only non-redundant rules +r <- reduce(ldata, rules=lrules, ratio=1) + +# View kept rules +rownames(as.data.frame(r)) +``` + +## Comparison with Traditional Methods + +### PbLD vs. Mamdani-Assilian + +| Aspect | PbLD | Mamdani-Assilian | +|--------|------|------------------| +| Rule aggregation | Selects most specific rules | Aggregates all firing rules | +| Implication | Residuated implication | Min or product implication | +| Output | Can be discontinuous | Usually smooth | +| Rule base | List of rules | Conjunction of rules | +| Specificity | Explicitly considered | Not considered | + +### Advantages of PbLD + +1. **Natural rule interpretation**: Rules with more specific antecedents take precedence +2. **Discontinuous outputs**: Allows for abrupt changes when appropriate +3. **Efficient**: Only most relevant rules are evaluated +4. **Linguistic**: Designed specifically for evaluative linguistic expressions + +## Aggregation Methods + +The `aggregateConsequents()` function combines consequents from fired rules: + +```{r} +# Example consequents from multiple rules +conseq <- matrix(c( + 0.8, 0.2, 0.1, + 0.1, 0.9, 0.3, + 0.2, 0.3, 0.9 +), nrow=3, byrow=TRUE) + +# Firing degrees +degrees <- c(0.7, 0.5, 0.6) + +# Aggregate using different methods +aggregateConsequents(conseq, degrees, algo="lukasiewicz") +``` + +## Summary + +PbLD provides a sophisticated inference method for linguistic fuzzy logic: + +- **Specificity-aware**: Prefers more specific rules +- **Flexible**: Supports both global and local defuzzification +- **Integrated**: Works seamlessly with `searchrules()` for data-driven rule extraction +- **Efficient**: Selective rule firing reduces computational cost +- **Linguistic**: Designed for evaluative linguistic expressions + +Key functions: +- `searchrules()`: Mine association rules +- `pbld()`: Perform PbLD inference +- `fire()`: Determine which rules fire +- `perceive()`: Select most specific rules +- `aggregateConsequents()`: Combine consequent fuzzy sets +- `reduce()`: Remove redundant rules + +## References + +- Novák, V., & Lehmke, S. (2006). Logical structure of fuzzy IF-THEN rules. *Fuzzy Sets and Systems*, 157(15), 2003-2029. +- Dvořák, A., & Novák, V. (2011). Formal theories and linguistic descriptions. *Fuzzy Sets and Systems*, 143(1), 169-188. +- Štěpnička, M., & Dvořák, A. (2013). Fuzzy relational compositions based on generalized quantifiers. In *Proceedings of EUSFLAT*.