1################################### 2# Parse the QMASM command line # 3# By Scott Pakin <pakin@lanl.gov> # 4################################### 5 6import argparse 7import re 8import shlex 9import string 10import sys 11 12class ParseCommandLine(object): 13 def parse_command_line(self): 14 "Parse the QMASM command line. Return an argparse.Namespace." 15 16 # Define all of our command-line arguments. 17 cl_parser = argparse.ArgumentParser(description="Assemble a symbolic Hamiltonian into a numeric one") 18 cl_parser.add_argument("input", nargs="*", 19 help="file(s) from which to read a symbolic Hamiltonian") 20 cl_parser.add_argument("-v", "--verbose", action="count", default=0, 21 help="increase output verbosity (can be specified repeatedly)") 22 cl_parser.add_argument("--run", action="store_true", 23 help="run the program on the current solver") 24 cl_parser.add_argument("-o", "--output", metavar="FILE", default="<stdout>", 25 help="file to which to write weights and strengths (default: none)") 26 cl_parser.add_argument("-O", type=int, nargs="?", const=1, default=0, 27 metavar="LEVEL", 28 help="optimize the layout; at -O1, remove unnecessary qubits") 29 cl_parser.add_argument("--pin", action="append", 30 help="pin a set of qubits to a set of true or false values") 31 cl_parser.add_argument("--format", choices=["qubist", "ocean", "qbsolv", "qmasm", "minizinc", "bqpjson"], default="qubist", 32 help="output-file format") 33 cl_parser.add_argument("--values", choices=["bools", "ints"], default="bools", 34 help="output solution values as Booleans or integers (default: bools)") 35 cl_parser.add_argument("--profile", type=str, default=None, metavar="NAME", 36 help="Profile name from dwave.conf to use") 37 cl_parser.add_argument("--solver", type=str, default=None, metavar="NAME", 38 help='Solver name from dwave.conf to use or one of the special names "exact", "sim-anneal", "tabu", "kerberos[,<solver>]", or "qbsolv[,<solver>]"') 39 cl_parser.add_argument("--chain-strength", metavar="NEG_NUM", type=float, 40 help="negative-valued chain strength (default: automatic)") 41 cl_parser.add_argument("--pin-weight", metavar="NEG_NUM", type=float, 42 help="negative-valued pin weight (default: automatic)") 43 cl_parser.add_argument("--qubo", action="store_true", 44 help="where supported, produce output files in QUBO rather than Ising format") 45 cl_parser.add_argument("--samples", metavar="POS_INT", type=int, default=1000, 46 help="number of samples to take (default: 1000)") 47 cl_parser.add_argument("--anneal-time", metavar="POS_INT", type=int, default=None, 48 help="annealing time in microseconds (default: automatic)") 49 cl_parser.add_argument("--spin-revs", metavar="POS_INT", type=int, default=0, 50 help="number of spin-reversal transforms to perform (default: 0)") 51 cl_parser.add_argument("--topology-file", default=None, metavar="FILE", 52 help="name of a file describing the topology (list of vertex pairs)") 53 cl_parser.add_argument("--postproc", choices=["none", "sample", "opt"], 54 default="none", 55 help='type of postprocessing to perform (default: "none")') 56 cl_parser.add_argument("--show", choices=["valid", "all", "best"], default="valid", 57 help='show valid solutions, all solutions, or the best (even if invalid) solutions (default: "valid")') 58 cl_parser.add_argument("--always-embed", action="store_true", 59 help="when writing an output file, embed the problem in the physical topology even when not required (default: false)") 60 cl_parser.add_argument("--composites", metavar="COMP1,COMP2,...", 61 default="", 62 help='wrap the solver within one or more composites (currently only "virtualgraph")') 63 cl_parser.add_argument("--pack-qubits", metavar="POS_INT", type=int, 64 help='attempt to pack the problem into an N-qubit "corner" of the physical topology during embedding') 65 cl_parser.add_argument("--physical", action="store_true", 66 help="map variables containing a number to the physical qubits represented by that number") 67 cl_parser.add_argument("--schedule", metavar="T,S,...", type=str, 68 help="specify an annealing schedule as alternating lists of times (microseconds) and annealing fractions (0.0 to 1.0)") 69 70 # Parse the command line. 71 cl_args = cl_parser.parse_args() 72 73 # Perform a few sanity checks on the parameters. 74 if cl_args.chain_strength != None and cl_args.chain_strength >= 0.0: 75 self.warn("A non-negative chain strength (%.20g) was specified\n" % cl_args.chain_strength) 76 if cl_args.pin_weight != None and cl_args.pin_weight >= 0.0: 77 self.warn("A non-negative pin strength (%.20g) was specified\n" % cl_args.pin_weight) 78 if cl_args.spin_revs > cl_args.samples: 79 self.abend("The number of spin reversals is not allowed to exceed the number of samples") 80 self.parse_composite_string(cl_args.composites) # Check for errors and discard the result. 81 self.parse_anneal_sched_string(cl_args.schedule) # Check for errors and discard the result. 82 return cl_args 83 84 def parse_composite_string(self, cstr): 85 "Split the composites string into a list. Abort on error." 86 comps = [] 87 if cstr == "": 88 return comps 89 for c in cstr.split(","): 90 if c == "virtualgraph": 91 comps.append("VirtualGraph") 92 else: 93 self.abend('Unrecognized composite "%s"' % c) 94 return comps 95 96 def parse_anneal_sched_string(self, astr): 97 "Parse an annealing schedule into a list of (time, frac) tuples." 98 if astr == None: 99 return None 100 num_re = re.compile(r'[-+Ee.\d]+') # All characters that can appear in a floating-point-number 101 nums = num_re.findall(astr) 102 if len(nums)%2 == 1: 103 self.abend('Failed to parse "%s" as alternating times and annealing fractions' % astr) 104 sched = [] 105 for i in range(0, len(nums), 2): 106 try: 107 t = float(nums[i]) 108 except ValueError: 109 self.abend('Failed to parse "%s" as a floating-point number' % nums[i]) 110 try: 111 s = float(nums[i + 1]) 112 except ValueError: 113 self.abend('Failed to parse "%s" as a floating-point number' % nums[i + 1]) 114 sched.append((t, s)) 115 if len(sched) < 2: 116 self.abend('Failed to parse "%s" into two or more (time, frac) pairs' % astr) 117 return sched 118 119 def get_command_line(self): 120 "Return the command line as a string, properly quoted." 121 return " ".join([shlex.quote(a) for a in sys.argv]) 122 123 def report_command_line(self, cl_args): 124 "For provenance and debugging purposes, report our command line parameters." 125 # Output the command line as is. 126 verbosity = cl_args.verbose 127 if verbosity < 1: 128 return 129 sys.stderr.write("Command line provided:\n\n") 130 sys.stderr.write(" %s\n\n" % self.get_command_line()) 131 132 # At higher levels of verbosity, output every single option. 133 if verbosity < 2: 134 return 135 sys.stderr.write("All QMASM parameters:\n\n") 136 params = vars(cl_args) 137 klen = max([len(a) for a in params.keys()]) 138 klen = max(klen + 2, len("Option")) # +2 for "--" 139 vlen = max([len(repr(a)) for a in params.values()]) 140 klen = max(vlen, len("Value(s)")) 141 sys.stderr.write(" %-*s %-*s\n" % (klen, "Option", vlen, "Value(s)")) 142 sys.stderr.write(" %s %s\n" % ("-" * klen, "-" * vlen)) 143 for k in sorted(params.keys()): 144 kname = k.replace("_", "-") 145 sys.stderr.write(" %-*s %-*s\n" % (klen, "--" + kname, vlen, repr(params[k]))) 146 sys.stderr.write("\n") 147