node.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. # vim: set fileencoding=utf-8 :
  2. # This file is part of TRS (http://math.kompiler.org)
  3. #
  4. # TRS is free software: you can redistribute it and/or modify it under the
  5. # terms of the GNU Affero General Public License as published by the Free
  6. # Software Foundation, either version 3 of the License, or (at your option) any
  7. # later version.
  8. #
  9. # TRS is distributed in the hope that it will be useful, but WITHOUT ANY
  10. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  12. # details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with TRS. If not, see <http://www.gnu.org/licenses/>.
  16. import os.path
  17. import sys
  18. import copy
  19. import re
  20. sys.path.insert(0, os.path.realpath('external'))
  21. from graph_drawing.graph import generate_graph
  22. from graph_drawing.line import generate_line, preprocess_node
  23. from graph_drawing.node import Node, Leaf
  24. TYPE_OPERATOR = 1
  25. TYPE_IDENTIFIER = 2
  26. TYPE_INTEGER = 4
  27. TYPE_FLOAT = 8
  28. # Unary
  29. OP_NEG = 1
  30. OP_ABS = 2
  31. # Binary
  32. OP_ADD = 3
  33. OP_SUB = 4
  34. OP_MUL = 5
  35. OP_DIV = 6
  36. OP_POW = 7
  37. OP_SUBSCRIPT = 8
  38. OP_AND = 9
  39. OP_OR = 10
  40. # Binary operators that are considered n-ary
  41. NARY_OPERATORS = [OP_ADD, OP_SUB, OP_MUL, OP_AND, OP_OR]
  42. # N-ary (functions)
  43. OP_INT = 11
  44. OP_INT_INDEF = 12
  45. OP_COMMA = 13
  46. OP_SQRT = 14
  47. OP_DER = 15
  48. OP_LOG = 16
  49. # Goniometry
  50. OP_SIN = 17
  51. OP_COS = 18
  52. OP_TAN = 19
  53. OP_SOLVE = 20
  54. OP_EQ = 21
  55. OP_POSSIBILITIES = 22
  56. OP_HINT = 23
  57. OP_REWRITE_ALL = 24
  58. OP_REWRITE_ALL_VERBOSE = 25
  59. OP_REWRITE = 26
  60. # Different types of derivative
  61. OP_PRIME = 27
  62. OP_DXDER = 28
  63. OP_PARENS = 29
  64. OP_BRACKETS = 30
  65. OP_CBRACKETS = 31
  66. UNARY_FUNCTIONS = [OP_INT, OP_DXDER, OP_LOG]
  67. # Special identifiers
  68. E = 'e'
  69. PI = 'pi'
  70. INFINITY = 'oo'
  71. SPECIAL_TOKENS = [PI, INFINITY]
  72. # Default base to use in parsing 'log(...)'
  73. DEFAULT_LOGARITHM_BASE = 10
  74. TYPE_MAP = {
  75. int: TYPE_INTEGER,
  76. float: TYPE_FLOAT,
  77. str: TYPE_IDENTIFIER,
  78. }
  79. OP_MAP = {
  80. ',': OP_COMMA,
  81. '+': OP_ADD,
  82. '-': OP_SUB,
  83. '*': OP_MUL,
  84. '/': OP_DIV,
  85. '^': OP_POW,
  86. '_': OP_SUBSCRIPT,
  87. '^^': OP_AND,
  88. '&': OP_AND,
  89. 'vv': OP_OR,
  90. 'sin': OP_SIN,
  91. 'cos': OP_COS,
  92. 'tan': OP_TAN,
  93. 'sqrt': OP_SQRT,
  94. 'int': OP_INT,
  95. '\'': OP_PRIME,
  96. 'solve': OP_SOLVE,
  97. 'log': OP_LOG,
  98. '=': OP_EQ,
  99. '??': OP_POSSIBILITIES,
  100. '?': OP_HINT,
  101. '@': OP_REWRITE,
  102. '@@': OP_REWRITE_ALL,
  103. '@@@': OP_REWRITE_ALL_VERBOSE,
  104. }
  105. OP_VALUE_MAP = dict([(v, k) for k, v in OP_MAP.iteritems()])
  106. OP_VALUE_MAP[OP_INT_INDEF] = 'indef'
  107. OP_VALUE_MAP[OP_ABS] = '||'
  108. OP_VALUE_MAP[OP_DXDER] = 'd/d'
  109. OP_VALUE_MAP[OP_PARENS] = '()'
  110. OP_VALUE_MAP[OP_BRACKETS] = '[]'
  111. OP_VALUE_MAP[OP_CBRACKETS] = '{}'
  112. OP_MAP['ln'] = OP_LOG
  113. TOKEN_MAP = {
  114. OP_COMMA: 'COMMA',
  115. OP_ADD: 'PLUS',
  116. OP_SUB: 'MINUS',
  117. OP_MUL: 'TIMES',
  118. OP_DIV: 'DIVIDE',
  119. OP_POW: 'POW',
  120. OP_SUBSCRIPT: 'SUB',
  121. OP_AND: 'AND',
  122. OP_OR: 'OR',
  123. OP_SQRT: 'FUNCTION',
  124. OP_SIN: 'FUNCTION',
  125. OP_COS: 'FUNCTION',
  126. OP_TAN: 'FUNCTION',
  127. OP_INT: 'INTEGRAL',
  128. OP_DXDER: 'DERIVATIVE',
  129. OP_PRIME: 'PRIME',
  130. OP_SOLVE: 'FUNCTION',
  131. OP_LOG: 'LOGARITHM',
  132. OP_EQ: 'EQ',
  133. OP_POSSIBILITIES: 'POSSIBILITIES',
  134. OP_HINT: 'HINT',
  135. OP_REWRITE: 'REWRITE',
  136. OP_REWRITE_ALL: 'REWRITE_ALL',
  137. OP_REWRITE_ALL_VERBOSE: 'REWRITE_ALL_VERBOSE',
  138. }
  139. def to_expression(obj):
  140. if isinstance(obj, ExpressionBase):
  141. return obj.clone()
  142. return ExpressionLeaf(obj)
  143. def bounds_str(f, a, b):
  144. left = str(ExpressionNode(OP_SUBSCRIPT, f, a, no_spacing=True))
  145. return left + str(ExpressionNode(OP_POW, Leaf(1), b, no_spacing=True))[1:]
  146. class ExpressionBase(object):
  147. def __lt__(self, other):
  148. """
  149. Comparison between this expression{node,leaf} and another
  150. expression{node,leaf}. This comparison will return True if this
  151. instance has less value than the other expression{node,leaf}.
  152. Otherwise, False is returned.
  153. The comparison is based on the following conditions:
  154. 1. Both are leafs. String comparison of the value is used.
  155. 2. This is a leaf and other is a node. This leaf has less value, thus
  156. True is returned.
  157. 3. This is a node and other is a leaf. This leaf has more value, thus
  158. False is returned.
  159. 4. Both are nodes. Compare the polynome properties of the nodes. True
  160. is returned if this node's root property is less than other's root
  161. property, or this node's exponent property is less than other's
  162. exponent property, or this node's coefficient property is less than
  163. other's coefficient property. Otherwise, False is returned.
  164. """
  165. if self.is_leaf:
  166. if other.is_leaf:
  167. # Both are leafs, string compare the value.
  168. self_value = '-' * (self.negated & 1) + str(self.value)
  169. other_value = '-' * (other.negated & 1) + str(other.value)
  170. return self_value < other_value
  171. # Self is a leaf, thus has less value than an expression node.
  172. return True
  173. if other.is_leaf:
  174. # Self is an expression node, and the other is a leaf. Thus, other
  175. # is greater than self.
  176. return False
  177. # Both are nodes, compare the polynome properties.
  178. s_coeff, s_root, s_exp = self.extract_polynome_properties()
  179. o_coeff, o_root, o_exp = other.extract_polynome_properties()
  180. return s_root < o_root or s_exp < o_exp or s_coeff < o_coeff
  181. def __gt__(self, other):
  182. return other < self
  183. def __ne__(self, other):
  184. """
  185. Check strict inequivalence, using the strict equivalence operator.
  186. """
  187. return not (self == other)
  188. def clone(self):
  189. return copy.deepcopy(self)
  190. def is_op(self, *ops):
  191. return not self.is_leaf and (self.op in ops or
  192. (self.op in (OP_DXDER, OP_PRIME) and OP_DER in ops))
  193. def is_power(self, exponent=None):
  194. if self.is_leaf or self.op != OP_POW:
  195. return False
  196. return exponent == None or self[1] == exponent
  197. def is_nary(self):
  198. return not self.is_leaf and self.op in NARY_OPERATORS
  199. def is_identifier(self, identifier=None):
  200. return self.type == TYPE_IDENTIFIER \
  201. and (identifier == None or self.value == identifier)
  202. def is_variable(self):
  203. return self.type == TYPE_IDENTIFIER and self.value not in (PI, E)
  204. def is_int(self):
  205. return self.type == TYPE_INTEGER
  206. def is_float(self):
  207. return self.type == TYPE_FLOAT
  208. def is_numeric(self):
  209. return self.type & (TYPE_FLOAT | TYPE_INTEGER)
  210. def __add__(self, other):
  211. return ExpressionNode(OP_ADD, self, to_expression(other))
  212. def __sub__(self, other):
  213. return ExpressionNode(OP_ADD, self, -to_expression(other))
  214. #FIXME: return ExpressionNode(OP_SUB, self, to_expression(other))
  215. def __mul__(self, other):
  216. return ExpressionNode(OP_MUL, self, to_expression(other))
  217. def __div__(self, other):
  218. return ExpressionNode(OP_DIV, self, to_expression(other))
  219. def __pow__(self, other):
  220. return ExpressionNode(OP_POW, self, to_expression(other))
  221. def __pos__(self):
  222. return self.reduce_negation()
  223. def __and__(self, other):
  224. return ExpressionNode(OP_AND, self, to_expression(other))
  225. def __or__(self, other):
  226. return ExpressionNode(OP_OR, self, to_expression(other))
  227. def reduce_negation(self, n=1):
  228. """Remove n negation flags from the node."""
  229. assert self.negated >= n
  230. return self.negate(-n)
  231. def negate(self, n=1, clone=True):
  232. """Negate the node n times."""
  233. return negate(self, self.negated + n, clone=clone)
  234. def contains(self, node, include_self=True):
  235. """
  236. Check if a node equal to the specified one exists within this node.
  237. """
  238. if include_self and self.equals(node, ignore_negation=True):
  239. return True
  240. if not self.is_leaf:
  241. for child in self:
  242. if child.contains(node, include_self=True):
  243. return True
  244. return False
  245. class ExpressionNode(Node, ExpressionBase):
  246. def __init__(self, *args, **kwargs):
  247. super(ExpressionNode, self).__init__(*args, **kwargs)
  248. self.type = TYPE_OPERATOR
  249. op = args[0]
  250. self.parens = False
  251. if isinstance(op, str):
  252. self.value = op
  253. self.op = OP_MAP[op]
  254. else:
  255. self.value = OP_VALUE_MAP[op]
  256. self.op = op
  257. def arity(self):
  258. if self.op in UNARY_FUNCTIONS:
  259. return 1
  260. #if self.op == OP_LOG and self[1].value in (E, DEFAULT_LOGARITHM_BASE):
  261. # return 1
  262. # Functions always have parentheses, so return a number higher than 1
  263. # to prevent graph_drawing from treating them as unary operators
  264. if self.op in TOKEN_MAP and TOKEN_MAP[self.op] == 'FUNCTION':
  265. return 2
  266. return len(self)
  267. def operator(self):
  268. # Append an opening parenthesis manually, the closing parentheses is
  269. # appended by postprocess_str
  270. if self.op == OP_LOG:
  271. base = self[1].value
  272. if base == DEFAULT_LOGARITHM_BASE:
  273. return self.value + '('
  274. if base == E:
  275. return 'ln('
  276. base = str(self[1])
  277. if not re.match('^[0-9]+|[a-zA-Z]$', base):
  278. base = '(' + base + ')'
  279. return '%s_%s(' % (self.value, base)
  280. if self.op == OP_DXDER:
  281. return self.value + str(self[1])
  282. if self.op == OP_INT and len(self) == 4:
  283. return bounds_str(Leaf('int'), self[2], self[3])
  284. return self.value
  285. def is_postfix(self):
  286. return self.op in (OP_PRIME, OP_INT_INDEF)
  287. def __str__(self): # pragma: nocover
  288. return generate_line(self)
  289. def custom_line(self):
  290. if self.op == OP_INT_INDEF:
  291. Fx, a, b = self
  292. return bounds_str(ExpressionNode(OP_BRACKETS, Fx), a, b)
  293. def preprocess_str_exp(self):
  294. if self.op == OP_PRIME and not self[0].is_op(OP_PRIME):
  295. self[0] = ExpressionNode(OP_BRACKETS, self[0])
  296. def postprocess_str(self, s):
  297. # A bit hacky, but forced because of operator() method
  298. if self.op == OP_LOG:
  299. return s.replace('( ', '(') + ')'
  300. if self.op == OP_INT:
  301. return '%s d%s' % (s, self[1])
  302. return s
  303. def __eq__(self, other):
  304. """
  305. Check strict equivalence.
  306. """
  307. return isinstance(other, ExpressionNode) and self.op == other.op \
  308. and self.negated == other.negated and self.nodes == other.nodes
  309. def substitute(self, old_child, new_child):
  310. self.nodes[self.nodes.index(old_child)] = new_child
  311. def graph(self): # pragma: nocover
  312. return generate_graph(preprocess_node(self))
  313. def extract_polynome_properties(self):
  314. """
  315. Extract polynome properties into tuple format: (coefficient, root,
  316. exponent). Thus: c * r ^ e will be extracted into the tuple (c, r, e).
  317. This function will normalize the expression before extracting the
  318. properties. Therefore, the expression r ^ e * c results the same tuple
  319. (c, r, e) as the expression c * r ^ e.
  320. >>> from src.node import ExpressionNode as N, ExpressionLeaf as L
  321. >>> c, r, e = L('c'), L('r'), L('e')
  322. >>> n1 = N(OP_MUL), c, N('^', r, e))
  323. >>> n1.extract_polynome()
  324. (c, r, e)
  325. >>> n2 = N(OP_MUL, N('^', r, e), c)
  326. >>> n2.extract_polynome()
  327. (c, r, e)
  328. >>> n3 = -r
  329. >>> n3.extract_polynome()
  330. (1, -r, 1)
  331. """
  332. # TODO: change "get_polynome" -> "extract_polynome".
  333. # TODO: change retval of c * r ^ e to (c, r, e).
  334. # was: (root, exponent, coefficient, literal_exponent)
  335. # rule: r ^ e -> (1, r, e)
  336. if self.is_power():
  337. return (ExpressionLeaf(1), self[0], self[1])
  338. # rule: -r -> (1, -r, 1)
  339. # rule: --r -> (1, --r, 1)
  340. # rule: ---r -> (1, ---r, 1)
  341. #if self.negated:
  342. # return (ExpressionLeaf(1), self, ExpressionLeaf(1))
  343. if self.op != OP_MUL:
  344. return
  345. # rule: 3 * 7 ^ e | 'a' * 'b' ^ e
  346. # expression: c * r ^ e ; tree:
  347. #
  348. # *
  349. # ╭┴───╮
  350. # c ^
  351. # ╭─┴╮
  352. # r e
  353. #
  354. # rule: c * r ^ e | (r ^ e) * c
  355. for i, j in ((0, 1), (1, 0)):
  356. if self[j].is_power():
  357. return (self[i], self[j][0], self[j][1])
  358. # Normalize c * r and r * c -> c * r. Otherwise, the tuple will not
  359. # match if the order of the expression is different. Example:
  360. # r ^ e * c == c * r ^ e
  361. # without normalization, those expressions will not match.
  362. #
  363. # rule: c * r | r * c
  364. if self[0] < self[1]:
  365. return (self[0], self[1], ExpressionLeaf(1))
  366. return (self[1], self[0], ExpressionLeaf(1))
  367. def equals(self, other, ignore_negation=False):
  368. """
  369. Perform a non-strict equivalence check between two nodes:
  370. - If the other node is a leaf, it cannot be equal to this node.
  371. - If their operators differ, the nodes are not equal.
  372. - If both nodes are additions or both are multiplications, match each
  373. node in one scope to one in the other (an injective relationship).
  374. Any difference in order of the scopes is irrelevant.
  375. - If both nodes are divisions, the nominator and denominator have to be
  376. non-strictly equal.
  377. """
  378. if not isinstance(other, ExpressionNode) or other.op != self.op:
  379. return False
  380. if self.op in NARY_OPERATORS:
  381. s0 = Scope(self)
  382. s1 = set(Scope(other))
  383. # Scopes should be of equal size
  384. if len(s0) != len(s1):
  385. return False
  386. # Each node in one scope should have an image node in the other
  387. matched = set()
  388. for n0 in s0:
  389. found = False
  390. for n1 in s1 - matched:
  391. if n0.equals(n1):
  392. found = True
  393. matched.add(n1)
  394. break
  395. if not found:
  396. return False
  397. else:
  398. # Check if all children are non-strictly equal, preserving order
  399. for i, child in enumerate(self):
  400. if not child.equals(other[i]):
  401. return False
  402. if ignore_negation:
  403. return True
  404. return self.negated == other.negated
  405. class ExpressionLeaf(Leaf, ExpressionBase):
  406. def __init__(self, *args, **kwargs):
  407. super(ExpressionLeaf, self).__init__(*args, **kwargs)
  408. self.type = TYPE_MAP[type(args[0])]
  409. self.parens = False
  410. def __eq__(self, other):
  411. """
  412. Check strict equivalence.
  413. """
  414. other_type = type(other)
  415. if other_type in TYPE_MAP:
  416. return self.type == TYPE_MAP[other_type] \
  417. and self.actual_value() == other
  418. return self.negated == other.negated and self.type == other.type \
  419. and self.value == other.value
  420. def __repr__(self):
  421. return str(self)
  422. def equals(self, other, ignore_negation=False):
  423. """
  424. Check non-strict equivalence.
  425. Between leaves, this is the same as strict equivalence, except when
  426. negations must be ignored.
  427. """
  428. if ignore_negation:
  429. other_type = type(other)
  430. if other_type in (int, float):
  431. return TYPE_MAP[other_type] == self.type \
  432. and self.value == abs(other)
  433. elif other_type == str:
  434. return self.type == TYPE_IDENTIFIER and self.value == other
  435. return self.type == other.type and self.value == other.value
  436. else:
  437. return self == other
  438. def extract_polynome_properties(self):
  439. """
  440. An expression leaf will return the polynome tuple (1, r, 1), where r is
  441. the leaf itself. See also the method extract_polynome_properties in
  442. ExpressionBase.
  443. """
  444. # rule: 1 * r ^ 1 -> (1, r, 1)
  445. return (ExpressionLeaf(1), self, ExpressionLeaf(1))
  446. def actual_value(self):
  447. if self.type == TYPE_IDENTIFIER:
  448. return self.value
  449. return (1 - 2 * (self.negated & 1)) * self.value
  450. class Scope(object):
  451. def __init__(self, node):
  452. self.node = node
  453. self.nodes = get_scope(node)
  454. for i, n in enumerate(self.nodes):
  455. n.scope_index = i
  456. def __getitem__(self, key):
  457. return self.nodes[key]
  458. def __setitem__(self, key, value):
  459. self.nodes[key] = value
  460. def __len__(self):
  461. return len(self.nodes)
  462. def __iter__(self):
  463. return iter(self.nodes)
  464. def __eq__(self, other):
  465. return isinstance(other, Scope) and self.node == other.node \
  466. and self.nodes == other.nodes
  467. def __repr__(self):
  468. return '<Scope of "%s">' % repr(self.node)
  469. def index(self, node):
  470. return node.scope_index
  471. def remove(self, node, replacement=None):
  472. try:
  473. i = node.scope_index
  474. if replacement:
  475. self[i] = replacement
  476. replacement.scope_index = i
  477. else:
  478. del self.nodes[i]
  479. # Update remaining scope indices
  480. for n in self.nodes[i:]:
  481. n.scope_index -= 1
  482. except AttributeError:
  483. raise ValueError('Node "%s" is not in the scope of "%s".'
  484. % (node, self.node))
  485. def replace(self, node, replacement):
  486. self.remove(node, replacement=replacement)
  487. # FIXME: def as_nary_node(self):
  488. def as_real_nary_node(self):
  489. return ExpressionNode(self.node.op, *self.nodes) \
  490. .negate(self.node.negated, clone=False)
  491. # FIXME: def as_binary_node(self):
  492. def as_nary_node(self):
  493. return nary_node(self.node.op, self.nodes) \
  494. .negate(self.node.negated, clone=False)
  495. def all_except(self, node):
  496. before = range(0, node.scope_index)
  497. after = range(node.scope_index + 1, len(self))
  498. nodes = [self[i] for i in before + after]
  499. return negate(nary_node(self.node.op, nodes), self.node.negated)
  500. def nary_node(operator, scope):
  501. """
  502. Create a binary expression tree for an n-ary operator. Takes the operator
  503. and a list of expression nodes as arguments.
  504. """
  505. if len(scope) == 1:
  506. return scope[0]
  507. return ExpressionNode(operator, nary_node(operator, scope[:-1]), scope[-1])
  508. def get_scope(node):
  509. """
  510. Find all n nodes within the n-ary scope of an operator node.
  511. """
  512. scope = []
  513. for child in node:
  514. if child.is_op(node.op) and not child.negated:
  515. scope += get_scope(child)
  516. else:
  517. scope.append(child)
  518. #for child in node:
  519. # if child.is_op(node.op) and (not child.negated or node.op == OP_MUL):
  520. # sub_scope = get_scope(child)
  521. # sub_scope[0] = sub_scope[0].negate(child.negated)
  522. # scope += sub_scope
  523. # else:
  524. # scope.append(child)
  525. return scope
  526. def negate(node, n=1, clone=False):
  527. """
  528. Negate the given node n times. If clone is set to true, return a new node
  529. so that the original node is not altered.
  530. """
  531. #assert n >= 0
  532. if clone:
  533. node = node.clone()
  534. node.negated = n
  535. return node
  536. def infinity():
  537. """
  538. Return an infinity leaf node.
  539. """
  540. return ExpressionLeaf(INFINITY)
  541. def absolute(exp):
  542. """
  543. Put an 'absolute value' operator on top of the given expression.
  544. """
  545. return ExpressionNode(OP_ABS, exp)
  546. def sin(*args):
  547. """
  548. Create a sinus function node.
  549. """
  550. return ExpressionNode(OP_SIN, *args)
  551. def cos(*args):
  552. """
  553. Create a cosinus function node.
  554. """
  555. return ExpressionNode(OP_COS, *args)
  556. def tan(*args):
  557. """
  558. Create a tangens function node.
  559. """
  560. return ExpressionNode(OP_TAN, *args)
  561. def log(exponent, base=None):
  562. """
  563. Create a logarithm function node (default base is 10).
  564. """
  565. if base is None:
  566. base = DEFAULT_LOGARITHM_BASE
  567. if not isinstance(base, ExpressionLeaf):
  568. base = ExpressionLeaf(base)
  569. return ExpressionNode(OP_LOG, exponent, base)
  570. def ln(exponent):
  571. """
  572. Create a natural logarithm node.
  573. """
  574. return log(exponent, base=E)
  575. def der(f, x=None):
  576. """
  577. Create a derivative node.
  578. """
  579. return ExpressionNode(OP_DXDER, f, x) if x else ExpressionNode(OP_PRIME, f)
  580. def integral(*args):
  581. """
  582. Create an integral node.
  583. """
  584. return ExpressionNode(OP_INT, *args)
  585. def indef(*args):
  586. """
  587. Create an indefinite integral node.
  588. """
  589. return ExpressionNode(OP_INT_INDEF, *args)
  590. def eq(left, right):
  591. """
  592. Create an equality operator node.
  593. """
  594. return ExpressionNode(OP_EQ, left, right)
  595. def sqrt(exp):
  596. """
  597. Create a square root node.
  598. """
  599. return ExpressionNode(OP_SQRT, exp)