«Success in computation depends partly on the proper

choice of a formula and partly on a neat and methological

arrangement of the work. For the latter computing paper is essential. A convenient size for such a paper is 26” by 16”; this should be devided by faint ruling into 1/4” squares… Every compuation should be performed with ink in preference to pencil; this not only ensures a much more lasting record of the work but also prevents eye-strain and fatigue»

(D.Gibb, A course in intepolation and numerical integration…, 1915)

1. Introduction

Inspired by this article I decided to embed a gnuplot output into a Qt application written in Common Lisp by means of CommonQt. As of May, 2015 there is no port of Qwt library to Common Lisp. So, gnuplot is an obvious alternative to witing a ploting widget from scratch.

We will develop an application to study interpolation of functions. We will use the Newton’s interpolation formula to approximate several functions and estimate approximation errors. The Newton’s interpolation formula is

where

,

,

,

etc. and the remainter term

is considered small and thus is usually omitted. The divided differences will be evaluated iteratively. First we have a string

, , , , .

Then we leave the first element and change the rest of the string to obtain the second-order divided differences:

, , , , ,

then

, , , , ,

and so on.

2. Interaction with gnuplot

The gnuplot program can be set up to take place of a given window. We only have to pass the window Id as an argument to the ‘set terminal x11 window %winId%’ command. So, our plan is: to design a Qt Gui with Qt Designer, save one widget for the gnuplot output, setup a comman pipe between our program and a running gnuplot process, then do actual computations and send the output data to the gnuplot process.

There’s one obstacle to the plan: a default QWidget is not considered as an XWindow object. That’s why there’s a special QX11EmbedContained widget class in Qt to overcome this difficulty.

First, let us create a .ui file with Qt Designer according to the following screenshot of Qt Designer’s Object Inspector:

You can see here the important control names such as spinBox, tableWidget, radioButton, etc. The radio buttons switch between a selection of the function to be approximated. We will also need a QSplitter (named splitter here) to insert an instance of QX11EmbedContainer later. Such widget can’t be inserted in Qt Designer directly, it’s not in the list.

The beginning of the Common Lisp program is traditional:

(require 'qt) (defpackage #:qt-approx (:use :cl #:qt) (:export #:approx-main)) (in-package #:qt-approx) (named-readtables:in-readtable :qt) (defun find-child (object name) (let ((children (#_children object))) (or (loop for child in children when (equal name (#_objectName child)) return child) (loop for child in children thereis (find-child child name)))))

Then goes the class declaration:

(defclass approx-mainform () ((buttonGroup :accessor buttonGroup) (spinBox :accessor spinBox) (tableWidget :accessor tableWidget) (gpstream :accessor gpstream) (gpprocess :accessor gpprocess)) (:metaclass qt-class) (:qt-superclass "QWidget") (:slots ("recalc()" recalc)) (:override ("closeEvent" close-event)))

Besides the three class members responsible for interaction with controls there are two variables for work with a gnuplot process. Although there are four radio buttons we want them grouped into a four-state switch. Let us have a look at the initialization code:

(defmethod initialize-instance :after ((instance approx-mainform) &key) (new instance) (#_setWindowTitle instance "Approximation of functions") (with-objects ((file (#_new QFile "mainform.ui")) (loader (#_new QUiLoader))) (if (#_open file 1) (let ((win (#_load loader file instance)) (layout (#_new QVBoxLayout)) (x11win (#_new QX11EmbedContainer))) (#_addWidget layout win) (#_setLayout instance layout) (#_setMinimumSize x11win 400 300) (#_addWidget (find-child instance "splitter") x11win) (format t "~X" (#_winId x11win)) (setf (gpprocess instance) (sb-ext:run-program "/usr/bin/gnuplot" nil :wait nil :input :stream :output nil :search t)) (setf (gpstream instance) (sb-ext:process-input (gpprocess instance))) (format (gpstream instance) "set terminal x11 window \"~X\"~% plot sin(x)~%" (#_winId x11win)) (force-output (gpstream instance)) (setf (spinBox instance) (find-child win "spinBox")) (setf (tableWidget instance) (find-child win "tableWidget")) (with-slots (buttonGroup) instance (setf buttonGroup (#_new QButtonGroup instance)) (#_addButton buttonGroup (find-child instance "radioButton") 0) (#_addButton buttonGroup (find-child instance "radioButton_2") 1) (#_addButton buttonGroup (find-child instance "radioButton_3") 2) (#_addButton buttonGroup (find-child instance "radioButton_4") 3) (connect buttonGroup "buttonClicked(int)" instance "recalc()") (connect (find-child instance "spinBox") "valueChanged(int)" instance "recalc()")) (recalc instance) (#_close file)) (error "Couldn't open .ui file!"))))

While creating this code I ran into a trouble with the newly created WX11EmbedContainer: initially I placed the variable into a with-objects wrapper andmy program didn’t work. The lesson is: one should use the with-objects macro when he wants the object to be destroyed at exit out of the macro. Finally, I added x11win variable to a casual (let (…) …) wrapper and it did the thing!

We also have created a QButtonGroup and connected signals from it to out custom function recalc() which performs tha actual computations with divided differences. Here’s its code:

(defun recalc(instance) (declare (optimize (debug 3))) (let* ((n (#_value (spinBox instance))) (k (#_checkedId (buttonGroup instance))) (x (make-array (1+ n) :element-type 'double-float)) (f (make-array (1+ n) :element-type 'double-float)) (fa (make-array (1+ n) :element-type 'double-float)) (x1 (make-array 1025 :element-type 'double-float)) (f1 (make-array 1025 :element-type 'double-float)) (fa1 (make-array 1025 :element-type 'double-float)) (er1 (make-array 1025 :element-type 'double-float)) item) (dotimes (i (1+ n)) (setf (aref x i) (/ (* 2.0d0 i) n) (aref f i) (case k (0 (abs (1- (aref x i)))) (1 (+ 1 (expt (1- (aref x i)) 5))) (2 (sin (* 10.0d0 (aref x i)))) (3 (expt 1.5d0 (aref x i)))))) (replace fa f) ;; calculate the divided differences (do* ((j 1 (1+ j)) (tmp1 (aref fa 0)) (tmp2 (aref fa 1))) ((> j n)) (setf tmp1 (aref fa (1- j)) tmp2 (aref fa j)) (do ((i j (1+ i))) ((> i n)) (setf (aref fa i) (/ (- tmp2 tmp1) (- (aref x i) (aref x (- i j))))) (setf tmp1 tmp2) (if (< i n) (setf tmp2 (aref fa (1+ i)))))) (#_setRowCount (tableWidget instance) (1+ n)) (dotimes (i (1+ n)) (setf item (#_new QTableWidgetItem (write-to-string (aref x i)))) (#_setItem (tableWidget instance) i 0 item) (setf item (#_new QTableWidgetItem (write-to-string (aref f i)))) (#_setItem (tableWidget instance) i 1 item) (setf item (#_new QTableWidgetItem (write-to-string (aref fa i)))) (#_setItem (tableWidget instance) i 2 item)) ;; doing the interpolation part (dotimes (i 1025) (setf (aref x1 i) (/ (* 2.2d0 i) 1024) (aref f1 i) (case k (0 (abs (1- (aref x1 i)))) (1 (+ 1 (expt (1- (aref x1 i)) 5))) (2 (sin (* 10.0d0 (aref x1 i)))) (3 (expt 1.5d0 (aref x1 i)))))) (dotimes (i 1025) ;;approximation for f( x1[i] ) (let ((dx (- (aref x1 i) (aref x 0)))) (setf (aref fa1 i) (aref fa 0)) (do ((j 1 (1+ j))) ((> j n) (setf (aref er1 i) (abs (- (aref f1 i) (aref fa1 i))))) (incf (aref fa1 i) (* dx (aref fa j))) (setf dx (* dx (- (aref x1 i) (aref x j))))))) ;; sending the plotting commands to the gnuplot ptocess (format (gpstream instance) "plot '-' w lines title 'Exact', '-' w lines title 'Approx.', '-' w lines title 'Absolute err.'~%") (dotimes (i 1025) (format (gpstream instance) "~f ~f~%" (aref x1 i) (aref f1 i))) (format (gpstream instance) "e~%") (dotimes (i 1025) (format (gpstream instance) "~f ~f~%" (aref x1 i) (aref fa1 i))) (format (gpstream instance) "e~%") (dotimes (i 1025) (format (gpstream instance) "~f ~f~%" (aref x1 i) (aref er1 i))) (format (gpstream instance) "e~%") (force-output (gpstream instance))))

We compute the divided differences for the number of nodes set in out spinBox, the interpolation nodes are in the array x and the function values are in the array f. Then we use the formulas in the Intriduction and store the divided differences in the array fa. Then we compute exact values and approximations for 1025 values inside and the outside of the initial range. Finally, we feed the computed values to the gnuplot process. One more thing which took long time to understand: when we want to finish tha data transmittion we must force-output on the stream!

Now the remaining stuff:

(defmethod close-event ((instance approx-mainform) close-event) (print "Close event") (sb-ext::process-kill (gpprocess instance) 9) (#_accept close-event)) (defun approx-main() (qt:ensure-smoke "qtuitools") (make-qapplication) (with-objects ((mainform (make-instance 'approx-mainform))) (#_show mainform) (#_exec *qapplication*)))

Here’s the result:



#lisp #commonlisp #gui #qt #commonqt #gnuplot