packages feed

minizinc-process-0.1.4.1: README.md

minizinc-process
================

MiniZinc is a language and a toolchain to solve discrete optimization problems.
This package offers wrappers around the `minizinc` executable to pass inputs and outputs.

Assume that a primitive MiniZinc model is available at the path `models/example001.mzn`.

```minizinc
0..100: x;
var int: y;
constraint x < y;
```

This model expects `x` as an Int and decides `y` as an Int if a solution is
found. Ideally we would like to use minizinc and this model like a function of
type `Int -> IO (Maybe Int)` function in Haskell.
This package provides building blocks to create such a mapping.

# Implementation

This package relies on JSON support for MiniZinc by using JSON as an
intermediary representation. On the Haskell side we picked the popular `aeson`
package for serializing values.

MiniZinc input binds names to variables, hence the `Int -> IO (Maybe Int)`
example above is insufficient: inputs and outputs need to translate to JSON
`Object` constructor of [Aeson's Value type](https://hackage.haskell.org/package/aeson-1.1.1.0/docs/Data-Aeson.html#t:Value).

# Example Use

The `runLastMinizincJSON` function requires some configuration object to
provide parameters like the solver backing MiniZinc, the timeout, where to
store MiniZinc data files. The `simpleMiniZinc` function provides a smart
constructor for building such an environment.

```haskell
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeApplications #-}

module Main where

import Data.Aeson
import Data.Hashable
import GHC.Generics
import Process.Minizinc
import Process.Minizinc.Inspect

data Input = Input {x :: Int}
  deriving (Generic)

instance ToJSON Input -- required by `runLastMinizincJSON` for serialization of input

instance Hashable Input -- required by `simpleMiniZinc` to create a somewhat unique filepath

data Output = Output {y :: Int}
  deriving (Show, Generic)

instance FromJSON Output -- required by `runLastMinizincJSON` for deserialization of output

main :: IO ()
main = do
  inspect "models/example001.mzn" >>= print
  let mzn = simpleMiniZinc @Input @Output "models/example001.mzn" 1000 Gecode
  let problem = Input 10
  runLastMinizincJSON mzn problem >>= print
```

The `@Input` and `@Output` syntax allow to pass type parameters to
`simpleMiniZinc`, this style is optional but helps the GHC compiler inference
(in our example, this type application is the only indication needed to tell
the compiler to deserialize `Output` objects).

A more-general function named `runMinizincJSON` allows to stream results
iteratively has the solver progresses. This function is more complicated than
`runLastMinizincJSON` has it takes a callback to consume an output and control
whether to continue reading inputs or not (i.e., a coroutine) plus some initial
state that is carried over (a bit like a fold). Luckily, we provide two
coroutines `keepLast` and `collectResults` for some typical use cases.

# Usage in a project

## MiniZinc model files

In a typical project, you will have fixed models and varying inputs.
That is, you would like to carry the models along with the code (e.g., a web
application or gRPC server using minizinc in the background) in a same
repository as your Haskell code. One option is to leverage the support of cabal
[data-files](https://www.haskell.org/cabal/users-guide/developing-packages.html#accessing-data-files-from-package-code).

## Serialization and DeSerialization

You will still need some mapping functions to translate between domain objects
like `User` into the JSON values that MiniZinc requires: objects do not map
well with relations. We may consider compile-time helpers like TemplateHaskell,
but at this time it would not be immediately feasible. Be at peace with this.

A module named `Process.Minizinc.TH` has TemplateHaskell functions to
generate. As of today, you'll still need to activate some extensions and
import some libraries so that the TemplateHaskell-generated code compiles: as in the following example.

```haskell
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TemplateHaskell #-}

import Data.Aeson
import Data.Hashable
import Process.Minizinc.TH
import GHC.Generics

genModelData "MyModel" "models/mymodel.mzn"
```

```minizinc
int: x;
array[1..2,1..2] of int: y;
var int: z;

...
```

Will generate the following haskell codes

```haskell
data MyModelOutput = MyModelOutput {
  z :: Int
} deriving (Show, Eq, Ord, Generic, Hashable, ToJSON, FromJSON)

data MyModelInput = MyModelInput {
  x :: Int,
  y :: [[Int]]
} deriving (Show, Eq, Ord, Generic, Hashable, ToJSON, FromJSON)
```

See `examples/Main.hs` for an example usage of TemplateHaskell.


## Temporary data files

For now, the implementation leverages file-system to pass the JSON object to
MiniZinc, this design means you should pay attention to disk usage and maybe
clean the clutter. A function named `cleanTmpFile` will remove the `.json` disk
file for a given pair of MiniZinc and input objects.

# Development

## Testing

We use [Hedgehog](https://hedgehog.qa/) to test the overall system at once rather than having
individual tests for the internal parser and other files.

Test cases are in `.hs` files under the `test` directory whereas the `.mzn`
models are in the `models` directory.
We use a naming nomenclature to help organize what files require what
input/output types: `test{inputtype}_{testnum}.mzn` where `inputtype` pertains
to the haskell Input/Output types and `testnum` pertains to the test number.
Thus: all `testnum` are unique and are groupable by `inputtype`.

# Misc.

The author of this package is not affiliated with MiniZinc.
See also: [https://www.minizinc.org/](https://www.minizinc.org/).