LLS currently only supports variables up to 2 dimensions in size, i.e.,
scalars, vectors, and matrices. Variables
have no value upon creation, but after solving a problem, LLS will populate
all variables in the problem with optimal values.
These values can be accessed using the evaluate function:

# x is a variable with value populated
x_value = evaluate(x)

Constants refer to any numerical scalars, vectors, or matrices of fixed value.
Together with variables, they serve as the building blocks for more complex expressions.

Affine expressions are linear functions of variables plus a constant.
Variables are themselves affine expressions.
The most basic way to build more affine expressions from variables is to use the overloaded
binary arithmetic operators +,-,*,/. The following operations are
supported:

Addition or subtraction of two affine expressions, provided they are the same size or one is scalar.

Addition or subtraction of an affine expression and a constant, provided they are the same size or one is scalar.

Scalar or matrix multiplication between an affine expression and a constant.

Division of an affine expression by a scalar, nonzero constant.

Here are some examples of using binary operators to construct affine expressions:

An affine expression can be evaluated to a numerical value if all variables the affine
expression depends on have been populated with values. For example, the following
code prints the value of the affine expression affine1, assuming both w
and x have been populated with values:

println(evaluate(affine1))

Affine expressions support indexing and slicing using Julia’s native syntax:

In LLS, a linear equality constraint is formed between an affine expression and a constant,
or two affine expressions, using the == operator.
Note that the == operator has been overloaded to no longer return a boolean,
but rather an object representing the linear equality constraint.
A linear equality constraint is only valid if the left hand side and the right hand side
of the == have the same size, or if one is scalar. Here are some examples of
linear equality constraints:

LLS can solve a system of linear equations using the solve! funciton. The
exclamation point after solve is a Julia convention signifying that this
function will have side effects; specifically, it will assign values to
variables after solving. After that, the values of the variables, and any
expressions that depend on them, can be accessed.

The arguments to the solve! function are either one or more comma separated
linear equality constraints or a list of linear equality constraints.
Only systems with unique solutions can
be solved by LLS; see the Solving LCLS section for detailed conditions.
The solve! function will issue an error if these conditions are not satisfied.

In LLS, a sum of squares expression is the sum of squares of the entries of a scalar, vector,
or matrix. The most basic way to create such an expression is to call the sum_squares function
on an affine expression argument.
For example, sum_squares(A*x-b) is the LLS representation of \(\|Ax - b\|_2^2\).
To create other sum of squares expressions, the +, *, and / operators can be used in
conjunction with the following rules:

Two sum of squares expressions can be added

A sum of squares expression can be multiplied or divided by a postive, scalar constant.

A nonnegative scalar constant may be added to a sum of squares expression.

Note that sum of squares expression cannot be subtracted from each other,
or multiplied or divided by a negative number. LLS will issue an error message if
the user attempts any of these.
Here are some examples of building sum of squares expressions:

Similar to an affine expression, a sum of squares expression can be evaluated
to a numerical value if all variables the sum of squares expression depends on
have been populated with values. For example, the following
code prints the value of the sum of squares expression reg_least_squares,
assuming x has been populated with a value:

println(evaluate(reg_least_squares))

Often you’ll find it useful to first initialize a sum of squares expression
to 0 and then add on more sum of squares expressions in a for loop.

The variance of the entries of an affine expression X can be expressed as
sum_squares(mean(X)-X)/(m*n), where m and n are the number of rows
and number of columns of X, respectively. For convenience, the function var
can be used to directly create this sum of squares expression for variance.

The first argument, or objective, of minimize! must be a sum of squares expression.
The remaining arguments are for constraints, and can be zero or more comma separated
linear equality constraints, or a list of linear equality constraints.
The minimize! function
will return the optimal value of the sum of squares expression, while
populating all variables with optimal values.
Here are some usage examples: