Skip to content

Commit e7a8385

Browse files
committed
feat: 三角函数化简逻辑
1 parent c3d7ed7 commit e7a8385

6 files changed

Lines changed: 253 additions & 11 deletions

File tree

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ target_link_libraries(test_complex_arithmetic PRIVATE lmcas LammpCore)
7979
add_executable(test_calculus tests/test_calculus.cpp)
8080
target_link_libraries(test_calculus PRIVATE lmcas LammpCore)
8181

82+
add_executable(test_trig tests/test_trig.cpp)
83+
target_link_libraries(test_trig PRIVATE lmcas LammpCore)
84+
8285
enable_testing()
8386
add_test(NAME TestCAS COMMAND test_cas)
8487
add_test(NAME TestBigIntDebug COMMAND test_bigint_debug)
@@ -89,3 +92,4 @@ add_test(NAME TestArithmetic COMMAND test_arithmetic)
8992
add_test(NAME TestNewFeatures COMMAND test_new_features)
9093
add_test(NAME TestComplexArithmetic COMMAND test_complex_arithmetic)
9194
add_test(NAME TestCalculus COMMAND test_calculus)
95+
add_test(NAME TestTrig COMMAND test_trig)

notes.txt

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
LMCAS缺少:
22
1.线性代数
3-
行列式
4-
矩阵求逆
5-
转置
6-
特征值(特征向量
7-
线性方程组求解(Ax=b 高斯消元法?
3+
行列式 OK
4+
矩阵求逆 OK
5+
转置 OK
6+
特征值(特征向量
7+
线性方程组求解(Ax=b 高斯消元法? OK
88
2.微积分
9-
符号积分(不定积分求解算法RISCH
10-
极限计算(洛必达法则、泰勒级数展开
9+
符号积分(不定积分求解算法RISCH 项式、对数规则、线性性质和部分启发式积分
10+
极限计算(洛必达法则、泰勒级数展开 代入法和 洛必达法则 启发式算法(针对 0/0 型)
11+
微分 实现了基础求导法则
1112
3.高级方程求解
1213
多项式
13-
方程组
14+
方程组 OK
1415
不等式
1516
4.多项式
16-
展开
17-
因式分解
18-
最大公约数
17+
展开 OK
18+
因式分解 OK
19+
最大公约数 OK
1920
5.三角函数
2021
恒等式化简
2122

symbolic.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,4 +531,7 @@ class LAMINA_API SymbolicExpr {
531531
std::shared_ptr<SymbolicExpr> simplify_multiply() const;
532532
std::shared_ptr<SymbolicExpr> simplify_add() const;
533533
std::shared_ptr<SymbolicExpr> simplify_power() const;
534+
std::shared_ptr<SymbolicExpr> simplify_sin() const;
535+
std::shared_ptr<SymbolicExpr> simplify_cos() const;
536+
std::shared_ptr<SymbolicExpr> simplify_tan() const;
534537
};

symbolic_core.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ std::shared_ptr<SymbolicExpr> SymbolicExpr::simplify() const {
121121
return SymbolicExpr::ln(val);
122122
}
123123

124+
case Type::Sin: return simplify_sin();
125+
case Type::Cos: return simplify_cos();
126+
case Type::Tan: return simplify_tan();
127+
124128
default:
125129
// TODO: Recursive simplify for other functions
126130
return std::make_shared<SymbolicExpr>(*this);

symbolic_simplify.cpp

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,177 @@ static std::shared_ptr<SymbolicExpr> expand_power_integer(const std::shared_ptr<
632632
return distribute_multiply(base, rest);
633633
}
634634

635+
// --- Trigonometric Simplifications ---
636+
637+
// Helper: matches c * pi or pi * c or pi
638+
// Returns pair {bool matched, Rational c}
639+
static std::pair<bool, Rational> match_pi_multiple(const std::shared_ptr<SymbolicExpr>& expr) {
640+
if (expr->type == SymbolicExpr::Type::Variable && (expr->identifier == "π" || expr->identifier == "pi")) {
641+
return {true, Rational(1)};
642+
}
643+
644+
if (expr->type == SymbolicExpr::Type::Multiply && expr->operands.size() == 2) {
645+
auto left = expr->operands[0];
646+
auto right = expr->operands[1];
647+
648+
bool left_is_pi = (left->type == SymbolicExpr::Type::Variable && (left->identifier == "π" || left->identifier == "pi"));
649+
bool right_is_pi = (right->type == SymbolicExpr::Type::Variable && (right->identifier == "π" || right->identifier == "pi"));
650+
651+
if (left_is_pi && right->is_number()) return {true, right->convert_rational()};
652+
if (right_is_pi && left->is_number()) return {true, left->convert_rational()};
653+
}
654+
655+
return {false, Rational(0)};
656+
}
657+
658+
std::shared_ptr<SymbolicExpr> SymbolicExpr::simplify_sin() const {
659+
auto arg = operands[0]->simplify();
660+
661+
// sin(0) = 0
662+
if (arg->is_number() && arg->convert_rational() == Rational(0)) return SymbolicExpr::number(0);
663+
664+
// sin(-x) = -sin(x)
665+
if (arg->type == SymbolicExpr::Type::Multiply && arg->operands.size() == 2) {
666+
if (arg->operands[0]->is_number() && arg->operands[0]->convert_rational() == Rational(-1)) {
667+
// sin(-u) -> -sin(u)
668+
return SymbolicExpr::multiply(SymbolicExpr::number(-1), SymbolicExpr::sin(arg->operands[1]))->simplify();
669+
}
670+
}
671+
672+
// Check for k * pi
673+
auto pi_match = match_pi_multiple(arg);
674+
if (pi_match.first) {
675+
Rational k = pi_match.second;
676+
// sin(n * pi) = 0 for integer n
677+
if (k.is_integer()) return SymbolicExpr::number(0);
678+
679+
// sin( (2n+1)/2 * pi ) = (-1)^n
680+
// k = n + 1/2 -> 2k = 2n + 1 an odd integer
681+
Rational two_k = k * Rational(2);
682+
if (two_k.is_integer()) {
683+
BigInt bk = two_k.get_numerator(); // This is 2k
684+
// Check if 2k is odd (not even)
685+
bool is_even = (bk._size == 0) || !(bk._data[0] & 1);
686+
if (!is_even) {
687+
// n = (2k - 1) / 4 ?? No.
688+
// Let 2k = m (odd). k = m/2.
689+
// sin(m/2 * pi).
690+
// m = 1 (pi/2) -> 1
691+
// m = 3 (3pi/2) -> -1
692+
// m = 5 -> 1
693+
// Pattern: (m-1)/2 is even -> 1, odd -> -1 ?
694+
// m=1 -> 0 -> 1. m=3 -> 1 -> -1.
695+
// index = (m-1)/2. if index even -> 1.
696+
697+
// We need m % 4.
698+
// 1 % 4 = 1 -> 1
699+
// 3 % 4 = 3 -> -1
700+
// 5 % 4 = 1 -> 1
701+
// -1 % 4 = -1 -> 3 -> -1
702+
703+
BigInt m = bk;
704+
long long m_ll = 0;
705+
try { m_ll = std::stoll(m.to_string()); } catch(...) { return SymbolicExpr::sin(arg); } // Too big
706+
707+
long long rem = m_ll % 4;
708+
if (rem < 0) rem += 4;
709+
710+
if (rem == 1) return SymbolicExpr::number(1);
711+
if (rem == 3) return SymbolicExpr::number(-1);
712+
}
713+
}
714+
715+
// Standard values: pi/6, pi/4, pi/3
716+
// sin(pi/6) = 1/2
717+
if (k == Rational(1, 6)) return SymbolicExpr::number(Rational(1, 2));
718+
// sin(pi/4) = sqrt(2)/2
719+
if (k == Rational(1, 4)) {
720+
auto sq2 = SymbolicExpr::sqrt(SymbolicExpr::number(2));
721+
return SymbolicExpr::multiply(SymbolicExpr::number(Rational(1, 2)), sq2)->simplify();
722+
}
723+
// sin(pi/3) = sqrt(3)/2
724+
if (k == Rational(1, 3)) {
725+
auto sq3 = SymbolicExpr::sqrt(SymbolicExpr::number(3));
726+
return SymbolicExpr::multiply(SymbolicExpr::number(Rational(1, 2)), sq3)->simplify();
727+
}
728+
}
729+
730+
return SymbolicExpr::sin(arg);
731+
}
732+
733+
std::shared_ptr<SymbolicExpr> SymbolicExpr::simplify_cos() const {
734+
auto arg = operands[0]->simplify();
735+
736+
// cos(0) = 1
737+
if (arg->is_number() && arg->convert_rational() == Rational(0)) return SymbolicExpr::number(1);
738+
739+
// cos(-x) = cos(x)
740+
if (arg->type == SymbolicExpr::Type::Multiply && arg->operands.size() == 2) {
741+
if (arg->operands[0]->is_number() && arg->operands[0]->convert_rational() == Rational(-1)) {
742+
return SymbolicExpr::cos(arg->operands[1])->simplify();
743+
}
744+
}
745+
746+
auto pi_match = match_pi_multiple(arg);
747+
if (pi_match.first) {
748+
Rational k = pi_match.second;
749+
// cos(pi) = -1, cos(2pi) = 1
750+
if (k.is_integer()) {
751+
BigInt num = k.get_numerator();
752+
bool num_even = (num._size == 0 || !(num._data[0] & 1));
753+
if (num_even) return SymbolicExpr::number(1);
754+
else return SymbolicExpr::number(-1);
755+
}
756+
757+
// cos(pi/2 + n*pi) = 0
758+
Rational two_k = k * Rational(2);
759+
if (two_k.is_integer()) {
760+
BigInt bk = two_k.get_numerator();
761+
bool bk_even = (bk._size == 0 || !(bk._data[0] & 1));
762+
if (!bk_even) return SymbolicExpr::number(0);
763+
}
764+
765+
// cos(pi/6) = sqrt(3)/2
766+
if (k == Rational(1, 6)) {
767+
auto sq3 = SymbolicExpr::sqrt(SymbolicExpr::number(3));
768+
return SymbolicExpr::multiply(SymbolicExpr::number(Rational(1, 2)), sq3)->simplify();
769+
}
770+
// cos(pi/4) = sqrt(2)/2
771+
if (k == Rational(1, 4)) {
772+
auto sq2 = SymbolicExpr::sqrt(SymbolicExpr::number(2));
773+
return SymbolicExpr::multiply(SymbolicExpr::number(Rational(1, 2)), sq2)->simplify();
774+
}
775+
// cos(pi/3) = 1/2
776+
if (k == Rational(1, 3)) {
777+
return SymbolicExpr::number(Rational(1, 2));
778+
}
779+
}
780+
781+
return SymbolicExpr::cos(arg);
782+
}
783+
784+
std::shared_ptr<SymbolicExpr> SymbolicExpr::simplify_tan() const {
785+
auto arg = operands[0]->simplify();
786+
787+
if (arg->is_number() && arg->convert_rational() == Rational(0)) return SymbolicExpr::number(0);
788+
789+
if (arg->type == SymbolicExpr::Type::Multiply && arg->operands.size() == 2) {
790+
if (arg->operands[0]->is_number() && arg->operands[0]->convert_rational() == Rational(-1)) {
791+
return SymbolicExpr::multiply(SymbolicExpr::number(-1), SymbolicExpr::tan(arg->operands[1]))->simplify();
792+
}
793+
}
794+
795+
// tan(pi/4) = 1
796+
auto pi_match = match_pi_multiple(arg);
797+
if (pi_match.first) {
798+
Rational k = pi_match.second;
799+
if (k == Rational(1, 4)) return SymbolicExpr::number(1);
800+
if (k.is_integer()) return SymbolicExpr::number(0);
801+
}
802+
803+
return SymbolicExpr::tan(arg);
804+
}
805+
635806
/*
636807
std::shared_ptr<SymbolicExpr> SymbolicExpr::expand() const {
637808
// Moved to symbolic_poly.cpp

tests/test_trig.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "test_common.hpp"
2+
#include "../symbolic.hpp"
3+
4+
int main() {
5+
auto x = SymbolicExpr::variable("x");
6+
auto pi = SymbolicExpr::variable("pi"); // Assuming "pi" or "π" is recognized
7+
8+
TEST_CASE("Trig Basic Values");
9+
{
10+
// sin(0) -> 0
11+
EXPECT_EQ_STR(SymbolicExpr::sin(SymbolicExpr::number(0))->simplify()->to_string(), "0", "sin(0)");
12+
// cos(0) -> 1
13+
EXPECT_EQ_STR(SymbolicExpr::cos(SymbolicExpr::number(0))->simplify()->to_string(), "1", "cos(0)");
14+
// tan(0) -> 0
15+
EXPECT_EQ_STR(SymbolicExpr::tan(SymbolicExpr::number(0))->simplify()->to_string(), "0", "tan(0)");
16+
}
17+
18+
TEST_CASE("Trig Parity");
19+
{
20+
// sin(-x) -> -1*sin(x) or -sin(x) depending on output format
21+
// The implementation specifically returns -1 * sin(x)
22+
auto sin_neg = SymbolicExpr::sin(SymbolicExpr::multiply(SymbolicExpr::number(-1), x))->simplify();
23+
// to_string for -1*sin(x) typically is "-1*sin(x)" or "-sin(x)"?
24+
// Let's check logic: SymbolicExpr::multiply(SymbolicExpr::number(-1), ... )
25+
// The toString might be "-1*sin(x)"
26+
// Let's try to verify if it contains sin(x) and starts with -
27+
std::string s = sin_neg->to_string();
28+
bool ok = (s == "-1*sin(x)" || s == "-sin(x)" || s == "-1*(sin(x))");
29+
if(!ok) std::cout << " Got: " << s << std::endl;
30+
EXPECT_TRUE(ok, "sin(-x) -> -sin(x)");
31+
32+
// cos(-x) -> cos(x)
33+
auto cos_neg = SymbolicExpr::cos(SymbolicExpr::multiply(SymbolicExpr::number(-1), x))->simplify();
34+
EXPECT_EQ_STR(cos_neg->to_string(), "cos(x)", "cos(-x)");
35+
}
36+
37+
TEST_CASE("Trig Pi Values");
38+
{
39+
// sin(pi) -> 0
40+
EXPECT_EQ_STR(SymbolicExpr::sin(pi)->simplify()->to_string(), "0", "sin(pi)");
41+
// cos(pi) -> -1
42+
EXPECT_EQ_STR(SymbolicExpr::cos(pi)->simplify()->to_string(), "-1", "cos(pi)");
43+
44+
// sin(pi/6) -> 1/2
45+
// pi/6 is pi * (1/6)
46+
auto pi_6 = SymbolicExpr::multiply(SymbolicExpr::number(Rational(1,6)), pi);
47+
EXPECT_EQ_STR(SymbolicExpr::sin(pi_6)->simplify()->to_string(), "1/2", "sin(pi/6)");
48+
49+
// cos(pi/3) -> 1/2
50+
auto pi_3 = SymbolicExpr::multiply(SymbolicExpr::number(Rational(1,3)), pi);
51+
EXPECT_EQ_STR(SymbolicExpr::cos(pi_3)->simplify()->to_string(), "1/2", "cos(pi/3)");
52+
53+
// tan(pi/4) -> 1
54+
auto pi_4 = SymbolicExpr::multiply(SymbolicExpr::number(Rational(1,4)), pi);
55+
EXPECT_EQ_STR(SymbolicExpr::tan(pi_4)->simplify()->to_string(), "1", "tan(pi/4)");
56+
}
57+
58+
return 0;
59+
}

0 commit comments

Comments
 (0)