Task 028: .AMPL File Format

Task 028: .AMPL File Format

1. List of Properties of the .AMPL File Format

Based on the specifications of the AMPL .nl file format (which is the structured format used by AMPL for solver communication, and aligns with the queried .AMPL format), the intrinsic properties are derived from the header structure. These are the key statistics and flags encoded in the first 10 ASCII lines of the file. The properties are:

  • Format type (ASCII 'g' or Binary 'b')
  • Option flags (list of integers from the first line, e.g., ARP code, wantsol flag)
  • Number of variables
  • Number of algebraic constraints
  • Number of objectives
  • Number of logical constraints
  • Number of range constraints
  • Number of equality constraints
  • Number of nonlinear constraints
  • Number of nonlinear objectives
  • Number of nonlinear network constraints
  • Number of linear network constraints
  • Number of nonlinear variables in constraints
  • Number of nonlinear variables in objectives
  • Number of nonlinear variables in both constraints and objectives
  • Number of linear network variables
  • Number of functions
  • Number of arithmetic expressions
  • Flags (bitfield for various model features)
  • Number of binary variables
  • Number of integer variables
  • Number of nonlinear binary/integer variables
  • Number of nonzeros in all Jacobian columns
  • Number of nonzeros in all objective gradients
  • Max constraint name length
  • Max variable name length
  • Number of common expressions in bounds
  • Number of common expressions in single-variable constraints
  • Number of common expressions in single-variable objectives
  • Number of common expressions in multiple-variable constraints
  • Number of common expressions in multiple-variable objectives

These properties define the core structure and dimensions of the optimization problem encoded in the file.

(Note: These are example .nl files from AMPL documentation, representing the .AMPL format structure.)

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop

This is a self-contained HTML snippet with embedded JavaScript that can be embedded in a Ghost blog post. It creates a drop zone where a user can drag and drop a .AMPL (.nl) file. The script reads the file as text, parses the first 10 lines to extract the properties, and dumps them to the screen in a readable list.

Drag and drop .AMPL file here

4. Python Class

This Python class can open a .AMPL (.nl) file, decode and read the header, print the properties to console, and also write a sample .nl file with predefined properties.

class AmplFileHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}

    def read_and_decode(self):
        with open(self.filepath, 'r') as f:
            lines = [f.readline().strip() for _ in range(10)]
        if len(lines) < 10:
            print("Invalid header")
            return
        self.properties['Format type'] = 'ASCII' if lines[0][0] == 'g' else 'Binary'
        self.properties['Option flags'] = ', '.join(lines[0][1:].split())
        
        line2 = list(map(int, lines[1].split()))
        self.properties['Number of variables'] = line2[0]
        self.properties['Number of algebraic constraints'] = line2[1]
        self.properties['Number of objectives'] = line2[2]
        self.properties['Number of logical constraints'] = line2[3] if len(line2) > 3 else 0
        self.properties['Number of range constraints'] = line2[4] if len(line2) > 4 else 0
        self.properties['Number of equality constraints'] = line2[5] if len(line2) > 5 else 0
        
        line3 = list(map(int, lines[2].split()))
        self.properties['Number of nonlinear constraints'] = line3[0]
        self.properties['Number of nonlinear objectives'] = line3[1]
        
        line4 = list(map(int, lines[3].split()))
        self.properties['Number of nonlinear network constraints'] = line4[0]
        self.properties['Number of linear network constraints'] = line4[1]
        
        line5 = list(map(int, lines[4].split()))
        self.properties['Number of nonlinear variables in constraints'] = line5[0]
        self.properties['Number of nonlinear variables in objectives'] = line5[1]
        self.properties['Number of nonlinear variables in both'] = line5[2] if len(line5) > 2 else 0
        
        line6 = list(map(int, lines[5].split()))
        self.properties['Number of linear network variables'] = line6[0]
        self.properties['Number of functions'] = line6[1]
        self.properties['Number of arithmetic expressions'] = line6[2]
        self.properties['Flags'] = line6[3] if len(line6) > 3 else 0
        
        line7 = list(map(int, lines[6].split()))
        self.properties['Number of binary variables'] = line7[0]
        self.properties['Number of integer variables'] = line7[1]
        self.properties['Number of nonlinear binary/integer variables'] = line7[2] if len(line7) > 2 else 0
        
        line8 = list(map(int, lines[7].split()))
        self.properties['Number of nonzeros in all Jacobian columns'] = line8[0]
        self.properties['Number of nonzeros in all objective gradients'] = line8[1]
        
        line9 = list(map(int, lines[8].split()))
        self.properties['Max constraint name length'] = line9[0]
        self.properties['Max variable name length'] = line9[1]
        
        line10 = list(map(int, lines[9].split()))
        self.properties['Number of common expressions in bounds'] = line10[0]
        self.properties['Number of common expressions in single-variable constraints'] = line10[1]
        self.properties['Number of common expressions in single-variable objectives'] = line10[2]
        self.properties['Number of common expressions in multiple-variable constraints'] = line10[3] if len(line10) > 3 else 0
        self.properties['Number of common expressions in multiple-variable objectives'] = line10[4] if len(line10) > 4 else 0
        
        self.print_properties()

    def print_properties(self):
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write_sample(self, output_path):
        with open(output_path, 'w') as f:
            f.write('g 1 0\n')  # Sample ASCII with options
            f.write('9 4 3 1 1 2\n')
            f.write('3 3\n')
            f.write('0 0\n')
            f.write('7 8 5\n')
            f.write('0 0 0 0\n')
            f.write('0 0 0\n')
            f.write('18 12\n')
            f.write('5 5\n')
            f.write('0 0 0 0 0\n')
            # Sample body segments can be added here if needed
        print(f"Sample .AMPL file written to {output_path}")

# Example usage:
# handler = AmplFileHandler('example.nl')
# handler.read_and_decode()
# handler.write_sample('output.nl')

5. Java Class

This Java class can open a .AMPL (.nl) file, decode and read the header, print the properties to console, and write a sample .nl file.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class AmplFileHandler {
    private String filepath;
    private Map<String, Object> properties = new HashMap<>();

    public AmplFileHandler(String filepath) {
        this.filepath = filepath;
    }

    public void readAndDecode() throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(filepath))) {
            String[] lines = new String[10];
            for (int i = 0; i < 10; i++) {
                lines[i] = br.readLine().trim();
            }
            properties.put("Format type", lines[0].charAt(0) == 'g' ? "ASCII" : "Binary");
            String[] optFlags = lines[0].substring(1).trim().split("\\s+");
            properties.put("Option flags", String.join(", ", optFlags));
            
            int[] line2 = parseInts(lines[1]);
            properties.put("Number of variables", line2[0]);
            properties.put("Number of algebraic constraints", line2[1]);
            properties.put("Number of objectives", line2[2]);
            properties.put("Number of logical constraints", getOrZero(line2, 3));
            properties.put("Number of range constraints", getOrZero(line2, 4));
            properties.put("Number of equality constraints", getOrZero(line2, 5));
            
            int[] line3 = parseInts(lines[2]);
            properties.put("Number of nonlinear constraints", line3[0]);
            properties.put("Number of nonlinear objectives", line3[1]);
            
            int[] line4 = parseInts(lines[3]);
            properties.put("Number of nonlinear network constraints", line4[0]);
            properties.put("Number of linear network constraints", line4[1]);
            
            int[] line5 = parseInts(lines[4]);
            properties.put("Number of nonlinear variables in constraints", line5[0]);
            properties.put("Number of nonlinear variables in objectives", line5[1]);
            properties.put("Number of nonlinear variables in both", getOrZero(line5, 2));
            
            int[] line6 = parseInts(lines[5]);
            properties.put("Number of linear network variables", line6[0]);
            properties.put("Number of functions", line6[1]);
            properties.put("Number of arithmetic expressions", line6[2]);
            properties.put("Flags", getOrZero(line6, 3));
            
            int[] line7 = parseInts(lines[6]);
            properties.put("Number of binary variables", line7[0]);
            properties.put("Number of integer variables", line7[1]);
            properties.put("Number of nonlinear binary/integer variables", getOrZero(line7, 2));
            
            int[] line8 = parseInts(lines[7]);
            properties.put("Number of nonzeros in all Jacobian columns", line8[0]);
            properties.put("Number of nonzeros in all objective gradients", line8[1]);
            
            int[] line9 = parseInts(lines[8]);
            properties.put("Max constraint name length", line9[0]);
            properties.put("Max variable name length", line9[1]);
            
            int[] line10 = parseInts(lines[9]);
            properties.put("Number of common expressions in bounds", line10[0]);
            properties.put("Number of common expressions in single-variable constraints", line10[1]);
            properties.put("Number of common expressions in single-variable objectives", line10[2]);
            properties.put("Number of common expressions in multiple-variable constraints", getOrZero(line10, 3));
            properties.put("Number of common expressions in multiple-variable objectives", getOrZero(line10, 4));
            
            printProperties();
        }
    }

    private int[] parseInts(String line) {
        String[] parts = line.split("\\s+");
        int[] ints = new int[parts.length];
        for (int i = 0; i < parts.length; i++) {
            ints[i] = Integer.parseInt(parts[i]);
        }
        return ints;
    }

    private int getOrZero(int[] arr, int index) {
        return index < arr.length ? arr[index] : 0;
    }

    public void printProperties() {
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }

    public void writeSample(String outputPath) throws IOException {
        try (FileWriter fw = new FileWriter(outputPath)) {
            fw.write("g 1 0\n");
            fw.write("9 4 3 1 1 2\n");
            fw.write("3 3\n");
            fw.write("0 0\n");
            fw.write("7 8 5\n");
            fw.write("0 0 0 0\n");
            fw.write("0 0 0\n");
            fw.write("18 12\n");
            fw.write("5 5\n");
            fw.write("0 0 0 0 0\n");
            // Add sample body if needed
        }
        System.out.println("Sample .AMPL file written to " + outputPath);
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     AmplFileHandler handler = new AmplFileHandler("example.nl");
    //     handler.readAndDecode();
    //     handler.writeSample("output.nl");
    // }
}

6. JavaScript Class

This JavaScript class can open a .AMPL (.nl) file (using Node.js fs module), decode and read the header, print the properties to console, and write a sample .nl file.

const fs = require('fs');

class AmplFileHandler {
  constructor(filepath) {
    this.filepath = filepath;
    this.properties = {};
  }

  readAndDecode() {
    const content = fs.readFileSync(this.filepath, 'utf8');
    const lines = content.split('\n').slice(0, 10).map(l => l.trim());
    if (lines.length < 10) {
      console.log('Invalid header');
      return;
    }
    this.properties['Format type'] = lines[0][0] === 'g' ? 'ASCII' : 'Binary';
    this.properties['Option flags'] = lines[0].slice(1).trim().split(/\s+/).join(', ');
    
    const line2 = lines[1].split(/\s+/).map(Number);
    this.properties['Number of variables'] = line2[0];
    this.properties['Number of algebraic constraints'] = line2[1];
    this.properties['Number of objectives'] = line2[2];
    this.properties['Number of logical constraints'] = line2[3] || 0;
    this.properties['Number of range constraints'] = line2[4] || 0;
    this.properties['Number of equality constraints'] = line2[5] || 0;
    
    const line3 = lines[2].split(/\s+/).map(Number);
    this.properties['Number of nonlinear constraints'] = line3[0];
    this.properties['Number of nonlinear objectives'] = line3[1];
    
    const line4 = lines[3].split(/\s+/).map(Number);
    this.properties['Number of nonlinear network constraints'] = line4[0];
    this.properties['Number of linear network constraints'] = line4[1];
    
    const line5 = lines[4].split(/\s+/).map(Number);
    this.properties['Number of nonlinear variables in constraints'] = line5[0];
    this.properties['Number of nonlinear variables in objectives'] = line5[1];
    this.properties['Number of nonlinear variables in both'] = line5[2] || 0;
    
    const line6 = lines[5].split(/\s+/).map(Number);
    this.properties['Number of linear network variables'] = line6[0];
    this.properties['Number of functions'] = line6[1];
    this.properties['Number of arithmetic expressions'] = line6[2];
    this.properties['Flags'] = line6[3] || 0;
    
    const line7 = lines[6].split(/\s+/).map(Number);
    this.properties['Number of binary variables'] = line7[0];
    this.properties['Number of integer variables'] = line7[1];
    this.properties['Number of nonlinear binary/integer variables'] = line7[2] || 0;
    
    const line8 = lines[7].split(/\s+/).map(Number);
    this.properties['Number of nonzeros in all Jacobian columns'] = line8[0];
    this.properties['Number of nonzeros in all objective gradients'] = line8[1];
    
    const line9 = lines[8].split(/\s+/).map(Number);
    this.properties['Max constraint name length'] = line9[0];
    this.properties['Max variable name length'] = line9[1];
    
    const line10 = lines[9].split(/\s+/).map(Number);
    this.properties['Number of common expressions in bounds'] = line10[0];
    this.properties['Number of common expressions in single-variable constraints'] = line10[1];
    this.properties['Number of common expressions in single-variable objectives'] = line10[2];
    this.properties['Number of common expressions in multiple-variable constraints'] = line10[3] || 0;
    this.properties['Number of common expressions in multiple-variable objectives'] = line10[4] || 0;
    
    this.printProperties();
  }

  printProperties() {
    for (const [key, value] of Object.entries(this.properties)) {
      console.log(`${key}: ${value}`);
    }
  }

  writeSample(outputPath) {
    const sampleContent = `
g 1 0
9 4 3 1 1 2
3 3
0 0
7 8 5
0 0 0 0
0 0 0
18 12
5 5
0 0 0 0 0
`.trim();
    fs.writeFileSync(outputPath, sampleContent);
    console.log(`Sample .AMPL file written to ${outputPath}`);
  }
}

// Example usage:
// const handler = new AmplFileHandler('example.nl');
// handler.readAndDecode();
// handler.writeSample('output.nl');

7. C Class (C++ Implementation)

This C++ class can open a .AMPL (.nl) file, decode and read the header, print the properties to console, and write a sample .nl file.

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>

class AmplFileHandler {
private:
    std::string filepath;
    std::map<std::string, std::string> properties;

    std::vector<int> parseInts(const std::string& line) {
        std::vector<int> ints;
        std::stringstream ss(line);
        int num;
        while (ss >> num) {
            ints.push_back(num);
        }
        return ints;
    }

    int getOrZero(const std::vector<int>& vec, size_t index) {
        return index < vec.size() ? vec[index] : 0;
    }

public:
    AmplFileHandler(const std::string& filepath) : filepath(filepath) {}

    void readAndDecode() {
        std::ifstream file(filepath);
        if (!file.is_open()) {
            std::cout << "Failed to open file" << std::endl;
            return;
        }
        std::vector<std::string> lines(10);
        for (int i = 0; i < 10; ++i) {
            std::getline(file, lines[i]);
            // Trim trailing whitespace
            size_t end = lines[i].find_last_not_of(" \t\r\n");
            if (end != std::string::npos) {
                lines[i] = lines[i].substr(0, end + 1);
            }
        }
        file.close();

        if (lines.size() < 10) {
            std::cout << "Invalid header" << std::endl;
            return;
        }

        properties["Format type"] = (lines[0][0] == 'g') ? "ASCII" : "Binary";
        std::string optStr = lines[0].substr(1);
        // Trim leading spaces
        size_t start = optStr.find_first_not_of(" \t");
        if (start != std::string::npos) {
            optStr = optStr.substr(start);
        }
        std::stringstream optSs(optStr);
        std::string opts;
        int opt;
        while (optSs >> opt) {
            if (!opts.empty()) opts += ", ";
            opts += std::to_string(opt);
        }
        properties["Option flags"] = opts;

        auto line2 = parseInts(lines[1]);
        properties["Number of variables"] = std::to_string(line2[0]);
        properties["Number of algebraic constraints"] = std::to_string(line2[1]);
        properties["Number of objectives"] = std::to_string(line2[2]);
        properties["Number of logical constraints"] = std::to_string(getOrZero(line2, 3));
        properties["Number of range constraints"] = std::to_string(getOrZero(line2, 4));
        properties["Number of equality constraints"] = std::to_string(getOrZero(line2, 5));

        auto line3 = parseInts(lines[2]);
        properties["Number of nonlinear constraints"] = std::to_string(line3[0]);
        properties["Number of nonlinear objectives"] = std::to_string(line3[1]);

        auto line4 = parseInts(lines[3]);
        properties["Number of nonlinear network constraints"] = std::to_string(line4[0]);
        properties["Number of linear network constraints"] = std::to_string(line4[1]);

        auto line5 = parseInts(lines[4]);
        properties["Number of nonlinear variables in constraints"] = std::to_string(line5[0]);
        properties["Number of nonlinear variables in objectives"] = std::to_string(line5[1]);
        properties["Number of nonlinear variables in both"] = std::to_string(getOrZero(line5, 2));

        auto line6 = parseInts(lines[5]);
        properties["Number of linear network variables"] = std::to_string(line6[0]);
        properties["Number of functions"] = std::to_string(line6[1]);
        properties["Number of arithmetic expressions"] = std::to_string(line6[2]);
        properties["Flags"] = std::to_string(getOrZero(line6, 3));

        auto line7 = parseInts(lines[6]);
        properties["Number of binary variables"] = std::to_string(line7[0]);
        properties["Number of integer variables"] = std::to_string(line7[1]);
        properties["Number of nonlinear binary/integer variables"] = std::to_string(getOrZero(line7, 2));

        auto line8 = parseInts(lines[7]);
        properties["Number of nonzeros in all Jacobian columns"] = std::to_string(line8[0]);
        properties["Number of nonzeros in all objective gradients"] = std::to_string(line8[1]);

        auto line9 = parseInts(lines[8]);
        properties["Max constraint name length"] = std::to_string(line9[0]);
        properties["Max variable name length"] = std::to_string(line9[1]);

        auto line10 = parseInts(lines[9]);
        properties["Number of common expressions in bounds"] = std::to_string(line10[0]);
        properties["Number of common expressions in single-variable constraints"] = std::to_string(line10[1]);
        properties["Number of common expressions in single-variable objectives"] = std::to_string(line10[2]);
        properties["Number of common expressions in multiple-variable constraints"] = std::to_string(getOrZero(line10, 3));
        properties["Number of common expressions in multiple-variable objectives"] = std::to_string(getOrZero(line10, 4));

        printProperties();
    }

    void printProperties() {
        for (const auto& pair : properties) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
    }

    void writeSample(const std::string& outputPath) {
        std::ofstream file(outputPath);
        if (!file.is_open()) {
            std::cout << "Failed to write file" << std::endl;
            return;
        }
        file << "g 1 0\n";
        file << "9 4 3 1 1 2\n";
        file << "3 3\n";
        file << "0 0\n";
        file << "7 8 5\n";
        file << "0 0 0 0\n";
        file << "0 0 0\n";
        file << "18 12\n";
        file << "5 5\n";
        file << "0 0 0 0 0\n";
        file.close();
        std::cout << "Sample .AMPL file written to " << outputPath << std::endl;
    }
};

// Example usage:
// int main() {
//     AmplFileHandler handler("example.nl");
//     handler.readAndDecode();
//     handler.writeSample("output.nl");
//     return 0;
// }