Reputation: 2492
In 1994, long before the specification of OWL, Thomas R. Gruber and Gregory R. Olsen published An Ontology for Engineering Mathematics. Due to the lack of any ontology-specific language it was implemented in lisp. The lisp code is still available (see example) but cannot be used (aka imported) by modern OWL ontologies.
What is the easiest way to make this ontology available to today's (OWL-based) tool chains?
Upvotes: 3
Views: 234
Reputation:
There are two possible approaches to solve this problem: which of them you want to take depends in part on whether the system in which this ontology was written is still accessible. These approaches are common to rescuing other old packages written in Lisp so I think they are worth describing.
This answer doesn't give detailed instructions for how to do this, because the process is necessarily somewhat iterative and experimental.
One thing that often confuses people is that this system isn't really implemented in Lisp at all.
A very good approach to solving problems with computers is to first implement a language in which it is easy to talk about the problem area. This is an extremely common approach: a very large number of large-scale systems involve one or more special-purpose languages. For instance all of the little configuration file formats which drive modern systems are, in fact, little special-purpose languages. Anyone who has ever worked on large software systems will have come across huge numbers of these things.
And most of them will just be terrible, because they were written by people who did not know what they were doing: they didn't understand they were designing a language but instead thought they were doing something else, usually involving substituting strings with other strings based on patterns.
Lisp is one of the few language families which is open about this: the way you solve problems in Lisp is, quite explicitly, by writing a language in which you can better represent the problem. The language will (often) look a bit like Lisp on the surface: it will use parenthesised prefix notation and so on, but it will also include constructs which simply don't exist in the underlying Lisp. (Indeed modern Lisps themselves are such languages: they're languages implemented in simpler versions of themselves.)
The EngMath system is, then, a program written in a language implemented on top of Lisp: it's not really written in Lisp at all. That language is either KIF or Ontolingua: I don't understand the relation between these two.
The first step in all of this is to have a good-quality Common Lisp environment, and to become somewhat familiar with its use. I won't recommend one here as I am very far out of the loop on this, but several are freely available.
So the first approach to rescuing the system is the one to use if an implementation of the the underlying language still usefully exists. I could not work out if this was the case: a lot of the links on the Stanford KSL website seem to now point nowhere, so it may or may not still exist. As I said above, I think the underlying language is either KIF or Ontolingua (and it may be Ontolingua is an implementation of KIF? not sure).
The approach is then as follows.
This is going to be a significant amount of work. Note that, if what you want to do is to just use the ontology you can branch from step (3) above: once you have the system running you can use the ontology.
This is an approach you can use if the underlying language has become either effectively or really unavailable. It may also be a better approach even if it is still available if all you want to do is conversion.
This approach is still painful but it may be less painful than the first one: it's also all you can do if the original system is lost. I've done things like this with programs written in antique (very antique) algebra systems.
All of these use the file you posted as data (which, below, is /tmp/engmath-sample.lisp
).
First of all check it can be read with a function like this
(defun read-file-naively (f)
;; Just read all the forms in a file and return a list of them
(with-open-file (in f)
(loop for form = (read in nil in)
until (eql form in)
collect form)))
With that, then (read-file-naively "/tmp/engmath-sample.lisp")
will return a fairly long list. Critically it won't puke, so this file, at least, is readable by Lisp.
Here is some code to slightly fake an ontology: this is just enough to successfully load the sample file:
;;;;
;;;
(defpackage :fake-ontolingua
(:use :cl)
(:export
#:clear-theories
#:find-theory
#:theory-name #:theory-supers #:theory-issues
#:theory-documentation #:theory-relations
#:frame-ontology
#:define-theory
#:in-theory
#:current-theory
#:current-relations
#:find-relation
#:clear-relations
#:relation-name #:relation-theory #:relation-arglist
#:relation-documentation #:relation-body
#:define-relation))
(defpackage :fake-ontolingua-user
(:nicknames :ontolingua-user)
(:use :cl :fake-ontolingua)) ;should it use CL?
(in-package :fake-ontolingua)
(defvar *theories* (make-hash-table))
(defclass theory ()
((name :initarg :name
:reader theory-name)
(supers :initform '()
:reader theory-supers)
(issues :initform '()
:initarg :issues
:reader theory-issues)
(documentation :initform nil
:initarg :documentation
:reader theory-documentation)
(relations :initform '()
:accessor the-theory-relations)))
(defun find-theory (name &optional (errorp t))
(let ((it (gethash name *theories*)))
(or it
(if errorp
(error "no theory ~S" name)
nil))))
(defun (setf find-theory) (theory name)
(setf (gethash name *theories*) theory))
(defgeneric ensure-theory (theory-or-name)
(:method ((name symbol))
(find-theory name))
(:method ((theory theory))
theory)
(:method (mutant)
(error "what?")))
(defmethod initialize-instance :after ((theory theory)
&key (supers '(frame-ontology)))
(setf (slot-value theory 'supers)
(mapcar #'ensure-theory supers)))
(defvar *current-theory* nil)
(defun in-theory (theory-name)
(setf *current-theory* (find-theory theory-name)))
(defun current-theory ()
*current-theory*)
(defun clear-theories ()
;; Reset everything
(clrhash *theories*)
(setf (gethash 'frame-ontology *theories*)
(make-instance 'theory :name 'frame-ontology :supers '()))
(in-theory 'frame-ontology)
(values))
;;; Bootstrap theories
(clear-theories)
(defun make-theory (name kws)
(when (find-theory name nil)
(restart-case
(error "redefinition not supported")
(continue ()
:report "clear all existing theories"
(clear-theories)
(make-theory name kws))))
(setf (find-theory name) (apply #'make-instance 'theory :name name kws)))
(defmacro define-theory (name supers &body doc/body)
(let ((effective-body (if (stringp (first doc/body))
(cons ':documentation doc/body)
doc/body)))
`(progn
(make-theory ',name (list* ':supers ',supers ',effective-body))
',name)))
(defun clear-relations (&optional (theory *current-theory*))
(setf (the-theory-relations theory) nil)
(values))
(defgeneric theory-relations (theory)
;; Get them in the right order. (Should use collecting to make
;; relation list...)
(:method ((theory theory))
(reverse (the-theory-relations theory))))
(defun current-relations (&optional (theory (current-theory)))
(theory-relations theory))
(defun find-relation (name &optional (theory (current-theory))
(errorp t))
(let ((it (assoc name (the-theory-relations theory))))
(cond
(it (cdr it))
(errorp (error "no relation ~A" name))
(t nil))))
(defclass relation ()
((name :initarg :name
:reader relation-name)
(theory :initarg :theory
:reader relation-theory)
(arglist :initarg :arglist
:reader relation-arglist)
(documentation :initarg :documentation
:initform nil
:reader relation-documentation)
(body :initarg :body
:reader relation-body)))
(defun make-relation (name kws)
(let* ((theory *current-theory*)
(relations (the-theory-relations theory))
(existing (assoc name relations))
(relation (apply #'make-instance 'relation
:name name
:theory theory
kws)))
(cond
(existing
(warn "redefining ~A in ~A" name (theory-name theory))
(setf (cdr existing) relation))
(t
(setf (the-theory-relations theory)
(acons name relation (the-theory-relations theory)))))
relation))
(defmacro define-relation (name arglist &body doc/body)
(let ((effective-body
(if (stringp (first doc/body))
(list ':documentation (first doc/body)
':body (rest doc/body))
(list ':body doc/body))))
`(progn
(make-relation ',name (list* ':arglist ',arglist ',effective-body))
',name)))
After loading and compiling this, then you can load the sample:
> (load (compile-file "fake-ontolingua"))
[noise from compiler removed]
> (in-package :ontolingua-user)
#<The FAKE-ONTOLINGUA-USER package, 0/16 internal, 0/16 external>
> (load "engmath-sample.lisp")
; Loading text file /private/tmp/engmath-sample.lisp
#P"/private/tmp/engmath-sample.lisp"
> (find-relation 'linear-space)
#<fake-ontolingua::relation 40200B6323>
ONTOLINGUA-USER 6 > (relation-body (find-relation 'linear-space))
(:iff-def
[lots of stuff])
Note that this code is just enough to read the sample file and build a structure from it. That structure is not an ontology, and in particular the thing knows nothing at all about what relations are, what the relation body is or anything at all like that. To actually do anything useful you'll need to learn enough about KIF / Ontolingua to understand what things mean, and then write a program which can walk over all the relations and spit out suitable OWL.
Upvotes: 3