Coverage report: /development/source/library/org/datagraph/spocq-shard/src/store/rdfcache/decimated-pattern.lisp

KindCoveredAll%
expression21922 0.1
branch0208 0.0
Key
Not instrumented
Conditionalized out
Executed
Not executed
 
Both branches taken
One branch taken
Neither branch taken
1
 ;;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.implementation; -*-
2
 
3
 (in-package :org.datagraph.spocq.implementation)
4
 
5
 
6
 ;;; (load (compile-file #P"/development/source/library/org/datagraph/spocq/src/store/decimated-pattern.lisp"))
7
 
8
 (:documentation "Modular BGP manipulation"
9
   "A basic graph pattern (BGP) comprises a set of statements to be
10
  matched against the content of the store to yield either a boolean indication, whether
11
  the denoted sub-graph exists, or the set of bindings for which the sub-graph exists.
12
  Each bgp form comprises
13
 
14
  - triple statement patterns (s p o)
15
  - implicit quad form derived from graph form scope
16
  - filters
17
 
18
  The standard semantics are, that the forms effect a null-suppressing natural join among the
19
  solution fields whereby the process excludes any solutions which do not satisfy the filters.
20
 
21
  One implementation mechanism is to interpret each statement pattern individually, to generate the
22
  respective solution field, apply any filter for which the solution binds all variables and
23
  then combine resulting fields accoding to the 'and' group operator, which produces a join.
24
  The combination order is arranged based on the respective and combined selectvity, to intend
25
  to limit the field size growth.
26
  An alternative is use the selectivity, order those which share
27
  dimensions such that those with the smallest expected solution counts come first and then interpret the
28
  statements in order while propagating bindings from the preceeding statements to 
29
  variables in successors to constrain their matches.
30
 
31
  The trade-off is between the space and time to perform a join between two solutions and
32
  the time required to perform successive matches. If the match rate is low - whether because of the communications
33
  delays to the data or because of the size of the repository, then the total time for
34
  repeated matches in succeeding statements will surpass the time for a combination against the
35
  scanned solutions field which results from the same pattern without the constraints.
36
 
37
  The constraint propagation mechanism can be implemented through dynamically compiled, iteratively
38
  nested lexical environments, but that makes it difficult to subsequently reorder the
39
  pattern evaluation or to change the strategy between propagated matching and joined scanning
40
  in response to the actual solution field sizes.
41
  The 'decimated pattern' approach implementes each pattern as an opportunistic operator in two 
42
  phases. an initial phase accepts a predecessor field and, in general, dependent upon the field size and
43
  contingent match and scan rate for the specific repository,
44
  either performs a match of the respective pattern with no qualification beyond that
45
  from the original query, followed by a scan-based combination with the argument or
46
  it transforms the argument field into
47
  additional constraints on its pattern for successive matches.
48
  Filters associate with the statement patterns in the same manner as
49
  propagated matching or joined scanning, but, as the order is not static, the filter implementation
50
  is computed on-demand to reflect the solution field dimensions at the point of application.
51
 
52
  In addition to the join which is inherent in the bgp and the filter which results from pushed
53
  filters, the decimated approach introduces operators to implement
54
  the logical implications of schema declarations:
55
 
56
  - and   : computes the natural join. this provides the standard bgp semantics
57
  - or    : passes the first non-null constituent solution field and suppresses all others
58
  - not   : intended to operate constrained by an initial field, in which situation, it passes each solution
59
            for which the constrained immediate match fails. when applied in isolation, it yields a unit table
60
            for a pattern which fails to match and a null result field for one which does.
61
  - xor   : similar to or, but passes solution succeeds only if there is exactly one.
62
  - sum   : accumulate all matched solutions (as union), but lower-level requiring constant dimensionality
63
 
64
  The original standard bgp form is wrapped with an 'and' prior to processing.
65
 
66
  the combination operators are implemented as independent interpreter steps with the respective
67
  semantics. each expects an initial state (nil, a field array, or a generator controller function),
68
  and one or more pattern matchers. depending on the relative size of the initial field and the
69
  pattern matcher's sensitivity, the matcher is invoked either to propagate or to scan. in the former case,
70
  the field is supplied to the matcher and each individual solution is combined as per operator semantics
71
  in the latter case,  no initial solutions are supplied to the matcher and its result field is combined
72
  with the initial field as per operator semantics.
73
 
74
  The general control structure is that each operator node in a bgp expression compiles
75
  into a function of two required arguments - the destination/continuation which accepts result pages
76
  and the repository transaction. In addition, the function accepts keyword arguments for
77
  - quad-offset
78
  - quad-count
79
  - plus each variable name in the pattern
80
 
81
  the top-level operator is invoked by the agp generator function with a continuation which
82
  introduces the result field into a queue. each operator passes control to
83
  its arguments assembling the result field according to the respective operator semantics.
84
  This serializes the processing of each bgp. Any desired parallelism can be introduced
85
  either by
86
  - splitting the patterns from a bgp among explicit legs of a join
87
  - by distributing the matching for a single statement pattern among store shards
88
  - by reintroducing an asynchronous connection between the match thread and the combiniation
89
    thread, eg the merge-join operation which combines statements. this makes page storage more complex,
90
    as they would have to be cloned prior to queueing
91
 
92
 add the channel fd to the field.
93
 when the expected operation time exceeds a limit, create or claim a thread,
94
 put its result channel in the field,
95
 clone the field, keep the copy to return, give the original to the additional thread,
96
 and task it to execute the operation in the task context.
97
 
98
 upon completeion, remove the channel from the field,
99
 record the operation time
100
 and the thread start-up delay for future comparison
101
 
102
 -------------
103
 sip cannot just run process-pattern, that would generate too many spurious fields.
104
 better
105
  - change matrix match to accept a matrix which it extends
106
  - change match/scan choice to compute two combined function in the match case
107
  the first  extracts values for constriant  dimensions from the base field and calls the second
108
  the second binds them for the matrix match call with a growing matrix
109
 the final result is the matrix encapsulates in a field
110
 
111
  ")
112
 
113
 
114
 (defgeneric pattern-selectivity (pattern)
115
   (:documentation "Follows Stocker (tr ifi-2007.03), as 'the fraction of triples satisfying the pattern'."))
116
 
117
 (defgeneric pattern-arity (pattern)
118
   )
119
 
120
 (defgeneric pattern-arity-and-selectivity (pattern)
121
   )
122
 
123
 (defgeneric pattern-count (pattern)
124
   )
125
 
126
 (defgeneric pattern-propagated-arity (pattern)
127
   )
128
 
129
 (defgeneric pattern-propagated-arity-and-selectivity (pattern)
130
   )
131
 
132
 (defgeneric pattern-precedes (pattern1 pattern2)
133
   )
134
 
135
 (defgeneric process-pattern (pattern result-field bindings modifiers)
136
   (:documentation "Perform a step from a pattern match against the current *transaction* repository given
137
  a logical combination of quad patterns, and optional binding and modifier property lists.
138
  Any bindings constrain variables in statement patterns while any modifier specifies how to
139
  slice the final result.
140
  If a result field is supplied, the resulting solutions will be emitted to that destination."))
141
 
142
 
143
 (defparameter *pattern-sort-key* #'pattern-propagated-arity-and-selectivity)
144
 
145
 
146
 ;;;
147
 ;;; field iteration support
148
 
149
 (defun binding-argument-list-dimensions (bindings)
150
   (loop for (dimension nil) on bindings by #'cddr
151
         collect dimension))
152
 
153
 (defun call-with-extended-bindings (op field bind-dimensions base-bindings)
154
   "given an initial binding property list, iterate over the given field, extend the bindings
155
  individually for each solution and invoke the given operator with the respectively
156
  extended binding argument list."
157
 
158
   (let* ((field-dimensions (solution-field-dimensions field))
159
          (iterator (compute-matrix-iterator (length field-dimensions)))
160
          (extender (compute-bindings-extender field-dimensions bind-dimensions)))
161
     ;; (print-lexical-frame call-with-extended-bindings)
162
     (funcall iterator (funcall extender op base-bindings) field)))
163
 
164
 
165
 (defun compute-bindings-extender (field-dimensions bind-dimensions)
166
   (let* ((field-parameters (loop for dimension in field-dimensions
167
                                  collect (or dimension (gensym "arg"))))
168
          (extension-lambda `(lambda (op base-bindings)
169
                               (function (lambda ,field-parameters
170
                                           (declare (ignorable ,@field-parameters))
171
                                           (funcall op (list* ,@(loop for parameter in field-parameters
172
                                                                      for field-dimension in field-dimensions
173
                                                                      when (find field-dimension bind-dimensions)
174
                                                                      append `(',field-dimension ,parameter))
175
                                                              base-bindings)))))))
176
     (values (spocq-compile extension-lambda)
177
             extension-lambda)))
178
 ;;; (compute-bindings-extender '(nil sub pred nil) '(sub extra))
179
 
180
 
181
 (defun compute-matrix-iterator (column-count)
182
   (let ((lambda (compute-matrix-operator-lambda 'iterator :column-count column-count)))
183
     (values (spocq-compile lambda)
184
             lambda)))
185
 ;;; (compute-matrix-iterator 4)
186
 
187
 
188
 (defmethod compute-matrix-operator-lambda ((operator (eql 'iterator)) &key column-count)
189
   `(lambda (operator field)
190
      ,(format nil "iterator operator for column count: ~s." column-count)
191
      (declare (type matrix-field field)
192
               ; (optimize ,@*field-optimization*)
193
               (dynamic-extent operator))
194
      (describe-field field :label "iteration-source")
195
      ; (print (list operator field (solution-field-solutions field)) *trace-output*)
196
      (finish-output *trace-output*)
197
      (unless (= (length (solution-field-dimensions field)) ,column-count) ()
198
              (matrix-dimension-error :matrix field :expected-dimensions '(* ,column-count)))
199
      
200
      (let ((%source-data (cffi::null-pointer))
201
            (source-row 0))
202
        (declare (foreign-type (foreign-array ,+matrix-element-type+ (* ,column-count)) %source-data)
203
                 (type sb-sys:system-area-pointer %source-data)
204
                 (type fixnum source-row)
205
                 )
206
        (loop while (not (cffi:null-pointer-p (setf (values %source-data source-row) (next-field-row field))))
207
              do (funcall operator
208
                          ,@(loop for source-column below column-count
209
                                  collect `(foreign-array-ref %source-data source-row ,source-column)))))))
210
 
211
 ;;;
212
 ;;; abstract classes
213
 
214
 (defclass applicable-pattern-class (c2mop:funcallable-standard-class)
215
   ())
216
 
217
 (eval-when (#+lispworks :compile-toplevel :load-toplevel :execute)
218
   (defmethod c2mop:validate-superclass ((subclass applicable-pattern-class)
219
                                         (superclass standard-class))
220
     t)
221
   (defmethod c2mop:validate-superclass ((subclass applicable-pattern-class)
222
                                         (superclass c2mop:funcallable-standard-class))
223
     t))
224
 
225
 
226
 
227
 (defclass applicable-pattern (c2mop:funcallable-standard-object)
228
   ((operator
229
     :initform (error "operator is required.")
230
     :reader pattern-operator)
231
    (function
232
     :initform (error "function is required.") :initarg :function
233
     :reader pattern-function
234
     :documentation "The compiled pattern matching function for the respective form.
235
      It accepts three required arguments, the result matrix, the bindings, and the modifiers,
236
      performs its match against the transaction store and collects the result in the given matrix, or
237
      given a null initial result, creates a new result matrix.")
238
    (dimensions
239
     :initarg :dimensions :initform ()
240
     :reader pattern-dimensions) ;;;!!! must take the internal dimension for otherise non-variable contexts
241
    (propagated-dimensions
242
     :initform nil
243
     :accessor pattern-propagated-dimensions
244
     :documentation "caches variables available for sip bindings to be used to comute the effective arity"))
245
   (:metaclass applicable-pattern-class))
246
 
247
 (defclass applicable-pattern-combination (applicable-pattern)
248
   ((forms
249
     :initform (error "forms is required.") :initarg :forms 
250
     :reader pattern-forms)
251
    (effective-pattern
252
     :initform nil
253
     :accessor pattern-effective-pattern)
254
    (effective-pattern-lambda
255
     :initform nil
256
     :accessor pattern-effective-pattern-lambda))
257
   (:metaclass applicable-pattern-class))
258
 
259
 (defclass applicable-pattern-statement (applicable-pattern)
260
   ((terms
261
     :initform (error "terms is required.") :initarg :terms
262
     :reader pattern-terms)
263
    (count
264
     :initform nil :initarg :count
265
     :reader get-pattern-count :writer setf-pattern-count)
266
    (identity-predicate
267
     :initform nil :initarg :identity-predicate
268
     :accessor pattern-identity-predicate))
269
   (:metaclass applicable-pattern-class))
270
 
271
 
272
 (defmethod initialize-instance ((instance applicable-pattern) &rest initargs &key function)
273
   (declare (dynamic-extent initargs))
274
   (flet ((process-pattern-function (result-field bindings modifiers)
275
            (process-pattern instance result-field bindings modifiers)))
276
     (unless function
277
       (setf function #'process-pattern-function))
278
     (apply #'call-next-method instance
279
            :function function
280
            initargs)
281
     (c2mop:set-funcallable-instance-function instance function)))
282
 
283
 (defmethod process-pattern ((pattern applicable-pattern) (result-field null) (bindings t) (modifiers t))
284
   (complete-field-data (process-pattern pattern
285
                                         (make-matrix-field :dimensions (pattern-dimensions pattern))
286
                                         bindings modifiers)))
287
 
288
 
289
 (defmethod print-object ((object applicable-pattern) stream)
290
   (print-unreadable-object (object stream :identity t :type t)
291
     (format stream "~s" (pattern-dimensions object))))
292
 
293
 (defmethod pattern-selectivity ((pattern applicable-pattern))
294
   ;; the base method just divides the count against the repository total
295
   (let ((repository-count (repository-statement-count *transaction*))
296
         (pattern-count (pattern-count pattern)))
297
     (if (> repository-count 0)
298
       (float (/ pattern-count repository-count))
299
       1)))
300
 
301
 
302
 (defun compute-intrasolution-identity-test (dimensions)
303
   (let ((duplicated-dimension (remove-duplicates (loop for dimension in dimensions
304
                                                        when (and dimension (> (count dimension dimensions) 1))
305
                                                        collect dimension))))
306
     (loop for duplicated in duplicated-dimension
307
           collect (loop with first-index = (position duplicated dimensions)
308
                         for dimension in dimensions
309
                         for index from 0
310
                         when (and (/= first-index index)
311
                                   (eq duplicated dimension))
312
                         collect `(= (foreign-array-ref %source-data source-row ,first-index)
313
                                     (foreign-array-ref %source-data source-row ,index))
314
                         into tests
315
                         finally (return (if (rest tests) `(and ,@tests) (first tests))))
316
           into tests
317
           finally (return (if (rest tests) `(and ,@tests) (first tests))))))
318
 
319
 (defmethod pattern-count ((pattern applicable-pattern-combination))
320
   (flet ((each-pattern-count (pattern)
321
            (pattern-count pattern)))
322
     (declare (dynamic-extent #'each-pattern-count))
323
     (reduce #'+ (pattern-forms pattern) :initial-value 0 :key #'each-pattern-count)))
324
 
325
 
326
 (defmethod initialize-instance ((instance applicable-pattern-statement) &rest args &key terms dimensions graph)
327
   ;; abstract the statement pattern terms to retain just the variables in their
328
   ;; respective positions; reorder them to reflect the arrangement returned by the store;
329
   ;; ensure that the context position is a variable even if the statement inclues a constant,
330
   ;; in order to ensure that joins across named graphs limit matched to intra-graph solutions.
331
   ;; replace other constants with unique undistingusihed variables to preclude join constains and
332
   ;; suppress projection.
333
   ;;
334
   ;; this approach does not incorporate special logic for constant patterns. where that
335
   ;; case generated code which acted as a filter over bindings to that point, this approach
336
   ;; will generate a single-solution matrix when the constants match, of which only the
337
   ;; context position will have a dimensions
338
   ;;
339
   ;; pushed filters' equivalents are handled by retaining the original statement patter, which includes
340
   ;; the variable as the dimension bound to solutions at the position of the constant term.
341
   ;;
342
   ;; intra-pattern identity is implemented as an additional filter step where it is required.
343
   (declare (dynamic-extent args)
344
            (ignore graph))
345
   (flet ((ensure-variable (term &optional (default nil))
346
            (cond ((variable-p term) term)
347
                  (default )
348
                  (t (cons-variable "bgp")))))
349
     ;; augment the pattern with explicit dimensions.
350
     ;; a given dimension remains; a constant is replaced with an unique undistinguished variable in order
351
     ;; to suppress it in projections and preclude joining, while the context is replaced with a singleton
352
     ;; variable
353
     (cond (dimensions
354
            (assert (= 4 (length dimensions)) () "dimensions is invalid: ~s." dimensions)
355
            (assert (= 4 (length terms)) () "terms is invalid: ~s." terms))
356
           (t
357
            (setf dimensions (destructuring-bind (subject predicate object context) terms
358
                               ;; if the context was supplied, provide a singleton dimension to constrain
359
                               ;; joins, otherwise supply a unique dimension to eliminate it from joins
360
                               (list (ensure-variable context (when context +context-variable+))
361
                                     (ensure-variable subject)
362
                                     (ensure-variable predicate)
363
                                     (ensure-variable object))))))
364
 
365
     (let* ((identity-test (compute-intrasolution-identity-test dimensions))
366
            (identity-predicate (when identity-test
367
                                  (spocq-compile `(lambda (%source-data source-row)
368
                                                    (declare (foreign-type (foreign-array ,+matrix-element-type+ (* ,(length dimensions))) %source-data)
369
                                                             (type sb-sys:system-area-pointer %source-data)
370
                                                             (type fixnum source-row))
371
                                                    ,identity-test)))))
372
       (flet ((filter-process-pattern-function (result-field bindings modifiers)
373
                (let* ((filter-operator (ensure-matrix-operator 'filter :projection (loop for i below (length dimensions) collect i)
374
                                                                :result-column-count (length dimensions))))
375
                  (let ((intermediate-result-field (process-pattern instance nil bindings modifiers)))
376
                    (flet ((filter-result (result-field)
377
                             (with-input-fields (intermediate-result-field)
378
                               (with-result-field (result-field :dimensions (solution-field-dimensions intermediate-result-field)
379
                                                                :sort-order (solution-field-sort-dimensions intermediate-result-field))
380
                                 (funcall filter-operator result-field intermediate-result-field identity-predicate))
381
                               result-field)))
382
                      (if result-field
383
                        (filter-result result-field)
384
                        (complete-field-data (filter-result (make-matrix-field)))))))))
385
         (apply #'call-next-method instance
386
                ;; always provide a context binding to make it available to joins so that patterns
387
                ;; do not cross graph boundaries
388
                :dimensions dimensions
389
                :function (when identity-predicate #'filter-process-pattern-function)
390
                args)))))
391
 
392
 
393
 ;;;
394
 ;;; abstract generic operator implementations
395
 
396
 (defmethod pattern-arity-and-selectivity ((pattern applicable-pattern))
397
   (cons (pattern-arity pattern) (pattern-selectivity pattern)))
398
 
399
 (defmethod pattern-propagated-arity-and-selectivity ((pattern applicable-pattern))
400
   (cons (pattern-propagated-arity pattern) (pattern-selectivity pattern)))
401
 
402
 (defmethod pattern-precedes ((value1 number) (value2 number))
403
   (< value1 value2))
404
 
405
 (defmethod pattern-precedes ((value1 cons) (value2 cons))
406
   (ecase (signum (- (first value1) (first value2)))
407
     (-1 t)
408
     (0 (< (rest value1) (rest value2)))
409
     (1 nil)))
410
 
411
 (defmethod pattern-arity ((pattern applicable-pattern))
412
   (length (pattern-dimensions pattern)))
413
 
414
 (defmethod pattern-propagated-arity ((pattern applicable-pattern))
415
   (- (length (pattern-dimensions pattern))
416
      (length (pattern-propagated-dimensions pattern))))
417
 
418
 (defun interned-pattern-terms (pattern bindings)
419
   (let* ((wildcard-term-number nil)
420
          (default-context-term nil)
421
          (named-contexts-term nil)
422
          (default-graphs (dataset-default-graphs *task*))
423
          (named-graphs (dataset-named-graphs *task*)))
424
     (labels ((wildcard-term-number ()
425
                (or wildcard-term-number (setf wildcard-term-number (repository-wildcard-term *transaction*))))
426
              (default-context-term ()
427
                (or default-context-term (setf default-context-term (repository-default-context-term *transaction*))))
428
              (named-contexts-term ()
429
                (or named-contexts-term (setf named-contexts-term (repository-named-contexts-term *transaction*))))
430
              (bind-if-variable (term)
431
                (if (variable-p term)
432
                  (or (getf bindings term)
433
                      (when (boundp term) (symbol-value term))
434
                      (wildcard-term-number))
435
                  (if (property-path-p term)
436
                    (intern-property-path term)
437
                    (intern-term term))))
438
              (intern-term (term)
439
                (if (or (null term) (spocq:blank-node-p term) (variable-p term))
440
                  (wildcard-term-number)
441
                  (or (object-term-number term)
442
                      ;; if the term is not in the store, no match is possible
443
                      (return-null-terms))))
444
              (effective-context (context)
445
                (cond ((null context)
446
                       (remove nil (mapcar #'lookup-object-term-number
447
                                           (if (or default-graphs named-graphs) default-graphs (list (default-context-term))))))
448
                      ((variable-p context)
449
                       ;; either use an initial soution binding or, if it is unbound,
450
                       ;; if the dataset specified named graphs, iterate across them - whereby if the effective
451
                       ;; dataset includes default graphs, but no named graphs, there is nowhere to search.
452
                       ;; see rdf-sparql-query 8.2.1
453
                       ;; absent a dataset, treat it as a wild-card in whichever domain is declared for named graphs.
454
                       (cond ((getf bindings context))
455
                             ((boundp context) (list (symbol-value context)))
456
                             ((or default-graphs named-graphs)
457
                              (remove nil (mapcar #'lookup-object-term-number named-graphs)))
458
                             (t
459
                              (list (intern-term (named-contexts-term))))))
460
                      ((typep context 'iri)
461
                       (let ((interned-context (intern-term context)))
462
                         (when interned-context (list interned-context))))
463
                      (t
464
                       (error "Invalid dataset specification: graph ~s, dataset ~s."
465
                              context (task-dataset-graphs *task*)))))
466
              (interned-effective-context (context)
467
                (or (effective-context context)
468
                    (return-null-terms)))
469
              (return-null-terms ()
470
                (return-from interned-pattern-terms (list nil nil nil nil))))
471
       (destructuring-bind (subject predicate object context) (pattern-terms pattern)
472
         (list (bind-if-variable subject)
473
               (bind-if-variable predicate)
474
               (bind-if-variable object)
475
               (effective-context context))))))
476
                                                             
477
 
478
 ;;;
479
 ;;; aggregate
480
 
481
 (defclass bgp:aggregate (applicable-pattern-combination)
482
   ((operator
483
     :initform 'bgp:aggregate :allocation :class)
484
    (projection
485
     :initform (error "projection is required.") :initarg :projection
486
     :accessor pattern-projection))
487
   (:metaclass applicable-pattern-class)
488
   (:documentation "The class of wrapped pattern functions aggregations."))
489
 
490
 (defparameter bgp:aggregate 'bgp:aggregate)
491
 
492
 (define-compiler-macro bgp:aggregate (pattern projection &rest initargs)
493
   `(make-instance bgp:aggregate :forms (list ,pattern) :projection ',projection ,@initargs))
494
 
495
 
496
 (defun bgp:aggregate (pattern projection &rest initargs)
497
   (declare (dynamic-extent initargs))
498
   (apply #'make-instance bgp:aggregate :forms (list pattern) :projection projection initargs))
499
 
500
 
501
 (defmethod process-pattern ((pattern bgp:aggregate) (result-field matrix-field) bindings modifiers)
502
   "an aggregation pattern applies its projection to the source field as generated in the context of its
503
  bindings and then applies any modifiers to produce its result."
504
 
505
   (flet ((matrix-aggregate (&rest args)
506
            (error "NYI: matrix-aggregate ~s" args)))
507
     (destructuring-bind (base-pattern) (pattern-forms pattern)
508
       (apply #'matrix-aggregate result-field (process-pattern base-pattern nil bindings nil)
509
              (pattern-projection pattern)
510
              modifiers))))
511
 
512
 ;;;
513
 ;;; and
514
 
515
 (defclass bgp:and (applicable-pattern-combination)
516
   ((operator
517
     :initform 'bgp:and :allocation :class))
518
   (:metaclass applicable-pattern-class)
519
   (:documentation "The class of wrapped pattern functions for conjunctively joined BGP statement patterns.
520
    given a repository transaction:
521
    - get pattern count = (selectivity * size)
522
    - if (pattern-count / scan-rate) < (initial-field-size / match-rate)
523
      - then scan: invoke the pattern w/ no solution binding
524
             merge join to yield a new field and emit it as the result
525
      - otherwise invoke the pattern iteratively w/ the bindings from the initial field and
526
        accumulate the results into a new field and emit it as the result"))
527
 ;;; (funcall (make-instance bgp:and :forms (list (bgp:quad 1 2 3 nil))) nil nil)
528
 
529
 (defparameter bgp:and 'bgp:and)
530
 
531
 (define-compiler-macro bgp:and (&rest arguments)
532
   ;; reduce the pattern in-place for one or zero arguments
533
   (let* ((initargs (member-if #'keywordp arguments))
534
          (patterns (ldiff arguments initargs)))
535
     (if patterns
536
       (if (rest patterns)
537
         `(make-instance bgp:and :forms (list ,@patterns) ,@initargs)
538
         (append (first patterns) initargs))
539
       `(bgp:null ,@initargs))))
540
 
541
 (defun bgp:and (&rest arguments)
542
   (let* ((initargs (member-if #'keywordp arguments))
543
          (patterns (ldiff arguments initargs)))
544
     (if patterns
545
       (if (rest patterns)
546
         (apply #'make-instance bgp:and :forms patterns initargs)
547
         (let ((arg (first patterns)))
548
           (when initargs
549
             (apply #'reinitialize-instance (first patterns) initargs))
550
           arg))
551
       (apply #'bgp:null initargs))))
552
   
553
 
554
 
555
 (defmethod initialize-instance ((instance bgp:and) &rest args &key forms dimensions)
556
   ;; compute the effective result dimenions as the combined join result dimensions of the constituents."
557
   (apply #'call-next-method instance
558
          :dimensions (or dimensions
559
                          (when forms
560
                            (if (rest forms)
561
                              (reduce #'join-result-dimensions forms :key #'pattern-dimensions)
562
                              (pattern-dimensions (first forms)))))
563
          args))
564
 
565
 
566
 (defmethod process-pattern ((pattern bgp:and) (result-field matrix-field) bindings modifiers)
567
   "given an and pattern's constituent statemetn forms and initial bindings, establish the arity of each
568
  statement respective the successively extended bindings, order the statements based on
569
  (initial statement size + match rate v/s the subsequence statement expected size + scan rate),
570
  pick the most favorable, record the combination in the effective method and combine them as such.
571
  continue this until all constituents are combined.
572
  finally, unless bindings were present, record the effective method lambda and return the result.
573
  on a subsequent call reuse a saved effective method."
574
 
575
   (let ((forms (copy-list (pattern-forms pattern))))
576
     (if forms
577
       (if (rest forms)
578
         (let ((effective-pattern (pattern-effective-pattern pattern)))
579
           (if effective-pattern
580
             (funcall effective-pattern pattern result-field bindings modifiers)
581
             (loop with bound-variables = (binding-argument-list-dimensions bindings)
582
                   for (next-form . remaining-forms) = (sort-bound-patterns forms bound-variables)
583
                   for combination-operator = nil
584
                   then (compute-pattern-combination pattern successive-result remaining-forms bound-variables)
585
                   for successive-result  = (funcall next-form nil bindings nil)
586
                   then (funcall combination-operator pattern (unless remaining-forms result-field)
587
                                 successive-result next-form
588
                                 bindings
589
                                 (unless remaining-forms modifiers))
590
                   for pattern-expression = `(,next-form nil bindings nil)
591
                   then `(,combination-operator ,pattern ,(unless remaining-forms 'result-field)
592
                                                ,pattern-expression ,next-form
593
                                                bindings
594
                                                ,(unless remaining-forms 'modifiers))
595
                   do (setf bound-variables (join-result-dimensions (solution-field-dimensions successive-result) bound-variables)
596
                            forms remaining-forms)
597
                   ;; cache prospectively for subsequent compilation
598
                   ;; returns the successive result - as is, if it was created locally, the combination completed it
599
                   ;; otherwise leave it incomplete for continued use
600
                   finally (let ((lambda `(lambda (pattern result-field bindings modifiers)
601
                                            (declare (ignoreable pattern))
602
                                            ,pattern-expression)))
603
                             (unless bindings
604
                               (setf (pattern-effective-pattern-lambda pattern) lambda
605
                                     (pattern-effective-pattern pattern) (spocq-compile lambda)))
606
                             (return successive-result)))))
607
         (funcall (first forms) result-field bindings modifiers))
608
       (matrix-null result-field (pattern-dimensions pattern)))))
609
 
610
 
611
 (defmethod merge-and-pattern ((op-pattern t) result-field field next-pattern bindings modifiers)
612
   (flet ((matrix-join (&rest args)
613
            (error "NYI: matrix-join ~s" args)))
614
     (apply #'matrix-join result-field field (funcall next-pattern nil bindings nil) modifiers)))
615
 
616
 (defmethod match-and-pattern (op-pattern (result-field null) field next-pattern bindings modifiers)
617
   (complete-field-data (match-and-pattern op-pattern (make-matrix-field) field next-pattern bindings modifiers)))
618
 
619
 (defmethod match-and-pattern ((op-pattern t) (result-field matrix-field) field next-pattern bindings modifiers)
620
   (if (plusp (solution-field-length field))
621
     (flet ((match-with-extended-bindings (propagated-bindings)
622
              ;; (print-lexical-frame match-with-extended-bindings)
623
              (setf result-field (funcall next-pattern result-field propagated-bindings modifiers))))
624
       (declare (dynamic-extent #'match-with-extended-bindings))
625
       (call-with-extended-bindings #'match-with-extended-bindings field (pattern-dimensions next-pattern) bindings)
626
       result-field)
627
     (matrix-null result-field (join-result-dimensions (solution-field-dimensions field) (pattern-dimensions next-pattern)))))
628
 
629
 (defun sort-bound-patterns (patterns bound-variables)
630
   ;; update each pattern to reflect the potentially reduced arity
631
   (loop for pattern in patterns
632
         do (setf (pattern-propagated-dimensions pattern) (intersection (pattern-dimensions pattern) bound-variables)))
633
   ;; then pick the least-resource option and decide whether to merge or match
634
   (sort patterns #'pattern-precedes :key *pattern-sort-key*))
635
 
636
 
637
 (defmethod compute-pattern-combination ((pattern bgp:and) field ordered-patterns bound-variables)
638
   ;; pick the least-resource option and decide whether to merge or match
639
   (let* ((next-pattern (first ordered-patterns))
640
          (operator nil))
641
     (let* ((field-count (solution-field-count field))
642
            (pattern-count (pattern-count next-pattern))
643
            (match-cost (/ field-count (repository-match-rate *transaction*)))
644
            (scan-cost (/ pattern-count (repository-scan-rate *transaction*))))
645
       (setf operator (if (or (> match-cost scan-cost)
646
                              (null (intersection (solution-field-dimensions field)
647
                                                  (pattern-dimensions next-pattern))))
648
                        'merge-and-pattern
649
                        'match-and-pattern)))
650
     operator))
651
 
652
 
653
 ;;;
654
 ;;; bgp:and-sum-left
655
 
656
 (defclass bgp:and-sum-left (applicable-pattern-combination)
657
   ((operator
658
     :initform 'bgp:and-sum-left :allocation :class))
659
   (:metaclass applicable-pattern-class)
660
   (:documentation "The class of wrapped pattern functions for optionally, conjunctively joined patterns.
661
    for a 'leftjoin', the inital pattern is dominant and the incrementally first joined and then,
662
    for primary solutions which fail to match, unioned with the dependent patterns. 
663
    as each successive combination extends the initial field, the conditions for matching can change."))
664
 
665
 ;;; (funcall (make-instance bgp:and-sum-left :forms (list (bgp:quad 1 2 3 nil))) nil nil)
666
 
667
 (defmethod spocq.e:leftjoin ((field1 applicable-pattern) (field2 applicable-pattern) &rest args)
668
   (apply #'bgp:and-sum-left field1 field2 args))
669
 
670
 (defparameter bgp:and-sum-left 'bgp:and-sum-left)
671
 
672
 (define-compiler-macro bgp:and-sum-left (&rest arguments)
673
   (let* ((initargs (member-if #'keywordp arguments))
674
          (patterns (ldiff arguments initargs)))
675
     (if patterns
676
       (if (rest patterns)
677
         `(make-instance bgp:and-sum-left :forms (list ,@patterns) ,@initargs)
678
         (append (first patterns) initargs))
679
       `(bgp:null ,@initargs))))
680
 
681
 (defun bgp:and-sum-left (&rest arguments)
682
   (let* ((initargs (member-if #'keywordp arguments))
683
          (patterns (ldiff arguments initargs)))
684
     (if patterns
685
       (if (rest patterns)
686
         (apply #'make-instance bgp:and-sum-left :forms patterns initargs)
687
         (let ((arg (first patterns)))
688
           (when initargs
689
             (apply #'reinitialize-instance (first patterns) initargs))
690
           arg))
691
       (apply #'bgp:null initargs))))
692
 
693
 
694
 (defmethod process-pattern ((pattern bgp:and-sum-left) (result-field matrix-field) bindings modifiers)
695
   "perform the same process as for bgp:and, but with different combination operators"
696
 
697
   (let ((forms (copy-list (pattern-forms pattern))))
698
     (if forms
699
       (if (rest forms)
700
         (let ((effective-pattern (pattern-effective-pattern pattern)))
701
           (if effective-pattern
702
             (funcall effective-pattern pattern result-field bindings modifiers)
703
             (loop with bound-variables = (binding-argument-list-dimensions bindings)
704
                   for (next-form . remaining-forms) = (sort-bound-patterns forms bound-variables)
705
                   for combination-operator = nil
706
                   then (compute-pattern-combination pattern successive-result remaining-forms bound-variables)
707
                   for successive-result  = (funcall next-form nil bindings nil)
708
                   then (funcall combination-operator pattern (unless remaining-forms result-field)
709
                                 successive-result next-form
710
                                 bindings
711
                                 (unless remaining-forms modifiers))
712
                   for pattern-expression = `(,next-form nil bindings nil)
713
                   then `(,combination-operator ,pattern ,(unless remaining-forms 'result-field)
714
                                                ,pattern-expression ,next-form
715
                                                bindings
716
                                                ,(unless remaining-forms 'modifiers))
717
                   do (setf bound-variables (join-result-dimensions (solution-field-dimensions successive-result) bound-variables)
718
                            forms remaining-forms)
719
                   ;; cache prospectively for subsequent compilation
720
                   ;; returns the successive result - as is, if it was created locally, the combination completed it
721
                   ;; otherwise leave it incomplete for continued use
722
                   finally (let ((lambda `(lambda (pattern result-field bindings modifiers)
723
                                            (declare (ignoreable pattern))
724
                                            ,pattern-expression)))
725
                             (unless bindings
726
                               (setf (pattern-effective-pattern-lambda pattern) lambda
727
                                     (pattern-effective-pattern pattern) (spocq-compile lambda)))
728
                             (return successive-result)))))
729
         (funcall (first forms) result-field bindings modifiers))
730
       (matrix-null result-field (pattern-dimensions pattern)))))
731
 
732
 
733
 (defmethod merge-and-sum-left-pattern ((op-pattern t) result-field field next-pattern bindings modifiers)
734
   (flet ((matrix-leftjoin (&rest args)
735
            (error "NYI: matrix-leftjoin ~s" args)))
736
     (apply #'matrix-leftjoin result-field field (funcall next-pattern nil bindings nil) modifiers)))
737
 
738
 (defmethod match-and-sum-left-pattern (op-pattern (result-field null) field next-pattern bindings modifiers)
739
   (complete-field-data (match-and-sum-left-pattern op-pattern (make-matrix-field) field next-pattern bindings modifiers)))
740
 
741
 (defmethod match-and-sum-left-pattern ((op-pattern t) (result-field matrix-field) field next-pattern bindings modifiers)
742
   "perform an incremental left-sum join where each step first performs a join as constrained with the
743
  propagated values, but then, if no solution resulted, adds the base solution without the extension."
744
   (if (plusp (solution-field-length field))
745
     (let ((projector (matrix-project-solution-operator (join-result-dimensions (solution-field-dimensions field)
746
                                                                                (pattern-dimensions next-pattern))
747
                                                        (solution-field-dimensions field))))
748
       (flet ((match-with-extended-bindings (propagated-bindings)
749
                (let ((initial-result-count (matrix-field-length result-field)))
750
                  (setf result-field (funcall next-pattern result-field propagated-bindings modifiers))
751
                  (unless (> (matrix-field-length result-field) initial-result-count)
752
                    (multiple-value-bind (%result-data result-row) (new-field-row result-field)
753
                      (multiple-value-bind (%base-data base-row) (current-field-row field)
754
                        (funcall projector %result-data result-row %base-data base-row)))))))
755
         (declare (dynamic-extent #'match-with-extended-bindings))
756
         (call-with-extended-bindings #'match-with-extended-bindings field (pattern-dimensions next-pattern) bindings)
757
         result-field)
758
       (matrix-null result-field (join-result-dimensions (solution-field-dimensions field) (pattern-dimensions next-pattern))))))
759
 
760
 
761
 (defmethod compute-pattern-combination ((pattern bgp:and-sum-left) field ordered-patterns bound-variables)
762
   ;; pick the least-resource option and decide whether to merge or match
763
   (let* ((next-pattern (first ordered-patterns))
764
          (operator nil))
765
     (let* ((field-count (solution-field-count field))
766
            (pattern-count (pattern-count next-pattern))
767
            (match-cost (/ field-count (repository-match-rate *transaction*)))
768
            (scan-cost (/ pattern-count (repository-scan-rate *transaction*))))
769
       (setf operator (if (or (> match-cost scan-cost)
770
                              (null (intersection (solution-field-dimensions field)
771
                                                  (pattern-dimensions next-pattern))))
772
                        'merge-and-sum-left-pattern
773
                          'match-and-sum-left-pattern)))
774
     operator))
775
 
776
 ;;;
777
 ;;; bgp:ask
778
 
779
 (defclass bgp:ask (applicable-pattern-combination)
780
   ((operator
781
     :initform 'bgp:ask :allocation :class))
782
   (:metaclass applicable-pattern-class)
783
   (:documentation "The class of wrapped pattern functions for ask queries"))
784
 
785
 
786
 (defparameter bgp:ask 'bgp:ask)
787
 
788
 (define-compiler-macro bgp:ask (pattern &rest initargs)
789
   `(make-instance bgp:ask :forms (list ,pattern) ,@initargs))
790
 
791
 (defun bgp:ask (pattern &rest initargs)
792
   (declare (dynamic-extent initargs))
793
   (apply #'make-instance bgp:ask :forms (list pattern) initargs))
794
 
795
 
796
 (defmethod process-pattern ((pattern bgp:ask) (result-field matrix-field) bindings modifiers)
797
   (flet ((matrix-ask (&rest args)
798
            (error "NYI: matrix-ask ~s" args)))
799
     (matrix-ask result-field (funcall (first (pattern-forms pattern)) nil bindings modifiers))))
800
 
801
 
802
 ;;;
803
 ;;; bgp:bindings
804
 
805
 (defclass bgp:bindings (applicable-pattern)
806
   ((operator
807
     :initform 'bgp:bindings :allocation :class)
808
    (solution-data
809
     :initform (error "solution-data is required") :initarg :solution-data
810
     :accessor pattern-solution-data))
811
   (:metaclass applicable-pattern-class)
812
   (:documentation "The class of wrapped pattern functions for generating static solution fields"))
813
 
814
 
815
 (defparameter bgp:bindings 'bgp:bindings)
816
 
817
 (define-compiler-macro bgp:bindings (&whole whole solution-data &rest initargs)
818
   (if (symbolp (first solution-data))
819
     whole
820
     `(make-instance bgp:bindings :solution-data ',solution-data ,@initargs)))
821
 
822
 (defun bgp:bindings (solution-data &rest initargs)
823
   (declare (dynamic-extent initargs))
824
   (apply #'make-instance bgp:bindings :solution-data solution-data initargs))
825
 
826
 
827
 (defmethod process-pattern ((pattern bgp:bindings) (result-field matrix-field) bindings modifiers)
828
   "gnerate a solution field for the given dimensions and solution data.
829
  where bindings are provided, perform any variable substitutions"
830
   (flet ((matrix-bindings (&rest args)
831
            (error "NYI: matrix-bindings ~s" args)))
832
     (let ((solution-data (pattern-solution-data pattern)))
833
       (when bindings
834
         (setf solution-data (sublis bindings solution-data)))
835
       (matrix-bindings result-field solution-data (pattern-dimensions pattern)))))
836
 
837
 
838
 ;;;
839
 ;;; bgp:construct
840
 
841
 (defclass bgp:construct (applicable-pattern-combination)
842
   ((operator
843
     :initform 'bgp:construct :allocation :class)
844
    (solution-data
845
     :initform (error "solution-data is required") :initarg :solution-data
846
     :accessor pattern-solution-data))
847
   (:metaclass applicable-pattern-class)
848
   (:documentation "The class of wrapped pattern functions for generating static solution fields"))
849
 
850
 
851
 (defparameter bgp:construct 'bgp:construct)
852
 
853
 (define-compiler-macro bgp:construct (&whole whole pattern solution-data &rest initargs)
854
   (if (symbolp (first solution-data))
855
     whole
856
     `(make-instance bgp:construct :forms (list ,pattern) :solution-data ',solution-data ,@initargs)))
857
 
858
 (defun bgp:construct (pattern solution-data &rest initargs)
859
   (declare (dynamic-extent initargs))
860
   (apply #'make-instance bgp:construct :forms (list pattern) :solution-data solution-data initargs))
861
 
862
 
863
 (defmethod process-pattern ((pattern bgp:construct) (result-field matrix-field) bindings modifiers)
864
   "generate a solution field for the given dimensions and solution pattern.
865
  resolve any bindings from the given solution field"
866
 
867
   (flet ((matrix-construct (&rest args)
868
            (error "NYI: matrix-construct ~s" args)))
869
     (let ((solution-data (pattern-solution-data pattern))
870
           (source-field (funcall (first (pattern-forms pattern)) nil bindings modifiers)))
871
       (when bindings
872
         (setf solution-data (sublis bindings solution-data)))
873
       (matrix-construct result-field source-field solution-data))))
874
 
875
 
876
 ;;;
877
 ;;; bgp:describe
878
 
879
 (defclass bgp:describe (applicable-pattern-combination)
880
   ((operator
881
     :initform 'bgp:describe :allocation :class)
882
    (resources
883
     :initform (error "resources is required") :initarg :resources
884
     :accessor pattern-resources))
885
   (:metaclass applicable-pattern-class)
886
   (:documentation "The class of wrapped pattern functions for generating resource-descriptions"))
887
 
888
 
889
 (defparameter bgp:describe 'bgp:describe)
890
 
891
 (define-compiler-macro bgp:describe (pattern subject-resources &rest initargs)
892
   `(make-instance bgp:describe :forms (list ,pattern) :resources ',subject-resources ,@initargs))
893
 
894
 (defun bgp:describe (pattern subject-resources &rest initargs)
895
   (declare (dynamic-extent initargs))
896
   (apply #'make-instance bgp:describe
897
          :forms (list pattern)
898
          :resources subject-resources
899
          initargs))
900
 
901
 
902
 (defmethod process-pattern ((pattern bgp:describe) (result-field matrix-field) bindings modifiers)
903
   "generate a solution field for the given dimensions and solution pattern.
904
  resolve any bindings from the given solution field"
905
 
906
   (flet ((matrix-describe (&rest args)
907
            (error "NYI: matrix-describe ~s" args)))
908
     (let ((resources (pattern-resources pattern))
909
           (source-field (funcall (first (pattern-forms pattern)) nil bindings modifiers)))
910
       (matrix-describe result-field source-field resources))))
911
 
912
 
913
 ;;;
914
 ;;; diff  : incorporates the solutions from the first sub-expression which do not appear in a successor
915
 
916
 (defclass bgp:diff (applicable-pattern-combination)
917
   ((operator
918
     :initform 'bgp:diff))
919
   (:metaclass applicable-pattern-class)
920
   (:documentation "The class of wrapped pattern functions for differentially merged BGP statement patterns.
921
    - scan each pattern invoke the pattern w/ no solution
922
    - merge-diff successive patterns to yield the result field."))
923
 
924
 
925
 (defparameter bgp:diff 'bgp:diff)
926
 
927
 (define-compiler-macro bgp:diff (&rest arguments)
928
   (let* ((initargs (member-if #'keywordp arguments))
929
          (patterns (ldiff arguments initargs)))
930
     (if (and (null initargs) (null (rest patterns)))
931
       (first patterns)
932
       `(make-instance bgp:diff
933
          :forms (list ,@patterns)
934
          ,@initargs))))
935
 
936
 (defun bgp:diff (&rest arguments)
937
   (let* ((initargs (member-if #'keywordp arguments))
938
          (patterns (ldiff arguments initargs)))
939
     (if (and (null initargs) (null (rest patterns)))
940
       (first patterns)
941
       (apply #'make-instance bgp:diff
942
              :forms patterns
943
              initargs))))
944
 
945
 
946
 (defmethod process-pattern ((pattern bgp:diff) (result-field matrix-field) bindings modifiers)
947
   (flet ((matrix-diff (&rest args)
948
            (error "NYI: matrix-diff ~s" args)))
949
     (loop for form in (pattern-forms pattern)
950
           for rest-forms on (rest (pattern-forms pattern))
951
           for field = (funcall form
952
                                (when (null rest-forms) result-field)
953
                                bindings
954
                                (when (null rest-forms) modifiers))
955
           then (if (null rest-forms)
956
                  (apply #'matrix-diff result-field field (funcall form nil bindings nil) modifiers)
957
                  (matrix-diff nil field (funcall form nil bindings nil)))
958
           finally (return field))))
959
 
960
 
961
 ;;;
962
 ;;; distinct  : eliminate duplicates from the source field
963
 
964
 (defclass bgp:distinct (applicable-pattern-combination)
965
   ((operator
966
     :initform 'bgp:distinct))
967
   (:metaclass applicable-pattern-class)
968
   (:documentation "The class of wrapped pattern functions for distinct fields.
969
    - compute the source pattern
970
    - use the given dimensions to project and eliminate duplicates"))
971
 
972
 
973
 (defparameter bgp:distinct 'bgp:distinct)
974
 
975
 (define-compiler-macro bgp:distinct (pattern &rest initargs)
976
   `(make-instance bgp:distinct
977
          :forms (list ,pattern)
978
          ,@initargs))
979
 
980
 (defun bgp:distinct (pattern &rest initargs)
981
   (declare (dynamic-extent initargs))
982
   (apply #'make-instance bgp:distinct
983
          :forms (list pattern)
984
          initargs))
985
 
986
 
987
 (defmethod process-pattern ((pattern bgp:distinct) (result-field matrix-field) bindings modifiers)
988
   (flet ((matrix-distinct (&rest args)
989
            (error "NYI: matrix-distinct ~s" args)))
990
     (apply #'matrix-distinct result-field (funcall (first (pattern-forms pattern)) nil bindings nil)
991
            modifiers)))
992
 
993
 
994
 ;;;
995
 ;;; extend : extend the source field according to binding specifications
996
 
997
 (defclass bgp:extend (applicable-pattern-combination)
998
   ((operator
999
     :initform 'bgp:extend)
1000
    (bindings
1001
     :initform (error "bindings is required.") :initarg :bindings
1002
     :accessor pattern-bindings))
1003
   (:metaclass applicable-pattern-class)
1004
   (:documentation "The class of wrapped pattern functions for extended fields.
1005
    - compute the source pattern
1006
    - extend the result according to the bindings."))
1007
 
1008
 
1009
 (defparameter bgp:extend 'bgp:extend)
1010
 
1011
 (define-compiler-macro bgp:extend (pattern bindings &rest initargs)
1012
   `(make-instance bgp:extend
1013
          :forms (list ,pattern)
1014
          :bindings ',bindings
1015
          ,@initargs))
1016
 
1017
 (defun bgp:extend (pattern bindings &rest initargs)
1018
   (apply #'make-instance bgp:extend
1019
          :forms (list pattern)
1020
          :bindings bindings
1021
          initargs))
1022
 
1023
 
1024
 
1025
 ;;;!!!! need to change the coll patter not to pass the result
1026
 (defmethod process-pattern ((pattern bgp:extend) (result-field matrix-field) bindings modifiers)
1027
   (apply #'spocq.e:extend (funcall (first (pattern-forms pattern)) nil bindings nil)
1028
          (pattern-bindings pattern)
1029
          modifiers))
1030
 
1031
 
1032
 ;;;
1033
 ;;; filter  : apply the test for to each solution and eliminate those which fail
1034
 
1035
 (defclass bgp:filter (applicable-pattern-combination)
1036
   ((operator
1037
     :initform 'bgp:filter)
1038
    (test
1039
     :initform (error "test is required.") :initarg :test
1040
     :accessor pattern-test))
1041
   (:metaclass applicable-pattern-class)
1042
   (:documentation "The class of wrapped pattern functions for filtered fields.
1043
    - compute the source pattern
1044
    - filter the result against the test end eilinate those which fail."))
1045
 
1046
 
1047
 (defparameter bgp:filter 'bgp:filter)
1048
 
1049
 (define-compiler-macro bgp:filter (pattern test &rest initargs)
1050
   `(make-instance bgp:filter
1051
          :forms (list ,pattern)
1052
          :test ',test
1053
          ,@initargs))
1054
 
1055
 (defun bgp:filter (pattern test &rest initargs)
1056
   (apply #'make-instance bgp:filter
1057
          :forms (list pattern)
1058
          :test test
1059
          initargs))
1060
 
1061
 
1062
 (defmethod process-pattern ((pattern bgp:filter) (result-field matrix-field) bindings modifiers)
1063
   (flet ((matrix-filter (&rest args)
1064
            (error "NYI: matrix-filter ~s" args)))
1065
     (apply #'matrix-filter result-field (funcall (first (pattern-forms pattern)) nil bindings nil)
1066
            (pattern-test pattern)
1067
            modifiers)))
1068
 
1069
 
1070
 ;;;
1071
 ;;; group
1072
 
1073
 (defclass bgp:group (applicable-pattern-combination)
1074
   ((operator
1075
     :initform 'bgp:group)
1076
    (bindings
1077
     :initform (error "bindings is required.") :initarg :bindings
1078
     :accessor pattern-bindings))
1079
   (:metaclass applicable-pattern-class)
1080
   (:documentation "The class of wrapped pattern functions for grouped fields.
1081
    - compute the source pattern
1082
    - group the result according to the bindings."))
1083
 
1084
 
1085
 (defparameter bgp:group 'bgp:group)
1086
 
1087
 (define-compiler-macro bgp:group (pattern bindings &rest initargs)
1088
   `(make-instance bgp:group
1089
          :forms (list ,pattern)
1090
          :bindings ',bindings
1091
          ,@initargs))
1092
 
1093
 (defun bgp:group (pattern bindings &rest initargs)
1094
   (apply #'make-instance bgp:group
1095
          :forms (list pattern)
1096
          :bindings bindings
1097
          initargs))
1098
 
1099
 
1100
 (defmethod process-pattern ((pattern bgp:group) (result-field matrix-field) bindings modifiers)
1101
   (flet ((matrix-group (&rest args)
1102
            (error "NYI: matrix-group ~s" args)))
1103
     (apply #'matrix-group result-field (funcall (first (pattern-forms pattern)) nil bindings nil)
1104
            (pattern-bindings pattern)
1105
            modifiers)))
1106
 
1107
 
1108
 ;;;
1109
 ;;; index-match
1110
 
1111
 (defclass bgp:index-match (applicable-pattern)
1112
   ((operator
1113
     :initform 'bgp:index-match :allocation :class)
1114
    (index-expression
1115
     :initform (error "index-expression is required.") :initarg :index-expression
1116
     :accessor pattern-index-expression)
1117
    (score-dimension
1118
     :initform nil :initarg :score-dimension
1119
     :accessor pattern-score-dimension))
1120
   (:metaclass applicable-pattern-class)
1121
   (:documentation "The class of wrapped predicates which translate BGP statement patterns and related
1122
    filter expressions into indexed retrieval operations."))
1123
 
1124
 
1125
 (defparameter bgp:index-match 'bgp:index-match)
1126
 
1127
 (define-compiler-macro bgp:index-match (subject predicates objects context &rest initargs &key dimensions score-dimension test index-expression)
1128
   (declare (ignore dimensions score-dimension test index-expression))
1129
   `(make-instance bgp:index-match
1130
      ;; nb. terms are retained as s-p-o-c as per a quad form.
1131
      :terms ',(list subject predicates objects context)
1132
      ,@initargs))
1133
 
1134
 (defun bgp:index-match (subject predicates objects context &rest initargs &key
1135
                                 dimensions score-dimension test index-expression)
1136
   (declare (ignore dimensions score-dimension test index-expression)
1137
            (dynamic-extent initargs))
1138
   (apply #'make-instance bgp:index-match
1139
          :terms (list subject predicates objects context)
1140
          initargs))
1141
 
1142
 (defmethod initialize-instance ((instance bgp:index-match) &rest initargs &key terms test index-expression)
1143
   (destructuring-bind (subject predicates objects context) terms
1144
     (declare (ignore subject context))
1145
     (apply #'call-next-method instance
1146
            :index-expression (or index-expression
1147
                                  (let ((map (loop for predicate in predicates
1148
                                                   for object in objects
1149
                                                   collect (cons object predicate)))
1150
                                        (index (loop with index = nil
1151
                                                     for predicate in predicates
1152
                                                     for predicate-index = (repository-index *transaction* predicate)
1153
                                                     if index
1154
                                                     do (assert (eq index predicate-index))
1155
                                                     else do (setf index predicate-index)
1156
                                                     finally (return index))))
1157
                                    (translate-index-expression index test map)))
1158
            initargs)))
1159
 
1160
 
1161
 (defmethod process-pattern ((pattern bgp:index-match) (result-field matrix-field) bindings modifiers)
1162
   (labels ((variable-value (term)
1163
              (if (variable-p term)
1164
                (term-number-object (or (getf bindings term)
1165
                                        (symbol-value term)))
1166
                term))
1167
            (ensure-variable (term &optional (default nil))
1168
              (cond ((variable-p term) term)
1169
                    (default )
1170
                    (t (cons-variable "bgp")))))
1171
     (destructuring-bind (subject predicates objects context) (pattern-terms pattern)
1172
       (let* ((score-dimension (pattern-score-dimension pattern))
1173
              (dimensions (list* (ensure-variable context +context-variable+)
1174
                                 (ensure-variable subject)
1175
                                 (append objects (when score-dimension (list score-dimension))))))
1176
         (let* ((solutions (matrix-field-solutions result-field))
1177
                (%xaction (transaction-record *transaction*))
1178
                (new-solutions (rdfcache::index-match %xaction solutions
1179
                                                      context subject
1180
                                                      (map-into (make-array (length predicates)) #'object-term-number predicates)
1181
                                                      (pattern-index-expression pattern)
1182
                                                      (not (null score-dimension)))))
1183
           (unless (eq solutions new-solutions)
1184
             (setf (matrix-field-solutions result-field) new-solutions))
1185
           (setf (solution-field-dimensions result-field) dimensions
1186
                 (solution-field-sort-dimensions result-field) (remove nil dimensions))
1187
           result-field)))))
1188
 
1189
 ;;;
1190
 ;;; not
1191
 
1192
 (defclass bgp:not (applicable-pattern-combination)
1193
   ((operator
1194
     :initform 'bgp:not))
1195
   (:metaclass applicable-pattern-class)
1196
   (:documentation "The class of wrapped pattern functions for inversion merged BGP statement patterns.
1197
    given a repository transaction:
1198
    - match the constituent pattern, invoking it w/any provided bindings.
1199
    - if it succeeds, return null, if it does not match, return a unit table."))
1200
 
1201
 
1202
 (defparameter bgp:not 'bgp:not)
1203
 
1204
 (define-compiler-macro bgp:not (form &rest initargs)
1205
   `(make-instance bgp:not
1206
      :forms (list ,form)
1207
      ,@initargs))
1208
 
1209
 (defun bgp:not (form &rest initargs)
1210
   (declare (dynamic-extent initargs))
1211
   (apply #'make-instance bgp:not
1212
          :forms (list form)
1213
          initargs))
1214
 
1215
 
1216
 (defmethod process-pattern ((pattern bgp:not) (result-field matrix-field) bindings modifiers)
1217
   (destructuring-bind (form) (pattern-forms pattern)    ; permit one constituent only
1218
     (let ((result (funcall form nil bindings (or modifiers '(:start 0 :end 1)))))
1219
       (if (plusp (solution-field-length result))
1220
         (matrix-null result-field (solution-field-dimensions result))
1221
         (matrix-table result-field (solution-field-dimensions result))))))
1222
 
1223
 
1224
 ;;;
1225
 ;;; null
1226
 
1227
 (defclass bgp:null (applicable-pattern)
1228
   ((operator
1229
     :initform 'bgp:null))
1230
   (:metaclass applicable-pattern-class)
1231
   (:documentation "The class of null patterns"))
1232
 
1233
 
1234
 (defparameter bgp:null 'bgp:null)
1235
 
1236
 (define-compiler-macro bgp:null (&rest args &key dimensions)
1237
   (declare (ignore dimensions))
1238
   `(make-instance bgp:null ,@args))
1239
 
1240
 (defun bgp:null (&rest args &key dimensions)
1241
   (declare (dynamic-extent args)
1242
            (ignore dimensions))
1243
   (apply #'make-instance bgp:null args))
1244
 
1245
 (defmethod process-pattern ((pattern bgp:null) (result-field matrix-field) (bindings t) (modifiers t))
1246
   (matrix-null result-field (pattern-dimensions pattern)))
1247
 
1248
 
1249
 ;;;
1250
 ;;; or    : incorporates the solutions from the first sub-expression which supplies it
1251
 
1252
 (defclass bgp:or (applicable-pattern-combination)
1253
   ((operator
1254
     :initform 'bgp:or))
1255
   (:metaclass applicable-pattern-class)
1256
   (:documentation "The class of wrapped pattern functions for disjunctively merged BGP statement patterns.
1257
    given a repository transaction:
1258
    - scan each pattern invoke the pattern w/ no solution
1259
    - merge or to yield a new field and use it as the base field"))
1260
 
1261
 
1262
 (defparameter bgp:or 'bgp:or)
1263
 
1264
 (define-compiler-macro bgp:or (&rest arguments)
1265
   (let* ((initargs (member-if #'keywordp arguments))
1266
          (patterns (ldiff arguments initargs)))
1267
     (if (and (null initargs) (null (rest patterns)))
1268
       (first patterns)
1269
       `(make-instance bgp:or
1270
          :forms (list ,@patterns)
1271
          ,@initargs))))
1272
 
1273
 (defun bgp:or (&rest arguments)
1274
   (let* ((initargs (member-if #'keywordp arguments))
1275
          (patterns (ldiff arguments initargs)))
1276
     (if (and (null initargs) (null (rest patterns)))
1277
       (first patterns)
1278
       (apply #'make-instance bgp:or
1279
              :forms patterns
1280
              initargs))))
1281
 
1282
 
1283
 (defmethod process-pattern ((pattern bgp:or) (result-field matrix-field) bindings modifiers)
1284
   (loop for form in (pattern-forms pattern)
1285
         for forms on (pattern-forms pattern)
1286
         for field = (funcall form 
1287
                              (when (null (rest forms)) result-field) 
1288
                              bindings
1289
                              (when (null (rest forms)) modifiers))
1290
         then (funcall #'merge-or-pattern pattern (when (null (rest forms)) result-field) 
1291
                       field
1292
                       form
1293
                       bindings
1294
                       (when (null (rest forms)) modifiers))
1295
         finally (return field)))
1296
 
1297
 (defmethod merge-or-pattern ((op-pattern t) result-field field pattern bindings modifiers)
1298
   (flet ((matrix-or (&rest args)
1299
            (error "NYI: matrix-or ~s" args)))
1300
     (apply #'matrix-or result-field field (funcall nil pattern bindings nil) modifiers)))
1301
 
1302
 
1303
 ;;;
1304
 ;;; order  : order the source field by ascending/descending dimensions 
1305
 
1306
 (defclass bgp:order (applicable-pattern-combination)
1307
   ((operator
1308
     :initform 'bgp:order)
1309
    (order-predicate-form
1310
     :initform (error "order-predicate-form is required.") :initarg :order-predicate-form
1311
     :accessor pattern-order-predicate-form))
1312
   (:metaclass applicable-pattern-class)
1313
   (:documentation "The class of wrapped pattern functions for ordered fields.
1314
    - compute the source pattern
1315
    - order the result according to the predicates."))
1316
 
1317
 
1318
 (defparameter bgp:order 'bgp:order)
1319
 
1320
 (define-compiler-macro bgp:order (pattern order-predicate-form &rest initargs)
1321
   `(make-instance bgp:order
1322
          :forms (list ,pattern)
1323
          :order-predicate-form ',order-predicate-form
1324
          ,@initargs))
1325
 
1326
 (defun bgp:order (pattern order-predicate-form &rest initargs)
1327
   (apply #'make-instance bgp:order
1328
          :forms (list pattern)
1329
          :order-predicate-form order-predicate-form
1330
          initargs))
1331
 
1332
 
1333
 (defmethod process-pattern ((pattern bgp:order) (result-field matrix-field) bindings modifiers)
1334
   (flet ((matrix-order (&rest args)
1335
            (error "NYI: matrix-order ~s" args)))
1336
     (apply #'matrix-order result-field (funcall (first (pattern-forms pattern)) nil bindings nil)
1337
            :order-predicate-form (pattern-order-predicate-form pattern)
1338
            modifiers)))
1339
 
1340
 
1341
 ;;;
1342
 ;;; project  : reduce the filed to the given dimensions 
1343
 
1344
 (defclass bgp:project (applicable-pattern-combination)
1345
   ((operator
1346
     :initform 'bgp:project))
1347
   (:metaclass applicable-pattern-class)
1348
   (:documentation "The class of wrapped pattern functions for projected fields.
1349
    - compute the source pattern
1350
    - project the result according to the given dimensions"))
1351
 
1352
 
1353
 (defparameter bgp:project 'bgp:project)
1354
 
1355
 (define-compiler-macro bgp:project (pattern dimensions &rest initargs)
1356
   `(make-instance bgp:project
1357
          :forms (list ,pattern)
1358
          ;; ignore any keyword dimensions
1359
          :dimensions ',dimensions
1360
          ,@initargs))
1361
 
1362
 (defun bgp:project (pattern dimensions &rest initargs)
1363
   (apply #'make-instance bgp:project
1364
          :forms (list pattern)
1365
          ;; ignore any keyword dimensions
1366
          :dimensions dimensions
1367
          initargs))
1368
 
1369
 
1370
 (defmethod process-pattern ((pattern bgp:project) (result-field matrix-field) bindings modifiers)
1371
   (apply #'process-project result-field (funcall (first (pattern-forms pattern)) nil bindings nil)
1372
          (pattern-dimensions pattern)
1373
          modifiers))
1374
 
1375
 
1376
 ;;;
1377
 ;;; quad
1378
 
1379
 (defclass bgp:quad (applicable-pattern-statement)
1380
   ((operator
1381
     :initform 'bgp:quad :allocation :class))
1382
   (:metaclass applicable-pattern-class)
1383
   (:documentation "The class of wrapped pattern functions individual BGP statement patterns.
1384
    in the context of the current *transaction* repository:
1385
    - establish the effective context given the current *dataset* and a variable v/s constant graph term
1386
    - combine the stated pattern with any given bindings to yield an effective pattern
1387
    - match the effective pattern with the repository
1388
    - accumulate successive matches if the dataset enumerates constant graphs 
1389
    - constrain the result given solution modifiers."))
1390
 
1391
 (defun make-pattern (type &rest args)
1392
   (declare (dynamic-extent args))
1393
   (apply #'make-instance type args))
1394
 
1395
 (defparameter bgp:quad 'bgp:quad)
1396
 
1397
 (define-compiler-macro bgp:quad (subject predicate object context &key dimensions count)
1398
   `(make-instance bgp:quad
1399
      ;; nb. terms are retained as s-p-o-c as per a quad form.
1400
      :terms ',(list subject predicate object context)
1401
      ,@(when dimensions `(:dimensions ',dimensions))
1402
      ,@(when count `(:count ,count))))
1403
 
1404
 (defun bgp:quad (subject predicate object context &rest initargs &key dimensions count)
1405
   (declare (dynamic-extent initargs)
1406
            (ignore dimensions count))
1407
   (apply #'make-instance bgp:quad
1408
      ;; nb. terms are retained as s-p-o-c as per a quad form.
1409
      :terms (list subject predicate object context)
1410
      initargs))
1411
      
1412
 
1413
 ;;; (bgp:quad ?::s <http://> ?::o ?::g)
1414
 ;;; (bgp:quad ?::g <http://> ?::o ?::g)
1415
 
1416
 
1417
 (defmethod process-pattern ((pattern bgp:quad) (result-field matrix-field) bindings modifiers)
1418
   (destructuring-bind (subject predicate object context) (interned-pattern-terms pattern bindings)
1419
     (if context
1420
       (let* ((solutions (matrix-field-solutions result-field))
1421
              (new-solutions (repository-match-matrix *transaction* solutions
1422
                                                      context
1423
                                                      subject
1424
                                                      predicate
1425
                                                      object)))
1426
         (set-solution-field-solutions result-field new-solutions)
1427
         (setf (solution-field-dimensions result-field) (pattern-dimensions pattern)
1428
               (solution-field-sort-dimensions result-field) (pattern-dimensions pattern))
1429
         (incf-stat *solutions-processed* (solution-field-row-count result-field))
1430
         ;; (print-lexical-frame "bgp:quad")
1431
         result-field)
1432
       ;; if no context is present in the store, there can be no match
1433
       (matrix-null result-field (pattern-dimensions pattern)))))
1434
 
1435
 
1436
 (defmethod pattern-count ((pattern bgp:quad))
1437
   (or (get-pattern-count pattern)
1438
       (setf-pattern-count (apply #'repository-pattern-count *transaction* (pattern-terms pattern))
1439
                           pattern)))
1440
 
1441
 (defmethod print-object ((object bgp:quad) stream)
1442
   (print-unreadable-object (object stream :identity t :type t)
1443
     (format stream "~s.~s" (pattern-terms object) (pattern-dimensions object))))
1444
 
1445
 ;;; (compute-intrasolution-identity-test '(a nil s a))
1446
 ;;; (compute-intrasolution-identity-test '(a s s a))
1447
 ;;; (compute-intrasolution-identity-test '(a a nil a))
1448
 ;;; (compute-intrasolution-identity-test '(a a a a))
1449
 
1450
 
1451
 ;;;
1452
 ;;; quad-call
1453
 
1454
 (defclass bgp:quad-call (applicable-pattern-statement)
1455
   ((operator
1456
     :initform 'bgp:quad-call :allocation :class)
1457
    (extension-function
1458
     :initform (error "extension-function is required.") :initarg :extension-function))
1459
   (:metaclass applicable-pattern-class)
1460
   (:documentation "The class of extension function patterns combines an operator with the s/o/c terms.
1461
    in the context of the current *transaction* repository:
1462
    - establish the effective context given the current *dataset* and a variable v/s constant graph term
1463
    - combine the stated pattern with any given bindings to yield an effective pattern
1464
    - execute the extension function with the repository
1465
    - accumulate successive matches if the dataset enumerates constant graphs 
1466
    - constrain the result given solution modifiers."))
1467
 
1468
 (defparameter bgp:quad-call 'bgp:quad-call)
1469
 
1470
 #+(or)
1471
 (defmacro bgp:quad-call (subject predicate object context &rest initargs)
1472
   `(make-instance bgp:quad-call
1473
      ;; nb. terms are retained as s-p-o-c as per a quad form.
1474
      :terms ',(list subject predicate object context)
1475
      :extension-function ',predicate
1476
      ,@initargs))
1477
 
1478
 (defmacro bgp:quad-call (subject predicate object context &rest initargs)
1479
   (apply #'make-instance bgp:quad-call
1480
      ;; nb. terms are retained as s-p-o-c as per a quad form.
1481
      :terms (list subject predicate object context)
1482
      :extension-function predicate
1483
      initargs))
1484
 
1485
 (defmethod pattern-count ((pattern bgp:quad-call))
1486
   1)
1487
 
1488
 (defmethod process-pattern ((pattern bgp:quad-call) (result-field matrix-field) (bindings t) (modifiers t))
1489
   (error "quad-call is NYI."))
1490
  
1491
 
1492
 ;;;
1493
 ;;; quad-entail
1494
 
1495
 (defclass bgp:quad-entail (applicable-pattern-statement)
1496
   ((operator
1497
     :initform 'bgp:quad-entail :allocation :class)
1498
    (antecedent
1499
     :initform (error "antecedent is required.") :initarg :antecedent
1500
     :accessor pattern-antecedent))
1501
   (:metaclass applicable-pattern-class)
1502
   (:documentation "The class of wrapped pattern functions for entailed BGP statement patterns.
1503
    in the context of the current *transaction* repository:
1504
    - establish the effective context given the current *dataset* and a variable v/s constant graph term
1505
    - combine the stated pattern with any given bindings to yield an effective pattern
1506
    - match the effective pattern with the repository
1507
    - accumulate successive matches if the dataset enumerates constant graphs 
1508
    - constrain the result given solution modifiers.
1509
    if the primary form fails to yield results, proceeed through the antecedents"))
1510
 
1511
 
1512
 (defparameter bgp:quad-entail 'bgp:quad-entail)
1513
 
1514
 (define-compiler-macro bgp:quad-entail ((quad-op subject predicate object context) &rest arguments)
1515
   (declare (ignore quad-op))
1516
   (let* ((initargs (member-if #'keywordp arguments))
1517
          (patterns (ldiff arguments initargs)))
1518
   `(make-instance bgp:quad-entail
1519
      ;; nb. terms are retained as per quad form.
1520
      :terms ',(list subject predicate object context)
1521
      :antecedent (bgp:or ,@patterns)
1522
      ,@initargs)))
1523
 
1524
 (defun bgp:quad-entail (form &rest arguments)
1525
   (destructuring-bind (quad-op subject predicate object context) form
1526
     (declare (ignore quad-op))
1527
     (let* ((initargs (member-if #'keywordp arguments))
1528
            (patterns (ldiff arguments initargs)))
1529
       (apply #'make-instance bgp:quad-entail
1530
              ;; nb. terms are retained as per quad form.
1531
              ;; but de/reconstructed from the arguments
1532
              :terms (list subject predicate object context)
1533
              :antecedent (apply #'bgp:or patterns)
1534
              initargs))))
1535
 
1536
 
1537
 (defmethod pattern-count ((pattern bgp:quad-entail))
1538
   (or (get-pattern-count pattern)
1539
       (setf-pattern-count (+ (apply #'repository-pattern-count *transaction* (pattern-terms pattern))
1540
                              (pattern-count (pattern-antecedent pattern)))
1541
                           pattern)))
1542
 
1543
 
1544
 (defparameter *entailing-patterns* ())
1545
 
1546
 (defmethod process-pattern ((pattern bgp:quad-entail) (result-field matrix-field) bindings modifiers)
1547
   "apply an entailment pattern to a store by first attempting the base pattern. if that succeeds,
1548
  use the field which results. if it produces no results, attempt the antecedents, which are
1549
  captured as an autonomous operator."
1550
 
1551
   (destructuring-bind (subject predicate object context) (interned-pattern-terms pattern bindings)
1552
     (if context
1553
       (let* ((solutions (matrix-field-solutions result-field))
1554
              (new-solutions (repository-match-matrix *transaction* solutions
1555
                                                      context
1556
                                                      subject
1557
                                                      predicate
1558
                                                      object)))
1559
         (cond ((and (zerop (rdfcache:matrix-row-count solutions))
1560
                     (not (find pattern *entailing-patterns* :test #'equalp :key #'pattern-terms)))
1561
                ;; try the entailment 
1562
                (let ((*entailing-patterns* (cons pattern *entailing-patterns*)))
1563
                  (declare (dynamic-extent *entailing-patterns*))
1564
                  (funcall (pattern-antecedent pattern) nil bindings modifiers)))
1565
               (t
1566
                (unless (eq solutions new-solutions)
1567
                  (setf (matrix-field-solutions result-field) new-solutions))
1568
                (setf (solution-field-dimensions result-field) (pattern-dimensions pattern)
1569
                      (solution-field-sort-dimensions result-field) (pattern-dimensions pattern))
1570
                result-field)))
1571
       ;; if no context is present in the store, there can be no match
1572
       (matrix-null result-field (pattern-dimensions pattern)))))
1573
 
1574
 (defmethod pattern-count ((pattern bgp:quad-entail))
1575
   (or (get-pattern-count pattern)
1576
       (setf-pattern-count (+ (apply #'repository-pattern-count *transaction* (pattern-terms pattern))
1577
                              (pattern-count (pattern-antecedent pattern)))
1578
                           pattern)))
1579
 
1580
 
1581
 ;;;
1582
 ;;; quad-path
1583
 
1584
 (defclass bgp:quad-path (applicable-pattern-statement)
1585
   ((operator
1586
     :initform 'bgp:quad-path :allocation :class)
1587
    (path
1588
     :initform (error "path is required.") :initarg :path
1589
     :accessor pattern-path))
1590
   (:metaclass applicable-pattern-class)
1591
   (:documentation "The class of extension function patterns combines an operator with the s/o/c terms.
1592
    in the context of the current *transaction* repository:
1593
    - establish the effective context given the current *dataset* and a variable v/s constant graph term
1594
    - combine the stated pattern with any given bindings to yield an effective pattern
1595
    - execute the extension function with the repository
1596
    - accumulate successive matches if the dataset enumerates constant graphs 
1597
    - constrain the result given solution modifiers."))
1598
 
1599
 
1600
 (defparameter bgp:quad-path 'bgp:quad-path)
1601
 
1602
 (define-compiler-macro bgp:quad-path (subject predicate object context &key dimensions)
1603
   `(make-instance bgp:quad-path
1604
      ;; nb. terms are retained as s-p-o-c as per a quad form.
1605
      :terms '(,subject ,predicate ,object ,context)
1606
      :path ',predicate
1607
      ,@(when dimensions `(:dimensions ',dimensions))))
1608
 
1609
 (defun bgp:quad-path (subject predicate object context &rest initargs &key dimensions)
1610
   (declare (dynamic-extent initargs)
1611
            (ignore dimensions))
1612
   (apply #'make-instance bgp:quad-path
1613
      ;; nb. terms are retained as s-p-o-c as per a quad form.
1614
      :terms (list subject predicate object context)
1615
      :path predicate
1616
      initargs))
1617
 
1618
 
1619
 (defun process-path-pattern (pattern result-field bindings modifiers)
1620
   (declare (type  bgp:quad-path pattern))
1621
   (destructuring-bind (subject predicate object contexts) (interned-pattern-terms pattern bindings)
1622
     ;; if no context existed, then no match can succeed
1623
     (if contexts
1624
       (let ((result-field (initialize-result-field result-field :dimensions (pattern-dimensions pattern)))
1625
             (result-count 0))
1626
         (destructuring-bind (&key (start 0) end) modifiers
1627
           (flet ((process-path-pattern-continue (context subject matched-predicate object)
1628
                    (declare (ignore matched-predicate))
1629
                    (let ((%result-data (cffi:null-pointer))
1630
                          (result-row 0))
1631
                      (declare (foreign-type (foreign-array #.+matrix-element-type+ (* 4)) %result-data)
1632
                               (type cffi:foreign-pointer %result-data))
1633
                      (when (> (incf result-count) start)
1634
                        (when (and end (>= result-count end))
1635
                          (return-from process-path-pattern result-field))
1636
                        (setf (values %result-data result-row) (new-field-row result-field))
1637
                        (setf (foreign-array-ref %result-data result-row 0) context
1638
                              (foreign-array-ref %result-data result-row 1) subject
1639
                              (foreign-array-ref %result-data result-row 2) 0 ; predicate constant - at the moment, the object is returned
1640
                              (foreign-array-ref %result-data result-row 3) object)))))
1641
             (declare (dynamic-extent #'process-path-pattern-continue))
1642
             (loop for context in contexts
1643
                   do (match-property-path *transaction* context subject predicate object #'process-path-pattern-continue))))
1644
         result-field)
1645
       (matrix-null result-field (pattern-dimensions pattern)))))
1646
 
1647
 (defmethod process-pattern ((pattern bgp:quad-path) (result-field matrix-field) bindings modifiers)
1648
   ;; sbcl compiles a method body once w/o a lexical environmet, which would cause the array reference to fail
1649
   ;; therefor call out to a simple function
1650
   (process-path-pattern pattern  bindings modifiers result-field))
1651
 
1652
 (defmethod pattern-count ((pattern bgp:quad-path))
1653
   (or (get-pattern-count pattern)
1654
       (destructuring-bind (subject predicate object context) (pattern-terms pattern)
1655
         (declare (ignore predicate))
1656
         (setf-pattern-count (repository-pattern-count *transaction* subject nil object context)
1657
                             pattern))))
1658
 
1659
 
1660
 ;;;
1661
 ;;; slice  : order the source field by ascending/descending dimensions 
1662
 
1663
 (defclass bgp:slice (applicable-pattern-combination)
1664
   ((operator
1665
     :initform 'bgp:slice)
1666
    (slice
1667
     :initform (error "slice is required.") :initarg :slice
1668
     :accessor pattern-slice))
1669
   (:metaclass applicable-pattern-class)
1670
   (:documentation "The class of wrapped pattern functions for sliced fields.
1671
    - compute the source pattern
1672
    - slice the result according to the given bounds unless the dynamic value supersedes"))
1673
 
1674
 
1675
 (defparameter bgp:slice 'bgp:slice)
1676
 
1677
 (define-compiler-macro bgp:slice (pattern slice &rest initargs)
1678
   `(make-instance bgp:slice
1679
          :forms (list ,pattern)
1680
          :slice ',slice
1681
          ,@initargs))
1682
 
1683
 (defun bgp:slice (pattern slice &rest initargs)
1684
   (declare (dynamic-extent initargs))
1685
   (apply #'make-instance bgp:slice
1686
          :forms (list pattern)
1687
          :slice slice
1688
          initargs))
1689
 
1690
 
1691
 (defmethod process-pattern ((pattern bgp:slice) (result-field abstract-page-channel) bindings modifiers)
1692
   (apply #'process-slice result-field (funcall (first (pattern-forms pattern)) nil bindings nil)
1693
          (append modifiers (pattern-slice pattern))))
1694
 
1695
 
1696
 ;;;
1697
 ;;; union   : accumulate all matched solutions (as union)
1698
 
1699
 (defclass bgp:union (applicable-pattern-combination)
1700
   ((operator
1701
     :initform 'bgp:union))
1702
   (:metaclass applicable-pattern-class)
1703
   (:documentation "The class of wrapped pattern functions for summed  BGP statement patterns.
1704
    given a repository transaction:
1705
    - scan each pattern invoke the pattern w/ no solution
1706
    - merge excluding anu conjunctions to yield a new field and use it as the base field"))
1707
 
1708
 
1709
 (defparameter bgp:union 'bgp:union)
1710
 
1711
 (define-compiler-macro bgp:union (&rest arguments)
1712
   (let* ((initargs (member-if #'keywordp arguments))
1713
          (patterns (ldiff arguments initargs)))
1714
     (if (and (null initargs) (null (rest patterns)))
1715
       (first patterns)
1716
       `(make-instance bgp:union
1717
          :forms (list ,@patterns)
1718
          ,@initargs))))
1719
 
1720
 (defun bgp:union (&rest arguments)
1721
   (let* ((initargs (member-if #'keywordp arguments))
1722
          (patterns (ldiff arguments initargs)))
1723
     (if (and (null initargs) (null (rest patterns)))
1724
       (first patterns)
1725
       (apply #'make-instance bgp:union
1726
              :forms patterns
1727
              initargs))))
1728
 
1729
 
1730
 (defmethod process-pattern ((pattern bgp:union) (result-field matrix-field) bindings modifiers)
1731
   (loop for form in (pattern-forms pattern)
1732
         for forms on (pattern-forms pattern)
1733
         for field = (funcall form (when (null (rest forms)) result-field)
1734
                              bindings
1735
                              (when (null (rest forms)) modifiers))
1736
         then (funcall #'merge-union-pattern pattern
1737
                       (when (null (rest forms)) result-field)
1738
                       field form bindings
1739
                       (when (null (rest forms)) modifiers))
1740
         finally (return field)))
1741
 
1742
 (defmethod merge-union-pattern ((op-pattern t) (result-field null) field pattern bindings modifiers)
1743
   (complete-field-data (merge-union-pattern op-pattern (make-matrix-field) field pattern bindings modifiers)))
1744
 
1745
 (defmethod merge-union-pattern ((op-pattern t) (result-field matrix-field) field pattern bindings modifiers)
1746
   (flet ((matrix-union (&rest args)
1747
            (error "NYI: matrix-union ~s" args)))
1748
     (apply #'matrix-union result-field field (funcall pattern nil bindings nil) modifiers)))
1749
   
1750
 
1751
 ;;;
1752
 ;;; xor   : incorporates whichever solution succeeds, but only if there is exactly one
1753
 
1754
 (defclass bgp:xor (applicable-pattern-combination)
1755
   ((operator
1756
     :initform 'bgp:xor))
1757
   (:metaclass applicable-pattern-class)
1758
   (:documentation "The class of wrapped pattern functions for inversion merged BGP statement patterns.
1759
    given a repository transaction:
1760
    - scan each pattern invoke the pattern w/ no solution
1761
    - merge excluding any conjunctions to yield a new field and use it as the base field"))
1762
 
1763
 
1764
 (defparameter bgp:xor 'bgp:xor)
1765
 
1766
 (define-compiler-macro bgp:xor (&rest arguments)
1767
   (let* ((initargs (member-if #'keywordp arguments))
1768
          (patterns (ldiff arguments initargs)))
1769
     (if (and (null initargs) (null (rest patterns)))
1770
       (first patterns)
1771
       `(make-instance bgp:xor
1772
          :forms (list ,@patterns)
1773
          ,@initargs))))
1774
 
1775
 (defun bgp:xor (&rest arguments)
1776
   (let* ((initargs (member-if #'keywordp arguments))
1777
          (patterns (ldiff arguments initargs)))
1778
     (if (and (null initargs) (null (rest patterns)))
1779
       (first patterns)
1780
       (apply #'make-instance bgp:xor
1781
              :forms patterns
1782
              initargs))))
1783
 
1784
 
1785
 (defmethod process-pattern ((pattern bgp:xor) result-field bindings modifiers)
1786
   (loop for form in (pattern-forms pattern)
1787
         for forms on (pattern-forms pattern)
1788
         for field = (funcall form (when (null (rest forms)) result-field)
1789
                              bindings
1790
                              (when (null (rest forms)) modifiers))
1791
         then (funcall #'merge-xor-pattern pattern
1792
                       (when (null (rest forms)) result-field) 
1793
                       field form
1794
                       bindings
1795
                       (when (null (rest forms)) modifiers))
1796
         finally (return field)))
1797
 
1798
 (defmethod merge-xor-pattern ((op-pattern t) result-field field pattern bindings modifiers)
1799
     (flet ((matrix-xor (&rest args)
1800
              (error "NYI: matrix-xor ~s" args)))
1801
       (apply #'matrix-xor result-field field (funcall pattern nil bindings nil) modifiers)))
1802
 
1803
 
1804
 ;;;
1805
 ;;;
1806
 
1807
 ;;;
1808
 ;;; basic graph pattern compilation
1809
 
1810
 (defmethod compute-bgp-lambda ((agp-repository rdfcache-decimated-matrix-repository) body &rest args &key &allow-other-keys)
1811
   (declare (dynamic-extent args))
1812
   (apply #'compute-decimated-bgp-lambda body args))
1813
 
1814
 ;; (defun query-pattern-rewrite-operators (task) (declare (ignore task)) nil)
1815
 
1816
 
1817
 
1818
 (defun compute-decimated-bgp-lambda (body &key
1819
                                           (graph (second (assoc 'spocq.a:|graph| body)))
1820
                                           (slice (rest (assoc 'spocq.a:|slice| body)))
1821
                                           graphs
1822
                                           default-graphs
1823
                                           base-dimensions
1824
                                           projection-dimensions
1825
                                           wildcard-term
1826
                                           named-graphs
1827
                                           default-context-term  
1828
                                           transaction
1829
                                           trace
1830
                                           dataset-graphs
1831
                                           variables
1832
                                           dynamic-variables
1833
                                           environment
1834
                                           )
1835
   
1836
   "transform into an applicable pattern by
1837
    - expanding all triples to add either the graph variable/value, iff in the scope, or the current default graph term, if not.
1838
    - wrap the body in a bgp:|and|
1839
    if a graph variable is present, retain it. otherwise suppress that dimension as a join constraint"
1840
   (declare (ignore environment))
1841
   (declare (ignore base-dimensions projection-dimensions wildcard-term DATASET-GRAPHS
1842
                    default-context-term default-graphs dynamic-variables
1843
                    named-graphs graphs transaction trace variables))
1844
   (when slice
1845
     (destructuring-bind (offset count) slice
1846
       (setf slice `(:start ,offset :end ,(+ (or offset 0) count)))))
1847
   ;; extract any filter/triple combinations which should be handled by an index
1848
   ;; combine the remainder into a single bgp:and, then combine the index and the match sections in an outer bgp:and
1849
   (labels ((graph-dimension ()
1850
              (cond ((variable-p graph)
1851
                     graph)
1852
                    (graph
1853
                     +context-variable+)
1854
                    (t                   ; if in a default dataset, suppress the graph dimension
1855
                     (gensym "graph"))))
1856
            (rewrite-body (body)
1857
              
1858
              ;; first, extend any triples to quads
1859
              (setf body (loop for form in body
1860
                               for operator = (first form)
1861
                               collect (case operator
1862
                                         (spocq.a:|triple|
1863
                                          (destructuring-bind (s p o &key dimensions &allow-other-keys) (rest form)
1864
                                            `(spocq.a:|quad| ,s ,p ,o ,graph
1865
                                                             ,@(when dimensions
1866
                                                                 `(:dimensions ,(cons (graph-dimension) dimensions))))))
1867
                                         (t form))))
1868
              ;; then, collect any statements for which an index applies and translate them into index forms 
1869
              (let* ((indexed-statements ())
1870
                     (statements-by-index (loop with cache = ()
1871
                                                for form in body
1872
                                                for operator = (first form)
1873
                                                do (case operator
1874
                                                     (spocq.a:|quad|
1875
                                                      (let* ((predicate (statement-predicate form))
1876
                                                             (index (repository-index *transaction* predicate)))
1877
                                                        (when index
1878
                                                          (push form indexed-statements)
1879
                                                          (push form (getf cache index))))))
1880
                                                finally (return cache)))
1881
                     (variables-by-index (loop for (index statements) on statements-by-index by #'cddr
1882
                                               append (list index (expression-variables statements))))
1883
                     (indexed-filters ())
1884
                     (filters-by-index (loop with cache = ()
1885
                                             for form in body
1886
                                             for operator = (first form)
1887
                                             do (case operator
1888
                                                  (spocq.a:|filter|
1889
                                                   (let* ((variables (expression-variables form))
1890
                                                          (index (loop for (index index-variables) on variables-by-index by #'cddr
1891
                                                                       when (null (set-difference variables index-variables))
1892
                                                                       return index)))
1893
                                                     (when index
1894
                                                       (push form indexed-filters)
1895
                                                       (push form (getf cache index))))))
1896
                                             finally (return cache)))
1897
                     (index-statements (loop for (index1 statements) on statements-by-index by #'cddr
1898
                                             for (index2 filters) on filters-by-index by #'cddr
1899
                                             do (assert (eq index1 index2) ()
1900
                                                        "corrupt bgp index cache: ~s ~s ~s"
1901
                                                        statements-by-index
1902
                                                        variables-by-index
1903
                                                        filters-by-index)
1904
                                             append (compute-index-statements index1 statements filters))))
1905
                ;; then remove those statements from the standard matching process
1906
                (setf body (loop for form in body
1907
                                 unless (or (member form indexed-statements) (member form indexed-filters))
1908
                                 collect form))
1909
                (let* ((collected-form `(bgp:and ,@index-statements)))
1910
                  (flet ((append-form (form)
1911
                           (ecase (first collected-form)
1912
                             (bgp:and
1913
                              (push form (rest (last collected-form))))
1914
                             (bgp:filter
1915
                              (setf collected-form `(bgp:and ,collected-form ,form))))))
1916
                    ;; and generate the match expression
1917
                    (loop for form in body
1918
                          for (operator . operands) = form
1919
                          do (case operator
1920
                               ((spocq.a:|graph| spocq.a:|slice| spocq.a::|id| spocq.a::|declare| spocq.a::|equivalents|)
1921
                                ;; already extracted
1922
                                )
1923
                               (spocq.a:|quad|
1924
                                (destructuring-bind (s p o c &rest options) operands
1925
                                  (let* ((quad-form
1926
                                          (cond ((property-path-p p)
1927
                                                 `(bgp:quad-path ,s ,p ,o ,c ,@options))
1928
                                                ((extension-operator-p p)
1929
                                                 `(bgp:quad-call ,s ,p ,o ,c ,@options))
1930
                                                (t
1931
                                                 #+(or) ;; entailment is bgp-based, note statement based
1932
                                                 (let ((entailed-pattern (compute-entailment-pattern *task* form)))
1933
                                                   (if (eq form entailed-pattern)
1934
                                                     `(bgp:quad ,s ,p ,o ,c ,@options)
1935
                                                     (append entailed-pattern options)))
1936
                                                 `(bgp:quad ,s ,p ,o ,c ,@options)))))
1937
                                    (ecase (first collected-form)
1938
                                      (bgp:and
1939
                                       (push quad-form (rest (last collected-form))))
1940
                                      (bgp:filter
1941
                                       (setf collected-form `(bgp:and ,collected-form ,quad-form)))))))
1942
                               (spocq.a:|filter|
1943
                                (ecase (first collected-form)
1944
                                  (bgp:and
1945
                                   (setf collected-form
1946
                                         `(bgp:filter ,collected-form (spocq.a:|&&| ,@(copy-tree operands)))))
1947
                                  (bgp:filter
1948
                                   (setf (rest (last (third collected-form))) (copy-tree operands)))))
1949
                               ;; compound forms rewrite the body and replace the resuling and with their
1950
                               ;; respective equivalent
1951
                               (spocq.a:|and|
1952
                                (append-form (rewrite-body operands)))
1953
                               (spocq.a:|not|
1954
                                (append-form `(bgp:not ,@(rest (rewrite-body operands)))))
1955
                               (spocq.a:|or|
1956
                                (append-form `(bgp:or ,@(rest (rewrite-body operands)))))
1957
                               (spocq.a:|union|
1958
                                (append-form `(bgp:union ,@(rest (rewrite-body operands)))))
1959
                               (spocq.a:|xor|
1960
                                (append-form `(bgp:xor ,@(rest (rewrite-body operands)))))
1961
                               (t ;; include as-is - eg index match
1962
                                (append-form form)))
1963
                          finally (return collected-form)))))))
1964
     `(lambda (&key (bindings (when *task* (let ((bindings (query-dynamic-bindings *task*))) (mapcar #'cons (first bindings) (rest bindings)))))
1965
                    (modifiers ',slice) (result-field nil))
1966
        (trace-algebra bgp:bgp ',body)
1967
        (funcall ,(rewrite-body body) result-field bindings modifiers))))
1968
 
1969
                           
1970
 ;;; (parse-sparql "select * where {?s ?p ?o . filter(?o > 2) filter(?s != <http://>) }")
1971
 
1972
 
1973