File: Algorithm.hh

package info (click to toggle)
cadabra2 2.4.3.2-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 78,732 kB
  • sloc: ansic: 133,450; cpp: 92,064; python: 1,530; javascript: 203; sh: 184; xml: 182; objc: 53; makefile: 51
file content (275 lines) | stat: -rw-r--r-- 10,870 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*

Cadabra: a field-theory motivated computer algebra system.
Copyright (C) 2001-2014  Kasper Peeters <kasper.peeters@phi-sci.com>

This program is free software: you can redistribute it and/or
	modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#pragma once

#include "Stopwatch.hh"
#include "Storage.hh"
#include "Compare.hh"
#include "Props.hh"
#include "Exceptions.hh"
#include "Kernel.hh"
#include "IndexIterator.hh"
#include "ProgressMonitor.hh"
#include "IndexClassifier.hh"

#include <map>
#include <fstream>
#include <cstddef>

namespace cadabra {

	/// \ingroup core
	///
	/// Base class for all algorithms, containing generic routines and in
	/// particular the logic for index classification. Also contains static
	/// algorithms acting on Ex objects which require property
	/// information and can therefore not be a member of Ex.
	///
	/// In order to implement a new algorithm, subclass Algorithm and
	/// implement the abstract members Algorithm::can_apply and
	/// Algorithm::apply (see there for further documentation).  The
	/// general logic is that the implementation of
	/// Algorithm::apply(iterator&) is not allowed to make the node pointed
	/// at by the iterator invalid. If the algorithm makes the node vanish,
	/// it should indicate so by setting its multiplier to zero; the
	/// calling logic will then take care of cleaning up the subtree
	/// at the node.
	///
	/// The algorithm is, however, allowed to change the node itself or
	/// replace it with another one, as long as it updates the iterator.

	class Algorithm : public IndexClassifier {
		public:
			/// Initialise the algorithm with a reference to the expression
			/// tree, but do not yet do anything with this tree. Algorithms
			/// are not typically allowed to mess with the settings in the
			/// Kernel, so it is passed const.

			Algorithm(const Kernel&, Ex&);

			virtual ~Algorithm();

			typedef Ex::iterator            iterator;
			typedef Ex::post_order_iterator post_order_iterator;
			typedef Ex::sibling_iterator    sibling_iterator;
			typedef Ex::result_t            result_t;

			bool interrupted;

			/// Provide the algorithm with a ProgressMonitor object on which to register
			/// (nested) progress information, to be reported out-of-band to a client.

			void set_progress_monitor(ProgressMonitor *);

			/// The main entry points for running algorithms, which traverse
			/// the tree post-order ('child before parent'). The 'deep' flag
			/// indicates whether sub-expressions should be acted on
			/// too. The 'repeat' flag indicates whether the algorithm
			/// should be applied until the expression no longer
			/// changes. The 'depth' flag, if not equal to -1, indicates the
			/// depth in the tree where the algorithm should start applying.

			result_t  apply_generic(bool deep=true, bool repeat=false, unsigned int depth=0);
			result_t  apply_generic(iterator&, bool deep, bool repeat, unsigned int depth);

			/// Apply algorithm with alternative traversal: starting from
			/// the top node, traverse the tree pre-order ('parent before
			/// child') and once the algorithm acts at a given node, do not
			/// traverse the subtree below anymore.

			result_t  apply_pre_order(bool repeat=false);

			// Global information
			unsigned int     number_of_calls;
			unsigned int     number_of_modifications;
			bool             suppress_normal_output;
			bool             discard_command_node;

			/// Given an expression top node, check index consistency.
			bool      check_consistency(iterator) const;
			bool      check_index_consistency(iterator) const;
			/// Given an expression top node, check differential form degree consistency.
			bool      check_degree_consistency(iterator) const;

			void report_progress(const std::string&, int todo, int done, int count=2);

			mutable Stopwatch index_sw;
			mutable Stopwatch get_dummy_sw;
			mutable Stopwatch report_progress_stopwatch;

			index_iterator begin_index(iterator it) const;
			index_iterator end_index(iterator it) const;

			// The number of indices of a node, taking into account IndexInherit-ance. These
			// indices do therefore not all have to be direct child nodes of 'it', they can
			// sit deeper down the tree.
			unsigned int number_of_indices(iterator it);
			static unsigned int number_of_indices(const Properties&, iterator it);

			// The number of indices of a node, counting only the direct ones (i.e. not those
			// inherited from child nodes).
			static unsigned int number_of_direct_indices(iterator it);

			// The set to which the first index belongs..
			std::string get_index_set_name(iterator it) const;

			/// Rename the dummies in the sub-tree starting with head at the given iterator.
			/// Ensures that no dummies in this sub-tree overlap with the indices elsewhere
			/// in the tree.			
			bool     rename_replacement_dummies(iterator, bool still_inside_algo=false);

			/// Determines whether the indicated node is 'like a term in a
			/// sum'.  This requires that the node is not a `\sum` node, not
			/// a child of a `\prod` node, and that its parent rel is of
			/// argument-type (p_none).
			static bool     is_termlike(iterator);

			/// Determines whether the indicated node is 'like a factor in a product'.
			/// This requires that the parent is a `\prod' node.
			static bool     is_factorlike(iterator);


		protected:
			Ex& tr;
			ProgressMonitor *pm;

			// The main entry point which is used by the public entry points listed
			// above. Override these in any subclass.
			//
			virtual bool can_apply(iterator)=0;
			virtual result_t apply(iterator&)=0;

			// Index stuff
			int      index_parity(iterator) const;
			static bool less_without_numbers(nset_t::iterator, nset_t::iterator);
			static bool equal_without_numbers(nset_t::iterator, nset_t::iterator);

			/// Finding objects in sets.
			typedef std::pair<sibling_iterator, sibling_iterator> range_t;
			typedef std::vector<range_t>                          range_vector_t;

			bool     contains(sibling_iterator from, sibling_iterator to, sibling_iterator arg);
			void     find_argument_lists(range_vector_t&, bool only_comma_lists=true) const;
			template<class Iter>
			range_vector_t::iterator find_arg_superset(range_vector_t&, Iter st, Iter nd);
			range_vector_t::iterator find_arg_superset(range_vector_t&, sibling_iterator it);

			// Locate objects inside the tree (formerly in the 'locate' algorithm).
			unsigned int locate_single_object(Ex::iterator obj_to_find,
			                                  Ex::iterator st, Ex::iterator nd,
			                                  std::vector<unsigned int>& store);
			bool         locate_object_set(const Ex& objs,
			                               Ex::iterator st, Ex::iterator nd,
			                               std::vector<unsigned int>& store);
			static bool  compare_(const str_node&, const str_node&);


			/// Take a single non-product node in a sum and wrap it in a
			/// product node, so it can be handled on the same footing as a proper product.
			bool     is_single_term(iterator);
			bool     is_nonprod_factor_in_prod(iterator);
			bool     prod_wrap_single_term(iterator&);
			bool     prod_unwrap_single_term(iterator&);
			bool     sum_wrap_single_term(iterator&);
			bool     sum_unwrap_single_term(iterator&);

			/// Wrap a term in a product or sum in a node with indicated
			/// name, irrespective of its parent (it usually makes more
			/// sense to call the safer prod_wrap_single_term or
			/// sum_wrap_single_term above). Sets the iterator to the
			/// new node.
			void     force_node_wrap(iterator&, std::string);

			/// Figure out whether two objects (commonly indices) are separated by a derivative
			/// operator, as in \f[ \partial_{a}{A_{b}} C^{b} \f].
			/// If the last iterator is pointing to a valid node, check whether it is
			/// independent of the derivative (using the Depends property).
			bool     separated_by_derivative(iterator, iterator, iterator check_dependence) const;

			// Given a node with non-zero multiplier, distribute this
			// multiplier up the tree when the node is a \sum node, or push it into the
			// `\prod` node if that is the parent. Do this recursively
			// in case a child is a sum as well. Note that 'pushup' is actually 'pushdown'
			// in the case of sums.
			// This never changes the tree structure, only the distribution of multipliers.

			// FIXME: this is now superseded by code in Cleanup.cc, and the generic way
			// to make a tree consistent is to call cleanup_dispatch, not pick-and-match from
			// the various algorithms.
			void     pushup_multiplier(iterator);

			// Return the number of elements in the first range for which an identical element
			// is present in the second range.
			template<class BinaryPredicate>
			unsigned int intersection_number(sibling_iterator, sibling_iterator,
			                                 sibling_iterator, sibling_iterator, BinaryPredicate) const;

			// Turn a node into a '1' or '0' node.
			void     node_zero(iterator);
			void     node_one(iterator);
			void     node_integer(iterator, int);



		protected:
			bool traverse_ldots;

		private:

			// Single or deep-scan apply operations. Do not call directly.
			result_t apply_once(Ex::iterator& it);
			result_t apply_deep(Ex::iterator& it);

			/// Given a node with zero multiplier, propagate this zero
			/// upwards in the tree.  Changes the iterator so that it points
			/// to the next node in a post_order traversal (post_order:
			/// children first, then node). The second node is the topmost
			/// node, beyond which this routine is not allowed to touch the
			/// tree (i.e. the 2nd iterator will always remain valid).
			void     propagate_zeroes(post_order_iterator&, const iterator&);

			//		bool cleanup_anomalous_products(Ex& tr, Ex::iterator& it);
		};


	/// Determine the number of elements in the first range which also occur in the
	/// second range.
	template<class BinaryPredicate>
	unsigned int Algorithm::intersection_number(sibling_iterator from1, sibling_iterator to1,
	      sibling_iterator from2, sibling_iterator to2,
	      BinaryPredicate fun) const
		{
		unsigned int ret=0;
		sibling_iterator it1=from1;
		while(it1!=to1) {
			sibling_iterator it2=from2;
			while(it2!=to2) {
				if(fun(*it1,*it2))
					++ret;
				++it2;
				}
			++it1;
			}
		return ret;
		}


	}