polymani.pl 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. %% -*- Mode: Prolog -*-
  2. %% vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
  3. /**
  4. *
  5. * polimani.pl
  6. *
  7. * Assignment 1 - Polynomial Manipulator
  8. * Programming in Logic - DCC-FCUP
  9. *
  10. * Diogo Peralta Cordeiro
  11. * up201705417@fc.up.pt
  12. *
  13. * Hugo David Cordeiro Sales
  14. * up201704178@fc.up.pt
  15. *
  16. *********************************************
  17. * Follows 'Coding guidelines for Prolog' *
  18. * https://doi.org/10.1017/S1471068411000391 *
  19. *********************************************/
  20. /*
  21. * Import the Constraint Logic Programming over Finite Domains library
  22. * Essentially, this library improves the way Prolog deals with integers,
  23. * allowing more predicates to be reversible.
  24. * For instance, number(N) is always false, which prevents the
  25. * reversing of a predicate.
  26. */
  27. :- use_module(library(clpfd)).
  28. /*
  29. * Import Constraint Logic Programming for Reals library, which is somewhat
  30. * similar to clpfd, but for real numbers
  31. */
  32. :- use_module(library(clpr)).
  33. /*******************************
  34. * USER INTERFACE *
  35. *******************************/
  36. /*
  37. poly2list/2 transforms a list representing a polynomial (second
  38. argument) into a polynomial represented as an expression (first
  39. argument) and vice-versa.
  40. */
  41. poly2list(P, L) :-
  42. is_polynomial_valid_in_predicate(P, "poly2list"),
  43. polynomial_to_list(P, L),
  44. !.
  45. /*
  46. simpolylist/2 simplifies a polynomial represented as a list into
  47. another polynomial as a list.
  48. */
  49. simpoly_list(L, S) :-
  50. is_polynomial_as_list_valid_in_predicate(L, "simpoly_list"),
  51. simplify_polynomial_as_list(L, S),
  52. !.
  53. /*
  54. simpoly/2 simplifies a polynomial represented as an expression
  55. as another polynomial as an expression.
  56. */
  57. simpoly(P, S) :-
  58. is_polynomial_valid_in_predicate(P, "simpoly"),
  59. simplify_polynomial(P, S),
  60. !.
  61. /*
  62. scalepoly/3 multiplies a polynomial represented as an expression by a scalar
  63. resulting in a second polynomial. The two first arguments are assumed to
  64. be ground. The polynomial resulting from the sum is in simplified form.
  65. */
  66. scalepoly(P1, C, S) :-
  67. is_polynomial_valid_in_predicate(P1, "scalepoly"),
  68. is_number_in_predicate(C, "scalepoly"),
  69. scale_polynomial(P1, C, S),
  70. !.
  71. %% Tests:
  72. %% ?- scalepoly(3*x*z+2*z, 4, S).
  73. %@ S = 12*x*z+8*z.
  74. %% ?- scalepoly(3*x*z+2*z, 2, S).
  75. %@ S = 6*x*z+4*z.
  76. /*
  77. addpoly/3 adds two polynomials as expressions resulting in a
  78. third one. The two first arguments are assumed to be ground.
  79. The polynomial resulting from the sum is in simplified form.
  80. */
  81. addpoly(P1, P2, S) :-
  82. is_polynomial_valid_in_predicate(P1, "addpoly"),
  83. is_polynomial_valid_in_predicate(P2, "addpoly"),
  84. add_polynomial(P1, P2, S),
  85. !.
  86. %% Tests:
  87. %% ?- addpoly(3 + x, 3 - x, S).
  88. %@ S = 6.
  89. %% is_polynomial_valid_in_predicate(+T, +F) is det
  90. %
  91. % Returns true if valid polynomial, fails with UI message otherwise.
  92. % The failure message reports which polynomial is invalid and in which
  93. % predicate the problem ocurred.
  94. %
  95. is_polynomial_valid_in_predicate(P, _) :-
  96. %% If P is a valid polynomial, return true
  97. polynomial(P),
  98. !.
  99. is_polynomial_valid_in_predicate(P, F) :-
  100. %% Otherwise, write the polynomial and fails
  101. write("Invalid polynomial in "),
  102. write(F),
  103. write(": "),
  104. write(P),
  105. fail.
  106. %% Tests:
  107. %% ?- is_polynomial_valid_in_predicate(1-x, "Test").
  108. %@ true.
  109. %% ?- is_polynomial_valid_in_predicate(a*4-0*x, "Test").
  110. %@ Invalid polynomial in Test: a*4-0*x
  111. %@ false.
  112. %% is_polynomial_as_list_valid_in_predicate(+L, +F) is det
  113. %
  114. % Returns true if the polynomial represented as list is valid,
  115. % fails with UI message otherwise.
  116. % The failure message reports which polynomial is invalid and
  117. % in which predicate the problem ocurred.
  118. %
  119. is_polynomial_as_list_valid_in_predicate(L, F) :-
  120. %% If L is a valid polynomial, return true
  121. list_to_polynomial(L, P),
  122. is_polynomial_valid_in_predicate(P, F).
  123. %% Tests:
  124. %% ?- is_polynomial_as_list_valid_in_predicate([1], "Test").
  125. %@ true.
  126. %% ?- is_polynomial_as_list_valid_in_predicate([0*x, a*4], "Test").
  127. %@ Invalid polynomial in Test: a*4+0*x
  128. %@ false.
  129. %% is_number_in_predicate(+C:number, +F:string) is det
  130. %
  131. % Validates that C is a number or prints F and it then it
  132. %
  133. is_number_in_predicate(C, _) :-
  134. number(C),
  135. !.
  136. is_number_in_predicate(C, F) :-
  137. %% Writes the argument and fails
  138. write("Invalid number in "),
  139. write(F),
  140. write(": "),
  141. write(C),
  142. fail.
  143. /*******************************
  144. * NLP *
  145. *******************************/
  146. polyplay :-
  147. write("> "),
  148. read(R),
  149. (
  150. R = bye,
  151. write("See ya")
  152. ;
  153. (
  154. nlp_understand(R, P, I),
  155. writeln("That's trivial:"),
  156. nlp_compute(P, I)
  157. ;
  158. writeln("I didn't understand what you want.")
  159. ),
  160. polyplay
  161. ).
  162. nlp_understand(R,P,I) :-
  163. (
  164. R = simplify_x_squared,
  165. P = simplify,
  166. I = x^2
  167. ;
  168. fail
  169. ).
  170. nlp_compute(simplify, P) :-
  171. simplify_polynomial(P, O),
  172. writeln(O).
  173. nlp_compute(_,_) :-
  174. fail.
  175. parse_digit(zero, 0).
  176. parse_digit(one, 1).
  177. parse_digit(two, 2).
  178. parse_digit(three, 3).
  179. parse_digit(four, 4).
  180. parse_digit(five, 5).
  181. parse_digit(six, 6).
  182. parse_digit(seven, 7).
  183. parse_digit(eight, 8).
  184. parse_digit(nine, 9).
  185. parse_digit(ten, 10).
  186. parse_digit(eleven, 11).
  187. parse_digit(twelve, 12).
  188. parse_digit(thirteen, 13).
  189. parse_digit(fourteen, 14).
  190. parse_digit(fifteen, 15).
  191. parse_digit(sixteen, 16).
  192. parse_digit(seventeen, 17).
  193. parse_digit(eighteen, 18).
  194. parse_digit(nineteen, 19).
  195. parse_digit(twenty, 20).
  196. parse_digit(thirty, 30).
  197. parse_digit(forty, 40).
  198. parse_digit(fifty, 50).
  199. parse_digit(sixty, 60).
  200. parse_digit(seventy, 70).
  201. parse_digit(eighty, 80).
  202. parse_digit(ninety, 90).
  203. parse_number([],0).
  204. parse_number([N|L], X) :-
  205. parse_digit(N, X1),
  206. parse_number(L, X2),
  207. X is X1 + X2.
  208. /*******************************
  209. * BACKEND *
  210. *******************************/
  211. %% polynomial_variable_list(-List) is det
  212. %
  213. % List of possible polynomial variables
  214. %
  215. polynomial_variable_list([x, y, z]).
  216. %% polynomial_variable(?X:atom) is semidet
  217. %
  218. % Returns true if X is a polynomial variable, false otherwise.
  219. %
  220. polynomial_variable(X) :-
  221. polynomial_variable_list(V),
  222. member(X, V).
  223. %% Tests:
  224. %% ?- polynomial_variable(x).
  225. %@ true .
  226. %% ?- polynomial_variable(a).
  227. %@ false.
  228. %% power(+X:atom) is semidet
  229. %
  230. % Returns true if X is a power term, false otherwise.
  231. %
  232. power(P^N) :-
  233. %% CLPFD comparison. Reversible
  234. N #>= 1,
  235. polynomial_variable(P).
  236. power(X) :-
  237. polynomial_variable(X).
  238. %% Tests:
  239. %% ?- power(x).
  240. %@ true .
  241. %% ?- power(a).
  242. %@ false.
  243. %% ?- power(x^1).
  244. %@ true .
  245. %% ?- power(x^3).
  246. %@ true .
  247. %% ?- power(x^(-3)).
  248. %@ false.
  249. %% ?- power(-x).
  250. %@ false.
  251. %% ?- power(X).
  252. %@ X = x^_462546,
  253. %@ _462546 in 1..sup ;
  254. %@ X = y^_462546,
  255. %@ _462546 in 1..sup ;
  256. %@ X = z^_462546,
  257. %@ _462546 in 1..sup ;
  258. %@ X = x ;
  259. %@ X = y ;
  260. %@ X = z.
  261. %% term(+N:atom) is semidet
  262. %
  263. % Returns true if N is a term, false otherwise.
  264. %
  265. term(N) :-
  266. % If N is not a free variable
  267. nonvar(N),
  268. % Assert it as a number
  269. number(N).
  270. term(N) :-
  271. % If N is a free variable and not compound
  272. not(compound(N)),
  273. var(N),
  274. % Assert it must be between negative and positive infinity
  275. % This uses the CLPR library, which makes this reversible,
  276. % whereas `number(N)` is always false, since it only succeeds
  277. % if the argument is bound (to a integer or float)
  278. {N >= 0; N < 0}.
  279. term(X) :-
  280. power(X).
  281. term(-X) :-
  282. power(X).
  283. term(L * R) :-
  284. term(L),
  285. term(R).
  286. %% Tests:
  287. %% ?- term(2*x^3).
  288. %@ true .
  289. %% ?- term(x^(-3)).
  290. %@ false.
  291. %% ?- term(a).
  292. %@ false.
  293. %% ?- term(-1*x).
  294. %@ true .
  295. %% ?- term(-x).
  296. %@ true .
  297. %% ?- term((-3)*x^2).
  298. %@ true .
  299. %% ?- term(3.2*x).
  300. %@ true .
  301. %% ?- term(-x*(-z)).
  302. %@ true .
  303. %% ?- term(X).
  304. %@ {X>=0.0} ;
  305. %@ {X<0.0} ;
  306. %@ X = x^_111514,
  307. %@ _111514 in 1..sup ;
  308. %@ X = y^_111514,
  309. %@ _111514 in 1..sup ;
  310. %@ X = z^_111514,
  311. %@ _111514 in 1..sup ;
  312. %@ X = x ;
  313. %@ X = y ;
  314. %@ X = z ;
  315. %@ X = -x^_111522,
  316. %@ _111522 in 1..sup ;
  317. %@ X = -y^_111522,
  318. %@ _111522 in 1..sup ;
  319. %@ X = -z^_111522,
  320. %@ _111522 in 1..sup ;
  321. %@ X = -x ;
  322. %@ X = -y ;
  323. %@ X = -z ;
  324. %% polynomial(+M:atom) is semidet
  325. %
  326. % Returns true if polynomial, false otherwise.
  327. %
  328. polynomial(M) :-
  329. %% A polynomial is either a term
  330. term(M).
  331. polynomial(L + R) :-
  332. %% Or a sum of terms
  333. polynomial(L),
  334. term(R).
  335. polynomial(L - R) :-
  336. %% Or a subtraction of terms
  337. polynomial(L),
  338. term(R).
  339. %% Tests:
  340. %% ?- polynomial(x).
  341. %@ true .
  342. %% ?- polynomial(x^3).
  343. %@ true .
  344. %% ?- polynomial(3*x^7).
  345. %@ true .
  346. %% ?- polynomial(2 + 3*x + 4*x*y^3).
  347. %@ true .
  348. %% ?- polynomial(2 - 3*x + 4*x*y^3).
  349. %@ true .
  350. %% ?- polynomial(a).
  351. %@ false.
  352. %% ?- polynomial(x^(-3)).
  353. %@ false.
  354. %% ?- polynomial(-x + 3).
  355. %@ true .
  356. %% ?- polynomial(-x - -z).
  357. %@ true .
  358. %% power_to_canon(+T:atom, -T^N:atom) is semidet
  359. %
  360. % Returns a canon power term.
  361. %
  362. power_to_canon(T^N, T^N) :-
  363. polynomial_variable(T),
  364. % CLP(FD) operator to ensure N is different from 1,
  365. % in a reversible way
  366. N #\= 1.
  367. power_to_canon(T, T^1) :-
  368. polynomial_variable(T).
  369. %% Tests:
  370. %% ?- power_to_canon(x, X).
  371. %@ X = x^1 .
  372. %% ?- power_to_canon(-x, X).
  373. %@ false.
  374. %@ X = -1*x^1 .
  375. %% ?- power_to_canon(X, x^1).
  376. %@ X = x .
  377. %% ?- power_to_canon(X, x^4).
  378. %@ X = x^4 .
  379. %% ?- power_to_canon(X, a^1).
  380. %@ false.
  381. %% ?- power_to_canon(X, x^(-3)).
  382. %@ X = x^ -3 .
  383. %% ?- power_to_canon(X, -1*x^1).
  384. %@ X = -x .
  385. %% term_to_list(?T, ?List) is semidet
  386. %
  387. % Converts a term to a list of its monomials and vice versa.
  388. % Can verify if term and monomials list are compatible.
  389. %
  390. term_to_list(L * N, [N | TS]) :-
  391. number(N),
  392. term_to_list(L, TS).
  393. term_to_list(L * P, [P2 | TS]) :-
  394. power(P),
  395. power_to_canon(P, P2),
  396. term_to_list(L, TS).
  397. term_to_list(L * -P, [-P2 | TS]) :-
  398. power(P),
  399. power_to_canon(P, P2),
  400. term_to_list(L, TS).
  401. term_to_list(N, [N]) :-
  402. number(N).
  403. term_to_list(P, [P2]) :-
  404. power(P),
  405. power_to_canon(P, P2).
  406. term_to_list(-P, [-P2]) :-
  407. power(P),
  408. power_to_canon(P, P2).
  409. %% Tests:
  410. %% ?- term_to_list(1, X).
  411. %@ X = [1] .
  412. %% ?- term_to_list(-1, X).
  413. %@ X = [-1] .
  414. %% ?- term_to_list(x, X).
  415. %@ X = [x^1] .
  416. %% ?- term_to_list(-x, X).
  417. %@ X = [-x^1] .
  418. %% ?- term_to_list(2 * 3, X).
  419. %@ X = [3, 2] .
  420. %% ?- term_to_list(1*2*y*z*23*x*y*x^3*x, X).
  421. %@ X = [x^1, x^3, y^1, x^1, 23, z^1, y^1, 2, 1] .
  422. %% ?- term_to_list(1*2*y*z*23*x*y*(-1), X).
  423. %@ X = [-1, y^1, x^1, 23, z^1, y^1, 2, 1] .
  424. %% ?- term_to_list(X, [-1]).
  425. %@ X = -1 .
  426. %% ?- term_to_list(X, [x^1, -1]).
  427. %@ X = -1*x .
  428. %% ?- term_to_list(X, [-x^1]).
  429. %@ X = -x .
  430. %% ?- term_to_list(X, [y^1, x^1]).
  431. %@ X = x*y .
  432. %% ?- term_to_list(X, [x^4]).
  433. %@ X = x^4 .
  434. %% ?- term_to_list(X, [y^6, z^2, x^4]).
  435. %@ X = x^4*z^2*y^6 .
  436. %% ?- term_to_list(X, [y^6, z^2, x^4, -2]).
  437. %@ X = -2*x^4*z^2*y^6 .
  438. %% ?- term_to_list(X, [x^1, 0]).
  439. %@ X = 0*x .
  440. %% ?- term_to_list(X, [y^1, -2]).
  441. %@ X = -2*y .
  442. %% simplify_term(+Term_In:term, ?Term_Out:term) is det
  443. %
  444. % Simplifies a given term.
  445. % This function can also be be used to verify if
  446. % a term is simplified.
  447. %
  448. simplify_term(Term_In, Term_Out) :-
  449. term_to_list(Term_In, L),
  450. %% Sort the list of numbers and power to group them,
  451. %% simplifying the job of `join_similar_parts_of_term`
  452. sort(0, @=<, L, L2),
  453. (
  454. %% If there's a 0 in the list, then the whole term is 0
  455. member(0, L2),
  456. Term_Out = 0
  457. ;
  458. %% Otherwise
  459. (
  460. %% If there's only one element, then the term was already simplified
  461. %% This is done so that the `exclude` following doesn't remove all ones
  462. length(L2, 1),
  463. Term_Out = Term_In
  464. ;
  465. %% Remove all remaining ones
  466. exclude(==(1), L2, L3),
  467. join_similar_parts_of_term(L3, L4),
  468. %% Reverse the list, since the following call gives the result in the
  469. %% reverse order otherwise
  470. reverse(L4, L5),
  471. term_to_list(Term_Out, L5)
  472. )
  473. ),
  474. % First result is always the most simplified form.
  475. !.
  476. %% Tests:
  477. %% ?- simplify_term(1, X).
  478. %@ X = 1.
  479. %% ?- simplify_term(x, X).
  480. %@ X = x.
  481. %% ?- simplify_term(2*y*z*x^3*x, X).
  482. %@ X = 2*x^4*y*z.
  483. %% ?- simplify_term(1*y*z*x^3*x, X).
  484. %@ X = x^4*y*z.
  485. %% ?- simplify_term(0*y*z*x^3*x, X).
  486. %@ X = 0.
  487. %% ?- simplify_term(6*y*z*7*x*y*x^3*x, X).
  488. %@ X = 42*x^5*y^2*z.
  489. %% ?- simplify_term(-x, X).
  490. %@ X = -x.
  491. %% ?- simplify_term(-x*y*(-z)*3, X).
  492. %@ X = 3* -x* -z*y.
  493. %% ?- simplify_term(a, X).
  494. %@ false.
  495. %% ?- simplify_term(x^(-3), X).
  496. %@ false.
  497. %% join_similar_parts_of_term(+List, -List) is det
  498. %
  499. % Combine powers of the same variable in the given list.
  500. % Requires that the list be sorted.
  501. %
  502. join_similar_parts_of_term([P1, P2 | L], L2) :-
  503. %% If both symbols are powers
  504. power(P1),
  505. power(P2),
  506. %% Decompose them into their parts
  507. B^N1 = P1,
  508. B^N2 = P2,
  509. %% Sum the exponent
  510. N is N1 + N2,
  511. join_similar_parts_of_term([B^N | L], L2),
  512. % First result is always the most simplified form.
  513. !.
  514. join_similar_parts_of_term([N1, N2 | L], L2) :-
  515. %% If they are both numbers
  516. number(N1),
  517. number(N2),
  518. %% Multiply them
  519. N is N1 * N2,
  520. join_similar_parts_of_term([N | L], L2),
  521. % First result is always the most simplified form.
  522. !.
  523. join_similar_parts_of_term([X | L], [X | L2]) :-
  524. %% Otherwise consume one element and recurse
  525. join_similar_parts_of_term(L, L2),
  526. % First result is always the most simplified form.
  527. !.
  528. join_similar_parts_of_term([], []).
  529. %% Tests:
  530. %% ?- join_similar_parts_of_term([3], T).
  531. %@ T = [3].
  532. %% ?- join_similar_parts_of_term([x^2], T).
  533. %@ T = [x^2].
  534. %% ?- join_similar_parts_of_term([x^1, x^1, x^1, x^1], T).
  535. %@ T = [x^4].
  536. %% ?- join_similar_parts_of_term([2, 3, x^1, x^2], T).
  537. %@ T = [6, x^3].
  538. %% ?- join_similar_parts_of_term([2, 3, x^1, x^2, y^1, y^6], T).
  539. %@ T = [6, x^3, y^7].
  540. %% ?- join_similar_parts_of_term([2, 3, -x^1, -x^2], T).
  541. %@ T = [6, -x^1, -x^2].
  542. %% simplify_polynomial(+P:atom, -P2:atom) is det
  543. %
  544. % Simplifies a polynomial.
  545. %
  546. simplify_polynomial(0, 0) :-
  547. % 0 is already fully simplified. This is an
  548. % exception to the following algorithm
  549. !.
  550. simplify_polynomial(P, P2) :-
  551. polynomial_to_list(P, L),
  552. simplify_polynomial_as_list(L, L2),
  553. list_to_polynomial(L2, P2),
  554. %% The first result is the most simplified one
  555. !.
  556. %% Tests:
  557. %% ?- simplify_polynomial(1, X).
  558. %@ X = 1.
  559. %% ?- simplify_polynomial(0, X).
  560. %@ X = 0.
  561. %% ?- simplify_polynomial(x, X).
  562. %@ X = x.
  563. %% ?- simplify_polynomial(x*x, X).
  564. %@ X = x^2.
  565. %% ?- simplify_polynomial(2 + 2, X).
  566. %@ X = 2*2.
  567. %% ?- simplify_polynomial(x + x, X).
  568. %@ X = 2*x.
  569. %% ?- simplify_polynomial(0 + x*x, X).
  570. %@ X = x^2.
  571. %% ?- simplify_polynomial(x^2*x + 3*x^3, X).
  572. %@ X = 4*x^3.
  573. %% ?- simplify_polynomial(x^2*x + 3*x^3 + x^3 + x*x*x, X).
  574. %@ X = 6*x^3.
  575. %% ?- simplify_polynomial(x^2*x + 3*x^3 + x^3 + x*x*4 + z, X).
  576. %@ X = 5*x^3+4*x^2+z.
  577. %% ?- simplify_polynomial(x^2*x + 3*x^3 - x^3 - x*x*4 + z, X).
  578. %@ X = 3*x^3-4*x^2+z.
  579. %% ?- simplify_polynomial(x + 1 + x, X).
  580. %@ X = 2*x+1.
  581. %% ?- simplify_polynomial(x + 1 + x + 1 + x + 1 + x, X).
  582. %@ X = 4*x+3.
  583. %% simplify_polynomial_as_list(+L1:List,-L3:List) is det
  584. %
  585. % Simplifies a polynomial represented as a list.
  586. %
  587. simplify_polynomial_as_list(L, L13) :-
  588. %% Convert each term to a list
  589. maplist(term_to_list, L, L2),
  590. %% Sort each sublist so that the next
  591. %% sort gives the correct results
  592. maplist(sort(0, @>=), L2, L3),
  593. %% Sort the outer list
  594. sort(0, @>=, L3, L4),
  595. %% For each of the parts of the terms, join them
  596. maplist(join_similar_parts_of_term, L4, L5),
  597. %% Sort each of the sublists
  598. %% Done so the next call simplifies has less work
  599. maplist(sort(0, @=<), L5, L6),
  600. join_similar_terms(L6, L7),
  601. %% Exclude any sublist that includes a 0 (such as the
  602. %% equivalent to the term 0*x)
  603. exclude(member(0), L7, L8),
  604. %% Reverse each sublist, because the next call
  605. %% reverses the result
  606. maplist(reverse, L8, L9),
  607. maplist(term_to_list, L10, L9),
  608. %% Delete any 0 from the list
  609. delete(L10, 0, L11),
  610. %% Sort list converting back gives the result in the correct order
  611. sort(0, @=<, L11, L12),
  612. (
  613. %% If the list is empty, the result is a list with 0
  614. L12 = [], L13 = [0]
  615. ;
  616. %% Otherwise, this is the result
  617. L13 = L12
  618. ).
  619. %% Tests:
  620. %% ?- simplify_polynomial_as_list([x, 1, x^2, x*y, 3*x^2, 4*x], L).
  621. %@ L = [1, 4*x^2, 5*x, x*y] .
  622. %% ?- simplify_polynomial_as_list([1, x^2, x*y, 3*x^2, -4, -1*x], L).
  623. %@ L = [-3, -1*x, 4*x^2, x*y] .
  624. %% ?- simplify_polynomial_as_list([0*x, 0], L).
  625. %@ L = [0] .
  626. %% join_similar_terms(+P:List, -P2:List) is det
  627. %
  628. % Joins similar sublists representing terms by using
  629. % `add_terms` to check if they can be merged and perform
  630. % the addition. Requires the list of list be sorted with
  631. % `maplist(sort(0, @>=), L, L2),
  632. % sort(0, @>=, L2, L3)`
  633. % and that the sublists to be sorted with
  634. % `sort(0, @=<)` since that is inherited from `add_terms`.
  635. %
  636. join_similar_terms([TL, TR | L], L2) :-
  637. %% Check if terms can be added and add them
  638. add_terms(TL, TR, T2),
  639. %% Recurse, accumulation on the first element
  640. join_similar_terms([T2 | L], L2),
  641. %% Give only first result. Red cut
  642. !.
  643. join_similar_terms([X | L], [X | L2]) :-
  644. %% If a pair of elements can't be added, skip one
  645. %% and recurse
  646. join_similar_terms(L, L2),
  647. %% Give only first result. Red cut
  648. !.
  649. join_similar_terms([], []).
  650. %% Tests:
  651. %% ?- join_similar_terms([[2, x^3], [3, x^3], [x^3]], L).
  652. %@ L = [[6, x^3]].
  653. %% term_to_canon(+T:List, -T2:List) is det
  654. %
  655. % Adds the coefficient of the term as the first element of the list
  656. %
  657. %% Special cases to make this predicate reversible
  658. term_to_canon([1], [1]) :-
  659. !.
  660. term_to_canon(L2, [1 | L]) :-
  661. nonvar(L),
  662. L2 = L,
  663. !.
  664. term_to_canon([-1], [-1]) :-
  665. !.
  666. term_to_canon([-P | L2], [-1, P | L]) :-
  667. nonvar(L),
  668. L2 = L,
  669. !.
  670. term_to_canon([N2 | L], [N | L]) :-
  671. number(N),
  672. N2 = N,
  673. !.
  674. %% Normal case
  675. term_to_canon(L, [N | L2]) :-
  676. term_to_canon_with_coefficient(N, L, L2),
  677. !.
  678. %% Tests:
  679. %% ?- term_to_canon([2], T).
  680. %@ T = [2].
  681. %% ?- term_to_canon([-x], T).
  682. %@ T = [-1, x].
  683. %% ?- term_to_canon([-x^3], T).
  684. %@ T = [-1, x^3].
  685. %% ?- term_to_canon([x^1], T).
  686. %@ T = [1, x^1].
  687. %% ?- term_to_canon([x^3], T).
  688. %@ T = [1, x^3].
  689. %% ?- term_to_canon([x^3, z], T).
  690. %@ T = [1, x^3, z].
  691. %% ?- term_to_canon([2, x^3], T).
  692. %@ T = [2, x^3].
  693. %% ?- term_to_canon([2, -x^3], T).
  694. %@ T = [-2, x^3].
  695. %% ?- term_to_canon([2, -x^3, -z], T).
  696. %@ T = [2, x^3, z].
  697. %% ?- term_to_canon(L, [-1]).
  698. %@ L = [-1].
  699. %% ?- term_to_canon(L, [1]).
  700. %@ L = [1].
  701. %% ?- term_to_canon(L, [-2]).
  702. %@ L = [-2].
  703. %% ?- term_to_canon(L, [-2, x]).
  704. %@ L = [-2, x].
  705. %% ?- term_to_canon(L, [1, x]).
  706. %@ L = [x].
  707. %% ?- term_to_canon(L, [-1, x]).
  708. %@ L = [-x].
  709. %% ?- term_to_canon(L, [1, x, z, y]).
  710. %@ L = [x, z, y].
  711. %% ?- term_to_canon(L, [-1, x, z, y]).
  712. %@ L = [-x, z, y].
  713. %% term_to_canon_with_coefficient(-N:number, +L:List, -L2:List) is semidet
  714. %
  715. % Calculates the coefficient of the term and removes negations of powers,
  716. % accumulating the results in N
  717. %
  718. term_to_canon_with_coefficient(N, [N2 | TS], TS2) :-
  719. number(N2),
  720. term_to_canon_with_coefficient(N3, TS, TS2),
  721. N is N2 * N3,
  722. !.
  723. term_to_canon_with_coefficient(N, [P | TS], [P2 | TS2]) :-
  724. sign_of_power(P, N2 * P2),
  725. term_to_canon_with_coefficient(N3, TS, TS2),
  726. N is N2 * N3,
  727. !.
  728. term_to_canon_with_coefficient(N, [], []) :-
  729. nonvar(N);
  730. N = 1.
  731. %% Tests:
  732. %% ?- term_to_canon_with_coefficient(N, [x], L).
  733. %@ N = 1,
  734. %@ L = [x].
  735. %% ?- term_to_canon_with_coefficient(N, [x, x^2, 2], L).
  736. %@ N = 2,
  737. %@ L = [x^1, x^2].
  738. %% ?- term_to_canon_with_coefficient(N, [x, x^2, 2, 4, z], L).
  739. %@ N = 8,
  740. %@ L = [x, x^2, z].
  741. %% ?- term_to_canon_with_coefficient(N, [x, x^2, 2, 4, -z], L).
  742. %@ N = -8,
  743. %@ L = [x, x^2, z].
  744. %% ?- term_to_canon_with_coefficient(N, [x, -x^2, 2, 4, -z], L).
  745. %@ N = 8,
  746. %@ L = [x, x^2, z].
  747. %% ?- term_to_canon_with_coefficient(N, L, [x]).
  748. %@ N = 1,
  749. %@ L = [x].
  750. %% ?- term_to_canon_with_coefficient(N, L, [1]).
  751. %@ N = 1,
  752. %@ L = [1].
  753. %% ?- term_to_canon_with_coefficient(N, L, [2]).
  754. %@ N = 1,
  755. %@ L = [2].
  756. %% sign_of_power(P:power, P:term) is det
  757. %
  758. % If there isn't a leading minus, multiplies the power by 1,
  759. % otherwise by a -1. This way it prefers the positive version.
  760. % Not idempotent
  761. %
  762. sign_of_power(P, 1*P) :-
  763. %% If P can't unify with a minus followed by an unnamed variable
  764. P \= -_,
  765. !.
  766. sign_of_power(-P, -1*P).
  767. %% Tests:
  768. %% ?- sign_of_power(x, X).
  769. %@ X = 1*x.
  770. %% ?- sign_of_power(-x, X).
  771. %@ X = -1*x.
  772. %% ?- sign_of_power(X, 1*x).
  773. %@ X = x.
  774. %% ?- sign_of_power(X, -1*x).
  775. %@ X = -x.
  776. %% add_terms(+L:List, +R:List, -Result:List) is det
  777. %
  778. % Adds two terms represented as list by adding
  779. % the coeficients if the power is the same.
  780. % Returns false if they can't be added
  781. % Requires the list of terms to be simplified.
  782. %
  783. add_terms([NL | TL], [NR | TR], [N2 | TL2]) :-
  784. %% Convert each term to a canon form. This ensures they
  785. %% have a number in front, so it can be added
  786. term_to_canon([NL | TL], [NL2 | TL2]),
  787. term_to_canon([NR | TR], [NR2 | TR2]),
  788. %% If the rest of the term is the same
  789. TL2 == TR2,
  790. %% Add the coeficients
  791. N2 is NL2 + NR2.
  792. %% Tests
  793. %% ?- add_terms([1], [1], R).
  794. %@ R = [2].
  795. %% ?- add_terms([x], [x], R).
  796. %@ R = [2, x].
  797. %% ?- add_terms([2, x^3], [x^3], R).
  798. %@ R = [3, x^3].
  799. %% ?- add_terms([2, x^3], [3, x^3], R).
  800. %@ R = [5, x^3].
  801. %% ?- add_terms([2, x^3], [3, x^2], R).
  802. %@ false.
  803. %% polynomial_to_list(+P:polynomial, -L:List) is det
  804. %
  805. % Converts a polynomial in a list.
  806. %
  807. polynomial_to_list(L - T, [T2 | LS]) :-
  808. term(T),
  809. negate_term(T, T2),
  810. polynomial_to_list(L, LS),
  811. !.
  812. polynomial_to_list(L + T, [T | LS]) :-
  813. term(T),
  814. polynomial_to_list(L, LS),
  815. !.
  816. polynomial_to_list(T, [T]) :-
  817. term(T),
  818. !.
  819. %% Tests:
  820. %% ?- polynomial_to_list(2, S).
  821. %@ S = [2].
  822. %% ?- polynomial_to_list(x^2, S).
  823. %@ S = [x^2].
  824. %% ?- polynomial_to_list(x^2 + x^2, S).
  825. %@ S = [x^2, x^2].
  826. %% ?- polynomial_to_list(2*x^2+5+y*2, S).
  827. %@ S = [y*2, 5, 2*x^2].
  828. %% ?- polynomial_to_list(2*x^2+5-y*2, S).
  829. %@ S = [-2*y, 5, 2*x^2].
  830. %% ?- polynomial_to_list(2*x^2-5-y*2, S).
  831. %@ S = [-2*y, -5, 2*x^2].
  832. %% list_to_polynomial(+L:List, -P:Polynomial) is det
  833. %
  834. % Converts a list in a polynomial.
  835. % An empty list will return false.
  836. %
  837. list_to_polynomial([T1|T2], P) :-
  838. % Start recursive calls until we are in the
  839. % end of the list. We know that the `-` will
  840. % always be at the left of a term.
  841. list_to_polynomial(T2, L1),
  842. (
  843. % If this is a negative term
  844. term_string(T1, S1),
  845. string_chars(S1, [First|_]),
  846. First = -,
  847. % Concat them
  848. term_string(L1, S2),
  849. string_concat(S2,S1,S3),
  850. term_string(P, S3)
  851. ;
  852. % Otherwise sum them
  853. P = L1+T1
  854. ),
  855. % The others computations are semantically meaningless
  856. !.
  857. list_to_polynomial([T], T).
  858. %% Tests:
  859. %% ?- list_to_polynomial([1, x, x^2], P).
  860. %@ P = x^2+x+1.
  861. %% ?- list_to_polynomial([-1, -x, -x^2], P).
  862. %@ P = -x^2-x-1.
  863. %% ?- list_to_polynomial([1, -x, x^2], P).
  864. %@ P = x^2-x+1.
  865. %% ?- list_to_polynomial([x^2, x, 1], P).
  866. %@ P = 1+x+x^2.
  867. %% ?- list_to_polynomial([a,-e], P).
  868. %@ P = -e+a.
  869. %% ?- list_to_polynomial([], P).
  870. %@ false.
  871. %% ?- list_to_polynomial([a], P).
  872. %@ P = a.
  873. %% negate_term(T, T2) is det
  874. %
  875. % Negate the coeficient of a term and return the negated term.
  876. %
  877. negate_term(T, T2) :-
  878. term_to_list(T, L),
  879. %% Ensure there is a coeficient
  880. term_to_canon(L, L2),
  881. [N | R] = L2,
  882. %% (-)/1 is an operator, needs to be evaluated, otherwise
  883. %% it gives a symbolic result, which messes with further processing
  884. N2 is -N,
  885. %% Convert the term back from canonic form
  886. term_to_canon(L3, [N2 | R]),
  887. %% Reverse the order of the list, because converting
  888. %% implicitly reverses it
  889. reverse(L3, L4),
  890. term_to_list(T2, L4),
  891. !.
  892. %% Tests:
  893. %% ?- negate_term(1, R).
  894. %@ R = -1.
  895. %% ?- negate_term(x, R).
  896. %@ R = -x.
  897. %% ?- negate_term(-x, R).
  898. %@ R = x.
  899. %% ?- negate_term(x^2, R).
  900. %@ R = -x^2.
  901. %% ?- negate_term(3*x*y^2, R).
  902. %@ R = -3*y^2*x.
  903. %% scale_polynomial(+P:Polynomial,+C:Constant,-S:Polynomial) is det
  904. %
  905. % Multiplies a polynomial by a scalar.
  906. %
  907. scale_polynomial(P, C, S) :-
  908. polynomial_to_list(P, L),
  909. %% Convert each term to a list
  910. maplist(term_to_list, L, L2),
  911. %% Canonize terms
  912. maplist(term_to_canon, L2, L3),
  913. %% Append C to the start of each sublist
  914. maplist(cons(C), L3, L4),
  915. %% Convert to a list of terms
  916. maplist(term_to_list, L5, L4),
  917. %% Simplify the resulting polynomial
  918. simplify_polynomial_as_list(L5, L6),
  919. %% Return as a simplified polynomial
  920. list_to_polynomial(L6, S),
  921. !.
  922. %% Tests:
  923. %% ?- scale_polynomial(3*x^2, 2, S).
  924. %@ S = 6*x^2.
  925. %% cons(+C:atom, +L:List, -L2:List) is det
  926. %
  927. % Add an atom C to the head of a list L.
  928. %
  929. cons(C, L, [C | L]).
  930. %% Tests:
  931. %% ?- cons(C, L, L2).
  932. %@ L2 = [C|L].
  933. %% add_polynomial(+P1:polynomial,+P2:polynomial,-S:polynomial) is det
  934. %
  935. % S = P1 + P2.
  936. %
  937. add_polynomial(P1, P2, S) :-
  938. %% Convert both polynomials to lists
  939. polynomial_to_list(P1, L1),
  940. polynomial_to_list(P2, L2),
  941. %% Join them
  942. append(L1, L2, L3),
  943. %% Simplify the resulting polynomial
  944. simplify_polynomial_as_list(L3, L4),
  945. %% Convert back
  946. list_to_polynomial(L4, S),
  947. !.
  948. %% Tests:
  949. %% ?- add_polynomial(2, 2, S).
  950. %@ S = 4.
  951. %% ?- add_polynomial(x, x, S).
  952. %@ S = 2*x.
  953. %% ?- add_polynomial(2*x+5*z, 2*z+6*x, S).
  954. %@ S = 8*x+7*z.