2020-05-28 00:02:08 +02:00
|
|
|
#lang racket
|
|
|
|
|
|
|
|
;;; dds/functions
|
|
|
|
|
|
|
|
;;; This modules provides some definitions for working with functions:
|
|
|
|
;;; tabulating, (re)constructing from tables, generating random
|
|
|
|
;;; functions, etc. Some definitions of particular kinds of functions
|
|
|
|
;;; are also provided (threshold Boolean functions, etc.).
|
2020-05-28 00:24:17 +02:00
|
|
|
|
|
|
|
(require "utils.rkt")
|
|
|
|
|
2022-03-05 21:37:17 +01:00
|
|
|
(module typed typed/racket
|
2022-03-06 21:45:21 +01:00
|
|
|
(require "utils.rkt"
|
2022-03-31 23:45:38 +02:00
|
|
|
syntax/parse/define
|
2022-03-06 22:53:33 +01:00
|
|
|
(only-in typed/racket/unsafe unsafe-provide)
|
2022-03-06 21:45:21 +01:00
|
|
|
(for-syntax syntax/parse))
|
2022-03-05 21:37:17 +01:00
|
|
|
|
2022-03-06 19:54:05 +01:00
|
|
|
(provide
|
2022-03-20 20:42:29 +01:00
|
|
|
pseudovariadic-lambda pvλ pseudovariadic-define pvdefine
|
2022-03-21 00:04:21 +01:00
|
|
|
tabulate* tabulate*/strict tabulate*/pv tabulate tabulate/strict tabulate/pv)
|
2022-03-06 19:54:05 +01:00
|
|
|
|
2022-03-06 22:53:33 +01:00
|
|
|
(unsafe-provide
|
2022-03-06 23:39:51 +01:00
|
|
|
(rename-out [tabulate* tabulate*/untyped]
|
|
|
|
[tabulate tabulate/untyped]))
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-03-05 21:37:17 +01:00
|
|
|
(module+ test
|
|
|
|
(require typed/rackunit))
|
2022-03-06 19:54:05 +01:00
|
|
|
|
2022-03-20 20:42:29 +01:00
|
|
|
(begin-for-syntax
|
|
|
|
(require racket (for-syntax syntax/parse))
|
|
|
|
|
|
|
|
(define (make-pseudovariadic-core args bodies tag-stx)
|
|
|
|
(define nargs-stx (datum->syntax args (length (syntax->list args))))
|
|
|
|
#`(λ xs
|
|
|
|
(match xs
|
|
|
|
[(list #,@args) #,@bodies]
|
|
|
|
[_ (error #,tag-stx "invalid arity, expected ~a argument(s)" #,nargs-stx)])))
|
|
|
|
|
|
|
|
(define (make-pseudovariadic-lambda stx)
|
|
|
|
(syntax-parse stx
|
|
|
|
[(_ (args:id ...) bodies:expr ...)
|
|
|
|
(make-pseudovariadic-core #'(args ...)
|
|
|
|
#'(bodies ...)
|
|
|
|
(datum->syntax stx ''pseudovariadic-lambda))]))
|
|
|
|
|
|
|
|
(define (make-pseudovariadic-define stx)
|
|
|
|
(syntax-parse stx
|
|
|
|
[(_ (name:id args:id ...) bodies:expr ...)
|
|
|
|
#`(define name
|
|
|
|
#,(make-pseudovariadic-core
|
|
|
|
#'(args ...)
|
|
|
|
#'(bodies ...)
|
|
|
|
(datum->syntax #'name `(quote ,(syntax->datum #'name)))))])))
|
|
|
|
|
|
|
|
(define-syntax (pseudovariadic-lambda stx) (make-pseudovariadic-lambda stx))
|
|
|
|
(define-syntax (pvλ stx) (make-pseudovariadic-lambda stx))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "pseudovariadic-lambda")
|
|
|
|
(check-false ((pseudovariadic-lambda (x y) (and x y)) #t #f))
|
|
|
|
(check-false ((pvλ (x y) (and x y)) #t #f))
|
|
|
|
(check-exn exn:fail? (λ () ((pseudovariadic-lambda (x y) (and x y)) #t #f #f)))
|
|
|
|
(check-exn exn:fail? (λ () ((pvλ (x y) (and x y)) #t #f #f))))
|
|
|
|
|
|
|
|
(define-syntax (pseudovariadic-define stx) (make-pseudovariadic-define stx))
|
|
|
|
(define-syntax (pvdefine stx) (make-pseudovariadic-define stx))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "pseudovariadic-define")
|
|
|
|
(: f (-> Boolean * Boolean))
|
|
|
|
(pseudovariadic-define (f x y) (and x y))
|
|
|
|
(check-false (f #t #f))
|
|
|
|
(check-exn exn:fail? (λ () (f #t #f #f)))
|
|
|
|
|
|
|
|
(: g (-> Boolean * Boolean))
|
|
|
|
(pvdefine (g x y) (and x y))
|
|
|
|
(check-false (g #t #f))
|
|
|
|
(check-exn exn:fail? (λ () (g #t #f #f))))
|
|
|
|
|
2022-03-31 23:45:38 +02:00
|
|
|
(define-syntax-parse-rule (make-tabulate* name:id row-op)
|
|
|
|
(define (name funcs doms)
|
|
|
|
(for/list ([xs (in-list (apply cartesian-product doms))])
|
|
|
|
(row-op xs (for/list ([f funcs]) : (Listof b)
|
|
|
|
(apply f xs))))))
|
2022-03-06 21:45:21 +01:00
|
|
|
|
2022-03-06 19:54:05 +01:00
|
|
|
(: tabulate* (All (b a ... ) (-> (Listof (-> a ... b)) (List (Listof a) ... a)
|
|
|
|
(Listof (Listof (U Any b))))))
|
2022-03-06 21:45:21 +01:00
|
|
|
(make-tabulate* tabulate* append)
|
2022-03-06 19:54:05 +01:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate*"
|
|
|
|
(check-equal? (tabulate*
|
|
|
|
(list (λ (x y) (and x y))
|
|
|
|
(λ (x y) (or x y)))
|
|
|
|
'((#f #t) (#f #t)))
|
|
|
|
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))
|
|
|
|
(check-equal? (tabulate* empty '((#f #t) (#f #t)))
|
|
|
|
'((#f #f) (#f #t) (#t #f) (#t #t)))))
|
|
|
|
|
|
|
|
(: tabulate*/strict (All (b a ...) (-> (Listof (-> a ... b)) (List (Listof a) ... a)
|
|
|
|
(Listof (List (List a ...) (Listof b))))))
|
2022-03-06 21:45:21 +01:00
|
|
|
(make-tabulate* tabulate*/strict list)
|
2022-03-06 19:54:05 +01:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate*/strict"
|
|
|
|
(check-equal? (tabulate*/strict
|
|
|
|
(list (λ (x y) (and x y))
|
|
|
|
(λ (x y) (or x y)))
|
|
|
|
'((#f #t) (#f #t)))
|
|
|
|
'(((#f #f) (#f #f)) ((#f #t) (#f #t)) ((#t #f) (#f #t)) ((#t #t) (#t #t))))))
|
2022-03-06 23:39:51 +01:00
|
|
|
|
2022-03-21 00:04:21 +01:00
|
|
|
|
|
|
|
(: tabulate*/pv (All (a b) (-> (Listof (-> a * b)) (Listof (Listof a))
|
|
|
|
(Listof (Listof (U a b))))))
|
|
|
|
(make-tabulate* tabulate*/pv append)
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate*/pv"
|
|
|
|
(check-equal? (tabulate*/pv (list (pvλ (x y) (and x y))
|
|
|
|
(pvλ (x y) (or x y)))
|
|
|
|
'((#f #t) (#f #t)))
|
|
|
|
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))))
|
|
|
|
|
2022-03-06 23:39:51 +01:00
|
|
|
(: tabulate (All (b a ...) (-> (-> a ... b) (List (Listof a) ... a)
|
|
|
|
(Listof (Listof (U Any b))))))
|
|
|
|
(define (tabulate func doms)
|
|
|
|
(tabulate* (list func) doms))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate"
|
|
|
|
(check-equal? (tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
|
|
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
|
|
|
|
|
|
|
(: tabulate/strict (All (b a ...) (-> (-> a ... b) (List (Listof a) ... a)
|
|
|
|
(Listof (List (List a ...) (Listof b))))))
|
|
|
|
(define (tabulate/strict func doms)
|
|
|
|
(tabulate*/strict (list func) doms))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate/strict"
|
|
|
|
(check-equal? (tabulate/strict (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
|
|
'(((#f #f) (#f)) ((#f #t) (#f)) ((#t #f) (#f)) ((#t #t) (#t))))))
|
2022-03-21 00:04:21 +01:00
|
|
|
|
|
|
|
(: tabulate/pv (All (a b) (-> (-> a * b) (Listof (Listof a))
|
|
|
|
(Listof (Listof (U a b))))))
|
|
|
|
(define (tabulate/pv func doms)
|
|
|
|
(tabulate*/pv (list func) doms))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate/pv"
|
|
|
|
(check-equal? (tabulate/pv (pvλ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
|
|
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
2022-03-05 21:37:17 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
(require 'typed)
|
2022-03-06 19:54:05 +01:00
|
|
|
(provide
|
2022-03-20 20:42:29 +01:00
|
|
|
pseudovariadic-lambda pvλ pseudovariadic-define pvdefine
|
2022-03-21 00:04:21 +01:00
|
|
|
tabulate* tabulate*/strict tabulate*/pv tabulate*/untyped
|
|
|
|
tabulate tabulate/strict tabulate/pv tabulate/untyped)
|
2022-03-05 21:37:17 +01:00
|
|
|
|
|
|
|
|
2020-05-28 00:24:17 +02:00
|
|
|
(provide
|
2020-06-03 22:51:06 +02:00
|
|
|
;; Structures
|
|
|
|
(contract-out
|
2020-07-07 22:45:19 +02:00
|
|
|
[struct tbf ((weights (vectorof number?)) (threshold number?))])
|
2020-05-28 00:24:17 +02:00
|
|
|
;; Functions
|
|
|
|
(contract-out
|
2022-02-13 20:09:50 +01:00
|
|
|
[tabulate/boolean (-> procedure? (listof (listof boolean?)))]
|
|
|
|
[tabulate*/boolean (-> (non-empty-listof procedure?) (listof (listof boolean?)))]
|
2020-06-10 23:25:39 +02:00
|
|
|
[tabulate/01 (-> procedure? (listof (listof (or/c 0 1))))]
|
|
|
|
[tabulate*/01 (-> (non-empty-listof procedure?) (listof (listof (or/c 0 1))))]
|
2020-05-28 00:24:17 +02:00
|
|
|
[table->function (-> (listof (*list/c any/c any/c)) procedure?)]
|
|
|
|
[table->function/list (-> (listof (*list/c any/c any/c)) procedure?)]
|
|
|
|
[enumerate-boolean-tables (-> number? (stream/c (listof (*list/c boolean? boolean?))))]
|
|
|
|
[enumerate-boolean-functions (-> number? (stream/c procedure?))]
|
|
|
|
[enumerate-boolean-functions/list (-> number? (stream/c procedure?))]
|
|
|
|
[random-boolean-table (-> number? (listof (*list/c boolean? boolean?)))]
|
|
|
|
[random-boolean-function (-> number? procedure?)]
|
2020-06-03 22:51:06 +02:00
|
|
|
[random-boolean-function/list (-> number? procedure?)]
|
|
|
|
[tbf-w (-> tbf? (vectorof number?))]
|
2020-06-10 23:51:01 +02:00
|
|
|
[tbf-θ (-> tbf? number?)]
|
2020-06-11 00:27:11 +02:00
|
|
|
[vector-boolean->01 (-> (vectorof boolean?) (vectorof (or/c 0 1)))]
|
2020-07-07 21:41:45 +02:00
|
|
|
[apply-tbf (-> tbf? (vectorof (or/c 0 1)) (or/c 0 1))]
|
2020-07-07 23:29:38 +02:00
|
|
|
[apply-tbf/boolean (-> tbf? (vectorof boolean?) boolean?)]
|
2020-07-09 00:45:00 +02:00
|
|
|
[list->tbf (-> (cons/c number? (cons/c number? (listof number?))) tbf?)]
|
2020-07-14 23:41:55 +02:00
|
|
|
[lists->tbfs (-> (listof (listof number?)) (listof tbf?))]
|
2020-07-11 00:46:58 +02:00
|
|
|
[read-org-tbfs (->* (string?) (#:headers boolean?) (listof tbf?))]
|
2020-07-12 00:18:25 +02:00
|
|
|
[tbf-tabulate* (-> (listof tbf?) (listof (listof (or/c 0 1))))]
|
2020-07-12 20:48:36 +02:00
|
|
|
[tbf-tabulate (-> tbf? (listof (listof (or/c 0 1))))]
|
2020-07-12 23:58:55 +02:00
|
|
|
[tbf-tabulate*/boolean (-> (listof tbf?) (listof (listof boolean?)))]
|
2020-07-14 23:37:38 +02:00
|
|
|
[sbf (-> (vectorof number?) tbf?)]
|
2020-07-14 23:55:55 +02:00
|
|
|
[list->sbf (-> (listof number?) sbf?)]
|
|
|
|
[read-org-sbfs (->* (string?) (#:headers boolean?) (listof sbf?))])
|
2020-07-12 23:50:51 +02:00
|
|
|
;; Predicates
|
|
|
|
(contract-out
|
|
|
|
[sbf? (-> any/c boolean?)]))
|
2020-05-28 00:24:17 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(require rackunit))
|
|
|
|
|
|
|
|
|
|
|
|
;;; ==========
|
|
|
|
;;; Tabulating
|
|
|
|
;;; ==========
|
|
|
|
|
|
|
|
;;; Like tabulate, but assumes the domains of all variables of the
|
|
|
|
;;; function are Boolean. func must have a fixed arity. It is an
|
|
|
|
;;; error to supply a function of variable arity.
|
|
|
|
(define (tabulate/boolean func)
|
2022-03-06 23:39:51 +01:00
|
|
|
(tabulate/untyped func (make-list (procedure-arity func) '(#f #t))))
|
2020-05-28 00:24:17 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate/boolean"
|
|
|
|
(check-equal? (tabulate/boolean (lambda (x y) (and x y)))
|
|
|
|
'((#f #f #f) (#f #t #f) (#t #f #f) (#t #t #t)))))
|
|
|
|
|
2020-05-31 23:31:54 +02:00
|
|
|
;;; Like tabulate/boolean, but takes a list of functions of the same
|
|
|
|
;;; arity.
|
|
|
|
(define (tabulate*/boolean funcs)
|
|
|
|
(define doms (make-list (procedure-arity (car funcs)) '(#f #t)))
|
2022-03-06 22:58:16 +01:00
|
|
|
(tabulate*/untyped funcs doms))
|
2020-05-31 23:31:54 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate*/boolean"
|
|
|
|
(check-equal? (tabulate*/boolean `(,(λ (x y) (and x y))
|
|
|
|
,(λ (x y) (or x y))))
|
|
|
|
'((#f #f #f #f) (#f #t #f #t) (#t #f #f #t) (#t #t #t #t)))))
|
|
|
|
|
2020-06-10 23:25:39 +02:00
|
|
|
;;; Like tabulate, but assumes the domains of all variables of the
|
|
|
|
;;; function are {0, 1}. func must have a fixed arity. It is an
|
|
|
|
;;; error to supply a function of variable arity.
|
|
|
|
(define (tabulate/01 func)
|
2022-03-06 23:39:51 +01:00
|
|
|
(tabulate/untyped func (make-list (procedure-arity func) '(0 1))))
|
2020-06-10 23:25:39 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate/01"
|
|
|
|
(check-equal? (tabulate/01 (λ (x y) (modulo (+ x y) 2)))
|
|
|
|
'((0 0 0) (0 1 1) (1 0 1) (1 1 0)))))
|
|
|
|
|
|
|
|
;;; Like tabulate/01, but takes a list of functions of the same arity.
|
|
|
|
(define (tabulate*/01 funcs)
|
2022-03-06 22:58:16 +01:00
|
|
|
(tabulate*/untyped funcs (make-list (procedure-arity (car funcs)) '(0 1))))
|
2020-06-10 23:25:39 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tabulate*/01"
|
|
|
|
(check-equal? (tabulate*/01 `(,(λ (x y) (min x y)) ,(λ (x y) (max x y))))
|
|
|
|
'((0 0 0 0) (0 1 0 1) (1 0 0 1) (1 1 1 1)))))
|
2020-05-28 00:24:17 +02:00
|
|
|
|
|
|
|
;;; ======================
|
|
|
|
;;; Constructing functions
|
|
|
|
;;; ======================
|
|
|
|
|
|
|
|
;;; Given a table like the one produced by the tabulate functions,
|
|
|
|
;;; creates a function which has this behaviour.
|
|
|
|
;;;
|
|
|
|
;;; More exactly, the input is a list of lists of values. All but the
|
|
|
|
;;; last elements of every list give the values of the parameters of
|
|
|
|
;;; the function, while the the last element of every list gives the
|
|
|
|
;;; value of the function. Thus, every list should have at least two
|
|
|
|
;;; elements.
|
|
|
|
;;;
|
|
|
|
;;; The produced function is implemented via lookups in hash tables,
|
|
|
|
;;; meaning that it may be sometimes more expensive to compute than by
|
|
|
|
;;; using an direct symbolic implementation.
|
|
|
|
(define (table->function table)
|
|
|
|
(let ([func (table->function/list table)])
|
|
|
|
(λ args (func args))))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "table->function"
|
|
|
|
(define negation (table->function '((#t #f) (#f #t))))
|
|
|
|
(check-true (negation #f))
|
|
|
|
(check-false (negation #t))))
|
|
|
|
|
|
|
|
;;; Like table->function, but the produced function accepts a single
|
|
|
|
;;; list of arguments instead of individual arguments.
|
|
|
|
(define (table->function/list table)
|
|
|
|
((curry hash-ref)
|
|
|
|
(for/hash ([line table])
|
|
|
|
(let-values ([(x fx) (split-at-right line 1)])
|
|
|
|
(values x (car fx))))))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "table->function/list"
|
|
|
|
(define negation/list (table->function/list '((#t #f) (#f #t))))
|
|
|
|
(check-true (negation/list '(#f)))
|
|
|
|
(check-false (negation/list '(#t)))))
|
|
|
|
|
|
|
|
;;; Returns the stream of the truth tables of all Boolean functions of
|
|
|
|
;;; a given arity.
|
|
|
|
;;;
|
|
|
|
;;; There are 2^(2^n) Boolean functions of arity n.
|
|
|
|
(define (enumerate-boolean-tables n)
|
|
|
|
(let ([inputs (boolean-power/stream n)]
|
|
|
|
[outputs (boolean-power/stream (expt 2 n))])
|
|
|
|
(for/stream ([out (in-stream outputs)])
|
|
|
|
(for/list ([in (in-stream inputs)] [o out])
|
|
|
|
(append in (list o))))))
|
|
|
|
|
|
|
|
;;; Returns the stream of all Boolean functions of a given arity.
|
|
|
|
;;;
|
|
|
|
;;; There are 2^(2^n) Boolean functions of arity n.
|
|
|
|
(define (enumerate-boolean-functions n)
|
|
|
|
(stream-map table->function (enumerate-boolean-tables n)))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "enumerate-boolean-tables"
|
|
|
|
(define f1 (stream-first (enumerate-boolean-functions 1)))
|
|
|
|
(check-false (f1 #f))
|
|
|
|
(check-false (f1 #t))))
|
|
|
|
|
|
|
|
;;; Returns the stream of all Boolean functions of a given arity. As
|
|
|
|
;;; different from the functions returned by
|
|
|
|
;;; enumerate-boolean-functions, the functions take lists of arguments
|
|
|
|
;;; instead of n arguments.
|
|
|
|
;;;
|
|
|
|
;;; There are 2^(2^n) Boolean functions of arity n.
|
|
|
|
(define (enumerate-boolean-functions/list n)
|
|
|
|
(stream-map table->function/list (enumerate-boolean-tables n)))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "enumerate-boolean-functions/list"
|
|
|
|
(define f1/list (stream-first (enumerate-boolean-functions/list 1)))
|
|
|
|
(check-false (f1/list '(#f)))
|
|
|
|
(check-false (f1/list '(#t)))))
|
|
|
|
|
|
|
|
|
|
|
|
;;; ================
|
|
|
|
;;; Random functions
|
|
|
|
;;; ================
|
|
|
|
|
|
|
|
;;; Generates a random truth table for a Boolean function of arity n.
|
|
|
|
(define (random-boolean-table n)
|
|
|
|
(define/match (num->bool x) [(0) #f] [(1) #t])
|
|
|
|
(define inputs (boolean-power n))
|
|
|
|
(define outputs (stream-take (in-random 2) (expt 2 n)))
|
|
|
|
(for/list ([i inputs] [o outputs])
|
|
|
|
(append i (list (num->bool o)))))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "random-boolean-table"
|
|
|
|
(random-seed 0)
|
|
|
|
(check-equal? (random-boolean-table 2) '((#f #f #t) (#f #t #t) (#t #f #f) (#t #t #f)))))
|
|
|
|
|
|
|
|
;;; Generates a random Boolean function of arity n.
|
|
|
|
(define random-boolean-function (compose table->function random-boolean-table))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "random-boolean-function"
|
|
|
|
(define f (random-boolean-function 2))
|
|
|
|
(check-true (f #f #f)) (check-false (f #f #t))
|
|
|
|
(check-true (f #t #f)) (check-false (f #t #t))))
|
|
|
|
|
|
|
|
;;; Like random-boolean-function, but the constructed function takes a
|
|
|
|
;;; list of arguments.
|
|
|
|
(define random-boolean-function/list (compose table->function/list random-boolean-table))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "random-boolean-function/list"
|
|
|
|
(define f (random-boolean-function/list 2))
|
|
|
|
(check-false (f '(#f #f))) (check-true (f '(#f #t)))
|
|
|
|
(check-true (f '(#t #f))) (check-false (f '(#t #t)))))
|
2020-06-03 22:51:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
;;; ===========================
|
|
|
|
;;; Threshold Boolean functions
|
|
|
|
;;; ===========================
|
|
|
|
|
|
|
|
;;; A threshold Boolean function (TBF) is a pair (w, θ), where w is a
|
|
|
|
;;; vector of weights and θ is the threshold.
|
|
|
|
(struct tbf (weights threshold) #:transparent)
|
|
|
|
|
|
|
|
;;; Unicode shortcuts for accessing the elements of a TBF.
|
|
|
|
(define tbf-w tbf-weights)
|
|
|
|
(define tbf-θ tbf-threshold)
|
2020-06-10 23:51:01 +02:00
|
|
|
|
|
|
|
;;; Converts a Boolean vector to a 0-1 vector.
|
|
|
|
(define (vector-boolean->01 bool-v)
|
|
|
|
(vector-map any->01 bool-v))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "boolean->0-1"
|
|
|
|
(check-equal? (vector-boolean->01 #(#t #f #f)) #(1 0 0))))
|
2020-06-11 00:27:11 +02:00
|
|
|
|
|
|
|
;;; Applies the TBF to its inputs.
|
|
|
|
;;;
|
|
|
|
;;; Applying a TBF consists in multiplying the weights by the
|
|
|
|
;;; corresponding inputs and comparing the sum of the products to the
|
|
|
|
;;; threshold.
|
2020-07-07 23:25:51 +02:00
|
|
|
(define (apply-tbf tbf inputs)
|
2020-06-11 00:27:11 +02:00
|
|
|
(any->01
|
|
|
|
(>
|
|
|
|
;; The scalar product between the inputs and the weights
|
2020-07-07 21:41:45 +02:00
|
|
|
(for/sum ([x (in-vector inputs)]
|
2020-06-11 00:27:11 +02:00
|
|
|
[w (in-vector (tbf-w tbf))])
|
|
|
|
(* x w))
|
|
|
|
(tbf-θ tbf))))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "apply-tbf"
|
|
|
|
(define f1 (tbf #(2 -2) 1))
|
2020-07-07 21:41:45 +02:00
|
|
|
(check-equal? (tabulate/01 (λ (x y) (apply-tbf f1 (vector x y))))
|
2020-06-11 00:27:11 +02:00
|
|
|
'((0 0 0) (0 1 0) (1 0 1) (1 1 0)))))
|
|
|
|
|
|
|
|
;;; Like apply-tbf, but takes Boolean values as inputs and outputs a
|
|
|
|
;;; boolean value.
|
2020-07-07 21:41:45 +02:00
|
|
|
(define (apply-tbf/boolean tbf inputs)
|
|
|
|
(01->boolean (apply-tbf tbf (vector-map any->01 inputs))))
|
2020-06-11 00:27:11 +02:00
|
|
|
|
|
|
|
(module+ test
|
2020-07-07 23:24:34 +02:00
|
|
|
(test-case "apply-tbf/boolean"
|
|
|
|
(define f1 (tbf #(2 -2) 1))
|
|
|
|
(check-equal? (tabulate/boolean (λ (x y) (apply-tbf/boolean f1 (vector x y))))
|
|
|
|
'((#f #f #f) (#f #t #f) (#t #f #t) (#t #t #f)))))
|
2020-07-07 23:29:38 +02:00
|
|
|
|
|
|
|
;;; Converts a list of numbers to a TBF. The last element of the list
|
|
|
|
;;; is taken to be the threshold, while the other elements are taken
|
|
|
|
;;; to be the weights.
|
|
|
|
(define (list->tbf lst)
|
|
|
|
(define-values (w θ) (split-at-right lst 1))
|
|
|
|
(tbf (list->vector w) (car θ)))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "list->tbf"
|
|
|
|
(check-equal? (list->tbf '(1 2 3)) (tbf #(1 2) 3))))
|
2020-07-09 00:45:00 +02:00
|
|
|
|
2020-07-09 00:45:26 +02:00
|
|
|
;;; Reads a list of TBF from an Org-mode table read by
|
2020-07-09 00:45:00 +02:00
|
|
|
;;; read-org-sexp.
|
2020-07-14 23:41:55 +02:00
|
|
|
(define lists->tbfs ((curry map) list->tbf))
|
2020-07-09 00:45:00 +02:00
|
|
|
|
|
|
|
(module+ test
|
2020-07-09 00:53:16 +02:00
|
|
|
(test-case "read-tbfs"
|
2020-07-14 23:41:55 +02:00
|
|
|
(check-equal? (lists->tbfs '((1 2 3) (2 3 4)))
|
2020-07-09 00:45:00 +02:00
|
|
|
(list (tbf '#(1 2) 3) (tbf '#(2 3) 4)))))
|
2020-07-09 01:03:24 +02:00
|
|
|
|
|
|
|
;;; Reads a list of TBF from an Org-mode string containing a sexp,
|
|
|
|
;;; containing a list of lists of numbers. If headers is #t, drops
|
|
|
|
;;; the first list, supposing that it contains the headers of the
|
|
|
|
;;; table.
|
|
|
|
;;;
|
|
|
|
;;; The input is typically what read-org-sexp reads.
|
|
|
|
(define (read-org-tbfs str #:headers [headers #f])
|
|
|
|
(define sexp (read-org-sexp str))
|
|
|
|
(define sexp-clean (cond [headers (cdr sexp)] [else sexp]))
|
2020-07-14 23:41:55 +02:00
|
|
|
(lists->tbfs sexp-clean))
|
2020-07-09 01:03:24 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "read-org-tbfs"
|
|
|
|
(check-equal? (read-org-tbfs "((1 2 1) (1 0 1))")
|
|
|
|
(list (tbf '#(1 2) 1) (tbf '#(1 0) 1)))))
|
2020-07-11 00:46:58 +02:00
|
|
|
|
2020-07-12 00:18:25 +02:00
|
|
|
;;; Tabulates a list of TBFs.
|
|
|
|
;;;
|
|
|
|
;;; The result is a list of lists describing the truth table of the
|
|
|
|
;;; given TBFs. The first elements of each line give the values of
|
|
|
|
;;; the inputs, while the last elements give the values of each the
|
|
|
|
;;; functions corresponding to the input.
|
|
|
|
;;;
|
|
|
|
;;; All the TBFs in tbfs must have the same number of inputs as the
|
|
|
|
;;; first TBF in the list. This function does not check this
|
|
|
|
;;; condition.
|
|
|
|
(define (tbf-tabulate* tbfs)
|
|
|
|
(define funcs (for/list ([tbf tbfs])
|
|
|
|
(λ in (apply-tbf tbf (list->vector in)))))
|
|
|
|
(define nvars (vector-length (tbf-w (car tbfs))))
|
2022-03-06 22:58:16 +01:00
|
|
|
(tabulate*/untyped funcs (make-list nvars '(0 1))))
|
2020-07-12 00:18:25 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tbf-tabulate*"
|
|
|
|
(check-equal? (tbf-tabulate* (list (tbf #(2 2) 1) (tbf #(1 1) 1)))
|
|
|
|
'((0 0 0 0) (0 1 1 0) (1 0 1 0) (1 1 1 1)))))
|
|
|
|
|
2020-07-11 00:46:58 +02:00
|
|
|
;;; Tabulates a TBF.
|
2020-07-12 00:20:15 +02:00
|
|
|
(define tbf-tabulate (compose tbf-tabulate* list))
|
2020-07-11 00:46:58 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tbf-tabulate"
|
|
|
|
(check-equal? (tbf-tabulate (tbf #(1 2) 1))
|
|
|
|
'((0 0 0) (0 1 1) (1 0 0) (1 1 1)))))
|
2020-07-12 20:48:36 +02:00
|
|
|
|
|
|
|
;;; Tabulates a list of TBFs like tbf-boolean*, but uses Boolean
|
|
|
|
;;; values #f and #t instead of 0 and 1.
|
|
|
|
;;;
|
|
|
|
;;; All the TBFs in tbfs must have the same number of inputs as the
|
|
|
|
;;; first TBF in the list. This function does not check this
|
|
|
|
;;; condition.
|
|
|
|
(define (tbf-tabulate*/boolean tbfs)
|
|
|
|
(define funcs (for/list ([tbf tbfs])
|
|
|
|
(λ in (apply-tbf/boolean tbf (list->vector in)))))
|
|
|
|
(define nvars (vector-length (tbf-w (car tbfs))))
|
2022-03-06 22:58:16 +01:00
|
|
|
(tabulate*/untyped funcs (make-list nvars '(#f #t))))
|
2020-07-12 20:48:36 +02:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "tbf-tabulate*/boolean"
|
|
|
|
(check-equal? (tbf-tabulate*/boolean `(,(tbf #(1 2) 1)))
|
|
|
|
'((#f #f #f) (#f #t #t) (#t #f #f) (#t #t #t)))))
|
2020-07-12 23:50:51 +02:00
|
|
|
|
|
|
|
;;; A sign Boolean function (SBF) is a TBF whose threshold is 0.
|
|
|
|
(define sbf? (and/c tbf? (λ (x) (= 0 (tbf-θ x)))))
|
|
|
|
|
|
|
|
(module+ test
|
2020-07-23 00:21:34 +02:00
|
|
|
(test-case "sbf?"
|
|
|
|
(check-false (sbf? (tbf #(1 2) 3)))
|
|
|
|
(check-true (sbf? (tbf #(1 2) 0)))))
|
2020-07-12 23:58:55 +02:00
|
|
|
|
|
|
|
;;; Creates a TBF which is an SBF from a vector of weights.
|
|
|
|
(define (sbf w) (tbf w 0))
|
|
|
|
|
|
|
|
(module+ test
|
2020-07-23 00:21:34 +02:00
|
|
|
(test-case "sbf"
|
|
|
|
(check-equal? (sbf #(1 -1)) (tbf '#(1 -1) 0))))
|
2020-07-14 23:37:38 +02:00
|
|
|
|
|
|
|
;;; Converts a list of numbers to an SBF. The elements of the list
|
|
|
|
;;; are taken to be the weights of the SBF.
|
|
|
|
(define list->sbf (compose sbf list->vector))
|
|
|
|
|
|
|
|
(module+ test
|
2020-07-23 00:21:34 +02:00
|
|
|
(test-case "list->sbf"
|
|
|
|
(check-equal? (list->sbf '(1 -1)) (tbf '#(1 -1) 0))))
|
2020-07-14 23:55:55 +02:00
|
|
|
|
|
|
|
;;; Reads a list of SBF from an Org-mode string containing a sexp,
|
|
|
|
;;; containing a list of lists of numbers. If headers is #t, drops
|
|
|
|
;;; the first list, supposing that it contains the headers of the
|
|
|
|
;;; table.
|
|
|
|
;;;
|
|
|
|
;;; The input is typically what read-org-sexp reads.
|
|
|
|
(define (read-org-sbfs str #:headers [headers #f])
|
|
|
|
(define sexp (read-org-sexp str))
|
|
|
|
(define sexp-clean (cond [headers (cdr sexp)] [else sexp]))
|
|
|
|
(map list->sbf sexp-clean))
|
|
|
|
|
|
|
|
(module+ test
|
2020-07-23 00:21:34 +02:00
|
|
|
(test-case "read-org-sbfs"
|
|
|
|
(check-equal? (read-org-sbfs "((1 1) (1 -1))")
|
|
|
|
(list (tbf '#(1 1) 0) (tbf '#(1 -1) 0)))))
|