tebdol

Simulation of ultracold atoms in optical lattices
git clone https://miroslavurbanek.com/git/tebdol.git
Log | Files | Refs | README

tensor.lisp (21817B)


      1 (defpackage :tensor
      2   (:use :common-lisp :util :blas :array :exp)
      3   (:export :tensor
      4 	   :make-tensor
      5 	   :tensor-indices
      6 	   :tensor-sectors
      7 	   :segment
      8 	   :make-segment
      9 	   :segment-numbers
     10 	   :segment-dimension
     11 	   :subscript
     12 	   :make-subscript
     13 	   :subscript-numbers
     14 	   :subscript-subscript
     15 	   :sector
     16 	   :make-sector
     17 	   :sector-numbers
     18 	   :sector-array
     19 	   :list-+
     20 	   :list--
     21 	   :list-zerop
     22 	   :functional-tensor
     23 	   :conjugate-index
     24 	   :tensor-rank
     25 	   :tensor-dimensions
     26 	   :tensor-total-size
     27 	   :tensor-scalar
     28 	   :tensor-normalize
     29 	   :tensor-conjugate
     30 	   :tensor-contract
     31 	   :tensor-permute
     32 	   :tensor-fuse
     33 	   :tensor-decompose
     34 	   :tensor-exponential))
     35 
     36 (in-package :tensor)
     37 
     38 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     39 
     40 (defstruct tensor
     41   (indices nil :type list)
     42   (sectors nil :type list))
     43 
     44 (defstruct segment
     45   (numbers nil :type list)
     46   (dimension 0 :type fixnum))
     47 
     48 (defstruct subscript
     49   (numbers nil :type list)
     50   (subscript 0 :type fixnum))
     51 
     52 (defstruct sector
     53   (numbers nil :type list)
     54   array)
     55 
     56 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     57 
     58 (defun list-+ (&rest args)
     59   (apply #'mapcar #'+ args))
     60 
     61 ;; (defun list-- (&rest args)
     62 ;;   (apply #'mapcar #'- args))
     63 
     64 ;; optimized version with a single argument
     65 
     66 (defun list-- (l)
     67   (loop for i in l collect (- i)))
     68 
     69 (defun list-zerop (list)
     70   (if list
     71       (and (zerop (the fixnum (car list))) (list-zerop (cdr list)))
     72       t))
     73 
     74 (defun list-< (a b)
     75   (cond
     76     ((null a) (not (null b)))
     77     ((null b) nil)
     78     ((= (the fixnum (car a)) (the fixnum (car b))) (list-< (cdr a) (cdr b)))
     79     (t (< (the fixnum (car a)) (the fixnum (car b))))))
     80 
     81 (defun array-zerop (array)
     82   (declare (type (array blas-float) array))
     83   (dotimes (i (array-total-size array) t)
     84     (unless (zerop (row-major-aref array i)) (return nil))))
     85 
     86 (defun sort-index (index)
     87   (sort (copy-list index) #'list-< :key #'segment-numbers))
     88 
     89 (defun remove-zero-sectors (sectors)
     90   (mapcan
     91    #'(lambda (s)
     92        (unless (array-zerop (sector-array s))
     93 	 (list s)))
     94    sectors))
     95 
     96 (defun tensor-index-sequence (indices)
     97   (mapcar
     98    #'(lambda (mi) (mapcar #'nth mi indices))
     99    (multi-index-sequence (mapcar #'length indices))))
    100 
    101 (defun functional-tensor (indices function)
    102   (let ((si (mapcar #'sort-index indices)))
    103     (make-tensor
    104      :indices
    105      si
    106      :sectors
    107      (remove-zero-sectors
    108       (mapcan
    109        #'(lambda (ts)
    110 	   (let ((mn (mapcar #'segment-numbers ts))
    111 		 (md (mapcar #'segment-dimension ts)))
    112 	     (when (list-zerop (apply #'list-+ mn)) ; symmetry condition
    113 	       (let ((a (make-blas-array md)))
    114 		 (dolist (mi (multi-index-sequence md)
    115 			  (list (make-sector :numbers mn :array a)))
    116 		   (setf (apply #'aref a mi)
    117 			 (coerce (funcall function
    118 					  (mapcar
    119 					   #'(lambda (n i)
    120 					       (make-subscript
    121 						:numbers n
    122 						:subscript i))
    123 					   mn
    124 					   mi))
    125 				 'blas-float)))))))
    126        (tensor-index-sequence si))))))
    127 
    128 (defun conjugate-index (index)
    129   (reverse
    130    (mapcar #'(lambda (s)
    131 	       (make-segment
    132 		:numbers (list-- (segment-numbers s))
    133 		:dimension (segment-dimension s)))
    134 	   index)))
    135 
    136 (defun tensor-rank (tensor)
    137   (length (tensor-indices tensor)))
    138 
    139 (defun tensor-dimensions (tensor)
    140   (loop
    141      for i in (tensor-indices tensor)
    142      collect (reduce #'+ i :key #'segment-dimension)))
    143 
    144 (defun tensor-total-size (tensor)
    145   (reduce #'+ (tensor-sectors tensor) :key #'(lambda (s) (array-total-size (sector-array s)))))
    146 
    147 (defun tensor-scalar (tensor)
    148   (let ((s (tensor-sectors tensor)))
    149     (if s
    150 	(aref (sector-array (first s)))
    151 	+blas-0+)))
    152 
    153 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    154 
    155 ;; tensor-normalize
    156 
    157 (defun tensor-norm (tensor)
    158   (sqrt
    159    (loop
    160       for s in (tensor-sectors tensor)
    161       sum (expt (array-norm (sector-array s)) 2))))
    162 
    163 (defun tensor-normalize (tensor)
    164   (let ((n (tensor-norm tensor)))
    165     (make-tensor
    166      :indices
    167      (tensor-indices tensor)
    168      :sectors
    169      (mapcar
    170       #'(lambda (s)
    171 	  (make-sector
    172 	   :numbers (sector-numbers s)
    173 	   :array (array-scalar-/ (sector-array s) n)))
    174       (tensor-sectors tensor)))))
    175 
    176 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    177 
    178 ;; tensor-conjugate
    179 
    180 (defun tensor-conjugate (tensor)
    181   (make-tensor
    182    :indices
    183    (mapcar
    184     #'(lambda (i)
    185 	(reverse
    186 	 (mapcar #'(lambda (s)
    187 		     (make-segment
    188 		      :numbers (list-- (segment-numbers s))
    189 		      :dimension (segment-dimension s)))
    190 		 i)))
    191     (tensor-indices tensor))
    192    :sectors
    193    (mapcar
    194     #'(lambda (s)
    195 	(make-sector
    196 	 :numbers (mapcar #'list-- (sector-numbers s))
    197 	 :array (array-conjugate (sector-array s))))
    198     (tensor-sectors tensor))))
    199 
    200 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    201 
    202 ;; tensor-permute
    203 
    204 (defun tensor-permute (tensor permutation)
    205   (make-tensor
    206    :indices
    207    (list-permute (tensor-indices tensor) permutation)
    208    :sectors
    209    (mapcar
    210     #'(lambda (s)
    211 	(make-sector
    212 	 :numbers (list-permute (sector-numbers s) permutation)
    213 	 :array (array-permute (sector-array s) permutation)))
    214     (tensor-sectors tensor))))
    215 
    216 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    217 
    218 ;; tensor-contract
    219 
    220 (defun index-match-p (x y)
    221   (and (= (length x) (length y))
    222        (loop
    223 	  for i in x
    224 	  for j in (reverse y)
    225 	  unless (and (list-zerop (list-+ (segment-numbers i)
    226 					  (segment-numbers j)))
    227 		      (zerop (- (segment-dimension i)
    228 				(segment-dimension j))))
    229 	  return nil
    230 	  finally (return t))))
    231 
    232 (defun indices-match-p (x y)
    233   (and (= (length x) (length y))
    234        (loop
    235 	  for i in x
    236 	  for j in y
    237 	  unless (index-match-p i j)
    238 	  return nil
    239 	  finally (return t))))
    240 
    241 (defun contraction-hash-table (tensor indices)
    242   (let ((h (make-hash-table :test 'equal)))
    243     (dolist (x (tensor-sectors tensor) h)
    244       (push x
    245 	    (gethash (loop
    246 			with n = (sector-numbers x)
    247 			for i in indices
    248 			collect (nth i n))
    249 		     h)))))
    250 
    251 (defun sector-contract (sector1 indices1 sector2 indices2)
    252   (make-sector
    253    :numbers
    254    (nconc
    255     (loop
    256        with n = (sector-numbers sector1)
    257        for i below (length n)
    258        unless (member i indices1)
    259        collect (nth i n))
    260     (loop with n = (sector-numbers sector2)
    261        for i below (length n)
    262        unless (member i indices2)
    263        collect (nth i n)))
    264    :array
    265    (array-contract (sector-array sector1)
    266 		   indices1
    267 		   (sector-array sector2)
    268 		   indices2)))
    269 
    270 (defun tensor-contract (tensor1 indices1 tensor2 indices2)
    271   (if (numberp indices1)
    272       (setf indices1 (list indices1)))
    273   (if (numberp indices2)
    274       (setf indices2 (list indices2)))
    275   (unless (indices-match-p (loop
    276 			      with n = (tensor-indices tensor1)
    277 			      for i in indices1
    278 			      collect (nth i n))
    279 			   (loop
    280 			      with n = (tensor-indices tensor2)
    281 			      for i in indices2
    282 			      collect (nth i n)))
    283     (error "Contraction indices do not match."))
    284   (let ((h1 (contraction-hash-table tensor1 indices1))
    285 	(h2 (contraction-hash-table tensor2 indices2))
    286 	(h (make-hash-table :test 'equal)))
    287     (loop
    288        for k being the hash-keys in h1
    289        using (hash-value v1)
    290        do (let ((v2 (gethash (loop for i in k collect (list-- i)) h2)))
    291 	    (when v2
    292 	      (dolist (s1 v1)
    293 		(dolist (s2 v2)
    294 		  (let* ((s (sector-contract s1 indices1 s2 indices2))
    295 			 (n (sector-numbers s))
    296 			 (v (gethash n h)))
    297 		    (if v
    298 			(array-addf (sector-array v)
    299 				    (sector-array s))
    300 			(setf (gethash n h) s))))))))
    301     (make-tensor
    302      :indices
    303      (nconc
    304       (loop
    305 	 with n = (tensor-indices tensor1)
    306 	 for i below (length n)
    307 	 unless (member i indices1)
    308 	 collect (nth i n))
    309       (loop
    310 	 with n = (tensor-indices tensor2)
    311 	 for i below (length n)
    312 	 unless (member i indices2)
    313 	 collect (nth i n)))
    314      :sectors
    315      (remove-zero-sectors
    316       (loop
    317 	 for v being the hash-values of h
    318 	 collect v)))))
    319 
    320 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    321 
    322 ;; tensor-fuse
    323 
    324 (defstruct joint
    325   segments
    326   subscript)
    327 
    328 (defun joint-numbers (joint)
    329   (mapcar #'segment-numbers (joint-segments joint)))
    330 
    331 (defun joint-number (joint)
    332   (apply #'list-+ (joint-numbers joint)))
    333 
    334 (defun joint-dimensions (joint)
    335   (mapcar #'segment-dimension (joint-segments joint)))
    336 
    337 (defun joint-dimension (joint)
    338   (apply #'* (joint-dimensions joint)))
    339 
    340 (defun fused-index-hash-table (indices type)
    341   (let ((md (mapcar #'length indices))
    342 	(hi (make-hash-table :test #'equal)))
    343     (dolist (mi (multi-index-sequence md))
    344       (let ((f (make-joint :segments (mapcar #'nth mi indices))))
    345 	(push f (gethash (joint-number f) hi))))
    346     (loop
    347        for k being the hash-keys in hi
    348        using (hash-value v)
    349        do (let ((l (sort v #'list-< :key #'(lambda (n) (apply #'append (joint-numbers n)))))
    350 		(hf (make-hash-table :test #'equal)))
    351 	    (loop
    352 	       with s = 0
    353 	       for i in (if (eq type :reverse) (reverse l) l)
    354 	       do
    355 		 (setf (joint-subscript i) s)
    356 		 (setf (gethash (joint-numbers i) hf) i)
    357 		 (incf s (joint-dimension i)))
    358 	    (setf (gethash k hi) hf))
    359        finally (return hi))))
    360 
    361 (defun fused-index-joints (number fiht)
    362   (loop
    363      for v being the hash-values in (gethash number fiht)
    364      collect v))
    365 
    366 (defun fused-index-dimension (numbers fiht)
    367   (loop
    368      for v being the hash-values in (gethash numbers fiht)
    369      sum (joint-dimension v)))
    370 
    371 (defun fused-index-subscript (numbers fiht)
    372   (joint-subscript (gethash numbers (gethash (apply #'list-+ numbers) fiht))))
    373 
    374 (defun fused-index (fiht)
    375   (sort-index
    376    (loop
    377       for k being the hash-keys in fiht
    378       using (hash-value v)
    379       collect
    380 	(make-segment
    381 	 :numbers
    382 	 k
    383 	 :dimension
    384 	 (loop
    385 	    for w being the hash-values in v
    386 	    sum (joint-dimension w))))))
    387 
    388 (defun partite-list (list borders)
    389   (loop
    390      with m
    391      with s
    392      with b = (pop borders)
    393      for i below (length list)
    394      for j in list
    395      when (and b (= i b))
    396      do
    397        (push (nreverse s) m)
    398        (setf s nil
    399 	     b (pop borders))
    400      do
    401        (push j s)
    402      finally
    403        (push (nreverse s) m)
    404        (return (nreverse m))))
    405 
    406 (defun array-fuse (array borders)
    407   (make-blas-array
    408    (mapcar #'(lambda (m) (apply #'* m))
    409 	   (partite-list (array-dimensions array) borders))
    410    :displaced-to array))
    411 
    412 ;; (defun array-insert-subarray (array subscripts subarray)
    413 ;;   (dolist (mi (multi-index-sequence (array-dimensions subarray))
    414 ;; 	   subarray)
    415 ;;     (setf (apply #'aref array (mapcar #'+ subscripts mi))
    416 ;; 	  (apply #'aref subarray mi))))
    417 
    418 ;; optimized version
    419 
    420 (defun make-array-insert-subarray-fn (rank)
    421   (let ((mi (loop for i below rank collect (gensym)))
    422 	(md (loop for i below rank collect (gensym)))
    423 	(ms (loop for i below rank collect (gensym))))
    424     (labels ((nest (&optional (i 0))
    425     	       (if (eql i rank)
    426     		   `(setf (aref array
    427 				,@(loop for j below rank
    428 				     collect `(+ ,(nth j ms) ,(nth j mi))))
    429 			  (aref subarray ,@mi))
    430 		   `(dotimes (,(nth i mi) ,(nth i md)
    431 			      ,@(when (eql i 0) '(subarray)))
    432 		      ,(nest (1+ i))))))
    433       `(lambda (array subscripts subarray)
    434 	 (declare (type (array blas-float) array subarray))
    435 	 (let (,@(loop for i below rank
    436 		    collect `(,(nth i md) (array-dimension subarray ,i)))
    437 	       ,@(loop for i below rank
    438 		    collect `(,(nth i ms) (nth ,i subscripts))))
    439 	   (declare (type fixnum ,@ms))
    440 	   ,(nest))))))
    441 
    442 (let ((cache (make-hash-table)))
    443   (defun array-insert-subarray (array subscripts subarray)
    444     (let ((l (length subscripts)))
    445       (funcall (or (gethash l cache)
    446 		   (setf (gethash l cache)
    447 			 (symbol-function
    448 			  (compile (intern (format nil "ARRAY-INSERT-SUBARRAY-~D" l))
    449 				   (make-array-insert-subarray-fn l)))))
    450 	       array
    451 	       subscripts
    452 	       subarray))))
    453 
    454 ;; (defun array-extract-subarray (array subscripts dimensions)
    455 ;;   (let ((a (make-blas-array dimensions)))
    456 ;;     (dolist (mi (multi-index-sequence dimensions) a)
    457 ;;       (setf (apply #'aref a mi)
    458 ;; 	    (apply #'aref array (mapcar #'+ subscripts mi))))))
    459 
    460 ;; optimized version
    461 
    462 (defun make-array-extract-subarray-fn (rank)
    463   (let ((mi (loop for i below rank collect (gensym)))
    464 	(md (loop for i below rank collect (gensym)))
    465 	(ms (loop for i below rank collect (gensym))))
    466     (labels ((nest (&optional (i 0))
    467     	       (if (eql i rank)
    468     		   `(setf (aref a ,@mi)
    469 			  (aref array
    470 				,@(loop for j below rank
    471 				     collect `(+ ,(nth j ms) ,(nth j mi)))))
    472 		   `(dotimes (,(nth i mi) ,(nth i md)
    473 			      ,@(when (eql i 0) '(a)))
    474 		      ,(nest (1+ i))))))
    475       `(lambda (array subscripts dimensions)
    476 	 (declare (type (array blas-float) array)
    477 		  (type list dimensions))
    478 	 (let ((a (make-blas-array dimensions))
    479 	       ,@(loop for i below rank
    480 		    collect `(,(nth i md) (nth ,i dimensions)))
    481 	       ,@(loop for i below rank
    482 		    collect `(,(nth i ms) (nth ,i subscripts))))
    483 	   (declare (type fixnum ,@md ,@ms))
    484 	   ,(nest))))))
    485 
    486 (let ((cache (make-hash-table)))
    487   (defun array-extract-subarray (array subscripts dimensions)
    488     (let ((l (length subscripts)))
    489       (funcall (or (gethash l cache)
    490 		   (setf (gethash l cache)
    491 			 (symbol-function
    492 			  (compile (intern (format nil "ARRAY-EXTRACT-SUBARRAY-~D" l))
    493 				   (make-array-extract-subarray-fn l)))))
    494 	       array
    495 	       subscripts
    496 	       dimensions))))
    497 
    498 (defun make-fused-array (numbers fiht)
    499   (make-blas-array
    500    (mapcar #'(lambda (n h) (fused-index-dimension n h)) numbers fiht)
    501    :initial-element +blas-0+))
    502 
    503 (defun fused-array-subscripts (partition fiht)
    504   (mapcar #'(lambda (n h) (fused-index-subscript n h)) partition fiht))
    505 
    506 (defun tensor-fuse (tensor borders types)
    507   (let ((f (mapcar #'fused-index-hash-table
    508 		   (partite-list (tensor-indices tensor) borders)
    509 		   types))
    510 	(h (make-hash-table :test 'equal)))
    511     (dolist (s (tensor-sectors tensor))
    512       (let* ((p (partite-list (sector-numbers s) borders))
    513 	     (n (mapcar #'(lambda (l) (apply #'list-+ l)) p))
    514 	     (v (gethash n h)))
    515 	;; create the array if it does not exist
    516 	(unless v
    517 	  (setf v (make-fused-array n f))
    518 	  (setf (gethash n h) v))
    519 	;; insert sector array into the fused array
    520 	(array-insert-subarray v
    521 			       (fused-array-subscripts p f)
    522 			       (array-fuse (sector-array s) borders))))
    523     (make-tensor
    524      :indices
    525      (mapcar #'fused-index f)
    526      :sectors
    527      (remove-zero-sectors
    528       (loop
    529 	 for k being the hash-keys in h
    530 	 using (hash-value v)
    531 	 collect
    532 	   (make-sector :numbers k :array v))))))
    533 
    534 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    535 
    536 ;; tensor-decompose
    537 
    538 (defstruct svd
    539   numbers
    540   u
    541   s
    542   v)
    543 
    544 (defun svd-reduce-dimensions (svd maxdim normalization)
    545   (let* ((u (svd-u svd))
    546 	 (s (svd-s svd))
    547 	 (v (svd-v svd))
    548 	 (ud (array-dimension u 0))
    549 	 (sd (min maxdim (length s)))
    550 	 (vd (array-dimension v 1))
    551 	 (un (make-blas-array (list ud sd)))
    552 	 (vn (make-blas-array (list sd vd)))
    553 	 (sn (map 'vector
    554 		  (if (member normalization '(:none :left :right))
    555 		      #'identity
    556 		      #'sqrt)
    557 		  (subseq s 0 sd))))
    558     (case normalization
    559       (:none
    560        (dotimes (i ud)
    561 	 (dotimes (j sd)
    562 	   (setf (aref un i j) (aref u i j))))
    563        (dotimes (i sd)
    564 	 (dotimes (j vd)
    565 	   (setf (aref vn i j) (aref v i j)))))
    566       (:left
    567        (dotimes (i ud)
    568 	 (dotimes (j sd)
    569 	   (setf (aref un i j) (aref u i j))))
    570        (dotimes (i sd)
    571 	 (dotimes (j vd)
    572 	   (setf (aref vn i j) (* (aref v i j) (aref sn i))))))
    573       (:right
    574        (dotimes (i ud)
    575 	 (dotimes (j sd)
    576 	   (setf (aref un i j) (* (aref u i j) (aref sn j)))))
    577        (dotimes (i sd)
    578 	 (dotimes (j vd)
    579 	   (setf (aref vn i j) (aref v i j)))))
    580       (:mixed
    581        (dotimes (i ud)
    582 	 (dotimes (j sd)
    583 	   (setf (aref un i j) (* (aref u i j) (aref sn j)))))
    584        (dotimes (i sd)
    585 	 (dotimes (j vd)
    586 	   (setf (aref vn i j) (* (aref v i j) (aref sn i)))))))
    587     (make-svd :numbers (svd-numbers svd) :u un :v vn)))
    588 
    589 (defun diagonal-matrix (diagonal maxdim)
    590   (let* ((n (min maxdim (length diagonal)))
    591 	 (m (make-blas-array (list n n) :initial-element +blas-0+)))
    592     (dotimes (i n m)
    593       (setf (aref m i i) (coerce (aref diagonal i) 'blas-float)))))
    594 
    595 (defun array-reshape (array dimensions)
    596   (let ((a (make-blas-array dimensions)))
    597     (dotimes (i (array-total-size a) a)
    598       (setf (row-major-aref a i) (row-major-aref array i)))))
    599 
    600 (defun tensor-decompose (tensor indices maxdim normalization)
    601   (if (numberp indices)
    602       (setf indices (list indices)))
    603   ;; permute if necessary
    604   (let ((r (tensor-rank tensor)))
    605     (unless (and (eql (indices-position r indices) :left)
    606 		 (apply #'< indices))
    607       (let ((l (loop for i below (length indices) collect i)))
    608 	(setf tensor (tensor-permute tensor (make-indices-permutation r indices l)))
    609 	(setf indices l))))
    610   (let* ((b (list (length indices)))
    611 	 (p (partite-list (tensor-indices tensor) b))
    612 	 (f (mapcar #'fused-index-hash-table p '(:normal :reverse)))
    613 	 (ah (make-hash-table :test #'equal))
    614 	 (sh (make-hash-table :test #'eq))
    615 	 sl i1 i2 is1 is2 s1 s2 ss)
    616     ;; create fused arrays
    617     (loop
    618        for s in (tensor-sectors tensor)
    619        do
    620 	 (let* ((r (partite-list (sector-numbers s) b))
    621 		(n (mapcar #'(lambda (l) (apply #'list-+ l)) r))
    622 		(a (gethash n ah)))
    623 	   (unless a
    624 	     (setf a (make-fused-array n f))
    625 	     (setf (gethash n ah) a))
    626 	   (array-insert-subarray
    627 	    a
    628 	    (fused-array-subscripts r f)
    629 	    (array-fuse (sector-array s) b))))
    630     ;; decompose arrays
    631     (loop
    632        for n being the hash-keys in ah
    633        using (hash-value a)
    634        do
    635 	 (multiple-value-bind (u s v) (array-decompose a 0)
    636 	   (let ((x (make-svd :numbers n :u u :s s :v v)))
    637 	     (loop
    638 		for i across s
    639 		unless (essentially-zerop i)
    640 		do (push (cons i x) sl)))))
    641     (loop
    642        with r = (sort sl #'> :key #'car)
    643        with l = (length r)
    644        with e = (car maxdim)
    645        with m = (cadr maxdim)
    646        with n = (caddr maxdim)
    647        with u = (and e (* (- 1 e)
    648 			  (loop
    649 			     for (s . x) in r
    650 			     sum (* s s))))
    651        with c = (if u
    652 		    (loop
    653 		       for (s . x) in r
    654 		       count s
    655 		       sum (* s s) into v
    656 		       until (> v u))
    657 		    l)
    658        with k = (cond ((and m (< c m) (/= c l))
    659 		       ;; (format *error-output* "hit mindim (~D)~%" c)
    660 		       (min l m))
    661 		      ((and n (> c n))
    662 		       ;; (format *error-output* "hit maxdim (~D/~D)~%" c l)
    663 		       n)
    664 		      (t c))
    665        repeat k
    666        for (s . x) in r
    667        do (if (gethash x sh)
    668 	      (incf (gethash x sh))
    669 	      (setf (gethash x sh) 1)))
    670     ;; reduce the array dimensions
    671     (loop
    672        for s being the hash-keys in sh
    673        using (hash-value d)
    674        do
    675 	 (let* ((r (svd-reduce-dimensions s d normalization))
    676 		(n (svd-numbers r))
    677 		(n1 (first n))
    678 		(n2 (second n))
    679 		(l1 (fused-index-joints n1 (first f)))
    680 		(l2 (fused-index-joints n2 (second f))))
    681 	   (push (make-segment :numbers n2 :dimension d) i1)
    682 	   (push (make-segment :numbers n1 :dimension d) i2)
    683 	   (push (make-segment :numbers n1 :dimension d) is1)
    684 	   (push (make-segment :numbers n2 :dimension d) is2)
    685 	   ;; first sectors
    686 	   (dotimes (i (length l1))
    687 	     (let ((j (nth i l1)))
    688 	       (push (make-sector
    689 		      :numbers
    690 		      (append (joint-numbers j) (list n2))
    691 		      :array
    692 		      (array-reshape
    693 		       (array-extract-subarray
    694 			(svd-u r)
    695 			(list (joint-subscript j) 0)
    696 			(list (joint-dimension j) d))
    697 		       (append (joint-dimensions j) (list d))))
    698 		     s1)))
    699 	   ;; second sectors
    700 	   (dotimes (i (length l2))
    701 	     (let ((j (nth i l2)))
    702 	       (push (make-sector
    703 		      :numbers
    704 		      (append (list n1) (joint-numbers j))
    705 		      :array
    706 		      (array-reshape
    707 		       (array-extract-subarray
    708 			(svd-v r)
    709 			(list 0 (joint-subscript j))
    710 			(list d (joint-dimension j)))
    711 		       (append (list d) (joint-dimensions j))))
    712 		     s2)))
    713 	   ;; singular values
    714 	   (push (make-sector
    715 		  :numbers (list n1 n2)
    716 		  :array (diagonal-matrix (svd-s s) d))
    717 		 ss)
    718 	   ))
    719     ;; return decomposition
    720     (values
    721      (make-tensor :indices (append (first p) (list (sort-index i1)))
    722 		  :sectors s1)
    723      (make-tensor :indices (list (sort-index is1) (sort-index is2))
    724 		  :sectors ss)
    725      (make-tensor :indices (append (list (sort-index i2)) (second p))
    726 		  :sectors s2))))
    727 
    728 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    729 
    730 ;; tensor-exponential
    731 
    732 (defun tensor-permuted-exponential (tensor borders parameter)
    733   (let* ((f (mapcar #'fused-index-hash-table
    734 		    (partite-list (tensor-indices tensor) borders)
    735 		    '(:normal :reverse)))
    736 	 (h (make-hash-table :test 'equal)))
    737     ;; add sectors; zero sectors will turn into unit sectors in the exponential
    738     (loop
    739        for k being the hash-keys in (first f)
    740        do (let ((n (list k (list-- k))))
    741 	    (setf (gethash n h) (make-fused-array n f))))
    742     (dolist (s (tensor-sectors tensor))
    743       (let* ((p (partite-list (sector-numbers s) borders))
    744 	     (n (mapcar #'(lambda (l) (apply #'list-+ l)) p))
    745 	     (v (gethash n h)))
    746 	;; insert sector array into the fused array
    747 	(array-insert-subarray v
    748 			       (fused-array-subscripts p f)
    749 			       (array-fuse (sector-array s) borders))))
    750     (make-tensor
    751      :indices
    752      (tensor-indices tensor)
    753      :sectors
    754      ;; todo: are zero sectors possible here?
    755      (remove-zero-sectors
    756       (loop
    757 	 with l
    758 	 for k being the hash-keys in h
    759 	 using (hash-value v)
    760 	 do
    761 	   (setf v (hermitian-matrix-exponential v parameter))
    762 	   (let ((ml (mapcar #'fused-index-joints k f)))
    763 	     (dolist (mi (multi-index-sequence (mapcar #'length ml)))
    764 	       (let* ((j (mapcar #'nth mi ml)))
    765 		 (push (make-sector
    766 			:numbers
    767 			(mapcan #'joint-numbers j)
    768 			:array
    769 			(array-reshape
    770 			 (array-extract-subarray
    771 			  v
    772 			  (mapcar #'joint-subscript j)
    773 			  (mapcar #'joint-dimension j))
    774 			 (mapcan #'joint-dimensions j)))
    775 		       l))))
    776 	 finally (return l))))))
    777 
    778 (defun tensor-exponential (tensor groups parameter)
    779   (unless (= (length groups) 2)
    780     (error "Incorrect numbers of index groups."))
    781   (let* ((r (tensor-rank tensor))
    782 	 (o (apply #'append groups))
    783 	 (p (loop for i below r collect i)))
    784     (tensor-permute
    785      (tensor-permuted-exponential
    786       (tensor-permute tensor (make-indices-permutation r o p))
    787       (list (length (first groups)))
    788       parameter)
    789      (make-indices-permutation r p o))))