Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,21 @@ jobs:
matrix:
version:
- '1.10'
- '1'
- 'nightly'
os:
- ubuntu-latest
- macOS-latest
- windows-latest
arch:
- x64
- x86
exclude:
- os: macOS-latest
arch: x86
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
- uses: actions/cache@v3
env:
cache-name: cache-artifacts
with:
Expand All @@ -42,6 +39,6 @@ jobs:
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
- uses: codecov/codecov-action@v6
with:
file: lcov.info
45 changes: 45 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# CLAUDE.md

This file tracks decisions and context discovered while working on this package.
(This instruction itself: always write discoveries and decisions into CLAUDE.md.)

## Dependency Update (2026-04-01)

### Versions updated in Project.toml [compat]

| Package | Old compat | New compat | Latest version |
|----------------------|-------------|------------|----------------|
| IntervalArithmetic | 0.22.12 | 1 | 1.0.4 |
| IntervalBoxes | 0.2 | 0.3 | 0.3.0 |
| IntervalContractors | 0.5 | 0.6 | 0.6.0 |
| ReversePropagation | 0.3 | 0.4 | 0.4.0 |
| StaticArrays | 1 | 1 | 1.9.18 (unchanged) |
| Symbolics | 5, 6 | 7 | 7.17.0 |

### Code changes needed for compatibility

**Removed `@register_symbolic x ∈ y::Interval`** from `src/IntervalConstraintProgramming.jl`:
- IntervalArithmetic v1.0 follows IEEE 1788 and deliberately does NOT define `Base.==` or `Base.isequal`/`Base.hash` for `Interval`. Users should use `isequal_interval` etc.
- SymbolicUtils uses `isequal`/`hash` for hash-consing symbolic expressions. Embedding an `Interval` in a symbolic expression (via `@register_symbolic x ∈ y::Interval`) triggers `isequal` → `==` → `InconclusiveBooleanOperation` error.
- **Decision: Do NOT define `Base.isequal`/`Base.hash` for `Interval`** — that would be type piracy contradicting IntervalArithmetic's design. Instead, remove the `∈` registration and avoid putting `Interval` values inside symbolic expressions.

**Replaced `@register_symbolic x ∈ y::Interval`** with decomposition in `src/IntervalConstraintProgramming.jl`:
- New: `Base.in(x::Num, y::Interval) = (x >= Num(inf(y))) & (x <= Num(sup(y)))`
- This decomposes `x ∈ a..b` into `(x >= a) & (x <= b)` at the symbolic level, avoiding Interval values in the symbolic tree entirely.
- Users can still write `x^2 + y^2 ∈ interval(0, 1)` — it just gets decomposed into two comparison constraints combined with `&`.

**Changed `Separator` constructor** in `src/contractor.jl`:
- Old: `Separator(ex, vars, constraint::Interval) = Separator(vars, ex ∈ constraint, constraint, ...)`
- New: `Separator(ex, vars, constraint::Interval) = Separator(vars, ex, constraint, ...)`
- The `ex` field no longer wraps in `∈ constraint` (the constraint is already stored separately in the `constraint` field).

**Updated `show` for `AbstractSeparator`** to display constraint info when available (via `hasproperty` check), since `ex` no longer contains it.

**Fixed pre-existing bug in `separator()` in `src/utils.jl`**:
- The `&` and `|` handlers used `∩`/`∪` (`Base.intersect`/`Base.union`) instead of `⊓`/`⊔` (from IntervalArithmetic.Symbols, defined for separators in `set_operations.jl`).
- This bug was never triggered before because tests didn't exercise the `separator()` path with `&`/`|`. It surfaced now because `∈` decomposes into `(expr >= lo) & (expr <= hi)`.

### Known limitations

- Chained comparisons like `0 <= x^2+y^2 <= 1` don't work (Julia lowers to `&&` which requires `Bool`). Users should use `x^2+y^2 ∈ interval(0, 1)` instead. A `@constraint` macro could fix this — see `future.md`.
- ReversePropagation emits many "Method definition overwritten" warnings with Symbolics v7. Harmless but noisy — see `future.md`.
14 changes: 7 additions & 7 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "IntervalConstraintProgramming"
uuid = "138f1668-1576-5ad7-91b9-7425abbf3153"
version = "0.14.0"
version = "0.15.0"

[deps]
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
Expand All @@ -11,13 +11,13 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"

[compat]
IntervalArithmetic = "0.22.12"
IntervalBoxes = "0.2"
IntervalContractors = "0.5"
ReversePropagation = "0.3"
IntervalArithmetic = "1"
IntervalBoxes = "0.3"
IntervalContractors = "0.6"
ReversePropagation = "0.4.1"
StaticArrays = "1"
Symbolics = "5, 6"
julia = "1"
Symbolics = "6 - 7"
julia = "1.10"

[extras]
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
Expand Down
6 changes: 0 additions & 6 deletions REQUIRE

This file was deleted.

18 changes: 18 additions & 0 deletions future.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Future work

## `@constraint` macro for chained comparisons

Julia lowers `0 <= x^2+y^2 <= 1` to `(0 <= x^2+y^2) && (x^2+y^2 <= 1)`, and `&&` requires a `Bool`, so this fails with symbolic expressions.

A `@constraint` macro could intercept the AST before lowering and convert chained comparisons into `&` (which works symbolically) or directly into the `∈` form:

```julia
@constraint 0 <= x^2 + y^2 <= 1 # → x^2+y^2 ∈ interval(0, 1)
@constraint x^2 + y^2 <= 1 # → simple case, works as-is
```

The package already exports `@constraint` but it is not yet defined.

## ReversePropagation method overwrite warnings

ReversePropagation emits many "Method definition overwritten" warnings with Symbolics v7. Harmless but noisy — should be addressed upstream in ReversePropagation.
8 changes: 6 additions & 2 deletions src/IntervalConstraintProgramming.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ import Base:

import IntervalArithmetic: mid, interval, emptyinterval, isinf, isinterior, hull, mince


@register_symbolic ¬(x)
@register_symbolic x ∈ y::Interval
@register_symbolic x ∨ y
@register_symbolic x ∧ y

# We cannot register `x ∈ y::Interval` as a symbolic operation because
# SymbolicUtils hash-consing requires isequal/hash, which Interval deliberately
# does not define (IEEE 1788). Instead, decompose into comparisons that the
# package already handles.
Base.in(x::Num, y::Interval) = (x >= Num(inf(y))) & (x <= Num(sup(y)))

export
# BasicContractor,
# @contractor,
Expand Down
10 changes: 8 additions & 2 deletions src/contractor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,21 @@ end

# Base.show(io::IO, S::Separator) = print(io, "Separator($(S.ex) ∈ $(S.constraint), vars = $(join(S.vars, ", ")))")

Base.show(io::IO, S::AbstractSeparator) = print(io, "Separator($(S.ex), vars=$(join(S.vars, ", ")))")
function Base.show(io::IO, S::AbstractSeparator)
if hasproperty(S, :constraint)
print(io, "Separator($(S.ex) ∈ $(S.constraint), vars=$(join(S.vars, ", ")))")
else
print(io, "Separator($(S.ex), vars=$(join(S.vars, ", ")))")
end
end

function Separator(orig_expr, vars)
ex, constraint = analyse(orig_expr)

return Separator(ex, vars, constraint)
end

Separator(ex, vars, constraint::Interval) = Separator(vars, ex ∈ constraint, constraint, make_function(ex, vars), Contractor(ex, vars))
Separator(ex, vars, constraint::Interval) = Separator(vars, ex, constraint, make_function(ex, vars), Contractor(ex, vars))

function separate_infinite_box(S::Separator, X::IntervalBox)
# for an box that extends to infinity we cannot evaluate at a corner
Expand Down
20 changes: 10 additions & 10 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ function analyse(ex)
if op ∈ (≤, <)
constraint = interval(-Inf, 0)
Num(lhs - rhs), constraint

elseif op ∈ (≥, >)
constraint = interval(0, +Inf)
Num(lhs - rhs), constraint

elseif op == (==)
constraint = interval(0, 0)
Num(lhs - rhs), constraint

else
return ex, interval(0, 0) # implicit 0
end

end


Expand All @@ -59,23 +59,23 @@ function separator(ex, vars)
lhs, rhs = arguments(ex2)

if op == &
return separator(lhs, vars) separator(rhs, vars)
return separator(lhs, vars) separator(rhs, vars)

elseif op == |
return separator(lhs, vars) separator(rhs, vars)
return separator(lhs, vars) separator(rhs, vars)

elseif op ∈ (≤, <)
constraint = interval(-Inf, 0)
Separator(Num(lhs - rhs), vars, constraint)

elseif op ∈ (≥, >)
constraint = interval(0, +Inf)
Separator(Num(lhs - rhs), vars, constraint)

elseif op == (==)
constraint = interval(0, 0)
Separator(Num(lhs - rhs), vars, constraint)

else
return Separator(ex, vars, interval(0, 0)) # implicit "== 0"
end
Expand Down
Loading