<!-- https://mathlive.io/compute-engine/guides/simplify/ -->

# Simplify

<Intro>
A complicated mathematical expression can often be transformed into a form that
is easier to understand.
</Intro>

The `expr.simplify()` function tries expanding, factoring and applying many
other transformations to find a simpler form of a symbolic expression.

Before the transformation rules are applied, the expression is put into a
canonical form.

When a function is simplified, its arguments are simplified as well, unless the
argument is "held". Which arguments are held is specified by the `hold` property
of the function definition. In addition, any argument wrapped with a `Hold`
function will be held, that is, not simplified. Conversely, a held argument
wrapped with a `ReleaseHold` function will not be held, and it will be
simplified.

## Parse + Simplify Free Function

For the common "parse then simplify" flow, use the `simplify()` free function:

```live
// import { simplify } from '@cortex-js/compute-engine';
console.log(simplify("x+x+1"));
```

The `simplify()` free function accepts either a LaTeX string or a
`Expression`. It uses a shared `ComputeEngine` instance created on
first call.

For trigonometric simplification, use `expr.simplify({ strategy: 'fu' })`
on a parsed expression.

## Defining "Simpler"

An expression may be represented by several equivalent forms.

For example \\( (x + 4)(x-5) \\) and \\(x^2 -x -20\\) represent the same
expression.

Determining which is "the simplest" depends on how the complexity is measured.

By default, the complexity of an expression is measured by counting the number
of operations in the expression, and giving an increasing cost to:

- integers with fewer digits
- integers with more digits
- other numeric values
- add, multiply, divide
- subtract and negate
- square root and root
- exp
- power and log
- trigonometric function
- inverse trigonometric function
- hyperbolic functions
- inverse hyperbolic functions
- other functions

**To influence how the complexity of an expression is measured**, set the
`costFunction` property of the compute engine to a function assigning a cost to
an expression.

The cost is applied strictly: a rewrite is kept only if the result does not
cost more than what it replaces. `simplify()` therefore never returns
something more complicated than what you gave it.

Some rewrites are mathematically preferred yet score worse — combining powers
takes \\(2 \cdot 2^x\\) from cost 4 to cost 5. Those do not rely on any
numeric allowance; they are tagged `purpose: 'transform'`, which exempts them
from the cost gate entirely. If you write a rule that should apply regardless
of cost, tag it the same way — see
[Patterns and Rules](/compute-engine/guides/patterns-and-rules/).

## Numeric Simplifications

The `expr.simplify()` function will apply some numeric simplifications, such as
combining small integer and rational values, simplifying division by 1, addition
or subtraction of 0, etc...

It avoids making any simplification that could result in a loss of precision.

For example, \\( 10^{300} + 1\\) cannot be simplified without losing the least
significant digit, so `expr.simplify()` will return the expression unmodified.

## Polynomial Simplifications

For univariate rational expressions (fractions with polynomials in a single
variable), `simplify()` automatically cancels common factors in the numerator
and denominator.

For example:
- $ \frac{x^2 - 1}{x - 1} $ simplifies to $ x + 1 $
- $ \frac{x^3 - x}{x^2 - 1} $ simplifies to $ x $
- $ \frac{x + 1}{x^2 + 3x + 2} $ simplifies to $ \frac{1}{x + 2} $

For more control over polynomial operations, or for multivariate expressions,
use the explicit `Cancel`, `PolynomialGCD`, `PolynomialQuotient`, and
`PolynomialRemainder` functions.

To check if an expression is a polynomial and extract its coefficients, use
`polynomialCoefficients()`:

```javascript
const coeffs = ce.parse('x^3 + 2x + 1').polynomialCoefficients('x');
// ➔ [1, 0, 2, 1]  (descending order: x³, x², x, constant)

// Check if an expression is a polynomial
ce.parse('sin(x)').polynomialCoefficients('x');
// ➔ undefined (not a polynomial)
```

To find the roots of a polynomial, use `polynomialRoots()`:

```javascript
ce.parse('x^2 - 5x + 6').polynomialRoots('x');
// ➔ [2, 3]

ce.parse('x^3 - 6x^2 + 11x - 6').polynomialRoots('x');
// ➔ [1, 2, 3]
```

### Automatic Factoring in Square Roots

When simplifying square root expressions, the engine automatically attempts to
factor the argument to enable further simplification. This handles cases where
the argument is an expanded perfect square.

For example:
- $ \sqrt{x^2 + 2x + 1} $ simplifies to $ |x + 1| $ (recognizes perfect square trinomial)
- $ \sqrt{4x^2 + 12x + 9} $ simplifies to $ |2x + 3| $
- $ \sqrt{a^2 + 2ab + b^2} $ simplifies to $ |a + b| $
- $ \sqrt{a^2 - 2ab + b^2} $ simplifies to $ |a - b| $

```javascript
parse('\\sqrt{x^2 + 2x + 1}').simplify().latex;
// ➔ "|x+1|"

parse('\\sqrt{4x^2 + 12x + 9}').simplify().latex;
// ➔ "|2x+3|"
```

This automatic factoring only occurs within `sqrt()` expressions during
simplification. For explicit factoring of polynomials, use the `Factor` function.

### Explicit Factoring and Expansion

For more control over polynomial form, use `Factor` and `Expand`:

```javascript
// Factor a polynomial
factor('x^2 + 5x + 6').latex;
// ➔ "(x+2)(x+3)"

factor('x^2 - 4').latex;
// ➔ "(x-2)(x+2)"

// Expand a product
expand('(x+1)(x+2)').latex;
// ➔ "x^2+3x+2"
```

The `Factor` function currently supports:
- **Perfect square trinomials**: $ a^2 \pm 2ab + b^2 \to (a \pm b)^2 $
- **Difference of squares**: $ a^2 - b^2 \to (a-b)(a+b) $
- **Quadratics with rational roots**: $ x^2 + bx + c $ when roots are rational
- **Degree 3+ with rational roots**: Uses the Rational Root Theorem (e.g., $ x^3 - 6x^2 + 11x - 6 \to (x-1)(x-2)(x-3) $)
- **Content extraction**: Extracts GCD of integer coefficients first (e.g., $ 6x^2 + 12x + 6 \to 6(x+1)^2 $)

### Partial Fraction Decomposition

For decomposing rational expressions into simpler fractions, use `PartialFraction`:

```javascript
evaluate('\\operatorname{PartialFraction}(\\frac{1}{(x+1)(x+2)}, x)').latex;
// ➔ "\\frac{1}{x+1} - \\frac{1}{x+2}"

evaluate('\\operatorname{PartialFraction}(\\frac{3x+5}{(x+1)^2}, x)').latex;
// ➔ "\\frac{3}{x+1} + \\frac{2}{(x+1)^2}"
```

This supports distinct and repeated linear factors, irreducible quadratic factors, and improper fractions (polynomial division is performed first). `Apart` is available as an alias for `PartialFraction`.

When a `Divide` expression has a denominator already in factored form (a product or power), `simplify()` automatically applies partial fraction decomposition if the result is simpler. This means `\frac{1}{(x+1)(x+2)}` is automatically simplified without needing to call `PartialFraction` explicitly.

<ReadMore path="/compute-engine/reference/arithmetic/" > Read more about
<strong>Polynomial Arithmetic</strong> <Icon name="chevron-right-bold" /></ReadMore>

## Using Assumptions

Assumptions are additional information available about some symbols, for example
\\( x > 0 \\) or \\(n \in \N\\).

Some transformations are only applicable if some assumptions can be verified.

For example, if no assumptions about \\(x\\) is available the expression \\(\sqrt\{x^2\}\\) cannot be simplified. However, if an assumption that \\(x \geq 0\\) is available, then the expression can be simplified to \\(x\\).

### Sign-Dependent Simplifications

When assumptions about the sign of a variable are available, several
simplifications become possible:

```javascript
// Without assumptions, sqrt(x^2) = |x|
ce.parse('\\sqrt{x^2}').simplify().latex;
// ➔ "|x|"

// With positive assumption
ce.assume(ce.parse('x > 0'));
ce.parse('\\sqrt{x^2}').simplify().latex;
// ➔ "x"

ce.parse('|x|').simplify().latex;
// ➔ "x"
```

```javascript
// With negative assumption
ce.assume(ce.parse('y < 0'));
ce.parse('\\sqrt{y^2}').simplify().latex;
// ➔ "-y"

ce.parse('|y|').simplify().latex;
// ➔ "-y"
```

<ReadMore path="/compute-engine/guides/assumptions/" > Read more about
<strong>Assumptions</strong> <Icon name="chevron-right-bold" /></ReadMore>

## Simplify and Assigned Values

The `expr.simplify()` **method** is _value-blind_: it never substitutes the
value you assigned to a symbol. Sign- and parity-dependent rewrites use a
symbol's **declared type** and **in-scope assumptions**, but never its assigned
value.

This is why an assumption and an assignment behave differently, even when the
value satisfies the assumption:

```javascript
// An assumption is a fact simplify() may use:
ce.assume(ce.parse('w > 0'));
ce.parse('|w|').simplify().latex;
// ➔ "w"    (w > 0 licenses dropping the absolute value)

// An assigned value is NOT used by simplify():
ce.forget('w');
ce.assign('w', 5);
ce.parse('|w|').simplify().latex;
// ➔ "|w|"  (an integer is not provably non-negative, so |w| stays symbolic)
```

Treating an assigned value as a fact would be a silent trap: the sign that held
when you simplified would be baked into the saved expression and give a wrong
answer after the value changes (assign `w := -3` and evaluate the baked-in `w`,
and you would get `-3` for `|w|`). The value-blind method avoids this — an
assigned value is treated as if the symbol were merely _declared_ with that
value's type.

### The method vs. the `Simplify` operator

The `.simplify()` method applies rewrite rules only — it does not run an
operator's evaluation. So a result that comes from a handler rather than a rule
(a determinant, a derivative, an integral) is returned unchanged by the method:

```javascript
const m = ['List', ['List', 'a', 'b'], ['List', 'c', 'd']];
ce.box(['Determinant', m]).simplify().operator;
// ➔ "Determinant"   (unchanged — no rewrite rule for it)
```

To compute _then_ simplify, evaluate first — the reliable recipe is
`evaluate().simplify()`:

```javascript
ce.box(['Determinant', m]).evaluate().simplify().latex;
// ➔ "ad-bc"
```

The `Simplify` **operator** does exactly this for you: `Simplify(expr)`
evaluates its argument first, then simplifies the result. Unlike the method, it
therefore substitutes assigned symbol values and runs evaluation handlers:

```javascript
ce.assign('x', 5);
ce.box(['Simplify', ['Add', ['Power', 'x', 2], 'x']]).evaluate().print();
// ➔ 30   (the operator evaluates x → 5, then simplifies)
```

## Nested Root Simplification

Nested roots are automatically simplified to a single root with the product
of the indices:

| Expression | Simplified |
|:-----------|:-----------|
| `sqrt(sqrt(x))` | `root(4)(x)` |
| `root(sqrt(x), n)` | `root(2n)(x)` |
| `sqrt(root(x, n))` | `root(2n)(x)` |
| `root(root(x, m), n)` | `root(m·n)(x)` |

```javascript
box(['Sqrt', ['Sqrt', 'x']]).simplify().latex;
// ➔ "\\sqrt[4]{x}"

box(['Root', ['Root', 'x', 3], 2]).simplify().latex;
// ➔ "\\sqrt[6]{x}"

box(['Sqrt', ['Root', 'x', 3]]).simplify().latex;
// ➔ "\\sqrt[6]{x}"
```

## Trigonometric Simplification

Trigonometric expressions can be simplified using the Fu algorithm, a systematic
approach based on the paper "Automated and readable simplification of
trigonometric expressions" by Fu, Zhong, and Zeng (2006).

The Fu algorithm applies a series of transformation rules (TR) organized into
rule lists (RL) that are optimized for different types of expressions:

- **RL1**: For expressions containing `tan` and `cot`
- **RL2**: For expressions containing `sin` and `cos`

Each rule applies specific identities:
- **Reciprocal forms**: `sec(x)` → `1/cos(x)`, `csc(x)` → `1/sin(x)`
- **Ratio forms**: `tan(x)` → `sin(x)/cos(x)`
- **Pythagorean identities**: `sin²(x) + cos²(x)` → `1`
- **Double angle**: `2sin(x)cos(x)` → `sin(2x)`
- **Product-to-sum**: `sin(x)cos(y)` → `½[sin(x+y) + sin(x-y)]`
- **Sum-to-product**: `sin(x) + sin(y)` → `2sin((x+y)/2)cos((x-y)/2)`
- **Morrie's law**: `cos(x)cos(2x)cos(4x)` → `sin(8x)/(8sin(x))`

### Using the Fu Algorithm

There are two ways to apply trigonometric simplification:

**Option 1: Strategy option with `simplify()`**

```javascript
const expr = parse("\\sin^2(x) + \\cos^2(x)");
const simplified = expr.simplify({ strategy: 'fu' });
// Returns: 1
```

**Option 2: Dedicated `trigSimplify()` method**

```javascript
const expr = parse("2\\sin(x)\\cos(x)");
const simplified = expr.trigSimplify();
// Returns: sin(2x)
```

### Examples

| Expression | Simplified |
|:-----------|:-----------|
| `sin²(x) + cos²(x)` | `1` |
| `tan(x)·cot(x)` | `1` |
| `2sin(x)cos(x)` | `sin(2x)` |
| `sin(x)⁴ - cos(x)⁴` | `-cos(2x)` |
| `cos(x)cos(2x)cos(4x)` | `sin(8x)/(8sin(x))` |

<ReadMore path="/compute-engine/reference/trigonometry/" > Read more about
<strong>Trigonometric Functions</strong> <Icon name="chevron-right-bold" /></ReadMore>

## Step-by-Step Explanations

The `expr.explain()` function returns the same result as `expr.simplify()`,
together with the chain of steps that produced it — the textbook presentation
*expression → step (with a reason) → … → result*.

```javascript
const expr = ce.parse("\\frac{x^2-1}{x-1}");
const explanation = expr.explain();

console.log(explanation.initial.latex);
// ➔ "\frac{x^2-1}{x-1}"

for (const step of explanation.steps)
  console.log(step.value.latex, "—", step.description);
// ➔ "x+1 — Cancel the common factors"

console.log(explanation.result.latex);
// ➔ "x+1"
```

An `Explanation` has four properties:

- `operation`: the operation that was traced (`'simplify'`, `'solve'`,
  `'D'` or `'Integrate'`)
- `initial`: the **canonical form** of the expression `explain()` was called
  on — the chain's step 0. Canonicalization (e.g. `x - 1` becoming
  `Add(x, -1)`, or `2 + 3` folding to `5`) happens before the first step is
  recorded and is not traced.
- `steps`: the chain of steps. Each step has the expression `value` after the
  step was applied, a stable machine `id` identifying the rule that fired,
  and a default English `description`.
- `result`: the same value `simplify()` returns

**Explaining is free of side effects and does not change results:** the
explanation runs the same code as `simplify()`, so `explanation.result` is
always the same value `expr.simplify()` returns.

### Explaining an Equation Solution

`expr.explain('solve')` traces `expr.solve()` for a univariate equation (an
expression `f` is read as the equation `f = 0`). The unknown is inferred, or
passed explicitly with `options.variable`. Step values are **equations** —
the state of the equation after each phase — so the chain reads like
textbook working, including candidate roots and the rejection of extraneous
candidates:

```javascript
const expr = ce.parse("\\sqrt{x+1} = x - 1");
for (const step of expr.explain('solve').steps)
  console.log(step.value.latex, "—", step.description);
// ➔ -x+\sqrt{x+1}+1=0 — Move all terms to one side
// ➔ -x^2+3x=0        — Square both sides to eliminate the radical
// ➔ [x=0, x=3]       — Apply the quadratic formula
// ➔ x=0              — Check each candidate in the original equation; reject the extraneous ones
// ➔ x=3              — The solutions
```

`explanation.result` is a `List` of the same roots `solve()` returns. A
case-split (an absolute value, a zero product) is rendered as one step whose
value lists the sub-equations.

#### Systems

A `List` (or `And`) of equations is traced through the same solvers
`solve()` runs. Pass the unknowns as an array. Step values are the whole
system — the state after each elimination and back-substitution phase:

```javascript
const system = ce.box(["List", ce.parse("x+y=5"), ce.parse("x-y=1")]);
for (const step of system.explain("solve", { variable: ["x", "y"] }).steps)
  console.log(step.value.latex, "—", step.description);
// ➔ \begin{cases}x+y=5\\-2y=-4\end{cases} — Eliminate a variable from the remaining equations
// ➔ \bigl\lbrack y=2\bigr\rbrack — Substitute the known value back to solve for the next variable
// ➔ \begin{cases}x=3\\y=2\end{cases} — Substitute the known value back to solve for the next variable
```

Nonlinear 2×2 systems show the product–sum or solve-and-substitute
strategy. A system of **linear inequalities** in two variables is traced
through constraint normalization (each inequality rewritten with zero on
the right-hand side), the intersection points of the boundary lines, and
the feasible corner points — the same vertices `solve()` returns:

```javascript
const region = ce.box([
  "List",
  ce.parse("x+y\\le 4"),
  ce.parse("x\\ge 0"),
  ce.parse("y\\ge 0"),
]);
region.explain("solve", { variable: ["x", "y"] });
// steps: normalize each inequality → boundary intersections → feasible vertices
// result: [{x=0, y=0}, {x=4, y=0}, {x=0, y=4}]
```

A **mixed** system of equations and inequalities shows the elimination
steps, then each candidate solution substituted into the constraints and
accepted or rejected. When a system genuinely cannot be solved,
`explain('solve')` throws a precise error.

### Explaining a Derivative

`expr.explain('D')` traces the differentiation of the expression. Steps are
whole-expression states in traversal order — each textbook rule first
appears with its unresolved sub-derivatives as inert `D(…)` terms, which
then resolve one by one:

```javascript
const expr = ce.parse("x \\sin x");
for (const step of expr.explain('D').steps)
  console.log(step.value.latex, "—", step.description);
// ➔ x\,\frac{\mathrm{d}}{\mathrm{d}x}\sin x+\sin x — Apply the product rule: (u·v)′ = u′·v + u·v′
// ➔ x\cos x+\sin x — Differentiate using a known derivative
```

The variable of differentiation is inferred when the expression has exactly
one unknown; otherwise pass it with `options.variable`. The `result`
matches evaluating `D(expr, variable)`. When the unfolded textbook form
differs from the engine's simplified result, the chain closes with a
*Simplify the result* step.

Higher-order and mixed partial derivatives are traced stage by stage: pass
`options.order` for the $n$-th derivative, or call `explain('D')` on a `D`
expression itself (including mixed partials such as `D(f, x, y)`). Each
stage replays the rule applications inside the remaining derivative
operators, folds to the simplified derivative, then differentiates again:

```javascript
ce.parse("x \\sin x").explain("D", { variable: "x", order: 2 });
```

### Explaining an Integral

`expr.explain('Integrate')` traces symbolic integration through the rule
chain of the opt-in **Integration Rules** library (the Rubi corpus) — it
requires `loadIntegrationRules(ce)` to have been called first, and throws a
precise error otherwise. Steps are whole-expression states: sums split term
by term, constant factors move out, and each rule application rewrites one
unevaluated integral until none remain:

```javascript
import { loadIntegrationRules } from "@cortex-js/compute-engine/integration-rules";
loadIntegrationRules(ce);

const expr = ce.parse("\\int (3x^2+2x+1)\\,dx");
for (const step of expr.explain("Integrate").steps)
  console.log(step.value.latex, "—", step.description);
// ➔ \int\!1\,\mathrm{d}x+\int\!2x\,\mathrm{d}x+\int\!3x^2\,\mathrm{d}x — Integrate term by term: ∫(u+v) dx = ∫u dx + ∫v dx
// ➔ x+\int\!2x\,\mathrm{d}x+\int\!3x^2\,\mathrm{d}x — The integral of a constant: ∫c dx = c·x
// ➔ x+2\int\!x\,\mathrm{d}x+\int\!3x^2\,\mathrm{d}x — Move the constant factor out of the integral
// ➔ x^2+x+\int\!3x^2\,\mathrm{d}x — The integral of the variable: ∫x dx = x²/2
// ➔ x^2+x+3\int\!x^2\,\mathrm{d}x — Move the constant factor out of the integral
// ➔ x^3+x^2+x — Apply integration rule 1.1.1.1#15 (Rubi)
```

Steps produced by a corpus rule carry a stable `rubi:…` id naming the rule.
The `result` matches evaluating the integral. When the rules cannot close
the integral a precise error is thrown rather than a partial explanation.

A **definite** integral is presented via the Fundamental Theorem of
Calculus: the chain reframes to finding the antiderivative $F$, then closes
with the bracket $F\big|_a^b$, the bounds substituted (unevaluated), and
the value:

```javascript
const expr = ce.parse("\\int_0^1 x^2\\,dx");
for (const step of expr.explain("Integrate").steps)
  console.log(step.value.latex, "—", step.description);
// ➔ \int\!x^2\,\mathrm{d}x — Find the antiderivative of the integrand
// ➔ \frac{x^3}{3} — Apply integration rule 1.1.1.1#15 (Rubi)
// ➔ \left.\left(\frac{x^3}{3}\right)\right|_{0}^{1} — Apply the Fundamental Theorem of Calculus: ∫ᵃᵇ f dx = F(b) − F(a)
// ➔ \frac{1^3}{3}-\frac{0^3}{3} — Evaluate the antiderivative at the bounds
// ➔ \frac{1}{3} — Simplify the result
```

Symbolic bounds work (`\int_0^a x\,dx` → `\frac{a^2}{2}`); an improper
integral (an infinite bound) skips the substitution step — the bracket is a
limit, not a substitution — and closes directly with the value.

<ReadMore path="/compute-engine/guides/integration-rules/" > Read more about
the <strong>Integration Rules</strong> library <Icon name="chevron-right-bold" /></ReadMore>

### Step Ids and Localization

The `id` of a step is a frozen, machine-readable identifier (e.g.
`'cancel common polynomial factors'`, `'x^n * x^m -> x^{n+m}'`,
`'fungrim:0010f3'`). The engine ships English descriptions only; to localize
or customize the copy, key your strings off `step.id` and use the
`description` as a fallback.

### Verbosity

By default the step chain is curated: work done while simplifying
sub-expressions in place is surfaced as its own labeled steps (e.g.
$\tan x\cot x \to 1$ inside a larger sum), consecutive applications of the
same rule are coalesced into a single step, and internal bookkeeping
markers are filtered out. Pass `verbosity: 'all'` to get the raw, uncurated
trace — useful for debugging and for rule authors:

```javascript
expr.explain('simplify', { verbosity: 'all' });
```

The options of `simplify()` (`rules`, `costFunction`, `strategy`) are also
accepted and honored, so `expr.explain('simplify', options).result` matches
`expr.simplify(options)` for any options.
