basic transpile
parent
c54b7e122f
commit
2c985945de
@ -1,18 +1,91 @@
|
|||||||
|
require Logger
|
||||||
|
|
||||||
defmodule ElixirStan do
|
defmodule ElixirStan do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Documentation for `ElixirStan`.
|
Documentation for `ElixirStan`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Hello world.
|
Get the version of the stanc3 executable bundled with the library.
|
||||||
|
"""
|
||||||
|
@spec stanc_version :: String.t() | {:error, String.t()}
|
||||||
|
def stanc_version() do
|
||||||
|
case System.cmd(Application.app_dir(:elixir_stan, "/priv/bin/stanc"), ["--version"]) do
|
||||||
|
{version_string, 0} -> String.trim(version_string)
|
||||||
|
{error, _} -> {:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Computes the hash of a file used for caching.
|
||||||
|
|
||||||
|
Computes SHA256 of the file and truncates it to 8 bytes, then encodes it in a hex string.
|
||||||
|
"""
|
||||||
|
@spec hash_file(binary()) :: String.t()
|
||||||
|
def hash_file(file) when is_binary(file) do
|
||||||
|
<<hash::bitstring-size(64), _::binary>> = :crypto.hash(:sha256, file)
|
||||||
|
|
||||||
|
Base.encode16 hash
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec model_tmp_dir(Path.t()) :: binary | {:error, atom}
|
||||||
|
def model_tmp_dir(path) do
|
||||||
|
with {:ok, model_code} <- File.read(path) do
|
||||||
|
model_name = Path.basename(path, ".stan")
|
||||||
|
model_hash = hash_file(model_code)
|
||||||
|
|
||||||
|
Path.join([
|
||||||
|
System.tmp_dir(),
|
||||||
|
"elixir_stan",
|
||||||
|
model_name <> model_hash <> String.replace(stanc_version(), " ", "_")
|
||||||
|
])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Transpiles the model into C++ and stores it in a temporary directory.
|
||||||
|
|
||||||
|
Temporary directory is based on `System.tmp_dir()`. An `elixir_stan` directory is created in the system temporary
|
||||||
|
directory and the model is stored in a subdirectory of that. The model directory is based on the model name, the hash
|
||||||
|
of the model code and the version of stanc used to compile it.
|
||||||
|
"""
|
||||||
|
@spec transpile_model(Path.t()) :: :ok | {:error, any()}
|
||||||
|
def transpile_model(path) do
|
||||||
|
with {:ok, model_code} <- File.read(path) do
|
||||||
|
model_tmp_path = model_tmp_dir(path)
|
||||||
|
|
||||||
## Examples
|
# Create model dir
|
||||||
|
:ok = File.mkdir_p(model_tmp_path)
|
||||||
|
|
||||||
iex> ElixirStan.hello()
|
model_tmp_file = Path.join(model_tmp_path, Path.basename(path))
|
||||||
:world
|
|
||||||
|
# Write model code
|
||||||
|
:ok = File.write(model_tmp_file, model_code)
|
||||||
|
|
||||||
|
case System.cmd(Application.app_dir(:elixir_stan, "/priv/bin/stanc"), [model_tmp_file]) do
|
||||||
|
{_, 0} -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Cleans the model tmp directory.
|
||||||
"""
|
"""
|
||||||
def hello do
|
@spec clean_model(Path.t()) :: :ok | {:error, String.t()}
|
||||||
:world
|
def clean_model(path) do
|
||||||
|
model_tmp_path = model_tmp_dir(path)
|
||||||
|
|
||||||
|
# Remove model dir
|
||||||
|
case File.rm_rf(model_tmp_path) do
|
||||||
|
{:ok, _} ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
{:error, error, file} ->
|
||||||
|
Logger.error(
|
||||||
|
"Could not clean model: #{path}\n\t Error: #{:file.format_error(error)}\n\t File: #{file}"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:error, :file.format_error(error)}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Binary file not shown.
@ -0,0 +1,13 @@
|
|||||||
|
data {
|
||||||
|
int<lower=0> N;
|
||||||
|
vector[N] y;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
real mu;
|
||||||
|
real<lower=0> sigma;
|
||||||
|
}
|
||||||
|
|
||||||
|
model {
|
||||||
|
y ~ normal(mu, sigma);
|
||||||
|
}
|
Loading…
Reference in New Issue