title: "Shape Constraints with BsplineQuantReg"
author: "Alexandre Abbes"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Shape Constraints with BsplineQuantReg}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, fig.width = 7, fig.height = 5)
library(BsplineQuantReg)
```

## Introduction

This vignette covers shape constraints in B-spline quantile regression using the Karlin-Studden SOCP formulation. Shape constraints allow you to incorporate prior knowledge about the function's behavior (monotonicity, convexity, etc.) into the estimation process.

## Available Demos

The package includes several demos that illustrate shape constraints:

```{r demos}
# Example of available demos
dev.new()
demo(package = "BsplineQuantReg",temperature2)

# Run specific demos:
# demo("monotonicity")      - Increasing/decreasing constraints
# demo("convexity")         - Convexity/concavity constraints  
# demo("der3")              - Third derivative constraints
# demo("comprehensive")     - All constraint types
# demo("temperature")       - Real-world example with temperature data
```

## Uniform Constraints

### Monotonicity Constraints

Monotonicity constraints force the fitted function to be non-decreasing (`monot = 1`) or non-increasing (`monot = -1`).

```{r monotonicity}
# Generate data
set.seed(42)
n <- 100
x <- seq(0, 1, length.out = n)
y <- 2*x + 0.5*sin(4*pi*x) + 0.1*rnorm(n)
knots <- quantile(x, probs = seq(0, 1, length.out = 10))

# Unconstrained
fit_uncon <- quantile_spline(x, y, knots, tau = 0.5)

# Increasing constraint
fit_inc <- quantile_spline(x, y, knots, tau = 0.5, monot = 1)

# Decreasing constraint
fit_dec <- quantile_spline(x, y, knots, tau = 0.5, monot = -1)

# Compare
x_eval <- seq(0, 1, length.out = 200)
plot(x, y, pch = 16, cex = 0.5, col = "gray", main = "Monotonicity Constraints")
lines(x_eval, spline_eval(fit_uncon, x_eval), col = "red", lwd = 2, lty = 2)
lines(x_eval, spline_eval(fit_inc, x_eval), col = "blue", lwd = 2)
lines(x_eval, spline_eval(fit_dec, x_eval), col = "green", lwd = 2)
legend("topleft", legend = c("Unconstrained", "Increasing", "Decreasing"),
       col = c("red", "blue", "green"), lty = c(2, 1, 1), lwd = 2)
```

### Convexity Constraints

Convexity constraints force the second derivative to be non-negative (`convcons = 1`) or non-positive (`convcons = -1`).

```{r convexity}
# Convex function
y_conv <- (x - 0.5)^2 + 0.05*rnorm(n)

fit_convex <- quantile_spline(x, y_conv, knots, tau = 0.5, convcons = 1)

plot(x, y_conv, pch = 16, cex = 0.5, col = "gray", main = "Convexity Constraint")
lines(x_eval, spline_eval(fit_convex, x_eval), col = "blue", lwd = 2)
```

### Third Derivative Constraints

Third derivative constraints control the curvature of the second derivative. The control of its sign allows enforcing smoothness of the curvature.

```{r der3}
# Cubic function with varying third derivative
y_cubic <- x^3 - 1.5*x^2 + 0.5*x + 0.05*rnorm(n)

fit_der3_pos <- quantile_spline(x, y_cubic, knots, tau = 0.5, der3cons = 1)

plot(x, y_cubic, pch = 16, cex = 0.5, col = "gray", main = "Third Derivative Constraint")
lines(x_eval, spline_eval(fit_der3_pos, x_eval), col = "blue", lwd = 2)
```

### Constraints by Degree

| Degree | Monotonicity | Convexity | Third Derivative |
|--------|--------------|-----------|------------------|
| 1 (Linear) | +(linear) | X | X |
| 2 (Quadratic) | + (Karlin) | +(constant) | X|
| 3 (Cubic) | +(Karlin) | +(Karlin) | +(linear (constant)) |
| 4 (Quartic) | +(Karlin) | +(Karlin) | +(linear) |
'Karlin' means in fact 'quadratic' inequality.

## Partial Constraints

Partial constraints apply monotonicity or convexity only on specific intervals, not the entire domain.

### Partial Monotonicity

```{r partial-monot}
# Increasing only on first half
monot_partial <- c(rep(1, 5), rep(0, 4))  # 5 intervals increasing, 4 unconstrained

fit_partial <- quantile_spline(x, y, knots, tau = 0.5, monot = monot_partial)

plot(x, y, pch = 16, cex = 0.5, col = "gray", main = "Partial Monotonicity")
lines(x_eval, spline_eval(fit_partial, x_eval), col = "blue", lwd = 2)
abline(v = knots[6], col = "red", lty = 2)  # Boundary of constrained region
```

### Partial Convexity

```{r partial-convex}
# Convex on right half only
conv_partial <- rep(0, length(knots))
for (i in 1:length(knots)) {
  if (knots[i] > 0.5) conv_partial[i] <- 1
}

fit_conv_partial <- quantile_spline(x, y, knots, tau = 0.5, convcons = conv_partial)
plot(x, y, pch = 16, cex = 0.5, col = "gray", main = "Partial Convexity")
lines(x_eval, spline_eval(fit_conv_partial, x_eval), col = "blue", lwd = 2)
abline(v = knots[6], col = "red", lty = 2)  # Boundary of constrained region
```
## Mixed Constraints

Multiple shape constraints can be applied simultaneously.

```{r}
# Increasing AND convex
fit_both <- quantile_spline(x, y, knots, tau = 0.5, 
                            monot = 1, convcons = 1)

plot(x, y, pch = 16, cex = 0.5, col = "gray", 
     main = "Mixed Constraints: Increasing + Convex")
lines(x_eval, spline_eval(fit_both, x_eval), col = "blue", lwd = 2)
```

## Constraints with Multiple Knots

When using knot multiplicities, constraints need to be adapted to the reduced regularity. 

```{r multiple-knots}
# Create knots with multiplicity at a point
sn_mult <- c(0, 0, 0, 0, 0.3, 0.5, 0.5,0.5,0.5, 0.7, 1, 1, 1, 1)
basis_mult <- Bspline_base(sn_mult, degree = 3)
knots_mult <- basis_mult$knot

# Fit with constraints
fit_mult <- quantile_spline(x, y, knots_mult, tau = 0.5, monot = 1)
plot(x, y, pch = 16, cex = 0.5, col = "gray", 
     main = "Constraints with Multiple knots")
lines(x_eval, spline_eval(fit_mult, x_eval), col = "blue", lwd = 2)
```

## Visualizing Constraint Effects

```{r visualize-constraints}
par(mfrow = c(2, 2))

# No constraints
plot(x, y, pch = 16, cex = 0.4, col = "gray", main = "Unconstrained")
lines(x_eval, spline_eval(fit_uncon, x_eval), col = "red", lwd = 2)

# Monotonicity
plot(x, y, pch = 16, cex = 0.4, col = "gray", main = "Monotonicity")
lines(x_eval, spline_eval(fit_inc, x_eval), col = "blue", lwd = 2)

# Convexity
plot(x, y, pch = 16, cex = 0.4, col = "gray", main = "Convexity")
lines(x_eval, spline_eval(fit_convex, x_eval), col = "blue", lwd = 2)

# Mixed
plot(x, y, pch = 16, cex = 0.4, col = "gray", main = "Mixed")
lines(x_eval, spline_eval(fit_both, x_eval), col = "blue", lwd = 2)

par(mfrow = c(1, 1))
```
## Karlin-Studden SOCP Formulation

The shape constraints are implemented using the **Karlin-Studden characterization** of non-negative polynomials of degree 2 or 3. 

Depending on the degree of the spline (3 or 4), these constraints apply to the first or second derivative:

| Spline Degree | Constraint Type | Polynomial Degree | Applied to |
|---------------|-----------------|-------------------|------------|
| 3 (Cubic) | Monotonicity | Quadratic (degree 2) | First derivative |
| 3 (Cubic) | Convexity | Linear (degree 1) | Second derivative |
| 4 (Quartic) | Monotonicity | Cubic (degree 3) | First derivative |
| 4 (Quartic) | Convexity | Quadratic (degree 2) | Second derivative |

**Other constraints** (third derivative, constraints at knots) are **linear** and are added as linear inequalities.

The complete set of constraints forms a **Second-Order Cone Program (SOCP)**. This SOCP formulation ensures **exact shape constraints**, unlike approximations that only enforce constraints at knots or at a finite set of points.

The particular structure of the problem allows the use of the **CVXR** package, which handles this type of mixed quadratic/linear conic problems efficiently through its DCP (Disciplined Convex Programming) framework and supports multiple solvers (CLARABEL, OSQP, ECOS, SCS).

## Practical Considerations

### When to Use Shape Constraints

| Constraint | When to Use |
|------------|-------------|
| Monotonicity | Demand curves, growth curves, dose-response |
| Convexity | Cost functions, risk aversion, production functions |
| Third derivative | Smoothing curvature, spline regularity |
| Partial | Data with known local behavior |

### Constraint Strength

Constraints can be too strong if:
- The data clearly violates the assumed shape
- Too few knots leading to over-smoothing
- Multiple constraints conflicting

### Choosing the Degree

| Degree | Smoothness | Flexibility |
|--------|------------|-------------|
| 1 | Piecewise linear | Low |
| 2 | C¹ | Medium |
| 3 | C² | High |
| 4 | C³ | Very high |

## Summary

The package provides:
- **Uniform constraints**: Apply globally
- **Partial constraints**: Apply on specific intervals
- **Mixed constraints**: Multiple constraints simultaneously
- **SOCP formulation**: Exact (not approximated) constraints
- **multiple knots**: for lower regularity handling at knots (shocks)

For more examples, see the demos:

```{r}
demo(package = "BsplineQuantReg")
```
```

## Additional Sections You Could Add

| Section | Content |
|---------|---------|
| **Verification** | How to check if constraints are satisfied |
| **Comparison with cobs** | Differences in constraint implementation |
| **Real Examples** | Temperature, economics, biology applications |
| **Troubleshooting** | Common issues and solutions |
