OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / examples / atomspace / factorial.scm

101 lines · scm ref:
;
; factorial.scm - compute the factorial function, recursively.
;
; This implements a classic textbook algorithm for computing the
; factorial. It defines the factorial function as `f(n) = n * f(n-1)`
; and using an if statement (conditional) to terminate recursion when
; `n` reaches 1.  This is tail-recursive: no stack in either C++ or
; in scheme (or in "Atomese") will increase in size.
;
; This is an imperfect demo: although it does show that classic
; recursive algorithms can be implemented in Atomese, this is not
; really recommended programming practice, for the following reasons:
;
;  * The AtomSpace is a database. Whatever atoms get created as a result
;    of the algorithm will continue to live in the AtomSpace,
;    indefinitely. Is this something you really want? Badly-written
;    algos risk filling the AtomSpace with junk nodes - be careful!
;
;  * Running algorithms in Atomese is slow. It's not intended to be
;    a high-performance numerical computing platform; it is meant to
;    be a general-purpose knowledge-representation and reasoning system.
;
;  * A better example of numerical computation in the AtomSpace is the
;    processing of Values. Values are inherently fleeting, and are much
;    more numerically-oriented. Other examples show how.
;
(use-modules (opencog))

; Compute the factorial of a NumberNode, using the classic
; recursive algorithm.
;
(Define
	(DefinedProcedure "factorial")
	(Lambda
		; A single argument; it must be a number
		(TypedVariable (Variable "$n") (Type "NumberNode"))

		; Conditional: if the first term (the conditional)
		; evaluates to "true", then the second term (the
		; consequent) will be executed; else the third term
		; (the alternative) will be executed
		(Cond
			; The condition: `n>0`
			(GreaterThan (Variable "$n") (Number 0))

			; The consequent: `f(n) = n * f(n-1)`
			(Times
				(Variable "$n")
				(ExecutionOutput
					(DefinedProcedure "factorial")
					(Minus (Variable "$n") (Number 1))))

			; The alternative: `f(1) = 1`
			(Number 1)))
)

; Call the above-defined factorial function, computing the
; factorial of five. Should return 120.
; (cog-execute! (ExecutionOutput (DefinedProcedure "factorial") (Number 5)))
;
#! ----------
How fast is this? Well, its slowwwww, but still, you can find out:
Just cut-n-paste the below. It takes about 16 seconds on my cheap
Intel Celeron laptop.

(define nrep 5000)
(define start (get-internal-real-time))
(for-each
	(lambda (x)
		(cog-execute! (ExecutionOutput (DefinedProcedure "factorial") (Number 100))))
	(iota nrep))
(define end (get-internal-real-time))

(define elapsed
	(exact->inexact
		(/ (- end start) internal-time-units-per-second)))

(format #t "Total run time=~6F seconds.  Each call took ~6F millisecs\n"
	elapsed (* 1000 (/ elapsed nrep)))

Note that the run-time does not depend on the AtomSpace size:
Lets create 400K Atoms:

(for-each (lambda (n)
   (Times (Number n) (Plus (Number (* n 3.14) (Number 1.57)))))
   (iota 100000))

and run the measurement again; there should be no change.

---- !#

; Also, lets take a look at the atomspace, after execution:
; (cog-report-counts)

; Notice the number of Atoms that were created. Effectively, the code
; is creating a tree that is N levels high, and then evaluating that
; tree, producing one NumberNode, holding the result of the evaluation.
;
; ---------------------------------------------------------------
; The End! That's all, folks!