Commit 6158ddbe authored by Thierry Martinez's avatar Thierry Martinez
Browse files

Arithmetic simplification algorithm

parent 9318f00e
......@@ -3,306 +3,460 @@
[
simplify/2,
distribute/2,
additive_normal_form/2
additive_normal_form/2,
always_negative/1,
always_positive/1
]).
simplify(Value, Value) :-
number(Value),
!.
simplify(Parameter, Parameter) :-
atom(Parameter),
!.
:- dynamic(canonical/3).
simplify(p(ParameterIndex), p(ParameterIndex)) :-
!.
simplify([VariableIndex], [VariableIndex]) :-
!.
simplify(In, Out) :-
(
simplify_aux(In, Out0)
->
Out = Out0
;
throw(error(simplify_failure))
).
simplify(- A, Result) :-
simplify_aux(In, Out) :-
additive_block(In, Blocks),
!,
simplify(A, Asimplified),
simplify_opposite(Asimplified, Result).
simplify_blocks(additive_block, additive_index, rebuild_additive_coef, Blocks, CoefSubBlocks),
rebuild_additive_blocks(CoefSubBlocks, Out).
simplify(A + B, Result) :-
simplify_aux(In, Out) :-
multiplicative_block(In, Blocks),
!,
simplify(A, Asimplified),
simplify(B, Bsimplified),
simplify_addition(Asimplified, Bsimplified, Result).
simplify_blocks(multiplicative_block, multiplicative_index, rebuild_multiplicative_coef, Blocks, CoefSubBlocks),
compute_product(CoefSubBlocks, CoefSubBlocksComputed),
rebuild_multiplicative_blocks(CoefSubBlocksComputed, Out).
simplify(A - B, Result) :-
simplify_aux(log(exp(Expr)), Out) :-
!,
simplify(A, Asimplified),
simplify(B, Bsimplified),
simplify_difference(Asimplified, Bsimplified, Result).
simplify_aux(Expr, Out).
simplify(A * B, Result) :-
simplify_aux(exp(log(Expr)), Out) :-
!,
simplify(A, Asimplified),
simplify(B, Bsimplified),
simplify_multiplication(Asimplified, Bsimplified, Result).
simplify_aux(Expr, Out).
simplify(A / B, Result) :-
simplify_aux(exp(N * log(Expr)), Out) :-
number(N),
!,
simplify(A, Asimplified),
simplify(B, Bsimplified),
simplify_division(Asimplified, Bsimplified, Result).
simplify_aux(Expr ^ N, Out).
simplify(A ^ B, Result) :-
simplify_aux(log(A) + log(B), Out) :-
!,
simplify(A, Asimplified),
simplify(B, Bsimplified),
simplify_power(Asimplified, Bsimplified, Result).
simplify_aux(log(A * B), Out).
simplify(sqrt(A), Result) :-
simplify_aux(exp(A) * exp(B), Out) :-
!,
simplify(A ^ 0.5, Result).
simplify_aux(exp(A + B), Out).
simplify(exp(A), Result) :-
simplify_aux(Expr ^ 0.5, Out) :-
!,
simplify(A, Asimplified),
simplify_exp(Asimplified, Result).
simplify_aux(sqrt(Expr), Out).
simplify(log(A), Result) :-
simplify_aux(Expr ^ 1, Out) :-
!,
simplify(A, Asimplified),
simplify_log(Asimplified, Result).
simplify_aux(Expr, Out).
simplify_aux(In, Out) :-
term_morphism(arithmetic_rules:simplify_aux, In, Middle),
(
rewrite_eval(Middle, Out)
->
true
;
Out = Middle
).
simplify(cos(A), Result) :-
simplify_blocks(Block, Index, RebuildCoef, Blocks, CoefSubBlocks) :-
gather_loop(Block, Blocks, SubBlocks),
maplist(Index, SubBlocks, IndexedSubBlocks),
sort(4, @=<, IndexedSubBlocks, SortedSubBlocks),
check_cleaned(arithmetic_rules:canonical/3),
gather_indexed(SortedSubBlocks),
reduce_blocks(IndexedSubBlocks, ReducedSubBlocks),
clean(arithmetic_rules:canonical/3),
map_blocks(RebuildCoef, ReducedSubBlocks, CoefSubBlocks).
gather_loop(Block, Blocks, SubBlocks) :-
gather_blocks(Block, Blocks, Blocks0),
map_blocks(simplify_aux, Blocks0, Blocks1),
(
Blocks0 = Blocks1
->
SubBlocks = Blocks0
;
gather_loop(Block, Blocks1, SubBlocks)
).
compute_product([], []).
compute_product([+ N | T], Out) :-
number(N),
!,
simplify(A, Asimplified),
simplify_cos(Asimplified, Result).
compute_product_with_others(T, PT, Others),
P is N * PT,
(
P = 1
->
Out = Others
;
Out = [+ P | Others]
).
compute_product([H | TIn], [H | TOut]) :-
compute_product(TIn, TOut).
simplify(sin(A), Result) :-
compute_product_with_others([], 1, []).
compute_product_with_others([+ N | T], P, Others) :-
number(N),
!,
simplify(A, Asimplified),
simplify_sin(Asimplified, Result).
compute_product_with_others(T, PT, Others),
P is N * PT.
compute_product_with_others([H | TIn], P, [H | TOut]) :-
compute_product_with_others(TIn, P, TOut).
simplify(
product(Pattern in List, Expression),
product(Pattern in List, SimplifiedExpression)
) :-
reduce_blocks([], []).
reduce_blocks([index(_, _, _, _, Canonical) | TailIn], Out) :-
retract(canonical(Canonical, Sum, Others)),
!,
simplify(Expression, SimplifiedExpression).
assert(canonical(Canonical, 0, Others)),
reduce_block(Sum, Others, Out, TailOut),
reduce_blocks(TailIn, TailOut).
reduce_blocks([index(Expr, Sign, Coef, _, _) | TailIn], Out) :-
(
Coef = 0
->
reduce_blocks(TailIn, Out)
;
reduce_block(Sign, Expr, Out, TailOut),
reduce_blocks(TailIn, TailOut)
).
simplify(FunctionApplication, SimplifiedFunctionApplication) :-
callable(FunctionApplication),
reduce_block(Coef, Expr, Out, TailOut) :-
(
Coef > 0
->
Out = [+ (Coef : Expr) | TailOut]
;
Coef < 0
->
CoefOpp is -Coef,
Out = [- (CoefOpp : Expr) | TailOut]
;
Out = TailOut
).
gather_indexed([]).
gather_indexed([index(_, _, Coef1, Others, Canonical), index(_, _, Coef2, _, Canonical) | Tail]) :-
!,
term_morphism(simplify, FunctionApplication, SimplifiedFunctionApplication).
gather_indexed_same(Canonical, Tail, TailSum, TailOthers),
Sum is Coef1 + Coef2 + TailSum,
assertz(canonical(Canonical, Sum, Others)),
gather_indexed(TailOthers).
simplify(E, _Result) :-
type_error(arithmetic_expression, E).
gather_indexed([_ | Tail]) :-
gather_indexed(Tail).
simplify_opposite(Value, ValueOpp) :-
number(Value),
gather_indexed_same(Canonical, [index(_, _, Coef, _, Canonical) | Tail], Sum, Others) :-
!,
ValueOpp is -Value.
gather_indexed_same(Canonical, Tail, TailSum, Others),
Sum is Coef + TailSum.
simplify_opposite(A, - A).
gather_indexed_same(_Canonical, Others, 0, Others).
simplify_addition(0, A, A) :-
!.
additive_index(H, index(Expr, Sign, Coef, Others, Canonical)) :-
sign(H, Sign, Expr),
(
extract_coefficient(Expr, SubCoef, Others)
->
true
;
number(Expr)
->
SubCoef = Expr,
Others = 1
;
SubCoef = 1,
Others = Expr
),
Coef is Sign * SubCoef,
canonical_expression(Others, Canonical).
simplify_addition(A, 0, A) :-
!.
simplify_addition(Value1, Value2, Value) :-
number(Value1),
number(Value2),
!,
Value is Value1 + Value2.
extract_coefficient(A * B, A, B) :-
number(A).
extract_coefficient(A * B, B, A) :-
number(B).
extract_coefficient(CA * B, C, A * B) :-
extract_coefficient(CA, C, A).
extract_coefficient(A * CB, C, A * B) :-
extract_coefficient(CB, C, B).
simplify_addition(A, Value, Result) :-
number(Value),
Value < 0,
extract_powers(sqrt(Expr), Power, Sub) :-
!,
ValueOpp is -Value,
Result = A - ValueOpp.
extract_powers(Expr, SubPower, Sub),
Power is 0.5 * SubPower.
simplify_addition(A, Value * B, Result) :-
number(Value),
Value < 0,
extract_powers(Expr ^ Pow, Power, Sub) :-
number(Pow),
!,
ValueOpp is -Value,
Result = A - ValueOpp * B.
extract_powers(Expr, SubPower, Sub),
Power is Pow * SubPower.
simplify_addition(-A, B, C) :-
extract_powers(Expr, 1, Expr).
multiplicative_index(H, index(Expr, Sign, Coef, Others, Canonical)) :-
sign(H, Sign, Expr),
extract_powers(Expr, SubCoef, Others),
Coef is Sign * SubCoef,
canonical_expression(Others, Canonical).
canonical_expression(In, Out) :-
additive_block(In, Blocks),
!,
simplify_difference(B, A, C).
gather_blocks(additive_block, Blocks, SubBlocks),
map_blocks(arithmetic_rules:canonical_expression, SubBlocks, CanonicalSubBlocks),
sort(CanonicalSubBlocks, SortedSubBlocks),
rebuild_additive_blocks(SortedSubBlocks, Out).
simplify_addition(A, -B, C) :-
canonical_expression(In, Out) :-
multiplicative_block(In, Blocks),
!,
simplify_difference(A, B, C).
gather_blocks(multiplicative_block, Blocks, SubBlocks),
map_blocks(arithmetic_rules:canonical_expression, SubBlocks, CanonicalSubBlocks),
sort(CanonicalSubBlocks, SortedSubBlocks),
rebuild_multiplicative_blocks(SortedSubBlocks, Out).
canonical_expression(In, Out) :-
term_morphism(arithmetic_rules:canonical_expression, In, Out).
simplify_addition(A, A, 2 * A) :-
!.
simplify_addition(A, B, A + B).
rebuild_additive_coef(Block, Value) :-
(
Block = (Coef : Expr)
->
(
Coef = 1
->
Value = Expr
;
Expr = 1
->
Value = Coef
;
insert_coef(Expr, Coef, Value)
)
;
Value = Block
).
simplify_difference(0, A, AOpp) :-
insert_coef(A * B, Coef, Result * B) :-
!,
simplify_opposite(A, AOpp).
insert_coef(A, Coef, Result).
simplify_difference(A, 0, A) :-
!.
insert_coef(A, Coef, Coef * A).
simplify_difference(Value1, Value2, Value) :-
number(Value1),
number(Value2),
!,
Value is Value1 + Value2.
simplify_difference(A, Value, Result) :-
number(Value),
Value < 0,
!,
ValueOpp is -Value,
Result = A + ValueOpp.
rebuild_multiplicative_coef(Block, Value) :-
(
Block = (Coef : Expr)
->
(
Coef = 1
->
Value = Expr
;
Expr = 1
->
Value = 1
;
Coef = 0.5
->
Value = sqrt(Expr)
;
Value = Expr ^ Coef
)
;
Value = Block
).
simplify_difference(A, Value * B, Result) :-
number(Value),
Value < 0,
!,
ValueOpp is -Value,
Result = A + ValueOpp * B.
simplify_difference(A, A, 0) :-
!.
rebuild_additive_blocks([], 0).
simplify_difference(A, -B, C) :-
rebuild_additive_blocks([+H | T], Out) :-
!,
simplify_addition(A, B, C).
rebuild_additive_blocks(H, T, Out).
simplify_difference(-A, B, -C) :-
rebuild_additive_blocks([-H | T], Out) :-
select(+A, T, Others),
!,
simplify_addition(A, B, C).
rebuild_additive_blocks(A, [-H | Others], Out).
simplify_difference(A, B, A - B).
rebuild_additive_blocks([-H | T], Out) :-
rebuild_additive_blocks(-H, T, Out).
simplify_multiplication(0, _B, 0) :-
rebuild_additive_blocks(A, [], A) :-
!.
simplify_multiplication(_A, 0, 0) :-
!.
rebuild_additive_blocks(A, [+H | T], Out) :-
!,
rebuild_additive_blocks(A + H, T, Out).
rebuild_additive_blocks(A, [-H | T], Out) :-
rebuild_additive_blocks(A - H, T, Out).
simplify_multiplication(1, B, B) :-
!.
simplify_multiplication(1 / A, B, Result) :-
rebuild_multiplicative_blocks([], 1).
rebuild_multiplicative_blocks([+H | T], Out) :-
!,
simplify_division(B, A, Result).
rebuild_multiplicative_blocks(H, T, Out).
simplify_multiplication(A, 1 / B, Result) :-
rebuild_multiplicative_blocks([-H | T], Out) :-
select(+A, T, Others),
!,
simplify_division(A, B, Result).
rebuild_multiplicative_blocks(A, [-H | Others], Out).
rebuild_multiplicative_blocks([-H | T], Out) :-
rebuild_multiplicative_blocks(1 / H, T, Out).
simplify_multiplication(A, 1, A) :-
rebuild_multiplicative_blocks(A, [], A) :-
!.
simplify_multiplication(-1, B, BOpp) :-
rebuild_multiplicative_blocks(A, [+H | T], Out) :-
!,
simplify_opposite(B, BOpp).
rebuild_multiplicative_blocks(A * H, T, Out).
simplify_multiplication(A, -1, AOpp) :-
!,
simplify_opposite(A, AOpp).
rebuild_multiplicative_blocks(A, [-H | T], Out) :-
rebuild_multiplicative_blocks(A / H, T, Out).
simplify_multiplication(A / B, B ^ Value, Result) :-
number(Value),
!,
ValuePred is Value - 1,
simplify_power(B, ValuePred, BPowerValuePred),
simplify_multiplication(A, BPowerValuePred, Result).
simplify_multiplication(A / B, sqrt(B), Result) :-
!,
simplify_power(B, 0.5, SqrtB),
simplify_division(A, SqrtB, Result).
gather_blocks(P, Blocks, SubBlocks) :-
map_blocks(maybe(P), Blocks, BlocksOfBlocks),
flatten_blocks(BlocksOfBlocks, SubBlocks).
simplify_multiplication(Value1, Value2, Result) :-
number(Value1),
number(Value2),
maybe(P, A, B) :-
call(P, A, SubBlocks),
!,
Result is Value1 * Value2.
gather_blocks(P, SubBlocks, B).
simplify_multiplication(A, B, A * B).
maybe(_P, A, [+A]).
simplify_division(0, _B, 0) :-
!.
map_blocks(P, Blocks, SubBlocks) :-
maplist(map_block(P), Blocks, SubBlocks).
simplify_division(A, 1, A) :-
!.
simplify_division(Value1, Value2, Result) :-
number(Value1),
number(Value2),
map_block(P, +A, +B) :-
!,
Result is Value1 / Value2.
call(P, A, B).
simplify_division(A, B, A / B).
map_block(P, -A, -B) :-
call(P, A, B).
simplify_power(A, 1, A) :-
!.
flatten_blocks([], []).
simplify_power(A, 0.5, sqrt(A)) :-
!.
flatten_blocks([H | TailIn], Out) :-
(
H = +L
->
true
;
H = -LOpp
->
maplist(opp, LOpp, L)
),
append(L, TailOut, Out),
flatten_blocks(TailIn, TailOut).
simplify_power(A, -1, AInv) :-
!,
simplify_division(1, A, AInv).
simplify_power(A, -0.5, InvSqrtA) :-
!,
simplify_division(1, sqrt(A), InvSqrtA).
opp(+ A, - A).
simplify_power(Value1, Value2, Result) :-
number(Value1),
number(Value2),
!,
Result is Value1 ^ Value2.
opp(- A, + A).
simplify_power(A, B, Result) :-
!,
Result = A ^ B.
sign(+ A, 1, A).
simplify_exp(Value, ExpValue) :-
number(Value),
!,
ExpValue is exp(Value).
sign(- A, -1, A).
simplify_exp(log(A), A) :-
!.
simplify_exp(B * log(A), Result) :-
!,
simplify_power(A, B, Result).
additive_block(+ A, [-A]).
simplify_exp(A, exp(A)).
additive_block(- A, [-A]).
additive_block(A + B, [+A, +B]).
simplify_log(Value, LogValue) :-
number(Value),
!,
LogValue is log(Value).